diff --git a/.gitignore b/.gitignore index 544eb84dfcd..0a68aa585c3 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ deploy */releases/* /monitor/TorHiddenServiceStartupTimeTests/* /monitor/monitor-tor/* +.java-version diff --git a/assets/src/main/java/bisq/asset/coins/Amitycoin.java b/assets/src/main/java/bisq/asset/coins/Amitycoin.java new file mode 100644 index 00000000000..8265df5d981 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Amitycoin.java @@ -0,0 +1,28 @@ +/* + * 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.asset.coins; + +import bisq.asset.Coin; +import bisq.asset.RegexAddressValidator; + +public class Amitycoin extends Coin { + + public Amitycoin() { + super("Amitycoin", "AMIT", new RegexAddressValidator("^amit[1-9A-Za-z^OIl]{94}")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Arqma.java b/assets/src/main/java/bisq/asset/coins/Arqma.java new file mode 100644 index 00000000000..472db1c0b65 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Arqma.java @@ -0,0 +1,30 @@ +/* + * 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.asset.coins; + +import bisq.asset.AltCoinAccountDisclaimer; +import bisq.asset.Coin; +import bisq.asset.CryptoNoteAddressValidator; + +@AltCoinAccountDisclaimer("account.altcoin.popup.arq.msg") +public class Arqma extends Coin { + + public Arqma() { + super("Arqma", "ARQ", new CryptoNoteAddressValidator(0x2cca, 0x6847)); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Bitzec.java b/assets/src/main/java/bisq/asset/coins/Bitzec.java new file mode 100644 index 00000000000..df1207315d5 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Bitzec.java @@ -0,0 +1,29 @@ +/* + * 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.asset.coins; + +import bisq.asset.Coin; +import bisq.asset.RegexAddressValidator; + +public class Bitzec extends Coin { + + public Bitzec() { + super("Bitzec", "BZC", new RegexAddressValidator("^t.*", "validation.altcoin.zAddressesNotSupported")); + } + +} diff --git a/assets/src/main/java/bisq/asset/coins/Bzedge.java b/assets/src/main/java/bisq/asset/coins/Bzedge.java new file mode 100644 index 00000000000..78c2c97a925 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Bzedge.java @@ -0,0 +1,29 @@ +/* + * 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.asset.coins; + +import bisq.asset.Coin; +import bisq.asset.RegexAddressValidator; + +public class Bzedge extends Coin { + + public Bzedge() { + super("Bzedge", "BZE", new RegexAddressValidator("^t.*", "validation.altcoin.zAddressesNotSupported")); + } + +} \ No newline at end of file diff --git a/assets/src/main/java/bisq/asset/coins/CloakCoin.java b/assets/src/main/java/bisq/asset/coins/CloakCoin.java new file mode 100644 index 00000000000..19b5d7727ed --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/CloakCoin.java @@ -0,0 +1,28 @@ +/* + * 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.asset.coins; + +import bisq.asset.Coin; +import bisq.asset.RegexAddressValidator; + +public class CloakCoin extends Coin { + + public CloakCoin() { + super("CloakCoin", "CLOAK", new RegexAddressValidator("^[B|C][a-km-zA-HJ-NP-Z1-9]{33}|^smY[a-km-zA-HJ-NP-Z1-9]{99}$")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/DSTRA.java b/assets/src/main/java/bisq/asset/coins/DSTRA.java new file mode 100644 index 00000000000..e7ec0e2ca3d --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/DSTRA.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.asset.coins; + +import bisq.asset.AddressValidationResult; +import bisq.asset.Base58BitcoinAddressValidator; +import bisq.asset.Coin; +import bisq.asset.NetworkParametersAdapter; + +public class DSTRA extends Coin { + + public DSTRA() { + super("DSTRA", "DST", new DSTRAAddressValidator()); + } + + + public static class DSTRAAddressValidator extends Base58BitcoinAddressValidator { + + public DSTRAAddressValidator() { + super(new DSTRAParams()); + } + + @Override + public AddressValidationResult validate(String address) { + if (!address.matches("^[D][a-km-zA-HJ-NP-Z1-9]{33}$")) + return AddressValidationResult.invalidStructure(); + + return super.validate(address); + } + } + + + public static class DSTRAParams extends NetworkParametersAdapter { + + public DSTRAParams() { + addressHeader = 30; + p2shHeader = 33; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Doichain.java b/assets/src/main/java/bisq/asset/coins/Doichain.java new file mode 100644 index 00000000000..d5b5d251278 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Doichain.java @@ -0,0 +1,36 @@ +/* + * 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.asset.coins; + +import bisq.asset.Base58BitcoinAddressValidator; +import bisq.asset.Coin; +import bisq.asset.NetworkParametersAdapter; + +public class Doichain extends Coin { + + public Doichain() { + super("Doichain", "DOI", new Base58BitcoinAddressValidator(new DoichainParams())); + } + + public static class DoichainParams extends NetworkParametersAdapter { + public DoichainParams() { + addressHeader = 52; + acceptableAddressCodes = new int[]{addressHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Galilel.java b/assets/src/main/java/bisq/asset/coins/Galilel.java new file mode 100644 index 00000000000..138e965330d --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Galilel.java @@ -0,0 +1,36 @@ +/* + * 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.asset.coins; + +import bisq.asset.Base58BitcoinAddressValidator; +import bisq.asset.Coin; +import bisq.asset.NetworkParametersAdapter; + +public class Galilel extends Coin { + public Galilel() { + super("Galilel", "GALI", new Base58BitcoinAddressValidator(new GalilelMainNetParams())); + } + + public static class GalilelMainNetParams extends NetworkParametersAdapter { + public GalilelMainNetParams() { + this.addressHeader = 68; + this.p2shHeader = 16; + this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Hatch.java b/assets/src/main/java/bisq/asset/coins/Hatch.java new file mode 100644 index 00000000000..bbff61765bf --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Hatch.java @@ -0,0 +1,36 @@ +/* + * 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.asset.coins; + +import bisq.asset.Base58BitcoinAddressValidator; +import bisq.asset.Coin; +import bisq.asset.NetworkParametersAdapter; + +public class Hatch extends Coin { + public Hatch() { + super("Hatch", "HATCH", new Base58BitcoinAddressValidator(new HatchMainNetParams()), Network.MAINNET); + } + + public static class HatchMainNetParams extends NetworkParametersAdapter { + public HatchMainNetParams() { + this.addressHeader = 76; + this.p2shHeader = 16; + this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/ZeroOneCoin.java b/assets/src/main/java/bisq/asset/coins/Helium.java similarity index 72% rename from assets/src/main/java/bisq/asset/coins/ZeroOneCoin.java rename to assets/src/main/java/bisq/asset/coins/Helium.java index 0fc6b3849cd..cec17a9f7ba 100644 --- a/assets/src/main/java/bisq/asset/coins/ZeroOneCoin.java +++ b/assets/src/main/java/bisq/asset/coins/Helium.java @@ -17,22 +17,22 @@ package bisq.asset.coins; +import bisq.asset.AddressValidationResult; import bisq.asset.Base58BitcoinAddressValidator; import bisq.asset.Coin; import bisq.asset.NetworkParametersAdapter; -public class ZeroOneCoin extends Coin { +public class Helium extends Coin { - public ZeroOneCoin() { - super("01coin", "ZOC", new Base58BitcoinAddressValidator(new ZeroOneCoinAddressParams())); + public Helium() { + super("Helium", "HLM", new Base58BitcoinAddressValidator(new HeliumParams())); } + public static class HeliumParams extends NetworkParametersAdapter { - public static class ZeroOneCoinAddressParams extends NetworkParametersAdapter { - - public ZeroOneCoinAddressParams() { - addressHeader = 80; - p2shHeader = 10; + public HeliumParams() { + addressHeader = 63; + p2shHeader = 5; acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } diff --git a/assets/src/main/java/bisq/asset/coins/LitecoinPlus.java b/assets/src/main/java/bisq/asset/coins/LitecoinPlus.java new file mode 100644 index 00000000000..aaf8d669b96 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/LitecoinPlus.java @@ -0,0 +1,38 @@ +/* + * 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.asset.coins; + +import bisq.asset.Base58BitcoinAddressValidator; +import bisq.asset.Coin; +import bisq.asset.NetworkParametersAdapter; + +public class LitecoinPlus extends Coin { + + public LitecoinPlus() { + super("LitecoinPlus", "LCP", new Base58BitcoinAddressValidator(new LitecoinPlusMainNetParams())); + } + + public static class LitecoinPlusMainNetParams extends NetworkParametersAdapter { + public LitecoinPlusMainNetParams() { + this.addressHeader = 75; + this.p2shHeader = 8; + this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; + } + } +} + diff --git a/assets/src/main/java/bisq/asset/coins/LitecoinZ.java b/assets/src/main/java/bisq/asset/coins/LitecoinZ.java index 735834cb830..2d6bc0a28ed 100644 --- a/assets/src/main/java/bisq/asset/coins/LitecoinZ.java +++ b/assets/src/main/java/bisq/asset/coins/LitecoinZ.java @@ -17,11 +17,9 @@ package bisq.asset.coins; -import bisq.asset.AltCoinAccountDisclaimer; import bisq.asset.Coin; import bisq.asset.RegexAddressValidator; -@AltCoinAccountDisclaimer("account.altcoin.popup.LTZ.msg") public class LitecoinZ extends Coin { public LitecoinZ() { diff --git a/assets/src/main/java/bisq/asset/coins/MirQuiX.java b/assets/src/main/java/bisq/asset/coins/MirQuiX.java new file mode 100644 index 00000000000..142daa46ca5 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/MirQuiX.java @@ -0,0 +1,28 @@ +/* + * 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.asset.coins; + +import bisq.asset.Coin; +import bisq.asset.RegexAddressValidator; + +public class MirQuiX extends Coin { + + public MirQuiX() { + super("MirQuiX", "MQX", new RegexAddressValidator("^[M][a-km-zA-HJ-NP-Z1-9]{33}$")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Navcoin.java b/assets/src/main/java/bisq/asset/coins/Navcoin.java new file mode 100644 index 00000000000..31c07866573 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Navcoin.java @@ -0,0 +1,36 @@ +/* + * 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.asset.coins; + +import bisq.asset.Base58BitcoinAddressValidator; +import bisq.asset.Coin; +import bisq.asset.NetworkParametersAdapter; + +public class Navcoin extends Coin { + public Navcoin() { + super("Navcoin", "NAV", new Base58BitcoinAddressValidator(new NavcoinParams())); + } + + public static class NavcoinParams extends NetworkParametersAdapter { + public NavcoinParams() { + this.addressHeader = 53; + this.p2shHeader = 85; + this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; + } + } +} diff --git a/assets/src/test/java/bisq/asset/coins/GridcoinTest.java b/assets/src/main/java/bisq/asset/coins/Plenteum.java similarity index 77% rename from assets/src/test/java/bisq/asset/coins/GridcoinTest.java rename to assets/src/main/java/bisq/asset/coins/Plenteum.java index 62b1241db51..4e99108383d 100644 --- a/assets/src/test/java/bisq/asset/coins/GridcoinTest.java +++ b/assets/src/main/java/bisq/asset/coins/Plenteum.java @@ -17,11 +17,12 @@ package bisq.asset.coins; -import bisq.asset.AbstractAssetWithDefaultValidatorTest; +import bisq.asset.Coin; +import bisq.asset.RegexAddressValidator; -public class GridcoinTest extends AbstractAssetWithDefaultValidatorTest { +public class Plenteum extends Coin { - public GridcoinTest() { - super(new Gridcoin()); + public Plenteum() { + super("Plenteum", "PLE", new RegexAddressValidator("^PLe[1-9A-Za-z^OIl]{95}")); } } diff --git a/assets/src/main/java/bisq/asset/coins/Qwertycoin.java b/assets/src/main/java/bisq/asset/coins/Qwertycoin.java new file mode 100644 index 00000000000..58d0849f294 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Qwertycoin.java @@ -0,0 +1,30 @@ +/* + * 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.asset.coins; + +import bisq.asset.AltCoinAccountDisclaimer; +import bisq.asset.Coin; +import bisq.asset.CryptoNoteAddressValidator; + +@AltCoinAccountDisclaimer("account.altcoin.popup.qwertycoin.msg") +public class Qwertycoin extends Coin { + + public Qwertycoin() { + super("Qwertycoin", "QWC", new CryptoNoteAddressValidator(false, 0x14820c)); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Veil.java b/assets/src/main/java/bisq/asset/coins/Veil.java new file mode 100644 index 00000000000..792cba487ab --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Veil.java @@ -0,0 +1,57 @@ +/* + * 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.asset.coins; + +import bisq.asset.*; +import org.bitcoinj.core.Address; +import org.bitcoinj.core.AddressFormatException; + +public class Veil extends Coin { + + public Veil() { + super("Veil", "VEIL", new VeilAddressValidator()); + } + + public static class VeilAddressValidator extends Base58BitcoinAddressValidator { + + public VeilAddressValidator() { + super(new VeilParams()); + } + + @Override + public AddressValidationResult validate(String address) { + if (address.startsWith("V")) { + return super.validate(address); + }else if (address.startsWith("bv")){ + // TODO: Add bech32 support + return AddressValidationResult.invalidAddress("Bech32 addresses not supported on bisq"); + } + return AddressValidationResult.invalidStructure(); + } + } + + public static class VeilParams extends NetworkParametersAdapter { + + public VeilParams() { + addressHeader = 70; + p2shHeader = 5; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} + diff --git a/assets/src/main/java/bisq/asset/coins/Gridcoin.java b/assets/src/main/java/bisq/asset/coins/XDR.java similarity index 81% rename from assets/src/main/java/bisq/asset/coins/Gridcoin.java rename to assets/src/main/java/bisq/asset/coins/XDR.java index 717573613c3..ea236b29e21 100644 --- a/assets/src/main/java/bisq/asset/coins/Gridcoin.java +++ b/assets/src/main/java/bisq/asset/coins/XDR.java @@ -18,11 +18,11 @@ package bisq.asset.coins; import bisq.asset.Coin; -import bisq.asset.DefaultAddressValidator; -public class Gridcoin extends Coin { +public class XDR extends Coin { - public Gridcoin() { - super("Gridcoin", "GRC", new DefaultAddressValidator()); + public XDR() { + super("XDR", "XDR0", new Mile.MileAddressValidator()); } + } diff --git a/assets/src/main/resources/META-INF/services/bisq.asset.Asset b/assets/src/main/resources/META-INF/services/bisq.asset.Asset index b2e508dd352..afcf7e6bac5 100644 --- a/assets/src/main/resources/META-INF/services/bisq.asset.Asset +++ b/assets/src/main/resources/META-INF/services/bisq.asset.Asset @@ -5,6 +5,8 @@ bisq.asset.coins.Actinium bisq.asset.coins.Adeptio bisq.asset.coins.Aeon +bisq.asset.coins.Amitycoin +bisq.asset.coins.Arqma bisq.asset.coins.Askcoin bisq.asset.coins.Australiacash bisq.asset.coins.Beam @@ -14,13 +16,16 @@ bisq.asset.coins.BitcoinRhodium bisq.asset.coins.Bitcoin$Testnet bisq.asset.coins.BitDaric bisq.asset.coins.Bitmark +bisq.asset.coins.Bitzec bisq.asset.coins.Blur bisq.asset.coins.BSQ$Mainnet bisq.asset.coins.BSQ$Regtest bisq.asset.coins.BSQ$Testnet bisq.asset.coins.Byteball +bisq.asset.coins.Bzedge bisq.asset.coins.Cash2 bisq.asset.coins.Chaucha +bisq.asset.coins.CloakCoin bisq.asset.coins.Counterparty bisq.asset.coins.Credits bisq.asset.coins.Croat @@ -30,36 +35,45 @@ bisq.asset.coins.Decred bisq.asset.coins.DeepOnion bisq.asset.coins.Dextro bisq.asset.coins.Dogecoin +bisq.asset.coins.Doichain bisq.asset.coins.Dragonglass +bisq.asset.coins.DSTRA bisq.asset.coins.Ether bisq.asset.coins.EtherClassic bisq.asset.coins.FourtyTwo bisq.asset.coins.Fujicoin +bisq.asset.coins.Galilel bisq.asset.coins.GambleCoin -bisq.asset.coins.Gridcoin bisq.asset.coins.Grin +bisq.asset.coins.Hatch +bisq.asset.coins.Helium bisq.asset.coins.Horizen bisq.asset.coins.IdaPay bisq.asset.coins.Iridium bisq.asset.coins.Kekcoin bisq.asset.coins.Litecoin +bisq.asset.coins.LitecoinPlus bisq.asset.coins.LitecoinZ bisq.asset.coins.Lytix bisq.asset.coins.Mask bisq.asset.coins.Mile +bisq.asset.coins.MirQuiX bisq.asset.coins.MobitGlobal bisq.asset.coins.Monero bisq.asset.coins.MonetaryUnit bisq.asset.coins.MoX bisq.asset.coins.Namecoin +bisq.asset.coins.Navcoin bisq.asset.coins.Neos bisq.asset.coins.Noir bisq.asset.coins.Persona bisq.asset.coins.Pinkcoin bisq.asset.coins.PIVX +bisq.asset.coins.Plenteum bisq.asset.coins.PZDC bisq.asset.coins.Qbase bisq.asset.coins.QMCoin +bisq.asset.coins.Qwertycoin bisq.asset.coins.Radium bisq.asset.coins.Remix bisq.asset.coins.Ryo @@ -72,13 +86,14 @@ bisq.asset.coins.SUB1X bisq.asset.coins.TurtleCoin bisq.asset.coins.UnitedCommunityCoin bisq.asset.coins.Unobtanium +bisq.asset.coins.Veil bisq.asset.coins.Webchain bisq.asset.coins.WrkzCoin +bisq.asset.coins.XDR bisq.asset.coins.Zcash bisq.asset.coins.Zcoin bisq.asset.coins.ZelCash bisq.asset.coins.Zero -bisq.asset.coins.ZeroOneCoin bisq.asset.tokens.AugmintEuro bisq.asset.tokens.DaiStablecoin bisq.asset.tokens.EtherStone diff --git a/assets/src/test/java/bisq/asset/coins/AmitycoinTest.java b/assets/src/test/java/bisq/asset/coins/AmitycoinTest.java new file mode 100644 index 00000000000..ae9135b981a --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/AmitycoinTest.java @@ -0,0 +1,48 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class AmitycoinTest extends AbstractAssetTest { + + public AmitycoinTest() { + super(new Amitycoin()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("amitMgDfvfUZ2CP1g1SEJQSN4n7qK4d45hqXSDtiFMwE5uo7DnSihknJzcEG9WtFc26fnhDHK6ydjBDKe6wjCoGt4RiP18a5Zb"); + assertValidAddress("amitUnFFwApLG9btiPWRgTjRCQUj9kZjQJ8kH3ZraSsCU4yzX4AzgaoP8jkgXhp5c5jQT3idFJChAPYzA2EydJ5A4bShqrEixa"); + assertValidAddress("amitAcVJTUZKJtYYsosMXJBQeEbt3ZV9qSvoQ1EqkvA45MRUaYWECYNKyRZ82BvLM9MPD2Gpud3DbGzGsStKnZ9x5yKVPVGJUa"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("amitAcVJTUZKJtYYsosMXJBQeEbt3ZV9qSvoQ1EqkvA45MRUaYWECYNKyRZ82BvLM9MPD2Gpud3DbGzGsStKnZ9"); + assertInvalidAddress("amitAcVJTUZKJtYYsosMXJBQeEbt3ZV9qSvoQ1EqkvA45MRUaYWECYNKyRZ82BvLM9MPD2Gpud3DbGzGsStKnZ9x5yKVPVGJUaljashfeafh"); + assertInvalidAddress(""); + assertInvalidAddress("amitAcVJTUZKJtYYsosMXJBQeEbt3ZV9qSvoQ1EqkvA45MRUaYWECY#RoPOWRwpsx1F"); + assertInvalidAddress("amitAcVJTUZKJtYYsosMXJByRZ82BvLM9MPD2Gpud3DbGzGsStKnZ9x5yKVPVGJUaJbc2q4C4fWN$C4fWNLoDLDvADvpjNYdt3sdRB434UidKXimQQn"); + assertInvalidAddress("dsfkjasd56yaSDdguaw"); + assertInvalidAddress("KEKlulzlksadfwe"); + assertInvalidAddress("HOleeSheetdsdjqwqwpoo3"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/ArqmaTest.java b/assets/src/test/java/bisq/asset/coins/ArqmaTest.java new file mode 100644 index 00000000000..3c5a5a9842b --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/ArqmaTest.java @@ -0,0 +1,44 @@ +/* + * 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.asset.coins; + + import bisq.asset.AbstractAssetTest; + import org.junit.Test; + + public class ArqmaTest extends AbstractAssetTest { + + public ArqmaTest() { + super(new Arqma()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("ar3ZLUTSac5DhxhyLJB11gcXWLYPKJchg7c8hoaKmqchC9TtHEdXzxGgt2vzCLUYwtSvkJQTXNCjzCR7KZiFUySV138PEopVC"); + assertValidAddress("aRS3V2hXuVAGAb5XWcDvN7McsSyqrEZ3XWyfMdEDCqioWNmVUuoKyNxDo7rwPCg55Ugb6KHXLN7hLZEGcnZzbm8M7uJ9YdVpeN"); + assertValidAddress("ar3mXR6SQeC3P9Dmq2LGsAeq5eDvjiNnYaywtqdNzixe6xLr38DiNVaaRKMkAQkR3NV3TuVAwAwEGH3QDgXJF3th1RwxABa9a"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress(""); + assertInvalidAddress("ar3ZLUTSac5DhxhyLJB11gcXWLYPKJchg7c8hoaKmqchC9TtHEdXzxGgt2vzCLUYwtSvkJQTXNCjzCR7KZiFUySV138PEopV"); + assertInvalidAddress("aRS3V2hXuVAGAb5XWcDvN7McsSyqrEZ3XWyfMdEDCqioWNmVUuoKyNxDo7rwPCg55Ugb6KHXLN7hLZEGcnZzbm8M7uJ9YdVpeNZz"); + assertInvalidAddress("aRV3V2hXuVAGAb5XWcDvN7McsSyqrEZ3XWyfMdEDCqioWNmVUuoKyNxDo7rwPCg55Ugb6KHXLN7hLZEGcnZzbm8M7uJ9YdVpeN"); + assertInvalidAddress("ar3mXR6SQeC3P9Dmq2LGsAeq5eDvjiNnYaywtqdNzi#exLr38DiNVaaRKMkAQkR3NV3TuVAwAwEGH3QDgXJF3th1RwxABa9a"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/BitzecTest.java b/assets/src/test/java/bisq/asset/coins/BitzecTest.java new file mode 100644 index 00000000000..dcc3ac36c75 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/BitzecTest.java @@ -0,0 +1,43 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class BitzecTest extends AbstractAssetTest { + + public BitzecTest() { + super(new Bitzec()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("t1K6LGT7z2uNTLxag6eK6XwGNpdkHbncBaK"); + assertValidAddress("t1ZjdqCGEkqL9nZ8fk9R6KA7bqNvXaVLUpF"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"); + assertInvalidAddress("38NwrYsD1HxQW5zfLT0QcUUXGMPvQgzTSn"); + assertInvalidAddress("8tP9rh3SH6n9cSLmV22vnSNNw56LKGpLrB"); + assertInvalidAddress("8Zbvjr"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/BzedgeTest.java b/assets/src/test/java/bisq/asset/coins/BzedgeTest.java new file mode 100644 index 00000000000..e65bf8d5274 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/BzedgeTest.java @@ -0,0 +1,43 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class BzedgeTest extends AbstractAssetTest { + + public BzedgeTest() { + super(new Bzedge()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("t1K6LGT7z2uNTLxag6eK6XwGNpdkHbncBaK"); + assertValidAddress("t1ZjdqCGEkqL9nZ8fk9R6KA7bqNvXaVLUpF"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"); + assertInvalidAddress("38NwrYsD1HxQW5zfLT0QcUUXGMPvQgzTSn"); + assertInvalidAddress("8tP9rh3SH6n9cSLmV22vnSNNw56LKGpLrB"); + assertInvalidAddress("8Zbvjr"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/CloakCoinTest.java b/assets/src/test/java/bisq/asset/coins/CloakCoinTest.java new file mode 100644 index 00000000000..4f6b3f7cf34 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/CloakCoinTest.java @@ -0,0 +1,49 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class CloakCoinTest extends AbstractAssetTest { + + public CloakCoinTest() { + super(new CloakCoin()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("C3MwbThsvquwA4Yg6recThXpAhR2hvRKws"); + assertValidAddress("B6MwbThsvquwA4Yg6recThXpAhR2hvKRsz"); + assertValidAddress("BCA31xPpijxiCuTQeYMpMTQsTH1m2jTg5t"); + assertValidAddress("smYmLVV33zExmaFyVp3AUjU3fJMK5E93kwzDfMnPLnEBQ7BoHZkSQhCP92hZz7Hm24yavCceNeQm8RHekqdvrhFe8gX7EdXNwnhQgQ"); + + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("1sA31xPpijxiCuTQeYMpMTQsTH1m2jTgtS"); + assertInvalidAddress("BsA31xPpijxiCuTQeYMpMTQsTH1m2jTgtSd"); + assertInvalidAddress("bech3ThsvquwA4Yg6recThXpAhR2hvRKws"); + assertInvalidAddress("smYmLYcVVzExmaFyVp3AUjU3fJMK5E93kwzDfMnPLnEBQ7BoHZkSQhCP92hZz7Hm24yavCceNeQm8RHekqdv"); + assertInvalidAddress("C3MwbThsvquwA4Yg6recThXpAhR2hvRKw"); + assertInvalidAddress(" B6MwbThsvquwA4Yg6recThXpAhR2hvKRsz"); + assertInvalidAddress("B6MwbThsvquwA4Yg6recThXpAhR2hvKRsz "); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/ZeroOneCoinTest.java b/assets/src/test/java/bisq/asset/coins/DSTRATest.java similarity index 58% rename from assets/src/test/java/bisq/asset/coins/ZeroOneCoinTest.java rename to assets/src/test/java/bisq/asset/coins/DSTRATest.java index 0dfdac67d1e..d371044cfa4 100644 --- a/assets/src/test/java/bisq/asset/coins/ZeroOneCoinTest.java +++ b/assets/src/test/java/bisq/asset/coins/DSTRATest.java @@ -21,26 +21,24 @@ import org.junit.Test; -public class ZeroOneCoinTest extends AbstractAssetTest { +public class DSTRATest extends AbstractAssetTest { - public ZeroOneCoinTest() { - super(new ZeroOneCoin()); + public DSTRATest() { + super(new DSTRA()); } @Test public void testValidAddresses() { - assertValidAddress("ZN17ww22Kg1cqM2VykoDwZW4fCTCvxGQMb"); - assertValidAddress("ZZJG1oqJ9VWHAy5AuE7bAugqoYvcGtPJcH"); - assertValidAddress("ZaUSzTWurWuaBw4zr8E4oEN25DzJK9vwbR"); - assertValidAddress("5AchYc7iQS7ynce7hNZ6Ya8djsbm5N9JBS"); + assertValidAddress("DGiwGS8n3tJZuKxUdWF6MyTYvv6xgDcyd7"); + assertValidAddress("DQcAKx5bFoeRwAEHE4EHQykyq8u2M1pwFa"); } @Test public void testInvalidAddresses() { - assertInvalidAddress("ZaUSzTWurWuaBw4zr8E4oEN25DzJK9vqqe"); - assertInvalidAddress("ZN17ww22Kg1cqM2VykoDwZW4fCTCvxGQM"); - assertInvalidAddress("ZaUSzTWurWuaBw4zr8E4oEN25DzJK9vwbb"); - assertInvalidAddress("Zb17ww22Kg1cqM2VykoDwZW4fCTCvxGQMb"); + assertInvalidAddress("DGiwGS8n3tJZuKxUdWF6MyTYvv6xgDcyd77"); + assertInvalidAddress("DGiwGS8n3tJZuKxUdWF6MyTYvv6xgDcyd"); + assertInvalidAddress("dGiwGS8n3tJZuKxUdWF6MyTYvv6xgDcyd7"); + assertInvalidAddress("FGiwGS8n3tJZuKxUdWF6MyTYvv6xgDcyd7"); + assertInvalidAddress("fGiwGS8n3tJZuKxUdWF6MyTYvv6xgDcyd7"); } - -} \ No newline at end of file +} diff --git a/assets/src/test/java/bisq/asset/coins/DoichainTest.java b/assets/src/test/java/bisq/asset/coins/DoichainTest.java new file mode 100644 index 00000000000..3f1501c8120 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/DoichainTest.java @@ -0,0 +1,41 @@ +/* + * 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.asset.coins; + +import org.junit.Test; +import bisq.asset.AbstractAssetTest; + +public class DoichainTest extends AbstractAssetTest { + + public DoichainTest() { + super(new Doichain()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("NGHV9LstnZfrkGx5QJmYhEepbzc66W7LN5"); + assertValidAddress("N4jeY9YhU49qHN5wUv7HBxeVZrFg32XFy7"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("NGHV9LstnZfrkGx5QJmYhEepbzc66W7LN5x"); + assertInvalidAddress("16iWWt1uoG8Dct56Cq6eKHFxvGSDha46Lo"); + assertInvalidAddress("38BFQkc9CdyJUxQK8PhebnDcA1tRRwLDW4"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/GalilelTest.java b/assets/src/test/java/bisq/asset/coins/GalilelTest.java new file mode 100644 index 00000000000..70726fcf7cb --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/GalilelTest.java @@ -0,0 +1,43 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class GalilelTest extends AbstractAssetTest { + + public GalilelTest() { + super(new Galilel()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("UVwXGh5B1NZbYdgWThqf2cLdkEupVXEVNi"); + assertValidAddress("UbNJbC1hZgBH5tQ4HyrrQMEPswKxwwfziw"); + assertValidAddress("UgqDDV8aekEXFP7BWLmTNpSQfk7uVk1jCF"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("1UgqDDV8aekEXFP7BWLmTNpSQfk7uVk1jCF"); + assertInvalidAddress("UgqDDV8aekEXFP7BWLmTNpSQfk7uVk1jCFd"); + assertInvalidAddress("UgqDDV8aekEXFP7BWLmTNpSQfk7uVk1jCF#"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/HatchTest.java b/assets/src/test/java/bisq/asset/coins/HatchTest.java new file mode 100644 index 00000000000..634bae0e02e --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/HatchTest.java @@ -0,0 +1,44 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class HatchTest extends AbstractAssetTest { + + public HatchTest() { + super(new Hatch()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("XgUfhrcfKWTVprA1GGhTggAA3VVQy1xqNp"); + assertValidAddress("Xo88XjP8RD2w3k7Fd16UT62y3oNcjbv4bz"); + assertValidAddress("XrA7ZGDLQkiLwUsfKT6y6tLrYjsvRLrZQG"); + + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("1XrA7ZGDLQkiLwUsfKT6y6tLrYjsvRLrZQG"); + assertInvalidAddress("XrA7ZGDLQkiLwUsfKT6y6tLrYjsvRLrZQGd"); + assertInvalidAddress("XrA7ZGDLQkiLwUsfKT6y6tLrYjsvRLrZQG#"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/HeliumTest.java b/assets/src/test/java/bisq/asset/coins/HeliumTest.java new file mode 100644 index 00000000000..0fa127d382d --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/HeliumTest.java @@ -0,0 +1,43 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class HeliumTest extends AbstractAssetTest { + + public HeliumTest() { + super(new Helium()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("SPSXRJSwzGKxSiYXePf1vnkk4v9WKVLhZp"); + assertValidAddress("SbzXDLmMfWDJZ1wEikUVAMbAzM2UnaSt4g"); + assertValidAddress("Sd14293Zhxxur2Pim7NkjxPGVaJTjGR5qY"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("1PSXRJSwzGKxSiYXePf1vnkk4v9WKVLhZp"); + assertInvalidAddress("SPSXRJSwzGKxSiYXePf1vnkk4v9WKVLhZpp"); + assertInvalidAddress("SPSSPSSPSGKxSiYXePf1vnkk4v9WKVLhZp"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/LitecoinPlusTest.java b/assets/src/test/java/bisq/asset/coins/LitecoinPlusTest.java new file mode 100644 index 00000000000..c3a8b169d4d --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/LitecoinPlusTest.java @@ -0,0 +1,42 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class LitecoinPlusTest extends AbstractAssetTest { + + public LitecoinPlusTest() { + super(new LitecoinPlus()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("XGnikpGiuDTaxq9vPfDF9m9VfTpv4SnNN5"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("1LgfapHEPhZbRF9pMd5WPT35hFXcZS1USrW"); + assertInvalidAddress("LgfapHEPhZbdRF9pMd5WPT35hFXcZS1USrW"); + assertInvalidAddress("LgfapHEPhZbRF9pMd5WPT35hFXcZS1USrW#"); + } +} + diff --git a/assets/src/test/java/bisq/asset/coins/MirQuiXTest.java b/assets/src/test/java/bisq/asset/coins/MirQuiXTest.java new file mode 100644 index 00000000000..cf86ad6ee56 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/MirQuiXTest.java @@ -0,0 +1,45 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class MirQuiXTest extends AbstractAssetTest { + + public MirQuiXTest() { + super(new MirQuiX()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("MCfFP5bFtN9riJiRRnH2QRkqCDqgNVC3FX"); + assertValidAddress("MEoLjNvFbNv63NtBW6eyYHUAGgLsJrpJbG"); + assertValidAddress("M84gmHb7mg4PMNBpVt3BeeAWVuKBmH6vtd"); + assertValidAddress("MNurUTgTSgg5ckmCcbjPrkgp7fekouLYgh"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("MCfFP5bFtN9riJiRRnH2QRkqCDqgNVC3FX2"); + assertInvalidAddress("MmEoLjNvFbNv63NtBW6eyYHUAGgLsJrpJbG"); + assertInvalidAddress("M84gmHb7mg4PMNBpVt3BeeAWVuKBmH63vtd"); + assertInvalidAddress("MNurUTgTSgg5ckmCcbjPrkgp7fekouLYfgh"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/NavcoinTest.java b/assets/src/test/java/bisq/asset/coins/NavcoinTest.java new file mode 100644 index 00000000000..55a1182d8c3 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/NavcoinTest.java @@ -0,0 +1,44 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class NavcoinTest extends AbstractAssetTest { + + public NavcoinTest() { + super(new Navcoin()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("NNR93HzmuhYKZ4Tnc9TGoD2DK6TVzXG9P7"); + assertValidAddress("NSm5NyCe5BFRuV3gFY5VcfhxWx7GTu9U9F"); + assertValidAddress("NaSdzJ64o8DQo5DMPexVrL4PYFCBZqcmsW"); + + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("NNR93HzmuhYKZ4Tnc9TGoD2DK6TVzXG9P"); + assertInvalidAddress("NNR93HzmuhYKZ4TnO9TGoD2DK6TVzXG9P8"); + assertInvalidAddress("NNR93HzmuhYKZ4Tnc9TGoD2DK6TVzXG9P71"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/PlenteumTest.java b/assets/src/test/java/bisq/asset/coins/PlenteumTest.java new file mode 100644 index 00000000000..ce36a0f819c --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/PlenteumTest.java @@ -0,0 +1,48 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class PlenteumTest extends AbstractAssetTest { + + public PlenteumTest() { + super(new Plenteum()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("PLeah9bvqxEDUWbRFqgNcYDeoL772WH9mcCQu9p29MC23NeCUkbVdUEfwDAtF8SgV81kf2hwCdpxqAJmC9k3nJsA7W4UThrufj"); + assertValidAddress("PLeavHTKHz9UcTCSCmd8eihuLxbsK9a7wSpfcYXPYY87JMpvYwwTH6Df32fRLc1r4rQMKoDLpTvywXx4FUVTggCR4jh9PEhvXb"); + assertValidAddress("PLeazd7iQEoFWJttR6353BMvs1cJfMqDmEUk2Z2XSoDdZigY5CbNLvrFUr7duvnEFdSKRdCQYTDkrcySYD1zaFtT9YMubRjHL2"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("PLev23ymatPTWgN1jncG33hMdJxZBLrBcCWQBGGGC14CFMUCq1nvxiV8d5cW92mmavzw542bpyjYXd8"); + assertInvalidAddress("PLeuxauCnCH7XZrSZSZw7XEEbkgrnZcaE1MK8wLtTYkF3g1J7nciYiaZDsTNYm2oDLTAM2JPq4rrlhVN5cXWpTPYh8P5wKbXNdoh"); + assertInvalidAddress(""); + assertInvalidAddress("PLev3xxpAFfXKwF5ond4sWDX3ATpZngT88KpPCCJKcuRjGktgp5HHTK2yV7NTo8687u5jwMigLmHaoFKho0OhVmF8WP9pVZhBL9kC#RoPOWRwpsx1F"); + assertInvalidAddress("PLeuwafXHTPzj1d2wc7c9X69r3qG1277ecnLnUaZ61M1YV5d3GYAs1Jbc2q4C4fWN$C4fWNLoDLDvADvpjNYdt3sdRB434UidKXimQQn"); + assertInvalidAddress("1jRo3rcp9fjdfjdSGpx"); + assertInvalidAddress("GDARp92UtmTWDjZatG8sduRockSteadyWasHere3atrHSXr9vJzjHq2TfPrjateDz9Wc8ZJKuDayqJ$%"); + assertInvalidAddress("F3xQ8Gv6xnvDhUrM57z71bfFvu9HeofXtXpZRLnrCN2s2cKvkQowrWjJTGz4676ymKvU4NzYT5Aadgsdhsdfhg4gfJwL2yhhkJ7"); + } +} \ No newline at end of file diff --git a/assets/src/test/java/bisq/asset/coins/QwertycoinTest.java b/assets/src/test/java/bisq/asset/coins/QwertycoinTest.java new file mode 100644 index 00000000000..ed39da44b71 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/QwertycoinTest.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.asset.coins; + +import bisq.asset.AbstractAssetTest; +import org.junit.Test; + +public class QwertycoinTest extends AbstractAssetTest { + + public QwertycoinTest() { + super(new Qwertycoin()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("QWC1NStUeRB9hZiYH8sG5RAWEt7YycyB44YZnpZQBpgq4CLwmLw4vAk9tU3h7Td21NL9aMbLHxseDGKdEv3gRexo2QCodNEZWa"); + assertValidAddress("QWC1anUNJRo2HePBmenLFkGu8rnug4odGLCjHaCqAwMxboiZZS3Gv9ACLfn2zvcsGVcCc51eqZXB8Dot9X5qAt3F53F8BxjDrG"); + assertValidAddress("QWC1hgpbsxsPrpxH9H3wL771p4KdgS7vA369PQTiznHiCC3NjZxKJSmBtJPVCJBBUKE346FcPTsQ18W6fgiDzj762BHNgo2sir"); + assertValidAddress("QWC1YAvWpYBVs8XT2eSt2JV5iAJSdm8CwbQhDruuBeTzRNKSdtdK8Mn3WjaXQrFvjMMWWTf24x89p31mWppJN2Br9uiA5zdYQu"); + assertValidAddress("QWC1YzR91Zmcj7fpf1HRZhSfz6cgXbxqAVTjQTtrUV6Bfv1ysEzb78qgVojE7FuQWSRnVqSb3LyxP9nH2q4vWyo82Fonutfkzr"); + assertValidAddress("QWC1KYAwX6sRXK94HabKLCFNMjfC12KFC74cRjTgFtsD79VUBydTtMd3G2z4xLg2e1LKaXsTt3zkYibH3pBrAMjd5z5ConjRXn"); + assertValidAddress("QWC1ZgSyFwS3tUbmCRPGDBi224ynMZXgXCHxvQ5pEmtuZSCrmid4z1de1DWRjhZKRZXe4E5LYhtP6e7FmpN8R2MM2SHGFvg12z"); + assertValidAddress("QWC1W7223e83cBdseddQp461j49bhr7y4VHh8FTPs7qWArhpqBzNvrYR5QXyFtc3eRaoASo3QVhuT6ogAa6AHhgt4bVMUNpZGh"); + assertValidAddress("QWC1NgBcSwvXghUkEqGttNPSSmPEgEdknXELNLyTG444Fx3cKkV2oJ9iCwzySbps7y9BqqkWAKbkvdkA8FTspfdm29ScDzASK1"); + assertValidAddress("QWC1FVgbYqkafwnpW8KU2gKXLTKoraMXuEJ2c1yG6PNdesh6BA3Wq8d1mgRYqfsbCn53g5VLHuxyLT8CXnGRLxN64wHssuSa9D"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress(""); + assertInvalidAddress("QW009s5NiYva6XS9bhhVc6jKYgXsH9wuHhZhWsqyAoPoWPUEiwEo9AZCDNbksvknyvZk73zwhHWSiXdgcDGLyhk5teEM7yxTgk"); + assertInvalidAddress("WC115a2NPZy7Xe2WZt3nKMZBsBpgNdevnKfy6PLk99eCjYw5fWQ5nu4uM6LerRBvVKTJpdGp2acZ25X3QDPHc7ocTxz1WfN2Za"); + assertInvalidAddress("34BQWC8imA1UH29uR6PHiGpcz9MYdnL3rku27gGeLosn5XuSedFC7jPBmzaB9DoxmDA5VUZa5hPv6PSe3tJH2MJhBktMEJXaZ8"); + assertInvalidAddress("KWC45Ghz2JRTTrLh8Z4bm6QhzZxVbq7LPiKbgjjhHPNDvVXZAJLop91zRu9A7wJMjyrU89uF7QpZB5kHhniuGZ88MJv7jRZXNi"); + assertInvalidAddress("ABc58FFmFEGcS52mTWmhAskQaKSSiX1BnHo8YcDjuhPdYBpWT9Q6ZCDz54k6cs3jPF2nk6desb1T6vRfHLfthiNf561qPct2SY"); + assertInvalidAddress("2K267rMF5ve4nt2wTHYJ1pZ6j3o2YP5KDBnE7GDxnr6bpem9WcqeHzw9yKWXvtxYdpDXCBbLiX9nm97r4aEtnXq8YNb9WPn15f"); + assertInvalidAddress("798Qr9sWTprQ2sH2y5PGpfV3RAnFxUsJYY2a2VQWCA9GjZ3MiyScD8VEh8ifWk4toYRCcbLZmRJw2dSsJBJAJ1Ava8WBzW7J12"); + assertInvalidAddress("A2o85CQSLDNNKR4HGHwhtsxhm8jheYEvk6ngf44AhqCRWDV2XsaTHr6ittDuyfCjinAP1SzBqnVJfqNhYGDJLzxq4Y7FBVofXV"); + assertInvalidAddress("QW9AeKW87bkao59oadmTXGf8Jv7sMYByPrKahRbnmZEmGzRgoxGRbWqmmXuPDW6jPJSUAdpZRZn6E5B9935LtWD5gHAPpZQA"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/VeilTest.java b/assets/src/test/java/bisq/asset/coins/VeilTest.java new file mode 100644 index 00000000000..68e9ca856a3 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/VeilTest.java @@ -0,0 +1,45 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; +import org.junit.Test; + +public class VeilTest extends AbstractAssetTest { + + public VeilTest() { + super(new Veil()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("VS2oF2pouKoLPJCjY8D7E1dStmUtitACu7"); + assertValidAddress("VV8VtpWTsYFBnhnvgQVnTvqoTx7XRRevte"); + assertValidAddress("VRZF4Am891FS224uuNirsrEugqMyg3VxjJ"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemqq"); + assertInvalidAddress("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"); + assertInvalidAddress("DRbnCYbuMXdKU4y8dya9EnocL47gFjErWeg"); + assertInvalidAddress("DTPAqTryNRCE2FgsxzohTtJXfCBODnG6Rc"); + assertInvalidAddress("DTPAqTryNRCE2FgsxzohTtJXfCB0DnG6Rc"); + assertInvalidAddress("DTPAqTryNRCE2FgsxzohTtJXfCBIDnG6Rc"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/XDRTest.java b/assets/src/test/java/bisq/asset/coins/XDRTest.java new file mode 100644 index 00000000000..dcf61c808fc --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/XDRTest.java @@ -0,0 +1,63 @@ +/* + * 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.asset.coins; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class XDRTest extends AbstractAssetTest { + + public XDRTest() { + super(new XDR()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("2WeY8JpRJgrvWQxbSPuyhsBMjtZMMN7cADEomPHh2bCkdZ7xQW"); + assertValidAddress("NTvSfK1Gr5Jg97UvJo2wvi7BTZo8KqJzgSL2FCGucF6nUH7yq"); + assertValidAddress("ztNdPsuyfDWt1ufCbDqaCDQH3FXvucXNZqVrdzsWvzDHPrkSh"); + assertValidAddress("jkvx3z98rJmuVKqMSktDpKTSBrsqJEtTBW1CBSWJEtchDGkDX"); + assertValidAddress("is2YXBxk91d4Lw4Pet7RoP8KAxCKFHUC6iQyaNgmac5ies6ko"); + assertValidAddress("2NNEr5YLniGxWajoeXiiAZPR68hJXncnhEmC4GWAaV5kwaLRcP"); + assertValidAddress("wGmjgRu8hgjgRsRV8k6h2puis1K9UQCTKWZEPa4yS8mrmJUpU"); + assertValidAddress("i8rc9oMunRtVbSxA4VBESxbYzHnfhP39aM5M1srtxVZ8oBiKD"); + assertValidAddress("vP4w8khXHFQ7cJ2BJNyPbJiV5kFfBHPVivHxKf5nyd8cEgB9U"); + assertValidAddress("QQQZZa46QJ3499RL8CatuqaUx4haKQGUuZ4ZE5SeL13Awkf6m"); + assertValidAddress("qqqfpHD3VbbyZXTHgCW2VX8jvoERcxanzQkCqVyHB8fRBszMn"); + assertValidAddress("BiSQkPqCCET4UovJASnnU1Hk5bnqBxBVi5bjA5wLZpN9HCA6A"); + assertValidAddress("bisqFm6Zbf6ULcpJqQ2ibn2adkL2E9iivQFTAP15Q18daQxnS"); + assertValidAddress("miLEgbhGv4ARoPG2kAhTCy8UGqBcFbsY6rr5tXq63nH8RyqcE"); + + + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("1WeY8JpRJgrvWQxbSPuyhsBMjtZMMN7cADEomPHh2bCkdZ7xQW"); + assertInvalidAddress("2WeY8JpRJgrvWQxbSPuyhsBMjtZMMN3cADEomPHh2bCkdZ7xQW"); + assertInvalidAddress("2WeY8JpRJgrvWQxbSPuyhsBMjtZMMN7cADEomPHh2bCkdZ7xQ1"); + assertInvalidAddress("2WeY8JpRJgrvWQxbSPuyhsBMjtZMMN7cADEomPHh2bCkdZ7xQ"); + assertInvalidAddress("WeY8JpRJgrvWQxbSPuyhsBMjtZMMN7cADEomPHh2bCkdZ7xQW"); + assertInvalidAddress("2WeY8JpRJgrvWQx"); + assertInvalidAddress("2WeY8JpRJgrvWQxbSPuyhsBMjtZMMN7cADEomPHh2bCkdZ7xQW1"); + assertInvalidAddress("milEgbhGv4ARoPG2kAhTCy8UGqBcFbsY6rr5tXq63nH8RyqcE"); + assertInvalidAddress("miLegbhGv4ARoPG2kAhTCy8UGqBcFbsY6rr5tXq63nH8RyqcE"); + assertInvalidAddress("1111111"); + } +} diff --git a/build.gradle b/build.gradle index acf90a6e45a..a1a78b8966c 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ configure(subprojects) { jmockitVersion = '1.42' joptVersion = '5.0.3' langVersion = '3.4' - bitcoinjVersion = 'cd30ad5b' + bitcoinjVersion = '00298b1' logbackVersion = '1.1.10' lombokVersion = '1.18.2' mockitoVersion = '2.21.0' @@ -132,8 +132,13 @@ configure([project(':desktop'), configure(project(':assets')) { dependencies { compile("com.github.bisq-network.bitcoinj:bitcoinj-core:$bitcoinjVersion") { + exclude(module: 'jsr305') + exclude(module: 'slf4j-api') + exclude(module: 'guava') exclude(module: 'protobuf-java') } + compile 'com.google.guava:guava:20.0' + compile "org.slf4j:slf4j-api:$slf4jVersion" compile "commons-codec:commons-codec:$codecVersion" compile "org.apache.commons:commons-lang3:$langVersion" compile "org.bouncycastle:bcpg-jdk15on:$bcVersion" @@ -189,10 +194,10 @@ configure(project(':common')) { configure(project(':p2p')) { dependencies { compile project(':common') - compile('com.github.JesusMcCloud.netlayer:tor.native:0.6.3') { + compile('com.github.JesusMcCloud.netlayer:tor.native:0.6.5') { exclude(module: 'slf4j-api') } - compile('com.github.JesusMcCloud.netlayer:tor.external:0.6.3') { + compile('com.github.JesusMcCloud.netlayer:tor.external:0.6.5') { exclude(module: 'slf4j-api') } compile('org.apache.httpcomponents:httpclient:4.5.3') { @@ -266,7 +271,7 @@ configure(project(':desktop')) { apply plugin: 'witness' apply from: '../gradle/witness/gradle-witness.gradle' - version = '0.9.3-SNAPSHOT' + version = '0.9.5-SNAPSHOT' mainClassName = 'bisq.desktop.app.BisqAppMain' @@ -329,7 +334,6 @@ configure(project(':monitor')) { } dependencies { - compile project(':p2p') compile project(':core') compile "org.slf4j:slf4j-api:$slf4jVersion" compile "ch.qos.logback:logback-core:$logbackVersion" @@ -339,10 +343,6 @@ configure(project(':monitor')) { compileOnly "org.projectlombok:lombok:$lombokVersion" annotationProcessor "org.projectlombok:lombok:$lombokVersion" - compile('com.github.JesusMcCloud.netlayer:tor.native:0.6.2') { - exclude(module: 'slf4j-api') - } - testCompile 'org.junit.jupiter:junit-jupiter-api:5.3.2' testCompile 'org.junit.jupiter:junit-jupiter-params:5.3.2' testCompileOnly "org.projectlombok:lombok:$lombokVersion" diff --git a/common/src/main/java/bisq/common/Envelope.java b/common/src/main/java/bisq/common/Envelope.java index 40fc91b8966..c713e8a26c6 100644 --- a/common/src/main/java/bisq/common/Envelope.java +++ b/common/src/main/java/bisq/common/Envelope.java @@ -18,7 +18,7 @@ package bisq.common; /** - * Interface for the outside envelope object sent over the network or persisted to disc. + * Interface for the outside envelope object sent over the network or persisted to disk. */ public interface Envelope extends Proto { } diff --git a/common/src/main/java/bisq/common/UserThread.java b/common/src/main/java/bisq/common/UserThread.java index c46b576d22d..d7b994eeda5 100644 --- a/common/src/main/java/bisq/common/UserThread.java +++ b/common/src/main/java/bisq/common/UserThread.java @@ -37,7 +37,7 @@ * For JavaFX it is usually the Platform::RunLater executor, for a headless application it is any single threaded * executor. * Additionally sets a timer class so JavaFX and headless applications can set different timers (UITimer for JavaFX - * otherwise we use teh default FrameRateTimer). + * otherwise we use the default FrameRateTimer). *

* Provides also methods for delayed and periodic executions. */ diff --git a/common/src/main/java/bisq/common/app/Capabilities.java b/common/src/main/java/bisq/common/app/Capabilities.java index 015a98c74f8..64d7cddac68 100644 --- a/common/src/main/java/bisq/common/app/Capabilities.java +++ b/common/src/main/java/bisq/common/app/Capabilities.java @@ -17,55 +17,102 @@ package bisq.common.app; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; -import lombok.Getter; -import lombok.Setter; +import lombok.EqualsAndHashCode; +/** + * hold a set of capabilities and offers appropriate comparison methods. + * + * @author Florian Reimair + */ +@EqualsAndHashCode public class Capabilities { - // We can define here special features the client is supporting. - // Useful for updates to new versions where a new data type would break backwards compatibility or to - // limit a node to certain behaviour and roles like the seed nodes. - // We don't use the Enum in any serialized data, as changes in the enum would break backwards compatibility. We use the ordinal integer instead. - // Sequence in the enum must not be changed (append only). - public enum Capability { - TRADE_STATISTICS, - TRADE_STATISTICS_2, - ACCOUNT_AGE_WITNESS, - SEED_NODE, - DAO_FULL_NODE, - PROPOSAL, - BLIND_VOTE, - ACK_MSG, - BSQ_BLOCK + + /** + * The global set of capabilities, i.e. the capabilities if the local app. + */ + public static final Capabilities app = new Capabilities(); + + protected final Set capabilities = new HashSet<>(); + + public Capabilities(Capability... capabilities) { + this(Arrays.asList(capabilities)); + } + + public Capabilities(Capabilities capabilities) { + this(capabilities.capabilities); } - // Application need to set supported capabilities at startup - @Getter - @Setter - private static List supportedCapabilities = new ArrayList<>(); + public Capabilities(Collection capabilities) { + this.capabilities.addAll(capabilities); + } + + public void set(Capability... capabilities) { + set(Arrays.asList(capabilities)); + } + + public void set(Capabilities capabilities) { + set(capabilities.capabilities); + } + + public void set(Collection capabilities) { + this.capabilities.clear(); + this.capabilities.addAll(capabilities); + } + + public void addAll(Capability... capabilities) { + this.capabilities.addAll(Arrays.asList(capabilities)); + } + + public void addAll(Capabilities capabilities) { + if(capabilities != null) + this.capabilities.addAll(capabilities.capabilities); + } + + public boolean containsAll(final Set requiredItems) { + return capabilities.containsAll(requiredItems); + } + + public boolean containsAll(final Capabilities capabilities) { + return containsAll(capabilities.capabilities); + } + + public boolean isEmpty() { + return capabilities.isEmpty(); + } + + + /** + * helper for protobuffer stuff + * + * @param capabilities + * @return int list of Capability ordinals + */ + public static List toIntList(Capabilities capabilities) { + return capabilities.capabilities.stream().map(capability -> capability.ordinal()).sorted().collect(Collectors.toList()); + } - public static void addCapability(int capability) { - supportedCapabilities.add(capability); + /** + * helper for protobuffer stuff + * + * @param capabilities a list of Capability ordinals + * @return a {@link Capabilities} object + */ + public static Capabilities fromIntList(List capabilities) { + return new Capabilities(capabilities.stream() + .filter(integer -> integer < Capability.values().length) + .map(integer -> Capability.values()[integer]) + .collect(Collectors.toSet())); } - public static boolean isCapabilitySupported(final List requiredItems, final List supportedItems) { - if (requiredItems != null && !requiredItems.isEmpty()) { - if (supportedItems != null && !supportedItems.isEmpty()) { - List matches = new ArrayList<>(); - for (int requiredItem : requiredItems) { - matches.addAll(supportedItems.stream() - .filter(supportedItem -> requiredItem == supportedItem) - .collect(Collectors.toList())); - } - return matches.size() == requiredItems.size(); - } else { - return false; - } - } else { - return true; - } + @Override + public String toString() { + return Arrays.toString(Capabilities.toIntList(this).toArray()); } } diff --git a/common/src/main/java/bisq/common/app/Capability.java b/common/src/main/java/bisq/common/app/Capability.java new file mode 100644 index 00000000000..8c5b2c62316 --- /dev/null +++ b/common/src/main/java/bisq/common/app/Capability.java @@ -0,0 +1,35 @@ +/* + * 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.common.app; + +// We can define here special features the client is supporting. +// Useful for updates to new versions where a new data type would break backwards compatibility or to +// limit a node to certain behaviour and roles like the seed nodes. +// We don't use the Enum in any serialized data, as changes in the enum would break backwards compatibility. We use the ordinal integer instead. +// Sequence in the enum must not be changed (append only). +public enum Capability { + TRADE_STATISTICS, + TRADE_STATISTICS_2, + ACCOUNT_AGE_WITNESS, + SEED_NODE, + DAO_FULL_NODE, + PROPOSAL, + BLIND_VOTE, + ACK_MSG, + BSQ_BLOCK +} diff --git a/common/src/main/java/bisq/common/app/HasCapabilities.java b/common/src/main/java/bisq/common/app/HasCapabilities.java new file mode 100644 index 00000000000..9652f8825cc --- /dev/null +++ b/common/src/main/java/bisq/common/app/HasCapabilities.java @@ -0,0 +1,28 @@ +/* + * 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.common.app; + +/** + * Holds a set of {@link Capabilities}. + * + * @author Florian Reimair + */ +public interface HasCapabilities { + + Capabilities getCapabilities(); +} diff --git a/common/src/main/java/bisq/common/app/Log.java b/common/src/main/java/bisq/common/app/Log.java index 45eae96b494..bb2268a70d6 100644 --- a/common/src/main/java/bisq/common/app/Log.java +++ b/common/src/main/java/bisq/common/app/Log.java @@ -91,26 +91,4 @@ public static void setup(String fileName) { public static void setCustomLogLevel(String pattern, Level logLevel) { ((Logger) LoggerFactory.getLogger(pattern)).setLevel(logLevel); } - - public static void traceCall() { - if (LoggerFactory.getLogger(Log.class).isTraceEnabled()) { - StackTraceElement stackTraceElement = new Throwable().getStackTrace()[1]; - String methodName = stackTraceElement.getMethodName(); - if (methodName.equals("")) - methodName = "Constructor "; - String className = stackTraceElement.getClassName(); - LoggerFactory.getLogger(className).trace("Called: {}", methodName); - } - } - - public static void traceCall(String message) { - if (LoggerFactory.getLogger(Log.class).isTraceEnabled()) { - StackTraceElement stackTraceElement = new Throwable().getStackTrace()[1]; - String methodName = stackTraceElement.getMethodName(); - if (methodName.equals("")) - methodName = "Constructor "; - String className = stackTraceElement.getClassName(); - LoggerFactory.getLogger(className).trace("Called: {} [{}]", methodName, message); - } - } } diff --git a/common/src/main/java/bisq/common/app/Version.java b/common/src/main/java/bisq/common/app/Version.java index 02a3bbab3cc..2d88eab21d5 100644 --- a/common/src/main/java/bisq/common/app/Version.java +++ b/common/src/main/java/bisq/common/app/Version.java @@ -27,7 +27,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 = "0.9.3"; + public static final String VERSION = "0.9.5"; public static int getMajorVersion(String version) { return getSubVersion(version, 0); diff --git a/common/src/main/java/bisq/common/proto/persistable/PersistableEnvelope.java b/common/src/main/java/bisq/common/proto/persistable/PersistableEnvelope.java index 32357d3fea2..dfab4f0b4dd 100644 --- a/common/src/main/java/bisq/common/proto/persistable/PersistableEnvelope.java +++ b/common/src/main/java/bisq/common/proto/persistable/PersistableEnvelope.java @@ -20,7 +20,7 @@ import bisq.common.Envelope; /** - * Interface for the outside envelope object persisted to disc. + * Interface for the outside envelope object persisted to disk. */ public interface PersistableEnvelope extends Envelope { } diff --git a/common/src/main/java/bisq/common/storage/FileManager.java b/common/src/main/java/bisq/common/storage/FileManager.java index 7291fb9cae5..fb28385f9b7 100644 --- a/common/src/main/java/bisq/common/storage/FileManager.java +++ b/common/src/main/java/bisq/common/storage/FileManager.java @@ -133,7 +133,6 @@ public synchronized T read(File file) { } public synchronized void removeFile(String fileName) { - log.debug("removeFile" + fileName); File file = new File(dir, fileName); boolean result = file.delete(); if (!result) diff --git a/common/src/main/java/bisq/common/storage/Storage.java b/common/src/main/java/bisq/common/storage/Storage.java index 199f26615ba..21e5a89fb76 100644 --- a/common/src/main/java/bisq/common/storage/Storage.java +++ b/common/src/main/java/bisq/common/storage/Storage.java @@ -114,7 +114,6 @@ public void setNumMaxBackupFiles(int numMaxBackupFiles) { // Save delayed and on a background thread public void queueUpForSave(T persistable) { if (persistable != null) { - log.trace("save " + fileName); checkNotNull(storageFile, "storageFile = null. Call setupFileStorage before using read/write."); fileManager.saveLater(persistable); @@ -125,7 +124,6 @@ public void queueUpForSave(T persistable) { public void queueUpForSave(T persistable, long delayInMilli) { if (persistable != null) { - log.trace("save " + fileName); checkNotNull(storageFile, "storageFile = null. Call setupFileStorage before using read/write."); fileManager.saveLater(persistable, delayInMilli); diff --git a/common/src/main/java/bisq/common/taskrunner/TaskRunner.java b/common/src/main/java/bisq/common/taskrunner/TaskRunner.java index ce0eaab15ac..f6f6d12d2bb 100644 --- a/common/src/main/java/bisq/common/taskrunner/TaskRunner.java +++ b/common/src/main/java/bisq/common/taskrunner/TaskRunner.java @@ -82,7 +82,6 @@ public void cancel() { } void handleComplete() { - log.trace("Task completed: " + currentTask.getSimpleName()); sharedModel.persist(); next(); } diff --git a/common/src/main/java/bisq/common/util/Utilities.java b/common/src/main/java/bisq/common/util/Utilities.java index 4c8f5e81903..11c72ac8a84 100644 --- a/common/src/main/java/bisq/common/util/Utilities.java +++ b/common/src/main/java/bisq/common/util/Utilities.java @@ -155,6 +155,21 @@ public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor(String return executor; } + /** + * @return true if defaults read -g AppleInterfaceStyle has an exit status of 0 (i.e. _not_ returning "key not found"). + */ + public static boolean isMacMenuBarDarkMode() { + try { + // check for exit status only. Once there are more modes than "dark" and "default", we might need to analyze string contents.. + Process process = Runtime.getRuntime().exec(new String[]{"defaults", "read", "-g", "AppleInterfaceStyle"}); + process.waitFor(100, TimeUnit.MILLISECONDS); + return process.exitValue() == 0; + } catch (IOException | InterruptedException | IllegalThreadStateException ex) { + // IllegalThreadStateException thrown by proc.exitValue(), if process didn't terminate + return false; + } + } + public static boolean isUnix() { return isOSX() || isLinux() || getOSName().contains("freebsd"); } @@ -171,6 +186,14 @@ public static boolean isLinux() { return getOSName().contains("linux"); } + public static boolean isDebianLinux() { + return isLinux() && new File("/etc/debian_version").isFile(); + } + + public static boolean isRedHatLinux() { + return isLinux() && new File("/etc/redhat-release").isFile(); + } + private static String getOSName() { return System.getProperty("os.name").toLowerCase(Locale.US); } @@ -489,15 +512,29 @@ public static void checkCryptoPolicySetup() throws NoSuchAlgorithmException, Lim throw new LimitedKeyStrengthException(); } + public static String toTruncatedString(Object message) { + return toTruncatedString(message, 200, true); + } + public static String toTruncatedString(Object message, int maxLength) { - if (message != null) { - return StringUtils.abbreviate(message.toString(), maxLength).replace("\n", ""); - } - return "null"; + return toTruncatedString(message, maxLength, true); } - public static String toTruncatedString(Object message) { - return toTruncatedString(message, 200); + public static String toTruncatedString(Object message, boolean removeLinebreaks) { + return toTruncatedString(message, 200, removeLinebreaks); + } + + public static String toTruncatedString(Object message, int maxLength, boolean removeLinebreaks) { + if (message == null) + return "null"; + + + String result = StringUtils.abbreviate(message.toString(), maxLength); + if (removeLinebreaks) + return result.replace("\n", ""); + + return result; + } public static String getRandomPrefix(int minLength, int maxLength) { diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index 8db992f910a..f3f876eb3b3 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -725,6 +725,7 @@ message PaymentAccountPayload { HalCashAccountPayload hal_cash_account_payload = 24; PromptPayAccountPayload prompt_pay_account_payload = 25; AdvancedCashAccountPayload advanced_cash_account_payload = 26; + InstantCryptoCurrencyAccountPayload instant_crypto_currency_account_payload = 27; } map exclude_from_json_data = 15; } @@ -836,6 +837,10 @@ message CryptoCurrencyAccountPayload { string address = 1; } +message InstantCryptoCurrencyAccountPayload { + string address = 1; +} + message FasterPaymentsAccountPayload { string sort_code = 1; string account_nr = 2; @@ -1272,7 +1277,7 @@ message PreferencesPayload { string bitcoin_nodes = 27; repeated string ignore_traders_list = 28; string directory_chooser_path = 29; - int64 buyer_security_deposit_as_long = 30; + int64 buyer_security_deposit_as_long = 30; // Deprectated: Superseded by buyerSecurityDepositAsPercent bool use_animations = 31; PaymentAccount selectedPayment_account_for_createOffer = 32; bool pay_fee_in_Btc = 33; @@ -1292,6 +1297,7 @@ message PreferencesPayload { string rpc_user = 47; string rpc_pw = 48; string take_offer_selected_payment_account_id = 49; + double buyer_security_deposit_as_percent = 50; } /////////////////////////////////////////////////////////////////////////////////////////// @@ -1514,6 +1520,8 @@ message Proposal { GenericProposal generic_proposal = 11; RemoveAssetProposal remove_asset_proposal = 12; } + // We leave some index space here in case we add more subclasses + map extra_data = 20; } message CompensationProposal { @@ -1533,6 +1541,8 @@ message ChangeParamProposal { message RoleProposal { Role role = 1; + int64 required_bond = 2; + int32 unlock_time = 3; } message ConfiscateBondProposal { @@ -1640,6 +1650,7 @@ message BlindVote { string tx_id = 2; int64 stake = 3; bytes encrypted_merit_list = 4; + map extra_data = 5; } message MyBlindVoteList { @@ -1681,8 +1692,6 @@ message ProposalVoteResult { message EvaluatedProposal { bool is_accepted = 1; ProposalVoteResult proposal_vote_result = 2; - int64 required_quorum = 3; - int64 required_threshold = 4; } message DecryptedBallotsWithMerits { diff --git a/common/src/test/java/bisq/common/app/CapabilitiesTest.java b/common/src/test/java/bisq/common/app/CapabilitiesTest.java index 23e7ef83387..d0166e130c6 100644 --- a/common/src/test/java/bisq/common/app/CapabilitiesTest.java +++ b/common/src/test/java/bisq/common/app/CapabilitiesTest.java @@ -17,39 +17,47 @@ package bisq.common.app; -import java.util.Arrays; +import java.util.HashSet; import org.junit.Test; +import static bisq.common.app.Capability.*; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class CapabilitiesTest { @Test - public void testVersionNumber() { - // if required are null or empty its true - assertTrue(Capabilities.isCapabilitySupported(null, null)); - assertTrue(Capabilities.isCapabilitySupported(null, Arrays.asList())); - assertTrue(Capabilities.isCapabilitySupported(null, Arrays.asList(0))); - assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(), null)); - assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(), Arrays.asList())); - assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(), Arrays.asList(0))); - - // required are not null and not empty but supported is null or empty its false - assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0), null)); - assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList())); + public void testNoCapabilitiesAvailable() { + Capabilities DUT = new Capabilities(); + + assertTrue(DUT.containsAll(new HashSet<>())); + assertFalse(DUT.containsAll(new Capabilities(SEED_NODE))); + } + + @Test + public void testO() { + Capabilities DUT = new Capabilities(TRADE_STATISTICS); + + assertTrue(DUT.containsAll(new HashSet<>())); + } + + @Test + public void testSingleMatch() { + Capabilities DUT = new Capabilities(TRADE_STATISTICS); // single match - assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList(0))); - assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(1), Arrays.asList(0))); - assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList(1))); - - // multi match - assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList(0, 1))); - assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0), Arrays.asList(1, 2))); - assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(0, 1), Arrays.asList(0, 1))); - assertTrue(Capabilities.isCapabilitySupported(Arrays.asList(0, 1), Arrays.asList(1, 0))); - assertFalse(Capabilities.isCapabilitySupported(Arrays.asList(0, 1), Arrays.asList(0))); + assertTrue(DUT.containsAll(new Capabilities(TRADE_STATISTICS))); + assertFalse(DUT.containsAll(new Capabilities(SEED_NODE))); + } + + @Test + public void testMultiMatch() { + Capabilities DUT = new Capabilities(TRADE_STATISTICS, TRADE_STATISTICS_2); + + assertTrue(DUT.containsAll(new Capabilities(TRADE_STATISTICS))); + assertFalse(DUT.containsAll(new Capabilities(SEED_NODE))); + assertTrue(DUT.containsAll(new Capabilities(TRADE_STATISTICS, TRADE_STATISTICS_2))); + assertFalse(DUT.containsAll(new Capabilities(SEED_NODE, TRADE_STATISTICS_2))); } } diff --git a/core/src/main/java/bisq/core/CoreModule.java b/core/src/main/java/bisq/core/CoreModule.java index 65e88eb18dd..e22d44406ab 100644 --- a/core/src/main/java/bisq/core/CoreModule.java +++ b/core/src/main/java/bisq/core/CoreModule.java @@ -30,7 +30,6 @@ import bisq.core.dao.DaoModule; import bisq.core.filter.FilterModule; import bisq.core.network.p2p.seed.DefaultSeedNodeRepository; -import bisq.core.network.p2p.seed.SeedNodeAddressLookup; import bisq.core.notifications.MobileMessageEncryption; import bisq.core.notifications.MobileModel; import bisq.core.notifications.MobileNotificationService; @@ -99,7 +98,6 @@ protected void configure() { bind(CorruptedDatabaseFilesHandler.class).in(Singleton.class); bind(AvoidStandbyModeService.class).in(Singleton.class); - bind(SeedNodeAddressLookup.class).in(Singleton.class); bind(SeedNodeRepository.class).to(DefaultSeedNodeRepository.class).in(Singleton.class); File storageDir = new File(environment.getRequiredProperty(Storage.STORAGE_DIR)); diff --git a/core/src/main/java/bisq/core/alert/AlertManager.java b/core/src/main/java/bisq/core/alert/AlertManager.java index 1473fc4ae2d..3b8bbeb6847 100644 --- a/core/src/main/java/bisq/core/alert/AlertManager.java +++ b/core/src/main/java/bisq/core/alert/AlertManager.java @@ -121,7 +121,7 @@ public boolean addAlertMessageIfKeyIsValid(Alert alert, String privKeyString) { user.setDevelopersAlert(alert); boolean result = p2PService.addProtectedStorageEntry(alert, true); if (result) { - log.trace("Add alertMessage to network was successful. AlertMessage = " + alert); + log.trace("Add alertMessage to network was successful. AlertMessage={}", alert); } } @@ -132,7 +132,7 @@ public boolean removeAlertMessageIfKeyIsValid(String privKeyString) { Alert alert = user.getDevelopersAlert(); if (isKeyValid(privKeyString) && alert != null) { if (p2PService.removeData(alert, true)) - log.trace("Remove alertMessage from network was successful. AlertMessage = " + alert); + log.trace("Remove alertMessage from network was successful. AlertMessage={}", alert); user.setDevelopersAlert(null); return true; diff --git a/core/src/main/java/bisq/core/app/BisqEnvironment.java b/core/src/main/java/bisq/core/app/BisqEnvironment.java index aa0aef44d13..e98faf4e658 100644 --- a/core/src/main/java/bisq/core/app/BisqEnvironment.java +++ b/core/src/main/java/bisq/core/app/BisqEnvironment.java @@ -25,6 +25,7 @@ import bisq.core.filter.FilterManager; import bisq.network.NetworkOptionKeys; +import bisq.network.p2p.network.ConnectionConfig; import bisq.common.CommonOptionKeys; import bisq.common.app.Version; @@ -57,6 +58,7 @@ import java.io.FileOutputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; @@ -166,8 +168,7 @@ private static String appDataDir(String userDataDir, String appName) { public static boolean isDaoActivated(Environment environment) { Boolean daoActivatedFromOptions = environment.getProperty(DaoOptionKeys.DAO_ACTIVATED, Boolean.class, false); BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); - boolean isRegTestOrTestNet = (baseCurrencyNetwork.isTestnet() || baseCurrencyNetwork.isRegtest()); - return daoActivatedFromOptions || isRegTestOrTestNet; + return daoActivatedFromOptions || !baseCurrencyNetwork.isMainnet(); } @@ -192,9 +193,10 @@ public static boolean isDaoActivated(Environment environment) { protected final String btcNodes, seedNodes, ignoreDevMsg, useDevPrivilegeKeys, useDevMode, useTorForBtc, rpcUser, rpcPassword, rpcPort, rpcBlockNotificationPort, dumpBlockchainData, fullDaoNode, - myAddress, banList, dumpStatistics, maxMemory, socks5ProxyBtcAddress, + banList, dumpStatistics, maxMemory, socks5ProxyBtcAddress, torRcFile, torRcOptions, externalTorControlPort, externalTorPassword, externalTorCookieFile, - socks5ProxyHttpAddress, useAllProvidedNodes, numConnectionForBtc, genesisTxId, genesisBlockHeight, referralId, daoActivated; + socks5ProxyHttpAddress, useAllProvidedNodes, numConnectionForBtc, genesisTxId, genesisBlockHeight, genesisTotalSupply, + referralId, daoActivated, msgThrottlePerSec, msgThrottlePer10Sec, sendMsgThrottleTrigger, sendMsgThrottleSleep; protected final boolean externalTorUseSafeCookieAuthentication, torStreamIsolation; @@ -257,9 +259,6 @@ public BisqEnvironment(PropertySource commandLineProperties) { (String) commandLineProperties.getProperty(NetworkOptionKeys.SEED_NODES_KEY) : ""; - myAddress = commandLineProperties.containsProperty(NetworkOptionKeys.MY_ADDRESS) ? - (String) commandLineProperties.getProperty(NetworkOptionKeys.MY_ADDRESS) : - ""; banList = commandLineProperties.containsProperty(NetworkOptionKeys.BAN_LIST) ? (String) commandLineProperties.getProperty(NetworkOptionKeys.BAN_LIST) : ""; @@ -284,12 +283,20 @@ public BisqEnvironment(PropertySource commandLineProperties) { externalTorCookieFile = commandLineProperties.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE) ? (String) commandLineProperties.getProperty(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE) : ""; - externalTorUseSafeCookieAuthentication = commandLineProperties.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE) ? - true : - false; - torStreamIsolation = commandLineProperties.containsProperty(NetworkOptionKeys.TOR_STREAM_ISOLATION) ? - true : - false; + externalTorUseSafeCookieAuthentication = commandLineProperties.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE); + torStreamIsolation = commandLineProperties.containsProperty(NetworkOptionKeys.TOR_STREAM_ISOLATION); + msgThrottlePerSec = commandLineProperties.containsProperty(NetworkOptionKeys.MSG_THROTTLE_PER_SEC) ? + (String) commandLineProperties.getProperty(NetworkOptionKeys.MSG_THROTTLE_PER_SEC) : + String.valueOf(ConnectionConfig.MSG_THROTTLE_PER_SEC); + msgThrottlePer10Sec = commandLineProperties.containsProperty(NetworkOptionKeys.MSG_THROTTLE_PER_10_SEC) ? + (String) commandLineProperties.getProperty(NetworkOptionKeys.MSG_THROTTLE_PER_10_SEC) : + String.valueOf(ConnectionConfig.MSG_THROTTLE_PER_10_SEC); + sendMsgThrottleTrigger = commandLineProperties.containsProperty(NetworkOptionKeys.SEND_MSG_THROTTLE_TRIGGER) ? + (String) commandLineProperties.getProperty(NetworkOptionKeys.SEND_MSG_THROTTLE_TRIGGER) : + String.valueOf(ConnectionConfig.SEND_MSG_THROTTLE_TRIGGER); + sendMsgThrottleSleep = commandLineProperties.containsProperty(NetworkOptionKeys.SEND_MSG_THROTTLE_SLEEP) ? + (String) commandLineProperties.getProperty(NetworkOptionKeys.SEND_MSG_THROTTLE_SLEEP) : + String.valueOf(ConnectionConfig.SEND_MSG_THROTTLE_SLEEP); //RpcOptionKeys rpcUser = commandLineProperties.containsProperty(DaoOptionKeys.RPC_USER) ? @@ -316,6 +323,9 @@ public BisqEnvironment(PropertySource commandLineProperties) { genesisBlockHeight = commandLineProperties.containsProperty(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) ? (String) commandLineProperties.getProperty(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) : "-1"; + genesisTotalSupply = commandLineProperties.containsProperty(DaoOptionKeys.GENESIS_TOTAL_SUPPLY) ? + (String) commandLineProperties.getProperty(DaoOptionKeys.GENESIS_TOTAL_SUPPLY) : + "-1"; daoActivated = commandLineProperties.containsProperty(DaoOptionKeys.DAO_ACTIVATED) ? (String) commandLineProperties.getProperty(DaoOptionKeys.DAO_ACTIVATED) : ""; @@ -346,7 +356,7 @@ public BisqEnvironment(PropertySource commandLineProperties) { bannedPriceRelayNodes = !bannedPriceRelayNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedPriceRelayNodesAsString).split(",")) : null; final String bannedSeedNodesAsString = getProperty(FilterManager.BANNED_SEED_NODES, ""); - bannedSeedNodes = !bannedSeedNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedSeedNodesAsString).split(",")) : null; + bannedSeedNodes = !bannedSeedNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedSeedNodesAsString).split(",")) : new ArrayList<>(); final String bannedBtcNodesAsString = getProperty(FilterManager.BANNED_BTC_NODES, ""); bannedBtcNodes = !bannedBtcNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedBtcNodesAsString).split(",")) : null; @@ -452,7 +462,6 @@ private PropertySource defaultProperties() { setProperty(CommonOptionKeys.USE_DEV_MODE, useDevMode); setProperty(NetworkOptionKeys.SEED_NODES_KEY, seedNodes); - setProperty(NetworkOptionKeys.MY_ADDRESS, myAddress); setProperty(NetworkOptionKeys.BAN_LIST, banList); setProperty(NetworkOptionKeys.TOR_DIR, Paths.get(btcNetworkDir, "tor").toString()); setProperty(NetworkOptionKeys.NETWORK_ID, String.valueOf(baseCurrencyNetwork.ordinal())); @@ -468,6 +477,11 @@ private PropertySource defaultProperties() { if (torStreamIsolation) setProperty(NetworkOptionKeys.TOR_STREAM_ISOLATION, "true"); + setProperty(NetworkOptionKeys.MSG_THROTTLE_PER_SEC, msgThrottlePerSec); + setProperty(NetworkOptionKeys.MSG_THROTTLE_PER_10_SEC, msgThrottlePer10Sec); + setProperty(NetworkOptionKeys.SEND_MSG_THROTTLE_TRIGGER, sendMsgThrottleTrigger); + setProperty(NetworkOptionKeys.SEND_MSG_THROTTLE_SLEEP, sendMsgThrottleSleep); + setProperty(AppOptionKeys.APP_DATA_DIR_KEY, appDataDir); setProperty(AppOptionKeys.DESKTOP_WITH_HTTP_API, desktopWithHttpApi); setProperty(AppOptionKeys.DESKTOP_WITH_GRPC_API, desktopWithGrpcApi); @@ -488,6 +502,7 @@ private PropertySource defaultProperties() { setProperty(DaoOptionKeys.FULL_DAO_NODE, fullDaoNode); setProperty(DaoOptionKeys.GENESIS_TX_ID, genesisTxId); setProperty(DaoOptionKeys.GENESIS_BLOCK_HEIGHT, genesisBlockHeight); + setProperty(DaoOptionKeys.GENESIS_TOTAL_SUPPLY, genesisTotalSupply); setProperty(DaoOptionKeys.DAO_ACTIVATED, daoActivated); setProperty(BtcOptionKeys.BTC_NODES, btcNodes); diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index 9050763edaa..dcd87d1c38d 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -33,6 +33,7 @@ import bisq.network.NetworkOptionKeys; import bisq.network.p2p.P2PService; +import bisq.network.p2p.network.ConnectionConfig; import bisq.common.CommonOptionKeys; import bisq.common.UserThread; @@ -68,9 +69,7 @@ import static bisq.core.app.BisqEnvironment.DEFAULT_APP_NAME; import static bisq.core.app.BisqEnvironment.DEFAULT_USER_DATA_DIR; -import static bisq.core.btc.BaseCurrencyNetwork.BTC_MAINNET; -import static bisq.core.btc.BaseCurrencyNetwork.BTC_REGTEST; -import static bisq.core.btc.BaseCurrencyNetwork.BTC_TESTNET; +import static bisq.core.btc.BaseCurrencyNetwork.*; import static com.google.common.base.Preconditions.checkNotNull; import static java.lang.String.format; @@ -256,7 +255,7 @@ protected void setupPersistedDataHosts(Injector injector) { // We need to delay it as the stage is not created yet and so popups would not be shown. if (DevEnv.isDevMode()) UserThread.runAfter(() -> { - log.error("Error at PersistedDataHost.apply: " + t.toString()); + log.error("Error at PersistedDataHost.apply: {}", t.toString()); throw t; }, 2); } @@ -339,11 +338,6 @@ protected void customizeOptionParsing(OptionParser parser) { .withRequiredArg() .describedAs("host:port[,...]"); - parser.accepts(NetworkOptionKeys.MY_ADDRESS, - "My own onion address (used for bootstrap nodes to exclude itself)") - .withRequiredArg() - .describedAs("host:port"); - parser.accepts(NetworkOptionKeys.BAN_LIST, "Nodes to exclude from network connections.") .withRequiredArg() @@ -415,6 +409,27 @@ protected void customizeOptionParsing(OptionParser parser) { parser.accepts(NetworkOptionKeys.TOR_STREAM_ISOLATION, "Use stream isolation for Tor [experimental!]."); + parser.accepts(NetworkOptionKeys.MSG_THROTTLE_PER_SEC, + format("Message throttle per sec for connection class (default: %s)", + String.valueOf(ConnectionConfig.MSG_THROTTLE_PER_SEC))) + .withRequiredArg() + .ofType(int.class); + parser.accepts(NetworkOptionKeys.MSG_THROTTLE_PER_10_SEC, + format("Message throttle per 10 sec for connection class (default: %s)", + String.valueOf(ConnectionConfig.MSG_THROTTLE_PER_10_SEC))) + .withRequiredArg() + .ofType(int.class); + parser.accepts(NetworkOptionKeys.SEND_MSG_THROTTLE_TRIGGER, + format("Time in ms when we trigger a sleep if 2 messages are sent (default: %s)", + String.valueOf(ConnectionConfig.SEND_MSG_THROTTLE_TRIGGER))) + .withRequiredArg() + .ofType(int.class); + parser.accepts(NetworkOptionKeys.SEND_MSG_THROTTLE_SLEEP, + format("Pause in ms to sleep if we get too many messages to send (default: %s)", + String.valueOf(ConnectionConfig.SEND_MSG_THROTTLE_SLEEP))) + .withRequiredArg() + .ofType(int.class); + //AppOptionKeys parser.accepts(AppOptionKeys.USER_DATA_DIR_KEY, format("User data directory (default: %s)", BisqEnvironment.DEFAULT_USER_DATA_DIR)) @@ -479,7 +494,7 @@ protected void customizeOptionParsing(OptionParser parser) { format("Base currency network (default: %s)", BisqEnvironment.getDefaultBaseCurrencyNetwork().name())) .withRequiredArg() .ofType(String.class) - .describedAs(format("%s|%s|%s", BTC_MAINNET, BTC_TESTNET, BTC_REGTEST)); + .describedAs(format("%s|%s|%s|%s", BTC_MAINNET, BTC_TESTNET, BTC_REGTEST, BTC_DAO_TESTNET, BTC_DAO_BETANET)); parser.accepts(BtcOptionKeys.REG_TEST_HOST, format("Bitcoin regtest host when using BTC_REGTEST network (default: %s)", RegTestHost.DEFAULT_HOST)) @@ -550,6 +565,10 @@ protected void customizeOptionParsing(OptionParser parser) { format("Genesis transaction block height when not using the hard coded one (default: %s)", "-1")) .withRequiredArg(); + parser.accepts(DaoOptionKeys.GENESIS_TOTAL_SUPPLY, + format("Genesis total supply when not using the hard coded one (default: %s)", "-1")) + .withRequiredArg(); + parser.accepts(DaoOptionKeys.DAO_ACTIVATED, format("Developer flag. If true it enables dao phase 2 features. (default: %s)", "false")) .withRequiredArg() diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index ceb00e46936..b8d355a0455 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -417,30 +417,35 @@ private void maybeShowTac() { } private void checkIfLocalHostNodeIsRunning() { - Thread checkIfLocalHostNodeIsRunningThread = new Thread(() -> { - Thread.currentThread().setName("checkIfLocalHostNodeIsRunningThread"); - Socket socket = null; - try { - socket = new Socket(); - socket.connect(new InetSocketAddress(InetAddresses.forString("127.0.0.1"), - BisqEnvironment.getBaseCurrencyNetwork().getParameters().getPort()), 5000); - log.info("Localhost Bitcoin node detected."); - UserThread.execute(() -> { - bisqEnvironment.setBitcoinLocalhostNodeRunning(true); - step3(); - }); - } catch (Throwable e) { - UserThread.execute(BisqSetup.this::step3); - } finally { - if (socket != null) { - try { - socket.close(); - } catch (IOException ignore) { + // For DAO testnet we ignore local btc node + if (BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet()) { + step3(); + } else { + Thread checkIfLocalHostNodeIsRunningThread = new Thread(() -> { + Thread.currentThread().setName("checkIfLocalHostNodeIsRunningThread"); + Socket socket = null; + try { + socket = new Socket(); + socket.connect(new InetSocketAddress(InetAddresses.forString("127.0.0.1"), + BisqEnvironment.getBaseCurrencyNetwork().getParameters().getPort()), 5000); + log.info("Localhost Bitcoin node detected."); + UserThread.execute(() -> { + bisqEnvironment.setBitcoinLocalhostNodeRunning(true); + step3(); + }); + } catch (Throwable e) { + UserThread.execute(BisqSetup.this::step3); + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException ignore) { + } } } - } - }); - checkIfLocalHostNodeIsRunningThread.start(); + }); + checkIfLocalHostNodeIsRunningThread.start(); + } } private void readMapsFromResources() { @@ -459,7 +464,6 @@ private void checkCryptoSetup() { Thread checkCryptoThread = new Thread(() -> { try { Thread.currentThread().setName("checkCryptoThread"); - log.trace("Run crypto test"); // just use any simple dummy msg Ping payload = new Ping(1, 1); SealedAndSigned sealedAndSigned = EncryptionService.encryptHybridWithSignature(payload, diff --git a/core/src/main/java/bisq/core/app/SetupUtils.java b/core/src/main/java/bisq/core/app/SetupUtils.java index c7cf8cafa3c..f3bfcd9fccd 100644 --- a/core/src/main/java/bisq/core/app/SetupUtils.java +++ b/core/src/main/java/bisq/core/app/SetupUtils.java @@ -54,7 +54,6 @@ public static void checkCryptoSetup(KeyRing keyRing, EncryptionService encryptio public void run() { try { Thread.currentThread().setName("checkCryptoThread"); - log.trace("Run crypto test"); // just use any simple dummy msg Ping payload = new Ping(1, 1); SealedAndSigned sealedAndSigned = EncryptionService.encryptHybridWithSignature(payload, @@ -86,8 +85,7 @@ public static BooleanProperty readFromResources(P2PDataStorage p2PDataStorage) { Thread.currentThread().setName("readFromResourcesThread"); // Used to load different files per base currency (EntryMap_BTC_MAINNET, EntryMap_LTC,...) final BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); - final String postFix = "_" + baseCurrencyNetwork.getCurrencyCode() + "_" - + baseCurrencyNetwork.getNetwork(); + final String postFix = "_" + baseCurrencyNetwork.name(); long ts = new Date().getTime(); p2PDataStorage.readFromResources(postFix); log.info("readFromResources took {} ms", (new Date().getTime() - ts)); diff --git a/core/src/main/java/bisq/core/app/misc/AppSetupWithP2P.java b/core/src/main/java/bisq/core/app/misc/AppSetupWithP2P.java index 1cb08ab7f80..07c74d7e760 100644 --- a/core/src/main/java/bisq/core/app/misc/AppSetupWithP2P.java +++ b/core/src/main/java/bisq/core/app/misc/AppSetupWithP2P.java @@ -77,7 +77,6 @@ public void initPersistedDataHosts() { // we apply at startup the reading of persisted data but don't want to get it triggered in the constructor persistedDataHosts.forEach(e -> { try { - log.info("call readPersisted at " + e.getClass().getSimpleName()); e.readPersisted(); } catch (Throwable e1) { log.error("readPersisted error", e1); @@ -120,7 +119,7 @@ public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection if (connection.getPeerType() == Connection.PeerType.SEED_NODE && closeConnectionReason == CloseConnectionReason.RULE_VIOLATION) { log.warn("RULE_VIOLATION onDisconnect closeConnectionReason=" + closeConnectionReason); - log.warn("RULE_VIOLATION onDisconnect connection=" + connection); + log.warn("RULE_VIOLATION onDisconnect connection={}", connection); } } 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 a48dd4384be..55e41da3827 100644 --- a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java +++ b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java @@ -47,7 +47,7 @@ @Slf4j public abstract class ExecutableForAppWithP2p extends BisqExecutable implements UncaughtExceptionHandler { private static final long MAX_MEMORY_MB_DEFAULT = 500; - private static final long CHECK_MEMORY_PERIOD_SEC = 10; + private static final long CHECK_MEMORY_PERIOD_SEC = 300; private volatile boolean stopped; private static long maxMemory = MAX_MEMORY_MB_DEFAULT; diff --git a/core/src/main/java/bisq/core/app/misc/ModuleForAppWithP2p.java b/core/src/main/java/bisq/core/app/misc/ModuleForAppWithP2p.java index 16d1c87ea70..35413918c2d 100644 --- a/core/src/main/java/bisq/core/app/misc/ModuleForAppWithP2p.java +++ b/core/src/main/java/bisq/core/app/misc/ModuleForAppWithP2p.java @@ -26,7 +26,6 @@ import bisq.core.dao.DaoModule; import bisq.core.filter.FilterModule; import bisq.core.network.p2p.seed.DefaultSeedNodeRepository; -import bisq.core.network.p2p.seed.SeedNodeAddressLookup; import bisq.core.offer.OfferModule; import bisq.core.proto.network.CoreNetworkProtoResolver; import bisq.core.proto.persistable.CorePersistenceProtoResolver; @@ -77,7 +76,6 @@ protected void configure() { bind(BridgeAddressProvider.class).to(Preferences.class).in(Singleton.class); bind(TorSetup.class).in(Singleton.class); - bind(SeedNodeAddressLookup.class).in(Singleton.class); bind(SeedNodeRepository.class).to(DefaultSeedNodeRepository.class).in(Singleton.class); File storageDir = new File(environment.getRequiredProperty(Storage.STORAGE_DIR)); diff --git a/core/src/main/java/bisq/core/arbitration/DisputeManager.java b/core/src/main/java/bisq/core/arbitration/DisputeManager.java index 69dc30a2b57..3110b5e2f7b 100644 --- a/core/src/main/java/bisq/core/arbitration/DisputeManager.java +++ b/core/src/main/java/bisq/core/arbitration/DisputeManager.java @@ -49,6 +49,7 @@ import bisq.common.Timer; import bisq.common.UserThread; +import bisq.common.app.Version; import bisq.common.crypto.KeyRing; import bisq.common.crypto.PubKeyRing; import bisq.common.handlers.FaultHandler; @@ -337,8 +338,8 @@ public void sendOpenNewDisputeMessage(Dispute dispute, boolean reOpen, ResultHan final Optional storedDisputeOptional = findDispute(dispute.getTradeId(), dispute.getTraderId()); if (!storedDisputeOptional.isPresent() || reOpen) { String sysMsg = dispute.isSupportTicket() ? - Res.get("support.youOpenedTicket", disputeInfo) - : Res.get("support.youOpenedDispute", disputeInfo); + Res.get("support.youOpenedTicket", disputeInfo, Version.VERSION) + : Res.get("support.youOpenedDispute", disputeInfo, Version.VERSION); DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage( dispute.getTradeId(), @@ -450,7 +451,7 @@ private String sendPeerOpenedDisputeMessage(Dispute disputeFromOpener, Contract final Optional storedDisputeOptional = findDispute(dispute.getTradeId(), dispute.getTraderId()); if (!storedDisputeOptional.isPresent()) { String sysMsg = dispute.isSupportTicket() ? - Res.get("support.peerOpenedTicket") + Res.get("support.peerOpenedTicket", disputeInfo) : Res.get("support.peerOpenedDispute", disputeInfo); DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage( dispute.getTradeId(), diff --git a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java index 11138d12a36..12d50336223 100644 --- a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java +++ b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java @@ -27,7 +27,9 @@ public enum BaseCurrencyNetwork { BTC_MAINNET(MainNetParams.get(), "BTC", "MAINNET", "Bitcoin"), BTC_TESTNET(TestNet3Params.get(), "BTC", "TESTNET", "Bitcoin"), - BTC_REGTEST(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"); + BTC_REGTEST(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), + BTC_DAO_TESTNET(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), // server side regtest + BTC_DAO_BETANET(MainNetParams.get(), "BTC", "MAINNET", "Bitcoin"); // mainnet test genesis @Getter private final NetworkParameters parameters; @@ -46,19 +48,23 @@ public enum BaseCurrencyNetwork { } public boolean isMainnet() { - return "MAINNET".equals(network); + return "BTC_MAINNET".equals(name()); } public boolean isTestnet() { - return "TESTNET".equals(network); + return "BTC_TESTNET".equals(name()); } - public boolean isRegtest() { - return "REGTEST".equals(network); + public boolean isDaoTestNet() { + return "BTC_DAO_TESTNET".equals(name()); + } + + public boolean isDaoBetaNet() { + return "BTC_DAO_BETANET".equals(name()); } - public boolean isBitcoin() { - return "BTC".equals(currencyCode); + public boolean isRegtest() { + return "BTC_REGTEST".equals(name()); } public long getDefaultMinFeePerByte() { diff --git a/core/src/main/java/bisq/core/btc/BitcoinModule.java b/core/src/main/java/bisq/core/btc/BitcoinModule.java index a5de4e8efab..bb4af9fafb1 100644 --- a/core/src/main/java/bisq/core/btc/BitcoinModule.java +++ b/core/src/main/java/bisq/core/btc/BitcoinModule.java @@ -18,6 +18,7 @@ package bisq.core.btc; import bisq.core.app.AppOptionKeys; +import bisq.core.app.BisqEnvironment; import bisq.core.btc.model.AddressEntryList; import bisq.core.btc.nodes.BtcNodes; import bisq.core.btc.setup.RegTestHost; @@ -53,13 +54,18 @@ public BitcoinModule(Environment environment) { @Override protected void configure() { - String regTestHost = environment.getProperty(BtcOptionKeys.REG_TEST_HOST, String.class, RegTestHost.DEFAULT_HOST); + // We we have selected BTC_DAO_TESTNET we use our master regtest node, otherwise the specified host or default + // (localhost) + String regTestHost = BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet() ? + "104.248.31.39" : + environment.getProperty(BtcOptionKeys.REG_TEST_HOST, String.class, RegTestHost.DEFAULT_HOST); + + RegTestHost.HOST = regTestHost; if (Arrays.asList("localhost", "127.0.0.1").contains(regTestHost)) { bind(RegTestHost.class).toInstance(RegTestHost.LOCALHOST); } else { bind(RegTestHost.class).toInstance(RegTestHost.REMOTE_HOST); } - RegTestHost.HOST = regTestHost; bindConstant().annotatedWith(named(UserAgent.NAME_KEY)).to(environment.getRequiredProperty(UserAgent.NAME_KEY)); bindConstant().annotatedWith(named(UserAgent.VERSION_KEY)).to(environment.getRequiredProperty(UserAgent.VERSION_KEY)); diff --git a/core/src/main/java/bisq/core/btc/exceptions/TxMalleabilityException.java b/core/src/main/java/bisq/core/btc/exceptions/TxMalleabilityException.java deleted file mode 100644 index caa262b4a30..00000000000 --- a/core/src/main/java/bisq/core/btc/exceptions/TxMalleabilityException.java +++ /dev/null @@ -1,50 +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.btc.exceptions; - -import org.bitcoinj.core.Transaction; - -import lombok.Getter; - -import javax.annotation.Nullable; - - -public class TxMalleabilityException extends TxBroadcastException { - @Getter - @Nullable - private final Transaction localTx; - @Getter - @Nullable - private final Transaction networkTx; - - public TxMalleabilityException(Transaction localTx, Transaction networkTx) { - super("The transaction we received from the Bitcoin network has a different txId as the one we broadcasted.\n" + - "txId of local tx=" + localTx.getHashAsString() + - ", txId of received tx=" + localTx.getHashAsString()); - this.localTx = localTx; - this.networkTx = networkTx; - } - - @Override - public String toString() { - return "TxMalleabilityException{" + - "\n localTx=" + localTx + - ",\n networkTx=" + networkTx + - "\n} " + super.toString(); - } -} 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 08b0ad188b9..6cb1bffc12b 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntryList.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java @@ -25,6 +25,7 @@ import com.google.protobuf.Message; +import org.bitcoinj.core.Transaction; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.wallet.Wallet; @@ -32,6 +33,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -110,16 +112,45 @@ public void onWalletReady(Wallet wallet) { list = new ArrayList<>(); add(new AddressEntry(wallet.freshReceiveKey(), AddressEntry.Context.ARBITRATOR)); - // In case we restore from seed words and have balance we need to add the relevant addresses to our list: + // In case we restore from seed words and have balance we need to add the relevant addresses to our list. + // IssuedReceiveAddresses does not contain all addressed where we expect balance so we need to listen to + // incoming txs at blockchain sync to add the rest. if (wallet.getBalance().isPositive()) { wallet.getIssuedReceiveAddresses().forEach(address -> { - log.info("Create AddressEntry for address={}", address); + log.info("Create AddressEntry for IssuedReceiveAddress. address={}", address.toString()); add(new AddressEntry((DeterministicKey) wallet.findKeyFromPubHash(address.getHash160()), AddressEntry.Context.AVAILABLE)); }); } - persist(); } + + // We add those listeners to get notified about potential new transactions and + // add an address entry list in case it does not exist yet. This is mainly needed for restore from seed words + // but can help as well in case the addressEntry list would miss an address where the wallet was received + // funds (e.g. if the user sends funds to an address which has not been provided in the main UI - like from the + // wallet details window). + wallet.addCoinsReceivedEventListener((w, tx, prevBalance, newBalance) -> { + updateList(tx); + }); + wallet.addCoinsSentEventListener((w, tx, prevBalance, newBalance) -> { + updateList(tx); + }); + } + + private void updateList(Transaction tx) { + tx.getOutputs().stream() + .filter(output -> output.isMine(wallet)) + .map(output -> output.getAddressFromP2PKHScript(wallet.getNetworkParameters())) + .filter(Objects::nonNull) + .filter(address -> !listContainsEntryWithAddress(address.toBase58())) + .map(address -> (DeterministicKey) wallet.findKeyFromPubHash(address.getHash160())) + .filter(Objects::nonNull) + .map(deterministicKey -> new AddressEntry(deterministicKey, AddressEntry.Context.AVAILABLE)) + .forEach(addressEntry -> list.add(addressEntry)); + } + + private boolean listContainsEntryWithAddress(String addressString) { + return list.stream().anyMatch(addressEntry -> Objects.equals(addressEntry.getAddressString(), addressString)); } private boolean add(AddressEntry addressEntry) { diff --git a/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java b/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java index 61bf64a4e50..2cb29b8f49e 100644 --- a/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java +++ b/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java @@ -23,7 +23,7 @@ import bisq.network.DnsLookupTor; import org.bitcoinj.core.PeerAddress; -import org.bitcoinj.net.OnionCat; +import org.bitcoinj.net.OnionCatConverter; import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; @@ -56,7 +56,7 @@ PeerAddress convertOnionHost(BtcNode node) { // no DNS lookup for onion addresses String onionAddress = Objects.requireNonNull(node.getOnionAddress()); try { - // OnionCat.onionHostToInetAddress converts onion to ipv6 representation + // OnionCatConverter.onionHostToInetAddress converts onion to ipv6 representation // inetAddress is not used but required for wallet persistence. Throws nullPointer if not set. InetAddress inetAddress = facade.onionHostToInetAddress(onionAddress); PeerAddress result = new PeerAddress(onionAddress, node.getPort()); @@ -127,7 +127,7 @@ private static PeerAddress create(String hostName, int port) { static class Facade { InetAddress onionHostToInetAddress(String onionAddress) throws UnknownHostException { - return OnionCat.onionHostToInetAddress(onionAddress); + return OnionCatConverter.onionHostToInetAddress(onionAddress); } InetAddress torLookup(Socks5Proxy proxy, String host) throws DnsLookupException { diff --git a/core/src/main/java/bisq/core/btc/nodes/BtcNodes.java b/core/src/main/java/bisq/core/btc/nodes/BtcNodes.java index 63ebc44179c..e6593cc7677 100644 --- a/core/src/main/java/bisq/core/btc/nodes/BtcNodes.java +++ b/core/src/main/java/bisq/core/btc/nodes/BtcNodes.java @@ -85,7 +85,7 @@ public List getProvidedBtcNodes() { } public boolean useProvidedBtcNodes() { - return BisqEnvironment.getBaseCurrencyNetwork().isBitcoin() && BisqEnvironment.getBaseCurrencyNetwork().isMainnet(); + return BisqEnvironment.getBaseCurrencyNetwork().isMainnet(); } public static List toBtcNodesList(Collection nodes) { diff --git a/core/src/main/java/bisq/core/btc/nodes/SeedPeersSocks5Dns.java b/core/src/main/java/bisq/core/btc/nodes/SeedPeersSocks5Dns.java index 6c1dabbf2b0..20f835de7b5 100644 --- a/core/src/main/java/bisq/core/btc/nodes/SeedPeersSocks5Dns.java +++ b/core/src/main/java/bisq/core/btc/nodes/SeedPeersSocks5Dns.java @@ -162,7 +162,7 @@ public static InetSocketAddress lookup(Socks5Proxy proxy, InetSocketAddress addr InetAddress addrResolved = proxySocket.getInetAddress(); proxySocket.close(); if (addrResolved != null) { - log.debug("Resolved " + addr.getHostString() + " to " + addrResolved.getHostAddress()); + //log.debug("Resolved " + addr.getHostString() + " to " + addrResolved.getHostAddress()); return new InetSocketAddress(addrResolved, addr.getPort()); } else { // note: .onion nodes fall in here when proxy is Tor. But they have no IP address. diff --git a/core/src/main/java/bisq/core/btc/setup/NonMMappedSPVBlockStore.java b/core/src/main/java/bisq/core/btc/setup/NonMMappedSPVBlockStore.java deleted file mode 100644 index 7bf151e7d05..00000000000 --- a/core/src/main/java/bisq/core/btc/setup/NonMMappedSPVBlockStore.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * 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.btc.setup; - -import org.bitcoinj.core.*; -import org.bitcoinj.store.BlockStore; -import org.bitcoinj.store.BlockStoreException; -import org.bitcoinj.store.ChainFileLockedException; -import org.bitcoinj.utils.*; -import org.slf4j.*; - -import javax.annotation.*; -import java.io.*; -import java.nio.*; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.locks.*; - -import static com.google.common.base.Preconditions.*; - -/** - * NonMMappedSPVBlockStore is like SPVBlockStore but it does not use a memory mapped file - * to access the file systen. Memory mapped file has problems on windows on restore from - * seed because of problems releasing control of mmapped files. - * We proposed a similar solution on bitcoinj upstream, so we hopefully will be able to - * remove this class and use the upstream version. - * See: - * Mapdb on mmapped files - * Java bug 1 - * Java bug 2 - * Java bug 3 - */ -public class NonMMappedSPVBlockStore implements BlockStore { - private static final Logger log = LoggerFactory.getLogger(NonMMappedSPVBlockStore.class); - - /** The default number of headers that will be stored in the ring buffer. */ - public static final int DEFAULT_NUM_HEADERS = 5000; - public static final String HEADER_MAGIC = "SPVB"; - - protected int numHeaders; - protected NetworkParameters params; - - protected ReentrantLock lock = Threading.lock("SPVBlockStore"); - - protected LinkedHashMap blockCache = new LinkedHashMap() { - @Override - protected boolean removeEldestEntry(Map.Entry entry) { - return size() > 2050; // Slightly more than the difficulty transition period. - } - }; - // Use a separate cache to track get() misses. This is to efficiently handle the case of an unconnected block - // during chain download. Each new block will do a get() on the unconnected block so if we haven't seen it yet we - // must efficiently respond. - // - // We don't care about the value in this cache. It is always notFoundMarker. Unfortunately LinkedHashSet does not - // provide the removeEldestEntry control. - protected static final Object notFoundMarker = new Object(); - protected LinkedHashMap notFoundCache = new LinkedHashMap() { - @Override - protected boolean removeEldestEntry(Map.Entry entry) { - return size() > 100; // This was chosen arbitrarily. - } - }; - // Used to stop other applications/processes from opening the store. - protected FileLock fileLock = null; - protected RandomAccessFile randomAccessFile = null; - private final String fileAbsolutePath; - - /** - * Creates and initializes an SPV block store. Will create the given file if it's missing. This operation - * will block on disk. - */ - public NonMMappedSPVBlockStore(NetworkParameters params, File file) throws BlockStoreException { - checkNotNull(file); - fileAbsolutePath = file.getAbsolutePath(); - this.params = checkNotNull(params); - try { - this.numHeaders = DEFAULT_NUM_HEADERS; - boolean exists = file.exists(); - // Set up the backing file. - randomAccessFile = new RandomAccessFile(file, "rw"); - long fileSize = getFileSize(); - if (!exists) { - log.info("Creating new SPV block chain file " + file); - randomAccessFile.setLength(fileSize); - } else if (randomAccessFile.length() != fileSize) { - throw new BlockStoreException("File size on disk does not match expected size: " + - randomAccessFile.length() + " vs " + fileSize); - } - - FileChannel channel = randomAccessFile.getChannel(); - fileLock = channel.tryLock(); - if (fileLock == null) - throw new ChainFileLockedException("Store file is already locked by another process"); - - // Check or initialize the header bytes to ensure we don't try to open some random file. - byte[] header; - if (exists) { - header = new byte[4]; - randomAccessFile.read(header); - if (!new String(header, "US-ASCII").equals(HEADER_MAGIC)) - throw new BlockStoreException("Header bytes do not equal " + HEADER_MAGIC); - } else { - initNewStore(params); - } - } catch (Exception e) { - try { - if (randomAccessFile != null) randomAccessFile.close(); - } catch (IOException e2) { - throw new BlockStoreException(e2); - } - throw new BlockStoreException(e); - } - } - - private void initNewStore(NetworkParameters params) throws Exception { - byte[] header; - header = HEADER_MAGIC.getBytes("US-ASCII"); - randomAccessFile.write(header); - // Insert the genesis block. - lock.lock(); - try { - setRingCursor(randomAccessFile, FILE_PROLOGUE_BYTES); - } finally { - lock.unlock(); - } - Block genesis = params.getGenesisBlock().cloneAsHeader(); - StoredBlock storedGenesis = new StoredBlock(genesis, genesis.getWork(), 0); - put(storedGenesis); - setChainHead(storedGenesis); - } - - /** Returns the size in bytes of the file that is used to store the chain with the current parameters. */ - public final int getFileSize() { - return RECORD_SIZE * numHeaders + FILE_PROLOGUE_BYTES /* extra kilobyte for stuff */; - } - - @Override - public void put(StoredBlock block) throws BlockStoreException { - final RandomAccessFile randomAccessFile = this.randomAccessFile; - if (randomAccessFile == null) throw new BlockStoreException("Store closed"); - - lock.lock(); - try { - int cursor = getRingCursor(randomAccessFile); - if (cursor == getFileSize()) { - // Wrapped around. - cursor = FILE_PROLOGUE_BYTES; - } - randomAccessFile.seek(cursor); - Sha256Hash hash = block.getHeader().getHash(); - notFoundCache.remove(hash); - randomAccessFile.write(hash.getBytes()); - ByteBuffer buffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE); - block.serializeCompact(buffer); - buffer.flip(); - FileChannel channel = randomAccessFile.getChannel(); - channel.write(buffer); - setRingCursor(randomAccessFile, (int) randomAccessFile.getFilePointer()); - blockCache.put(hash, block); - } catch (IOException ioException) { - throw new BlockStoreException(ioException); - } finally { - lock.unlock(); - } - } - - @Override - @Nullable - public StoredBlock get(Sha256Hash hash) throws BlockStoreException { - final RandomAccessFile randomAccessFile = this.randomAccessFile; - if (randomAccessFile == null) throw new BlockStoreException("Store closed"); - - lock.lock(); - try { - StoredBlock cacheHit = blockCache.get(hash); - if (cacheHit != null) - return cacheHit; - if (notFoundCache.get(hash) != null) - return null; - - // Starting from the current tip of the ring work backwards until we have either found the block or - // wrapped around. - int cursor = getRingCursor(randomAccessFile); - final int startingPoint = cursor; - final int fileSize = getFileSize(); - final byte[] targetHashBytes = hash.getBytes(); - byte[] scratch = new byte[32]; - do { - cursor -= RECORD_SIZE; - if (cursor < FILE_PROLOGUE_BYTES) { - // We hit the start, so wrap around. - cursor = fileSize - RECORD_SIZE; - } - // Cursor is now at the start of the next record to check, so read the hash and compare it. - randomAccessFile.seek(cursor); - randomAccessFile.read(scratch); - if (Arrays.equals(scratch, targetHashBytes)) { - // Found the target. - ByteBuffer buffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE); - FileChannel channel = randomAccessFile.getChannel(); - channel.read(buffer); - buffer.flip(); - StoredBlock storedBlock = StoredBlock.deserializeCompact(params, buffer); - blockCache.put(hash, storedBlock); - return storedBlock; - } - } while (cursor != startingPoint); - // Not found. - notFoundCache.put(hash, notFoundMarker); - return null; - } catch (ProtocolException e) { - throw new RuntimeException(e); // Cannot happen. - } catch (IOException ioException) { - throw new BlockStoreException(ioException); - } finally { lock.unlock(); } - } - - protected StoredBlock lastChainHead = null; - - @Override - public StoredBlock getChainHead() throws BlockStoreException { - final RandomAccessFile randomAccessFile = this.randomAccessFile; - if (randomAccessFile == null) throw new BlockStoreException("Store closed"); - - lock.lock(); - try { - if (lastChainHead == null) { - byte[] headHash = new byte[32]; - randomAccessFile.seek(8); - randomAccessFile.read(headHash); - Sha256Hash hash = Sha256Hash.wrap(headHash); - StoredBlock block = get(hash); - if (block == null) - throw new BlockStoreException("Corrupted block store: could not find chain head: " + hash - +"\nFile path: "+ fileAbsolutePath); - lastChainHead = block; - } - return lastChainHead; - } catch (IOException ioException) { - throw new BlockStoreException(ioException); - } finally { - lock.unlock(); - } - } - - @Override - public void setChainHead(StoredBlock chainHead) throws BlockStoreException { - final RandomAccessFile randomAccessFile = this.randomAccessFile; - if (randomAccessFile == null) throw new BlockStoreException("Store closed"); - - lock.lock(); - try { - lastChainHead = chainHead; - byte[] headHash = chainHead.getHeader().getHash().getBytes(); - randomAccessFile.seek(8); - randomAccessFile.write(headHash); - } catch (IOException ioException) { - throw new BlockStoreException(ioException); - } finally { - lock.unlock(); - } - } - - @Override - public void close() throws BlockStoreException { - try { - randomAccessFile.close(); - randomAccessFile = null; // Set to null to avoid trying to use a closed file. - } catch (IOException e) { - throw new BlockStoreException(e); - } - } - - @Override - public NetworkParameters getParams() { - return params; - } - - protected static final int RECORD_SIZE = 32 /* hash */ + StoredBlock.COMPACT_SERIALIZED_SIZE; - - // File format: - // 4 header bytes = "SPVB" - // 4 cursor bytes, which indicate the offset from the first kb where the next block header should be written. - // 32 bytes for the hash of the chain head - // - // For each header (128 bytes) - // 32 bytes hash of the header - // 12 bytes of chain work - // 4 bytes of height - // 80 bytes of block header data - protected static final int FILE_PROLOGUE_BYTES = 1024; - - /** Returns the offset from the file start where the latest block should be written (end of prev block). */ - private int getRingCursor(RandomAccessFile randomAccessFile) throws IOException { - long filePointer = randomAccessFile.getFilePointer(); - randomAccessFile.seek(4); - int c = randomAccessFile.readInt(); - randomAccessFile.seek(filePointer); - checkState(c >= FILE_PROLOGUE_BYTES, "Integer overflow"); - return c; - } - - private void setRingCursor(RandomAccessFile randomAccessFile, int newCursor) throws IOException { - checkArgument(newCursor >= 0); - long filePointer = randomAccessFile.getFilePointer(); - randomAccessFile.seek(4); - randomAccessFile.writeInt(newCursor); - randomAccessFile.seek(filePointer); - } - -} 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 bf5d659f008..e8c19ea9c60 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -22,7 +22,6 @@ import bisq.core.btc.wallet.BisqRiskAnalysis; import bisq.common.app.Version; -import bisq.common.util.Utilities; import org.bitcoinj.core.BlockChain; import org.bitcoinj.core.CheckpointManager; @@ -125,7 +124,7 @@ public interface BisqWalletFactory extends WalletProtobufSerializer.WalletFactor private DeterministicSeed seed; private volatile BlockChain vChain; - private volatile BlockStore vStore; + private volatile SPVBlockStore vStore; private volatile PeerGroup vPeerGroup; private boolean useAutoSave = true; private PeerAddress[] peerAddresses; @@ -210,9 +209,12 @@ public Wallet create(NetworkParameters params, KeyChainGroup keyChainGroup, bool } private PeerGroup createPeerGroup() { + PeerGroup peerGroup; // no proxy case. if (socks5Proxy == null) { - return new PeerGroup(params, vChain); + peerGroup = new PeerGroup(params, vChain); + // For dao testnet (server side regtest) we prevent to connect to a localhost node to avoid confusion + // if local btc node is not synced with our dao testnet master node. } else { // proxy case (tor). Proxy proxy = new Proxy(Proxy.Type.SOCKS, @@ -220,18 +222,23 @@ private PeerGroup createPeerGroup() { socks5Proxy.getPort())); ProxySocketFactory proxySocketFactory = new ProxySocketFactory(proxy); - // we dont use tor mode if we have a local node running + // We don't use tor mode if we have a local node running BlockingClientManager blockingClientManager = bisqEnvironment.isBitcoinLocalhostNodeRunning() ? new BlockingClientManager() : new BlockingClientManager(proxySocketFactory); - PeerGroup peerGroup = new PeerGroup(params, vChain, blockingClientManager); + peerGroup = new PeerGroup(params, vChain, blockingClientManager); blockingClientManager.setConnectTimeoutMillis(TIMEOUT); peerGroup.setConnectTimeoutMillis(TIMEOUT); - - return peerGroup; } + + // For dao testnet (server side regtest) we prevent to connect to a localhost node to avoid confusion + // if local btc node is not synced with our dao testnet master node. + if (BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet()) + peerGroup.setUseLocalhostPeerWhenPossible(false); + + return peerGroup; } /** @@ -318,17 +325,6 @@ private List provideWalletExtensions() { return ImmutableList.of(); } - /** - * Override this to use a {@link BlockStore} that isn't the default of {@link SPVBlockStore}. - */ - private BlockStore provideBlockStore(File file) throws BlockStoreException { - if (Utilities.isWindows()) { - return new NonMMappedSPVBlockStore(params, file); - } else { - return new SPVBlockStore(params, file); - } - } - /** * This method is invoked on a background thread after all objects are initialised, but before the peer group * or block chain download is started. You can tweak the objects configuration here. @@ -399,7 +395,7 @@ protected void startUp() throws Exception { vBsqWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer()); // Initiate Bitcoin network objects (block store, blockchain and peer group) - vStore = provideBlockStore(chainFile); + vStore = new SPVBlockStore(params, chainFile); if (!chainFileExists || seed != null) { if (checkpoints != null) { // Initialize the chain file with a checkpoint to speed up first-run sync. @@ -409,11 +405,8 @@ protected void startUp() throws Exception { // we created both wallets at the same time time = seed.getCreationTimeSeconds(); if (chainFileExists) { - log.info("Deleting the chain file in preparation from restore."); - vStore.close(); - if (!chainFile.delete()) - throw new IOException("Failed to delete chain file in preparation for restore."); - vStore = provideBlockStore(chainFile); + log.info("Clearing the chain file in preparation from restore."); + vStore.clear(); } } else { time = vBtcWallet.getEarliestKeyCreationTime(); @@ -425,11 +418,8 @@ protected void startUp() throws Exception { else log.warn("Creating a new uncheckpointed block store due to a wallet with a creation time of zero: this will result in a very slow chain sync"); } else if (chainFileExists) { - log.info("Deleting the chain file in preparation from restore."); - vStore.close(); - if (!chainFile.delete()) - throw new IOException("Failed to delete chain file in preparation for restore."); - vStore = provideBlockStore(chainFile); + log.info("Clearing the chain file in preparation from restore."); + vStore.clear(); } } vChain = new BlockChain(params, vStore); @@ -447,6 +437,7 @@ protected void startUp() throws Exception { int maxConnections = Math.min(numConnectionForBtc, peerAddresses.length); log.info("We try to connect to {} btc nodes", maxConnections); vPeerGroup.setMaxConnections(maxConnections); + vPeerGroup.setAddPeersFromAddressMessage(false); peerAddresses = null; } else if (!params.equals(RegTestParams.get())) { vPeerGroup.addPeerDiscovery(discovery != null ? discovery : new DnsDiscovery(params)); 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 c329cc662ab..14dd75236ac 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java @@ -33,7 +33,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.handlers.ExceptionHandler; import bisq.common.handlers.ResultHandler; import bisq.common.storage.FileUtil; @@ -170,8 +169,6 @@ public WalletsSetup(RegTestHost regTestHost, /////////////////////////////////////////////////////////////////////////////////////////// public void initialize(@Nullable DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { - Log.traceCall(); - // Tell bitcoinj to execute event handlers on the JavaFX UI thread. This keeps things simple and means // we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener // we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in diff --git a/core/src/main/java/bisq/core/btc/wallet/BisqRiskAnalysis.java b/core/src/main/java/bisq/core/btc/wallet/BisqRiskAnalysis.java index d148202165c..dfd81746c92 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BisqRiskAnalysis.java +++ b/core/src/main/java/bisq/core/btc/wallet/BisqRiskAnalysis.java @@ -113,6 +113,13 @@ private Result analyzeIsFinal() { if (tx.getConfidence().getSource() == TransactionConfidence.Source.SELF) return Result.OK; + // Relative time-locked transactions are risky too. We can't check the locks because usually we don't know the + // spent outputs (to know when they were created). + if (tx.hasRelativeLockTime()) { + nonFinal = tx; + return Result.NON_FINAL; + } + if (wallet == null) return null; @@ -157,7 +164,7 @@ public enum RuleViolation { */ public static RuleViolation isStandard(Transaction tx) { // TODO: Finish this function off. - if (tx.getVersion() > 1 || tx.getVersion() < 1) { + if (tx.getVersion() > 2 || tx.getVersion() < 1) { log.warn("TX considered non-standard due to unknown version number {}", tx.getVersion()); return RuleViolation.VERSION; } 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 b07577a7c1d..c9554359b3c 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java @@ -212,6 +212,7 @@ private void updateBsqBalance() { // unlockingBondsBalance long outputs = tx.getOutputs().stream() .filter(out -> out.isMine(wallet)) + .filter(TransactionOutput::isAvailableForSpending) .mapToLong(out -> out.getValue().value) .sum(); // Account for spending of locked connectedOutputs @@ -248,15 +249,17 @@ private void updateBsqBalance() { .filter(txOutput -> confirmedTxIdSet.contains(txOutput.getTxId())) .mapToLong(TxOutput::getValue) .sum()); + lockupBondsBalance = Coin.valueOf(daoStateService.getLockupTxOutputs().stream() .filter(txOutput -> daoStateService.isUnspent(txOutput.getKey())) - /*.filter(txOutput -> !daoStateService.isSpentByUnlockTx(txOutput))*/ // TODO SQ + .filter(txOutput -> !daoStateService.isConfiscatedLockupTxOutput(txOutput.getTxId())) .filter(txOutput -> confirmedTxIdSet.contains(txOutput.getTxId())) .mapToLong(TxOutput::getValue) .sum()); unlockingBondsBalance = Coin.valueOf(daoStateService.getUnspentUnlockingTxOutputsStream() .filter(txOutput -> confirmedTxIdSet.contains(txOutput.getTxId())) + .filter(txOutput -> !daoStateService.isConfiscatedUnlockTxOutput(txOutput.getTxId())) .mapToLong(TxOutput::getValue) .sum()); @@ -297,8 +300,9 @@ public Stream getPendingWalletTransactionsStream() { private void updateBsqWalletTransactions() { walletTransactions.setAll(getTransactions(false)); - // walletTransactions.setAll(getBsqWalletTransactions()); - updateBsqBalance(); + if (daoStateService.isParseBlockChainComplete()) { + updateBsqBalance(); + } } private Set getBsqWalletTransactions() { 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 188690e955e..8b765eeb6af 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcCoinSelector.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcCoinSelector.java @@ -62,10 +62,7 @@ class BtcCoinSelector extends BisqDefaultCoinSelector { protected boolean isTxOutputSpendable(TransactionOutput output) { if (WalletService.isOutputScriptConvertibleToAddress(output)) { Address address = WalletService.getAddressFromOutput(output); - boolean containsAddress = addresses.contains(address); - if (!containsAddress) - log.trace("addresses not containing address " + addresses + " / " + address); - return containsAddress; + return addresses.contains(address); } else { log.warn("transactionOutput.getScriptPubKey() not isSentToAddress or isPayToScriptHash"); return false; 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 5760df2abf9..92fdb953849 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -589,6 +589,12 @@ public AddressEntry getFreshAddressEntry() { return getOrCreateAddressEntry(context, addressEntry); } + public AddressEntry getNewAddressEntry(String offerId, AddressEntry.Context context) { + AddressEntry entry = new AddressEntry(wallet.freshReceiveKey(), context, offerId); + addressEntryList.addAddressEntry(entry); + return entry; + } + private AddressEntry getOrCreateAddressEntry(AddressEntry.Context context, Optional addressEntry) { if (addressEntry.isPresent()) { return addressEntry.get(); diff --git a/core/src/main/java/bisq/core/btc/wallet/NonBsqCoinSelector.java b/core/src/main/java/bisq/core/btc/wallet/NonBsqCoinSelector.java index 40ad176ed49..84d39022333 100644 --- a/core/src/main/java/bisq/core/btc/wallet/NonBsqCoinSelector.java +++ b/core/src/main/java/bisq/core/btc/wallet/NonBsqCoinSelector.java @@ -38,7 +38,7 @@ public class NonBsqCoinSelector extends BisqDefaultCoinSelector { @Inject public NonBsqCoinSelector(DaoStateService daoStateService) { - super(true); + super(false); this.daoStateService = daoStateService; } @@ -49,6 +49,8 @@ protected boolean isTxOutputSpendable(TransactionOutput output) { if (parentTransaction == null) return false; + // It is important to not allow pending txs as otherwise unconfirmed BSQ txs would considered nonBSQ as + // below outputIsNotInBsqState would be true. if (parentTransaction.getConfidence().getConfidenceType() != TransactionConfidence.ConfidenceType.BUILDING) return false; diff --git a/core/src/main/java/bisq/core/btc/wallet/Restrictions.java b/core/src/main/java/bisq/core/btc/wallet/Restrictions.java index 6bb6590bed9..6ee395f7b4e 100644 --- a/core/src/main/java/bisq/core/btc/wallet/Restrictions.java +++ b/core/src/main/java/bisq/core/btc/wallet/Restrictions.java @@ -23,9 +23,7 @@ public class Restrictions { private static Coin MIN_TRADE_AMOUNT; - private static Coin MAX_BUYER_SECURITY_DEPOSIT; private static Coin MIN_BUYER_SECURITY_DEPOSIT; - private static Coin DEFAULT_BUYER_SECURITY_DEPOSIT; // For the seller we use a fixed one as there is no way the seller can cancel the trade // To make it editable would just increase complexity. private static Coin SELLER_SECURITY_DEPOSIT; @@ -48,32 +46,38 @@ public static boolean isDust(Coin amount) { public static Coin getMinTradeAmount() { if (MIN_TRADE_AMOUNT == null) - MIN_TRADE_AMOUNT = Coin.valueOf(10_000); // 2 USD @ 20000 USD/BTC + MIN_TRADE_AMOUNT = Coin.valueOf(10_000); // 0,4 USD @ 4000 USD/BTC return MIN_TRADE_AMOUNT; } - // Can be reduced but not increased. Otherwise would break existing offers! - public static Coin getMaxBuyerSecurityDeposit() { - if (MAX_BUYER_SECURITY_DEPOSIT == null) - MAX_BUYER_SECURITY_DEPOSIT = Coin.valueOf(5_000_000); // 1000 USD @ 20000 USD/BTC - return MAX_BUYER_SECURITY_DEPOSIT; + public static double getDefaultBuyerSecurityDepositAsPercent() { + return 0.02; // 2% of trade amount. For a 1 BTC trade it is about 80 USD @ 4000 USD/BTC. } - public static Coin getMinBuyerSecurityDeposit() { + public static double getMinBuyerSecurityDepositAsPercent() { + return 0.0005; // 0.05% of trade amount. For a 1 BTC trade it is about 2 USD @ 4000 USD/BTC but MIN_BUYER_SECURITY_DEPOSIT would require 0.001 BTC anyway (4 USD) + } + + public static double getMaxBuyerSecurityDepositAsPercent() { + return 0.2; // 20% of trade amount. For a 1 BTC trade it is about 800 USD @ 4000 USD/BTC + } + + // We use MIN_BUYER_SECURITY_DEPOSIT as well as lower bound in case of small trade amounts. + // So 0.0005 BTC is the min. buyer security deposit even with amount of 0.0001 BTC and 0.05% percentage value. + public static Coin getMinBuyerSecurityDepositAsCoin() { if (MIN_BUYER_SECURITY_DEPOSIT == null) - MIN_BUYER_SECURITY_DEPOSIT = Coin.valueOf(50_000); // 10 USD @ 20000 USD/BTC + MIN_BUYER_SECURITY_DEPOSIT = Coin.parseCoin("0.001"); // 0.001 BTC about 4 USD @ 4000 USD/BTC return MIN_BUYER_SECURITY_DEPOSIT; } - public static Coin getDefaultBuyerSecurityDeposit() { - if (DEFAULT_BUYER_SECURITY_DEPOSIT == null) - DEFAULT_BUYER_SECURITY_DEPOSIT = Coin.valueOf(1_000_000); // 200 EUR @ 20000 USD/BTC - return DEFAULT_BUYER_SECURITY_DEPOSIT; + + public static double getSellerSecurityDepositAsPercent() { + return 0.005; // 0.5% of trade amount. } - public static Coin getSellerSecurityDeposit() { + public static Coin getMinSellerSecurityDepositAsCoin() { if (SELLER_SECURITY_DEPOSIT == null) - SELLER_SECURITY_DEPOSIT = Coin.valueOf(300_000); // 60 USD @ 20000 USD/BTC + SELLER_SECURITY_DEPOSIT = Coin.parseCoin("0.005"); // 0.005 BTC about 20 USD @ 4000 USD/BTC return SELLER_SECURITY_DEPOSIT; } } 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 69a5162568a..9e3b2e2990f 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -29,8 +29,6 @@ import bisq.core.btc.setup.WalletsSetup; import bisq.core.locale.Res; -import bisq.common.app.Log; - import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Coin; @@ -176,16 +174,17 @@ public Transaction createBtcTradingFeeTx(Address fundingAddress, Coin tradingFee, Coin txFee, String feeReceiverAddresses, - TxBroadcaster.Callback callback) + boolean doBroadcast, + @Nullable TxBroadcaster.Callback callback) throws InsufficientMoneyException, AddressFormatException { - log.debug("fundingAddress " + fundingAddress.toString()); - log.debug("reservedForTradeAddress " + reservedForTradeAddress.toString()); - log.debug("changeAddress " + changeAddress.toString()); - log.info("reservedFundsForOffer " + reservedFundsForOffer.toPlainString()); - log.debug("useSavingsWallet " + useSavingsWallet); - log.info("tradingFee " + tradingFee.toPlainString()); - log.info("txFee " + txFee.toPlainString()); - log.debug("feeReceiverAddresses " + feeReceiverAddresses); + log.debug("fundingAddress {}", fundingAddress.toString()); + log.debug("reservedForTradeAddress {}", reservedForTradeAddress.toString()); + log.debug("changeAddress {}", changeAddress.toString()); + log.info("reservedFundsForOffer {}", reservedFundsForOffer.toPlainString()); + log.debug("useSavingsWallet {}", useSavingsWallet); + log.info("tradingFee {}", tradingFee.toPlainString()); + log.info("txFee {}", txFee.toPlainString()); + log.debug("feeReceiverAddresses {}", feeReceiverAddresses); Transaction tradingFeeTx = new Transaction(params); SendRequest sendRequest = null; try { @@ -216,7 +215,8 @@ public Transaction createBtcTradingFeeTx(Address fundingAddress, wallet.completeTx(sendRequest); WalletService.printTx("tradingFeeTx", tradingFeeTx); - broadcastTx(tradingFeeTx, callback); + if (doBroadcast && callback != null) + broadcastTx(tradingFeeTx, callback); return tradingFeeTx; } catch (Throwable t) { @@ -240,12 +240,12 @@ public Transaction completeBsqTradingFeeTx(Transaction preparedBsqTx, TransactionVerificationException, WalletException, InsufficientMoneyException, AddressFormatException { - log.debug("preparedBsqTx " + preparedBsqTx.toString()); - log.debug("fundingAddress " + fundingAddress.toString()); - log.debug("changeAddress " + changeAddress.toString()); - log.debug("reservedFundsForOffer " + reservedFundsForOffer.toPlainString()); - log.debug("useSavingsWallet " + useSavingsWallet); - log.debug("txFee " + txFee.toPlainString()); + log.debug("preparedBsqTx {}", preparedBsqTx.toString()); + log.debug("fundingAddress {}", fundingAddress.toString()); + log.debug("changeAddress {}", changeAddress.toString()); + log.debug("reservedFundsForOffer {}", reservedFundsForOffer.toPlainString()); + log.debug("useSavingsWallet {}", useSavingsWallet); + log.debug("txFee {}", txFee.toPlainString()); // preparedBsqTx has following structure: // inputs [1-n] BSQ inputs @@ -327,19 +327,19 @@ public Transaction completeBsqTradingFeeTx(Transaction preparedBsqTx, * The taker creates a dummy transaction to get the input(s) and optional change output for the amount and the takersAddress for that trade. * That will be used to send to the maker for creating the deposit transaction. * + * + * @param takeOfferFeeTx The take offer fee tx * @param inputAmount Amount of takers input * @param txFee Mining fee - * @param takersAddress Address of taker * @return A data container holding the inputs, the output value and address * @throws TransactionVerificationException - * @throws WalletException */ - public InputsAndChangeOutput takerCreatesDepositsTxInputs(Coin inputAmount, Coin txFee, Address takersAddress, Address takersChangeAddress) throws - TransactionVerificationException, WalletException { + public InputsAndChangeOutput takerCreatesDepositsTxInputs(Transaction takeOfferFeeTx, Coin inputAmount, Coin txFee, Address takersAddress) throws + TransactionVerificationException { log.debug("takerCreatesDepositsTxInputs called"); - log.debug("inputAmount " + inputAmount.toFriendlyString()); - log.debug("txFee " + txFee.toFriendlyString()); - log.debug("takersAddress " + takersAddress.toString()); + log.debug("inputAmount {}", inputAmount.toFriendlyString()); + log.debug("txFee {}", txFee.toFriendlyString()); + log.debug("takersAddress {}", takersAddress.toString()); // We add the mining fee 2 times to the deposit tx: // 1. Will be spent when publishing the deposit tx (paid by buyer) @@ -349,15 +349,13 @@ public InputsAndChangeOutput takerCreatesDepositsTxInputs(Coin inputAmount, Coin /* The tx we create has that structure: - IN[0] any input > inputAmount (including tx fee) (unsigned) - IN[1...n] optional inputs supported, but normally there is just 1 input (unsigned) + IN[0] input from taker fee tx > inputAmount (including tx fee) (unsigned) OUT[0] dummyOutputAmount (inputAmount - tx fee) - OUT[1] Optional Change = inputAmount - dummyOutputAmount - tx fee - We are only interested in the inputs and the optional change output. + We are only interested in the inputs. + We get the exact input value from the taker fee tx so we don't create a change output. */ - // inputAmount includes the tx fee. So we subtract the fee to get the dummyOutputAmount. Coin dummyOutputAmount = inputAmount.subtract(txFee); @@ -370,11 +368,12 @@ OUT[0] dummyOutputAmount (inputAmount - tx fee) // Find the needed inputs to pay the output, optionally add 1 change output. // Normally only 1 input and no change output is used, but we support multiple inputs and 1 change output. // Our spending transaction output is from the create offer fee payment. - addAvailableInputsAndChangeOutputs(dummyTX, takersAddress, takersChangeAddress, txFee); - // The completeTx() call signs the input, but we don't want to pass over signed tx inputs so we remove the signature - WalletService.removeSignatures(dummyTX); + // We created the take offer fee tx in the structure that the second output is for the funds for the deposit tx. + TransactionOutput reservedForTradeOutput = takeOfferFeeTx.getOutputs().get(1); + dummyTX.addInput(reservedForTradeOutput); + WalletService.removeSignatures(dummyTX); WalletService.verifyTransaction(dummyTX); //WalletService.printTx("dummyTX", dummyTX); @@ -388,20 +387,13 @@ OUT[0] dummyOutputAmount (inputAmount - tx fee) }) .collect(Collectors.toList()); - // We don't support more then 1 change outputs, so there are max. 2 outputs - checkArgument(dummyTX.getOutputs().size() < 3); - // Only interested in optional change output, the dummy output at index 0 is ignored (that's why we use index 1) - TransactionOutput changeOutput = dummyTX.getOutputs().size() == 2 ? dummyTX.getOutputs().get(1) : null; - long changeOutputValue = 0L; - String changeOutputAddress = null; - if (changeOutput != null) { - changeOutputValue = changeOutput.getValue().getValue(); - Address addressFromP2PKHScript = changeOutput.getAddressFromP2PKHScript(params); - checkNotNull(addressFromP2PKHScript, "changeOutput.getAddressFromP2PKHScript(params) must not be null"); - changeOutputAddress = addressFromP2PKHScript.toString(); - } - return new InputsAndChangeOutput(new ArrayList<>(rawTransactionInputList), changeOutputValue, changeOutputAddress); + // TODO changeOutputValue and changeOutputAddress is not used as taker spends exact amount from fee tx. + // Change is handled already at the fee tx creation so the handling of a change output for the deposit tx + // can be removed here. We still keep it atm as we prefer to not introduce a larger + // refactoring. When new trade protocol gets implemented this can be cleaned. + // The maker though can have a change output if the taker takes less as the max. offer amount! + return new InputsAndChangeOutput(new ArrayList<>(rawTransactionInputList), 0, null); } /** @@ -438,17 +430,17 @@ public PreparedDepositTxAndMakerInputs makerCreatesAndSignsDepositTx(boolean mak byte[] arbitratorPubKey) throws SigningException, TransactionVerificationException, WalletException, AddressFormatException { log.debug("makerCreatesAndSignsDepositTx called"); - log.debug("makerIsBuyer " + makerIsBuyer); - log.debug("makerInputAmount " + makerInputAmount.toFriendlyString()); - log.debug("msOutputAmount " + msOutputAmount.toFriendlyString()); - log.debug("takerRawInputs " + takerRawTransactionInputs.toString()); - log.debug("takerChangeOutputValue " + takerChangeOutputValue); - log.debug("takerChangeAddressString " + takerChangeAddressString); - log.debug("makerAddress " + makerAddress); - log.debug("makerChangeAddress " + makerChangeAddress); - log.debug("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString()); - log.debug("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString()); - log.debug("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString()); + log.debug("makerIsBuyer {}", makerIsBuyer); + log.debug("makerInputAmount {}", makerInputAmount.toFriendlyString()); + log.debug("msOutputAmount {}", msOutputAmount.toFriendlyString()); + log.debug("takerRawInputs {}", takerRawTransactionInputs.toString()); + log.debug("takerChangeOutputValue {}", takerChangeOutputValue); + log.debug("takerChangeAddressString {}", takerChangeAddressString); + log.debug("makerAddress {}", makerAddress); + log.debug("makerChangeAddress {}", makerChangeAddress); + log.debug("buyerPubKey {}", ECKey.fromPublicOnly(buyerPubKey).toString()); + log.debug("sellerPubKey {}", ECKey.fromPublicOnly(sellerPubKey).toString()); + log.debug("arbitratorPubKey {}", ECKey.fromPublicOnly(arbitratorPubKey).toString()); checkArgument(!takerRawTransactionInputs.isEmpty()); @@ -582,13 +574,13 @@ public Transaction takerSignsAndPublishesDepositTx(boolean takerIsSeller, Transaction makersDepositTx = new Transaction(params, makersDepositTxSerialized); log.debug("signAndPublishDepositTx called"); - log.debug("takerIsSeller " + takerIsSeller); - log.debug("makersDepositTx " + makersDepositTx.toString()); - log.debug("buyerConnectedOutputsForAllInputs " + buyerInputs.toString()); - log.debug("sellerConnectedOutputsForAllInputs " + sellerInputs.toString()); - log.debug("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString()); - log.debug("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString()); - log.debug("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString()); + log.debug("takerIsSeller {}", takerIsSeller); + log.debug("makersDepositTx {}", makersDepositTx.toString()); + log.debug("buyerConnectedOutputsForAllInputs {}", buyerInputs.toString()); + log.debug("sellerConnectedOutputsForAllInputs {}", sellerInputs.toString()); + log.debug("buyerPubKey {}", ECKey.fromPublicOnly(buyerPubKey).toString()); + log.debug("sellerPubKey {}", ECKey.fromPublicOnly(sellerPubKey).toString()); + log.debug("arbitratorPubKey {}", ECKey.fromPublicOnly(arbitratorPubKey).toString()); checkArgument(!buyerInputs.isEmpty()); checkArgument(!sellerInputs.isEmpty()); @@ -626,9 +618,9 @@ public Transaction takerSignsAndPublishesDepositTx(boolean takerIsSeller, // Check if OP_RETURN output with contract hash matches the one from the maker TransactionOutput contractHashOutput = new TransactionOutput(params, makersDepositTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(contractHash).getProgram()); - log.debug("contractHashOutput " + contractHashOutput); + log.debug("contractHashOutput {}", contractHashOutput); TransactionOutput makersContractHashOutput = makersDepositTx.getOutputs().get(1); - log.debug("makersContractHashOutput " + makersContractHashOutput); + log.debug("makersContractHashOutput {}", makersContractHashOutput); if (!makersContractHashOutput.getScriptPubKey().equals(contractHashOutput.getScriptPubKey())) throw new TransactionVerificationException("Maker's transaction output for the contract hash is not matching takers version."); @@ -683,11 +675,11 @@ public byte[] buyerSignsPayoutTx(Transaction depositTx, byte[] arbitratorPubKey) throws AddressFormatException, TransactionVerificationException { log.trace("sellerSignsPayoutTx called"); - log.trace("depositTx " + depositTx.toString()); - log.trace("buyerPayoutAmount " + buyerPayoutAmount.toFriendlyString()); - log.trace("sellerPayoutAmount " + sellerPayoutAmount.toFriendlyString()); - log.trace("buyerPayoutAddressString " + buyerPayoutAddressString); - log.trace("sellerPayoutAddressString " + sellerPayoutAddressString); + log.trace("depositTx {}", depositTx.toString()); + log.trace("buyerPayoutAmount {}", buyerPayoutAmount.toFriendlyString()); + log.trace("sellerPayoutAmount {}", sellerPayoutAmount.toFriendlyString()); + log.trace("buyerPayoutAddressString {}", buyerPayoutAddressString); + log.trace("sellerPayoutAddressString {}", sellerPayoutAddressString); log.trace("multiSigKeyPair (not displayed for security reasons)"); log.info("buyerPubKey HEX=" + ECKey.fromPublicOnly(buyerPubKey).getPublicKeyAsHex()); log.info("sellerPubKey HEX=" + ECKey.fromPublicOnly(sellerPubKey).getPublicKeyAsHex()); @@ -745,17 +737,17 @@ public Transaction sellerSignsAndFinalizesPayoutTx(Transaction depositTx, byte[] arbitratorPubKey) throws AddressFormatException, TransactionVerificationException, WalletException { log.trace("buyerSignsAndFinalizesPayoutTx called"); - log.trace("depositTx " + depositTx.toString()); - log.trace("buyerSignature r " + ECKey.ECDSASignature.decodeFromDER(buyerSignature).r.toString()); - log.trace("buyerSignature s " + ECKey.ECDSASignature.decodeFromDER(buyerSignature).s.toString()); - log.trace("buyerPayoutAmount " + buyerPayoutAmount.toFriendlyString()); - log.trace("sellerPayoutAmount " + sellerPayoutAmount.toFriendlyString()); - log.trace("buyerPayoutAddressString " + buyerPayoutAddressString); - log.trace("sellerPayoutAddressString " + sellerPayoutAddressString); + log.trace("depositTx {}", depositTx.toString()); + log.trace("buyerSignature r {}", ECKey.ECDSASignature.decodeFromDER(buyerSignature).r.toString()); + log.trace("buyerSignature s {}", ECKey.ECDSASignature.decodeFromDER(buyerSignature).s.toString()); + log.trace("buyerPayoutAmount {}", buyerPayoutAmount.toFriendlyString()); + log.trace("sellerPayoutAmount {}", sellerPayoutAmount.toFriendlyString()); + log.trace("buyerPayoutAddressString {}", buyerPayoutAddressString); + log.trace("sellerPayoutAddressString {}", sellerPayoutAddressString); log.trace("multiSigKeyPair (not displayed for security reasons)"); - log.info("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString()); - log.info("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString()); - log.info("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString()); + log.info("buyerPubKey {}", ECKey.fromPublicOnly(buyerPubKey).toString()); + log.info("sellerPubKey {}", ECKey.fromPublicOnly(sellerPubKey).toString()); + log.info("arbitratorPubKey {}", ECKey.fromPublicOnly(arbitratorPubKey).toString()); Transaction payoutTx = createPayoutTx(depositTx, buyerPayoutAmount, @@ -825,15 +817,15 @@ public byte[] arbitratorSignsDisputedPayoutTx(byte[] depositTxSerialized, throws AddressFormatException, TransactionVerificationException { Transaction depositTx = new Transaction(params, depositTxSerialized); log.trace("signDisputedPayoutTx called"); - log.trace("depositTx " + depositTx.toString()); - log.trace("buyerPayoutAmount " + buyerPayoutAmount.toFriendlyString()); - log.trace("sellerPayoutAmount " + sellerPayoutAmount.toFriendlyString()); - log.trace("buyerAddressString " + buyerAddressString); - log.trace("sellerAddressString " + sellerAddressString); + log.trace("depositTx {}", depositTx.toString()); + log.trace("buyerPayoutAmount {}", buyerPayoutAmount.toFriendlyString()); + log.trace("sellerPayoutAmount {}", sellerPayoutAmount.toFriendlyString()); + log.trace("buyerAddressString {}", buyerAddressString); + log.trace("sellerAddressString {}", sellerAddressString); log.trace("arbitratorKeyPair (not displayed for security reasons)"); - log.info("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString()); - log.info("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString()); - log.info("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString()); + log.info("buyerPubKey {}", ECKey.fromPublicOnly(buyerPubKey).toString()); + log.info("sellerPubKey {}", ECKey.fromPublicOnly(sellerPubKey).toString()); + log.info("arbitratorPubKey {}", ECKey.fromPublicOnly(arbitratorPubKey).toString()); // Our MS is index 0 TransactionOutput p2SHMultiSigOutput = depositTx.getOutput(0); @@ -892,17 +884,17 @@ public Transaction traderSignAndFinalizeDisputedPayoutTx(byte[] depositTxSeriali Transaction depositTx = new Transaction(params, depositTxSerialized); log.trace("signAndFinalizeDisputedPayoutTx called"); - log.trace("depositTx " + depositTx); - log.trace("arbitratorSignature r " + ECKey.ECDSASignature.decodeFromDER(arbitratorSignature).r.toString()); - log.trace("arbitratorSignature s " + ECKey.ECDSASignature.decodeFromDER(arbitratorSignature).s.toString()); - log.trace("buyerPayoutAmount " + buyerPayoutAmount.toFriendlyString()); - log.trace("sellerPayoutAmount " + sellerPayoutAmount.toFriendlyString()); - log.trace("buyerAddressString " + buyerAddressString); - log.trace("sellerAddressString " + sellerAddressString); + log.trace("depositTx {}", depositTx); + log.trace("arbitratorSignature r {}", ECKey.ECDSASignature.decodeFromDER(arbitratorSignature).r.toString()); + log.trace("arbitratorSignature s {}", ECKey.ECDSASignature.decodeFromDER(arbitratorSignature).s.toString()); + log.trace("buyerPayoutAmount {}", buyerPayoutAmount.toFriendlyString()); + log.trace("sellerPayoutAmount {}", sellerPayoutAmount.toFriendlyString()); + log.trace("buyerAddressString {}", buyerAddressString); + log.trace("sellerAddressString {}", sellerAddressString); log.trace("tradersMultiSigKeyPair (not displayed for security reasons)"); - log.info("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString()); - log.info("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString()); - log.info("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString()); + log.info("buyerPubKey {}", ECKey.fromPublicOnly(buyerPubKey).toString()); + log.info("sellerPubKey {}", ECKey.fromPublicOnly(sellerPubKey).toString()); + log.info("arbitratorPubKey {}", ECKey.fromPublicOnly(arbitratorPubKey).toString()); TransactionOutput p2SHMultiSigOutput = depositTx.getOutput(0); @@ -961,20 +953,20 @@ public Transaction emergencySignAndPublishPayoutTx(String depositTxHex, TxBroadcaster.Callback callback) throws AddressFormatException, TransactionVerificationException, WalletException { log.info("signAndPublishPayoutTx called"); - log.info("depositTxHex " + depositTxHex); - log.info("buyerPayoutAmount " + buyerPayoutAmount.toFriendlyString()); - log.info("sellerPayoutAmount " + sellerPayoutAmount.toFriendlyString()); - log.info("arbitratorPayoutAmount " + arbitratorPayoutAmount.toFriendlyString()); - log.info("buyerAddressString " + buyerAddressString); - log.info("sellerAddressString " + sellerAddressString); - log.info("arbitratorAddressString " + arbitratorAddressString); + log.info("depositTxHex {}", depositTxHex); + log.info("buyerPayoutAmount {}", buyerPayoutAmount.toFriendlyString()); + log.info("sellerPayoutAmount {}", sellerPayoutAmount.toFriendlyString()); + log.info("arbitratorPayoutAmount {}", arbitratorPayoutAmount.toFriendlyString()); + log.info("buyerAddressString {}", buyerAddressString); + log.info("sellerAddressString {}", sellerAddressString); + log.info("arbitratorAddressString {}", arbitratorAddressString); log.info("buyerPrivateKeyAsHex (not displayed for security reasons)"); log.info("sellerPrivateKeyAsHex (not displayed for security reasons)"); log.info("arbitratorPrivateKeyAsHex (not displayed for security reasons)"); - log.info("buyerPubKeyAsHex " + buyerPubKeyAsHex); - log.info("sellerPubKeyAsHex " + sellerPubKeyAsHex); - log.info("arbitratorPubKeyAsHex " + arbitratorPubKeyAsHex); - log.info("P2SHMultiSigOutputScript " + P2SHMultiSigOutputScript); + log.info("buyerPubKeyAsHex {}", buyerPubKeyAsHex); + log.info("sellerPubKeyAsHex {}", sellerPubKeyAsHex); + log.info("arbitratorPubKeyAsHex {}", arbitratorPubKeyAsHex); + log.info("P2SHMultiSigOutputScript {}", P2SHMultiSigOutputScript); checkNotNull((buyerPrivateKeyAsHex != null || sellerPrivateKeyAsHex != null), "either buyerPrivateKeyAsHex or sellerPrivateKeyAsHex must not be null"); @@ -1062,8 +1054,6 @@ public void broadcastTx(Transaction tx, TxBroadcaster.Callback callback, int tim * @throws VerificationException */ public Transaction addTxToWallet(Transaction transaction) throws VerificationException { - Log.traceCall("transaction " + transaction.toString()); - // We need to recreate the transaction otherwise we get a null pointer... Transaction result = new Transaction(params, transaction.bitcoinSerialize()); result.getConfidence(Context.get()).setSource(TransactionConfidence.Source.SELF); @@ -1079,12 +1069,9 @@ public Transaction addTxToWallet(Transaction transaction) throws VerificationExc * @throws VerificationException */ public Transaction addTxToWallet(byte[] serializedTransaction) throws VerificationException { - Log.traceCall(); - // We need to recreate the tx otherwise we get a null pointer... Transaction transaction = new Transaction(params, serializedTransaction); transaction.getConfidence(Context.get()).setSource(TransactionConfidence.Source.NETWORK); - log.trace("transaction from serializedTransaction: " + transaction.toString()); if (wallet != null) wallet.receivePending(transaction, null, true); diff --git a/core/src/main/java/bisq/core/btc/wallet/TxBroadcaster.java b/core/src/main/java/bisq/core/btc/wallet/TxBroadcaster.java index 30a5973b991..6ed1d0653d0 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TxBroadcaster.java +++ b/core/src/main/java/bisq/core/btc/wallet/TxBroadcaster.java @@ -19,7 +19,6 @@ import bisq.core.btc.exceptions.TxBroadcastException; import bisq.core.btc.exceptions.TxBroadcastTimeoutException; -import bisq.core.btc.exceptions.TxMalleabilityException; import bisq.common.Timer; import bisq.common.UserThread; @@ -48,35 +47,21 @@ public interface Callback { default void onTimeout(TxBroadcastTimeoutException exception) { Transaction tx = exception.getLocalTx(); if (tx != null) { - log.warn("TxBroadcaster.onTimeout called: {} \n" + - "We optimistically assume that the tx broadcast succeeds later and call onSuccess on the " + - "callback handler. This behaviour carries less potential problems than if we would trigger " + - "a failure (e.g. which would cause a failed create offer attempt of failed take offer attempt).\n" + - "We have no guarantee how long it will take to get the information that sufficiently many BTC " + - "nodes have reported back to BitcoinJ that the tx is in their mempool.\n" + - "In normal situations " + - "that's very fast but in some cases it can take minutes (mostly related to Tor connection " + - "issues). So if we just go on in the application logic and treat it as successful and the " + - "tx will be broadcast successfully later all is fine.\n" + - "If it will fail to get broadcast, " + - "it will lead to a failure state, the same as if we would trigger a failure due the timeout." + - "So we can assume that this behaviour will lead to less problems as otherwise.\n" + - "Long term we should implement better monitoring for Tor and the provided Bitcoin nodes to " + - "find out why those delays happen and add some rollback behaviour to the app state in case " + - "the tx will never get broadcast.", - exception.toString()); - - // The wallet.maybeCommitTx() call is required in case the tx is spent by a follow up tx as otherwise there would be an - // InsufficientMoneyException thrown. But in some test scenarios we also got issues with wallet - // inconsistency if the tx was committed twice. It should be prevented by the maybeCommitTx methods but - // not 100% if that is always the case. Just added that comment to make clear that this might be a risky - // strategy and might need improvement if we get problems. - // UPDATE: We got reported an wallet problem that a tx was added twice and wallet could not be loaded anymore even after a SPV resync. - // So it seems that this trategy is too risky to cause more problems as it tries to solve. - // Need more work from a BitcoinJ expert! For now we comment the call out here but leave it as reference - // for future improvements. - // exception.getWallet().maybeCommitTx(tx); - + // We optimistically assume that the tx broadcast succeeds later and call onSuccess on the callback handler. + // This behaviour carries less potential problems than if we would trigger a failure (e.g. which would cause + // a failed create offer attempt or failed take offer attempt). + // We have no guarantee how long it will take to get the information that sufficiently many BTC nodes have + // reported back to BitcoinJ that the tx is in their mempool. + // In normal situations that's very fast but in some cases it can take minutes (mostly related to Tor + // connection issues). So if we just go on in the application logic and treat it as successful and the + // tx will be broadcast successfully later all is fine. + // If it will fail to get broadcast, it will lead to a failure state, the same as if we would trigger a + // failure due the timeout. + // So we can assume that this behaviour will lead to less problems as otherwise. + // Long term we should implement better monitoring for Tor and the provided Bitcoin nodes to find out + // why those delays happen and add some rollback behaviour to the app state in case the tx will never + // get broadcast. + log.warn("TxBroadcaster.onTimeout called: {}", exception.toString()); onSuccess(tx); } else { log.error("TxBroadcaster.onTimeout: Tx is null. exception={} ", exception.toString()); @@ -84,11 +69,6 @@ default void onTimeout(TxBroadcastTimeoutException exception) { } } - default void onTxMalleability(TxMalleabilityException exception) { - log.error("onTxMalleability.onTimeout " + exception.toString()); - onFailure(exception); - } - void onFailure(TxBroadcastException exception); } @@ -118,30 +98,23 @@ public static void broadcastTx(Wallet wallet, PeerGroup peerGroup, Transaction t "which has an open timeoutTimer. txId=" + txId, txId))); } + // We decided the least risky scenario is to commit the tx to the wallet and broadcast it later. + // If it's a bsq tx WalletManager.publishAndCommitBsqTx() should have commited the tx to both bsq and btc + // wallets so the next line causes no effect. + // If it's a btc tx, the next line adds the tx to the wallet. + wallet.maybeCommitTx(tx); + Futures.addCallback(peerGroup.broadcastTransaction(tx).future(), new FutureCallback() { @Override public void onSuccess(@Nullable Transaction result) { - if (result != null) { - if (txId.equals(result.getHashAsString())) { - // We expect that there is still a timeout in our map, otherwise the timeout got triggered - if (broadcastTimerMap.containsKey(txId)) { - wallet.maybeCommitTx(tx); - stopAndRemoveTimer(txId); - // At regtest we get called immediately back but we want to make sure that the handler is not called - // before the caller is finished. - UserThread.execute(() -> callback.onSuccess(tx)); - } else { - stopAndRemoveTimer(txId); - log.warn("We got an onSuccess callback for a broadcast which already triggered the timeout.", txId); - } - } else { - stopAndRemoveTimer(txId); - UserThread.execute(() -> callback.onTxMalleability(new TxMalleabilityException(tx, result))); - } - } else { + // We expect that there is still a timeout in our map, otherwise the timeout got triggered + if (broadcastTimerMap.containsKey(txId)) { stopAndRemoveTimer(txId); - UserThread.execute(() -> callback.onFailure(new TxBroadcastException("Transaction returned from the " + - "broadcastTransaction call back is null.", txId))); + // At regtest we get called immediately back but we want to make sure that the handler is not called + // before the caller is finished. + UserThread.execute(() -> callback.onSuccess(tx)); + } else { + log.warn("We got an onSuccess callback for a broadcast which already triggered the timeout.", txId); } } 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 43b77805ad3..d1ec44248da 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -197,7 +197,6 @@ public void removeBalanceListener(BalanceListener listener) { public static void checkWalletConsistency(Wallet wallet) throws WalletException { try { - log.trace("Check if wallet is consistent before commit."); checkNotNull(wallet); checkState(wallet.isConsistent()); } catch (Throwable t) { @@ -209,7 +208,6 @@ public static void checkWalletConsistency(Wallet wallet) throws WalletException public static void verifyTransaction(Transaction transaction) throws TransactionVerificationException { try { - log.trace("Verify transaction " + transaction); transaction.verify(); } catch (Throwable t) { t.printStackTrace(); @@ -226,7 +224,6 @@ public static void checkAllScriptSignaturesForTx(Transaction transaction) throws public static void checkScriptSig(Transaction transaction, TransactionInput input, int inputIndex) throws TransactionVerificationException { try { - log.trace("Verifies that this script (interpreted as a scriptSig) correctly spends the given scriptPubKey. Check input at index: " + inputIndex); checkNotNull(input.getConnectedOutput(), "input.getConnectedOutput() must not be null"); input.getScriptSig().correctlySpends(transaction, inputIndex, input.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); } catch (Throwable t) { @@ -592,6 +589,7 @@ public boolean isEncrypted() { } public List getRecentTransactions(int numTransactions, boolean includeDead) { + // Returns a list ordered by tx.getUpdateTime() desc return wallet.getRecentTransactions(numTransactions, includeDead); } diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java index 21c29f132a8..15323d4e704 100644 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ b/core/src/main/java/bisq/core/dao/DaoFacade.java @@ -19,7 +19,6 @@ import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.ballot.BallotListPresentation; import bisq.core.dao.governance.ballot.BallotListService; import bisq.core.dao.governance.blindvote.BlindVoteConsensus; @@ -30,7 +29,6 @@ import bisq.core.dao.governance.bond.reputation.BondedReputationRepository; import bisq.core.dao.governance.bond.reputation.MyBondedReputation; import bisq.core.dao.governance.bond.reputation.MyBondedReputationRepository; -import bisq.core.dao.governance.bond.reputation.MyReputationListService; import bisq.core.dao.governance.bond.role.BondedRole; import bisq.core.dao.governance.bond.role.BondedRolesRepository; import bisq.core.dao.governance.bond.unlock.UnlockTxService; @@ -41,6 +39,8 @@ import bisq.core.dao.governance.proposal.MyProposalListService; import bisq.core.dao.governance.proposal.ProposalConsensus; import bisq.core.dao.governance.proposal.ProposalListPresentation; +import bisq.core.dao.governance.proposal.ProposalService; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.governance.proposal.compensation.CompensationConsensus; @@ -103,6 +103,7 @@ @Slf4j public class DaoFacade implements DaoSetupService { private final ProposalListPresentation proposalListPresentation; + private final ProposalService proposalService; private final BallotListService ballotListService; private final BallotListPresentation ballotListPresentation; private final MyProposalListService myProposalListService; @@ -119,7 +120,6 @@ public class DaoFacade implements DaoSetupService { private final RemoveAssetProposalFactory removeAssetProposalFactory; private final BondedRolesRepository bondedRolesRepository; private final BondedReputationRepository bondedReputationRepository; - private final MyReputationListService myReputationListService; private final MyBondedReputationRepository myBondedReputationRepository; private final LockupTxService lockupTxService; private final UnlockTxService unlockTxService; @@ -130,6 +130,7 @@ public class DaoFacade implements DaoSetupService { @Inject public DaoFacade(MyProposalListService myProposalListService, ProposalListPresentation proposalListPresentation, + ProposalService proposalService, BallotListService ballotListService, BallotListPresentation ballotListPresentation, DaoStateService daoStateService, @@ -145,12 +146,12 @@ public DaoFacade(MyProposalListService myProposalListService, RemoveAssetProposalFactory removeAssetProposalFactory, BondedRolesRepository bondedRolesRepository, BondedReputationRepository bondedReputationRepository, - MyReputationListService myReputationListService, MyBondedReputationRepository myBondedReputationRepository, LockupTxService lockupTxService, UnlockTxService unlockTxService, DaoStateStorageService daoStateStorageService) { this.proposalListPresentation = proposalListPresentation; + this.proposalService = proposalService; this.ballotListService = ballotListService; this.ballotListPresentation = ballotListPresentation; this.myProposalListService = myProposalListService; @@ -167,7 +168,6 @@ public DaoFacade(MyProposalListService myProposalListService, this.removeAssetProposalFactory = removeAssetProposalFactory; this.bondedRolesRepository = bondedRolesRepository; this.bondedReputationRepository = bondedReputationRepository; - this.myReputationListService = myReputationListService; this.myBondedReputationRepository = myBondedReputationRepository; this.lockupTxService = lockupTxService; this.unlockTxService = unlockTxService; @@ -228,7 +228,7 @@ public ObservableList getActiveOrMyUnconfirmedProposals() { public ProposalWithTransaction getCompensationProposalWithTransaction(String name, String link, Coin requestedBsq) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { return compensationProposalFactory.createProposalWithTransaction(name, link, requestedBsq); @@ -237,7 +237,7 @@ public ProposalWithTransaction getCompensationProposalWithTransaction(String nam public ProposalWithTransaction getReimbursementProposalWithTransaction(String name, String link, Coin requestedBsq) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { return reimbursementProposalFactory.createProposalWithTransaction(name, link, requestedBsq); @@ -247,7 +247,7 @@ public ProposalWithTransaction getParamProposalWithTransaction(String name, String link, Param param, String paramValue) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { return changeParamProposalFactory.createProposalWithTransaction(name, link, param, @@ -257,27 +257,27 @@ public ProposalWithTransaction getParamProposalWithTransaction(String name, public ProposalWithTransaction getConfiscateBondProposalWithTransaction(String name, String link, String lockupTxId) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { return confiscateBondProposalFactory.createProposalWithTransaction(name, link, lockupTxId); } public ProposalWithTransaction getBondedRoleProposalWithTransaction(Role role) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { return roleProposalFactory.createProposalWithTransaction(role); } public ProposalWithTransaction getGenericProposalWithTransaction(String name, String link) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { return genericProposalFactory.createProposalWithTransaction(name, link); } public ProposalWithTransaction getRemoveAssetProposalWithTransaction(String name, String link, Asset asset) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { return removeAssetProposalFactory.createProposalWithTransaction(name, link, asset); } @@ -362,7 +362,7 @@ public Coin getBlindVoteFeeForCycle() { return BlindVoteConsensus.getFee(daoStateService, daoStateService.getChainHeight()); } - public Tuple2 getMiningFeeAndTxSize(Coin stake) + public Tuple2 getBlindVoteMiningFeeAndTxSize(Coin stake) throws WalletException, InsufficientMoneyException, TransactionVerificationException { return myBlindVoteListService.getMiningFeeAndTxSize(stake); } @@ -535,6 +535,12 @@ public List getAllBonds() { return bonds; } + public List getAllActiveBonds() { + List bonds = new ArrayList<>(bondedReputationRepository.getActiveBonds()); + bonds.addAll(bondedRolesRepository.getActiveBonds()); + return bonds; + } + public ObservableList getMyBondedReputations() { return myBondedReputationRepository.getMyBondedReputations(); } @@ -672,4 +678,12 @@ public boolean isMyRole(Role role) { public Optional getBondByLockupTxId(String lockupTxId) { return getAllBonds().stream().filter(e -> lockupTxId.equals(e.getLockupTxId())).findAny(); } + + public double getRequiredThreshold(Proposal proposal) { + return proposalService.getRequiredThreshold(proposal); + } + + public Coin getRequiredQuorum(Proposal proposal) { + return proposalService.getRequiredQuorum(proposal); + } } diff --git a/core/src/main/java/bisq/core/dao/DaoModule.java b/core/src/main/java/bisq/core/dao/DaoModule.java index 0807afd18bb..2fff6c55feb 100644 --- a/core/src/main/java/bisq/core/dao/DaoModule.java +++ b/core/src/main/java/bisq/core/dao/DaoModule.java @@ -41,7 +41,7 @@ import bisq.core.dao.governance.proposal.MyProposalListService; import bisq.core.dao.governance.proposal.ProposalListPresentation; import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.ProposalValidator; +import bisq.core.dao.governance.proposal.ProposalValidatorProvider; import bisq.core.dao.governance.proposal.compensation.CompensationProposalFactory; import bisq.core.dao.governance.proposal.compensation.CompensationValidator; import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposalFactory; @@ -135,7 +135,8 @@ protected void configure() { bind(ProposalStorageService.class).in(Singleton.class); bind(TempProposalStore.class).in(Singleton.class); bind(TempProposalStorageService.class).in(Singleton.class); - bind(ProposalValidator.class).in(Singleton.class); + + bind(ProposalValidatorProvider.class).in(Singleton.class); bind(CompensationValidator.class).in(Singleton.class); bind(CompensationProposalFactory.class).in(Singleton.class); @@ -158,7 +159,6 @@ protected void configure() { bind(RemoveAssetValidator.class).in(Singleton.class); bind(RemoveAssetProposalFactory.class).in(Singleton.class); - // Ballot bind(BallotListService.class).in(Singleton.class); bind(BallotListPresentation.class).in(Singleton.class); @@ -189,6 +189,9 @@ protected void configure() { Integer genesisBlockHeight = environment.getProperty(DaoOptionKeys.GENESIS_BLOCK_HEIGHT, Integer.class, -1); bind(Integer.class).annotatedWith(Names.named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT)).toInstance(genesisBlockHeight); + Integer genesisTotalSupply = environment.getProperty(DaoOptionKeys.GENESIS_TOTAL_SUPPLY, Integer.class, -1); + bind(Integer.class).annotatedWith(Names.named(DaoOptionKeys.GENESIS_TOTAL_SUPPLY)).toInstance(genesisTotalSupply); + // Bonds bind(LockupTxService.class).in(Singleton.class); bind(UnlockTxService.class).in(Singleton.class); diff --git a/core/src/main/java/bisq/core/dao/DaoOptionKeys.java b/core/src/main/java/bisq/core/dao/DaoOptionKeys.java index 48d5b40dffd..945fd51adad 100644 --- a/core/src/main/java/bisq/core/dao/DaoOptionKeys.java +++ b/core/src/main/java/bisq/core/dao/DaoOptionKeys.java @@ -30,5 +30,6 @@ public class DaoOptionKeys { public static final String FULL_DAO_NODE = "fullDaoNode"; public static final String GENESIS_TX_ID = "genesisTxId"; public static final String GENESIS_BLOCK_HEIGHT = "genesisBlockHeight"; + public static final String GENESIS_TOTAL_SUPPLY = "genesisTotalSupply"; public static final String DAO_ACTIVATED = "daoActivated"; } diff --git a/core/src/main/java/bisq/core/dao/exceptions/ValidationException.java b/core/src/main/java/bisq/core/dao/exceptions/ValidationException.java deleted file mode 100644 index 9869254fa9b..00000000000 --- a/core/src/main/java/bisq/core/dao/exceptions/ValidationException.java +++ /dev/null @@ -1,48 +0,0 @@ -package bisq.core.dao.exceptions; - -import bisq.core.dao.state.model.blockchain.Tx; - -import org.bitcoinj.core.Coin; - -import lombok.Getter; - -import javax.annotation.Nullable; - -@Getter -public class ValidationException extends Exception { - @Nullable - private Coin requestedBsq; - @Nullable - private Coin minRequestAmount; - @Nullable - private Tx tx; - - public ValidationException(String message, Coin requestedBsq, Coin minRequestAmount) { - super(message); - this.requestedBsq = requestedBsq; - this.minRequestAmount = minRequestAmount; - } - - public ValidationException(Throwable cause) { - super(cause); - } - - public ValidationException(String message) { - super(message); - } - - public ValidationException(String message, Throwable throwable) { - super(message, throwable); - - } - - public ValidationException(String message, Tx tx) { - super(message); - this.tx = tx; - } - - public ValidationException(Throwable cause, Tx tx) { - super(cause); - this.tx = tx; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java index bb18a0d089c..92b37ae4502 100644 --- a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java +++ b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java @@ -19,10 +19,12 @@ import bisq.core.dao.governance.period.PeriodService; import bisq.core.dao.governance.proposal.ProposalValidator; +import bisq.core.dao.governance.proposal.ProposalValidatorProvider; import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.governance.Ballot; +import bisq.core.dao.state.model.governance.Proposal; import com.google.inject.Inject; @@ -43,7 +45,7 @@ public class BallotListPresentation implements BallotListService.BallotListChangeListener, DaoStateListener { private final BallotListService ballotListService; private final PeriodService periodService; - private final ProposalValidator proposalValidator; + private final ProposalValidatorProvider proposalValidatorProvider; @Getter private final ObservableList allBallots = FXCollections.observableArrayList(); @@ -59,10 +61,10 @@ public class BallotListPresentation implements BallotListService.BallotListChang public BallotListPresentation(BallotListService ballotListService, PeriodService periodService, DaoStateService daoStateService, - ProposalValidator proposalValidator) { + ProposalValidatorProvider proposalValidatorProvider) { this.ballotListService = ballotListService; this.periodService = periodService; - this.proposalValidator = proposalValidator; + this.proposalValidatorProvider = proposalValidatorProvider; daoStateService.addBsqStateListener(this); ballotListService.addListener(this); @@ -97,8 +99,11 @@ public void onListChanged(List list) { // We cannot do a phase and cycle check as we are interested in historical ballots as well public List getAllValidBallots() { return allBallots.stream() - .filter(ballot -> proposalValidator.areDataFieldsValid(ballot.getProposal())) - .filter(ballot -> proposalValidator.isTxTypeValid(ballot.getProposal())) + .filter(ballot -> { + Proposal proposal = ballot.getProposal(); + ProposalValidator validator = proposalValidatorProvider.getValidator(proposal); + return validator.areDataFieldsValid(proposal) && validator.isTxTypeValid(proposal); + }) .collect(Collectors.toList()); } } diff --git a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java index 6c04a019c0a..1760ba3f956 100644 --- a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java +++ b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java @@ -20,7 +20,7 @@ import bisq.core.dao.DaoSetupService; import bisq.core.dao.governance.period.PeriodService; import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.ProposalValidator; +import bisq.core.dao.governance.proposal.ProposalValidatorProvider; import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; import bisq.core.dao.state.model.governance.Ballot; import bisq.core.dao.state.model.governance.BallotList; @@ -57,18 +57,20 @@ public interface BallotListChangeListener { private final ProposalService proposalService; private final PeriodService periodService; - private final ProposalValidator proposalValidator; + private final ProposalValidatorProvider validatorProvider; private final Storage storage; private final BallotList ballotList = new BallotList(); private final List listeners = new CopyOnWriteArrayList<>(); @Inject - public BallotListService(ProposalService proposalService, PeriodService periodService, - ProposalValidator proposalValidator, Storage storage) { + public BallotListService(ProposalService proposalService, + PeriodService periodService, + ProposalValidatorProvider validatorProvider, + Storage storage) { this.proposalService = proposalService; this.periodService = periodService; - this.proposalValidator = proposalValidator; + this.validatorProvider = validatorProvider; this.storage = storage; } @@ -152,13 +154,13 @@ public void addListener(BallotListChangeListener listener) { public List getValidatedBallotList() { return ballotList.stream() - .filter(ballot -> proposalValidator.isTxTypeValid(ballot.getProposal())) + .filter(ballot -> validatorProvider.getValidator(ballot.getProposal()).isTxTypeValid(ballot.getProposal())) .collect(Collectors.toList()); } public List getValidBallotsOfCycle() { return ballotList.stream() - .filter(ballot -> proposalValidator.isTxTypeValid(ballot.getProposal())) + .filter(ballot -> validatorProvider.getValidator(ballot.getProposal()).isTxTypeValid(ballot.getProposal())) .filter(ballot -> periodService.isTxInCorrectCycle(ballot.getTxId(), periodService.getChainHeight())) .collect(Collectors.toList()); } diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java index 5e256e69262..ebc800138b0 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java @@ -27,6 +27,11 @@ import com.google.protobuf.ByteString; +import org.springframework.util.CollectionUtils; + +import java.util.Map; +import java.util.Optional; + import lombok.Value; import lombok.extern.slf4j.Slf4j; @@ -48,14 +53,19 @@ public final class BlindVote implements PersistablePayload, NetworkPayload, Cons private final long stake; private byte[] encryptedMeritList; + // This hash map allows addition of data in future versions without breaking consensus + private final Map extraDataMap; + public BlindVote(byte[] encryptedVotes, String txId, long stake, - byte[] encryptedMeritList) { + byte[] encryptedMeritList, + Map extraDataMap) { this.encryptedVotes = encryptedVotes; this.txId = txId; this.stake = stake; this.encryptedMeritList = encryptedMeritList; + this.extraDataMap = extraDataMap; } @@ -71,7 +81,9 @@ public PB.BlindVote toProtoMessage() { @NotNull public PB.BlindVote.Builder getBuilder() { - return PB.BlindVote.newBuilder() + PB.BlindVote.Builder builder = PB.BlindVote.newBuilder(); + Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); + return builder .setEncryptedVotes(ByteString.copyFrom(encryptedVotes)) .setTxId(txId) .setStake(stake) @@ -82,7 +94,9 @@ public static BlindVote fromProto(PB.BlindVote proto) { return new BlindVote(proto.getEncryptedVotes().toByteArray(), proto.getTxId(), proto.getStake(), - proto.getEncryptedMeritList().toByteArray()); + proto.getEncryptedMeritList().toByteArray(), + CollectionUtils.isEmpty(proto.getExtraDataMap()) ? + null : proto.getExtraDataMap()); } @@ -96,6 +110,7 @@ public String toString() { ",\n txId='" + txId + '\'' + ",\n stake=" + stake + ",\n encryptedMeritList=" + Utilities.bytesAsHexString(encryptedMeritList) + + ",\n extraDataMap=" + extraDataMap + "\n}"; } } diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java index 8d5d2bdda4d..a23af032cbf 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java @@ -105,7 +105,7 @@ public void onParseBlockChainComplete() { @Override public void onAdded(PersistableNetworkPayload payload) { - onAppendOnlyDataAdded(payload); + onAppendOnlyDataAdded(payload, true); } @@ -126,10 +126,10 @@ public List getBlindVotesInPhaseAndCycle() { /////////////////////////////////////////////////////////////////////////////////////////// private void fillListFromAppendOnlyDataStore() { - p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().forEach(this::onAppendOnlyDataAdded); + p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().forEach(e -> onAppendOnlyDataAdded(e, false)); } - private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload) { + private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload, boolean doLog) { if (persistableNetworkPayload instanceof BlindVotePayload) { BlindVotePayload blindVotePayload = (BlindVotePayload) persistableNetworkPayload; if (!blindVotePayloads.contains(blindVotePayload)) { @@ -140,7 +140,9 @@ private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkP if (blindVoteValidator.areDataFieldsValid(blindVote)) { // We don't validate as we might receive blindVotes from other cycles or phases at startup. blindVotePayloads.add(blindVotePayload); - log.info("We received a blindVotePayload. blindVoteTxId={}", txId); + if (doLog) { + log.info("We received a blindVotePayload. blindVoteTxId={}", txId); + } } else { log.warn("We received an invalid blindVotePayload. blindVoteTxId={}", txId); } diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java index 9e4eb4c419b..26e62636860 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java @@ -17,7 +17,7 @@ package bisq.core.dao.governance.blindvote; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.period.PeriodService; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.Tx; @@ -53,7 +53,7 @@ public boolean areDataFieldsValid(BlindVote blindVote) { } } - private void validateDataFields(BlindVote blindVote) throws ValidationException { + private void validateDataFields(BlindVote blindVote) throws ProposalValidationException { try { checkNotNull(blindVote.getEncryptedVotes(), "encryptedProposalList must not be null"); checkArgument(blindVote.getEncryptedVotes().length > 0, @@ -68,7 +68,7 @@ private void validateDataFields(BlindVote blindVote) throws ValidationException //TODO should we use a min/max for stake, atm its just dust limit as the min. value } catch (Throwable e) { log.warn(e.toString()); - throw new ValidationException(e); + throw new ProposalValidationException(e); } } diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java b/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java index 806e2c54ce2..8d0ab1de9c8 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java @@ -72,6 +72,7 @@ import java.io.IOException; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Set; @@ -207,7 +208,11 @@ public void publishBlindVote(Coin stake, ResultHandler resultHandler, ExceptionH // blind vote stored and broadcasted to the p2p network. The tx might get re-broadcasted at a restart and // in worst case if it does not succeed the blind vote will be ignored anyway. // Inconsistently propagated blind votes in the p2p network could have potentially worse effects. - BlindVote blindVote = new BlindVote(encryptedVotes, blindVoteTxId, stake.value, encryptedMeritList); + BlindVote blindVote = new BlindVote(encryptedVotes, + blindVoteTxId, + stake.value, + encryptedMeritList, + new HashMap<>()); addBlindVoteToList(blindVote); addToP2PNetwork(blindVote, errorMessage -> { @@ -221,6 +226,8 @@ public void publishBlindVote(Coin stake, ResultHandler resultHandler, ExceptionH publishTx(resultHandler, exceptionHandler, blindVoteTx); } catch (CryptoException | TransactionVerificationException | InsufficientMoneyException | WalletException | IOException exception) { + log.error(exception.toString()); + exception.printStackTrace(); exceptionHandler.handleException(exception); } } 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 75e34d2bbc5..569796343b8 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 @@ -29,6 +29,7 @@ import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import javax.inject.Inject; @@ -164,22 +165,20 @@ private void connectToNextNode() { } private void connectToAnyFullNode() { - List required = new ArrayList<>(Collections.singletonList( - Capabilities.Capability.DAO_FULL_NODE.ordinal() - )); + Capabilities required = new Capabilities(Capability.DAO_FULL_NODE); List list = peerManager.getLivePeers(null).stream() - .filter(peer -> Capabilities.isCapabilitySupported(required, peer.getSupportedCapabilities())) + .filter(peer -> peer.getCapabilities().containsAll(required)) .collect(Collectors.toList()); if (list.isEmpty()) list = peerManager.getReportedPeers().stream() - .filter(peer -> Capabilities.isCapabilitySupported(required, peer.getSupportedCapabilities())) + .filter(peer -> peer.getCapabilities().containsAll(required)) .collect(Collectors.toList()); if (list.isEmpty()) list = peerManager.getPersistedPeers().stream() - .filter(peer -> Capabilities.isCapabilitySupported(required, peer.getSupportedCapabilities())) + .filter(peer -> peer.getCapabilities().containsAll(required)) .collect(Collectors.toList()); if (!list.isEmpty()) { diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/network/messages/RepublishGovernanceDataRequest.java b/core/src/main/java/bisq/core/dao/governance/blindvote/network/messages/RepublishGovernanceDataRequest.java index c11ac0500c7..420677bb86f 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/network/messages/RepublishGovernanceDataRequest.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/network/messages/RepublishGovernanceDataRequest.java @@ -21,15 +21,12 @@ import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.app.Version; import bisq.common.proto.network.NetworkEnvelope; import io.bisq.generated.protobuffer.PB; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import lombok.EqualsAndHashCode; import lombok.Getter; @@ -62,10 +59,8 @@ public static NetworkEnvelope fromProto(PB.RepublishGovernanceDataRequest proto, } @Override - public List getRequiredCapabilities() { - return new ArrayList<>(Collections.singletonList( - Capabilities.Capability.DAO_FULL_NODE.ordinal() - )); + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.DAO_FULL_NODE); } @Override diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVotePayload.java b/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVotePayload.java index ef081a1a874..0d0496d5f61 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVotePayload.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVotePayload.java @@ -25,6 +25,7 @@ import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.crypto.Hash; import bisq.common.proto.persistable.PersistableEnvelope; import bisq.common.util.Utilities; @@ -33,10 +34,7 @@ import com.google.protobuf.ByteString; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.concurrent.TimeUnit; import lombok.EqualsAndHashCode; @@ -134,10 +132,8 @@ public boolean isDateInTolerance() { /////////////////////////////////////////////////////////////////////////////////////////// @Override - public List getRequiredCapabilities() { - return new ArrayList<>(Collections.singletonList( - Capabilities.Capability.BLIND_VOTE.ordinal() - )); + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.BLIND_VOTE); } @Override diff --git a/core/src/main/java/bisq/core/dao/governance/bond/BondState.java b/core/src/main/java/bisq/core/dao/governance/bond/BondState.java index 86eb4dc2235..eb7dfe11f0e 100644 --- a/core/src/main/java/bisq/core/dao/governance/bond/BondState.java +++ b/core/src/main/java/bisq/core/dao/governance/bond/BondState.java @@ -22,6 +22,7 @@ * Used also in string properties ("dao.bond.bondState.*") */ public enum BondState { + UNDEFINED, READY_FOR_LOCKUP, // Accepted by voting (if role) but no lockup tx made yet. LOCKUP_TX_PENDING, // Tx broadcasted but not confirmed. Used only by tx publisher. LOCKUP_TX_CONFIRMED, diff --git a/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupReason.java b/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupReason.java index abf4ecde08c..6f41beec735 100644 --- a/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupReason.java +++ b/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupReason.java @@ -26,6 +26,7 @@ * Reason for locking up a bond. */ public enum LockupReason { + UNDEFINED((byte) 0x00), BONDED_ROLE((byte) 0x01), REPUTATION((byte) 0x02); diff --git a/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRolesRepository.java b/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRolesRepository.java index 21aab0b53fd..2f25d972eca 100644 --- a/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRolesRepository.java +++ b/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRolesRepository.java @@ -22,6 +22,7 @@ import bisq.core.dao.governance.bond.BondRepository; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.TxOutput; +import bisq.core.dao.state.model.governance.EvaluatedProposal; import bisq.core.dao.state.model.governance.Proposal; import bisq.core.dao.state.model.governance.Role; import bisq.core.dao.state.model.governance.RoleProposal; @@ -62,12 +63,16 @@ public boolean isMyRole(Role role) { Set myWalletTransactionIds = bsqWalletService.getWalletTransactions().stream() .map(Transaction::getHashAsString) .collect(Collectors.toSet()); - return getBondedRoleProposalStream() + return getAcceptedBondedRoleProposalStream() .filter(roleProposal -> roleProposal.getRole().equals(role)) .map(Proposal::getTxId) .anyMatch(myWalletTransactionIds::contains); } + public Optional getAcceptedBondedRoleProposal(Role role) { + return getAcceptedBondedRoleProposalStream().filter(e -> e.getRole().getUid().equals(role.getUid())).findAny(); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Protected @@ -109,4 +114,11 @@ private Stream getBondedRoleProposalStream() { .filter(evaluatedProposal -> evaluatedProposal.getProposal() instanceof RoleProposal) .map(e -> ((RoleProposal) e.getProposal())); } + + private Stream getAcceptedBondedRoleProposalStream() { + return daoStateService.getEvaluatedProposalList().stream() + .filter(evaluatedProposal -> evaluatedProposal.getProposal() instanceof RoleProposal) + .filter(EvaluatedProposal::isAccepted) + .map(e -> ((RoleProposal) e.getProposal())); + } } diff --git a/core/src/main/java/bisq/core/dao/governance/param/Param.java b/core/src/main/java/bisq/core/dao/governance/param/Param.java index b04a499144d..6107192ff8a 100644 --- a/core/src/main/java/bisq/core/dao/governance/param/Param.java +++ b/core/src/main/java/bisq/core/dao/governance/param/Param.java @@ -38,20 +38,26 @@ * Parameters which are only used in proposals and voting are less strict limited as we can require that those users are * using the latest software version. * The UNDEFINED entry is used as fallback for error cases and will get ignored. + * + * Name of the params must not change as that is used for serialisation in Protobuffer. The data fields are not part of + * the PB serialisation so changes for those would not change the hash for the dao state hash chain. + * Though changing the values might break consensus as the validations might return a different result (e.g. a param + * change proposal was accepted with older min/max values but then after a change it is not valid anymore and + * might break the consequences of that change. So in fact we MUST not change anything here, only way is to add new + * entries and don't use the deprecated enum in future releases anymore. */ @Slf4j public enum Param { - UNDEFINED(null, ParamType.UNDEFINED), + UNDEFINED("N/A", ParamType.UNDEFINED), - // Fee in BTC satoshi for a 1 BTC trade. 200_000 Satoshi = 0.00200000 BTC = 0.2%. - // 10 USD at BTC price 5_000 USD for a 1 BTC trade; + // Fee in BTC for a 1 BTC trade. 0.001 is 0.1%. @5000 USD/BTC price 0.1% fee is 5 USD. DEFAULT_MAKER_FEE_BTC("0.001", ParamType.BTC, 5, 5), DEFAULT_TAKER_FEE_BTC("0.003", ParamType.BTC, 5, 5), // 0.2% of trade amount MIN_MAKER_FEE_BTC("0.00005", ParamType.BTC, 5, 5), // 0.005% of trade amount MIN_TAKER_FEE_BTC("0.00005", ParamType.BTC, 5, 5), - // Fee in BSQ satoshi for a 1 BTC trade. 100 Satoshi = 1 BSQ => about 0.02%. - // About 1 USD if 1 BSQ = 1 USD for a 1 BTC trade which is about 10% of the BTC fee., + // Fee in BSQ satoshi for a 1 BTC trade. 100 Satoshi = 1 BSQ + // If 1 BTS is 1 USD the fee @5000 USD/BTC is 0.5 USD which is 10% of the BTC fee of 5 USD. // Might need adjustment if BSQ/BTC rate changes. DEFAULT_MAKER_FEE_BSQ("0.50", ParamType.BSQ, 5, 5), // ~ 0.01% of trade amount DEFAULT_TAKER_FEE_BSQ("1.5", ParamType.BSQ, 5, 5), @@ -87,22 +93,22 @@ public enum Param { // E.g. If the result ends up in 65% weighted vote for acceptance and threshold was 50% it is accepted. // The result must be larger than the threshold. A 50% vote result for a threshold with 50% is not sufficient, // it requires min. 50.01%. + // The maxDecrease value is only relevant if the decreased value will not result in a value below 50.01%. THRESHOLD_COMP_REQUEST("50.01", ParamType.PERCENT, 1.2, 1.2), THRESHOLD_REIMBURSEMENT("50.01", ParamType.PERCENT, 1.2, 1.2), - THRESHOLD_CHANGE_PARAM("75", ParamType.PERCENT, 1.2, 1.2), // That might change the THRESHOLD_CHANGE_PARAM and QUORUM_CHANGE_PARAM as well. So we have to be careful here! + THRESHOLD_CHANGE_PARAM("75.01", ParamType.PERCENT, 1.2, 1.2), // That might change the THRESHOLD_CHANGE_PARAM and QUORUM_CHANGE_PARAM as well. So we have to be careful here! THRESHOLD_ROLE("50.01", ParamType.PERCENT, 1.2, 1.2), - THRESHOLD_CONFISCATION("85", ParamType.PERCENT, 1.2, 1.2), // Confiscation is considered an exceptional case and need very high consensus among the stakeholders. + THRESHOLD_CONFISCATION("85.01", ParamType.PERCENT, 1.2, 1.2), // Confiscation is considered an exceptional case and need very high consensus among the stakeholders. THRESHOLD_GENERIC("50.01", ParamType.PERCENT, 1.2, 1.2), THRESHOLD_REMOVE_ASSET("50.01", ParamType.PERCENT, 1.2, 1.2), // BTC address as recipient for BTC trade fee once the arbitration system is replaced as well as destination for // the time locked payout tx in case the traders do not cooperate. Will be likely a donation address (Bisq, Tor,...) // but can be also a burner address if we prefer to burn the BTC - RECIPIENT_BTC_ADDRESS(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? - "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7" : // mainnet - BisqEnvironment.getBaseCurrencyNetwork().isRegtest() ? - "2N5J6MyjAsWnashimGiNwoRzUXThsQzRmbv" : // regtest - "2N4mVTpUZAnhm9phnxB7VrHB4aBhnWrcUrV", // testnet + RECIPIENT_BTC_ADDRESS(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7" : // mainnet + BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7" : // mainnet + BisqEnvironment.getBaseCurrencyNetwork().isTestnet() ? "2N4mVTpUZAnhm9phnxB7VrHB4aBhnWrcUrV" : // testnet + "mquz1zFmhs7iy8qJTkhY7C9bhJ5S3g8Xim", // regtest or DAO testnet (regtest) ParamType.ADDRESS), // Fee for activating an asset or re-listing after deactivation due lack of trade activity. Fee per day of trial period without activity checks. @@ -110,11 +116,10 @@ public enum Param { // Min required trade volume to not get de-listed. Check starts after trial period and use trial period afterwards to look back for trade activity. ASSET_MIN_VOLUME("0.01", ParamType.BTC, 10, 10), - LOCK_TIME_TRADE_PAYOUT("4320", ParamType.BLOCK), // 30 days - ARBITRATOR_FEE("0", ParamType.BTC), - MAX_TRADE_LIMIT("2", ParamType.BTC), // max trade limit for lowest risk payment method. Others will get derived from that. + LOCK_TIME_TRADE_PAYOUT("4320", ParamType.BLOCK), // 30 days, can be disabled by setting to 0 + ARBITRATOR_FEE("0", ParamType.PERCENT), // % of trade. For new trade protocol. Arbitration will become optional and we can apply a fee to it. Initially we start with no fee. + MAX_TRADE_LIMIT("2", ParamType.BTC, 2, 2), // max trade limit for lowest risk payment method. Others will get derived from that. - // See: https://github.com/bisq-network/proposals/issues/46 // The last block in the proposal and vote phases are not shown to the user as he cannot make a tx there as it would be // confirmed in the next block which would be the following break phase. To hide that complexity we show only the // blocks where the user can be active. To have still round numbers for the durations we add 1 block to those @@ -128,44 +133,58 @@ public enum Param { "3601" : // mainnet; 24 days BisqEnvironment.getBaseCurrencyNetwork().isRegtest() ? "4" : // regtest - "380", // testnet; 2.6 days - ParamType.BLOCK, 3, 3), + BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? + "144" : // daoBetaNet; 1 day + "380", // testnet or dao testnet (server side regtest); 2.6 days + ParamType.BLOCK, 2, 2), PHASE_BREAK1(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "149" : // mainnet; 1 day BisqEnvironment.getBaseCurrencyNetwork().isRegtest() ? "1" : // regtest - "10", // testnet - ParamType.BLOCK, 3, 3), + BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? + "10" : // daoBetaNet + "10", // testnet or dao testnet (server side regtest) + ParamType.BLOCK, 2, 2), PHASE_BLIND_VOTE(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? - "601" : // mainnet; 4 days + "451" : // mainnet; 3 days BisqEnvironment.getBaseCurrencyNetwork().isRegtest() ? "2" : // regtest - "300", // testnet; 2 days - ParamType.BLOCK, 3, 3), + BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? + "144" : // daoBetaNet; 1 day + "300", // testnet or dao testnet (server side regtest); 2 days + ParamType.BLOCK, 2, 2), PHASE_BREAK2(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "9" : // mainnet BisqEnvironment.getBaseCurrencyNetwork().isRegtest() ? "1" : // regtest - "10", // testnet - ParamType.BLOCK, 3, 23), + BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? + "10" : // daoBetaNet + "10", // testnet or dao testnet (server side regtest) + ParamType.BLOCK, 2, 2), PHASE_VOTE_REVEAL(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? - "301" : // mainnet; 2 days + "451" : // mainnet; 3 days BisqEnvironment.getBaseCurrencyNetwork().isRegtest() ? "2" : // regtest - "300", // testnet; 2 days - ParamType.BLOCK, 3, 3), + BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? + "144" : // daoBetaNet; 1 day + "300", // testnet or dao testnet (server side regtest); 2 days + ParamType.BLOCK, 2, 2), PHASE_BREAK3(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "9" : // mainnet BisqEnvironment.getBaseCurrencyNetwork().isRegtest() ? "1" : // regtest - "10", // testnet - ParamType.BLOCK, 3, 3), + BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? + "10" : // daoBetaNet + "10", // testnet or dao testnet (server side regtest) + ParamType.BLOCK, 2, 2), PHASE_RESULT(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "10" : // mainnet BisqEnvironment.getBaseCurrencyNetwork().isRegtest() ? "2" : // regtest - "2", // testnet - ParamType.BLOCK, 3, 3); + BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? + "10" : // daoBetaNet + "2", // testnet or dao testnet (server side regtest) + ParamType.BLOCK, 2, 2); @Getter private final String defaultValue; @@ -185,8 +204,8 @@ public enum Param { /** * @param defaultValue Value at the start of the DAO * @param paramType Type of parameter - * @param maxDecrease Decrease of param value limited to current value / maxDecrease - * @param maxIncrease Increase of param value limited to current value * maxIncrease + * @param maxDecrease Decrease of param value limited to current value / maxDecrease. If 0 we don't apply the check and any change is possible + * @param maxIncrease Increase of param value limited to current value * maxIncrease. If 0 we don't apply the check and any change is possible */ Param(String defaultValue, ParamType paramType, double maxDecrease, double maxIncrease) { this.defaultValue = defaultValue; diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalFactory.java index 26b187bd288..99c3a3e1a3a 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalFactory.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalFactory.java @@ -21,7 +21,6 @@ import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.OpReturnType; import bisq.core.dao.state.model.governance.Proposal; @@ -68,7 +67,7 @@ public BaseProposalFactory(BsqWalletService bsqWalletService, protected ProposalWithTransaction createProposalWithTransaction(String name, String link) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { this.name = name; this.link = link; // As we don't know the txId yes we create a temp proposal with txId set to an empty string. diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalListPresentation.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalListPresentation.java index 2fd7a4d3dd7..abb69d99aea 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalListPresentation.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalListPresentation.java @@ -52,7 +52,7 @@ public class ProposalListPresentation implements DaoStateListener, MyProposalLis private final DaoStateService daoStateService; private final MyProposalListService myProposalListService; private final BsqWalletService bsqWalletService; - private final ProposalValidator proposalValidator; + private final ProposalValidatorProvider validatorProvider; private final ObservableList allProposals = FXCollections.observableArrayList(); @Getter private final FilteredList activeOrMyUnconfirmedProposals = new FilteredList<>(allProposals); @@ -67,12 +67,12 @@ public ProposalListPresentation(ProposalService proposalService, DaoStateService daoStateService, MyProposalListService myProposalListService, BsqWalletService bsqWalletService, - ProposalValidator proposalValidator) { + ProposalValidatorProvider validatorProvider) { this.proposalService = proposalService; this.daoStateService = daoStateService; this.myProposalListService = myProposalListService; this.bsqWalletService = bsqWalletService; - this.proposalValidator = proposalValidator; + this.validatorProvider = validatorProvider; daoStateService.addBsqStateListener(this); myProposalListService.addListener(this); @@ -114,7 +114,7 @@ private void updateLists() { List tempProposals = proposalService.getTempProposals(); Set verifiedProposals = proposalService.getProposalPayloads().stream() .map(ProposalPayload::getProposal) - .filter(proposalValidator::isValidAndConfirmed) + .filter(proposal -> validatorProvider.getValidator(proposal).isValidAndConfirmed(proposal)) .collect(Collectors.toSet()); Set set = new HashSet<>(tempProposals); set.addAll(verifiedProposals); @@ -138,7 +138,7 @@ private void updateLists() { allProposals.clear(); allProposals.addAll(set); - activeOrMyUnconfirmedProposals.setPredicate(proposal -> proposalValidator.isValidAndConfirmed(proposal) || + activeOrMyUnconfirmedProposals.setPredicate(proposal -> validatorProvider.getValidator(proposal).isValidAndConfirmed(proposal) || myUnconfirmedProposals.contains(proposal)); } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java index 2069304d75d..04de0060749 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java @@ -26,6 +26,7 @@ import bisq.core.dao.governance.proposal.storage.temp.TempProposalStorageService; import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.DaoStateService; +import bisq.core.dao.state.model.blockchain.BaseTx; import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.Tx; import bisq.core.dao.state.model.governance.DaoPhase; @@ -40,6 +41,8 @@ import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; import bisq.network.p2p.storage.persistence.ProtectedDataStoreService; +import org.bitcoinj.core.Coin; + import com.google.inject.Inject; import javax.inject.Named; @@ -64,7 +67,7 @@ public class ProposalService implements HashMapChangedListener, AppendOnlyDataSt private final P2PService p2PService; private final PeriodService periodService; private final DaoStateService daoStateService; - private final ProposalValidator proposalValidator; + private final ProposalValidatorProvider validatorProvider; // Proposals we receive in the proposal phase. They can be removed in that phase. That list must not be used for // consensus critical code. @@ -90,12 +93,12 @@ public ProposalService(P2PService p2PService, AppendOnlyDataStoreService appendOnlyDataStoreService, ProtectedDataStoreService protectedDataStoreService, DaoStateService daoStateService, - ProposalValidator proposalValidator, + ProposalValidatorProvider validatorProvider, @Named(DaoOptionKeys.DAO_ACTIVATED) boolean daoActivated) { this.p2PService = p2PService; this.periodService = periodService; this.daoStateService = daoStateService; - this.proposalValidator = proposalValidator; + this.validatorProvider = validatorProvider; if (daoActivated) { // We add our stores to the global stores @@ -131,7 +134,7 @@ public void start() { @Override public void onAdded(ProtectedStorageEntry entry) { - onProtectedDataAdded(entry); + onProtectedDataAdded(entry, true); } @Override @@ -146,7 +149,7 @@ public void onRemoved(ProtectedStorageEntry entry) { @Override public void onAdded(PersistableNetworkPayload payload) { - onAppendOnlyDataAdded(payload); + onAppendOnlyDataAdded(payload, true); } @@ -180,26 +183,40 @@ public void onParseBlockChainComplete() { public List getValidatedProposals() { return proposalPayloads.stream() .map(ProposalPayload::getProposal) - .filter(proposalValidator::isTxTypeValid) + .filter(proposal -> validatorProvider.getValidator(proposal).isTxTypeValid(proposal)) .collect(Collectors.toList()); } + public Coin getRequiredQuorum(Proposal proposal) { + int chainHeight = daoStateService.getTx(proposal.getTxId()) + .map(BaseTx::getBlockHeight). + orElse(daoStateService.getChainHeight()); + return daoStateService.getParamValueAsCoin(proposal.getQuorumParam(), chainHeight); + } + + public double getRequiredThreshold(Proposal proposal) { + int chainHeight = daoStateService.getTx(proposal.getTxId()) + .map(BaseTx::getBlockHeight). + orElse(daoStateService.getChainHeight()); + return daoStateService.getParamValueAsPercentDouble(proposal.getThresholdParam(), chainHeight); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Private /////////////////////////////////////////////////////////////////////////////////////////// private void fillListFromProtectedStore() { - p2PService.getDataMap().values().forEach(this::onProtectedDataAdded); + p2PService.getDataMap().values().forEach(e -> onProtectedDataAdded(e, false)); } private void fillListFromAppendOnlyDataStore() { - p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().forEach(this::onAppendOnlyDataAdded); + p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().forEach(e -> onAppendOnlyDataAdded(e, false)); } private void publishToAppendOnlyDataStore() { tempProposals.stream() - .filter(proposalValidator::isValidAndConfirmed) + .filter(proposal -> validatorProvider.getValidator(proposal).isValidAndConfirmed(proposal)) .map(ProposalPayload::new) .forEach(proposalPayload -> { boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, true); @@ -211,16 +228,19 @@ private void publishToAppendOnlyDataStore() { }); } - private void onProtectedDataAdded(ProtectedStorageEntry entry) { + private void onProtectedDataAdded(ProtectedStorageEntry entry, boolean doLog) { ProtectedStoragePayload protectedStoragePayload = entry.getProtectedStoragePayload(); if (protectedStoragePayload instanceof TempProposalPayload) { Proposal proposal = ((TempProposalPayload) protectedStoragePayload).getProposal(); // We do not validate if we are in current cycle and if tx is confirmed yet as the tx might be not // available/confirmed. But we check if we are in the proposal phase. if (!tempProposals.contains(proposal)) { - if (proposalValidator.isValidOrUnconfirmed(proposal)) { - log.info("We received a TempProposalPayload and store it to our protectedStoreList. proposalTxId={}", - proposal.getTxId()); + ProposalValidator validator = validatorProvider.getValidator(proposal); + if (validator.isValidOrUnconfirmed(proposal)) { + if (doLog) { + log.info("We received a TempProposalPayload and store it to our protectedStoreList. proposalTxId={}", + proposal.getTxId()); + } tempProposals.add(proposal); } else { log.debug("We received an invalid proposal from the P2P network. Proposal.txId={}, blockHeight={}", @@ -256,14 +276,16 @@ private void onProtectedDataRemoved(ProtectedStorageEntry entry) { } } - private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload) { + private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload, boolean doLog) { if (persistableNetworkPayload instanceof ProposalPayload) { ProposalPayload proposalPayload = (ProposalPayload) persistableNetworkPayload; if (!proposalPayloads.contains(proposalPayload)) { Proposal proposal = proposalPayload.getProposal(); - if (proposalValidator.areDataFieldsValid(proposal)) { - log.info("We received a ProposalPayload and store it to our appendOnlyStoreList. proposalTxId={}", - proposal.getTxId()); + if (validatorProvider.getValidator(proposal).areDataFieldsValid(proposal)) { + if (doLog) { + log.info("We received a ProposalPayload and store it to our appendOnlyStoreList. proposalTxId={}", + proposal.getTxId()); + } proposalPayloads.add(proposalPayload); } else { log.warn("We received a invalid append-only proposal from the P2P network. " + diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalType.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalType.java index 0348fe66355..079f37b7227 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalType.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalType.java @@ -20,6 +20,7 @@ import bisq.core.locale.Res; public enum ProposalType { + UNDEFINED, COMPENSATION_REQUEST, REIMBURSEMENT_REQUEST, CHANGE_PARAM, diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidationException.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidationException.java new file mode 100644 index 00000000000..4e0e2065f50 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidationException.java @@ -0,0 +1,57 @@ +package bisq.core.dao.governance.proposal; + +import bisq.core.dao.state.model.blockchain.Tx; + +import org.bitcoinj.core.Coin; + +import lombok.Getter; + +import javax.annotation.Nullable; + +@Getter +public class ProposalValidationException extends Exception { + @Nullable + private Coin requestedBsq; + @Nullable + private Coin minRequestAmount; + @Nullable + private Tx tx; + + public ProposalValidationException(String message, Coin requestedBsq, Coin minRequestAmount) { + super(message); + this.requestedBsq = requestedBsq; + this.minRequestAmount = minRequestAmount; + } + + public ProposalValidationException(Throwable cause) { + super(cause); + } + + public ProposalValidationException(String message) { + super(message); + } + + public ProposalValidationException(String message, Throwable throwable) { + super(message, throwable); + + } + + public ProposalValidationException(String message, Tx tx) { + super(message); + this.tx = tx; + } + + public ProposalValidationException(Throwable cause, Tx tx) { + super(cause); + this.tx = tx; + } + + @Override + public String toString() { + return "ProposalValidationException{" + + "\n requestedBsq=" + requestedBsq + + ",\n minRequestAmount=" + minRequestAmount + + ",\n tx=" + tx + + "\n} " + super.toString(); + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java index 4434339d808..e56f99fed66 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java @@ -17,9 +17,10 @@ package bisq.core.dao.governance.proposal; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.ConsensusCritical; import bisq.core.dao.governance.period.PeriodService; import bisq.core.dao.state.DaoStateService; +import bisq.core.dao.state.model.blockchain.BaseTx; import bisq.core.dao.state.model.blockchain.Tx; import bisq.core.dao.state.model.blockchain.TxType; import bisq.core.dao.state.model.governance.CompensationProposal; @@ -27,22 +28,21 @@ import bisq.core.dao.state.model.governance.Proposal; import bisq.core.dao.state.model.governance.ReimbursementProposal; -import javax.inject.Inject; - import java.util.Optional; import lombok.extern.slf4j.Slf4j; import static org.apache.commons.lang3.Validate.notEmpty; +/** + * Changes here can potentially break consensus! + */ @Slf4j -public class ProposalValidator { - +public abstract class ProposalValidator implements ConsensusCritical { protected final DaoStateService daoStateService; protected final PeriodService periodService; - @Inject - public ProposalValidator(DaoStateService daoStateService, PeriodService periodService) { + protected ProposalValidator(DaoStateService daoStateService, PeriodService periodService) { this.daoStateService = daoStateService; this.periodService = periodService; } @@ -51,17 +51,18 @@ public boolean areDataFieldsValid(Proposal proposal) { try { validateDataFields(proposal); return true; - } catch (ValidationException e) { + } catch (ProposalValidationException e) { + log.warn("proposal data fields are invalid. proposal={}, error={}", proposal, e.toString()); return false; } } - public void validateDataFields(Proposal proposal) throws ValidationException { + public void validateDataFields(Proposal proposal) throws ProposalValidationException { try { notEmpty(proposal.getName(), "name must not be empty"); notEmpty(proposal.getLink(), "link must not be empty"); } catch (Throwable throwable) { - throw new ValidationException(throwable); + throw new ProposalValidationException(throwable); } } @@ -88,7 +89,6 @@ public boolean isTxTypeValid(Proposal proposal) { private boolean isValid(Proposal proposal, boolean allowUnconfirmed) { if (!areDataFieldsValid(proposal)) { - log.warn("proposal data fields are invalid. proposal={}", proposal); return false; } @@ -140,4 +140,12 @@ private boolean isValid(Proposal proposal, boolean allowUnconfirmed) { return false; } } + + protected Integer getBlockHeight(Proposal proposal) { + // When we receive a temp proposal the tx is usually not confirmed so we cannot lookup the block height of + // the tx. We take the current block height in that case as it would be in the same cycle anyway. + return daoStateService.getTx(proposal.getTxId()) + .map(BaseTx::getBlockHeight) + .orElseGet(daoStateService::getChainHeight); + } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidatorProvider.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidatorProvider.java new file mode 100644 index 00000000000..f5da2d27713 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidatorProvider.java @@ -0,0 +1,83 @@ +/* + * 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.dao.governance.proposal; + +import bisq.core.dao.governance.proposal.compensation.CompensationValidator; +import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondValidator; +import bisq.core.dao.governance.proposal.generic.GenericProposalValidator; +import bisq.core.dao.governance.proposal.param.ChangeParamValidator; +import bisq.core.dao.governance.proposal.reimbursement.ReimbursementValidator; +import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetValidator; +import bisq.core.dao.governance.proposal.role.RoleValidator; +import bisq.core.dao.state.model.governance.Proposal; + +import javax.inject.Inject; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ProposalValidatorProvider { + private final CompensationValidator compensationValidator; + private final ConfiscateBondValidator confiscateBondValidator; + private final GenericProposalValidator genericProposalValidator; + private final ChangeParamValidator changeParamValidator; + private final ReimbursementValidator reimbursementValidator; + private final RemoveAssetValidator removeAssetValidator; + private final RoleValidator roleValidator; + + @Inject + public ProposalValidatorProvider(CompensationValidator compensationValidator, + ConfiscateBondValidator confiscateBondValidator, + GenericProposalValidator genericProposalValidator, + ChangeParamValidator changeParamValidator, + ReimbursementValidator reimbursementValidator, + RemoveAssetValidator removeAssetValidator, + RoleValidator roleValidator) { + this.compensationValidator = compensationValidator; + this.confiscateBondValidator = confiscateBondValidator; + this.genericProposalValidator = genericProposalValidator; + this.changeParamValidator = changeParamValidator; + this.reimbursementValidator = reimbursementValidator; + this.removeAssetValidator = removeAssetValidator; + this.roleValidator = roleValidator; + } + + public ProposalValidator getValidator(Proposal proposal) { + return getValidator(proposal.getType()); + } + + public ProposalValidator getValidator(ProposalType proposalType) { + switch (proposalType) { + case COMPENSATION_REQUEST: + return compensationValidator; + case REIMBURSEMENT_REQUEST: + return reimbursementValidator; + case CHANGE_PARAM: + return changeParamValidator; + case BONDED_ROLE: + return roleValidator; + case CONFISCATE_BOND: + return confiscateBondValidator; + case GENERIC: + return genericProposalValidator; + case REMOVE_ASSET: + return removeAssetValidator; + } + throw new RuntimeException("Proposal type " + proposalType.name() + " was not covered by switch case."); + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalFactory.java index 0b61fac400f..bd307f55a05 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalFactory.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalFactory.java @@ -21,9 +21,9 @@ import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.BaseProposalFactory; import bisq.core.dao.governance.proposal.ProposalConsensus; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.state.DaoStateService; @@ -39,6 +39,8 @@ import javax.inject.Inject; +import java.util.HashMap; + import lombok.extern.slf4j.Slf4j; /** @@ -64,7 +66,7 @@ public CompensationProposalFactory(BsqWalletService bsqWalletService, public ProposalWithTransaction createProposalWithTransaction(String name, String link, Coin requestedBsq) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { this.requestedBsq = requestedBsq; this.bsqAddress = bsqWalletService.getUnusedBsqAddressAsString(); @@ -77,7 +79,8 @@ protected CompensationProposal createProposalWithoutTxId() { name, link, requestedBsq, - bsqAddress); + bsqAddress, + new HashMap<>()); } @Override diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationValidator.java index ef91bc06a7d..e76f77fa025 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationValidator.java @@ -17,8 +17,9 @@ package bisq.core.dao.governance.proposal.compensation; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.ConsensusCritical; import bisq.core.dao.governance.period.PeriodService; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalValidator; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.governance.CompensationProposal; @@ -33,8 +34,11 @@ import static com.google.common.base.Preconditions.checkArgument; import static org.apache.commons.lang3.Validate.notEmpty; +/** + * Changes here can potentially break consensus! + */ @Slf4j -public class CompensationValidator extends ProposalValidator { +public class CompensationValidator extends ProposalValidator implements ConsensusCritical { @Inject public CompensationValidator(DaoStateService daoStateService, PeriodService periodService) { @@ -42,7 +46,7 @@ public CompensationValidator(DaoStateService daoStateService, PeriodService peri } @Override - public void validateDataFields(Proposal proposal) throws ValidationException { + public void validateDataFields(Proposal proposal) throws ProposalValidationException { try { super.validateDataFields(proposal); @@ -53,15 +57,17 @@ public void validateDataFields(Proposal proposal) throws ValidationException { compensationProposal.getAddress(); // throws AddressFormatException if wrong address Coin requestedBsq = compensationProposal.getRequestedBsq(); - Coin maxCompensationRequestAmount = CompensationConsensus.getMaxCompensationRequestAmount(daoStateService, periodService.getChainHeight()); + int chainHeight = getBlockHeight(proposal); + Coin maxCompensationRequestAmount = CompensationConsensus.getMaxCompensationRequestAmount(daoStateService, chainHeight); checkArgument(requestedBsq.compareTo(maxCompensationRequestAmount) <= 0, "Requested BSQ must not exceed " + (maxCompensationRequestAmount.value / 100L) + " BSQ"); - Coin minCompensationRequestAmount = CompensationConsensus.getMinCompensationRequestAmount(daoStateService, periodService.getChainHeight()); + Coin minCompensationRequestAmount = CompensationConsensus.getMinCompensationRequestAmount(daoStateService, chainHeight); checkArgument(requestedBsq.compareTo(minCompensationRequestAmount) >= 0, "Requested BSQ must not be less than " + (minCompensationRequestAmount.value / 100L) + " BSQ"); - + } catch (ProposalValidationException e) { + throw e; } catch (Throwable throwable) { - throw new ValidationException(throwable); + throw new ProposalValidationException(throwable); } } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalFactory.java index 72fc31fab78..8b4c2a2710a 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalFactory.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalFactory.java @@ -19,8 +19,8 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.BaseProposalFactory; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.state.DaoStateService; @@ -30,6 +30,8 @@ import javax.inject.Inject; +import java.util.HashMap; + import lombok.extern.slf4j.Slf4j; /** @@ -58,7 +60,7 @@ public ConfiscateBondProposalFactory(BsqWalletService bsqWalletService, public ProposalWithTransaction createProposalWithTransaction(String name, String link, String lockupTxId) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { this.lockupTxId = lockupTxId; return super.createProposalWithTransaction(name, link); @@ -69,6 +71,7 @@ protected ConfiscateBondProposal createProposalWithoutTxId() { return new ConfiscateBondProposal( name, link, - lockupTxId); + lockupTxId, + new HashMap<>()); } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java index d6d79670be2..dd6d68225db 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java @@ -17,8 +17,9 @@ package bisq.core.dao.governance.proposal.confiscatebond; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.ConsensusCritical; import bisq.core.dao.governance.period.PeriodService; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalValidator; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.governance.ConfiscateBondProposal; @@ -28,8 +29,13 @@ import lombok.extern.slf4j.Slf4j; +import static org.apache.commons.lang3.Validate.notEmpty; + +/** + * Changes here can potentially break consensus! + */ @Slf4j -public class ConfiscateBondValidator extends ProposalValidator { +public class ConfiscateBondValidator extends ProposalValidator implements ConsensusCritical { @Inject public ConfiscateBondValidator(DaoStateService daoStateService, PeriodService periodService) { @@ -37,14 +43,15 @@ public ConfiscateBondValidator(DaoStateService daoStateService, PeriodService pe } @Override - public void validateDataFields(Proposal proposal) throws ValidationException { + public void validateDataFields(Proposal proposal) throws ProposalValidationException { try { super.validateDataFields(proposal); - ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; - //TODO + notEmpty(confiscateBondProposal.getLockupTxId(), "LockupTxId must not be empty"); + } catch (ProposalValidationException e) { + throw e; } catch (Throwable throwable) { - throw new ValidationException(throwable); + throw new ProposalValidationException(throwable); } } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalFactory.java index bf7d89f90cc..8feafdcd7bb 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalFactory.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalFactory.java @@ -19,8 +19,8 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.BaseProposalFactory; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.state.DaoStateService; @@ -30,6 +30,8 @@ import javax.inject.Inject; +import java.util.HashMap; + import lombok.extern.slf4j.Slf4j; /** @@ -55,13 +57,15 @@ public GenericProposalFactory(BsqWalletService bsqWalletService, } public ProposalWithTransaction createProposalWithTransaction(String name, String link) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { return super.createProposalWithTransaction(name, link); } @Override protected GenericProposal createProposalWithoutTxId() { - return new GenericProposal(name, link); + return new GenericProposal(name, + link, + new HashMap<>()); } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalValidator.java index 0ce069647b3..f9d38a6d63d 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalValidator.java @@ -17,19 +17,22 @@ package bisq.core.dao.governance.proposal.generic; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.ConsensusCritical; import bisq.core.dao.governance.period.PeriodService; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalValidator; import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.GenericProposal; import bisq.core.dao.state.model.governance.Proposal; import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; +/** + * Changes here can potentially break consensus! + */ @Slf4j -public class GenericProposalValidator extends ProposalValidator { +public class GenericProposalValidator extends ProposalValidator implements ConsensusCritical { @Inject public GenericProposalValidator(DaoStateService daoStateService, PeriodService periodService) { @@ -37,14 +40,13 @@ public GenericProposalValidator(DaoStateService daoStateService, PeriodService p } @Override - public void validateDataFields(Proposal proposal) throws ValidationException { + public void validateDataFields(Proposal proposal) throws ProposalValidationException { try { super.validateDataFields(proposal); - - GenericProposal genericProposalProposal = (GenericProposal) proposal; - //TODO + } catch (ProposalValidationException e) { + throw e; } catch (Throwable throwable) { - throw new ValidationException(throwable); + throw new ProposalValidationException(throwable); } } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalFactory.java index 4ff64e37f34..1935a53b66d 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalFactory.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalFactory.java @@ -19,9 +19,9 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.param.Param; import bisq.core.dao.governance.proposal.BaseProposalFactory; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.state.DaoStateService; @@ -31,6 +31,8 @@ import javax.inject.Inject; +import java.util.HashMap; + import lombok.extern.slf4j.Slf4j; /** @@ -61,7 +63,7 @@ public ProposalWithTransaction createProposalWithTransaction(String name, String link, Param param, String paramValue) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { this.param = param; this.paramValue = paramValue; @@ -74,6 +76,7 @@ protected ChangeParamProposal createProposalWithoutTxId() { name, link, param, - paramValue); + paramValue, + new HashMap<>()); } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java index 9ab41fc6c12..307ba29dd18 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java @@ -17,136 +17,159 @@ package bisq.core.dao.governance.proposal.param; +import bisq.core.app.BisqEnvironment; import bisq.core.btc.wallet.Restrictions; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.ConsensusCritical; import bisq.core.dao.governance.param.Param; import bisq.core.dao.governance.period.PeriodService; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalValidator; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.governance.ChangeParamProposal; import bisq.core.dao.state.model.governance.Proposal; import bisq.core.locale.Res; -import bisq.core.util.BsqFormatter; import bisq.core.util.validation.BtcAddressValidator; import bisq.core.util.validation.InputValidator; -import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Coin; import javax.inject.Inject; +import com.google.common.annotations.VisibleForTesting; + +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; +//TODO Use translation properties in error messages a they are shown to user. + +/** + * Changes here can potentially break consensus! + * + * We do not store the values as domain types (Coin, int, String) but all as Strings. So we need to parse it to the + * expected data type even if we get the data not from user input. + */ @Slf4j -public class ChangeParamValidator extends ProposalValidator { +public class ChangeParamValidator extends ProposalValidator implements ConsensusCritical { + public enum Result { + OK, + SAME("New parameter value must be different to current value."), + NO_CHANGE_POSSIBLE("Parameter cannot be changed."), + TOO_LOW("New parameter value is too small."), + TOO_HIGH("New parameter value is too large."); + + @Getter + private final String errorMsg; - private BsqFormatter bsqFormatter; + Result(String errorMsg) { + this.errorMsg = errorMsg; + } + + Result() { + this.errorMsg = ""; + } + + @Override + public String toString() { + return "Result{" + + "\n errorMsg='" + errorMsg + '\'' + + "\n} " + super.toString(); + } + } @Inject - public ChangeParamValidator(DaoStateService daoStateService, PeriodService periodService, BsqFormatter bsqFormatter) { + public ChangeParamValidator(DaoStateService daoStateService, PeriodService periodService) { super(daoStateService, periodService); - this.bsqFormatter = bsqFormatter; } @Override - public void validateDataFields(Proposal proposal) throws ValidationException { + public void validateDataFields(Proposal proposal) throws ProposalValidationException { try { super.validateDataFields(proposal); - ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; - - validateParamValue(changeParamProposal.getParam(), changeParamProposal.getParamValue()); + validateParamValue(changeParamProposal.getParam(), changeParamProposal.getParamValue(), getBlockHeight(proposal)); + } catch (ProposalValidationException e) { + throw e; } catch (Throwable throwable) { - throw new ValidationException(throwable); + throw new ProposalValidationException(throwable); } } - public void validateParamValue(Param param, String inputValue) throws ValidationException { - double maxDecrease = param.getMaxDecrease(); - double maxIncrease = param.getMaxIncrease(); - Coin newValueAsBtcCoin; - Coin newValueAsBsqCoin = null; - Coin currentValueAsCoin; - switch (param.getParamType()) { - case UNDEFINED: - break; - case BSQ: - bsqFormatter.validateBsqInput(inputValue); - newValueAsBsqCoin = bsqFormatter.parseToCoin(inputValue); - checkArgument(newValueAsBsqCoin.isPositive(), "Input must be positive"); - currentValueAsCoin = daoStateService.getParamValueAsCoin(param, periodService.getChainHeight()); - checkArgument(!currentValueAsCoin.equals(newValueAsBsqCoin), "Your input must be different to the current value"); - validateChangeRange((double) currentValueAsCoin.value, (double) newValueAsBsqCoin.value, maxDecrease, maxIncrease); - break; - case BTC: - bsqFormatter.validateBtcInput(inputValue); - newValueAsBtcCoin = bsqFormatter.parseToBTC(inputValue); - checkArgument(!newValueAsBtcCoin.isNegative(), "Input must not be negative"); - // We allow 0 values (arbitration fee) - checkArgument(newValueAsBtcCoin.value == 0 || newValueAsBtcCoin.value >= Restrictions.getMinNonDustOutput().value, - Res.get("validation.amountBelowDust", Restrictions.getMinNonDustOutput().value)); - currentValueAsCoin = daoStateService.getParamValueAsCoin(param, periodService.getChainHeight()); - checkArgument(!currentValueAsCoin.equals(newValueAsBtcCoin), "Your input must be different to the current value"); - validateChangeRange((double) currentValueAsCoin.value, (double) newValueAsBtcCoin.value, maxDecrease, maxIncrease); - break; - case PERCENT: - double newValueAsPercentDouble = bsqFormatter.parsePercentStringToDouble(inputValue); - checkArgument(newValueAsPercentDouble > 0.5, "Threshold must be larger than 50%."); - double currentValueAsPercentDouble = daoStateService.getParamValueAsPercentDouble(param, periodService.getChainHeight()); - checkArgument(currentValueAsPercentDouble != newValueAsPercentDouble, "Your input must be different to the current value"); - validateChangeRange(currentValueAsPercentDouble, newValueAsPercentDouble, maxDecrease, maxIncrease); - break; - case BLOCK: - int newValueAsBlock = Integer.parseInt(inputValue); - // We allow 0 values (e.g. time lock for trade) - checkArgument(newValueAsBlock >= 0, "newValueAsBlock must be >= 0"); - int currentValueAsBlock = daoStateService.getParamValueAsBlock(param, periodService.getChainHeight()); - checkArgument(currentValueAsBlock != newValueAsBlock, "Your input must be different to the current value"); - validateChangeRange((double) currentValueAsBlock, (double) newValueAsBlock, maxDecrease, maxIncrease); - break; - case ADDRESS: - String currentValue = daoStateService.getParamValue(param, periodService.getChainHeight()); - checkArgument(!currentValue.equals(inputValue), "Your input must be different to the current value"); - InputValidator.ValidationResult validationResult = new BtcAddressValidator().validate(inputValue); - if (!validationResult.isValid) - throw new AddressFormatException(validationResult.errorMessage); + public void validateParamValue(Param param, String inputValue) throws ParamValidationException { + int blockHeight = periodService.getChainHeight(); + validateParamValue(param, inputValue, blockHeight); + } - break; + private void validateParamValue(Param param, String inputValue, int blockHeight) throws ParamValidationException { + String currentParamValue = daoStateService.getParamValue(param, blockHeight); + validateParamValue(param, currentParamValue, inputValue); + } + + private void validateParamValue(Param param, String currentParamValue, String inputValue) throws ParamValidationException { + try { + Coin currentParamValueAsCoin, inputValueAsCoin; + switch (param.getParamType()) { + case UNDEFINED: + break; + case BSQ: + currentParamValueAsCoin = daoStateService.getParamValueAsCoin(param, currentParamValue); + inputValueAsCoin = daoStateService.getParamValueAsCoin(param, inputValue); + validateBsqValue(currentParamValueAsCoin, inputValueAsCoin, param); + break; + case BTC: + currentParamValueAsCoin = daoStateService.getParamValueAsCoin(param, currentParamValue); + inputValueAsCoin = daoStateService.getParamValueAsCoin(param, inputValue); + validateBtcValue(currentParamValueAsCoin, inputValueAsCoin, param); + break; + case PERCENT: + double currentParamValueAsPercentDouble = daoStateService.getParamValueAsPercentDouble(currentParamValue); + double inputValueAsPercentDouble = daoStateService.getParamValueAsPercentDouble(inputValue); + validatePercentValue(currentParamValueAsPercentDouble, inputValueAsPercentDouble, param); + break; + case BLOCK: + int currentParamValueAsBlock = daoStateService.getParamValueAsBlock(currentParamValue); + int inputValueAsBlock = daoStateService.getParamValueAsBlock(inputValue); + validateBlockValue(currentParamValueAsBlock, inputValueAsBlock, param); + break; + case ADDRESS: + validateAddressValue(currentParamValue, inputValue); + break; + default: + log.warn("Param type {} not handled in switch case at validateParamValue", param.getParamType()); + } + } catch (ParamValidationException e) { + throw e; + } catch (Throwable t) { + throw new ParamValidationException(t); } + } - // Add here more fine tuned custom validations... - switch (param) { - case UNDEFINED: - break; - - case DEFAULT_MAKER_FEE_BTC: - case DEFAULT_TAKER_FEE_BTC: - case MIN_MAKER_FEE_BTC: - case MIN_TAKER_FEE_BTC: - break; + private void validateBsqValue(Coin currentParamValueAsCoin, Coin inputValueAsCoin, Param param) throws ParamValidationException { + checkArgument(inputValueAsCoin.isPositive(), "Input must be positive"); + validationChange((double) currentParamValueAsCoin.value, (double) inputValueAsCoin.value, param); + switch (param) { case DEFAULT_MAKER_FEE_BSQ: case DEFAULT_TAKER_FEE_BSQ: case MIN_MAKER_FEE_BSQ: case MIN_TAKER_FEE_BSQ: break; - case PROPOSAL_FEE: case BLIND_VOTE_FEE: break; case COMPENSATION_REQUEST_MIN_AMOUNT: case REIMBURSEMENT_MIN_AMOUNT: + checkArgument(inputValueAsCoin.value >= Restrictions.getMinNonDustOutput().value, + Res.get("validation.amountBelowDust", Restrictions.getMinNonDustOutput().value)); case COMPENSATION_REQUEST_MAX_AMOUNT: case REIMBURSEMENT_MAX_AMOUNT: - checkNotNull(newValueAsBsqCoin, "newValueAsBsqCoin must not be null"); - checkArgument(newValueAsBsqCoin.value >= Restrictions.getMinNonDustOutput().value, + checkArgument(inputValueAsCoin.value >= Restrictions.getMinNonDustOutput().value, Res.get("validation.amountBelowDust", Restrictions.getMinNonDustOutput().value)); + checkArgument(inputValueAsCoin.value <= 200000000, + "Amounts larger than 200 000 BSQ are not permitted"); break; - case QUORUM_COMP_REQUEST: case QUORUM_REIMBURSEMENT: case QUORUM_CHANGE_PARAM: @@ -154,8 +177,36 @@ public void validateParamValue(Param param, String inputValue) throws Validation case QUORUM_CONFISCATION: case QUORUM_GENERIC: case QUORUM_REMOVE_ASSET: + checkArgument(inputValueAsCoin.value >= 100000, + "Quorum must be at least 1000 BSQ"); break; + case ASSET_LISTING_FEE_PER_DAY: + break; + } + } + + private void validateBtcValue(Coin currentParamValueAsCoin, Coin inputValueAsCoin, Param param) throws ParamValidationException { + validationChange((double) currentParamValueAsCoin.value, (double) inputValueAsCoin.value, param); + switch (param) { + case DEFAULT_MAKER_FEE_BTC: + case DEFAULT_TAKER_FEE_BTC: + case MIN_MAKER_FEE_BTC: + case MIN_TAKER_FEE_BTC: + checkArgument(inputValueAsCoin.value >= Restrictions.getMinNonDustOutput().value, + Res.get("validation.amountBelowDust", Restrictions.getMinNonDustOutput().value)); + break; + case ASSET_MIN_VOLUME: + case MAX_TRADE_LIMIT: + checkArgument(inputValueAsCoin.isPositive(), "Input must be positive"); + break; + } + } + + private void validatePercentValue(double currentParamValueAsPercentDouble, double inputValueAsPercentDouble, Param param) throws ParamValidationException { + validationChange(currentParamValueAsPercentDouble, inputValueAsPercentDouble, param); + + switch (param) { case THRESHOLD_COMP_REQUEST: case THRESHOLD_REIMBURSEMENT: case THRESHOLD_CHANGE_PARAM: @@ -163,24 +214,27 @@ public void validateParamValue(Param param, String inputValue) throws Validation case THRESHOLD_CONFISCATION: case THRESHOLD_GENERIC: case THRESHOLD_REMOVE_ASSET: + // We show only 2 decimals in the UI for % value + checkArgument(inputValueAsPercentDouble >= 0.5001, + "Threshold must be larger than 50%."); + checkArgument(inputValueAsPercentDouble <= 1, + "Threshold cannot be more than 100%."); break; - - case RECIPIENT_BTC_ADDRESS: + case ARBITRATOR_FEE: + checkArgument(inputValueAsPercentDouble >= 0, "Input must not be negative"); break; + } + } - case ASSET_LISTING_FEE_PER_DAY: - break; - case ASSET_MIN_VOLUME: - break; + private void validateBlockValue(int currentParamValueAsBlock, int inputValueAsBlock, Param param) throws ParamValidationException { + validationChange((double) currentParamValueAsBlock, (double) inputValueAsBlock, param); + // We allow 0 values (e.g. time lock for trade) + checkArgument(inputValueAsBlock >= 0, "inputValueAsBlock must be >= 0"); + boolean isMainnet = BisqEnvironment.getBaseCurrencyNetwork().isMainnet(); + switch (param) { case LOCK_TIME_TRADE_PAYOUT: break; - case ARBITRATOR_FEE: - break; - - case MAX_TRADE_LIMIT: - break; - case PHASE_UNDEFINED: break; case PHASE_PROPOSAL: @@ -189,17 +243,65 @@ public void validateParamValue(Param param, String inputValue) throws Validation case PHASE_BREAK2: case PHASE_VOTE_REVEAL: case PHASE_BREAK3: + if (isMainnet) + checkArgument(inputValueAsBlock >= 6, "The break must have at least 6 blocks"); + break; case PHASE_RESULT: + if (isMainnet) + checkArgument(inputValueAsBlock >= 1, "The break must have at least 1 block"); break; } } - private void validateChangeRange(double currentValue, double newValue, double min, double max) throws ValidationException { + private void validateAddressValue(String currentParamValue, String inputValue) throws ParamValidationException { + checkArgument(!inputValue.equals(currentParamValue), "Your input must be different to the current value"); + InputValidator.ValidationResult validationResult = new BtcAddressValidator().validate(inputValue); + if (!validationResult.isValid) + throw new ParamValidationException(validationResult.errorMessage); + } + + private static void validationChange(double currentParamValue, double inputValue, Param param) throws ParamValidationException { + Result result = getChangeValidationResult(currentParamValue, + inputValue, + param.getMaxDecrease(), + param.getMaxIncrease()); + if (result != Result.OK) { + throw new ParamValidationException(result); + } + } + + /** + * + * @param currentValue Current value + * @param newValue New value + * @param min Decrease of param value limited to current value / maxDecrease. If 0 we don't apply the check and any change is possible + * @param max Increase of param value limited to current value * maxIncrease. If 0 we don't apply the check and any change is possible + * @return Validation result + */ + @VisibleForTesting + static Result getChangeValidationResult(double currentValue, double newValue, double min, double max) { + checkArgument(min >= 0, "Min must be >= 0"); + checkArgument(max >= 0, "Max must be >= 0"); + if (currentValue == newValue) + return Result.SAME; + + if (max == 0) + return Result.OK; + double change = currentValue != 0 ? newValue / currentValue : 0; - if (max > 0 && change > max) - throw new ValidationException("Input is larger than " + max + " times the current value."); + if (change > max) + return Result.TOO_HIGH; + + if (min == 0) + return Result.OK; + + // If min/max are > 0 and currentValue or newValue is 0 it cannot be changed. min/max must be 0 in such cases. + if (currentValue == 0 || newValue == 0) + return Result.NO_CHANGE_POSSIBLE; + + if (change < (1 / min)) + return Result.TOO_LOW; - if (min > 0 && change < (1 / min)) - throw new ValidationException("Input is smaller than " + 1 / min + " times the current value."); + return Result.OK; } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/param/ParamValidationException.java b/core/src/main/java/bisq/core/dao/governance/proposal/param/ParamValidationException.java new file mode 100644 index 00000000000..ff782bc9c7f --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/proposal/param/ParamValidationException.java @@ -0,0 +1,32 @@ +package bisq.core.dao.governance.proposal.param; + +import lombok.Getter; + +import javax.annotation.Nullable; + +@Getter +public class ParamValidationException extends Exception { + @Nullable + private ChangeParamValidator.Result result; + + ParamValidationException(ChangeParamValidator.Result result) { + super(result.getErrorMsg()); + this.result = result; + } + + ParamValidationException(Throwable throwable) { + super(throwable.getMessage()); + initCause(throwable); + } + + ParamValidationException(String errorMessage) { + super(errorMessage); + } + + @Override + public String toString() { + return "ParamValidationException{" + + "\n result=" + result + + "\n} " + super.toString(); + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementProposalFactory.java index 5a4b34244f3..994b9ef8c54 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementProposalFactory.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementProposalFactory.java @@ -21,9 +21,9 @@ import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.BaseProposalFactory; import bisq.core.dao.governance.proposal.ProposalConsensus; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.state.DaoStateService; @@ -39,6 +39,8 @@ import javax.inject.Inject; +import java.util.HashMap; + import lombok.extern.slf4j.Slf4j; /** @@ -64,7 +66,7 @@ public ReimbursementProposalFactory(BsqWalletService bsqWalletService, public ProposalWithTransaction createProposalWithTransaction(String name, String link, Coin requestedBsq) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { this.requestedBsq = requestedBsq; this.bsqAddress = bsqWalletService.getUnusedBsqAddressAsString(); @@ -77,7 +79,8 @@ protected ReimbursementProposal createProposalWithoutTxId() { name, link, requestedBsq, - bsqAddress); + bsqAddress, + new HashMap<>()); } @Override diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementValidator.java index 632c85a2e7b..23b7c9d84e1 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementValidator.java @@ -17,9 +17,11 @@ package bisq.core.dao.governance.proposal.reimbursement; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.ConsensusCritical; import bisq.core.dao.governance.period.PeriodService; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalValidator; +import bisq.core.dao.governance.proposal.compensation.CompensationConsensus; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.governance.Proposal; import bisq.core.dao.state.model.governance.ReimbursementProposal; @@ -33,8 +35,11 @@ import static com.google.common.base.Preconditions.checkArgument; import static org.apache.commons.lang3.Validate.notEmpty; +/** + * Changes here can potentially break consensus! + */ @Slf4j -public class ReimbursementValidator extends ProposalValidator { +public class ReimbursementValidator extends ProposalValidator implements ConsensusCritical { @Inject public ReimbursementValidator(DaoStateService daoStateService, PeriodService periodService) { @@ -42,7 +47,7 @@ public ReimbursementValidator(DaoStateService daoStateService, PeriodService per } @Override - public void validateDataFields(Proposal proposal) throws ValidationException { + public void validateDataFields(Proposal proposal) throws ProposalValidationException { try { super.validateDataFields(proposal); @@ -53,14 +58,17 @@ public void validateDataFields(Proposal proposal) throws ValidationException { reimbursementProposal.getAddress(); // throws AddressFormatException if wrong address Coin requestedBsq = reimbursementProposal.getRequestedBsq(); - Coin maxReimbursementRequestAmount = ReimbursementConsensus.getMaxReimbursementRequestAmount(daoStateService, periodService.getChainHeight()); - checkArgument(requestedBsq.compareTo(maxReimbursementRequestAmount) <= 0, - "Requested BSQ must not exceed " + (maxReimbursementRequestAmount.value / 100L) + " BSQ"); - Coin minReimbursementRequestAmount = ReimbursementConsensus.getMinReimbursementRequestAmount(daoStateService, periodService.getChainHeight()); - checkArgument(requestedBsq.compareTo(minReimbursementRequestAmount) >= 0, - "Requested BSQ must not be less than " + (minReimbursementRequestAmount.value / 100L) + " BSQ"); + int chainHeight = getBlockHeight(proposal); + Coin maxCompensationRequestAmount = CompensationConsensus.getMaxCompensationRequestAmount(daoStateService, chainHeight); + checkArgument(requestedBsq.compareTo(maxCompensationRequestAmount) <= 0, + "Requested BSQ must not exceed " + (maxCompensationRequestAmount.value / 100L) + " BSQ"); + Coin minCompensationRequestAmount = CompensationConsensus.getMinCompensationRequestAmount(daoStateService, chainHeight); + checkArgument(requestedBsq.compareTo(minCompensationRequestAmount) >= 0, + "Requested BSQ must not be less than " + (minCompensationRequestAmount.value / 100L) + " BSQ"); + } catch (ProposalValidationException e) { + throw e; } catch (Throwable throwable) { - throw new ValidationException(throwable); + throw new ProposalValidationException(throwable); } } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalFactory.java index 71d8c56ea6f..865700a9e7a 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalFactory.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalFactory.java @@ -19,8 +19,8 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.BaseProposalFactory; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.state.DaoStateService; @@ -32,6 +32,8 @@ import javax.inject.Inject; +import java.util.HashMap; + import lombok.extern.slf4j.Slf4j; /** @@ -60,7 +62,7 @@ public RemoveAssetProposalFactory(BsqWalletService bsqWalletService, public ProposalWithTransaction createProposalWithTransaction(String name, String link, Asset asset) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { this.asset = asset; return super.createProposalWithTransaction(name, link); @@ -71,6 +73,7 @@ protected RemoveAssetProposal createProposalWithoutTxId() { return new RemoveAssetProposal( name, link, - asset.getTickerSymbol()); + asset.getTickerSymbol(), + new HashMap<>()); } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java index 4a638ef64e6..9a6f59d7c2f 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java @@ -17,8 +17,9 @@ package bisq.core.dao.governance.proposal.removeAsset; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.ConsensusCritical; import bisq.core.dao.governance.period.PeriodService; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalValidator; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.governance.Proposal; @@ -28,8 +29,13 @@ import lombok.extern.slf4j.Slf4j; +import static org.apache.commons.lang3.Validate.notEmpty; + +/** + * Changes here can potentially break consensus! + */ @Slf4j -public class RemoveAssetValidator extends ProposalValidator { +public class RemoveAssetValidator extends ProposalValidator implements ConsensusCritical { @Inject public RemoveAssetValidator(DaoStateService daoStateService, PeriodService periodService) { @@ -37,14 +43,16 @@ public RemoveAssetValidator(DaoStateService daoStateService, PeriodService perio } @Override - public void validateDataFields(Proposal proposal) throws ValidationException { + public void validateDataFields(Proposal proposal) throws ProposalValidationException { try { super.validateDataFields(proposal); RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal; - //TODO + notEmpty(removeAssetProposal.getTickerSymbol(), "TickerSymbol must not be empty"); + } catch (ProposalValidationException e) { + throw e; } catch (Throwable throwable) { - throw new ValidationException(throwable); + throw new ProposalValidationException(throwable); } } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleProposalFactory.java index 89c0083757a..4211b3d6616 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleProposalFactory.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleProposalFactory.java @@ -19,8 +19,8 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.BaseProposalFactory; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.state.DaoStateService; @@ -31,6 +31,8 @@ import javax.inject.Inject; +import java.util.HashMap; + import lombok.extern.slf4j.Slf4j; /** @@ -57,7 +59,7 @@ public RoleProposalFactory(BsqWalletService bsqWalletService, } public ProposalWithTransaction createProposalWithTransaction(Role role) - throws ValidationException, InsufficientMoneyException, TxException { + throws ProposalValidationException, InsufficientMoneyException, TxException { this.role = role; return super.createProposalWithTransaction(role.getName(), role.getLink()); @@ -65,6 +67,6 @@ public ProposalWithTransaction createProposalWithTransaction(Role role) @Override protected RoleProposal createProposalWithoutTxId() { - return new RoleProposal(role); + return new RoleProposal(role, new HashMap<>()); } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleValidator.java index a57d6f6d6a6..9f2ff334eb8 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleValidator.java @@ -17,22 +17,25 @@ package bisq.core.dao.governance.proposal.role; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.ConsensusCritical; import bisq.core.dao.governance.period.PeriodService; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalValidator; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.Role; import bisq.core.dao.state.model.governance.RoleProposal; import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; -import static org.apache.commons.lang3.Validate.notEmpty; +import static org.apache.commons.lang3.Validate.notNull; +/** + * Changes here can potentially break consensus! + */ @Slf4j -public class RoleValidator extends ProposalValidator { +public class RoleValidator extends ProposalValidator implements ConsensusCritical { @Inject public RoleValidator(DaoStateService daoStateService, PeriodService periodService) { @@ -40,18 +43,14 @@ public RoleValidator(DaoStateService daoStateService, PeriodService periodServic } @Override - public void validateDataFields(Proposal proposal) throws ValidationException { + public void validateDataFields(Proposal proposal) throws ProposalValidationException { try { super.validateDataFields(proposal); RoleProposal roleProposal = (RoleProposal) proposal; - Role role = roleProposal.getRole(); - - //TODO - notEmpty(role.getName(), "role.name must not be empty"); - + notNull(roleProposal.getRole(), "Bonded role must not be null"); } catch (Throwable throwable) { - throw new ValidationException(throwable); + throw new ProposalValidationException(throwable); } } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalPayload.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalPayload.java index 45bdc837b84..c6fa08a6c64 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalPayload.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalPayload.java @@ -24,6 +24,7 @@ import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.crypto.Hash; import bisq.common.proto.persistable.PersistableEnvelope; import bisq.common.util.Utilities; @@ -32,10 +33,6 @@ import com.google.protobuf.ByteString; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import lombok.Value; import lombok.extern.slf4j.Slf4j; @@ -111,10 +108,8 @@ public byte[] getHash() { /////////////////////////////////////////////////////////////////////////////////////////// @Override - public List getRequiredCapabilities() { - return new ArrayList<>(Collections.singletonList( - Capabilities.Capability.PROPOSAL.ordinal() - )); + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.PROPOSAL); } @Override diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java index e8b510e5b5b..8ba16390920 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java @@ -25,6 +25,7 @@ import bisq.network.p2p.storage.payload.ProtectedStoragePayload; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.crypto.Sig; import bisq.common.proto.persistable.PersistablePayload; @@ -36,9 +37,6 @@ import java.security.PublicKey; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -130,10 +128,8 @@ public PublicKey getOwnerPubKey() { /////////////////////////////////////////////////////////////////////////////////////////// @Override - public List getRequiredCapabilities() { - return new ArrayList<>(Collections.singletonList( - Capabilities.Capability.PROPOSAL.ordinal() - )); + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.PROPOSAL); } diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultException.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultException.java index 06cd04d46a9..399ec31778d 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultException.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultException.java @@ -69,6 +69,10 @@ public static class ValidationException extends Exception { super("Validation of vote result failed.", cause); } + public ValidationException(String message) { + super(message); + } + @Override public String toString() { return "VoteResultException{" + diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java index c3b558f1c6c..ee145434524 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java @@ -304,6 +304,8 @@ private Function txOutputToDecryptedBallot return getDecryptedBallotsWithMerits(voteRevealTxId, currentCycle, voteRevealOpReturnData, blindVoteTxId, hashOfBlindVoteList, blindVoteStake, optionalBlindVote.get()); } + + // We are missing P2P network data return getEmptyDecryptedBallotsWithMerits(voteRevealTxId, blindVoteTxId, hashOfBlindVoteList, blindVoteStake); } catch (Throwable e) { @@ -526,17 +528,14 @@ private Set getEvaluatedProposals(Set requiredVoteThreshold) { - evaluatedProposals.add(new EvaluatedProposal(true, proposalVoteResult, - requiredQuorum, requiredVoteThreshold)); + evaluatedProposals.add(new EvaluatedProposal(true, proposalVoteResult)); } else { - evaluatedProposals.add(new EvaluatedProposal(false, proposalVoteResult, - requiredQuorum, requiredVoteThreshold)); + evaluatedProposals.add(new EvaluatedProposal(false, proposalVoteResult)); log.info("Proposal did not reach the requiredVoteThreshold. reachedThreshold={} %, " + "requiredVoteThreshold={} %", reachedThreshold / 100D, requiredVoteThreshold / 100D); } } else { - evaluatedProposals.add(new EvaluatedProposal(false, proposalVoteResult, - requiredQuorum, requiredVoteThreshold)); + evaluatedProposals.add(new EvaluatedProposal(false, proposalVoteResult)); log.info("Proposal did not reach the requiredQuorum. reachedQuorum={}, requiredQuorum={}", reachedQuorum, requiredQuorum); } @@ -554,10 +553,7 @@ private Set getEvaluatedProposals(Set() { public void onSuccess(Void ignore) { - log.trace("onSuccess"); } public void onFailure(@NotNull Throwable throwable) { diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutputType.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutputType.java index 5b21bd5bcb3..0018f273d50 100644 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutputType.java +++ b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutputType.java @@ -21,7 +21,8 @@ // Need to be in sync with TxOutputType enum JsonTxOutputType { - UNDEFINED_OUTPUT("Undefined"), + UNDEFINED("Undefined"), + UNDEFINED_OUTPUT("Undefined output"), GENESIS_OUTPUT("Genesis"), BSQ_OUTPUT("BSQ"), BTC_OUTPUT("BTC"), diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java index b58c2b6723e..f0e972c6ea8 100644 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java +++ b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java @@ -21,7 +21,8 @@ // Need to be in sync with TxOutputType enum JsonTxType { - UNDEFINED_TX_TYPE("Undefined"), + UNDEFINED("Undefined"), + UNDEFINED_TX_TYPE("Undefined tx type"), UNVERIFIED("Unverified"), INVALID("Invalid"), GENESIS("Genesis"), 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 4d84494b758..82db3d23acc 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 @@ -185,7 +185,9 @@ private void parseBlocksIfNewBlockAvailable(int chainHeight) { } else { log.info("parseBlocksIfNewBlockAvailable did not result in a new block, so we complete."); log.info("parse {} blocks took {} seconds", blocksToParseInBatch, (System.currentTimeMillis() - parseInBatchStartTime) / 1000d); - onParseBlockChainComplete(); + if (!parseBlockchainComplete) { + onParseBlockChainComplete(); + } } }, this::handleError); diff --git a/core/src/main/java/bisq/core/dao/node/full/RpcService.java b/core/src/main/java/bisq/core/dao/node/full/RpcService.java index 303b3677e28..3abe81bfbc4 100644 --- a/core/src/main/java/bisq/core/dao/node/full/RpcService.java +++ b/core/src/main/java/bisq/core/dao/node/full/RpcService.java @@ -74,7 +74,6 @@ public class RpcService { private final String rpcPassword; private final String rpcPort; private final String rpcBlockPort; - private final boolean dumpBlockchainData; private BtcdClient client; private BtcdDaemon daemon; @@ -92,8 +91,7 @@ public class RpcService { @Inject public RpcService(Preferences preferences, @Named(DaoOptionKeys.RPC_PORT) String rpcPort, - @Named(DaoOptionKeys.RPC_BLOCK_NOTIFICATION_PORT) String rpcBlockPort, - @Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) { + @Named(DaoOptionKeys.RPC_BLOCK_NOTIFICATION_PORT) String rpcBlockPort) { this.rpcUser = preferences.getRpcUser(); this.rpcPassword = preferences.getRpcPw(); @@ -101,14 +99,15 @@ public RpcService(Preferences preferences, boolean isPortSet = rpcPort != null && !rpcPort.isEmpty(); boolean isMainnet = BisqEnvironment.getBaseCurrencyNetwork().isMainnet(); boolean isTestnet = BisqEnvironment.getBaseCurrencyNetwork().isTestnet(); + boolean isDaoTestNet = BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet(); + boolean isDaoBetaNet = BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet(); this.rpcPort = isPortSet ? rpcPort : - isMainnet ? "8332" : + isMainnet || isDaoBetaNet ? "8332" : isTestnet ? "18332" : - "18443"; // regtest + isDaoTestNet ? "18443" : + "18443"; // regtest this.rpcBlockPort = rpcBlockPort != null && !rpcBlockPort.isEmpty() ? rpcBlockPort : "5125"; - this.dumpBlockchainData = dumpBlockchainData; - log.info("Version of btcd-cli4j library: {}", BtcdCli4jVersion.VERSION); } @@ -223,8 +222,8 @@ void requestBtcBlock(int blockHeight, List txList = rawBtcBlock.getTx().stream() .map(e -> getTxFromRawTransaction(e, rawBtcBlock)) .collect(Collectors.toList()); - log.debug("requestBtcBlock with all txs took {} ms at blockHeight {}; txList.size={}", - System.currentTimeMillis() - startTs, blockHeight, txList.size()); + log.info("requestBtcBlock from bitcoind at blockHeight {} with {} txs took {} ms", + blockHeight, txList.size(), System.currentTimeMillis() - startTs); return new RawBlock(rawBtcBlock.getHeight(), rawBtcBlock.getTime() * 1000, // rawBtcBlock.getTime() is in sec but we want ms rawBtcBlock.getHash(), @@ -303,7 +302,7 @@ private RawTx getTxFromRawTransaction(RawTransaction rawBtcTx, com.neemre.btcdcl // We don't support raw MS which are the only case where scriptPubKey.getAddresses()>1 String address = scriptPubKey.getAddresses() != null && scriptPubKey.getAddresses().size() == 1 ? scriptPubKey.getAddresses().get(0) : null; - PubKeyScript pubKeyScript = dumpBlockchainData ? new PubKeyScript(scriptPubKey) : null; + PubKeyScript pubKeyScript = new PubKeyScript(scriptPubKey); return new RawTxOutput(rawBtcTxOutput.getN(), rawBtcTxOutput.getValue().movePointRight(8).longValue(), rawBtcTx.getTxId(), diff --git a/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java b/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java index c53742c9719..4a919428e4c 100644 --- a/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java +++ b/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java @@ -32,7 +32,6 @@ import bisq.network.p2p.peers.PeerManager; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; import javax.inject.Inject; @@ -96,7 +95,6 @@ public void start() { @SuppressWarnings("Duplicates") public void shutDown() { - Log.traceCall(); stopped = true; networkNode.removeMessageListener(this); peerManager.removeListener(this); @@ -138,7 +136,6 @@ public void onAwakeFromStandby() { public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof GetBlocksRequest) { // We received a GetBlocksRequest from a liteNode - Log.traceCall(networkEnvelope.toString() + "\n\tconnection=" + connection); if (!stopped) { final String uid = connection.getUid(); if (!getBlocksRequestHandlers.containsKey(uid)) { @@ -148,7 +145,6 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { @Override public void onComplete() { getBlocksRequestHandlers.remove(uid); - log.trace("requestDataHandshake completed.\n\tConnection={}", connection); } @Override diff --git a/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java b/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java index c0060bf9736..e741afabcca 100644 --- a/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java +++ b/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java @@ -29,7 +29,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; @@ -90,7 +89,6 @@ public GetBlocksRequestHandler(NetworkNode networkNode, DaoStateService daoState /////////////////////////////////////////////////////////////////////////////////////////// public void onGetBlocksRequest(GetBlocksRequest getBlocksRequest, final Connection connection) { - Log.traceCall(getBlocksRequest + "\n\tconnection=" + connection); List blocks = new LinkedList<>(daoStateService.getBlocksFromBlockHeight(getBlocksRequest.getFromBlockHeight())); List rawBlocks = blocks.stream().map(RawBlock::fromBlock).collect(Collectors.toList()); GetBlocksResponse getBlocksResponse = new GetBlocksResponse(rawBlocks, getBlocksRequest.getNonce()); 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 0bd7df0ce5b..25e85eb3159 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 @@ -32,7 +32,6 @@ import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.app.DevEnv; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; import bisq.common.util.Tuple2; @@ -125,7 +124,6 @@ public void start() { @SuppressWarnings("Duplicates") public void shutDown() { - Log.traceCall(); stopped = true; stopRetryTimer(); networkNode.removeMessageListener(this); @@ -165,12 +163,10 @@ public void reset() { @Override public void onConnection(Connection connection) { - Log.traceCall(); } @Override public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { - Log.traceCall(); closeHandler(connection); if (peerManager.isNodeBanned(closeConnectionReason, connection)) { @@ -192,7 +188,6 @@ public void onError(Throwable throwable) { @Override public void onAllConnectionsLost() { - Log.traceCall(); closeAllHandlers(); stopRetryTimer(); stopped = true; @@ -202,7 +197,6 @@ public void onAllConnectionsLost() { @Override public void onNewConnectionAfterAllConnectionsLost() { - Log.traceCall(); closeAllHandlers(); stopped = false; tryWithNewSeedNode(lastRequestedBlockHeight); @@ -309,7 +303,6 @@ public void onFault(String errorMessage, @Nullable Connection connection) { /////////////////////////////////////////////////////////////////////////////////////////// private void tryWithNewSeedNode(int startBlockHeight) { - Log.traceCall(); if (retryTimer == null) { retryCounter++; if (retryCounter <= MAX_RETRY) { @@ -357,7 +350,7 @@ private void closeHandler(Connection connection) { NodeAddress nodeAddress = peersNodeAddressOptional.get(); removeFromRequestBlocksHandlerMap(nodeAddress); } else { - log.trace("closeHandler: nodeAddress not set in connection " + connection); + log.trace("closeHandler: nodeAddress not set in connection {}", connection); } } diff --git a/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java b/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java index 4b3d6051e6c..638f34e78ec 100644 --- a/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java +++ b/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java @@ -29,7 +29,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; import com.google.common.util.concurrent.FutureCallback; @@ -169,7 +168,6 @@ public void onFailure(@NotNull Throwable throwable) { public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof GetBlocksResponse) { if (connection.getPeersNodeAddressOptional().isPresent() && connection.getPeersNodeAddressOptional().get().equals(nodeAddress)) { - Log.traceCall(networkEnvelope.toString() + "\n\tconnection=" + connection); if (!stopped) { GetBlocksResponse getBlocksResponse = (GetBlocksResponse) networkEnvelope; if (getBlocksResponse.getRequestNonce() == nonce) { @@ -214,7 +212,6 @@ private void handleFault(String errorMessage, NodeAddress nodeAddress, CloseConn } private void cleanup() { - Log.traceCall(); stopped = true; networkNode.removeMessageListener(this); stopTimeoutTimer(); diff --git a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java index ee66b69d1a6..c8a5fac7ec4 100644 --- a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java +++ b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java @@ -21,15 +21,12 @@ import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.app.Version; import bisq.common.proto.network.NetworkEnvelope; import io.bisq.generated.protobuffer.PB; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import lombok.EqualsAndHashCode; import lombok.Getter; @@ -68,10 +65,8 @@ public static NetworkEnvelope fromProto(PB.GetBlocksRequest proto, int messageVe } @Override - public List getRequiredCapabilities() { - return new ArrayList<>(Collections.singletonList( - Capabilities.Capability.DAO_FULL_NODE.ordinal() - )); + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.DAO_FULL_NODE); } diff --git a/core/src/main/java/bisq/core/dao/node/messages/NewBlockBroadcastMessage.java b/core/src/main/java/bisq/core/dao/node/messages/NewBlockBroadcastMessage.java index fea0f3295fa..4bca1438788 100644 --- a/core/src/main/java/bisq/core/dao/node/messages/NewBlockBroadcastMessage.java +++ b/core/src/main/java/bisq/core/dao/node/messages/NewBlockBroadcastMessage.java @@ -23,15 +23,12 @@ import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.app.Version; import bisq.common.proto.network.NetworkEnvelope; import io.bisq.generated.protobuffer.PB; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import lombok.EqualsAndHashCode; import lombok.Getter; @@ -70,9 +67,7 @@ public static NetworkEnvelope fromProto(PB.NewBlockBroadcastMessage proto, int m } @Override - public List getRequiredCapabilities() { - return new ArrayList<>(Collections.singletonList( - Capabilities.Capability.BSQ_BLOCK.ordinal() - )); + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.BSQ_BLOCK); } } diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java index 6f4b111bd9a..89470b1e693 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java @@ -68,7 +68,7 @@ public TxInputParser(DaoStateService daoStateService) { /////////////////////////////////////////////////////////////////////////////////////////// void process(TxOutputKey txOutputKey, int blockHeight, String txId, int inputIndex) { - if (!daoStateService.isConfiscated(txOutputKey)) { + if (!daoStateService.isConfiscatedOutput(txOutputKey)) { daoStateService.getUnspentTxOutput(txOutputKey) .ifPresent(connectedTxOutput -> { long inputValue = connectedTxOutput.getValue(); diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java index 7a313adae81..1838d5c1d6a 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java @@ -100,7 +100,7 @@ void processOpReturnOutput(TempTxOutput tempTxOutput) { } void processTxOutput(TempTxOutput tempTxOutput) { - if (!daoStateService.isConfiscated(tempTxOutput.getKey())) { + if (!daoStateService.isConfiscatedOutput(tempTxOutput.getKey())) { // We don not expect here an opReturn output as we do not get called on the last output. Any opReturn at // another output index is invalid. if (tempTxOutput.isOpReturnOutput()) { diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateService.java b/core/src/main/java/bisq/core/dao/state/DaoStateService.java index 281f7ef7949..f7cbde5ef84 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateService.java @@ -197,16 +197,17 @@ public void onNewBlockWithEmptyTxs(Block block) { if (daoState.getBlocks().isEmpty() && block.getHeight() != getGenesisBlockHeight()) { log.warn("We don't have any blocks yet and we received a block which is not the genesis block. " + "We ignore that block as the first block need to be the genesis block. " + - "That might happen in edge cases at reorgs."); + "That might happen in edge cases at reorgs. Received block={}", block); } else { daoState.getBlocks().add(block); - log.info("New Block added at blockHeight " + block.getHeight()); + log.info("New Block added at blockHeight {}", block.getHeight()); } } // Third we get the onParseBlockComplete called after all rawTxs of blocks have been parsed public void onParseBlockComplete(Block block) { + log.info("Parse block completed: Block height {}, {} BSQ transactions.", block.getHeight(), block.getTxs().size()); // We use 2 different handlers as we don't want to update domain listeners during batch processing of all // blocks as that cause performance issues. In earlier versions when we updated at each block it took // 50 sec. for 4000 blocks, after that change it was about 4 sec. @@ -218,6 +219,7 @@ public void onParseBlockComplete(Block block) { // Called after parsing of all pending blocks is completed public void onParseBlockChainComplete() { + log.info("Parse blockchain completed"); parseBlockChainComplete = true; getLastBlock().ifPresent(block -> { @@ -294,7 +296,7 @@ public int getGenesisBlockHeight() { } public Coin getGenesisTotalSupply() { - return GenesisTxInfo.GENESIS_TOTAL_SUPPLY; + return Coin.valueOf(genesisTxInfo.getGenesisTotalSupply()); } public Optional getGenesisTx() { @@ -748,6 +750,10 @@ public Optional getLockupTxFromUnlockTxId(String unlockTxId) { return getTx(unlockTxId).flatMap(tx -> getTx(tx.getTxInputs().get(0).getConnectedTxOutputTxId())); } + public Optional getUnlockTxFromLockupTxId(String lockupTxId) { + return getTx(lockupTxId).flatMap(tx -> getSpentInfo(tx.getTxOutputs().get(0))).flatMap(spentInfo -> getTx(spentInfo.getTxId())); + } + // Unlocked public Optional getUnlockBlockHeight(String txId) { return getTx(txId).map(Tx::getUnlockBlockHeight); @@ -817,7 +823,7 @@ private void doConfiscateBond(String lockupTxId) { daoState.getConfiscatedLockupTxList().add(lockupTxId); } - public boolean isConfiscated(TxOutputKey txOutputKey) { + public boolean isConfiscatedOutput(TxOutputKey txOutputKey) { if (isLockupOutput(txOutputKey)) return isConfiscatedLockupTxOutput(txOutputKey.getTxId()); else if (isUnspentUnlockOutput(txOutputKey)) @@ -825,17 +831,13 @@ else if (isUnspentUnlockOutput(txOutputKey)) return false; } - public boolean isConfiscated(String lockupTxId) { - return daoState.getConfiscatedLockupTxList().contains(lockupTxId); - } - public boolean isConfiscatedLockupTxOutput(String lockupTxId) { - return isConfiscated(lockupTxId); + return daoState.getConfiscatedLockupTxList().contains(lockupTxId); } public boolean isConfiscatedUnlockTxOutput(String unlockTxId) { return getLockupTxFromUnlockTxId(unlockTxId). - map(lockupTx -> isConfiscated(lockupTx.getId())). + map(lockupTx -> isConfiscatedLockupTxOutput(lockupTx.getId())). orElse(false); } @@ -855,21 +857,6 @@ public void setNewParam(int blockHeight, Param param, String paramValue) { }); } - public Coin getParamValueAsCoin(Param param, int blockHeight) { - String paramValue = getParamValue(param, blockHeight); - return bsqFormatter.parseParamValueToCoin(param, paramValue); - } - - public double getParamValueAsPercentDouble(Param param, int blockHeight) { - String paramValue = getParamValue(param, blockHeight); - return bsqFormatter.parsePercentStringToDouble(paramValue); - } - - public int getParamValueAsBlock(Param param, int blockHeight) { - String paramValue = getParamValue(param, blockHeight); - return Integer.parseInt(paramValue); - } - public String getParamValue(Param param, int blockHeight) { List paramChangeList = new ArrayList<>(daoState.getParamChangeList()); if (!paramChangeList.isEmpty()) { @@ -887,6 +874,30 @@ public String getParamValue(Param param, int blockHeight) { return param.getDefaultValue(); } + public Coin getParamValueAsCoin(Param param, String paramValue) { + return bsqFormatter.parseParamValueToCoin(param, paramValue); + } + + public double getParamValueAsPercentDouble(String paramValue) { + return bsqFormatter.parsePercentStringToDouble(paramValue); + } + + public int getParamValueAsBlock(String paramValue) { + return Integer.parseInt(paramValue); + } + + public Coin getParamValueAsCoin(Param param, int blockHeight) { + return getParamValueAsCoin(param, getParamValue(param, blockHeight)); + } + + public double getParamValueAsPercentDouble(Param param, int blockHeight) { + return getParamValueAsPercentDouble(getParamValue(param, blockHeight)); + } + + public int getParamValueAsBlock(Param param, int blockHeight) { + return getParamValueAsBlock(getParamValue(param, blockHeight)); + } + /////////////////////////////////////////////////////////////////////////////////////////// // SpentInfo diff --git a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java index d0b6656b76a..51a797a62b0 100644 --- a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java +++ b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java @@ -43,16 +43,25 @@ public class GenesisTxInfo { // Static /////////////////////////////////////////////////////////////////////////////////////////// - public static final Coin GENESIS_TOTAL_SUPPLY = Coin.valueOf(250_000_000); // 2.5M BSQ - private static final String MAINNET_GENESIS_TX_ID = "81855816eca165f17f0668898faa8724a105196e90ffc4993f4cac980176674e"; private static final int MAINNET_GENESIS_BLOCK_HEIGHT = 524717; // 2018-05-27 + private static final Coin MAINNET_GENESIS_TOTAL_SUPPLY = Coin.valueOf(250_000_000); // 2.5M BSQ / 2.50000000 BTC private static final String TESTNET_GENESIS_TX_ID = "09e70ce0ab7a962a82a2ca84c9ae8a89140bf1c3fb6f7efad6162e39e4b362ae"; private static final int TESTNET_GENESIS_BLOCK_HEIGHT = 1446300; // 2018-12-02 + private static final Coin TESTNET_GENESIS_TOTAL_SUPPLY = Coin.valueOf(250_000_000); // 2.5M BSQ / 2.50000000 BTC + + private static final String DAO_TESTNET_GENESIS_TX_ID = "cb316a186b9e88d1b8e1ce8dc79cc6a2080cc7bbc6df94f2be325d8253417af1"; + private static final int DAO_TESTNET_GENESIS_BLOCK_HEIGHT = 104; // 2019-02-19 + private static final Coin DAO_TESTNET_GENESIS_TOTAL_SUPPLY = Coin.valueOf(250_000_000); // 2.5M BSQ / 2.50000000 BTC + + private static final String DAO_BETANET_GENESIS_TX_ID = "81855816eca165f17f0668898faa8724a105196e90ffc4993f4cac980176674e"; + private static final int DAO_BETANET_GENESIS_BLOCK_HEIGHT = 524717; // 2018-05-27 + private static final Coin DAO_BETANET_GENESIS_TOTAL_SUPPLY = Coin.valueOf(100_000); // 1000 BSQ / 0.00100000 BTC 0.0 79 986 95 private static final String REGTEST_GENESIS_TX_ID = "30af0050040befd8af25068cc697e418e09c2d8ebd8d411d2240591b9ec203cf"; private static final int REGTEST_GENESIS_BLOCK_HEIGHT = 111; + private static final Coin REGTEST_GENESIS_TOTAL_SUPPLY = Coin.valueOf(250_000_000); // 2.5M BSQ / 2.50000000 BTC /////////////////////////////////////////////////////////////////////////////////////////// @@ -65,6 +74,8 @@ public class GenesisTxInfo { private final String genesisTxId; @Getter private final int genesisBlockHeight; + @Getter + private final long genesisTotalSupply; // mainnet // this tx has a lot of outputs @@ -89,10 +100,13 @@ public class GenesisTxInfo { @Inject public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, - @Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) Integer genesisBlockHeight) { + @Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) Integer genesisBlockHeight, + @Named(DaoOptionKeys.GENESIS_TOTAL_SUPPLY) Long genesisTotalSupply) { BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); boolean isMainnet = baseCurrencyNetwork.isMainnet(); boolean isTestnet = baseCurrencyNetwork.isTestnet(); + boolean isDaoTestNet = baseCurrencyNetwork.isDaoTestNet(); + boolean isDaoBetaNet = baseCurrencyNetwork.isDaoBetaNet(); boolean isRegtest = baseCurrencyNetwork.isRegtest(); if (!genesisTxId.isEmpty()) { this.genesisTxId = genesisTxId; @@ -100,6 +114,10 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, this.genesisTxId = MAINNET_GENESIS_TX_ID; } else if (isTestnet) { this.genesisTxId = TESTNET_GENESIS_TX_ID; + } else if (isDaoTestNet) { + this.genesisTxId = DAO_TESTNET_GENESIS_TX_ID; + } else if (isDaoBetaNet) { + this.genesisTxId = DAO_BETANET_GENESIS_TX_ID; } else if (isRegtest) { this.genesisTxId = REGTEST_GENESIS_TX_ID; } else { @@ -112,10 +130,30 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, this.genesisBlockHeight = MAINNET_GENESIS_BLOCK_HEIGHT; } else if (isTestnet) { this.genesisBlockHeight = TESTNET_GENESIS_BLOCK_HEIGHT; + } else if (isDaoTestNet) { + this.genesisBlockHeight = DAO_TESTNET_GENESIS_BLOCK_HEIGHT; + } else if (isDaoBetaNet) { + this.genesisBlockHeight = DAO_BETANET_GENESIS_BLOCK_HEIGHT; } else if (isRegtest) { this.genesisBlockHeight = REGTEST_GENESIS_BLOCK_HEIGHT; } else { this.genesisBlockHeight = 0; } + + if (genesisTotalSupply > -1) { + this.genesisTotalSupply = genesisTotalSupply; + } else if (isMainnet) { + this.genesisTotalSupply = MAINNET_GENESIS_TOTAL_SUPPLY.value; + } else if (isTestnet) { + this.genesisTotalSupply = TESTNET_GENESIS_TOTAL_SUPPLY.value; + } else if (isDaoTestNet) { + this.genesisTotalSupply = DAO_TESTNET_GENESIS_TOTAL_SUPPLY.value; + } else if (isDaoBetaNet) { + this.genesisTotalSupply = DAO_BETANET_GENESIS_TOTAL_SUPPLY.value; + } else if (isRegtest) { + this.genesisTotalSupply = REGTEST_GENESIS_TOTAL_SUPPLY.value; + } else { + this.genesisTotalSupply = 0; + } } } diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTxOutput.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTxOutput.java index e8b5015ccf9..62f5b20c706 100644 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTxOutput.java +++ b/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTxOutput.java @@ -45,9 +45,10 @@ public abstract class BaseTxOutput implements ImmutableDaoStateModel { protected final long value; protected final String txId; - // Only set if dumpBlockchainData is true + // Before v0.9.6 it was only set if dumpBlockchainData was set to true but we changed that with 0.9.6 + // so that is is always set. We still need to support it because of backward compatibility. @Nullable - protected final PubKeyScript pubKeyScript; + protected final PubKeyScript pubKeyScript; // Has about 50 bytes, total size of TxOutput is about 300 bytes. @Nullable protected final String address; @Nullable @@ -69,7 +70,6 @@ public BaseTxOutput(int index, this.address = address; this.opReturnData = opReturnData; this.blockHeight = blockHeight; - } diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/OpReturnType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/OpReturnType.java index 2c758b7ef5f..812288a6844 100644 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/OpReturnType.java +++ b/core/src/main/java/bisq/core/dao/state/model/blockchain/OpReturnType.java @@ -31,7 +31,7 @@ */ @Immutable public enum OpReturnType implements ImmutableDaoStateModel { - //TODO add undefined ? + UNDEFINED((byte) 0x00), PROPOSAL((byte) 0x10), COMPENSATION_REQUEST((byte) 0x11), REIMBURSEMENT_REQUEST((byte) 0x12), diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/ScriptType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/ScriptType.java index e41ce970153..b09bec6760e 100644 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/ScriptType.java +++ b/core/src/main/java/bisq/core/dao/state/model/blockchain/ScriptType.java @@ -39,7 +39,7 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public enum ScriptType implements ImmutableDaoStateModel { - + UNDEFINED("undefined"), // https://github.com/bitcoin/bitcoin/blob/master/src/script/standard.cpp PUB_KEY("pubkey"), PUB_KEY_HASH("pubkeyhash"), diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputType.java index 6ddb51bea73..498c984746e 100644 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputType.java +++ b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputType.java @@ -27,6 +27,7 @@ @Immutable public enum TxOutputType implements ImmutableDaoStateModel { + UNDEFINED, // only fallback for backward compatibility in case we add a new value and old clients fall back to UNDEFINED UNDEFINED_OUTPUT, GENESIS_OUTPUT, BSQ_OUTPUT, diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java index 96d0ef00fbf..8e5339db7fc 100644 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java +++ b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java @@ -30,6 +30,7 @@ @Immutable public enum TxType implements ImmutableDaoStateModel { + UNDEFINED(false, false), // only fallback for backward compatibility in case we add a new value and old clients fall back to UNDEFINED UNDEFINED_TX_TYPE(false, false), UNVERIFIED(false, false), INVALID(false, false), diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/BondedRoleType.java b/core/src/main/java/bisq/core/dao/state/model/governance/BondedRoleType.java index d6df4dacfde..455cea5c55c 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/BondedRoleType.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/BondedRoleType.java @@ -17,6 +17,7 @@ package bisq.core.dao.state.model.governance; +import bisq.core.app.BisqEnvironment; import bisq.core.locale.Res; import lombok.Getter; @@ -28,32 +29,44 @@ * role might get removed. * * Add entry to translation file "dao.bond.bondedRoleType...." + * + * Name of the BondedRoleType must not change as that is used for serialisation in Protobuffer. The data fields are not part of + * the PB serialisation so changes for those would not change the hash for the dao state hash chain. + * As the data is not used in consensus critical code yet changing fields can be tolerated. + * For mediators and arbitrators we will use automated verification of the bond so there might be issues when we change + * the values. + * */ public enum BondedRoleType { + UNDEFINED(0, 0, "N/A", false), // admins - GITHUB_ADMIN(50_000, 60, "https://github.com/bisq-network/roles/issues/16", true), - FORUM_ADMIN(20_000, 60, "https://github.com/bisq-network/roles/issues/19", true), - TWITTER_ADMIN(20_000, 60, "https://github.com/bisq-network/roles/issues/21", true), - ROCKET_CHAT_ADMIN(20_000, 60, "https://github.com/bisq-network/roles/issues/79", true), - YOUTUBE_ADMIN(5_000, 60, "https://github.com/bisq-network/roles/issues/56", true), + GITHUB_ADMIN(50_000, 75, "https://bisq.network/roles/16", true), + FORUM_ADMIN(20_000, 75, "https://bisq.network/roles/19", true), + TWITTER_ADMIN(20_000, 75, "https://bisq.network/roles/21", true), + ROCKET_CHAT_ADMIN(20_000, 75, "https://bisq.network/roles/79", true), + YOUTUBE_ADMIN(10_000, 75, "https://bisq.network/roles/56", true), // maintainers - BISQ_MAINTAINER(50_000, 60, "https://github.com/bisq-network/roles/issues/63", true), + BISQ_MAINTAINER(50_000, 75, "https://bisq.network/roles/63", true), + BITCOINJ_MAINTAINER(20_000, 75, "https://bisq.network/roles/8", true), + NETLAYER_MAINTAINER(20_000, 75, "https://bisq.network/roles/81", true), // operators - WEBSITE_OPERATOR(50_000, 60, "https://github.com/bisq-network/roles/issues/12", true), - FORUM_OPERATOR(50_000, 60, "https://github.com/bisq-network/roles/issues/19", true), - SEED_NODE_OPERATOR(20_000, 60, "https://github.com/bisq-network/roles/issues/15", true), - PRICE_NODE_OPERATOR(20_000, 60, "https://github.com/bisq-network/roles/issues/14", true), - BTC_NODE_OPERATOR(5_000, 60, "https://github.com/bisq-network/roles/issues/67", true), - MARKETS_OPERATOR(20_000, 60, "https://github.com/bisq-network/roles/issues/9", true), - BSQ_EXPLORER_OPERATOR(20_000, 60, "https://github.com/bisq-network/roles/issues/11", true), + WEBSITE_OPERATOR(50_000, 75, "https://bisq.network/roles/12", true), + FORUM_OPERATOR(50_000, 75, "https://bisq.network/roles/19", true), + SEED_NODE_OPERATOR(20_000, 75, "https://bisq.network/roles/15", true), + DATA_RELAY_NODE_OPERATOR(20_000, 75, "https://bisq.network/roles/14", true), + BTC_NODE_OPERATOR(5_000, 75, "https://bisq.network/roles/67", true), + MARKETS_OPERATOR(20_000, 75, "https://bisq.network/roles/9", true), + BSQ_EXPLORER_OPERATOR(20_000, 75, "https://bisq.network/roles/11", true), + MOBILE_NOTIFICATIONS_RELAY_OPERATOR(20_000, 75, "https://bisq.network/roles/82", true), // other - DOMAIN_NAME_HOLDER(50_000, 60, "https://github.com/bisq-network/roles/issues/77", false), - DNS_ADMIN(50_000, 60, "https://github.com/bisq-network/roles/issues/18", false), - MEDIATOR(10_000, 60, "N/A", true), - ARBITRATOR(200_000, 60, "https://github.com/bisq-network/roles/issues/13", true); + DOMAIN_NAME_HOLDER(50_000, 75, "https://bisq.network/roles/77", false), + DNS_ADMIN(20_000, 75, "https://bisq.network/roles/18", false), + MEDIATOR(10_000, 75, "N/A", true), + ARBITRATOR(200_000, 75, "https://bisq.network/roles/13", true), + BTC_DONATION_ADDRESS_OWNER(20_000, 75, "https://bisq.network/roles/80", true); // Satoshi value of BSQ bond @@ -76,8 +89,11 @@ public enum BondedRoleType { */ BondedRoleType(long requiredBondInBsq, int unlockTimeInDays, String link, boolean allowMultipleHolders) { this.requiredBond = requiredBondInBsq * 100; - this.unlockTimeInBlocks = 5; // TODO for dev testing - //this.unlockTimeInBlocks = unlockTimeInDays * 144; + this.unlockTimeInBlocks = BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? + unlockTimeInDays * 144 : // mainnet (144 blocks per day) + BisqEnvironment.getBaseCurrencyNetwork().isRegtest() ? + 5 : // regtest (arbitrarily low value for dev testing) + 144; // testnet (relatively short time for testing purposes) this.link = link; this.allowMultipleHolders = allowMultipleHolders; } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java index b68c7877544..4c221d6e5d8 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java @@ -26,7 +26,10 @@ import io.bisq.generated.protobuffer.PB; +import org.springframework.util.CollectionUtils; + import java.util.Date; +import java.util.Map; import java.util.Objects; import lombok.Value; @@ -44,14 +47,16 @@ public final class ChangeParamProposal extends Proposal implements ImmutableDaoS public ChangeParamProposal(String name, String link, Param param, - String paramValue) { + String paramValue, + Map extraDataMap) { this(name, link, param, paramValue, Version.PROPOSAL, new Date().getTime(), - ""); + "", + extraDataMap); } @@ -65,12 +70,14 @@ private ChangeParamProposal(String name, String paramValue, byte version, long creationDate, - String txId) { + String txId, + Map extraDataMap) { super(name, link, version, creationDate, - txId); + txId, + extraDataMap); this.param = param; this.paramValue = paramValue; @@ -92,7 +99,9 @@ public static ChangeParamProposal fromProto(PB.Proposal proto) { proposalProto.getParamValue(), (byte) proto.getVersion(), proto.getCreationDate(), - proto.getTxId()); + proto.getTxId(), + CollectionUtils.isEmpty(proto.getExtraDataMap()) ? + null : proto.getExtraDataMap()); } @@ -128,7 +137,8 @@ public Proposal cloneProposalAndAddTxId(String txId) { getParamValue(), getVersion(), getCreationDate().getTime(), - txId); + txId, + extraDataMap); } @Override diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java index 13f42cbc286..38e26bb9492 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java @@ -32,7 +32,10 @@ import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Coin; +import org.springframework.util.CollectionUtils; + import java.util.Date; +import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Value; @@ -51,14 +54,16 @@ public final class CompensationProposal extends Proposal implements IssuanceProp public CompensationProposal(String name, String link, Coin requestedBsq, - String bsqAddress) { + String bsqAddress, + Map extraDataMap) { this(name, link, bsqAddress, requestedBsq.value, Version.COMPENSATION_REQUEST, new Date().getTime(), - ""); + "", + extraDataMap); } @@ -72,12 +77,14 @@ private CompensationProposal(String name, long requestedBsq, byte version, long creationDate, - String txId) { + String txId, + Map extraDataMap) { super(name, link, version, creationDate, - txId); + txId, + extraDataMap); this.requestedBsq = requestedBsq; this.bsqAddress = bsqAddress; @@ -99,7 +106,9 @@ public static CompensationProposal fromProto(PB.Proposal proto) { proposalProto.getRequestedBsq(), (byte) proto.getVersion(), proto.getCreationDate(), - proto.getTxId()); + proto.getTxId(), + CollectionUtils.isEmpty(proto.getExtraDataMap()) ? + null : proto.getExtraDataMap()); } @@ -146,7 +155,8 @@ public Proposal cloneProposalAndAddTxId(String txId) { getRequestedBsq().value, getVersion(), getCreationDate().getTime(), - txId); + txId, + extraDataMap); } @Override diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java index 8c6b64d09b9..85da27dd6fc 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java @@ -26,7 +26,10 @@ import io.bisq.generated.protobuffer.PB; +import org.springframework.util.CollectionUtils; + import java.util.Date; +import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Value; @@ -43,13 +46,15 @@ public final class ConfiscateBondProposal extends Proposal implements ImmutableD public ConfiscateBondProposal(String name, String link, - String lockupTxId) { + String lockupTxId, + Map extraDataMap) { this(name, link, lockupTxId, Version.PROPOSAL, new Date().getTime(), - ""); + "", + extraDataMap); } @@ -62,12 +67,14 @@ private ConfiscateBondProposal(String name, String lockupTxId, byte version, long creationDate, - String txId) { + String txId, + Map extraDataMap) { super(name, link, version, creationDate, - txId); + txId, + extraDataMap); this.lockupTxId = lockupTxId; } @@ -85,7 +92,9 @@ public static ConfiscateBondProposal fromProto(PB.Proposal proto) { proposalProto.getLockupTxId(), (byte) proto.getVersion(), proto.getCreationDate(), - proto.getTxId()); + proto.getTxId(), + CollectionUtils.isEmpty(proto.getExtraDataMap()) ? + null : proto.getExtraDataMap()); } @@ -120,7 +129,8 @@ public Proposal cloneProposalAndAddTxId(String txId) { getLockupTxId(), getVersion(), getCreationDate().getTime(), - txId); + txId, + extraDataMap); } @Override diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/DaoPhase.java b/core/src/main/java/bisq/core/dao/state/model/governance/DaoPhase.java index 4594fd3466f..5ae7e13ff1d 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/DaoPhase.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/DaoPhase.java @@ -26,6 +26,7 @@ import java.util.Objects; import lombok.Value; +import lombok.extern.slf4j.Slf4j; import javax.annotation.concurrent.Immutable; @@ -35,6 +36,7 @@ */ @Immutable @Value +@Slf4j public class DaoPhase implements PersistablePayload, ImmutableDaoStateModel { /** @@ -77,7 +79,14 @@ public PB.DaoPhase toProtoMessage() { } public static DaoPhase fromProto(PB.DaoPhase proto) { - return new DaoPhase(Phase.values()[proto.getPhaseOrdinal()], proto.getDuration()); + int ordinal = proto.getPhaseOrdinal(); + if (ordinal >= Phase.values().length) { + log.warn("We tried to access a ordinal outside of the DaoPhase.Phase enum bounds and set it to " + + "UNDEFINED. ordinal={}", ordinal); + return new DaoPhase(Phase.UNDEFINED, 0); + } + + return new DaoPhase(Phase.values()[ordinal], proto.getDuration()); } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/EvaluatedProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/EvaluatedProposal.java index b91cf5d18b0..d20a9f81e54 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/EvaluatedProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/EvaluatedProposal.java @@ -33,15 +33,9 @@ public class EvaluatedProposal implements PersistablePayload, ImmutableDaoStateM private final boolean isAccepted; private final ProposalVoteResult proposalVoteResult; - //TODO remove - can be derived from params - private final long requiredQuorum; - private final long requiredThreshold; - - public EvaluatedProposal(boolean isAccepted, ProposalVoteResult proposalVoteResult, long requiredQuorum, long requiredThreshold) { + public EvaluatedProposal(boolean isAccepted, ProposalVoteResult proposalVoteResult) { this.isAccepted = isAccepted; this.proposalVoteResult = proposalVoteResult; - this.requiredQuorum = requiredQuorum; - this.requiredThreshold = requiredThreshold; } @@ -53,17 +47,13 @@ public EvaluatedProposal(boolean isAccepted, ProposalVoteResult proposalVoteResu public PB.EvaluatedProposal toProtoMessage() { PB.EvaluatedProposal.Builder builder = PB.EvaluatedProposal.newBuilder() .setIsAccepted(isAccepted) - .setProposalVoteResult(proposalVoteResult.toProtoMessage()) - .setRequiredQuorum(requiredQuorum) - .setRequiredThreshold(requiredThreshold); + .setProposalVoteResult(proposalVoteResult.toProtoMessage()); return builder.build(); } public static EvaluatedProposal fromProto(PB.EvaluatedProposal proto) { return new EvaluatedProposal(proto.getIsAccepted(), - ProposalVoteResult.fromProto(proto.getProposalVoteResult()), - proto.getRequiredQuorum(), - proto.getRequiredThreshold()); + ProposalVoteResult.fromProto(proto.getProposalVoteResult())); } @@ -84,8 +74,6 @@ public String toString() { return "EvaluatedProposal{" + "\n isAccepted=" + isAccepted + ",\n proposalVoteResult=" + proposalVoteResult + - ",\n requiredQuorum=" + requiredQuorum + - ",\n requiredThreshold=" + requiredThreshold + "\n}"; } } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java index 4af6afefaf2..e2d142deb57 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java @@ -26,7 +26,10 @@ import io.bisq.generated.protobuffer.PB; +import org.springframework.util.CollectionUtils; + import java.util.Date; +import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Value; @@ -41,12 +44,14 @@ public final class GenericProposal extends Proposal implements ImmutableDaoStateModel { public GenericProposal(String name, - String link) { + String link, + Map extraDataMap) { this(name, link, Version.PROPOSAL, new Date().getTime(), - ""); + "", + extraDataMap); } @@ -58,12 +63,14 @@ private GenericProposal(String name, String link, byte version, long creationDate, - String txId) { + String txId, + Map extraDataMap) { super(name, link, version, creationDate, - txId); + txId, + extraDataMap); } @Override @@ -77,7 +84,9 @@ public static GenericProposal fromProto(PB.Proposal proto) { proto.getLink(), (byte) proto.getVersion(), proto.getCreationDate(), - proto.getTxId()); + proto.getTxId(), + CollectionUtils.isEmpty(proto.getExtraDataMap()) ? + null : proto.getExtraDataMap()); } @@ -111,7 +120,8 @@ public Proposal cloneProposalAndAddTxId(String txId) { getLink(), getVersion(), getCreationDate().getTime(), - txId); + txId, + extraDataMap); } @Override diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java index 25baae5c3e7..2dead889ca5 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java @@ -30,6 +30,7 @@ import io.bisq.generated.protobuffer.PB; import java.util.Date; +import java.util.Map; import java.util.Optional; import lombok.EqualsAndHashCode; @@ -51,18 +52,25 @@ public abstract class Proposal implements PersistablePayload, NetworkPayload, Co protected final String link; protected final byte version; protected final long creationDate; + @Nullable protected final String txId; + // This hash map allows addition of data in future versions without breaking consensus + @Nullable + protected final Map extraDataMap; + protected Proposal(String name, String link, byte version, long creationDate, - @Nullable String txId) { + @Nullable String txId, + Map extraDataMap) { this.name = name; this.link = link; this.version = version; this.creationDate = creationDate; this.txId = txId; + this.extraDataMap = extraDataMap; } @@ -76,6 +84,7 @@ public PB.Proposal.Builder getProposalBuilder() { .setLink(link) .setVersion(version) .setCreationDate(creationDate); + Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); Optional.ofNullable(txId).ifPresent(builder::setTxId); return builder; } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java index e2b3185c43c..628de5ad118 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java @@ -32,7 +32,10 @@ import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Coin; +import org.springframework.util.CollectionUtils; + import java.util.Date; +import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Value; @@ -51,14 +54,16 @@ public final class ReimbursementProposal extends Proposal implements IssuancePro public ReimbursementProposal(String name, String link, Coin requestedBsq, - String bsqAddress) { + String bsqAddress, + Map extraDataMap) { this(name, link, bsqAddress, requestedBsq.value, Version.REIMBURSEMENT_REQUEST, new Date().getTime(), - ""); + "", + extraDataMap); } @@ -72,12 +77,14 @@ private ReimbursementProposal(String name, long requestedBsq, byte version, long creationDate, - String txId) { + String txId, + Map extraDataMap) { super(name, link, version, creationDate, - txId); + txId, + extraDataMap); this.requestedBsq = requestedBsq; this.bsqAddress = bsqAddress; @@ -99,7 +106,9 @@ public static ReimbursementProposal fromProto(PB.Proposal proto) { proposalProto.getRequestedBsq(), (byte) proto.getVersion(), proto.getCreationDate(), - proto.getTxId()); + proto.getTxId(), + CollectionUtils.isEmpty(proto.getExtraDataMap()) ? + null : proto.getExtraDataMap()); } @@ -146,7 +155,8 @@ public Proposal cloneProposalAndAddTxId(String txId) { getRequestedBsq().value, getVersion(), getCreationDate().getTime(), - txId); + txId, + extraDataMap); } @Override diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java index 47d32963265..45ea9babaf1 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java @@ -26,7 +26,10 @@ import io.bisq.generated.protobuffer.PB; +import org.springframework.util.CollectionUtils; + import java.util.Date; +import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Value; @@ -43,13 +46,15 @@ public final class RemoveAssetProposal extends Proposal implements ImmutableDaoS public RemoveAssetProposal(String name, String link, - String tickerSymbol) { + String tickerSymbol, + Map extraDataMap) { this(name, link, tickerSymbol, Version.PROPOSAL, new Date().getTime(), - ""); + "", + extraDataMap); } @@ -62,12 +67,14 @@ private RemoveAssetProposal(String name, String tickerSymbol, byte version, long creationDate, - String txId) { + String txId, + Map extraDataMap) { super(name, link, version, creationDate, - txId); + txId, + extraDataMap); this.tickerSymbol = tickerSymbol; } @@ -86,7 +93,9 @@ public static RemoveAssetProposal fromProto(PB.Proposal proto) { proposalProto.getTickerSymbol(), (byte) proto.getVersion(), proto.getCreationDate(), - proto.getTxId()); + proto.getTxId(), + CollectionUtils.isEmpty(proto.getExtraDataMap()) ? + null : proto.getExtraDataMap()); } @@ -121,7 +130,8 @@ public Proposal cloneProposalAndAddTxId(String txId) { getTickerSymbol(), getVersion(), getCreationDate().getTime(), - txId); + txId, + extraDataMap); } @Override diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java index 7bf90de6cce..b4b8d535718 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java @@ -26,7 +26,10 @@ import io.bisq.generated.protobuffer.PB; +import org.springframework.util.CollectionUtils; + import java.util.Date; +import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Value; @@ -40,14 +43,19 @@ @Value public final class RoleProposal extends Proposal implements ImmutableDaoStateModel { private final Role role; + private final long requiredBond; + private final int unlockTime; // in blocks - public RoleProposal(Role role) { + public RoleProposal(Role role, Map extraDataMap) { this(role.getName(), role.getLink(), role, + role.getBondedRoleType().getRequiredBond(), + role.getBondedRoleType().getUnlockTimeInBlocks(), Version.PROPOSAL, new Date().getTime(), - ""); + "", + extraDataMap); } @@ -58,22 +66,30 @@ public RoleProposal(Role role) { private RoleProposal(String name, String link, Role role, + long requiredBond, + int unlockTime, byte version, long creationDate, - String txId) { + String txId, + Map extraDataMap) { super(name, link, version, creationDate, - txId); + txId, + extraDataMap); this.role = role; + this.requiredBond = requiredBond; + this.unlockTime = unlockTime; } @Override public PB.Proposal.Builder getProposalBuilder() { final PB.RoleProposal.Builder builder = PB.RoleProposal.newBuilder() - .setRole(role.toProtoMessage()); + .setRole(role.toProtoMessage()) + .setRequiredBond(requiredBond) + .setUnlockTime(unlockTime); return super.getProposalBuilder().setRoleProposal(builder); } @@ -82,9 +98,13 @@ public static RoleProposal fromProto(PB.Proposal proto) { return new RoleProposal(proto.getName(), proto.getLink(), Role.fromProto(proposalProto.getRole()), + proposalProto.getRequiredBond(), + proposalProto.getUnlockTime(), (byte) proto.getVersion(), proto.getCreationDate(), - proto.getTxId()); + proto.getTxId(), + CollectionUtils.isEmpty(proto.getExtraDataMap()) ? + null : proto.getExtraDataMap()); } @@ -114,18 +134,23 @@ public TxType getTxType() { @Override public Proposal cloneProposalAndAddTxId(String txId) { - return new RoleProposal(getName(), - getLink(), - this.getRole(), - getVersion(), - getCreationDate().getTime(), - txId); + return new RoleProposal(name, + link, + role, + requiredBond, + unlockTime, + version, + creationDate, + txId, + extraDataMap); } @Override public String toString() { return "RoleProposal{" + "\n role=" + role + + "\n requiredBond=" + requiredBond + + "\n unlockTime=" + unlockTime + "\n} " + super.toString(); } } diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 5aeaa556616..bbabf1ae535 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -257,7 +257,7 @@ public boolean addFilterMessageIfKeyIsValid(Filter filter, String privKeyString) boolean result = p2PService.addProtectedStorageEntry(filter, true); if (result) - log.trace("Add filter to network was successful. FilterMessage = " + filter); + log.trace("Add filter to network was successful. FilterMessage = {}", filter); } return isKeyValid; @@ -269,7 +269,7 @@ public boolean removeFilterMessageIfKeyIsValid(String privKeyString) { if (filter == null) { log.warn("Developers filter is null"); } else if (p2PService.removeData(filter, true)) { - log.trace("Remove filter from network was successful. FilterMessage = " + filter); + log.trace("Remove filter from network was successful. FilterMessage = {}", filter); user.setDevelopersFilter(null); } else { log.warn("Filter remove failed"); diff --git a/core/src/main/java/bisq/core/locale/CurrencyUtil.java b/core/src/main/java/bisq/core/locale/CurrencyUtil.java index 96510e26df4..cf790f204ae 100644 --- a/core/src/main/java/bisq/core/locale/CurrencyUtil.java +++ b/core/src/main/java/bisq/core/locale/CurrencyUtil.java @@ -132,7 +132,6 @@ public static List getMainCryptoCurrencies() { result.add(new CryptoCurrency("DASH", "Dash")); result.add(new CryptoCurrency("DCR", "Decred")); result.add(new CryptoCurrency("ETH", "Ether")); - result.add(new CryptoCurrency("GRC", "Gridcoin")); result.add(new CryptoCurrency("GRIN", "Grin")); result.add(new CryptoCurrency("LTC", "Litecoin")); result.add(new CryptoCurrency("XMR", "Monero")); @@ -152,6 +151,9 @@ public static List getRemovedCryptoCurrencies() { currencies.add(new CryptoCurrency("SC", "SpaceCash")); currencies.add(new CryptoCurrency("PPI", "PiedPiper Coin")); currencies.add(new CryptoCurrency("PEPECASH", "Pepe Cash")); + currencies.add(new CryptoCurrency("GRC", "Gridcoin")); + currencies.add(new CryptoCurrency("LTZ", "LitecoinZ")); + currencies.add(new CryptoCurrency("ZOC", "01coin")); return currencies; } @@ -381,7 +383,7 @@ public static String getNameByCode(String currencyCode) { try { return Currency.getInstance(currencyCode).getDisplayName(); } catch (Throwable t) { - log.debug("No currency name available " + t.getMessage()); + log.debug("No currency name available {}", t.getMessage()); return currencyCode; } } @@ -420,8 +422,7 @@ public static boolean assetMatchesNetworkIfMainnet(Asset asset, BaseCurrencyNetw // We want all coins available also in testnet or regtest for testing purpose public static boolean coinMatchesNetworkIfMainnet(Coin coin, BaseCurrencyNetwork baseCurrencyNetwork) { boolean matchesNetwork = assetMatchesNetwork(coin, baseCurrencyNetwork); - return !baseCurrencyNetwork.isMainnet() || - matchesNetwork; + return !baseCurrencyNetwork.isMainnet() || matchesNetwork; } private static CryptoCurrency assetToCryptoCurrency(Asset asset) { diff --git a/core/src/main/java/bisq/core/locale/Res.java b/core/src/main/java/bisq/core/locale/Res.java index a1c3a07d75e..c2cd3ac56fd 100644 --- a/core/src/main/java/bisq/core/locale/Res.java +++ b/core/src/main/java/bisq/core/locale/Res.java @@ -115,7 +115,7 @@ public static String get(String key) { .replace("Bitcoin", baseCurrencyName) .replace("bitcoin", baseCurrencyNameLowerCase); } catch (MissingResourceException e) { - log.warn("Missing resource for key: " + key); + log.warn("Missing resource for key: {}", key); e.printStackTrace(); if (DevEnv.isDevMode()) UserThread.runAfter(() -> { diff --git a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeAddresses.java b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeAddresses.java deleted file mode 100644 index 885b016ef80..00000000000 --- a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeAddresses.java +++ /dev/null @@ -1,89 +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.network.p2p.seed; - -import bisq.network.p2p.NodeAddress; - -import com.google.common.collect.ImmutableSet; - -import java.util.Set; - -class DefaultSeedNodeAddresses { - // Addresses are used if the last digit of their port match the network id: - // - mainnet use port ends in 0 - // - testnet use port ends in 1 - // - regtest use port ends in 2 - public static final Set DEFAULT_LOCALHOST_SEED_NODE_ADDRESSES = ImmutableSet.of( - // BTC - // mainnet - new NodeAddress("localhost:2000"), - new NodeAddress("localhost:3000"), - new NodeAddress("localhost:4000"), - - // testnet - new NodeAddress("localhost:2001"), - new NodeAddress("localhost:3001"), - new NodeAddress("localhost:4001"), - - // regtest - new NodeAddress("localhost:2002"), - new NodeAddress("localhost:3002") - /* new NodeAddress("localhost:4002"),*/ - ); - - // Addresses are used if their port match the network id: - // - mainnet uses port 8000 - // - testnet uses port 8001 - // - regtest uses port 8002 - public static final Set DEFAULT_TOR_SEED_NODE_ADDRESSES = ImmutableSet.of( - // BTC mainnet - new NodeAddress("5quyxpxheyvzmb2d.onion:8000"), // @miker - new NodeAddress("s67qglwhkgkyvr74.onion:8000"), // @emzy - new NodeAddress("ef5qnzx6znifo3df.onion:8000"), // @manfredkarrer - new NodeAddress("jhgcy2won7xnslrb.onion:8000"), // @manfredkarrer - new NodeAddress("3f3cu2yw7u457ztq.onion:8000"), // @manfredkarrer - new NodeAddress("723ljisnynbtdohi.onion:8000"), // @manfredkarrer - new NodeAddress("rm7b56wbrcczpjvl.onion:8000"), // @manfredkarrer - new NodeAddress("fl3mmribyxgrv63c.onion:8000"), // @manfredkarrer - - // local dev - // new NodeAddress("joehwtpe7ijnz4df.onion:8000"), - // new NodeAddress("uqxi3zrpobhtoes6.onion:8000"), - - // BTC testnet - // new NodeAddress("vjkh4ykq7x5skdlt.onion:8001"), // local dev test - new NodeAddress("fjr5w4eckjghqtnu.onion:8001"), // testnet seed 1 - new NodeAddress("74w2sttlo4qk6go3.onion:8001"), // testnet seed 2 - new NodeAddress("jmc5ajqvtnzqaggm.onion:8001"), // testnet seed 3 - new NodeAddress("3d56s6acbi3vk52v.onion:8001"), // testnet seed 4 - - // BTC regtest - // For development you need to change that to your local onion addresses - // 1. Run a seed node with prog args: --bitcoinNetwork=regtest --nodePort=8002 --myAddress=rxdkppp3vicnbgqt:8002 --appName=bisq_seed_node_rxdkppp3vicnbgqt.onion_8002 - // 2. Find your local onion address in bisq_seed_node_rxdkppp3vicnbgqt.onion_8002/regtest/tor/hiddenservice/hostname - // 3. Shut down the seed node - // 4. Rename the directory with your local onion address - // 5. Edit here your found onion address (new NodeAddress("YOUR_ONION.onion:8002") - new NodeAddress("rxdkppp3vicnbgqt.onion:8002"), - new NodeAddress("4ie52dse64kaarxw.onion:8002") - - ); - - private DefaultSeedNodeAddresses() { - } -} diff --git a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java index 6c9e160bebf..aa385dab463 100644 --- a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java +++ b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java @@ -17,59 +17,85 @@ package bisq.core.network.p2p.seed; +import bisq.core.app.BisqEnvironment; + +import bisq.network.NetworkOptionKeys; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.seed.SeedNodeRepository; import javax.inject.Inject; +import javax.inject.Named; -import java.util.Set; -import java.util.stream.Stream; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; -public class DefaultSeedNodeRepository implements SeedNodeRepository { +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; - private final Set seedNodeAddresses; - private final Set torSeedNodeAddresses; - private final Set localhostSeedNodeAddresses; +import javax.annotation.Nullable; + +public class DefaultSeedNodeRepository implements SeedNodeRepository { + //TODO add support for localhost addresses + private static final Pattern pattern = Pattern.compile("^([a-z0-9]+\\.onion:\\d+)"); + private static final String ENDING = ".seednodes"; + private static final Collection cache = new HashSet<>(); + private final BisqEnvironment bisqEnvironment; + private final String seedNodes; @Inject - public DefaultSeedNodeRepository(SeedNodeAddressLookup lookup) { - this.seedNodeAddresses = lookup.resolveNodeAddresses(); - this.torSeedNodeAddresses = DefaultSeedNodeAddresses.DEFAULT_TOR_SEED_NODE_ADDRESSES; - this.localhostSeedNodeAddresses = DefaultSeedNodeAddresses.DEFAULT_LOCALHOST_SEED_NODE_ADDRESSES; + public DefaultSeedNodeRepository(BisqEnvironment environment, + @Nullable @Named(NetworkOptionKeys.SEED_NODES_KEY) String seedNodes) { + bisqEnvironment = environment; + this.seedNodes = seedNodes; } - @Override - public Set getSeedNodeAddresses() { - return seedNodeAddresses; - } + private void reload() { + + // see if there are any seed nodes configured manually + if (seedNodes != null && !seedNodes.isEmpty()) { + cache.clear(); + Arrays.stream(seedNodes.split(",")).forEach(s -> cache.add(new NodeAddress(s))); - @Override - public String getOperator(NodeAddress nodeAddress) { - switch (nodeAddress.getFullAddress()) { - case "5quyxpxheyvzmb2d.onion:8000": - return "@miker"; - case "ef5qnzx6znifo3df.onion:8000": - return "@manfredkarrer"; - case "s67qglwhkgkyvr74.onion:8000": - return "@emzy"; - case "jhgcy2won7xnslrb.onion:8000": - return "@manfredkarrer"; - case "3f3cu2yw7u457ztq.onion:8000": - return "@manfredkarrer"; - case "723ljisnynbtdohi.onion:8000": - return "@manfredkarrer"; - case "rm7b56wbrcczpjvl.onion:8000": - return "@manfredkarrer"; - case "fl3mmribyxgrv63c.onion:8000": - return "@manfredkarrer"; - default: - return "Undefined"; + return; } + + // else, we fetch the seed nodes from our resources + final InputStream fileInputStream = DefaultSeedNodeRepository.class.getClassLoader().getResourceAsStream(BisqEnvironment.getBaseCurrencyNetwork().name().toLowerCase() + ENDING); + final BufferedReader seedNodeFile = new BufferedReader(new InputStreamReader(fileInputStream)); + + // only clear if we have a fresh data source (otherwise, an exception would prevent us from getting here) + cache.clear(); + + // refill the cache + seedNodeFile.lines().forEach(line -> { + final Matcher matcher = pattern.matcher(line); + if (matcher.find()) + cache.add(new NodeAddress(matcher.group(1))); + + // Maybe better include in regex... + if (line.startsWith("localhost")) + cache.add(new NodeAddress(line)); + }); + + // filter + cache.removeAll(bisqEnvironment.getBannedSeedNodes().stream().map(NodeAddress::new).collect(Collectors.toSet())); + } + + public Collection getSeedNodeAddresses() { + if (cache.isEmpty()) + reload(); + + return cache; } - @Override public boolean isSeedNode(NodeAddress nodeAddress) { - return Stream.concat(localhostSeedNodeAddresses.stream(), torSeedNodeAddresses.stream()) - .anyMatch(e -> e.equals(nodeAddress)); + if (cache.isEmpty()) + reload(); + return cache.contains(nodeAddress); } } diff --git a/core/src/main/java/bisq/core/network/p2p/seed/SeedNodeAddressLookup.java b/core/src/main/java/bisq/core/network/p2p/seed/SeedNodeAddressLookup.java deleted file mode 100644 index dc28119d615..00000000000 --- a/core/src/main/java/bisq/core/network/p2p/seed/SeedNodeAddressLookup.java +++ /dev/null @@ -1,106 +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.network.p2p.seed; - -import bisq.core.app.BisqEnvironment; - -import bisq.network.NetworkOptionKeys; -import bisq.network.p2p.NodeAddress; - -import com.google.inject.name.Named; - -import javax.inject.Inject; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; - -public class SeedNodeAddressLookup { - private static final Logger log = LoggerFactory.getLogger(SeedNodeAddressLookup.class); - - private final BisqEnvironment environment; - private final boolean isLocalHostUsed; - private final int networkId; - @Nullable - private final String myAddress; - @Nullable - private final String seedNodes; - - @Inject - public SeedNodeAddressLookup(BisqEnvironment environment, - @Named(NetworkOptionKeys.USE_LOCALHOST_FOR_P2P) boolean useLocalhostForP2P, - @Named(NetworkOptionKeys.NETWORK_ID) int networkId, - @Nullable @Named(NetworkOptionKeys.MY_ADDRESS) String myAddress, - @Nullable @Named(NetworkOptionKeys.SEED_NODES_KEY) String seedNodes) { - this.environment = environment; - this.isLocalHostUsed = useLocalhostForP2P; - this.networkId = networkId; - this.myAddress = myAddress; - this.seedNodes = seedNodes; - } - - public Set resolveNodeAddresses() { - SeedNodeAddresses allSeedNodeAddresses = getAllAddresses(); - - Set bannedHosts = getBannedHosts(); - allSeedNodeAddresses = allSeedNodeAddresses.excludeByHost(bannedHosts); - - if (myAddress != null) { - allSeedNodeAddresses = allSeedNodeAddresses.excludeByFullAddress(myAddress); - } - - log.debug("We received banned seed nodes={}, seedNodeAddresses={}", bannedHosts, allSeedNodeAddresses); - return allSeedNodeAddresses; - } - - private Set getBannedHosts() { - return Optional.ofNullable(environment.getBannedSeedNodes()) - .map(HashSet::new) - .map(hosts -> (Set) hosts) - .orElse(Collections.emptySet()); - } - - private SeedNodeAddresses getAllAddresses() { - SeedNodeAddresses seedNodeAddresses = Optional.ofNullable(seedNodes) - .map(nodes -> SeedNodeAddresses.fromString(seedNodes)) - .orElse(new SeedNodeAddresses(Collections.emptySet())); - - if (seedNodeAddresses.isEmpty()) { - Set delegate = isLocalHostUsed - ? DefaultSeedNodeAddresses.DEFAULT_LOCALHOST_SEED_NODE_ADDRESSES - : DefaultSeedNodeAddresses.DEFAULT_TOR_SEED_NODE_ADDRESSES; - seedNodeAddresses = delegate.stream() - .filter(address -> isAddressFromNetwork(address, networkId)) - .collect(SeedNodeAddresses.collector()); - } - return seedNodeAddresses; - } - - private static boolean isAddressFromNetwork(NodeAddress address, int networkId) { - String suffix = "0" + networkId; - int port = address.getPort(); - String portAsString = String.valueOf(port); - return portAsString.endsWith(suffix); - } -} diff --git a/core/src/main/java/bisq/core/network/p2p/seed/SeedNodeAddresses.java b/core/src/main/java/bisq/core/network/p2p/seed/SeedNodeAddresses.java deleted file mode 100644 index ad451e21722..00000000000 --- a/core/src/main/java/bisq/core/network/p2p/seed/SeedNodeAddresses.java +++ /dev/null @@ -1,67 +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.network.p2p.seed; - -import bisq.network.p2p.NodeAddress; - -import org.apache.commons.lang3.StringUtils; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collector; -import java.util.stream.Collectors; - -class SeedNodeAddresses extends ImmutableSetDecorator { - - public SeedNodeAddresses(Set delegate) { - super(delegate); - } - - public SeedNodeAddresses excludeByHost(Set hosts) { - Set copy = new HashSet<>(this); - copy.removeIf(address -> { - String hostName = address.getHostName(); - return hosts.contains(hostName); - }); - return new SeedNodeAddresses(copy); - } - - public SeedNodeAddresses excludeByFullAddress(String fullAddress) { - Set copy = new HashSet<>(this); - copy.removeIf(address -> fullAddress.equals(address.getFullAddress())); - return new SeedNodeAddresses(copy); - } - - public static Collector collector() { - return Collectors.collectingAndThen(Collectors.toSet(), SeedNodeAddresses::new); - } - - public static SeedNodeAddresses fromString(String seedNodes) { - if (seedNodes.isEmpty()) { - return new SeedNodeAddresses(Collections.emptySet()); - } - - String trimmed = StringUtils.deleteWhitespace(seedNodes); - String[] nodes = trimmed.split(","); - return Arrays.stream(nodes) - .map(NodeAddress::new) - .collect(collector()); - } -} diff --git a/core/src/main/java/bisq/core/notifications/MobileNotificationService.java b/core/src/main/java/bisq/core/notifications/MobileNotificationService.java index 9e75453f1bc..f673376c4c5 100644 --- a/core/src/main/java/bisq/core/notifications/MobileNotificationService.java +++ b/core/src/main/java/bisq/core/notifications/MobileNotificationService.java @@ -169,7 +169,6 @@ private boolean sendMessage(MobileMessage message, boolean useSound, Consumer resultHandler, Consumer errorHandler) throws Exception { - log.info("Send message: '{}'", message.getMessage()); if (mobileModel.getKey() == null) return false; @@ -199,6 +198,9 @@ private boolean sendMessage(MobileMessage message, if (!doSend) return false; + log.info("Send message: '{}'", message.getMessage()); + + log.info("sendMessage message={}", message); Gson gson = new Gson(); String json = gson.toJson(message); diff --git a/core/src/main/java/bisq/core/notifications/alerts/DisputeMsgEvents.java b/core/src/main/java/bisq/core/notifications/alerts/DisputeMsgEvents.java index a28293219e8..e989317f770 100644 --- a/core/src/main/java/bisq/core/notifications/alerts/DisputeMsgEvents.java +++ b/core/src/main/java/bisq/core/notifications/alerts/DisputeMsgEvents.java @@ -61,9 +61,9 @@ public void onAllServicesInitialized() { private void setDisputeListener(Dispute dispute) { //TODO use weak ref or remove listener - log.info("We got a dispute added. id={}, tradeId={}", dispute.getId(), dispute.getTradeId()); + log.debug("We got a dispute added. id={}, tradeId={}", dispute.getId(), dispute.getTradeId()); dispute.getDisputeCommunicationMessages().addListener((ListChangeListener) c -> { - log.info("We got a DisputeCommunicationMessage added. id={}, tradeId={}", dispute.getId(), dispute.getTradeId()); + log.debug("We got a DisputeCommunicationMessage added. id={}, tradeId={}", dispute.getId(), dispute.getTradeId()); c.next(); if (c.wasAdded()) { c.getAddedSubList().forEach(this::setDisputeCommunicationMessage); diff --git a/core/src/main/java/bisq/core/offer/OfferBookService.java b/core/src/main/java/bisq/core/offer/OfferBookService.java index 78feab5a39d..4865876d260 100644 --- a/core/src/main/java/bisq/core/offer/OfferBookService.java +++ b/core/src/main/java/bisq/core/offer/OfferBookService.java @@ -134,7 +134,6 @@ public void onRemoved(Offer offer) { public void addOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { boolean result = p2PService.addProtectedStorageEntry(offer.getOfferPayload(), true); if (result) { - log.trace("Add offer to network was successful. OfferPayload ID = " + offer.getId()); resultHandler.handleResult(); } else { errorMessageHandler.handleErrorMessage("Add offer failed"); @@ -144,7 +143,6 @@ public void addOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandl public void refreshTTL(OfferPayload offerPayload, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { boolean result = p2PService.refreshTTL(offerPayload, true); if (result) { - log.trace("Refresh TTL was successful. OfferPayload ID = " + offerPayload.getId()); resultHandler.handleResult(); } else { errorMessageHandler.handleErrorMessage("Refresh TTL failed."); @@ -161,7 +159,6 @@ public void deactivateOffer(OfferPayload offerPayload, @Nullable ResultHandler r public void removeOffer(OfferPayload offerPayload, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) { if (p2PService.removeData(offerPayload, true)) { - log.trace("Remove offer from network was successful. OfferPayload ID = " + offerPayload.getId()); if (resultHandler != null) resultHandler.handleResult(); } else { diff --git a/core/src/main/java/bisq/core/offer/OfferPayload.java b/core/src/main/java/bisq/core/offer/OfferPayload.java index 9480ee7ea5d..a58b0ec65a5 100644 --- a/core/src/main/java/bisq/core/offer/OfferPayload.java +++ b/core/src/main/java/bisq/core/offer/OfferPayload.java @@ -356,7 +356,7 @@ public static OfferPayload fromProto(PB.OfferPayload proto) { @Override public long getTTL() { - return TimeUnit.MINUTES.toMillis(7); + return TimeUnit.MINUTES.toMillis(8); } @Override diff --git a/core/src/main/java/bisq/core/offer/OfferUtil.java b/core/src/main/java/bisq/core/offer/OfferUtil.java index 6455ade5955..e68045ed3cb 100644 --- a/core/src/main/java/bisq/core/offer/OfferUtil.java +++ b/core/src/main/java/bisq/core/offer/OfferUtil.java @@ -347,18 +347,18 @@ public static Map getExtraDataMap(AccountAgeWitnessService accou public static void validateOfferData(FilterManager filterManager, P2PService p2PService, - Coin buyerSecurityDepositAsCoin, + double buyerSecurityDeposit, PaymentAccount paymentAccount, String currencyCode, Coin makerFeeAsCoin) { checkNotNull(makerFeeAsCoin, "makerFee must not be null"); checkNotNull(p2PService.getAddress(), "Address must not be null"); - checkArgument(buyerSecurityDepositAsCoin.compareTo(Restrictions.getMaxBuyerSecurityDeposit()) <= 0, - "securityDeposit must be not exceed " + - Restrictions.getMaxBuyerSecurityDeposit().toFriendlyString()); - checkArgument(buyerSecurityDepositAsCoin.compareTo(Restrictions.getMinBuyerSecurityDeposit()) >= 0, - "securityDeposit must be not be less than " + - Restrictions.getMinBuyerSecurityDeposit().toFriendlyString()); + checkArgument(buyerSecurityDeposit <= Restrictions.getMaxBuyerSecurityDepositAsPercent(), + "securityDeposit must not exceed " + + Restrictions.getMaxBuyerSecurityDepositAsPercent()); + checkArgument(buyerSecurityDeposit >= Restrictions.getMinBuyerSecurityDepositAsPercent(), + "securityDeposit must not be less than " + + Restrictions.getMinBuyerSecurityDepositAsPercent()); checkArgument(!filterManager.isCurrencyBanned(currencyCode), Res.get("offerbook.warning.currencyBanned")); checkArgument(!filterManager.isPaymentMethodBanned(paymentAccount.getPaymentMethod()), diff --git a/core/src/main/java/bisq/core/offer/OpenOffer.java b/core/src/main/java/bisq/core/offer/OpenOffer.java index cc8999ecc14..8505bdc85b4 100644 --- a/core/src/main/java/bisq/core/offer/OpenOffer.java +++ b/core/src/main/java/bisq/core/offer/OpenOffer.java @@ -126,10 +126,9 @@ public void setStorage(Storage> storage) { } public void setState(State state) { - log.trace("setState" + state); boolean changed = this.state != state; this.state = state; - if (changed) + if (changed && storage != null) storage.queueUpForSave(); // We keep it reserved for a limited time, if trade preparation fails we revert to available state diff --git a/core/src/main/java/bisq/core/offer/OpenOfferManager.java b/core/src/main/java/bisq/core/offer/OpenOfferManager.java index 695dc018474..eff251f463c 100644 --- a/core/src/main/java/bisq/core/offer/OpenOfferManager.java +++ b/core/src/main/java/bisq/core/offer/OpenOfferManager.java @@ -48,7 +48,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.crypto.KeyRing; import bisq.common.crypto.PubKeyRing; import bisq.common.handlers.ErrorMessageHandler; @@ -678,7 +677,6 @@ public void onFault(String errorMessage) { private void republishOffers() { int size = openOffers.size(); final ArrayList openOffersList = new ArrayList<>(openOffers.getList()); - Log.traceCall("Number of offer for republish: " + size); if (!stopped) { stopPeriodicRefreshOffersTimer(); for (int i = 0; i < size; i++) { @@ -728,7 +726,6 @@ private void republishOffer(OpenOffer openOffer) { } private void startPeriodicRepublishOffersTimer() { - Log.traceCall(); stopped = false; if (periodicRepublishOffersTimer == null) periodicRepublishOffersTimer = UserThread.runPeriodically(() -> { @@ -745,15 +742,12 @@ private void startPeriodicRepublishOffersTimer() { } private void startPeriodicRefreshOffersTimer() { - Log.traceCall(); stopped = false; // refresh sufficiently before offer would expire if (periodicRefreshOffersTimer == null) periodicRefreshOffersTimer = UserThread.runPeriodically(() -> { if (!stopped) { int size = openOffers.size(); - Log.traceCall("Number of offer for refresh: " + size); - //we clone our list as openOffers might change during our delayed call final ArrayList openOffersList = new ArrayList<>(openOffers.getList()); for (int i = 0; i < size; i++) { diff --git a/core/src/main/java/bisq/core/offer/availability/OfferAvailabilityProtocol.java b/core/src/main/java/bisq/core/offer/availability/OfferAvailabilityProtocol.java index 6acc22e77ad..218abca67bb 100644 --- a/core/src/main/java/bisq/core/offer/availability/OfferAvailabilityProtocol.java +++ b/core/src/main/java/bisq/core/offer/availability/OfferAvailabilityProtocol.java @@ -71,7 +71,6 @@ public OfferAvailabilityProtocol(OfferAvailabilityModel model, ResultHandler res Validator.nonEmptyStringOf(offerMessage.offerId); if (networkEnvelope instanceof OfferAvailabilityResponse && model.getOffer().getId().equals(offerMessage.offerId)) { - log.trace("handle OfferAvailabilityResponse = " + networkEnvelope.getClass().getSimpleName() + " from " + peersNodeAddress); handleOfferAvailabilityResponse((OfferAvailabilityResponse) networkEnvelope, peersNodeAddress); } } diff --git a/core/src/main/java/bisq/core/offer/messages/OfferAvailabilityRequest.java b/core/src/main/java/bisq/core/offer/messages/OfferAvailabilityRequest.java index 9f0c9728801..75c4905ec22 100644 --- a/core/src/main/java/bisq/core/offer/messages/OfferAvailabilityRequest.java +++ b/core/src/main/java/bisq/core/offer/messages/OfferAvailabilityRequest.java @@ -25,7 +25,6 @@ import io.bisq.generated.protobuffer.PB; -import java.util.List; import java.util.Optional; import java.util.UUID; @@ -42,7 +41,7 @@ public final class OfferAvailabilityRequest extends OfferMessage implements Supp private final PubKeyRing pubKeyRing; private final long takersTradePrice; @Nullable - private final List supportedCapabilities; + private final Capabilities supportedCapabilities; public OfferAvailabilityRequest(String offerId, PubKeyRing pubKeyRing, @@ -50,7 +49,7 @@ public OfferAvailabilityRequest(String offerId, this(offerId, pubKeyRing, takersTradePrice, - Capabilities.getSupportedCapabilities(), + Capabilities.app, Version.getP2PMessageVersion(), UUID.randomUUID().toString()); } @@ -63,7 +62,7 @@ public OfferAvailabilityRequest(String offerId, private OfferAvailabilityRequest(String offerId, PubKeyRing pubKeyRing, long takersTradePrice, - @Nullable List supportedCapabilities, + @Nullable Capabilities supportedCapabilities, int messageVersion, @Nullable String uid) { super(messageVersion, offerId, uid); @@ -79,7 +78,7 @@ public PB.NetworkEnvelope toProtoNetworkEnvelope() { .setPubKeyRing(pubKeyRing.toProtoMessage()) .setTakersTradePrice(takersTradePrice); - Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(supportedCapabilities)); + Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities))); Optional.ofNullable(uid).ifPresent(e -> builder.setUid(uid)); return getNetworkEnvelopeBuilder() @@ -91,7 +90,7 @@ public static OfferAvailabilityRequest fromProto(PB.OfferAvailabilityRequest pro return new OfferAvailabilityRequest(proto.getOfferId(), PubKeyRing.fromProto(proto.getPubKeyRing()), proto.getTakersTradePrice(), - proto.getSupportedCapabilitiesList().isEmpty() ? null : proto.getSupportedCapabilitiesList(), + Capabilities.fromIntList(proto.getSupportedCapabilitiesList()), messageVersion, proto.getUid().isEmpty() ? null : proto.getUid()); } diff --git a/core/src/main/java/bisq/core/offer/messages/OfferAvailabilityResponse.java b/core/src/main/java/bisq/core/offer/messages/OfferAvailabilityResponse.java index 94493ba2dd8..03ecfd275d4 100644 --- a/core/src/main/java/bisq/core/offer/messages/OfferAvailabilityResponse.java +++ b/core/src/main/java/bisq/core/offer/messages/OfferAvailabilityResponse.java @@ -29,7 +29,6 @@ import io.bisq.generated.protobuffer.PB; -import java.util.List; import java.util.Optional; import java.util.UUID; @@ -45,7 +44,7 @@ public final class OfferAvailabilityResponse extends OfferMessage implements SupportedCapabilitiesMessage { private final AvailabilityResult availabilityResult; @Nullable - private final List supportedCapabilities; + private final Capabilities supportedCapabilities; // Was introduced in v 0.9.0. Might be null if msg received from node with old version @Nullable @@ -54,7 +53,7 @@ public final class OfferAvailabilityResponse extends OfferMessage implements Sup public OfferAvailabilityResponse(String offerId, AvailabilityResult availabilityResult, NodeAddress arbitrator) { this(offerId, availabilityResult, - Capabilities.getSupportedCapabilities(), + Capabilities.app, Version.getP2PMessageVersion(), UUID.randomUUID().toString(), arbitrator); @@ -67,7 +66,7 @@ public OfferAvailabilityResponse(String offerId, AvailabilityResult availability private OfferAvailabilityResponse(String offerId, AvailabilityResult availabilityResult, - @Nullable List supportedCapabilities, + @Nullable Capabilities supportedCapabilities, int messageVersion, @Nullable String uid, @Nullable NodeAddress arbitrator) { @@ -83,7 +82,7 @@ public PB.NetworkEnvelope toProtoNetworkEnvelope() { .setOfferId(offerId) .setAvailabilityResult(PB.AvailabilityResult.valueOf(availabilityResult.name())); - Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(supportedCapabilities)); + Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities))); Optional.ofNullable(uid).ifPresent(e -> builder.setUid(uid)); Optional.ofNullable(arbitrator).ifPresent(e -> builder.setArbitrator(arbitrator.toProtoMessage())); @@ -95,7 +94,7 @@ public PB.NetworkEnvelope toProtoNetworkEnvelope() { public static OfferAvailabilityResponse fromProto(PB.OfferAvailabilityResponse proto, int messageVersion) { return new OfferAvailabilityResponse(proto.getOfferId(), ProtoUtil.enumFromProto(AvailabilityResult.class, proto.getAvailabilityResult().name()), - proto.getSupportedCapabilitiesList().isEmpty() ? null : proto.getSupportedCapabilitiesList(), + Capabilities.fromIntList(proto.getSupportedCapabilitiesList()), messageVersion, proto.getUid().isEmpty() ? null : proto.getUid(), proto.hasArbitrator() ? NodeAddress.fromProto(proto.getArbitrator()) : null); diff --git a/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java b/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java index b7b2fab9ae1..8d3b2557554 100644 --- a/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java +++ b/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java @@ -80,6 +80,7 @@ protected void run() { offer.getMakerFee(), offer.getTxFee(), arbitrator.getBtcAddress(), + true, new TxBroadcaster.Callback() { @Override public void onSuccess(Transaction transaction) { diff --git a/core/src/main/java/bisq/core/payment/AccountAgeWitness.java b/core/src/main/java/bisq/core/payment/AccountAgeWitness.java index 876d1dcdd97..78660248a55 100644 --- a/core/src/main/java/bisq/core/payment/AccountAgeWitness.java +++ b/core/src/main/java/bisq/core/payment/AccountAgeWitness.java @@ -24,6 +24,7 @@ import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.proto.persistable.PersistableEnvelope; import bisq.common.util.Utilities; @@ -31,10 +32,7 @@ import com.google.protobuf.ByteString; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.concurrent.TimeUnit; import lombok.Value; @@ -104,10 +102,8 @@ public boolean verifyHashSize() { // Pre 0.6 version don't know the new message type and throw an error which leads to disconnecting the peer. @Override - public List getRequiredCapabilities() { - return new ArrayList<>(Collections.singletonList( - Capabilities.Capability.ACCOUNT_AGE_WITNESS.ordinal() - )); + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.ACCOUNT_AGE_WITNESS); } @Override diff --git a/core/src/main/java/bisq/core/payment/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/payment/AccountAgeWitnessService.java index e30617ef8df..1c428da8c80 100644 --- a/core/src/main/java/bisq/core/payment/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/payment/AccountAgeWitnessService.java @@ -126,7 +126,7 @@ public void onUpdatedDataReceived() { private void republishAllFiatAccounts() { if (user.getPaymentAccounts() != null) user.getPaymentAccounts().stream() - .filter(e -> !(e instanceof CryptoCurrencyAccount)) + .filter(e -> !(e instanceof AssetAccount)) .forEach(e -> { // We delay with a random interval of 20-60 sec to ensure to be better connected and don't stress the // P2P network with publishing all at once at startup time. @@ -329,7 +329,7 @@ public boolean verifyAccountAgeWitness(Trade trade, // Check if the peers trade limit is not less than the trade amount if (!verifyPeersTradeLimit(trade, peersWitness, peersCurrentDate, errorMessageHandler)) { - log.error("verifyPeersTradeLimit failed: peersPaymentAccountPayload " + peersPaymentAccountPayload); + log.error("verifyPeersTradeLimit failed: peersPaymentAccountPayload {}", peersPaymentAccountPayload); return false; } // Check if the signature is correct diff --git a/core/src/main/java/bisq/core/payment/AssetAccount.java b/core/src/main/java/bisq/core/payment/AssetAccount.java new file mode 100644 index 00000000000..de7c2e72d2f --- /dev/null +++ b/core/src/main/java/bisq/core/payment/AssetAccount.java @@ -0,0 +1,35 @@ +/* + * 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.payment; + +import bisq.core.payment.payload.AssetsAccountPayload; +import bisq.core.payment.payload.PaymentMethod; + +public abstract class AssetAccount extends PaymentAccount { + protected AssetAccount(PaymentMethod paymentMethod) { + super(paymentMethod); + } + + public void setAddress(String address) { + ((AssetsAccountPayload) paymentAccountPayload).setAddress(address); + } + + public String getAddress() { + return ((AssetsAccountPayload) paymentAccountPayload).getAddress(); + } +} diff --git a/core/src/main/java/bisq/core/payment/CashAppAccount.java b/core/src/main/java/bisq/core/payment/CashAppAccount.java new file mode 100644 index 00000000000..acd4f6b6a5d --- /dev/null +++ b/core/src/main/java/bisq/core/payment/CashAppAccount.java @@ -0,0 +1,49 @@ +/* + * 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.payment; + +import bisq.core.locale.FiatCurrency; +import bisq.core.payment.payload.CashAppAccountPayload; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.payment.payload.PaymentMethod; + +import lombok.EqualsAndHashCode; + +// Removed due too high chargeback risk +// Cannot be deleted as it would break old trade history entries +@Deprecated +@EqualsAndHashCode(callSuper = true) +public final class CashAppAccount extends PaymentAccount { + public CashAppAccount() { + super(PaymentMethod.CASH_APP); + setSingleTradeCurrency(new FiatCurrency("USD")); + } + + @Override + protected PaymentAccountPayload createPayload() { + return new CashAppAccountPayload(paymentMethod.getId(), id); + } + + public void setCashTag(String cashTag) { + ((CashAppAccountPayload) paymentAccountPayload).setCashTag(cashTag); + } + + public String getCashTag() { + return ((CashAppAccountPayload) paymentAccountPayload).getCashTag(); + } +} diff --git a/core/src/main/java/bisq/core/payment/CryptoCurrencyAccount.java b/core/src/main/java/bisq/core/payment/CryptoCurrencyAccount.java index ad096d15d99..534157bd1f8 100644 --- a/core/src/main/java/bisq/core/payment/CryptoCurrencyAccount.java +++ b/core/src/main/java/bisq/core/payment/CryptoCurrencyAccount.java @@ -24,7 +24,7 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) -public final class CryptoCurrencyAccount extends PaymentAccount { +public final class CryptoCurrencyAccount extends AssetAccount { public CryptoCurrencyAccount() { super(PaymentMethod.BLOCK_CHAINS); @@ -34,12 +34,4 @@ public CryptoCurrencyAccount() { protected PaymentAccountPayload createPayload() { return new CryptoCurrencyAccountPayload(paymentMethod.getId(), id); } - - public void setAddress(String address) { - ((CryptoCurrencyAccountPayload) paymentAccountPayload).setAddress(address); - } - - public String getAddress() { - return ((CryptoCurrencyAccountPayload) paymentAccountPayload).getAddress(); - } } diff --git a/core/src/main/java/bisq/core/network/p2p/seed/ImmutableSetDecorator.java b/core/src/main/java/bisq/core/payment/InstantCryptoCurrencyAccount.java similarity index 55% rename from core/src/main/java/bisq/core/network/p2p/seed/ImmutableSetDecorator.java rename to core/src/main/java/bisq/core/payment/InstantCryptoCurrencyAccount.java index 31fbb644b5f..17ddf5eca77 100644 --- a/core/src/main/java/bisq/core/network/p2p/seed/ImmutableSetDecorator.java +++ b/core/src/main/java/bisq/core/payment/InstantCryptoCurrencyAccount.java @@ -15,31 +15,23 @@ * along with Bisq. If not, see . */ -package bisq.core.network.p2p.seed; +package bisq.core.payment; -import com.google.common.collect.ImmutableSet; +import bisq.core.payment.payload.InstantCryptoCurrencyPayload; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.payment.payload.PaymentMethod; -import java.util.AbstractSet; -import java.util.Iterator; -import java.util.Set; +import lombok.EqualsAndHashCode; -import org.jetbrains.annotations.NotNull; +@EqualsAndHashCode(callSuper = true) +public final class InstantCryptoCurrencyAccount extends AssetAccount { -class ImmutableSetDecorator extends AbstractSet { - private final Set delegate; - - public ImmutableSetDecorator(Set delegate) { - this.delegate = ImmutableSet.copyOf(delegate); - } - - @NotNull - @Override - public Iterator iterator() { - return delegate.iterator(); + public InstantCryptoCurrencyAccount() { + super(PaymentMethod.BLOCK_CHAINS_INSTANT); } @Override - public int size() { - return delegate.size(); + protected PaymentAccountPayload createPayload() { + return new InstantCryptoCurrencyPayload(paymentMethod.getId(), id); } } diff --git a/core/src/main/java/bisq/core/payment/OKPayAccount.java b/core/src/main/java/bisq/core/payment/OKPayAccount.java new file mode 100644 index 00000000000..24730181708 --- /dev/null +++ b/core/src/main/java/bisq/core/payment/OKPayAccount.java @@ -0,0 +1,50 @@ +/* + * 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.payment; + +import bisq.core.locale.CurrencyUtil; +import bisq.core.payment.payload.OKPayAccountPayload; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.payment.payload.PaymentMethod; + +import lombok.EqualsAndHashCode; + +// Cannot be deleted as it would break old trade history entries +@Deprecated +@EqualsAndHashCode(callSuper = true) +public final class OKPayAccount extends PaymentAccount { + public OKPayAccount() { + super(PaymentMethod.OK_PAY); + + // Incorrect call but we don't want to keep Deprecated code in CurrencyUtil if not needed... + tradeCurrencies.addAll(CurrencyUtil.getAllUpholdCurrencies()); + } + + @Override + protected PaymentAccountPayload createPayload() { + return new OKPayAccountPayload(paymentMethod.getId(), id); + } + + public void setAccountNr(String accountNr) { + ((OKPayAccountPayload) paymentAccountPayload).setAccountNr(accountNr); + } + + public String getAccountNr() { + return ((OKPayAccountPayload) paymentAccountPayload).getAccountNr(); + } +} diff --git a/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java b/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java index b35dabc3ff1..044967cd7a6 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccountFactory.java @@ -74,6 +74,17 @@ public static PaymentAccount getPaymentAccount(PaymentMethod paymentMethod) { return new PromptPayAccount(); case PaymentMethod.ADVANCED_CASH_ID: return new AdvancedCashAccount(); + case PaymentMethod.BLOCK_CHAINS_INSTANT_ID: + return new InstantCryptoCurrencyAccount(); + + // Cannot be deleted as it would break old trade history entries + case PaymentMethod.OK_PAY_ID: + return new OKPayAccount(); + case PaymentMethod.CASH_APP_ID: + return new CashAppAccount(); + case PaymentMethod.VENMO_ID: + return new VenmoAccount(); + default: throw new RuntimeException("Not supported PaymentMethod: " + paymentMethod); } diff --git a/core/src/main/java/bisq/core/payment/VenmoAccount.java b/core/src/main/java/bisq/core/payment/VenmoAccount.java new file mode 100644 index 00000000000..cd546601442 --- /dev/null +++ b/core/src/main/java/bisq/core/payment/VenmoAccount.java @@ -0,0 +1,57 @@ +/* + * 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.payment; + +import bisq.core.locale.FiatCurrency; +import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.payment.payload.PaymentMethod; +import bisq.core.payment.payload.VenmoAccountPayload; + +import lombok.EqualsAndHashCode; + +// Removed due too high chargeback risk +// Cannot be deleted as it would break old trade history entries +@Deprecated +@EqualsAndHashCode(callSuper = true) +public final class VenmoAccount extends PaymentAccount { + public VenmoAccount() { + super(PaymentMethod.VENMO); + setSingleTradeCurrency(new FiatCurrency("USD")); + } + + @Override + protected PaymentAccountPayload createPayload() { + return new VenmoAccountPayload(paymentMethod.getId(), id); + } + + public void setVenmoUserName(String venmoUserName) { + ((VenmoAccountPayload) paymentAccountPayload).setVenmoUserName(venmoUserName); + } + + public String getVenmoUserName() { + return ((VenmoAccountPayload) paymentAccountPayload).getVenmoUserName(); + } + + public void setHolderName(String holderName) { + ((VenmoAccountPayload) paymentAccountPayload).setHolderName(holderName); + } + + public String getHolderName() { + return ((VenmoAccountPayload) paymentAccountPayload).getHolderName(); + } +} diff --git a/core/src/main/java/bisq/core/payment/payload/AssetsAccountPayload.java b/core/src/main/java/bisq/core/payment/payload/AssetsAccountPayload.java new file mode 100644 index 00000000000..76d34790e62 --- /dev/null +++ b/core/src/main/java/bisq/core/payment/payload/AssetsAccountPayload.java @@ -0,0 +1,82 @@ +/* + * 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.payment.payload; + +import bisq.core.locale.Res; + +import java.nio.charset.Charset; + +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; + +@EqualsAndHashCode(callSuper = true) +@ToString +@Setter +@Getter +@Slf4j +public abstract class AssetsAccountPayload extends PaymentAccountPayload { + protected String address = ""; + + protected AssetsAccountPayload(String paymentMethod, String id) { + super(paymentMethod, id); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + protected AssetsAccountPayload(String paymentMethod, + String id, + String address, + long maxTradePeriod, + @Nullable Map excludeFromJsonDataMap) { + super(paymentMethod, + id, + maxTradePeriod, + excludeFromJsonDataMap); + this.address = address; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public String getPaymentDetails() { + return Res.getWithCol("payment.altcoin.receiver.address") + " " + address; + } + + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } + + @Override + public byte[] getAgeWitnessInputData() { + return super.getAgeWitnessInputData(address.getBytes(Charset.forName("UTF-8"))); + } +} diff --git a/core/src/main/java/bisq/core/payment/payload/CashAppAccountPayload.java b/core/src/main/java/bisq/core/payment/payload/CashAppAccountPayload.java new file mode 100644 index 00000000000..3448a48489b --- /dev/null +++ b/core/src/main/java/bisq/core/payment/payload/CashAppAccountPayload.java @@ -0,0 +1,107 @@ +/* + * 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.payment.payload; + +import bisq.core.locale.Res; + +import io.bisq.generated.protobuffer.PB; + +import com.google.protobuf.Message; + +import org.springframework.util.CollectionUtils; + +import java.nio.charset.Charset; + +import java.util.HashMap; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +// Cannot be deleted as it would break old trade history entries +// Removed due too high chargeback risk +@Deprecated +@EqualsAndHashCode(callSuper = true) +@ToString +@Setter +@Getter +@Slf4j +public final class CashAppAccountPayload extends PaymentAccountPayload { + private String cashTag = ""; + + public CashAppAccountPayload(String paymentMethod, String id) { + super(paymentMethod, id); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private CashAppAccountPayload(String paymentMethod, + String id, + String cashTag, + long maxTradePeriod, + Map excludeFromJsonDataMap) { + super(paymentMethod, + id, + maxTradePeriod, + excludeFromJsonDataMap); + + this.cashTag = cashTag; + } + + @Override + public Message toProtoMessage() { + return getPaymentAccountPayloadBuilder() + .setCashAppAccountPayload(PB.CashAppAccountPayload.newBuilder() + .setCashTag(cashTag)) + .build(); + } + + public static CashAppAccountPayload fromProto(PB.PaymentAccountPayload proto) { + return new CashAppAccountPayload(proto.getPaymentMethodId(), + proto.getId(), + proto.getCashAppAccountPayload().getCashTag(), + proto.getMaxTradePeriod(), + CollectionUtils.isEmpty(proto.getExcludeFromJsonDataMap()) ? null : new HashMap<>(proto.getExcludeFromJsonDataMap())); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public String getPaymentDetails() { + return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account") + " " + cashTag; + } + + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } + + @Override + public byte[] getAgeWitnessInputData() { + return super.getAgeWitnessInputData(cashTag.getBytes(Charset.forName("UTF-8"))); + } +} diff --git a/core/src/main/java/bisq/core/payment/payload/CryptoCurrencyAccountPayload.java b/core/src/main/java/bisq/core/payment/payload/CryptoCurrencyAccountPayload.java index 04dcbe94f9f..a295816f642 100644 --- a/core/src/main/java/bisq/core/payment/payload/CryptoCurrencyAccountPayload.java +++ b/core/src/main/java/bisq/core/payment/payload/CryptoCurrencyAccountPayload.java @@ -17,16 +17,12 @@ package bisq.core.payment.payload; -import bisq.core.locale.Res; - import io.bisq.generated.protobuffer.PB; import com.google.protobuf.Message; import org.springframework.util.CollectionUtils; -import java.nio.charset.Charset; - import java.util.HashMap; import java.util.Map; @@ -43,8 +39,7 @@ @Setter @Getter @Slf4j -public final class CryptoCurrencyAccountPayload extends PaymentAccountPayload { - private String address = ""; +public final class CryptoCurrencyAccountPayload extends AssetsAccountPayload { public CryptoCurrencyAccountPayload(String paymentMethod, String id) { super(paymentMethod, id); @@ -62,9 +57,9 @@ private CryptoCurrencyAccountPayload(String paymentMethod, @Nullable Map excludeFromJsonDataMap) { super(paymentMethod, id, + address, maxTradePeriod, excludeFromJsonDataMap); - this.address = address; } @Override @@ -83,23 +78,4 @@ public static CryptoCurrencyAccountPayload fromProto(PB.PaymentAccountPayload pr CollectionUtils.isEmpty(proto.getExcludeFromJsonDataMap()) ? null : new HashMap<>(proto.getExcludeFromJsonDataMap())); } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public String getPaymentDetails() { - return Res.getWithCol("payment.altcoin.receiver.address") + " " + address; - } - - @Override - public String getPaymentDetailsForTradePopup() { - return getPaymentDetails(); - } - - @Override - public byte[] getAgeWitnessInputData() { - return super.getAgeWitnessInputData(address.getBytes(Charset.forName("UTF-8"))); - } } diff --git a/core/src/main/java/bisq/core/payment/payload/InstantCryptoCurrencyPayload.java b/core/src/main/java/bisq/core/payment/payload/InstantCryptoCurrencyPayload.java new file mode 100644 index 00000000000..bc112d213d2 --- /dev/null +++ b/core/src/main/java/bisq/core/payment/payload/InstantCryptoCurrencyPayload.java @@ -0,0 +1,80 @@ +/* + * 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.payment.payload; + +import io.bisq.generated.protobuffer.PB; + +import com.google.protobuf.Message; + +import org.springframework.util.CollectionUtils; + +import java.util.HashMap; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; + +@EqualsAndHashCode(callSuper = true) +@ToString +@Setter +@Getter +@Slf4j +public final class InstantCryptoCurrencyPayload extends AssetsAccountPayload { + + public InstantCryptoCurrencyPayload(String paymentMethod, String id) { + super(paymentMethod, id); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private InstantCryptoCurrencyPayload(String paymentMethod, + String id, + String address, + long maxTradePeriod, + @Nullable Map excludeFromJsonDataMap) { + super(paymentMethod, + id, + address, + maxTradePeriod, + excludeFromJsonDataMap); + } + + @Override + public Message toProtoMessage() { + return getPaymentAccountPayloadBuilder() + .setInstantCryptoCurrencyAccountPayload(PB.InstantCryptoCurrencyAccountPayload.newBuilder() + .setAddress(address)) + .build(); + } + + public static InstantCryptoCurrencyPayload fromProto(PB.PaymentAccountPayload proto) { + return new InstantCryptoCurrencyPayload(proto.getPaymentMethodId(), + proto.getId(), + proto.getInstantCryptoCurrencyAccountPayload().getAddress(), + proto.getMaxTradePeriod(), + CollectionUtils.isEmpty(proto.getExcludeFromJsonDataMap()) ? null : new HashMap<>(proto.getExcludeFromJsonDataMap())); + } +} diff --git a/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java b/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java new file mode 100644 index 00000000000..4be25fb12a4 --- /dev/null +++ b/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java @@ -0,0 +1,106 @@ +/* + * 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.payment.payload; + +import bisq.core.locale.Res; + +import io.bisq.generated.protobuffer.PB; + +import com.google.protobuf.Message; + +import org.springframework.util.CollectionUtils; + +import java.nio.charset.Charset; + +import java.util.HashMap; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +// Cannot be deleted as it would break old trade history entries +@Deprecated +@EqualsAndHashCode(callSuper = true) +@ToString +@Setter +@Getter +@Slf4j +public final class OKPayAccountPayload extends PaymentAccountPayload { + private String accountNr = ""; + + public OKPayAccountPayload(String paymentMethod, String id) { + super(paymentMethod, id); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private OKPayAccountPayload(String paymentMethod, + String id, + String accountNr, + long maxTradePeriod, + Map excludeFromJsonDataMap) { + super(paymentMethod, + id, + maxTradePeriod, + excludeFromJsonDataMap); + + this.accountNr = accountNr; + } + + @Override + public Message toProtoMessage() { + return getPaymentAccountPayloadBuilder() + .setOKPayAccountPayload(PB.OKPayAccountPayload.newBuilder() + .setAccountNr(accountNr)) + .build(); + } + + public static OKPayAccountPayload fromProto(PB.PaymentAccountPayload proto) { + return new OKPayAccountPayload(proto.getPaymentMethodId(), + proto.getId(), + proto.getOKPayAccountPayload().getAccountNr(), + proto.getMaxTradePeriod(), + CollectionUtils.isEmpty(proto.getExcludeFromJsonDataMap()) ? null : new HashMap<>(proto.getExcludeFromJsonDataMap())); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public String getPaymentDetails() { + return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.no") + " " + accountNr; + } + + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } + + @Override + public byte[] getAgeWitnessInputData() { + return super.getAgeWitnessInputData(accountNr.getBytes(Charset.forName("UTF-8"))); + } +} diff --git a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java index 7722dca9ae7..2751531304e 100644 --- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java @@ -89,6 +89,15 @@ public final class PaymentMethod implements PersistablePayload, Comparable { public static final String BLOCK_CHAINS_ID = "BLOCK_CHAINS"; public static final String PROMPT_PAY_ID = "PROMPT_PAY"; public static final String ADVANCED_CASH_ID = "ADVANCED_CASH"; + public static final String BLOCK_CHAINS_INSTANT_ID = "BLOCK_CHAINS_INSTANT"; + + // Cannot be deleted as it would break old trade history entries + @Deprecated + public static final String OK_PAY_ID = "OK_PAY"; + @Deprecated + public static final String CASH_APP_ID = "CASH_APP"; // Removed due too high chargeback risk + @Deprecated + public static final String VENMO_ID = "VENMO"; // Removed due too high chargeback risk public static PaymentMethod UPHOLD; public static PaymentMethod MONEY_BEAM; @@ -116,6 +125,15 @@ public final class PaymentMethod implements PersistablePayload, Comparable { public static PaymentMethod BLOCK_CHAINS; public static PaymentMethod PROMPT_PAY; public static PaymentMethod ADVANCED_CASH; + public static PaymentMethod BLOCK_CHAINS_INSTANT; + + // Cannot be deleted as it would break old trade history entries + @Deprecated + public static PaymentMethod OK_PAY = getDummyPaymentMethod(OK_PAY_ID); + @Deprecated + public static PaymentMethod CASH_APP = getDummyPaymentMethod(CASH_APP_ID); // Removed due too high chargeback risk + @Deprecated + public static PaymentMethod VENMO = getDummyPaymentMethod(VENMO_ID); // Removed due too high chargeback risk // The limit and duration assignment must not be changed as that could break old offers (if amount would be higher // than new trade limit) and violate the maker expectation when he created the offer (duration). @@ -166,7 +184,9 @@ public final class PaymentMethod implements PersistablePayload, Comparable { PROMPT_PAY = new PaymentMethod(PROMPT_PAY_ID, DAY, DEFAULT_TRADE_LIMIT_LOW_RISK), // Altcoins - BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, DAY, DEFAULT_TRADE_LIMIT_VERY_LOW_RISK) + BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, DAY, DEFAULT_TRADE_LIMIT_VERY_LOW_RISK), + // Altcoins with 1 hour trade period + BLOCK_CHAINS_INSTANT = new PaymentMethod(BLOCK_CHAINS_INSTANT_ID, TimeUnit.HOURS.toMillis(1), DEFAULT_TRADE_LIMIT_VERY_LOW_RISK) )); static { @@ -292,4 +312,8 @@ public int compareTo(@NotNull Object other) { else return 0; } + + public boolean isAsset() { + return this.equals(BLOCK_CHAINS_INSTANT) || this.equals(BLOCK_CHAINS); + } } diff --git a/core/src/main/java/bisq/core/payment/payload/VenmoAccountPayload.java b/core/src/main/java/bisq/core/payment/payload/VenmoAccountPayload.java new file mode 100644 index 00000000000..713851c1adb --- /dev/null +++ b/core/src/main/java/bisq/core/payment/payload/VenmoAccountPayload.java @@ -0,0 +1,113 @@ +/* + * 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.payment.payload; + +import bisq.core.locale.Res; + +import io.bisq.generated.protobuffer.PB; + +import com.google.protobuf.Message; + +import org.springframework.util.CollectionUtils; + +import java.nio.charset.Charset; + +import java.util.HashMap; +import java.util.Map; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +// Cannot be deleted as it would break old trade history entries +// Removed due too high chargeback risk +@Deprecated +@EqualsAndHashCode(callSuper = true) +@ToString +@Setter +@Getter +@Slf4j +public final class VenmoAccountPayload extends PaymentAccountPayload { + private String venmoUserName = ""; + private String holderName = ""; + + public VenmoAccountPayload(String paymentMethod, String id) { + super(paymentMethod, id); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private VenmoAccountPayload(String paymentMethod, + String id, + String venmoUserName, + String holderName, + long maxTradePeriod, + Map excludeFromJsonDataMap) { + super(paymentMethod, + id, + maxTradePeriod, + excludeFromJsonDataMap); + + this.venmoUserName = venmoUserName; + this.holderName = holderName; + } + + @Override + public Message toProtoMessage() { + return getPaymentAccountPayloadBuilder() + .setVenmoAccountPayload(PB.VenmoAccountPayload.newBuilder() + .setVenmoUserName(venmoUserName) + .setHolderName(holderName)) + .build(); + } + + public static VenmoAccountPayload fromProto(PB.PaymentAccountPayload proto) { + return new VenmoAccountPayload(proto.getPaymentMethodId(), + proto.getId(), + proto.getVenmoAccountPayload().getVenmoUserName(), + proto.getVenmoAccountPayload().getHolderName(), + proto.getMaxTradePeriod(), + CollectionUtils.isEmpty(proto.getExcludeFromJsonDataMap()) ? null : new HashMap<>(proto.getExcludeFromJsonDataMap())); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public String getPaymentDetails() { + return Res.get(paymentMethodId) + " - " + Res.getWithCol("payment.account.owner") + " " + holderName + ", " + + Res.getWithCol("payment.venmo.venmoUserName") + " " + venmoUserName; + } + + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } + + @Override + public byte[] getAgeWitnessInputData() { + return super.getAgeWitnessInputData(venmoUserName.getBytes(Charset.forName("UTF-8"))); + } +} diff --git a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java index b5cd297bd42..ebc101175e1 100644 --- a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java @@ -22,6 +22,7 @@ import bisq.core.payment.AccountAgeWitness; import bisq.core.payment.payload.AdvancedCashAccountPayload; import bisq.core.payment.payload.AliPayAccountPayload; +import bisq.core.payment.payload.CashAppAccountPayload; import bisq.core.payment.payload.CashDepositAccountPayload; import bisq.core.payment.payload.ChaseQuickPayAccountPayload; import bisq.core.payment.payload.ClearXchangeAccountPayload; @@ -29,10 +30,12 @@ import bisq.core.payment.payload.F2FAccountPayload; import bisq.core.payment.payload.FasterPaymentsAccountPayload; import bisq.core.payment.payload.HalCashAccountPayload; +import bisq.core.payment.payload.InstantCryptoCurrencyPayload; import bisq.core.payment.payload.InteracETransferAccountPayload; import bisq.core.payment.payload.MoneyBeamAccountPayload; import bisq.core.payment.payload.MoneyGramAccountPayload; import bisq.core.payment.payload.NationalBankAccountPayload; +import bisq.core.payment.payload.OKPayAccountPayload; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.payment.payload.PerfectMoneyAccountPayload; import bisq.core.payment.payload.PopmoneyAccountPayload; @@ -45,6 +48,7 @@ import bisq.core.payment.payload.SwishAccountPayload; import bisq.core.payment.payload.USPostalMoneyOrderAccountPayload; import bisq.core.payment.payload.UpholdAccountPayload; +import bisq.core.payment.payload.VenmoAccountPayload; import bisq.core.payment.payload.WeChatPayAccountPayload; import bisq.core.payment.payload.WesternUnionAccountPayload; import bisq.core.trade.statistics.TradeStatistics2; @@ -132,6 +136,17 @@ public PaymentAccountPayload fromProto(PB.PaymentAccountPayload proto) { return PromptPayAccountPayload.fromProto(proto); case ADVANCED_CASH_ACCOUNT_PAYLOAD: return AdvancedCashAccountPayload.fromProto(proto); + case INSTANT_CRYPTO_CURRENCY_ACCOUNT_PAYLOAD: + return InstantCryptoCurrencyPayload.fromProto(proto); + + // Cannot be deleted as it would break old trade history entries + case O_K_PAY_ACCOUNT_PAYLOAD: + return OKPayAccountPayload.fromProto(proto); + case CASH_APP_ACCOUNT_PAYLOAD: + return CashAppAccountPayload.fromProto(proto); + case VENMO_ACCOUNT_PAYLOAD: + return VenmoAccountPayload.fromProto(proto); + default: throw new ProtobufferRuntimeException("Unknown proto message case(PB.PaymentAccountPayload). messageCase=" + messageCase); } 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 852cd3a219d..09e8ed93559 100644 --- a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java @@ -30,7 +30,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.handlers.FaultHandler; import bisq.common.util.MathUtils; import bisq.common.util.Tuple2; @@ -393,7 +392,6 @@ private boolean applyPriceToConsumer() { } private void requestAllPrices(PriceProvider provider, Runnable resultHandler, FaultHandler faultHandler) { - Log.traceCall(); PriceRequest priceRequest = new PriceRequest(); SettableFuture, Map>> future = priceRequest.requestAllPrices(provider); Futures.addCallback(future, new FutureCallback, Map>>() { diff --git a/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java b/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java index fc4f4faca20..c8360dfb132 100644 --- a/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java +++ b/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java @@ -21,29 +21,30 @@ import bisq.core.dao.DaoOptionKeys; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import java.util.ArrayList; import java.util.Arrays; public class CoreNetworkCapabilities { public static void setSupportedCapabilities(BisqEnvironment bisqEnvironment) { - final ArrayList supportedCapabilities = new ArrayList<>(Arrays.asList( - Capabilities.Capability.TRADE_STATISTICS.ordinal(), - Capabilities.Capability.TRADE_STATISTICS_2.ordinal(), - Capabilities.Capability.ACCOUNT_AGE_WITNESS.ordinal(), - Capabilities.Capability.ACK_MSG.ordinal() + final ArrayList supportedCapabilities = new ArrayList<>(Arrays.asList( + Capability.TRADE_STATISTICS, + Capability.TRADE_STATISTICS_2, + Capability.ACCOUNT_AGE_WITNESS, + Capability.ACK_MSG )); if (BisqEnvironment.isDaoActivated(bisqEnvironment)) { - supportedCapabilities.add(Capabilities.Capability.PROPOSAL.ordinal()); - supportedCapabilities.add(Capabilities.Capability.BLIND_VOTE.ordinal()); - supportedCapabilities.add(Capabilities.Capability.BSQ_BLOCK.ordinal()); + supportedCapabilities.add(Capability.PROPOSAL); + supportedCapabilities.add(Capability.BLIND_VOTE); + supportedCapabilities.add(Capability.BSQ_BLOCK); String isFullDaoNode = bisqEnvironment.getProperty(DaoOptionKeys.FULL_DAO_NODE, String.class, ""); if (isFullDaoNode != null && !isFullDaoNode.isEmpty()) - supportedCapabilities.add(Capabilities.Capability.DAO_FULL_NODE.ordinal()); + supportedCapabilities.add(Capability.DAO_FULL_NODE); } - Capabilities.setSupportedCapabilities(supportedCapabilities); + Capabilities.app.set(supportedCapabilities); } } diff --git a/core/src/main/java/bisq/core/trade/Trade.java b/core/src/main/java/bisq/core/trade/Trade.java index ef9e7438850..dd14c4f092a 100644 --- a/core/src/main/java/bisq/core/trade/Trade.java +++ b/core/src/main/java/bisq/core/trade/Trade.java @@ -44,7 +44,6 @@ import bisq.network.p2p.P2PService; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.crypto.KeyRing; import bisq.common.crypto.PubKeyRing; import bisq.common.proto.ProtoUtil; @@ -504,7 +503,6 @@ public void init(P2PService p2PService, KeyRing keyRing, boolean useSavingsWallet, Coin fundsNeededForTrade) { - Log.traceCall(); processModel.onAllServicesInitialized(offer, tradeManager, openOfferManager, @@ -619,9 +617,9 @@ public void onComplete() { /////////////////////////////////////////////////////////////////////////////////////////// public void setState(State state) { - log.info("Set new state at {} (id={}): {}", this.getClass().getSimpleName(), getShortId(), state); + log.debug("Set new state at {} (id={}): {}", this.getClass().getSimpleName(), getShortId(), state); if (state.getPhase().ordinal() < this.state.getPhase().ordinal()) { - final String message = "We got a state change to a previous phase.\n" + + String message = "We got a state change to a previous phase.\n" + "Old state is: " + this.state + ". New state is: " + state; log.warn(message); } @@ -639,7 +637,6 @@ public void setState(State state) { } public void setDisputeState(DisputeState disputeState) { - Log.traceCall("disputeState=" + disputeState + "\n\ttrade=" + this); boolean changed = this.disputeState != disputeState; this.disputeState = disputeState; disputeStateProperty.set(disputeState); @@ -752,7 +749,8 @@ private long getTradeStartTime() { if (depositTx != null && getTakeOfferDate() != null) { if (depositTx.getConfidence().getDepthInBlocks() > 0) { final long tradeTime = getTakeOfferDate().getTime(); - long blockTime = depositTx.getUpdateTime().getTime(); + // Use tx.getIncludedInBestChainAt() when available, otherwise use tx.getUpdateTime() + long blockTime = depositTx.getIncludedInBestChainAt() != null ? depositTx.getIncludedInBestChainAt().getTime() : depositTx.getUpdateTime().getTime(); // If block date is in future (Date in Bitcoin blocks can be off by +/- 2 hours) we use our current date. // If block date is earlier than our trade date we use our trade date. if (blockTime > now) diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index ee05439a53e..4e70e45ad6e 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -49,7 +49,6 @@ import bisq.common.Clock; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.crypto.KeyRing; import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.FaultHandler; @@ -224,7 +223,6 @@ public void readPersisted() { /////////////////////////////////////////////////////////////////////////////////////////// public void onAllServicesInitialized() { - Log.traceCall(); if (p2PService.isBootstrapped()) initPendingTrades(); else @@ -252,8 +250,6 @@ public void shutDown() { } private void initPendingTrades() { - Log.traceCall(); - List addTradeToFailedTradesList = new ArrayList<>(); List removePreparedTradeList = new ArrayList<>(); tradesForStatistics = new ArrayList<>(); diff --git a/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java index b77d702e01b..740d187fa4f 100644 --- a/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java @@ -34,6 +34,7 @@ import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSignAndPublishDepositTx; import bisq.core.trade.protocol.tasks.taker.CreateTakerFeeTx; import bisq.core.trade.protocol.tasks.taker.TakerProcessPublishDepositTxRequest; +import bisq.core.trade.protocol.tasks.taker.TakerPublishFeeTx; import bisq.core.trade.protocol.tasks.taker.TakerSelectMediator; import bisq.core.trade.protocol.tasks.taker.TakerSendDepositTxPublishedMessage; import bisq.core.trade.protocol.tasks.taker.TakerSendPayDepositRequest; @@ -149,6 +150,7 @@ private void handle(PublishDepositTxRequest tradeMessage, NodeAddress sender) { VerifyPeersAccountAgeWitness.class, TakerVerifyMakerFeePayment.class, TakerVerifyAndSignContract.class, + TakerPublishFeeTx.class, BuyerAsTakerSignAndPublishDepositTx.class, TakerSendDepositTxPublishedMessage.class, PublishTradeStatistics.class diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java index 5d3cb300686..3a1a963d472 100644 --- a/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/SellerAsTakerProtocol.java @@ -34,6 +34,7 @@ import bisq.core.trade.protocol.tasks.seller_as_taker.SellerAsTakerSignAndPublishDepositTx; import bisq.core.trade.protocol.tasks.taker.CreateTakerFeeTx; import bisq.core.trade.protocol.tasks.taker.TakerProcessPublishDepositTxRequest; +import bisq.core.trade.protocol.tasks.taker.TakerPublishFeeTx; import bisq.core.trade.protocol.tasks.taker.TakerSelectMediator; import bisq.core.trade.protocol.tasks.taker.TakerSendDepositTxPublishedMessage; import bisq.core.trade.protocol.tasks.taker.TakerSendPayDepositRequest; @@ -141,6 +142,7 @@ private void handle(PublishDepositTxRequest tradeMessage, NodeAddress sender) { VerifyPeersAccountAgeWitness.class, TakerVerifyMakerFeePayment.class, TakerVerifyAndSignContract.class, + TakerPublishFeeTx.class, SellerAsTakerSignAndPublishDepositTx.class, TakerSendDepositTxPublishedMessage.class, PublishTradeStatistics.class 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 3f4dbd049a8..90164b9740e 100644 --- a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java @@ -49,7 +49,7 @@ @Slf4j public abstract class TradeProtocol { - private static final long TIMEOUT = 120; + private static final long TIMEOUT = 90; protected final ProcessModel processModel; private final DecryptedDirectMessageListener decryptedDirectMessageListener; @@ -67,7 +67,7 @@ public TradeProtocol(Trade trade) { PublicKey signaturePubKey = decryptedMessageWithPubKey.getSignaturePubKey(); if (tradingPeerPubKeyRing != null && signaturePubKey.equals(tradingPeerPubKeyRing.getSignaturePubKey())) { NetworkEnvelope networkEnvelope = decryptedMessageWithPubKey.getNetworkEnvelope(); - log.trace("handleNewMessage: message = " + networkEnvelope.getClass().getSimpleName() + " from " + peersNodeAddress); + log.trace("handleNewMessage: message = {} from {}", networkEnvelope.getClass().getSimpleName(), peersNodeAddress); if (networkEnvelope instanceof TradeMessage) { TradeMessage tradeMessage = (TradeMessage) networkEnvelope; nonEmptyStringOf(tradeMessage.getTradeId()); @@ -111,7 +111,6 @@ public void completed() { } private void cleanup() { - log.debug("cleanup " + this); stopTimeout(); trade.stateProperty().removeListener(stateChangeListener); // We removed that from here earlier as it broke the trade process in some non critical error cases. @@ -120,7 +119,7 @@ private void cleanup() { } public void applyMailboxMessage(DecryptedMessageWithPubKey decryptedMessageWithPubKey, Trade trade) { - log.debug("applyMailboxMessage " + decryptedMessageWithPubKey.getNetworkEnvelope()); + log.debug("applyMailboxMessage {}", decryptedMessageWithPubKey.getNetworkEnvelope()); if (processModel.getTradingPeer().getPubKeyRing() != null && decryptedMessageWithPubKey.getSignaturePubKey().equals(processModel.getTradingPeer().getPubKeyRing().getSignaturePubKey())) { processModel.setDecryptedMessageWithPubKey(decryptedMessageWithPubKey); @@ -157,7 +156,7 @@ protected void handleTaskRunnerSuccess(String info) { } protected void handleTaskRunnerSuccess(@Nullable TradeMessage tradeMessage, String info) { - log.debug("handleTaskRunnerSuccess " + info); + log.debug("handleTaskRunnerSuccess {}", info); sendAckMessage(tradeMessage, true, null); } @@ -231,7 +230,7 @@ public void onFault(String errorMessage) { private void cleanupTradableOnFault() { final Trade.State state = trade.getState(); - log.warn("cleanupTradableOnFault tradeState=" + state); + log.warn("cleanupTradableOnFault tradeState={}", state); TradeManager tradeManager = processModel.getTradeManager(); if (trade.isInPreparation()) { // no funds left. we just clean up the trade list diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerCreatesDepositTxInputs.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerCreatesDepositTxInputs.java index 653525fda5e..451a1d51a77 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerCreatesDepositTxInputs.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerCreatesDepositTxInputs.java @@ -18,8 +18,8 @@ package bisq.core.trade.protocol.tasks.buyer_as_taker; import bisq.core.btc.model.AddressEntry; -import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.model.InputsAndChangeOutput; +import bisq.core.btc.wallet.BtcWalletService; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; @@ -51,12 +51,11 @@ protected void run() { BtcWalletService walletService = processModel.getBtcWalletService(); Address takersAddress = walletService.getOrCreateAddressEntry(processModel.getOffer().getId(), AddressEntry.Context.RESERVED_FOR_TRADE).getAddress(); - Address takersChangeAddress = walletService.getFreshAddressEntry().getAddress(); InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs( + processModel.getTakeOfferFeeTx(), takerInputAmount, txFee.subtract(bsqTakerFee), - takersAddress, - takersChangeAddress); + takersAddress); processModel.setRawTransactionInputs(result.rawTransactionInputs); processModel.setChangeOutputValue(result.changeOutputValue); processModel.setChangeOutputAddress(result.changeOutputAddress); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerCreateAndSignContract.java b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerCreateAndSignContract.java index 29833835764..043e8a5baba 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerCreateAndSignContract.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerCreateAndSignContract.java @@ -94,7 +94,6 @@ protected void run() { taker.getMultiSigPubKey() ); String contractAsJson = Utilities.objectToJson(contract); - log.trace("Contract as json:{}", contractAsJson); String signature = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), contractAsJson); trade.setContract(contract); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerCreatesDepositTxInputs.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerCreatesDepositTxInputs.java index 14befb65d44..72f2d5791b8 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerCreatesDepositTxInputs.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerCreatesDepositTxInputs.java @@ -18,8 +18,8 @@ package bisq.core.trade.protocol.tasks.seller_as_taker; import bisq.core.btc.model.AddressEntry; -import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.model.InputsAndChangeOutput; +import bisq.core.btc.wallet.BtcWalletService; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; @@ -49,12 +49,11 @@ protected void run() { BtcWalletService walletService = processModel.getBtcWalletService(); Address takersAddress = walletService.getOrCreateAddressEntry(processModel.getOffer().getId(), AddressEntry.Context.RESERVED_FOR_TRADE).getAddress(); - Address takersChangeAddress = walletService.getFreshAddressEntry().getAddress(); InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs( + processModel.getTakeOfferFeeTx(), takerInputAmount, txFee, - takersAddress, - takersChangeAddress); + takersAddress); processModel.setRawTransactionInputs(result.rawTransactionInputs); processModel.setChangeOutputValue(result.changeOutputValue); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/CreateTakerFeeTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/CreateTakerFeeTx.java index 9334c2ec322..6a7ccfc8467 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/CreateTakerFeeTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/CreateTakerFeeTx.java @@ -18,12 +18,9 @@ package bisq.core.trade.protocol.tasks.taker; import bisq.core.arbitration.Arbitrator; -import bisq.core.btc.exceptions.TxBroadcastException; import bisq.core.btc.model.AddressEntry; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; -import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.btc.wallet.WalletService; import bisq.core.dao.exceptions.DaoDisabledException; import bisq.core.offer.availability.ArbitratorSelection; @@ -37,8 +34,6 @@ import lombok.extern.slf4j.Slf4j; -import javax.annotation.Nullable; - @Slf4j public class CreateTakerFeeTx extends TradeTask { @@ -56,15 +51,25 @@ protected void run() { processModel.getArbitratorManager()); BtcWalletService walletService = processModel.getBtcWalletService(); String id = processModel.getOffer().getId(); + + // We enforce here to create a MULTI_SIG and TRADE_PAYOUT address entry to avoid that the change output would be used later + // for those address entries. Because we do not commit our fee tx yet the change address would + // appear as unused and therefor selected for the outputs for the MS tx. + // That would cause incorrect display of the balance as + // the change output would be considered as not available balance (part of the locked trade amount). + walletService.getNewAddressEntry(id, AddressEntry.Context.MULTI_SIG); + walletService.getNewAddressEntry(id, AddressEntry.Context.TRADE_PAYOUT); + AddressEntry addressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.OFFER_FUNDING); AddressEntry reservedForTradeAddressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.RESERVED_FOR_TRADE); AddressEntry changeAddressEntry = walletService.getFreshAddressEntry(); Address fundingAddress = addressEntry.getAddress(); Address reservedForTradeAddress = reservedForTradeAddressEntry.getAddress(); Address changeAddress = changeAddressEntry.getAddress(); - final TradeWalletService tradeWalletService = processModel.getTradeWalletService(); + TradeWalletService tradeWalletService = processModel.getTradeWalletService(); + Transaction transaction; if (trade.isCurrencyForTakerFeeBtc()) { - tradeWalletService.createBtcTradingFeeTx( + transaction = tradeWalletService.createBtcTradingFeeTx( fundingAddress, reservedForTradeAddress, changeAddress, @@ -73,31 +78,9 @@ protected void run() { trade.getTakerFee(), trade.getTxFee(), arbitrator.getBtcAddress(), - new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction transaction) { - if (!completed) { - processModel.setTakeOfferFeeTx(transaction); - trade.setTakerFeeTxId(transaction.getHashAsString()); - walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.OFFER_FUNDING); - trade.setState(Trade.State.TAKER_PUBLISHED_TAKER_FEE_TX); - complete(); - } else { - log.warn("We got the onSuccess callback called after the timeout has been triggered a complete()."); - } - } - - @Override - public void onFailure(TxBroadcastException exception) { - if (!completed) { - failed(exception); - } else { - log.warn("We got the onFailure callback called after the timeout has been triggered a complete()."); - } - } - }); + false, + null); } else { - final BsqWalletService bsqWalletService = processModel.getBsqWalletService(); Transaction preparedBurnFeeTx = processModel.getBsqWalletService().getPreparedBurnFeeTx(trade.getTakerFee()); Transaction txWithBsqFee = tradeWalletService.completeBsqTradingFeeTx(preparedBurnFeeTx, fundingAddress, @@ -106,47 +89,17 @@ public void onFailure(TxBroadcastException exception) { processModel.getFundsNeededForTradeAsLong(), processModel.isUseSavingsWallet(), trade.getTxFee()); + transaction = processModel.getBsqWalletService().signTx(txWithBsqFee); + WalletService.checkAllScriptSignaturesForTx(transaction); + } - Transaction signedTx = processModel.getBsqWalletService().signTx(txWithBsqFee); - WalletService.checkAllScriptSignaturesForTx(signedTx); - bsqWalletService.commitTx(signedTx); - // We need to create another instance, otherwise the tx would trigger an invalid state exception - // if it gets committed 2 times - tradeWalletService.commitTx(tradeWalletService.getClonedTransaction(signedTx)); - - bsqWalletService.broadcastTx(signedTx, new TxBroadcaster.Callback() { - @Override - public void onSuccess(@Nullable Transaction transaction) { - if (!completed) { - if (transaction != null) { - log.debug("Successfully sent tx with id " + transaction.getHashAsString()); - trade.setTakerFeeTxId(transaction.getHashAsString()); - processModel.setTakeOfferFeeTx(transaction); - walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.OFFER_FUNDING); - trade.setState(Trade.State.TAKER_PUBLISHED_TAKER_FEE_TX); - - complete(); - } - } else { - log.warn("We got the onSuccess callback called after the timeout has been triggered a complete()."); - } - } + // We did not broadcast and commit the tx yet to avoid issues with lost trade fee in case the + // take offer attempt failed. - @Override - public void onFailure(TxBroadcastException exception) { - if (!completed) { - log.error(exception.toString()); - exception.printStackTrace(); - trade.setErrorMessage("An error occurred.\n" + - "Error message:\n" - + exception.getMessage()); - failed(exception); - } else { - log.warn("We got the onFailure callback called after the timeout has been triggered a complete()."); - } - } - }); - } + trade.setTakerFeeTxId(transaction.getHashAsString()); + processModel.setTakeOfferFeeTx(transaction); + walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.OFFER_FUNDING); + complete(); } catch (Throwable t) { if (t instanceof DaoDisabledException) { failed("You cannot pay the trade fee in BSQ at the moment because the DAO features have been " + diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerPublishFeeTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerPublishFeeTx.java new file mode 100644 index 00000000000..0325bbb8c3e --- /dev/null +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerPublishFeeTx.java @@ -0,0 +1,113 @@ +/* + * 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.protocol.tasks.taker; + +import bisq.core.btc.exceptions.TxBroadcastException; +import bisq.core.btc.wallet.BsqWalletService; +import bisq.core.btc.wallet.TradeWalletService; +import bisq.core.btc.wallet.TxBroadcaster; +import bisq.core.dao.state.model.blockchain.TxType; +import bisq.core.trade.Trade; +import bisq.core.trade.protocol.tasks.TradeTask; + +import bisq.common.taskrunner.TaskRunner; + +import org.bitcoinj.core.Transaction; + +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; + +@Slf4j +public class TakerPublishFeeTx extends TradeTask { + @SuppressWarnings({"WeakerAccess", "unused"}) + public TakerPublishFeeTx(TaskRunner taskHandler, Trade trade) { + super(taskHandler, trade); + } + + @Override + protected void run() { + try { + runInterceptHook(); + + TradeWalletService tradeWalletService = processModel.getTradeWalletService(); + Transaction takeOfferFeeTx = processModel.getTakeOfferFeeTx(); + + if (trade.isCurrencyForTakerFeeBtc()) { + // We committed to be sure the tx gets into the wallet even in the broadcast process it would be + // committed as well, but if user would close app before success handler returns the commit would not + // be done. + tradeWalletService.commitTx(takeOfferFeeTx); + + tradeWalletService.broadcastTx(takeOfferFeeTx, + new TxBroadcaster.Callback() { + @Override + public void onSuccess(Transaction transaction) { + trade.setState(Trade.State.TAKER_PUBLISHED_TAKER_FEE_TX); + complete(); + } + + @Override + public void onFailure(TxBroadcastException exception) { + failed(exception); + } + }); + } else { + BsqWalletService bsqWalletService = processModel.getBsqWalletService(); + bsqWalletService.commitTx(takeOfferFeeTx, TxType.PAY_TRADE_FEE); + // We need to create another instance, otherwise the tx would trigger an invalid state exception + // if it gets committed 2 times + tradeWalletService.commitTx(tradeWalletService.getClonedTransaction(takeOfferFeeTx)); + + bsqWalletService.broadcastTx(takeOfferFeeTx, + new TxBroadcaster.Callback() { + @Override + public void onSuccess(@Nullable Transaction transaction) { + if (!completed) { + if (transaction != null) { + trade.setTakerFeeTxId(transaction.getHashAsString()); + processModel.setTakeOfferFeeTx(transaction); + trade.setState(Trade.State.TAKER_PUBLISHED_TAKER_FEE_TX); + + complete(); + } + } else { + log.warn("We got the onSuccess callback called after the timeout has been triggered a complete()."); + } + } + + @Override + public void onFailure(TxBroadcastException exception) { + if (!completed) { + log.error(exception.toString()); + exception.printStackTrace(); + trade.setErrorMessage("An error occurred.\n" + + "Error message:\n" + + exception.getMessage()); + failed(exception); + } else { + log.warn("We got the onFailure callback called after the timeout has been triggered a complete()."); + } + } + }); + } + } catch (Throwable t) { + failed(t); + } + } +} diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendPayDepositRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendPayDepositRequest.java index 353690b2e4e..8db28d04908 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendPayDepositRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendPayDepositRequest.java @@ -65,12 +65,15 @@ protected void run() { BtcWalletService walletService = processModel.getBtcWalletService(); String id = processModel.getOffer().getId(); - checkArgument(!walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG).isPresent(), - "addressEntry must not be set here."); + checkArgument(walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG).isPresent(), + "MULTI_SIG addressEntry must have been already set here."); AddressEntry addressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.MULTI_SIG); byte[] takerMultiSigPubKey = addressEntry.getPubKey(); processModel.setMyMultiSigPubKey(takerMultiSigPubKey); + + checkArgument(walletService.getAddressEntry(id, AddressEntry.Context.TRADE_PAYOUT).isPresent(), + "TRADE_PAYOUT addressEntry must have been already set here."); AddressEntry takerPayoutAddressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.TRADE_PAYOUT); String takerPayoutAddressString = takerPayoutAddressEntry.getAddressString(); diff --git a/core/src/main/java/bisq/core/trade/statistics/AssetTradeActivityCheck.java b/core/src/main/java/bisq/core/trade/statistics/AssetTradeActivityCheck.java index 5e3e00d4823..a53fa51ea70 100644 --- a/core/src/main/java/bisq/core/trade/statistics/AssetTradeActivityCheck.java +++ b/core/src/main/java/bisq/core/trade/statistics/AssetTradeActivityCheck.java @@ -194,6 +194,29 @@ private boolean isWarmingUp(String code) { newlyAdded.add("WEB"); newlyAdded.add("WRKZ"); + // v0.9.3 - nothing added, was hotfix + + // v0.9.4 (Feb 18 2019) + newlyAdded.add("ADE"); + newlyAdded.add("ASK"); + newlyAdded.add("AEUR"); + newlyAdded.add("AUS"); + newlyAdded.add("CASH2"); + newlyAdded.add("DARX"); + newlyAdded.add("CRDS"); + newlyAdded.add("CRCL"); + newlyAdded.add("DAI"); + newlyAdded.add("ONION"); + newlyAdded.add("FJC"); + newlyAdded.add("LYTX"); + newlyAdded.add("LTZ"); + newlyAdded.add("MILE"); + newlyAdded.add("PRSN"); + newlyAdded.add("TUSD"); + newlyAdded.add("USDC"); + newlyAdded.add("VXV"); + newlyAdded.add("ZEL"); + return newlyAdded.contains(code); } } 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 f2b77c60f2e..443b489fe67 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java @@ -29,6 +29,7 @@ import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.crypto.Hash; import bisq.common.proto.persistable.PersistableEnvelope; import bisq.common.util.JsonExclude; @@ -44,10 +45,7 @@ import org.springframework.util.CollectionUtils; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import java.util.Optional; @@ -215,10 +213,8 @@ public static TradeStatistics2 fromProto(PB.TradeStatistics2 proto) { /////////////////////////////////////////////////////////////////////////////////////////// @Override - public List getRequiredCapabilities() { - return new ArrayList<>(Collections.singletonList( - Capabilities.Capability.TRADE_STATISTICS_2.ordinal() - )); + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.TRADE_STATISTICS_2); } @Override diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index ce095e90bd1..754e6b75a0e 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -39,8 +39,6 @@ import bisq.common.storage.Storage; import bisq.common.util.Utilities; -import org.bitcoinj.core.Coin; - import javax.inject.Inject; import javax.inject.Named; @@ -56,6 +54,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; @@ -89,8 +88,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid new BlockChainExplorer("SoChain. Wow.", "https://chain.so/tx/BTC/", "https://chain.so/address/BTC/"), new BlockChainExplorer("Blockchain.info", "https://blockchain.info/tx/", "https://blockchain.info/address/"), new BlockChainExplorer("Insight", "https://insight.bitpay.com/tx/", "https://insight.bitpay.com/address/") - ) - ); + )); private static final ArrayList BTC_TEST_NET_EXPLORERS = new ArrayList<>(Arrays.asList( new BlockChainExplorer("Blockstream.info", "https://blockstream.info/testnet/tx/", "https://blockstream.info/testnet/address/"), new BlockChainExplorer("Blockstream.info Tor V3", "http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion/testnet/tx/", "http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion/testnet/address/"), @@ -100,11 +98,16 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid new BlockChainExplorer("Smartbit", "https://testnet.smartbit.com.au/tx/", "https://testnet.smartbit.com.au/address/"), new BlockChainExplorer("SoChain. Wow.", "https://chain.so/tx/BTCTEST/", "https://chain.so/address/BTCTEST/") )); + private static final ArrayList BTC_DAO_TEST_NET_EXPLORERS = new ArrayList<>(Collections.singletonList( + new BlockChainExplorer("BTC DAO-testnet explorer", "https://bisq.network/explorer/btc/dao_testnet/tx/", "https://bisq.network/explorer/btc/dao_testnet/address/") + )); public static final BlockChainExplorer BSQ_MAIN_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.network/tx.html?tx=", "https://explorer.bisq.network/Address.html?addr="); - public static final BlockChainExplorer BSQ_TEST_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.network/testnet/tx.html?tx=", - "https://explorer.bisq.network/testnet/Address.html?addr="); + private static final BlockChainExplorer BSQ_BETA_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.betanet.bisq.network/tx.html?tx=", + "https://explorer.betanet.bisq.network/Address.html?addr="); + private static final BlockChainExplorer BSQ_TEST_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.testnet.bisq.network/tx.html?tx=", + "https://explorer.testnet.bisq.network/Address.html?addr="); // payload is initialized so the default values are available for Property initialization. @Setter @@ -237,7 +240,9 @@ public void readPersisted() { prefPayload.setSellScreenCurrencyCode(preferredTradeCurrency.getCode()); } - prefPayload.setBsqBlockChainExplorer(baseCurrencyNetwork.isMainnet() ? BSQ_MAIN_NET_EXPLORER : BSQ_TEST_NET_EXPLORER); + prefPayload.setBsqBlockChainExplorer(baseCurrencyNetwork.isMainnet() ? BSQ_MAIN_NET_EXPLORER : + baseCurrencyNetwork.isDaoBetaNet() ? BSQ_BETA_NET_EXPLORER : + BSQ_TEST_NET_EXPLORER); // We don't want to pass Preferences to all popups where the dont show again checkbox is used, so we use // that static lookup class to avoid static access to the Preferences directly. @@ -484,10 +489,10 @@ public void setWithdrawalTxFeeInBytes(long withdrawalTxFeeInBytes) { withdrawalTxFeeInBytesProperty.set(withdrawalTxFeeInBytes); } - public void setBuyerSecurityDepositAsLong(long buyerSecurityDepositAsLong) { - prefPayload.setBuyerSecurityDepositAsLong(Math.min(Restrictions.getMaxBuyerSecurityDeposit().value, - Math.max(Restrictions.getMinBuyerSecurityDeposit().value, - buyerSecurityDepositAsLong))); + public void setBuyerSecurityDepositAsPercent(double buyerSecurityDepositAsPercent) { + double max = Restrictions.getMaxBuyerSecurityDepositAsPercent(); + double min = Restrictions.getMinBuyerSecurityDepositAsPercent(); + prefPayload.setBuyerSecurityDepositAsPercent(Math.min(max, Math.max(min, buyerSecurityDepositAsPercent))); persist(); } @@ -639,20 +644,34 @@ public ObservableMap getDontShowAgainMapAsObservable() { } public BlockChainExplorer getBlockChainExplorer() { - if (BisqEnvironment.getBaseCurrencyNetwork().isMainnet()) - return prefPayload.getBlockChainExplorerMainNet(); - else - return prefPayload.getBlockChainExplorerTestNet(); + BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); + switch (baseCurrencyNetwork) { + case BTC_MAINNET: + return prefPayload.getBlockChainExplorerMainNet(); + case BTC_TESTNET: + case BTC_REGTEST: + return prefPayload.getBlockChainExplorerTestNet(); + case BTC_DAO_TESTNET: + return BTC_DAO_TEST_NET_EXPLORERS.get(0); + case BTC_DAO_BETANET: + return prefPayload.getBlockChainExplorerMainNet(); + default: + throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork); + } } public ArrayList getBlockChainExplorers() { - final BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); + BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); switch (baseCurrencyNetwork) { case BTC_MAINNET: return BTC_MAIN_NET_EXPLORERS; case BTC_TESTNET: case BTC_REGTEST: return BTC_TEST_NET_EXPLORERS; + case BTC_DAO_TESTNET: + return BTC_DAO_TEST_NET_EXPLORERS; + case BTC_DAO_BETANET: + return BTC_MAIN_NET_EXPLORERS; default: throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork); } @@ -683,8 +702,9 @@ public LongProperty withdrawalTxFeeInBytesProperty() { return withdrawalTxFeeInBytesProperty; } - public Coin getBuyerSecurityDepositAsCoin() { - return Coin.valueOf(prefPayload.getBuyerSecurityDepositAsLong()); + public double getBuyerSecurityDepositAsPercent() { + double value = prefPayload.getBuyerSecurityDepositAsPercent(); + return value == 0 ? Restrictions.getDefaultBuyerSecurityDepositAsPercent() : value; } //TODO remove and use isPayFeeInBtc instead @@ -762,8 +782,6 @@ private interface ExcludesDelegateMethods { void setWithdrawalTxFeeInBytes(long withdrawalTxFeeInBytes); - void setBuyerSecurityDepositAsLong(long buyerSecurityDepositAsLong); - void setSelectedPaymentAccountForCreateOffer(@Nullable PaymentAccount paymentAccount); void setBsqBlockChainExplorer(BlockChainExplorer bsqBlockChainExplorer); @@ -819,5 +837,9 @@ private interface ExcludesDelegateMethods { void setRpcPw(String value); void setTakeOfferSelectedPaymentAccountId(String value); + + void setBuyerSecurityDepositAsPercent(double buyerSecurityDepositAsPercent); + + double getBuyerSecurityDepositAsPercent(); } } diff --git a/core/src/main/java/bisq/core/user/PreferencesPayload.java b/core/src/main/java/bisq/core/user/PreferencesPayload.java index 662126bfa34..9c62b20c815 100644 --- a/core/src/main/java/bisq/core/user/PreferencesPayload.java +++ b/core/src/main/java/bisq/core/user/PreferencesPayload.java @@ -89,7 +89,10 @@ public final class PreferencesPayload implements PersistableEnvelope { private String bitcoinNodes = ""; private List ignoreTradersList = new ArrayList<>(); private String directoryChooserPath; - private long buyerSecurityDepositAsLong = Restrictions.getDefaultBuyerSecurityDeposit().value; + + @Deprecated // Superseded by buyerSecurityDepositAsPercent + private long buyerSecurityDepositAsLong; + private boolean useAnimations; @Nullable private PaymentAccount selectedPaymentAccountForCreateOffer; @@ -117,6 +120,7 @@ public final class PreferencesPayload implements PersistableEnvelope { String rpcPw; @Nullable String takeOfferSelectedPaymentAccountId; + private double buyerSecurityDepositAsPercent = Restrictions.getDefaultBuyerSecurityDepositAsPercent(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -172,7 +176,8 @@ public Message toProtoMessage() { .setUseMarketNotifications(useMarketNotifications) .setUsePriceNotifications(usePriceNotifications) .setUseStandbyMode(useStandbyMode) - .setIsDaoFullNode(isDaoFullNode); + .setIsDaoFullNode(isDaoFullNode) + .setBuyerSecurityDepositAsPercent(buyerSecurityDepositAsPercent); Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory); Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((PB.TradeCurrency) e.toProtoMessage())); Optional.ofNullable(offerBookChartScreenCurrencyCode).ifPresent(builder::setOfferBookChartScreenCurrencyCode); @@ -253,6 +258,7 @@ public static PersistableEnvelope fromProto(PB.PreferencesPayload proto, CorePro proto.getIsDaoFullNode(), proto.getRpcUser().isEmpty() ? null : proto.getRpcUser(), proto.getRpcPw().isEmpty() ? null : proto.getRpcPw(), - proto.getTakeOfferSelectedPaymentAccountId().isEmpty() ? null : proto.getTakeOfferSelectedPaymentAccountId()); + proto.getTakeOfferSelectedPaymentAccountId().isEmpty() ? null : proto.getTakeOfferSelectedPaymentAccountId(), + proto.getBuyerSecurityDepositAsPercent()); } } diff --git a/core/src/main/java/bisq/core/util/BSFormatter.java b/core/src/main/java/bisq/core/util/BSFormatter.java index 8d6677b2a43..bf0db4618ea 100644 --- a/core/src/main/java/bisq/core/util/BSFormatter.java +++ b/core/src/main/java/bisq/core/util/BSFormatter.java @@ -51,6 +51,7 @@ import java.util.Date; import java.util.List; import java.util.Locale; +import java.util.TimeZone; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -482,9 +483,18 @@ public String languageCodesToString(List languageLocales) { } public String formatDateTime(Date date) { - return formatDateTime(date, - DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale()), - DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())); + return formatDateTime(date, true); + } + + public String formatDateTime(Date date, boolean useLocaleAndLocalTimezone) { + Locale locale = useLocaleAndLocalTimezone ? getLocale() : Locale.US; + DateFormat dateInstance = DateFormat.getDateInstance(DateFormat.DEFAULT, locale); + DateFormat timeInstance = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale); + if (!useLocaleAndLocalTimezone) { + dateInstance.setTimeZone(TimeZone.getTimeZone("UTC")); + timeInstance.setTimeZone(TimeZone.getTimeZone("UTC")); + } + return formatDateTime(date, dateInstance, timeInstance); } public String formatDateTime(Date date, DateFormat dateFormatter, DateFormat timeFormatter) { @@ -546,7 +556,7 @@ public double parsePercentStringToDouble(String percentString) throws NumberForm String input = percentString.replace("%", ""); input = cleanDoubleInput(input); double value = Double.parseDouble(input); - return value / 100d; + return MathUtils.roundDouble(value / 100d, 2); } public long parsePriceStringToLong(String currencyCode, String amount, int precision) { diff --git a/core/src/main/java/bisq/core/util/BsqFormatter.java b/core/src/main/java/bisq/core/util/BsqFormatter.java index 3b04bb1c5b2..6dc5defc2b9 100644 --- a/core/src/main/java/bisq/core/util/BsqFormatter.java +++ b/core/src/main/java/bisq/core/util/BsqFormatter.java @@ -18,7 +18,7 @@ package bisq.core.util; import bisq.core.app.BisqEnvironment; -import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.param.Param; import bisq.core.locale.GlobalSettings; import bisq.core.locale.Res; @@ -136,19 +136,19 @@ public Coin parseToBTC(String input) { return super.parseToCoin(input, btcCoinFormat); } - public void validateBtcInput(String input) throws ValidationException { + public void validateBtcInput(String input) throws ProposalValidationException { validateCoinInput(input, btcCoinFormat); } - public void validateBsqInput(String input) throws ValidationException { + public void validateBsqInput(String input) throws ProposalValidationException { validateCoinInput(input, this.coinFormat); } - private void validateCoinInput(String input, MonetaryFormat coinFormat) throws ValidationException { + private void validateCoinInput(String input, MonetaryFormat coinFormat) throws ProposalValidationException { try { coinFormat.parse(cleanDoubleInput(input)); } catch (Throwable t) { - throw new ValidationException("Invalid format for a " + coinFormat.code() + " value"); + throw new ProposalValidationException("Invalid format for a " + coinFormat.code() + " value"); } } @@ -156,7 +156,7 @@ public String formatParamValue(Param param, String value) { switch (param.getParamType()) { case UNDEFINED: // In case we add a new param old clients will not know that enum and fall back to UNDEFINED. - return ""; + return Res.get("shared.na"); case BSQ: return formatCoinWithCode(parseToCoin(value)); case BTC: @@ -168,7 +168,8 @@ public String formatParamValue(Param param, String value) { case ADDRESS: return value; default: - throw new IllegalArgumentException("Unsupported paramType. param: " + param); + log.warn("Param type {} not handled in switch case at formatParamValue", param.getParamType()); + return Res.get("shared.na"); } } @@ -192,17 +193,16 @@ public int parseParamValueToBlocks(Param param, String inputValue) { } } - public String parseParamValueToString(Param param, String inputValue) throws ValidationException { + public String parseParamValueToString(Param param, String inputValue) throws ProposalValidationException { switch (param.getParamType()) { case UNDEFINED: - throw new IllegalArgumentException("ParamType UNDEFINED. param: " + param); + return Res.get("shared.na"); case BSQ: return formatCoin(parseParamValueToCoin(param, inputValue)); case BTC: return formatBTC(parseParamValueToCoin(param, inputValue)); case PERCENT: return formatToPercent(parsePercentStringToDouble(inputValue)); - case BLOCK: return Integer.toString(parseParamValueToBlocks(param, inputValue)); case ADDRESS: @@ -210,9 +210,10 @@ public String parseParamValueToString(Param param, String inputValue) throws Val if (validationResult.isValid) return inputValue; else - throw new ValidationException(validationResult.errorMessage); + throw new ProposalValidationException(validationResult.errorMessage); default: - throw new IllegalArgumentException("Unsupported paramType. param: " + param); + log.warn("Param type {} not handled in switch case at parseParamValueToString", param.getParamType()); + return Res.get("shared.na"); } } } diff --git a/core/src/main/java/bisq/core/util/CoinUtil.java b/core/src/main/java/bisq/core/util/CoinUtil.java index 26c8982643a..7357c61c366 100644 --- a/core/src/main/java/bisq/core/util/CoinUtil.java +++ b/core/src/main/java/bisq/core/util/CoinUtil.java @@ -24,10 +24,11 @@ public class CoinUtil { public static Coin getFeePerBtc(Coin feePerBtc, Coin amount) { - double feePerBtcAsDouble = (double) feePerBtc.value; - double amountAsDouble = (double) amount.value; + double feePerBtcAsDouble = feePerBtc != null ? (double) feePerBtc.value : 0; + double amountAsDouble = amount != null ? (double) amount.value : 0; double btcAsDouble = (double) Coin.COIN.value; - return Coin.valueOf(Math.round(feePerBtcAsDouble * (amountAsDouble / btcAsDouble))); + double fact = amountAsDouble / btcAsDouble; + return Coin.valueOf(Math.round(feePerBtcAsDouble * fact)); } public static Coin minCoin(Coin a, Coin b) { @@ -39,6 +40,27 @@ public static Coin maxCoin(Coin a, Coin b) { } public static double getFeePerByte(Coin miningFee, int txSize) { - return MathUtils.roundDouble(((double) miningFee.value / (double) txSize), 2); + double value = miningFee != null ? miningFee.value : 0; + return MathUtils.roundDouble((value / (double) txSize), 2); + } + + /** + * @param value Btc amount to be converted to percent value. E.g. 0.01 BTC is 1% (of 1 BTC) + * @return The percentage value as double (e.g. 1% is 0.01) + */ + public static double getAsPercentPerBtc(Coin value) { + double asDouble = value != null ? (double) value.value : 0; + double btcAsDouble = (double) Coin.COIN.value; + return MathUtils.roundDouble(asDouble / btcAsDouble, 4); + } + + /** + * @param percent The percentage value as double (e.g. 1% is 0.01) + * @param amount The amount as Coin for the percentage calculation + * @return The percentage as Coin (e.g. 1% of 1 BTC is 0.01 BTC) + */ + public static Coin getPercentOfAmountAsCoin(double percent, Coin amount) { + double amountAsDouble = amount != null ? (double) amount.value : 0; + return Coin.valueOf(Math.round(percent * amountAsDouble)); } } diff --git a/core/src/main/resources/bisq.policy b/core/src/main/resources/bisq.policy index 18e48705bb1..71c75aa4cd0 100644 --- a/core/src/main/resources/bisq.policy +++ b/core/src/main/resources/bisq.policy @@ -15,7 +15,6 @@ grant { permission "java.util.PropertyPermission" "torDir", "read"; permission "java.util.PropertyPermission" "maxConnections", "read"; permission "java.util.PropertyPermission" "networkId", "read"; - permission "java.util.PropertyPermission" "myAddress", "read"; permission "java.util.PropertyPermission" "banList", "read"; permission "java.util.PropertyPermission" "socks5ProxyBtcAddress", "read"; permission "java.util.PropertyPermission" "socks5ProxyHttpAddress", "read"; @@ -63,7 +62,6 @@ grant { permission "java.lang.RuntimePermission" "getenv.dumpStatistics"; permission "java.lang.RuntimePermission" "getenv.torDir"; permission "java.lang.RuntimePermission" "getenv.maxConnections"; - permission "java.lang.RuntimePermission" "getenv.myAddress"; permission "java.lang.RuntimePermission" "getenv.networkId"; permission "java.lang.RuntimePermission" "getenv.banList"; permission "java.lang.RuntimePermission" "getenv.socks5ProxyBtcAddress"; diff --git a/core/src/main/resources/btc_dao_betanet.seednodes b/core/src/main/resources/btc_dao_betanet.seednodes new file mode 100644 index 00000000000..774a9ec9840 --- /dev/null +++ b/core/src/main/resources/btc_dao_betanet.seednodes @@ -0,0 +1,3 @@ +# nodeaddress.onion:port [(@owner)] +localhost:3004 + diff --git a/core/src/main/resources/btc_dao_testnet.seednodes b/core/src/main/resources/btc_dao_testnet.seednodes new file mode 100644 index 00000000000..fa63d9f5aed --- /dev/null +++ b/core/src/main/resources/btc_dao_testnet.seednodes @@ -0,0 +1,5 @@ +# nodeaddress.onion:port [(@owner)] +fjr5w4eckjghqtnu.onion:8003 +3d56s6acbi3vk52v.onion:8003 +74w2sttlo4qk6go3.onion:8003 +jmc5ajqvtnzqaggm.onion:8003 diff --git a/core/src/main/resources/btc_mainnet.seednodes b/core/src/main/resources/btc_mainnet.seednodes new file mode 100644 index 00000000000..27a6f28feb9 --- /dev/null +++ b/core/src/main/resources/btc_mainnet.seednodes @@ -0,0 +1,9 @@ +# nodeaddress.onion:port [(@owner)] +5quyxpxheyvzmb2d.onion:8000 (@miker) +s67qglwhkgkyvr74.onion:8000 (@emzy) +ef5qnzx6znifo3df.onion:8000 (@freimair) +jhgcy2won7xnslrb.onion:8000 (@freimair) +3f3cu2yw7u457ztq.onion:8000 (@manfredkarrer) +723ljisnynbtdohi.onion:8000 (@manfredkarrer) +rm7b56wbrcczpjvl.onion:8000 (@manfredkarrer) +fl3mmribyxgrv63c.onion:8000 (@manfredkarrer) diff --git a/core/src/main/resources/btc_regtest.seednodes b/core/src/main/resources/btc_regtest.seednodes new file mode 100644 index 00000000000..caa87710d68 --- /dev/null +++ b/core/src/main/resources/btc_regtest.seednodes @@ -0,0 +1,17 @@ +# By default developers use either port 2002 or 3002 or both as local seed nodes. If they want to use regtest +# with Tor they have to add a program argument to pass the custom onion address of the local Tor seed node. +# E.g. --seedNodes=YOUR_ONION.onion:2002 + +# To create your local onion addresses follow those steps: +# 1. Run a seed node with prog args: --bitcoinNetwork=regtest --nodePort=2002 --appName=bisq_seed_node_localhost_YOUR_ONION +# 2. Find your local onion address in bisq_seed_node_localhost_YOUR_ONION/regtest/tor/hiddenservice/hostname +# 3. Shut down the seed node +# 4. Rename YOUR_ONION at the directory with your local onion address as well as the appName program argument to reflect +# the real onion address. +# 5. Start the seed node again +# 6. Start the Bisq app which wants to connect to that seed node with program argument `--seedNodes=YOUR_ONION.onion:2002` + + +# nodeaddress.onion:port [(@owner)] +localhost:2002 +localhost:3002 diff --git a/core/src/main/resources/btc_testnet.seednodes b/core/src/main/resources/btc_testnet.seednodes new file mode 100644 index 00000000000..2f7938a97bc --- /dev/null +++ b/core/src/main/resources/btc_testnet.seednodes @@ -0,0 +1,3 @@ +# nodeaddress.onion:port [(@owner)] +crcez6idqpyramyk.onion:8001 +fjcwevejusrournu.onion:8001 diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index f51cff53b71..769a812ec3b 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -113,6 +113,7 @@ shared.reasonForPayment=Reason for payment shared.sendingConfirmation=Sending confirmation... shared.sendingConfirmationAgain=Please send confirmation again shared.exportCSV=Export to csv +shared.exportJSON=Export to JSON shared.noDateAvailable=No date available shared.arbitratorsFee=Arbitrator's fee shared.noDetailsAvailable=No details available @@ -235,8 +236,8 @@ mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo=Bitcoin network peers: {0} / {1} {2} mainView.footer.btcInfo.initializing=Connecting to Bitcoin network mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO -mainView.footer.btcInfo.synchronizedWith=synchronized with -mainView.footer.btcInfo.connectingTo=connecting to +mainView.footer.btcInfo.synchronizedWith=Synchronized with +mainView.footer.btcInfo.connectingTo=Connecting to mainView.footer.btcInfo.connectionFailed=connection failed mainView.footer.p2pInfo=P2P network peers: {0} @@ -368,7 +369,7 @@ createOffer.amountPriceBox.amountDescription=Amount of BTC to {0} createOffer.amountPriceBox.buy.volumeDescription=Amount in {0} to spend createOffer.amountPriceBox.sell.volumeDescription=Amount in {0} to receive createOffer.amountPriceBox.minAmountDescription=Minimum amount of BTC -createOffer.securityDeposit.prompt=Security deposit in BTC +createOffer.securityDeposit.prompt=Security deposit createOffer.fundsBox.title=Fund your offer createOffer.fundsBox.offerFee=Trade fee createOffer.fundsBox.networkFee=Mining fee @@ -416,7 +417,10 @@ createOffer.priceOutSideOfDeviation=The price you have entered is outside the ma createOffer.changePrice=Change price createOffer.tac=With publishing this offer I agree to trade with any trader who fulfills the conditions as defined in this screen. createOffer.currencyForFee=Trade fee -createOffer.setDeposit=Set buyer's security deposit +createOffer.setDeposit=Set buyer's security deposit (%) +createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) +createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} #################################################################### @@ -667,6 +671,15 @@ tradeFeedbackWindow.msg.part1=We'd love to hear back from you about your experie tradeFeedbackWindow.msg.part2=If you have any questions, or experienced any problems, please get in touch with other users and contributors via the Bisq forum at: tradeFeedbackWindow.msg.part3=Thanks for using Bisq! + +daoTestingFeedbackWindow.title=Thank you for testing the Bisq DAO +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\ + \nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Visit forum +daoTestingFeedbackWindow.msg.part3=Thanks for using Bisq! + portfolio.pending.role=My role portfolio.pending.tradeInformation=Trade information portfolio.pending.remainingTime=Remaining time @@ -846,9 +859,9 @@ support.initialInfo=Please enter a description of your problem in the text field \t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\n\ You can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=System message: {0} -support.youOpenedTicket=You opened a request for support.\n\n{0} -support.youOpenedDispute=You opened a request for a dispute.\n\n{0} -support.peerOpenedTicket=Your trading peer has requested support due technical problems. Please wait for further instructions. +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0} @@ -1018,6 +1031,24 @@ described on the {1} web page.\nUsing wallets from centralized exchanges where ( (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe arbitrator is \ not a {2} specialist and cannot help in such cases. account.altcoin.popup.wallet.confirm=I understand and confirm that I know which wallet I need to use. +account.altcoin.popup.arq.msg=Trading ARQ on Bisq requires that you understand and fulfill \ +the following requirements:\n\n\ +For 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.\n\ +arqma-wallet-cli (use the command get_tx_key)\n\ +arqma-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\n\ +At normal block explorers the transfer is not verifiable.\n\n\ +You 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\n\ +Failure 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 \ +arbitrator in case of a dispute.\n\n\ +There is no payment ID required, just the normal public address.\n\ +If 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\n\ For sending XMR, you need to use either the official Monero GUI wallet or Monero CLI wallet with the \ @@ -1066,6 +1097,18 @@ transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\n\ Failure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the \ CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\n\ If you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill \ +the following requirements:\n\n\ +To send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\n\ +After a transaction is sent, the transaction ID will be displayed. You must save this information. \ +Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the \ +transaction secret key. \n\n\ +In the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, \ +2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC \ +transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\n\ +Failure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the \ +QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\n\ +If you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill \ the following requirements:\n\n\ Because of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you \ @@ -1140,8 +1183,12 @@ account.seed.enterPw=Enter password to view seed words account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is \ only for emergency cases and might cause problems with the internal wallet database.\n\ It is not a way for applying a backup! Please use a backup from the application data directory for restoring a \ - previous application state. -account.seed.restore.ok=Ok, I understand and want to restore + previous application state.\n\n\ + After restoring the application will shut down automatically. After you have restarted the application it will resync \ + with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and \ + had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file \ + again or repeat the restore process. +account.seed.restore.ok=Ok, do the restore and shut down Bisq #################################################################### @@ -1356,17 +1403,12 @@ dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC # suppress inspection "UnusedProperty" dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC - dao.param.currentValue=Current value: {0} dao.param.blocks={0} blocks -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Duration of {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} block(s) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(default value) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(has been changed in voting) dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was \ @@ -1411,9 +1453,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Lockup dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\nMining fee: {3} ({4} Satoshis/byte)\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\nMining fee: {3} ({4} Satoshis/byte)\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds @@ -1441,6 +1483,8 @@ dao.bond.table.button.lockup=Lockup dao.bond.table.button.unlock=Unlock dao.bond.table.button.revoke=Revoke +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Not bonded yet # suppress inspection "UnusedProperty" @@ -1458,13 +1502,17 @@ dao.bond.bondState.UNLOCKED=Bond unlocked # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Bond confiscated +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Bonded reputation # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github admin +dao.bond.bondedRoleType.UNDEFINED=Undefined +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin # suppress inspection "UnusedProperty" @@ -1472,24 +1520,30 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Rocket chat admin # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Youtube admin +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq maintainer # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-Bisq-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Website operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Forum operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Price node operator +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Btc node operator +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ explorer operator # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domain name holder # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS admin @@ -1497,6 +1551,8 @@ dao.bond.bondedRoleType.DNS_ADMIN=DNS admin dao.bond.bondedRoleType.MEDIATOR=Mediator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Arbitrator +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=Asset listing fee @@ -1574,6 +1630,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Vote reveal # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Vote result +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Compensation request # suppress inspection "UnusedProperty" @@ -1589,6 +1647,9 @@ dao.proposal.type.GENERIC=Generic proposal # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Proposal for confiscating a bond + +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Compensation request # suppress inspection "UnusedProperty" @@ -1634,7 +1695,7 @@ dao.proposal=proposal dao.proposal.display.type=Proposal type dao.proposal.display.name=Name/nickname dao.proposal.display.link=Link to detail info -dao.proposal.display.link.prompt=Link to Github issue +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=Requested amount in BSQ dao.proposal.display.bsqAddress=BSQ address dao.proposal.display.txId=Proposal transaction ID @@ -1715,7 +1776,7 @@ dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censor dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by \ running it on testnet. -dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on testnet +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the \ BSQ address below and make a request for taking part of the BSQ genesis distribution. dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution @@ -1737,6 +1798,8 @@ dao.wallet.chainHeightSynced=Latest verified block: {0} dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1} dao.wallet.tx.type=Type +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Not recognized # suppress inspection "UnusedProperty" @@ -1807,17 +1870,17 @@ dao.news.pastContribution.description=If you have contributed to Bisq please use dao.news.pastContribution.yourAddress=Your BSQ Wallet Address dao.news.pastContribution.requestNow=Request now -dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON TESTNET -dao.news.DAOOnTestnet.description=The main net Bisq DAO is not launched yet but you can learn about the Bisq DAO \ - by running it on testnet. -dao.news.DAOOnTestnet.firstSection.title=1. Switch to Testnet Mode -dao.news.DAOOnTestnet.firstSection.content=Switch to testnet from the Settings screen. +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO \ + by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer -dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on any bitcoin block explorer. +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. dao.news.DAOOnTestnet.readMoreLink=Read the full documentation #################################################################### @@ -2283,6 +2346,10 @@ BTC_MAINNET=Bitcoin Mainnet BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=Year time.month=Month @@ -2361,6 +2428,11 @@ payment.country=Country payment.extras=Extra requirements payment.email.mobile=Email or mobile no. payment.altcoin.address=Altcoin address +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able \ + to complete the trade in less than 1 hour.\n\n\ + If you have offers open and you are not available please disable \ + those offers under the 'Portfolio' screen. payment.altcoin=Altcoin payment.select.altcoin=Select or search altcoin payment.secret=Secret question @@ -2468,6 +2540,13 @@ payment.cashDeposit.info=Please confirm your bank allows you to send cash deposi payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut \ otherwise the BTC buyer cannot send you the funds. +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\n\ + However, please be aware of potentially increased risks associated with their use. Bisq will not bear any \ + responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC \ + to the sender of the money order, provided they can produce tracking information and receipts. \ + It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the \ + risk that the money order is cashed by someone else. + payment.f2f.contact=Contact info payment.f2f.contact.prompt=How you want to get contacted by the trading peer? (email address, phone number,...) payment.f2f.city=City for 'Face to face' meeting @@ -2559,6 +2638,17 @@ BLOCK_CHAINS=Altcoins PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY=OKPay +# suppress inspection "UnusedProperty" +CASH_APP=Cash App +# suppress inspection "UnusedProperty" +VENMO=Venmo + # suppress inspection "UnusedProperty" UPHOLD_SHORT=Uphold @@ -2596,6 +2686,17 @@ BLOCK_CHAINS_SHORT=Altcoins PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index b70420d0ef9..0312a6d0d3d 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -96,15 +96,15 @@ shared.faq=FAQ-Webseite besuchen shared.yesCancel=Ja, abbrechen shared.nextStep=Nächster Schritt shared.selectTradingAccount=Handelskonto auswählen -shared.fundFromSavingsWalletButton=Gelder aus Bisq-Brieftasche überweisen -shared.fundFromExternalWalletButton=Ihre externe Brieftasche zum Finanzieren öffnen -shared.openDefaultWalletFailed=Das Öffnen einer Bitcoin-Brieftasche-Standardanwendung schlug fehl. Haben Sie möglicherweise keine installiert? +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.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-Brieftasche.\nSie benötigen {0}, haben aber nur {1} in Ihrer Bisq-Brieftasche.\n\nFinanzieren Sie diesen Handel bitte von einer externen Bitcoin-Brieftasche oder finanzieren Sie Ihre Bisq-Brieftasche unter \"Gelder/Gelder erhalten\". +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.waitingForFunds=Warte auf Gelder... shared.depositTransactionId=Kautionstransaktions-ID shared.TheBTCBuyer=Der BTC-Käufer @@ -113,6 +113,7 @@ shared.reasonForPayment=Verwendungszweck shared.sendingConfirmation=Sende Bestätigung... shared.sendingConfirmationAgain=Bitte senden Sie die Bestätigung erneut shared.exportCSV=In CSV exportieren +shared.exportJSON=Exportiere als JSON shared.noDateAvailable=Kein Datum verfügbar shared.arbitratorsFee=Vergütung des Vermittlers shared.noDetailsAvailable=Keine Details vorhanden @@ -179,8 +180,8 @@ shared.yourLanguage=Ihre Sprachen shared.addLanguage=Sprache hinzufügen shared.total=Insgesamt shared.totalsNeeded=Benötigte Gelder -shared.tradeWalletAddress=Adresse der Handels-Brieftasche -shared.tradeWalletBalance=Guthaben der Handels-Brieftasche +shared.tradeWalletAddress=Adresse der Handels-Wallet +shared.tradeWalletBalance=Guthaben der Handels-Wallet shared.makerTxFee=Ersteller: {0} shared.takerTxFee=Abnehmer: {0} shared.securityDepositBox.description=Kaution für BTC {0} @@ -196,6 +197,8 @@ shared.interval=Interval shared.actions=Aktionen shared.buyerUpperCase=Käufer shared.sellerUpperCase=Verkäufer +shared.new=NEU +shared.blindVoteTxId=Geheime Wahl-Transaktion ID #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=Gesperrt mainView.footer.usingTor=(nutzt Tor) mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo=Bitcoin-Netzwerk-Peers: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Initialisiere -mainView.footer.btcInfo.synchronizedWith=synchronisiert mit -mainView.footer.btcInfo.connectingTo=verbinde mit +mainView.footer.btcInfo.initializing=Verbindung mit Bitcoin-Netzwerk wird hergestellt +mainView.footer.bsqInfo.synchronizing=/ Synchronisiere DAO +mainView.footer.btcInfo.synchronizedWith=Synchronisiert mit +mainView.footer.btcInfo.connectingTo=Verbinde mit mainView.footer.btcInfo.connectionFailed=Verbindung fehlgeschlagen mainView.footer.p2pInfo=P2P-Netzwerk-Peers: {0} @@ -299,8 +303,8 @@ market.trades.tooltip.candle.date=Datum: offerbook.createOffer=Angebot erstellen offerbook.takeOffer=Angebot annehmen -offerbook.takeOfferToBuy=Take offer to buy {0} -offerbook.takeOfferToSell=Take offer to sell {0} +offerbook.takeOfferToBuy=Angebot annehmen {0} zu kaufen +offerbook.takeOfferToSell=Angebot annehmen {0} zu verkaufen offerbook.trader=Händler offerbook.offerersBankId=Bankkennung des Erstellers (BIC/SWIFT): {0} offerbook.offerersBankName=Bankname des Erstellers: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=Betrag in BTC zu {0} createOffer.amountPriceBox.buy.volumeDescription=Auszugebender Betrag in {0} createOffer.amountPriceBox.sell.volumeDescription=Zu erhaltender Betrag in {0} createOffer.amountPriceBox.minAmountDescription=Minimaler Betrag in BTC -createOffer.securityDeposit.prompt=Kaution in BTC +createOffer.securityDeposit.prompt=Kaution createOffer.fundsBox.title=Ihr Angebot finanzieren createOffer.fundsBox.offerFee=Handelsgebühr createOffer.fundsBox.networkFee=Mining-Gebühr createOffer.fundsBox.placeOfferSpinnerInfo=Das Angebot wird veröffentlicht ... createOffer.fundsBox.paymentLabel=Bisq-Handel mit der ID {0} createOffer.fundsBox.fundsStructure=({0} Kaution, {1} Handelsgebühr, {2} Mining-Gebühr) +createOffer.fundsBox.fundsStructure.BSQ=({0} Kaution, {1} Mining-Gebühr) + {2} Handelsgebühr createOffer.success.headline=Ihr Angebot wurde veröffentlicht createOffer.success.info=Sie können Ihre offenen Angebote unter \"Portfolio/Meine offenen Angebote\" verwalten. createOffer.info.sellAtMarketPrice=Sie verkaufen immer zum aktuellen Marktpreis, da ihr Angebot ständig aktualisiert wird. @@ -383,16 +388,17 @@ createOffer.tradeFee.fiatAndPercent=≈ {0} / {1} vom Handelsbetrag # new entries createOffer.placeOfferButton=Überprüfung: Anbieten Bitcoins zu {0} -createOffer.alreadyFunded=Sie hatten das Angebot bereits finanziert.\nIhre Gelder wurden in Ihre lokale Bisq-Brieftasche verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden. +createOffer.alreadyFunded=Sie hatten das Angebot bereits finanziert.\nIhre Gelder wurden in Ihre lokale Bisq-Wallet verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden. createOffer.createOfferFundWalletInfo.headline=Ihr Angebot finanzieren # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Handelsbetrag: {0} \n -createOffer.createOfferFundWalletInfo.msg=Sie müssen zum Annehmen dieses Angebots {0} einzahlen.\n\nDiese Gelder werden in Ihrer lokalen Brieftasche reserviert und in die MultiSig-Kautionsadresse eingesperrt, wenn jemand Ihr Angebot annimmt.\n\nDer Betrag ist die Summe aus:\n{1}- Kaution: {2}\n- Handelsgebühr: {3}\n- Mining-Gebühr: {4}\n\nSie haben zwei Möglichkeiten, Ihren Handel zu finanzieren:\n- Nutzen Sie Ihre Bisq-Brieftasche (bequem, aber Transaktionen können nachverfolgbar sein) ODER\n- Von einer externen Brieftasche überweisen (möglicherweise vertraulicher)\n\nSie werden nach dem Schließen dieses Dialogs alle Finanzierungsmöglichkeiten und Details sehen. +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} und {1} +createOffer.createOfferFundWalletInfo.msg=Sie müssen zum Annehmen dieses Angebots {0} einzahlen.\n\nDiese Gelder werden in Ihrer lokalen Wallet reserviert und in die MultiSig-Kautionsadresse eingesperrt, wenn jemand Ihr Angebot annimmt.\n\nDer Betrag ist die Summe aus:\n{1}- Kaution: {2}\n- Handelsgebühr: {3}\n- Mining-Gebühr: {4}\n\nSie haben zwei Möglichkeiten, Ihren Handel zu finanzieren:\n- Nutzen Sie Ihre Bisq-Wallet (bequem, aber Transaktionen können nachverfolgbar sein) ODER\n- Von einer externen Wallet überweisen (möglicherweise vertraulicher)\n\nSie werden nach dem Schließen dieses Dialogs alle Finanzierungsmöglichkeiten und Details sehen. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) -createOffer.amountPriceBox.error.message=Es gab einen Fehler beim Erstellen des Angebots:\n\n{0}\n\nEs haben noch keine Gelder Ihre Brieftasche verlassen.\nBitte starten Sie Ihre Anwendung neu und überprüfen Sie Ihre Netzwerkverbindung. +createOffer.amountPriceBox.error.message=Es gab einen Fehler beim Erstellen des Angebots:\n\n{0}\n\nEs haben noch keine Gelder Ihre Wallet verlassen.\nBitte starten Sie Ihre Anwendung neu und überprüfen Sie Ihre Netzwerkverbindung. createOffer.setAmountPrice=Betrag und Preis festlegen -createOffer.warnCancelOffer=Sie haben das Angebot bereits finanziert.\nWenn Sie jetzt abbrechen, werden Ihre Gelder in Ihre lokale Bisq-Brieftasche verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden.\nSind Sie sicher, dass Sie abbrechen wollen? +createOffer.warnCancelOffer=Sie haben das Angebot bereits finanziert.\nWenn Sie jetzt abbrechen, werden Ihre Gelder in Ihre lokale Bisq-Wallet verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden.\nSind Sie sicher, dass Sie abbrechen wollen? createOffer.timeoutAtPublishing=Beim Veröffentlichen des Angebots ist eine Zeitüberschreitung aufgetreten. createOffer.errorInfo=\n\nDie Erstellungsgebühr wurde schon gezahlt. Im schlimmsten Fall haben Sie diese Gebühr verloren.\nVersuchen Sie bitte die Anwendung neu zu starten und überprüfen Sie Ihre Netzwerkverbindung um zu sehen, ob Sie das Problem beheben können. createOffer.tooLowSecDeposit.warning=Sie haben die Kaution auf einen niedrigeren Wert als den empfohlenen Standardwert von {0} gesetzt.\nSind Sie sicher, dass Sie eine niedrigere Kaution nutzen wollen? @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=Der eingegebene Preis liegt außerhalb der m createOffer.changePrice=Preis ändern createOffer.tac=Mit der Erstellung dieses Angebots stimme ich zu, mit jedem Händler zu handeln, der die oben festgelegten Bedingungen erfüllt. createOffer.currencyForFee=Handelsgebühr -createOffer.setDeposit=Käufers Kaution festlegen +createOffer.setDeposit=Kaution des Käufers festlegen (%) +createOffer.setDepositAsBuyer=Meine Kaution als Käufer festlegen (%) +createOffer.securityDepositInfo=Die Kaution ihres Käufers wird {0} +createOffer.securityDepositInfoAsBuyer=Ihre Kaution als Käufer wird {0} #################################################################### @@ -435,15 +444,15 @@ takeOffer.error.message=Bei der Angebotsannahme trat ein Fehler auf.\n\n{0} # new entries takeOffer.takeOfferButton=Überprüfung: Angebot annehmen Bitcoins zu {0} takeOffer.noPriceFeedAvailable=Sie können dieses Angebot nicht annehmen, da es auf einem Prozentsatz vom Marktpreis basiert, jedoch keiner verfügbar ist. -takeOffer.alreadyFunded.movedFunds=Sie haben das Angebot bereits finanziert.\nIhre Gelder wurden in Ihre lokale Bisq-Brieftasche verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden. +takeOffer.alreadyFunded.movedFunds=Sie haben das Angebot bereits finanziert.\nIhre Gelder wurden in Ihre lokale Bisq-Wallet verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden. takeOffer.takeOfferFundWalletInfo.headline=Ihren Handel finanzieren # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Handelsbetrag: {0}\n -takeOffer.takeOfferFundWalletInfo.msg=Sie müssen zum Annehmen dieses Angebots {0} einzahlen.\n\nDer Betrag ist die Summe aus:\n{1}- Ihre Kaution: {2}\n- Handelsgebühr: {3}\n- Gesamte Mining-Gebühr: {4}\n\nSie haben zwei Möglichkeiten Ihren Handel zu finanzieren:\n- Nutzen Sie Ihre Bisq-Brieftasche (bequem, aber Transaktionen können nach verfolgbar sein) ODER\n- Von einer externen Brieftasche überweisen (möglicherweise vertraulicher)\n\nSie werden nach dem Schließen dieses Dialogs alle Finanzierungsmöglichkeiten und Details sehen. +takeOffer.takeOfferFundWalletInfo.msg=Sie müssen zum Annehmen dieses Angebots {0} einzahlen.\n\nDer Betrag ist die Summe aus:\n{1}- Ihre Kaution: {2}\n- Handelsgebühr: {3}\n- Gesamte Mining-Gebühr: {4}\n\nSie haben zwei Möglichkeiten Ihren Handel zu finanzieren:\n- Nutzen Sie Ihre Bisq-Wallet (bequem, aber Transaktionen können nach verfolgbar sein) ODER\n- Von einer externen Wallet überweisen (möglicherweise vertraulicher)\n\nSie werden nach dem Schließen dieses Dialogs alle Finanzierungsmöglichkeiten und Details sehen. takeOffer.alreadyPaidInFunds=Wenn Sie bereits Gelder gezahlt haben, können Sie diese unter \"Gelder/Gelder senden\" abheben. takeOffer.paymentInfo=Zahlungsinformationen takeOffer.setAmountPrice=Betrag festlegen -takeOffer.alreadyFunded.askCancel=Sie haben das Angebot bereits finanziert.\nWenn Sie jetzt abbrechen, werden Ihre Gelder in Ihre lokale Bisq-Brieftasche verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden.\nSind Sie sicher, dass Sie abbrechen wollen? +takeOffer.alreadyFunded.askCancel=Sie haben das Angebot bereits finanziert.\nWenn Sie jetzt abbrechen, werden Ihre Gelder in Ihre lokale Bisq-Wallet verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden.\nSind Sie sicher, dass Sie abbrechen wollen? takeOffer.failed.offerNotAvailable=Die Annahme des Angebots ist fehlgeschlagen, da das Angebot nicht mehr verfügbar ist. Möglicherweise hat zwischenzeitlich ein anderer Händler das Angebot angenommen. takeOffer.failed.offerTaken=Sie können dieses Angebot nicht annehmen, da es bereits von einem anderen Händler angenommen wurde. takeOffer.failed.offerRemoved=Sie können dieses Angebot nicht annehmen, da es inzwischen entfernt wurde. @@ -451,7 +460,7 @@ takeOffer.failed.offererNotOnline=Die Angebotsannahme ist fehlgeschlagen, da der takeOffer.failed.offererOffline=Sie können das Angebot nicht annehmen, da der Ersteller offline ist. takeOffer.warning.connectionToPeerLost=Sie haben die Verbindung zum Ersteller verloren.\nEr könnte offline gegangen sein oder die Verbindung zu Ihnen aufgrund zu vieler Verbindungen geschlossen haben.\n\nFalls Sie das Angebot noch im Angebotsbuch sehen, können Sie versuchen das Angebot erneut anzunehmen. -takeOffer.error.noFundsLost=\n\nEs haben noch keine Gelder Ihre Brieftasche verlassen.\nVersuchen Sie bitte Ihre Anwendung neu zu starten und überprüfen Sie Ihre Netzwerkverbindung, um zu sehen ob Sie das Problem beheben können. +takeOffer.error.noFundsLost=\n\nEs haben noch keine Gelder Ihre Wallet verlassen.\nVersuchen Sie bitte Ihre Anwendung neu zu starten und überprüfen Sie Ihre Netzwerkverbindung, um zu sehen ob Sie das Problem beheben können. takeOffer.error.feePaid=\n\nDie Abnehmergebühr wurde schon gezahlt. Im schlimmsten Fall haben Sie diese Gebühr verloren.\nVersuchen Sie bitte die Anwendung neu zu starten und überprüfen Sie Ihre Netzwerkverbindung, um zu sehen ob Sie das Problem beheben können. takeOffer.error.depositPublished=\n\nDie Kautionstransaktion wurde schon veröffentlicht.\nVersuchen Sie bitte Ihre Anwendung neu zu starten und überprüfen Sie Ihre Netzwerkverbindung, um zu sehen ob Sie das Problem beheben können.\nWenn das Problem weiter besteht, kontaktieren Sie bitte die Entwickler für Support. takeOffer.error.payoutPublished=\n\nDie Auszahlungstransaktion wurde schon veröffentlicht.\nVersuchen Sie bitte Ihre Anwendung neu zu starten und überprüfen Sie Ihre Netzwerkverbindung, um zu sehen ob Sie das Problem beheben können.\nWenn das Problem weiter besteht, kontaktieren Sie bitte die Entwickler für Support. @@ -486,8 +495,8 @@ portfolio.pending.step3_seller.confirmPaymentReceived=Zahlungseingang bestätige portfolio.pending.step5.completed=Abgeschlossen 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 wurde noch nicht bestätigt.\nDies kann in seltenen Fällen passieren, wenn die Finanzierungsgebühr von der externen Brieftasche eines Händlers zu niedrig war. -portfolio.pending.step1.openForDispute=Die Kautionstransaktion wurde noch nicht bestätigt.\nDies kann in seltenen Fällen passieren, wenn die Finanzierungsgebühr, von der externen Brieftasche eines Händlers, zu niedrig war.\nDie maximale Dauer des Handels wurde überschritten.\n\nBitte kontaktieren Sie den Vermittler, um einen Konflikt zu öffnen. +portfolio.pending.step1.warn=Die Kautionstransaktion wurde noch nicht bestätigt.\nDies kann in seltenen Fällen passieren, wenn die Finanzierungsgebühr von der externen Wallet eines Händlers zu niedrig war. +portfolio.pending.step1.openForDispute=Die Kautionstransaktion wurde noch nicht bestätigt.\nDies kann in seltenen Fällen passieren, wenn die Finanzierungsgebühr, von der externen Wallet eines Händlers, zu niedrig war.\nDie maximale Dauer des Handels wurde überschritten.\n\nBitte kontaktieren Sie den Vermittler, um einen Konflikt zu öffnen. # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2.confReached=Ihr Handel hat wenigstens eine Blockchain-Bestätigung erreicht.\n(Falls Sie möchten können Sie weitere Bestätigungen abwarten. 6 Bestätigungen werden als sehr sicher angesehen.)\n\n @@ -501,7 +510,7 @@ portfolio.pending.step2_buyer.tradeId=Vergessen Sie bitte nicht, die Handels-ID portfolio.pending.step2_buyer.assign=als \"Verwendungszweck\", damit der Empfänger Ihre Zahlung diesem Handel zuordnen kann.\n\n portfolio.pending.step2_buyer.fees=Sollte Ihre Bank Gebühren erheben, müssen Sie diese decken. # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step2_buyer.altcoin=Bitte überweisen Sie von Ihrer externen {0}-Brieftasche\n{1} an den BTC-Verkäufer.\n\n +portfolio.pending.step2_buyer.altcoin=Bitte überweisen Sie von Ihrer externen {0}-Wallet\n{1} an den BTC-Verkäufer.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.cash=Bitte gehen Sie zu einer Bank und zahlen Sie {0} an den BTC-Verkäufer.\n\n portfolio.pending.step2_buyer.cash.extra=WICHTIGE VORAUSSETZUNG:\nNachdem Sie die Zahlung getätigt haben, schreiben Sie auf die Quittung: NO REFUNDS.\nReißen Sie diese in zwei Teile und machen Sie ein Foto, das Sie an die E-Mail-Adresse des BTC-Verkäufers senden. @@ -564,7 +573,7 @@ portfolio.pending.step3_buyer.openForDispute=Der BTC-Verkäufer hat Ihre Zahlung # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.part=Ihr Handelspartner hat bestätigt, die {0}-Zahlung begonnen zu haben.\n\n portfolio.pending.step3_seller.altcoin.explorer=in ihrem bevorzugten {0} Blockchain Explorer -portfolio.pending.step3_seller.altcoin.wallet=in ihrer {0} Brieftasche +portfolio.pending.step3_seller.altcoin.wallet=in ihrer {0} Wallet portfolio.pending.step3_seller.altcoin={0}Bitte überprüfen Sie mit Ihrem bevorzugten {1}-Blockchain-Explorer, ob die Transaktion zu Ihrer Empfangsadresse\n{2}\nschon genug Blockchain-Bestätigungen hat.\nDer Zahlungsbetrag muss {3} sein\n\nSie können Ihre {4}-Adresse vom Hauptbildschirm kopieren und woanders einfügen, nachdem dieser Dialog geschlossen wurde. portfolio.pending.step3_seller.postal={0}Bitte überprüfen Sie, ob Sie {1} per \"US Postal Money Order\" vom BTC-Käufer erhalten haben.\n\nDie Handels-ID (\"Verwendungszweck\") der Transaktion ist: \"{2}\" portfolio.pending.step3_seller.bank=Ihr Handelspartner hat den Beginn der {0}-Zahlung bestätigt.\n\nBitte gehen Sie auf Ihre Online-Banking-Website und überprüfen Sie, ob Sie {1} vom BTC-Käufer erhalten haben.\n\nDie Handels-ID (\"Verwendungszweck\") der Transaktion ist: \"{2}\"\n\n @@ -583,7 +592,7 @@ portfolio.pending.step3_seller.yourAccount=Ihr Handelskonto portfolio.pending.step3_seller.buyersAccount=Handelskonto des Käufers 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-Brieftasche oder Ihren Block-Explorer auf Blockchain-Bestätigungen und bestätigen Sie die Zahlung, wenn ausreichend viele Blockchain-Bestätigungen angezeigt werden. +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. portfolio.pending.step3_seller.buyerStartedPayment.fiat=Prüfen Sie Ihr Handelskonto (z.B. Bankkonto) und bestätigen Sie, wenn Sie die Zahlung erhalten haben. portfolio.pending.step3_seller.warn.part1a=in der {0}-Blockchain portfolio.pending.step3_seller.warn.part1b=bei Ihrem Zahlungsanbieter (z.B. Bank) @@ -607,8 +616,8 @@ portfolio.pending.step5_buyer.refunded=Rückerstattete Kaution portfolio.pending.step5_buyer.withdrawBTC=Ihre Bitcoins abheben portfolio.pending.step5_buyer.amount=Abzuhebender Betrag portfolio.pending.step5_buyer.withdrawToAddress=An diese Adresse abheben -portfolio.pending.step5_buyer.moveToBisqWallet=Gelder in Bisq-Brieftasche bewegen -portfolio.pending.step5_buyer.withdrawExternal=An externe Brieftasche abheben +portfolio.pending.step5_buyer.moveToBisqWallet=Gelder in Bisq-Wallet bewegen +portfolio.pending.step5_buyer.withdrawExternal=An externe Wallet abheben portfolio.pending.step5_buyer.alreadyWithdrawn=Ihre Gelder wurden bereits abgehoben.\nBitte überprüfen Sie den Transaktionsverlauf. portfolio.pending.step5_buyer.confirmWithdrawal=Anfrage zum Abheben bestätigen portfolio.pending.step5_buyer.amountTooLow=Der zu überweisende Betrag ist kleiner als die Transaktionsgebühr und der minimale Tx-Wert (Staub). @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=Wir würden gerne von Ihren Erfahrungen mit Bisq h tradeFeedbackWindow.msg.part2=Sollten Sie Fragen oder Probleme haben, kontaktieren Sie andere Nutzer und Mitwirkende im Bisq-Forum auf: tradeFeedbackWindow.msg.part3=Vielen Dank, dass Sie Bisq benutzen! + +daoTestingFeedbackWindow.title=Vielen Dank, dass Sie den Bisq DAO testen +daoTestingFeedbackWindow.msg.part1=Können Sie 3 Minuten für eine kurze Umfrage entbehren? Wir bieten 20 BSQ für eine abgeschlossene Umfrage.\nIhr Feedback ist für einen sanften Start das Hauptnetzes entscheidend. +daoTestingFeedbackWindow.surveyLinkLabel=Umfrage +daoTestingFeedbackWindow.msg.part2=Fragen oder andere Probleme? Diskutieren Sie mit anderen Bisq Nutzern und Mitarbeitern im Forum: +daoTestingFeedbackWindow.forumLinkLabel=Forum besuchen +daoTestingFeedbackWindow.msg.part3=Vielen Dank, dass Sie Bisq benutzen! + portfolio.pending.role=Meine Rolle portfolio.pending.tradeInformation=Handelsinformationen portfolio.pending.remainingTime=Verbleibende Zeit @@ -669,10 +686,10 @@ funds.tab.transactions=Transaktionen funds.deposit.unused=Ungenutzt funds.deposit.usedInTx=In {0} Transaktion(en) genutzt -funds.deposit.fundBisqWallet=Bisq-Brieftasche finanzieren +funds.deposit.fundBisqWallet=Bisq-Wallet finanzieren funds.deposit.noAddresses=Es wurden noch keine Kautionsadressen generiert -funds.deposit.fundWallet=Ihre Brieftasche finanzieren -funds.deposit.withdrawFromWallet=Gelder aus Brieftasche übertragen +funds.deposit.fundWallet=Ihre Wallet finanzieren +funds.deposit.withdrawFromWallet=Gelder aus Wallet übertragen funds.deposit.amount=Betrag in BTC (optional) funds.deposit.generateAddress=Neue Adresse generieren funds.deposit.selectUnused=Bitte wählen Sie eine ungenutzte Adresse aus der Tabelle oben, anstatt eine neue zu generieren. @@ -692,7 +709,7 @@ funds.withdrawal.noFundsAvailable=Keine Gelder zum Abheben verfügbar funds.withdrawal.confirmWithdrawalRequest=Anfrage zum Abheben bestätigen funds.withdrawal.withdrawMultipleAddresses=Von mehreren Adressen abheben ({0}) funds.withdrawal.withdrawMultipleAddresses.tooltip=Von mehreren Adressen abheben:\n{0} -funds.withdrawal.notEnoughFunds=Sie haben nicht genug Geld in Ihrer Brieftasche. +funds.withdrawal.notEnoughFunds=Sie haben nicht genug Geld in Ihrer Wallet. funds.withdrawal.selectAddress=Wählen Sie eine Quelladresse aus der Tabelle funds.withdrawal.setAmount=Legen Sie den abzuhebenen Betrag fest funds.withdrawal.fillDestAddress=Geben Sie Ihre Zieladresse an @@ -700,7 +717,7 @@ funds.withdrawal.warn.noSourceAddressSelected=Sie müssen eine Quelladresse aus funds.withdrawal.warn.amountExceeds=Ihre Gelder in den ausgewählten Adressen reichen nicht aus.\nWählen Sie mehrere Adressen aus der Tabelle oben, oder ändern Sie die Gebühren-Schalter, um die Mining-Gebühr zu beinhalten. funds.reserved.noFunds=Es sind keine Gelder in offenen Angeboten reserviert -funds.reserved.reserved=In lokaler Brieftasche für das Angebot mit dieser ID reserviert: {0} +funds.reserved.reserved=In lokaler Wallet für das Angebot mit dieser ID reserviert: {0} funds.locked.noFunds=Es sind keine Gelder in Händel eingesperrt funds.locked.locked=Für den Handel mit dieser ID in MultiSig eingesperrt: {0} @@ -718,10 +735,10 @@ funds.tx.disputeLost=Verlorener Konflikt: {0} funds.tx.unknown=Unbekannter Grund: {0} funds.tx.noFundsFromDispute=Keine Rückzahlung vom Konflikt funds.tx.receivedFunds=Gelder erhalten -funds.tx.withdrawnFromWallet=Von Brieftasche abgehoben +funds.tx.withdrawnFromWallet=Von Wallet abgehoben funds.tx.noTxAvailable=Keine Transaktionen verfügbar funds.tx.revert=Umkehren -funds.tx.txSent=Transaktion erfolgreich zu einer neuen Adresse in der lokalen Bisq-Brieftasche gesendet. +funds.tx.txSent=Transaktion erfolgreich zu einer neuen Adresse in der lokalen Bisq-Wallet gesendet. funds.tx.direction.self=An Sie selbst senden funds.tx.daoTxFee=Mining-Gebühr für DAO Tx funds.tx.reimbursementRequestTxFee=Rückerstattungsantrag @@ -769,11 +786,11 @@ support.buyerTaker=BTC-Käufer/Abnehmer support.sellerTaker=BTC-Verkäufer/Abnehmer support.backgroundInfo=Bisq ist keine Firma und betreibt keine Form von Kundendienst.\n\nFalls es während des Handelsprozess (z.B. ein Händler befolgt nicht das Handelsprotokoll) zu Konflikten kommen sollte, wird die Anwendung nach der Handelsdauer eine \"Konflikt öffnen\"-Schaltfläche anzeigen, um den Vermittler zu kontaktieren.\n\nIm Falle von Softwarefehlern oder anderen Problemen, die von der Anwendung entdeckt werden, wird eine \"Support-Ticket öffnen\" Schaltfläche angezeigt, um den Vermittler zu kontaktieren, der die Probleme an die Entwickler weiterleitet.\n\nFalls Sie ein Problem haben, aber die \"Support-Ticket öffnen\"-Schaltfläche nicht angezeigt wird, können Sie manuell ein Support-Ticket öffnen indem Sie den Probleme bereitende Handel unter \"Mappe/Offene Händel\" wählen und die Tastenkombination \"alt + o\" oder \"option + o\" drücken.. Bitte nutzen Sie diese nur, wenn Sie sicher sind, dass sich die Software nicht wie erwartet verhält. Falls Sie nicht wissen wie man Bisq verwendet oder andere Fragen haben, überprüfen Sie bitte die FAQ auf der bisq.io-Website oder erstellen Sie einen Post im Support-Abschnitt des Bisq-Forums. -support.initialInfo=Bitte beachten Sie die grundlegenden Regeln für den Konfliktprozess:\n1. Sie müssen innerhalb von zwei Tagen auf die Anfrage des Vermittlers reagieren.\n2. Die maximale Dauer des Konflikts ist 14 Tage.\n3. Sie müssen befolgen, was der Vermittler von Ihnen verlangt, um Beweise für ihren Fall zu liefern.\n4. Sie haben beim ersten Start dieser Anwendung die Regeln akzeptiert, die im Wiki in den Nutzungsbedingungen zusammengefasst wurden.\n\nErfahren Sie mehr über den Konfliktprozess in unserem Wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Bitte geben Sie eine Beschreibung Ihres Problems im Textfeld unterhalb ein. Fügen Sie so viele Informationen wie möglich ein, damit der Konflikt schnell gelöst wird.\n\nHier eine Liste an Informationen, die Sie bereitstellen sollten:\n\t● Falls Sie der BTC Käufer sind: Haben Sie den Fiat- oder Altcoin-Transfer durchgeführt? Falls ja, haben Sie den 'Zahlung begonnen' Knopf in der Anwendung geklickt?\n\t● Falls Sie der BTC Verkäufer sind: Haben Sie die Fiat- oder Altcoin-Zahlung erhalten? Falls ja, haben Sie den 'Zahlung erhalten' Knopf in der Anwendung geklickt?\n\t● Welche Version von Bisq nutzen Sie?\n\t● Welches Betriebsystem nutzen Sie?\n\t● Falls Sie Probleme mit einer fehlgeschlagenen Transaktion haben, sollten Sie in ein neues Datenverzeichnis wechseln.\n\t Manchmal wird das Datenverzeichnis beschädigt, was zu seltsamen Fehlern führt. \n\t Beachte: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nBitte machen Sie sich mit den Grundregeln des Konfliktprozesses vertraut:\n\t● Sie müssen innerhalb von 2 Tagen auf Anfragen vom Vermittler reagieren.\n\t● Die maximale Dauer eines Konflikts sind 14 Tage.\n\t● Sie müssen mit dem Vermittler kooperieren und die angefragten Informationen bereitstellen um Ihren Fall darzustellen.\n\t● Sie haben die Regeln das Konfliktdokuments in den Nutzungsbedingungen akzeptiert, als Sie die Anwendung das erste Mal gestartet haben.\n\nSie können mehr über den Konfliktprozess erfahren, auf: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=Systemnachricht: {0} -support.youOpenedTicket=Sie haben eine Anfrage für Support geöffnet. -support.youOpenedDispute=Sie haben eine Anfrage für einen Konflikt geöffnet.\n\n{0} -support.peerOpenedTicket=Ihr Handelspartner hat aufgrund technischer Probleme Support angefragt. Bitte warten Sie auf weitere Anweisungen. +support.youOpenedTicket=Sie haben eine Anfrage auf Support geöffnet.\n\n{0}\n\nBisq-Version: {1} +support.youOpenedDispute=Sie haben eine Anfrage für einen Konflikt geöffnet.\n\n{0}\n\nBisq-version: {1} +support.peerOpenedTicket=Ihr Handelspartner hat aufgrund technischer Probleme Support angefragt.\n\n{0} support.peerOpenedDispute=Ihr Handelspartner hat einen Konflikt angefragt.\n\n{0} @@ -835,10 +852,10 @@ settings.net.bitcoinNodesLabel=Mit Bitcoin-Core-Knoten verbinden settings.net.useProvidedNodesRadio=Bereitgestellte Bitcoin-Core-Knoten verwenden settings.net.usePublicNodesRadio=Öffentliches Bitcoin-Netzwerk benutzen settings.net.useCustomNodesRadio=Spezifische Bitcoin-Core-Knoten verwenden -settings.net.warn.usePublicNodes=Falls Sie das öffentliche Bitcoin-Netzwerk verwenden sind Sie einem schwerwiegenden Datenschutzproblem ausgesetzt durch das fehlerhafte Design und Implementierung des Bloom Filters in SPV-Brieftaschen wie BitcoinJ (verwendet in Bisq). Jeder verbundene Bitcoin-Core-Knoten könnte herausfinden, dass alle Ihre Brieftaschen-Adressen einer Person gehören.\n\nBitte informieren Sie sich dazu auf folgender Seite: https://bisq.network/blog/privacy-in-bitsquare.\n\nSind Sie sicher, dass Sie öffentliche Knoten verwenden möchten? +settings.net.warn.usePublicNodes=Falls Sie das öffentliche Bitcoin-Netzwerk verwenden sind Sie einem schwerwiegenden Datenschutzproblem ausgesetzt durch das fehlerhafte Design und Implementierung des Bloom Filters in SPV-Wallets wie BitcoinJ (verwendet in Bisq). Jeder verbundene Bitcoin-Core-Knoten könnte herausfinden, dass alle Ihre Wallets-Adressen einer Person gehören.\n\nBitte informieren Sie sich dazu auf folgender Seite: https://bisq.network/blog/privacy-in-bitsquare.\n\nSind Sie sicher, dass Sie öffentliche Knoten verwenden möchten? settings.net.warn.usePublicNodes.useProvided=Nein, bereitgestellte Knoten verwenden settings.net.warn.usePublicNodes.usePublic=Ja, öffentliches Netzwerk verwenden -settings.net.warn.useCustomNodes.B2XWarning=Bitte stellen Sie sicher, dass Sie sich mit einem vertrauenswürdigen Bitcoin-Core-Knoten verbinden!\n\nWenn Sie sich mit Knoten verbinden, die gegen die Bitcoin Core Konsensus-Regeln verstoßen, kann es zu Problemen in Ihrer Brieftasche und im Verlauf des Handelsprozesses kommen.\n\nBenutzer die sich zu oben genannten Knoten verbinden, sind für den verursachten Schaden verantwortlich. Dadurch entstandene Konflikte werden zugunsten des anderen Teilnehmers entschieden. Benutzer die unsere Warnungen und Sicherheitsmechanismen ignorieren wird keine technische Unterstützung geleistet! +settings.net.warn.useCustomNodes.B2XWarning=Bitte stellen Sie sicher, dass Sie sich mit einem vertrauenswürdigen Bitcoin-Core-Knoten verbinden!\n\nWenn Sie sich mit Knoten verbinden, die gegen die Bitcoin Core Konsensus-Regeln verstoßen, kann es zu Problemen in Ihrer Wallet und im Verlauf des Handelsprozesses kommen.\n\nBenutzer die sich zu oben genannten Knoten verbinden, sind für den verursachten Schaden verantwortlich. Dadurch entstandene Konflikte werden zugunsten des anderen Teilnehmers entschieden. Benutzer die unsere Warnungen und Sicherheitsmechanismen ignorieren wird keine technische Unterstützung geleistet! settings.net.localhostBtcNodeInfo=(Hintergrundinformation: Falls Sie einen lokalen Bitcoin-Knoten einsetzen (localhost) werden Sie nur mit diesem verbunden.) settings.net.p2PPeersLabel=Verbundene Peers settings.net.onionAddressColumn=Onion-Adresse @@ -867,7 +884,7 @@ settings.net.reSyncSPVAfterRestart=Die SPV-Kettendatei wurde gelöscht. Haben Si settings.net.reSyncSPVAfterRestartCompleted=Die erneute Synchronisation ist jetzt abgeschlossen. Bitte starten Sie die Anwendung neu. settings.net.reSyncSPVFailed=Konnte SPV-Kettendatei nicht löschen.\nFehler: {0} setting.about.aboutBisq=Über Bisq -setting.about.about=Bisq ist ein Open-Source-Projekt und ein dezentrales Netzwerk von Nutzern, die Bitcoins gegen nationale Währungen oder alternative Cryptowährungen auf einem Weg tauschen wollen, der die Privatsphäre schützt. Lernen Sie auf unser Projektwebsite mehr über Bisq. +setting.about.about=Bisq ist Open-Source Software, die den Tausch von Bitcoin mit nationaler Währung (und anderen Kryptowährungen), durch ein dezentralisiertes Peer-zu-Peer Netzwerk auf eine Weise ermöglicht, die Ihre Privatsphäre stark beschützt. Lernen Sie auf unserer Projektwebseite mehr über Bisq. setting.about.web=Bisq-Website setting.about.code=Quellcode setting.about.agpl=AGPL-Lizenz @@ -876,8 +893,8 @@ setting.about.def=Bisq ist keine Firma, sondern ein Gemeinschaftsprojekt, das of setting.about.contribute=Mitwirken setting.about.donate=Spenden setting.about.providers=Datenanbieter -setting.about.apisWithFee=Bisq nutzt für Fiatgeld- und Altcoin-Marktpreise sowie geschätzte Mining-Gebühren die APIs Dritter. -setting.about.apis=Bisq nutzt für Fiatgeld- und Altcoin-Marktpreise die APIs Dritter. +setting.about.apisWithFee=Bisq nutzt für Fiatgeld- und Altcoin-Marktpreise sowie geschätzte Mining-Gebühren die APIs 3tr. +setting.about.apis=Bisq nutzt für Fiatgeld- und Altcoin-Marktpreise die APIs 3tr. setting.about.pricesProvided=Marktpreise zur Verfügung gestellt von setting.about.pricesProviders={0}, {1} und {2} setting.about.feeEstimation.label=Geschätzte Mining-Gebühr bereitgestellt von @@ -898,8 +915,8 @@ account.info.msg=Hier können Sie Handelskonten für nationale Währungen & Altc account.menu.paymentAccount=Nationale Währungskonten account.menu.altCoinsAccountView=Altcoin-Konten -account.menu.password=Brieftasche-Passwort -account.menu.seedWords=Brieftasche-Seed +account.menu.password=Wallet-Passwort +account.menu.seedWords=Wallet-Seed account.menu.backup=Backup account.menu.notifications=Benachrichtigungen @@ -908,13 +925,13 @@ account.arbitratorRegistration.pubKey=Öffentlicher Schlüssel account.arbitratorRegistration.register=Vermittler registrieren account.arbitratorRegistration.revoke=Registrierung widerrufen account.arbitratorRegistration.info.msg=Beachten Sie bitte, dass Sie nach dem Widerrufen für 15 Tage verfügbar bleiben müssen, da es Händel geben kann, die Sie als Vermittler nutzen. Die maximal erlaubte Handelsdauer ist 8 Tage und der Konfliktprozess kann bis zu 7 Tage dauern. -account.arbitratorRegistration.warn.min1Language=Sie müssen wenigstens eine Sprache festlegen.\nWir haben Ihre Standardsprache für Sie hinzugefügt. +account.arbitratorRegistration.warn.min1Language=Sie müssen wenigstens 1 Sprache festlegen.\nWir haben Ihre Standardsprache für Sie hinzugefügt. account.arbitratorRegistration.removedSuccess=Sie haben Ihren Vermittler erfolgreich aus dem P2P-Netzwerk entfernt. account.arbitratorRegistration.removedFailed=Der Vermittler konnte nicht entfernt werden.{0} account.arbitratorRegistration.registerSuccess=Sie haben Ihren Vermittler erfolgreich im P2P-Netzwerk registriert. account.arbitratorRegistration.registerFailed=Vermittler konnte nicht registriert werden.{0} -account.arbitratorSelection.minOneArbitratorRequired=Sie müssen wenigstens eine Sprache festlegen.\nWir haben Ihre Standardsprache für Sie hinzugefügt. +account.arbitratorSelection.minOneArbitratorRequired=Sie müssen wenigstens 1 Sprache festlegen.\nWir haben Ihre Standardsprache für Sie hinzugefügt. account.arbitratorSelection.whichLanguages=Welche Sprachen sprechen Sie? account.arbitratorSelection.whichDoYouAccept=Welche Vermittler akzeptieren Sie account.arbitratorSelection.autoSelect=Alle Vermittler mit passender Sprache automatisch wählen @@ -922,24 +939,26 @@ account.arbitratorSelection.regDate=Registrierungsdatum account.arbitratorSelection.languages=Sprachen account.arbitratorSelection.cannotSelectHimself=Ein Vermittler kann sich nicht selbst zum Handeln wählen. account.arbitratorSelection.noMatchingLang=Keine passende Sprache gefunden. -account.arbitratorSelection.noLang=Sie können nur Vermittler wählen, die wenigstens eine gemeinsame Sprache sprechen. +account.arbitratorSelection.noLang=Sie können nur Vermittler wählen, die wenigstens 1 gemeinsame Sprache sprechen. account.arbitratorSelection.minOne=Sie müssen wenigstens einen Vermittler auswählen. account.altcoin.yourAltcoinAccounts=Ihre Altcoin-Konten -account.altcoin.popup.wallet.msg=Bitte stellen Sie sicher, dass Sie die Anforderungen für die Nutzung von {0}-Brieftaschen befolgen, die auf der {1}-Website beschrieben werden.\nDie Verwendung von Brieftaschen zentraler Börsen, bei denen Sie (a) Ihre Schlüssel nicht selber verwalten, oder (b) das Nutzen inkompatibler Brieftasche-Software können zum Verlust der gehandelten Gelder führen!\nDer Vermittler ist kein {2}-Spezialist und kann Ihnen in einem solchen Fall nicht helfen. -account.altcoin.popup.wallet.confirm=Ich verstehe und bestätige, dass ich weiß, welche Brieftasche ich benutzen muss. -account.altcoin.popup.xmr.msg=Wenn Sie XMR auf Bisq handeln wollen, stellen Sie bitte sicher, dass Sie die folgenden Bedingungen verstehen und erfüllen:\n\nUm XMR zu senden, brauchen Sie entweder die offizielle Monero GUI Wallet oder die einfache Monero CLI Wallet mit aktivierter store-tx-info (Standard in neuen Versionen).\nStellen Sie bitte sicher, dass Sie auf den Tx-Schlüssel zugreifen können.\nmonero-wallet-cli: (nutzen Sie das get_tx_key Kommando)\nmonero-wallet-gui: (gehen sie zum Verlauf Tab und klicken Sie die (P) Schaltfläche um die Zahlungsbestätigung anzuzeigen)\n\nZusätzlich zum XMR checktx Werkzeug (https://xmr.llcoins.net/checktx.html) kann die Überprüfung auch innerhalb der Brieftasche durchgeführt werden. \nmonero-wallet-cli: verwenden Sie den Befehl (check_tx_key)\nmonero-wallet-gui: gehen Sie zur Erweitert > Beweisen/Prüfen-Seite\n\nIn normalen Blockexplorern ist die Übertragung nicht überprüfbar.\n\nIm Fall eines Konflikts müssen Sie dem Vermittler folgende Daten übergeben:\n- Den privaten Tx-Schlüssel\n- Den Transaktionshash\n- Die öffentliche Adresse des Empfängers\n\nSollten Sie die Daten nicht übergeben können oder eine inkompatible Wallet verwendet haben, werden Sie den Konflikt verlieren. Der XMR Sender ist in der Verantwortung die Übertragung der XMR gegenüber dem Vermittler im Falle eines Konflikts zu beweisen.\n\nEs wird keine Zahlungskennung benötigt, nur eine normale öffentliche Adresse.\n\nFalls Sie sich über diesen Prozess im Unklaren sind, besuchen Sie (https://www.getmonero.org/resources/user-guides/prove-payment.html) oder das Moneroforum (https://forum.getmonero.org) um weitere Informationen zu finden. -account.altcoin.popup.blur.msg=Stellen Sie sicher, die folgenden Bedingungen verstanden zu haben, falls Sie BLUR auf Bisq handeln möchten:\n\nUm BLUR zu senden, müssen sie die Blur CLI Brieftasche (blur-wallte-cli) nutzen.\n\nNachdem dem Senden der Zahlung, wird die Brieftasche den Transaktions-Hash (tx ID). Diese müssen Sie speichern. Sie müssen auch das 'get_tx_key' Kommando ausführen um den privaten Transaktionschlüssel zu erhalten. Sie müssen beides im Falle eines Konflikts haben.\nIn diesem Fall müssen Sie beides mit der öffentlichen Adresse des Empfängers dem Vermittler geben,\nder dann die BLUR Übertragung mi dem Blur Transaction Viewer (https://blur.cash/#tx-viewer) bestätigt.\n\nSollten Sie die benötigten Daten nicht bereitstellen, verlieren Sie den Konflikt.\nDer BLUR Sender ist verantwortlich den BLUR Transfer dem Vermittler, im Falle eines Konflikts, zu beweisen.\n\nSollten Sie diese Bedingungen nicht verstehen, suchen Sie Hilfe im Blur Network Discord (https://discord.gg/dMWaqVW). -account.altcoin.popup.ccx.msg=Stellen Sie sicher, die folgenden Bedingungen verstanden zu haben, falls Sie CCX auf Bisq handeln möchten:\n\nUm CCX zu senden, müssen sie eine offizielle Conceal Brieftasche nutzen, Cli oder GUI. Nachdem dem Senden\nder Zahlung, wird die Brieftasche den Transaktions-Hash (ID) und geheimen Schlüssel anzeigen. Sie müssen beides im Falle eines Konflikts haben.\nIn diesem Fall müssen Sie beides mit der öffentlichen Adresse des Empfängers dem Vermittler geben,\nder dann die CCX Übertragung mi dem Conceal Transaction Viewer (https://explorer.conceal.network/txviewer) bestätigt.\nWeil Conceal ein Privatsphären Coin ist, kann ein Blockforscher den Transfer nicht bestätigen.\n\nSollten Sie die benötigten Daten nicht bereitstellen können, verlieren Sie den Konflikt.\nWenn Sie den geheimen Schlüssel nicht sofort nach der CCX Transaktion speichern, kann dieser nicht wiederhergestellt werden.\nSollten Sie diese Bedingungen nicht verstehen, suchen Sie Hilfe im Conceal Discord (http://discord.conceal.network). -account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides a transaction is not verifyable on the public blockchain. If required you can prove your payment thru use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at \ (http://drgl.info/#check_txn).\n\nIf you cannot provide the above data or if you used an incompatible wallet it would result in losing the dispute case. The Dragonglass sender is responsible to be able to verify the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=Wenn Sie {0} verwenden, können Sie nur die transparenten Adressen verwenden (diese beginnen mit t), nicht die z-Adressen (privat), da der Vermittler die Transaktionen mit z-Adressen nicht überprüfen könnte. -account.altcoin.popup.XZC.msg=Wenn Sie {0} verwenden, können Sie nur die transparenten (verfolgbaren) Adressen verwenden, nicht die unverfolgbaren, da der Vermittler Transaktionen mit unverfolgbaren Adressen in einem Blockforscher nicht überprüfen könnte. -account.altcoin.popup.bch=Bitcoin Cash und Bitcoin Clashic leiden unter Replay-Protection. Wenn Sie diese Coins benutzen, stellen Sie sicher, dass Sie ausreichend vorsorgen und die Konsequenzen verstehen. Sie können Verluste erleiden, wenn Sie einen Coin senden und ungewollt die gleichen Gelder in einer anderen Blockkette senden. Weil diese "airdrop coins" den gleichen Verlauf wie die Bitcoin Blockkette haben, gibt es auch Sicherheitsrisiken und beachtliche Risiken für die Privatsphären.\n\nLesen Sie bitte mehr über das Thema im Bisq-Forum: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Weil Bitcoin Gold den Verlauf wie die Bitcoin Blockkette hat, kommt damit ein gewissen Sicherheitsrisiken und beachtlichen Risiken für die Privatsphäre. Wenn Sie Bitcoin Gold benutzen, stellen Sie sicher, dass Sie ausreichend vorsorgen und die Konsequenzen verstehen.\n\n\nLesen Sie bitte mehr über das Thema im Bisq-Forum: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.wallet.msg=Bitte stellen Sie sicher, dass Sie die Anforderungen für die Nutzung von {0}-Wallets befolgen, die auf der {1}-Website beschrieben werden.\nDie Verwendung von Wallets zentraler Börsen, bei denen Sie (a) Ihre Schlüssel nicht selber verwalten, oder (b) das Nutzen inkompatibler Wallet-Software können zum Verlust der gehandelten Gelder führen!\nDer Vermittler ist kein {2}-Spezialist und kann Ihnen in einem solchen Fall nicht helfen. +account.altcoin.popup.wallet.confirm=Ich verstehe und bestätige, dass ich weiß, welche Wallet ich benutzen muss. +account.altcoin.popup.arq.msg=ARQ auf Bisq zu Handel verlangt, dass Sie die folgenden Bedingungen verstehen:\n\nUm ARQ zu senden, müssen Sie entweder die offizielle ArQmA GUI Wallet oder ArQmA CLI Wallet mit der store-tx-info Flag aktiv (Standard in neuen Versionen). Bitte stellen Sie sicher, dass Sie Zugriff auf die Tx Schlüssel haben, weil diese im Falle eines Konflikts benötigt werden. \narqma-wallet-cli (Nutzen Sie das Kommando get_tx_key)\narqma-wallet-gui (Wählen Sie den Verlauf Reiter und klicken Sie auf den (P) Knopf, für Zahlungsbeweis)\n\nIn normalen Blockforschern ist die Transaktion nicht überprüfbar.\n\nSie müssen dem Vermittler die folgenden Daten bereitstellen, im Fall eines Konflikts:\n- Der private Schlüssel der Tx\n- Den Transaktionhash\n- Die öffentliche Adresse des Empfängers \n\nSollten Sie die Daten nicht übergeben können oder eine inkompatible Wallet verwendet haben, werden Sie den Konflikt verlieren. Der ARQ Sender ist in der Verantwortung die Übertragung der ARQ gegenüber dem Vermittler, im Falle eines Konflikts, zu beweisen.\n\nEs wird keine Zahlungskennung benötigt, nur eine normale öffentliche Adresse.\nFalls Sie sich über diesen Prozess im Unklaren sind, besuchen Sie ArQmA Discord Kanal (https://discord.gg/s9BQpJT) oder das ArQmA Forum (https://labs.arqma.com) für weiter Informationen. +account.altcoin.popup.xmr.msg=Wenn Sie XMR auf Bisq handeln wollen, stellen Sie bitte sicher, dass Sie die folgenden Bedingungen verstehen und erfüllen:\n\nUm XMR zu senden, brauchen Sie entweder die offizielle Monero GUI Wallet oder die einfache Monero CLI Wallet mit aktivierter store-tx-info (Standard in neuen Versionen).\nStellen Sie bitte sicher, dass Sie auf den Tx-Schlüssel zugreifen können.\nmonero-wallet-cli: (nutzen Sie das get_tx_key Kommando)\nmonero-wallet-gui: (gehen sie zum Verlauf Tab und klicken Sie die (P) Schaltfläche um die Zahlungsbestätigung anzuzeigen)\n\nZusätzlich zum XMR checktx Werkzeug (https://xmr.llcoins.net/checktx.html) kann die Überprüfung auch innerhalb der Wallet durchgeführt werden. \nmonero-wallet-cli: verwenden Sie den Befehl (check_tx_key)\nmonero-wallet-gui: gehen Sie zur Erweitert > Beweisen/Prüfen-Seite\n\nIn normalen Blockexplorern ist die Übertragung nicht überprüfbar.\n\nIm Fall eines Konflikts müssen Sie dem Vermittler folgende Daten übergeben:\n- Der private Schlüssel der Tx\n- Den Transaktionshash\n- Die öffentliche Adresse des Empfängers\n\nSollten Sie die Daten nicht übergeben können oder eine inkompatible Wallet verwendet haben, werden Sie den Konflikt verlieren. Der XMR Sender ist in der Verantwortung die Übertragung der XMR gegenüber dem Vermittler im Falle eines Konflikts zu beweisen.\n\nEs wird keine Zahlungskennung benötigt, nur eine normale öffentliche Adresse.\n\nFalls Sie sich über diesen Prozess im Unklaren sind, besuchen Sie (https://www.getmonero.org/resources/user-guides/prove-payment.html) oder das Moneroforum (https://forum.getmonero.org) um weitere Informationen zu finden. +account.altcoin.popup.blur.msg=Stellen Sie sicher, die folgenden Bedingungen verstanden zu haben, falls Sie BLUR auf Bisq handeln möchten:\n\nUm BLUR zu senden, müssen sie die Blur CLI Wallet oder Blur Netzwerk GUI Wallet nutzen.\n\nNach dem Senden der Zahlung, wird die Wallet den Transaktions-Hash (Tx ID). Diese müssen Sie speichern. Sie müssen auch das 'get_tx_key' Kommando ausführen damit der private Schlüssel angezeigt wird. Sie können diese INformationen nicht später erhalten.\n\nNutzen Sie die Blur Netzwerk GUI Wallet, können der private Schlüssel und Transaktion ID im "History" Tab gefunden werden. Sofort nach dem Senden, finden Sie die Transaktion. Klicken Sie auf das "?" Symbol in der unteren rechten Ecke der Box mit der Transaktion. Sie brauchen diese Informationen.\n\nIm Fall, dass Vermittlung nötig ist, müssen Sie folgendes dem Vermittler vorzeigen: 1.) Die Transaktion ID 2.) Der private Schlüssel der Transaktion und 3.) Die Adresse des Empfängers. Der Vermittler wird die BLUR Transaktion mithilfe des Transaktions Sichters (https://blur.cash/#tx-viewer) überprüfen.\n\nSollten Sie die benötigten Daten dem Vermittler nicht bereitstellen, verlieren Sie den Konflikt. Der BLUR Sender ist 100% verantwortlich den BLUR Transfer dem Vermittler, im Falle eines Konflikts, zu beweisen.\n\nSollten Sie diese Bedingungen nicht verstehen, suchen Sie Hilfe im Blur Network Discord (https://discord.gg/dMWaqVW). +account.altcoin.popup.cash2.msg=Stellen Sie sicher, die folgenden Bedingungen verstanden zu haben, falls Sie CASH2 auf Bisq handeln möchten:\n\nUm CASH2 zu senden, müssen sie die CASH2 Wallet Version 3 oder höher nutzen.\n\nNach dem Senden der Zahlung, wird die Transaktions-ID angezeigt. Diese müssen Sie speichern. Sofort nach der Zahlung müssen Sie das 'get_tx_key' Kommando in simplewallet ausführen, um den geheimen Transaktionsschlüssel zu erhalten, den Sie auch speichern müssen.\n\nIm Falle eines Konflikts müssen Sie folgendes vorzeigen: 1) die Transaktion-ID, 2) den geheimen Transaktionsschlüssel und 3) die Cash2-Adresse des Empfängers. Der Vermittler wird den CASH2 Transfer mithilfe des Cash2 Blockforscher überprüfen (https://blocks.cash2.org).\n\nSollten Sie die benötigten Daten nicht dem Vermittler bereitstellen, verlieren Sie den Konflikt. In jedem Fall eines Konflikts, ist der CASH2-Sender 100% verantwortlich die Transaktion dem Vermittler zu beweisen.\n\nSollten Sie diese Bedingungen nicht verstehen, handeln Sie nicht auf Bisq. Suchen Sie erst Hilfe im Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Stellen Sie sicher, die folgenden Bedingungen verstanden zu haben, falls Sie Qwertycoin auf Bisq handeln möchten:\n\nUm QWC zu senden, müssen sie die offizielle QWC Wallet Version 5.1.3 oder höher nutzen.\n\nNach dem Senden der Zahlung, wird die Transaktions-ID angezeigt. Diese müssen Sie speichern. Sofort nach der Zahlung müssen Sie das 'get_tx_key' Kommando in simplewallet ausführen, um den geheimen Transaktionsschlüssel zu erhalten, den Sie auch speichern müssen.\n\nIm Falle eines Konflikts müssen Sie folgendes vorzeigen: 1) die Transaktion-ID, 2) den geheimen Transaktionsschlüssel und 3) die QWC-Adresse des Empfängers. Der Vermittler wird den QWC Transfer mithilfe des QWC Blockforscher überprüfen (https://explorer.qwertycoin.org).\n\nSollten Sie die benötigten Daten nicht dem Vermittler bereitstellen, verlieren Sie den Konflikt. In jedem Fall eines Konflikts, ist der QWC-Sender 100% verantwortlich die Transaktion dem Vermittler zu beweisen.\n\nSollten Sie diese Bedingungen nicht verstehen, handeln Sie nicht auf Bisq. Suchen Sie erst Hilfe im QWC Discord (https://discord.gg/rUkfnpC). +account.altcoin.popup.drgl.msg=Dragonglass auf Bisq zu Handel verlangt, dass Sie die folgenden Bedingungen verstehen:\n\nAufgrund der privatsphäre, die Dragonglass ermöglicht, ist eine Transaktion nicht in der öffentlichen Blockchain überprüfbar. Falls benötigt, können Sie Ihre Zahlung mithilfe des TXN private Schlüssel beweisen.\nDer TXN private Schlüssel ist ein für jede Transaktion einmalig automatisch generierter Schlüssel, der nur aus Ihrer DRGL Wallet erreichbar ist.\nEntweder mit DRGL-wallet GUI (in Transaktionendetails Dialog) oder mit der Dragonglass CLI simplewallet (nutze Kommando "get_tx_key").\n\nDRGL Version 'Oathkeeper' und höher sind für beides BENÖTIGT.\n\nSie müssen dem Vermittler die folgenden Daten bereitstellen, im Fall eines Konflikts:\n- Der private Schlüssel der TXN\n- Den Transaktionhash\n- Die öffentliche Adresse des Empfängers \n\nÜberprüfen der Zahlung erfolgt mithilfe der obigen Daten als Eingabe auf (http://drgl.info/#check_txn).\n\nSollten Sie die Daten nicht übergeben können oder eine inkompatible Wallet verwendet haben, werden Sie den Konflikt verlieren. Der Dragonglass Sender ist in der Verantwortung die Übertragung der DRGL gegenüber dem Vermittler, im Falle eines Konflikts, zu beweisen. Nutzen der PaymentID ist nicht nötig\n\nSind Sie nicht sicher über das Vorgehen. besuchen Sie Dragonglass auf Discord (http://discord.drgl.info) für Hilfe. +account.altcoin.popup.ZEC.msg=Wenn Sie Zcash verwenden, können Sie nur die transparenten Adressen verwenden (die mit t beginnen), nicht die z-Adressen (privat), da der Vermittler die Transaktionen mit z-Adressen nicht überprüfen könnte. +account.altcoin.popup.XZC.msg=Wenn Sie Zcoin verwenden, können Sie nur die transparenten (verfolgbaren) Adressen verwenden, nicht die unverfolgbaren, da der Vermittler Transaktionen mit unverfolgbaren Adressen in einem Block Explorer nicht überprüfen könnte. +account.altcoin.popup.grin.msg=GRIN benötigt einen interaktiven Prozess zwischen Sender und Empfänger, um die Transaktion zu erstellen. Stellen Sie sicher, den Anweisungen der GRIN Projekt Webseite zu folgen, um zuverlässig GRIN zu senden und empfangen (der Empfänger muss oinline oder wenigstens während eines gewissen Zeitfensters). \n\nBisq unterstützt nur das Grinbox (Wallet713) Wallet URL Format. \n\nDer GRIN Sender muss beweisen können, die GRIN erfolgreich gesendet zu haben. Wenn die Wallet dies nicht kann, wird ein potentieller Konflikt zugunsten des GRIN Empfängers entschieden. Bitte stellen Sie sicher, dass Sie die letzte Grinbox Software nutzen, die den Transaktionsbeweis unterstützt, und Sie den Prozess verstehen, wie GRIN gesendet und empfangen wird, sowie wie man den Beweis erstellt. \n\nBeachten Sie https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only für weitere Informationen über das Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM benötigt einen interaktiven Prozess zwischen Sender und Empfänger, um die Transaktion zu erstellen.\n\nStellen Sie sicher, den Anweisungen der BEAM Projekt Webseite zu folgen, um zuverlässig BEAM zu senden und empfangen (der Empfänger muss oinline oder wenigstens während eines gewissen Zeitfensters). \n\nDer BEAM Sender muss beweisen können, die BEAM erfolgreich gesendet zu haben. Bitte stellen Sie sicher, dass Sie Wallet Software verwenden, die solche Beweise erstellen kann. Falls Die Wallet den Beweis nicht erstellen kann, wird ein potentieller Konflikt zugunsten des BEAM Empfängers entschieden. account.fiat.yourFiatAccounts=Ihre Nationalen Währungskonten -account.backup.title=Backup der Brieftasche erstellen +account.backup.title=Backup der Wallet erstellen account.backup.location=Speicherort des Backups account.backup.selectLocation=Speicherort des Backups wählen account.backup.backupNow=Backup jetzt erstellen (Backup ist nicht verschlüsselt!) @@ -951,18 +970,18 @@ account.backup.success=Backup erfolgreich gespeichert in:\n{0} account.backup.directoryNotAccessible=Der ausgewählt Ordner ist nicht erreichbar. {0} account.password.removePw.button=Passwort entfernen -account.password.removePw.headline=Passwortschutz Ihrer Brieftasche entfernen +account.password.removePw.headline=Passwortschutz Ihrer Wallet entfernen account.password.setPw.button=Passwort festlegen -account.password.setPw.headline=Passwortschutz Ihrer Brieftasche einrichten -account.password.info=Mit Passwortschutz müssen Sie Ihr Passwort eingeben, sowohl wenn Sie Bitcoins aus Ihrer Brieftasche abheben, wenn Sie Ihre Brieftasche einsehen oder aus den Seed-Wörtern wiederherstellen wollen, als auch beim Start der Anwendung. +account.password.setPw.headline=Passwortschutz Ihrer Wallet einrichten +account.password.info=Mit Passwortschutz müssen Sie Ihr Passwort eingeben, sowohl wenn Sie Bitcoins aus Ihrer Wallet abheben, wenn Sie Ihre Wallet einsehen oder aus den Seed-Wörtern wiederherstellen wollen, als auch beim Start der Anwendung. -account.seed.backup.title=Backup der Seed-Wörter Ihrer Brieftasche erstellen -account.seed.info=Bitte schreiben Sie die sowohl Seed-Wörter als auch das Datum auf! Mit diesen Seed-Wörtern und dem Datum können Sie Ihre Brieftasche jederzeit wiederherstellen.\nDie Seed-Wörter werden für die BTC- und BSQ-Brieftasche genutzt.\n\nSchreiben Sie die Seed-Wörter auf ein Blatt Papier schreiben und speichern Sie sie nicht auf Ihrem Computer.\n\nBitte beachten Sie, dass die Seed-Wörter KEIN Ersatz für ein Backup sind.\nSie müssen ein Backup des gesamten Anwendungsverzeichnisses unter \"Konto/Backup\" erstellen, um den ursprünglichen Zustand der Anwendung wiederherstellen zu können.\nDas Importieren der Seed-Wörter wird nur für Notfälle empfohlen. Die Anwendung wird ohne richtiges Backup der Datenbankdateien und Schlüssel nicht funktionieren! -account.seed.warn.noPw.msg=Sie haben kein Brieftasche-Passwort festgelegt, was das Anzeigen der Seed-Wörter schützen würde.\n\nMöchten Sie die Seed-Wörter jetzt anzeigen? +account.seed.backup.title=Backup der Seed-Wörter Ihrer Wallet erstellen +account.seed.info=Bitte schreiben Sie die sowohl Seed-Wörter als auch das Datum auf! Mit diesen Seed-Wörtern und dem Datum können Sie Ihre Wallet jederzeit wiederherstellen.\nDie Seed-Wörter werden für die BTC- und BSQ-Wallet genutzt.\n\nSchreiben Sie die Seed-Wörter auf ein Blatt Papier schreiben und speichern Sie sie nicht auf Ihrem Computer.\n\nBitte beachten Sie, dass die Seed-Wörter KEIN Ersatz für ein Backup sind.\nSie müssen ein Backup des gesamten Anwendungsverzeichnisses unter \"Konto/Backup\" erstellen, um den ursprünglichen Zustand der Anwendung wiederherstellen zu können.\nDas Importieren der Seed-Wörter wird nur für Notfälle empfohlen. Die Anwendung wird ohne richtiges Backup der Datenbankdateien und Schlüssel nicht funktionieren! +account.seed.warn.noPw.msg=Sie haben kein Wallet-Passwort festgelegt, was das Anzeigen der Seed-Wörter schützen würde.\n\nMöchten Sie die Seed-Wörter jetzt anzeigen? account.seed.warn.noPw.yes=Ja, und nicht erneut fragen account.seed.enterPw=Geben Sie Ihr Passwort ein um die Seed-Wörter zu sehen -account.seed.restore.info=Erstellen Sie vor dem Wiederherstellen der Keimwörter eine Sicherung!\nBeachten Sie auch, dass Brieftasche-Wiederherstellung nur für Notfälle ist und Probleme mit der internen Brieftasche-Datenbank verursachen kann.\nEs ist kein Weg ein Backup anzuwenden! Bitte nutzen Sie ein Backup aus dem Anwendungsdatenordner um eine vorherigen Zustand wiederherzustellen. -account.seed.restore.ok=Ok, ich verstehe und möchte wiederherstellen +account.seed.restore.info=Bitte erstellen Sie vor dem Wiederherstellen durch Keimwörter ein Backup. Beachten Sie auch, dass Wallet-Wiederherstellung nur für Notfälle ist und Probleme mit der internen Wallet-Datenbank verursachen kann.\nEs ist kein Weg ein Backup anzuwenden! Bitte nutzen Sie ein Backup des Anwendungsdatenordner um eine vorherigen Zustand wiederherzustellen. \n\nNach der Wiederherstellung wird die Anwendung herunterfahren. Nachdem Sie die Anwendung wieder gestartet haben, wird sie wieder mit dem Bitcoin-Netzwerk synchronisieren. Dies kann lange dauern und die CPU stark beanspruchen, vor allem, wenn die Wallet alt und viele Transaktionen hatte. Bitte unterbreche Sie diesen Prozess nicht, sonst müssen Sie vielleicht die SPV Kettendatei löschen und den Wiederherstellungsprozess wiederholen. +account.seed.restore.ok=Ok, mache die Wiederherstellung und fahre Bisq herunter #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=Der tiefe Preis muss # DAO #################################################################### -dao.tab.bsqWallet=BSQ-Brieftasche -dao.tab.proposals=Führung +dao.tab.factsAndFigures=Zahlen, Daten & Fakten +dao.tab.bsqWallet=BSQ-Wallet +dao.tab.proposals=Führung der DAO dao.tab.bonding=Kopplung -dao.tab.proofOfBurn=Listungsgebühr für Altcoins/Nachweis der Verbrennung +dao.tab.proofOfBurn=Listungsgebühr für Gut/Nachweis der Verbrennung +dao.tab.monitor=Netzwerkmonitor +dao.tab.news=Neuigkeiten dao.paidWithBsq=bezahlt mit BSQ -dao.availableBsqBalance=Verfügbar -dao.availableNonBsqBalance=Verfügbares nicht-BSQ-Guthaben (BTC) -dao.unverifiedBsqBalance=Nicht bestätigt (warte auf Block-Bestätigung) -dao.lockedForVoteBalance=Zum Abstimmen gesperrt +dao.availableBsqBalance=Zum Ausgeben verfügbar (bestätigte und unbestätigte Restbeträge) +dao.verifiedBsqBalance=Guthaben aller verifizierter UTXOs +dao.unconfirmedChangeBalance=Guthaben aller unbestätigter Restbeträge +dao.unverifiedBsqBalance=Guthaben aller unbestätigten Transaktionen (warte auf Block-Bestätigung) +dao.lockedForVoteBalance=Zum wählen genutzt dao.lockedInBonds=In Kopplungen gesperrt +dao.availableNonBsqBalance=Verfügbares nicht-BSQ-Guthaben (BTC) dao.totalBsqBalance=Gesamtes BSQ-Guthaben dao.tx.published.success=Ihre Transaktion wurde erfolgreich veröffentlicht. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Wahlzyklus-Übersicht dao.cycle.currentPhase=Aktuelle Phase dao.cycle.currentBlockHeight=Momentane Blockhöhe dao.cycle.proposal=Vorschlag-Phase -dao.cycle.blindVote=Blindabstimmungs-Phase -dao.cycle.voteReveal=Stimmoffenbarung-Phase +dao.cycle.proposal.next=Nächste Vorschlag-Phase +dao.cycle.blindVote=Geheime Wahl-Phase +dao.cycle.voteReveal=Wahloffenbarung-Phase dao.cycle.voteResult=Wahlergebnis dao.cycle.phaseDuration={0} Blöcke (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Wahloffenbarung Transaktion veröffentlicht +dao.voteReveal.txPublished=Ihre Wahloffenbarung-Transaktion mit ID {0} wurde erfolgreich veröffentlicht.\n\nDies geschieht automatisch durch die Software, wenn Sie an einer DAO Wahl teilgenommen haben. dao.results.cycles.header=Zyklen dao.results.cycles.table.header.cycle=Zyklus @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Wahlergebnis dao.results.proposals.voting.detail.header=Wahlergebnisse für gewählten Vorschlag +dao.results.exceptions=Wahlergebnisseausnahme(n) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Undefiniert @@ -1105,7 +1136,7 @@ dao.param.MIN_TAKER_FEE_BTC=Min. BTC Abnehmergebühr # suppress inspection "UnusedProperty" dao.param.PROPOSAL_FEE=Gebühr für Antrag # suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Gebühr für Abstimmung +dao.param.BLIND_VOTE_FEE=Gebühr für Wahl in BSQ # suppress inspection "UnusedProperty" dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Min. BSQ-Betrag für Entlohnungsantrag @@ -1125,7 +1156,7 @@ dao.param.QUORUM_REIMBURSEMENT=Benötigtes Quorum für Rückerstattungsantrag # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Benötigtes Quorum in BSQ um einen Parameter zu ändern # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Benötigtes Quorum in BSQ um einen Altcoin zu entfernen +dao.param.QUORUM_REMOVE_ASSET=Benötigtes Quorum in BSQ um ein Gut zu entfernen # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Benötigtes Quorum für Konfiszierungsantrag # suppress inspection "UnusedProperty" @@ -1140,7 +1171,7 @@ dao.param.THRESHOLD_REIMBURSEMENT=Benötigter Schwellwert in % für Entschädigu # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Benötigter Schwellwert in % um einen Parameter zu ändern # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Benötigter Schwellwert in % um einen Altcoin zu entfernen +dao.param.THRESHOLD_REMOVE_ASSET=Benötigter Schwellwert in % um ein Gut zu entfernen # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Benötigter Schwellwert in % für Konfiszierungsantrag # suppress inspection "UnusedProperty" @@ -1150,22 +1181,33 @@ dao.param.THRESHOLD_ROLE=Benötigter Schwellwert in % für Antrag einer Rolle mi dao.param.RECIPIENT_BTC_ADDRESS=Adresse des BTC Empfängers # suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Listungsgebühr pro Tag +dao.param.ASSET_LISTING_FEE_PER_DAY=Gut Listungsgebühr pro Tag +# suppress inspection "UnusedProperty" +dao.param.ASSET_MIN_VOLUME=Min. Handelsvolumen für Güter + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Sperrzeit für alternative Handelsauszahlung Tx # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. Handelsvolumen +dao.param.ARBITRATOR_FEE=Vermittlergebühr in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. Handels-Limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Gebundene Rolle Einheitsfaktor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Ausgabelimit pro Zyklus in BSQ dao.param.currentValue=Aktueller Wert: {0} dao.param.blocks={0} Blöcke -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Dauer von {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} Blöcke(Block) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(Standard Wert) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(Wurde in Wahl geändert) +dao.results.invalidVotes=Wir hatten ungültige Wahlen in diesem Wahlzyklus. Das kann passieren, wenn eine Wahl im P2P Netzwerk nicht gut verteilt wurde.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Undefiniert # suppress inspection "UnusedProperty" @@ -1173,20 +1215,20 @@ dao.phase.PHASE_PROPOSAL=Vorschlag-Phase # suppress inspection "UnusedProperty" dao.phase.PHASE_BREAK1=Pause 1 # suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Blindabstimmungs-Phase +dao.phase.PHASE_BLIND_VOTE=Geheime Wahl-Phase # suppress inspection "UnusedProperty" dao.phase.PHASE_BREAK2=Pause 2 # suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Stimmoffenbarung-Phase +dao.phase.PHASE_VOTE_REVEAL=Wahloffenbarung-Phase # suppress inspection "UnusedProperty" dao.phase.PHASE_BREAK3=Pause 3 # suppress inspection "UnusedProperty" dao.phase.PHASE_RESULT=Ergebnis-Phase -dao.results.votes.table.header.stakeAndMerit=Stimm-Gewicht +dao.results.votes.table.header.stakeAndMerit=Wahl-Gewicht dao.results.votes.table.header.stake=Einsatz dao.results.votes.table.header.merit=Verdient -dao.results.votes.table.header.vote=Stimme +dao.results.votes.table.header.vote=Wahl dao.bond.menuItem.bondedRoles=Gekoppelte Rollen dao.bond.menuItem.reputation=Gekoppeltes Ansehen @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Sperren dao.bond.reputation.lockup.headline=Sperrung-Transaktion bestätigen -dao.bond.reputation.lockup.details=Gesperrter Betrag: {0}\nSperr Zeit: {1} Blöcke(Block)\n\nSind Sie sicher, dass Sie fortfahren möchten? +dao.bond.reputation.lockup.details=Gesperrter Betrag: {0}\nEntsperrzeit: {1} Block(Blöcke) (≈{2})\n\nMining-Gebühr: {3} ({4} Satoshis/Byte)\nTransaktionsgröße: {5} Kb\n\nSind Sie sicher, dass Sie fortfahren möchten? dao.bond.reputation.unlock.headline=Entsperrung-Transaktion bestätigen -dao.bond.reputation.unlock.details=Entsperrter Betrag: {0}\nSperr Zeit: {1} Blöcke(Block)\n\nSind Sie sicher, dass Sie fortfahren möchten? +dao.bond.reputation.unlock.details=Entsperrter Betrag: {0}\nEntsperrzeit: {1} Block(Blöcke) (≈{2})\n\nMining-Gebühr: {3} ({4} Satoshis/Byte)\nTransaktionsgröße: {5} Kb\n\nSind Sie sicher, dass Sie fortfahren möchten? dao.bond.allBonds.header=Alle Pfänder @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Sperren dao.bond.table.button.unlock=Entsperren dao.bond.table.button.revoke=Widerrufen +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Undefiniert # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Noch nicht gekoppelt # suppress inspection "UnusedProperty" @@ -1252,11 +1296,15 @@ dao.bond.bondState.UNLOCKED=Kopplung entsperrt # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Pfand konfisziert +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Undefiniert # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Gekoppelte Rolle # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Gekoppeltes Ansehen +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.UNDEFINED=Undefiniert # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub Administrator # suppress inspection "UnusedProperty" @@ -1270,20 +1318,26 @@ dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube Administrator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq Betreuer # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-Fork Betreuer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer Betreuer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Betreiber der Webseite # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Betreiber des Forums # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed-Knoten Betreiber # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Preis-Netzknoten Betreiber +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Datenweiterleitung Knoten Betreiber # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC Netzknoten Betreiber +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC-Netzknoten Betreiber # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=Betreiber der Handelsstatistik Webseite # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Betreiber des BSQ Explorers # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile Benachrichtigung Weiterleitung Betreiber +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domainnamen Inhaber # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS Administrator @@ -1291,24 +1345,26 @@ dao.bond.bondedRoleType.DNS_ADMIN=DNS Administrator dao.bond.bondedRoleType.MEDIATOR=Mediator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Vermittler +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC Spendenadresse Besitzer -dao.burnBsq.assetFee=Listungsgebühr für Altcoin -dao.burnBsq.menuItem.assetFee=Listungsgebühr für Altcoin +dao.burnBsq.assetFee=Gut Listung +dao.burnBsq.menuItem.assetFee=Gut Listungsgebühr dao.burnBsq.menuItem.proofOfBurn=Nachweis der Verbrennung -dao.burnBsq.header=Listungsgebühr für Altcoin -dao.burnBsq.selectAsset=Altcoin auswählen +dao.burnBsq.header=Listungsgebühr für Gut +dao.burnBsq.selectAsset=Gut auswählen dao.burnBsq.fee=Gebühr dao.burnBsq.trialPeriod=Probezeit dao.burnBsq.payFee=Gebühr bezahlen -dao.burnBsq.allAssets=Alle Altcoins -dao.burnBsq.assets.nameAndCode=Altcoin name +dao.burnBsq.allAssets=Alle Güter +dao.burnBsq.assets.nameAndCode=Gutsname dao.burnBsq.assets.state=Status dao.burnBsq.assets.tradeVolume=Handelsvolumen dao.burnBsq.assets.lookBackPeriod=Überprüfungsphase dao.burnBsq.assets.trialFee=Gebühr für Probezeit dao.burnBsq.assets.totalFee=Insgesamt gezahlte Gebühren dao.burnBsq.assets.days={0} Tage -dao.burnBsq.assets.toFewDays=Die Altcoingebühr ist zu niedrig. Die min. Anzahl Tage für die Probezeit sind {0} Tage. +dao.burnBsq.assets.toFewDays=Die Gutsgebühr ist zu niedrig. Die min. Anzahl Tage für die Probezeit sind {0} Tage. # suppress inspection "UnusedProperty" dao.assetState.UNDEFINED=Undefiniert @@ -1319,7 +1375,7 @@ dao.assetState.ACTIVELY_TRADED=Aktiv gehandelt # suppress inspection "UnusedProperty" dao.assetState.DE_LISTED=Aufgrund von Inaktivität aus der Liste entfernt # suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Durch Abstimmung entfernt +dao.assetState.REMOVED_BY_VOTING=Per Wahl entfernt dao.proofOfBurn.header=Nachweis der Verbrennung dao.proofOfBurn.amount=Betrag @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=Datum dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=Transaktionen dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Nachricht mit Schlüssel vom Nachweis der Verbrennung unterzeichnen -dao.proofOfBurn.verify.window.title=Verifizieren Sie eine Nachricht mit dem Schlüssel der Proof-Of-Burn Transaktion +dao.proofOfBurn.signature.window.title=Unterzeichnen einer Nachricht mit Schlüssel vom Nachweis der Verbrennung +dao.proofOfBurn.verify.window.title=Verifizieren einer Nachricht mit Schlüssel vom Nachweis der Verbrennung dao.proofOfBurn.copySig=Signatur in Zwischenablage kopieren dao.proofOfBurn.sign=Unterzeichnen dao.proofOfBurn.message=Nachricht dao.proofOfBurn.sig=Signatur dao.proofOfBurn.verify=Bestätigen -dao.proofOfBurn.verify.header=Nachricht mit Schlüssel vom Nachweis der Verbrennung überprüfen +dao.proofOfBurn.verify.header=Verifiziere Nachricht mit Schlüssel vom Nachweis der Verbrennung dao.proofOfBurn.verificationResult.ok=Überprüfung erfolgreich dao.proofOfBurn.verificationResult.failed=Überprüfung fehlgeschlagen @@ -1347,13 +1403,13 @@ dao.phase.UNDEFINED=Undefiniert # suppress inspection "UnusedProperty" dao.phase.PROPOSAL=Vorschlag-Phase # suppress inspection "UnusedProperty" -dao.phase.BREAK1=Pause vor Blindabstimmungs-Phase +dao.phase.BREAK1=Pause vor geheime Wahl-Phase # suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Blindabstimmungs-Phase +dao.phase.BLIND_VOTE=Geheime Wahl-Phase # suppress inspection "UnusedProperty" -dao.phase.BREAK2=Pause vor Stimmoffenbarung-Phase +dao.phase.BREAK2=Pause vor Wahloffenbarung-Phase # suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Stimmoffenbarung-Phase +dao.phase.VOTE_REVEAL=Wahloffenbarung-Phase # suppress inspection "UnusedProperty" dao.phase.BREAK3=Pause vor Ergebnis-Phase # suppress inspection "UnusedProperty" @@ -1362,12 +1418,14 @@ dao.phase.RESULT=Wahlergebnisse-Phase # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.PROPOSAL=Vorschlag-Phase # suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Blinde Wahl +dao.phase.separatedPhaseBar.BLIND_VOTE=Geheime Wahl # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.VOTE_REVEAL=Wahl offenbaren # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Wahlergebnis +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Undefiniert # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Entlohnungsanfrage # suppress inspection "UnusedProperty" @@ -1375,7 +1433,7 @@ dao.proposal.type.REIMBURSEMENT_REQUEST=Rückerstattungsantrag # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Vorschlag für Gekoppelte Rolle # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Antrag einen Altcoin zu entfernen +dao.proposal.type.REMOVE_ASSET=Vorschlag ein Gut zu entfernen # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Vorschlag einen Parameter zu ändern # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=Allgemeiner Vorschlag # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Vorschlag eine Kopplung zu konfiszieren +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Undefiniert # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Entlohnungsanfrage # suppress inspection "UnusedProperty" @@ -1398,28 +1458,30 @@ dao.proposal.type.short.GENERIC=Allgemeiner Vorschlag # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Konfisziere eine Kopplung - dao.proposal.details=Vorschlagdetails dao.proposal.selectedProposal=Ausgewählte Anträge dao.proposal.active.header=Vorschläge des momentanen Zyklus dao.proposal.active.remove.confirm=Sind Sie sicher, dass Sie diesen Antrag entfernen wollen?\nDie Erstellergebühr geht verloren, wenn Sie den Antrag entfernen. dao.proposal.active.remove.doRemove=Ja, Antrag entfernen dao.proposal.active.remove.failed=Konnte Vorschlag nicht entfernen +dao.proposal.myVote.title=Wahl dao.proposal.myVote.accept=Vorschlag annehmen dao.proposal.myVote.reject=Vorschlag ablehnen dao.proposal.myVote.removeMyVote=Vorschlag ignorieren -dao.proposal.myVote.merit=Stimmgewicht durch verdiente BSQ -dao.proposal.myVote.stake=Stimmgewicht vom Einsatz -dao.proposal.myVote.blindVoteTxId=Blindabstimmung Transaktion ID -dao.proposal.myVote.revealTxId=Stimmoffenbarung Transaktion ID -dao.proposal.myVote.stake.prompt=Max. verfügbares Guthaben zum Abstimmen: {0} -dao.proposal.votes.header=Für alle Vorschläge abstimmen -dao.proposal.votes.header.voted=Meine Stimme -dao.proposal.myVote.button=Für alle Vorschläge abstimmen +dao.proposal.myVote.merit=Wahlgewicht durch verdiente BSQ +dao.proposal.myVote.stake=Wahlgewicht vom Einsatz +dao.proposal.myVote.revealTxId=Wahloffenbarung Transaktion ID +dao.proposal.myVote.stake.prompt=Max. verfügbarer Einsatz zum wählen: {0} +dao.proposal.votes.header=Einsatz für Wahl festlegen und Ihre Wahlen veröffentlichen +dao.proposal.myVote.button=Wahlen veröffentlichen +dao.proposal.myVote.setStake.description=Nach dem Sie für alle Vorschläge gewählt haben, müssen Sie Ihren Einsatz festlegen, indem Sie BSQ sperren. Desto mehr BSQ Sie sperren, desto mehr Gewicht hat Ihre Wahl.\n\nGesperrte BSQ wird während der Wahl Veröffentlichungsphase entsperrt. dao.proposal.create.selectProposalType=Vorschlagtyp auswählen +dao.proposal.create.phase.inactive=Bitte warten sie auf die nächste Vorschlags-Phase dao.proposal.create.proposalType=Vorschlagtype -dao.proposal.create.createNew=Neuen Antrag erstellen -dao.proposal.create.create.button=Antrag erstellen +dao.proposal.create.new=Neuen Antrag erstellen +dao.proposal.create.button=Antrag erstellen +dao.proposal.create.publish=Vorschlag veröffentlichen +dao.proposal.create.publishing=Vorschlag wird veröffentlicht ... dao.proposal=Vorschlag dao.proposal.display.type=Vorschlagtyp dao.proposal.display.name=Name/Spitzname @@ -1429,8 +1491,8 @@ dao.proposal.display.requestedBsq=Gelder in BSQ anfordern dao.proposal.display.bsqAddress=BSQ Adresse dao.proposal.display.txId=Antrag Transaktion-ID: dao.proposal.display.proposalFee=Vorschlag-Gebühr -dao.proposal.display.myVote=Meine Stimme -dao.proposal.display.voteResult=Zusammenfassung des Abstimmungsergebnisses +dao.proposal.display.myVote=Meine Wahl +dao.proposal.display.voteResult=Zusammenfassung des Wahlergebnisses dao.proposal.display.bondedRoleComboBox.label=Typ der Rolle mit Pfand dao.proposal.display.requiredBondForRole.label=Benötigter Pfand für Rolle dao.proposal.display.tickerSymbol.label=Symbol für Ticker @@ -1438,7 +1500,7 @@ dao.proposal.display.option=Option dao.proposal.table.header.proposalType=Vorschlagtyp dao.proposal.table.header.link=Link -dao.proposal.table.header.myVote=Meine Stimme +dao.proposal.table.header.myVote=Meine Wahl dao.proposal.table.header.remove=Entfernen dao.proposal.table.icon.tooltip.removeProposal=Antrag entfernen dao.proposal.table.icon.tooltip.changeVote=Aktuelles Votum: ''{0}''. Votum abändern zu: ''{1}'' @@ -1446,7 +1508,8 @@ dao.proposal.table.icon.tooltip.changeVote=Aktuelles Votum: ''{0}''. Votum abän dao.proposal.display.myVote.accepted=Angenommen dao.proposal.display.myVote.rejected=Abgelehnt dao.proposal.display.myVote.ignored=Ignoriert -dao.proposal.myVote.summary=Gewählt: {0}; Stimmgewicht: {1} (verdient: {2} + Einsatz: {3}); +dao.proposal.myVote.summary=Gewählt: {0}; Wahlgewicht: {1} (verdient: {2} + Einsatz: {3}); +dao.proposal.myVote.invalid=Wahl war ungültig dao.proposal.voteResult.success=Angenommen dao.proposal.voteResult.failed=Abgelehnt @@ -1456,44 +1519,29 @@ dao.proposal.display.paramComboBox.label=Zu ändernden Parameter auswählen dao.proposal.display.paramValue=Wert des Parameter dao.proposal.display.confiscateBondComboBox.label=Kopplung wählen -dao.proposal.display.assetComboBox.label=Zu entfernender Altcoin +dao.proposal.display.assetComboBox.label=Zu entfernendes Gut -dao.blindVote=blinde Stimme +dao.blindVote=Geheime Wahl -dao.blindVote.startPublishing=Veröffentliche Blindabstimmung-Transaktion... -dao.blindVote.success=Ihre Blindabstimmung wurde erfolgreich veröffentlicht. +dao.blindVote.startPublishing=Veröffentliche geheimen Wahl Transaktion... +dao.blindVote.success=Die Transaktion ihrer geheimen Wahl wurde erfolgreich veröffentlicht.\n\nDamit ihre Stimme auch gezählt wird, müssen sie im Zeitraum der Stimmoffenbahrungs-Phase in Bisq online sein. Wenn die Stimmoffenbahrungs-Transaktion nicht veröffentlicht wird, ist ihre Stimme nicht gültig! dao.wallet.menuItem.send=Senden dao.wallet.menuItem.receive=Empfangen dao.wallet.menuItem.transactions=Transaktionen -dao.wallet.dashboard.myBalance=Mein Guthaben der Handels-Brieftasche -dao.wallet.dashboard.distribution=Verteilung aller BSQ -dao.wallet.dashboard.locked=Globaler Zustand der gesperrten BSQ -dao.wallet.dashboard.market=Marktdaten -dao.wallet.dashboard.genesis=Ursprungstransaktion -dao.wallet.dashboard.txDetails=BSQ Transationsstatistiken -dao.wallet.dashboard.genesisBlockHeight=Genesisblock-Höhe -dao.wallet.dashboard.genesisTxId=Genesis-Transaktions-ID -dao.wallet.dashboard.genesisIssueAmount=Ausgestellte BSQ in Genesis-Transaktion -dao.wallet.dashboard.compRequestIssueAmount=Ausgestellte BSQ für Entlohnungsanträge -dao.wallet.dashboard.reimbursementAmount=Ausgestellte BSQ für Rückerstattungsanträge -dao.wallet.dashboard.availableAmount=Insgesamt verfügbare BSQ -dao.wallet.dashboard.burntAmount=Verbrannte BSQ (Gebühren) -dao.wallet.dashboard.totalLockedUpAmount=In Pfänden gesperrt -dao.wallet.dashboard.totalUnlockingAmount=BSQ von Pfand auslösen -dao.wallet.dashboard.totalUnlockedAmount=BSQ von Pfand ausgelöst -dao.wallet.dashboard.totalConfiscatedAmount=Konfiszierte BSQ von Pfänden -dao.wallet.dashboard.allTx=Anzahl aller BSQ-Transaktionen -dao.wallet.dashboard.utxo=Anzahl aller nicht ausgegebenen Transaktionsausgängen -dao.wallet.dashboard.compensationIssuanceTx=Anzahl aller Transaktionen von Entlohnungsanfragen -dao.wallet.dashboard.reimbursementIssuanceTx=Anzahl aller Transaktionen von Entschädigungsanfragen -dao.wallet.dashboard.burntTx=Anzahl aller Transaktionen von bezahlten Gebühren -dao.wallet.dashboard.price=Aktueller BSQ/BTC Handelspreis (in Bisq) -dao.wallet.dashboard.marketCap=Marktkapitalisierung (basierend auf Handelspreis) - -dao.wallet.receive.fundYourWallet=Ihre BSQ-Brieftasche finanzieren -dao.wallet.receive.bsqAddress=Adresse der BSQ-Brieftasche +dao.wallet.dashboard.myBalance=Mein Guthaben der Handels-Wallet + +dao.wallet.receive.fundYourWallet=Ihre BSQ Empfangs-Adresse +dao.wallet.receive.bsqAddress=Adresse der BSQ-Wallet (Neue ungebrauchte Adresse) + +dao.wallet.receive.dao.headline=Der Bisq DAO +dao.wallet.receive.daoInfo=Genauso wie der Bisq Handelsplatz dezentral und resistent gegen Zensur ist, ist auch die Führung der DAO - die Bisq DAO und der BSQ Token machen es möglich. +dao.wallet.receive.daoInfo.button=Erfahren Sie mehr über den Bisq DAO +dao.wallet.receive.daoTestnetInfo=Die Bisq DAO wurde im Mainnet noch nicht veröffentlicht, jedoch können sie die Bisq DAO in unserem Testnet ausprobieren. +dao.wallet.receive.daoTestnetInfo.button=Wie sie die Bisq DAO in unserem Testnet verwenden +dao.wallet.receive.daoContributorInfo=Falls sie zu Bisq beigetragen haben, verwenden sie die unterstehende BSQ Adresse um einen Antrag Teil der BSQ Genesis Transaktion zu sein. +dao.wallet.receive.daoContributorInfo.button=Wie sie Teil der BSQ Genesis Verteilung werden dao.wallet.send.sendFunds=Gelder senden dao.wallet.send.sendBtcFunds=Sende nicht-BSQ-Gelder (BTC) @@ -1509,9 +1557,11 @@ dao.wallet.send.sendBtc=BTC-Gelder senden dao.wallet.send.sendFunds.headline=Abhebeanfrage bestätigen dao.wallet.send.sendFunds.details=Sende: {0}\nAn Empfangsadresse: {1}\nBenötigte Transaktionsgebühr ist: {2} ({3} Satoshis/Byte)\nTransaktionsgröße: {4} Kb\n\nDer Empfänger erhält: {5}\n\nSind Sie sicher, dass Sie diesen Betrag abheben wollen? dao.wallet.chainHeightSynced=Synchronisiert bis Block: {0} -dao.wallet.chainHeightSyncing=Empfange Blöcke... Synchronisiere Block: {0} (letzter Block: {1}) +dao.wallet.chainHeightSyncing=Erwarte Blöcke... {0} von {1} Blöcken verifiziert dao.wallet.tx.type=Typ +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Undefiniert # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Nicht erkannt # suppress inspection "UnusedProperty" @@ -1535,7 +1585,7 @@ dao.tx.type.enum.REIMBURSEMENT_REQUEST=Gebühr für Rückerstattungsantrag # suppress inspection "UnusedProperty" dao.tx.type.enum.PROPOSAL=Gebühr für Vorschlag # suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Gebühr für blinde Wahl +dao.tx.type.enum.BLIND_VOTE=Gebühr für geheime Wahl # suppress inspection "UnusedProperty" dao.tx.type.enum.VOTE_REVEAL=Wahl offenbaren # suppress inspection "UnusedProperty" @@ -1543,7 +1593,7 @@ dao.tx.type.enum.LOCKUP=Sperre Kopplung # suppress inspection "UnusedProperty" dao.tx.type.enum.UNLOCK=Entsperre Kopplung # suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Listungsgebühr für Altcoin +dao.tx.type.enum.ASSET_LISTING_FEE=Listungsgebühr für Gut # suppress inspection "UnusedProperty" dao.tx.type.enum.PROOF_OF_BURN=Nachweis der Verbrennung @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=Entlohnungsanfrage/ausgabe dao.tx.issuanceFromCompReq.tooltip=Entlohnungsanfrage, die zur Ausgabe neuere BSQ führte.\nAusgabedatum: {0} dao.tx.issuanceFromReimbursement=Rückerstattungsantrag/Ausgabe dao.tx.issuanceFromReimbursement.tooltip=Rückerstattungsanfrage, die zur Ausgabe neuer BSQ führte.\nAusgabedatum: {0} -dao.proposal.create.missingBsqFunds=Sie haben nicht genügend Gelder um den Vorschlag zu erstellen.\nFehlend: {0} +dao.proposal.create.missingBsqFunds=Sie haben nicht ausreichend BSQ um diesen Vorschlag zu erstellen. Falls sie nicht bestätigte BSQ-Transaktionen offen haben, müssen sie auf die Blockchain-Bestätigung warten. BSQ kann nur validiert werden wenn es in einen Block aufgenommen wurde.\nEs fehlen: {0} + +dao.proposal.create.missingBsqFundsForBond=Du hast nicht ausreichend BSQ für diese Rolle. Du kannst den Vorschlag zwar veröffentlichen, benötigst jedoch den vollen BSQ Betrag für diese Rolle, falls dein Vorschlag akzeptiert wird.\nEs fehlen: {0} + +dao.proposal.create.missingMinerFeeFunds=Du hast nicht ausreichend BTC, um die Vorschlags-Transaktion zu erstellen. Jede BSQ-Transaktion benötigt Mining-Gebühr in BTC.\nEs fehlen: {0} dao.feeTx.confirm=Bestätige {0} Transaktion dao.feeTx.confirm.details={0} Gebühr: {1}\nMining-Gebühr: {2} ({3} Satoshis/Byte)\nTransaktionsgröße: {4} Kb\n\nSind Sie sicher, dass Sie die {5} Transaktion senden wollen? +dao.news.bisqDAO.title=DER BISQ DAO +dao.news.bisqDAO.description=Genauso wie der Bisq Handelsplatz dezentral und resistent gegen Zensur ist, ist auch die Führung der DAO - die Bisq DAO und der BSQ Token machen es möglich. +dao.news.bisqDAO.readMoreLink=Erfahren Sie mehr über den Bisq DAO + +dao.news.pastContribution.title=BEREITS ZU BISQ BEIGETRAGEN? BSQ ANFRAGEN +dao.news.pastContribution.description=Falls sie in der Vergangenheit zu Bisq beigetragen haben, verwenden sie die darunterstehende BSQ Adresse um einen Antrag zur Aufnahme in die BSQ Genesis Transaktion zu erstellen. +dao.news.pastContribution.yourAddress=Ihre BSQ-Wallets-Adresse +dao.news.pastContribution.requestNow=Jetzt anfordern + +dao.news.DAOOnTestnet.title=DEN BISQ DAO AUF UNSEREM TESTNETZWERK LAUFEN LASSEN +dao.news.DAOOnTestnet.description=Die Bisq DAO wurde auf Mainnet noch nicht veröffentlicht, jedoch können sie die Bisq DAO jetzt schon in unserem Testnet ausprobieren. +dao.news.DAOOnTestnet.firstSection.title=1. Nach DAO-Testnetzmodus wechseln +dao.news.DAOOnTestnet.firstSection.content=Vom Einstellungsmenü ins DAO-Testnetzwerk wechseln. +dao.news.DAOOnTestnet.secondSection.title=2. Einige BSQ erwerben +dao.news.DAOOnTestnet.secondSection.content=Fragen sie einfach auf Slack nach BSQ oder kaufen sie direkt BSQ in Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Beim Wahl-Zyklus teilhaben +dao.news.DAOOnTestnet.thirdSection.content=Erstellen sie Vorschläge/Anträge und stimmen sie über bestehende ab, um unterschiedliche Aspekte von Bisq zu verändern. +dao.news.DAOOnTestnet.fourthSection.title=4. Einen BSQ-Block-Forscher erkunden +dao.news.DAOOnTestnet.fourthSection.content=Da es sich bei BSQ um Bitcoin handelt, können sie die BSQ-Transaktionen in ihrem Bitcoin Block Explorer einsehen. +dao.news.DAOOnTestnet.readMoreLink=Die volle Dokumentation lesen. + +dao.monitor.daoState=DAO Status +dao.monitor.proposals=Vorschlag Status +dao.monitor.blindVotes=Geheime Wahl-Status + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Konflikte +dao.monitor.state=Status +dao.monitor.requestAlHashes=Alle Hashs anfragen +dao.monitor.resync=DAO Status neu syncronisieren +dao.monitor.table.header.cycleBlockHeight=Zyklus / Blockhöhe +dao.monitor.table.cycleBlockHeight=Zyklus {0} / Block {1} + +dao.monitor.daoState.headline=DAO Status +dao.monitor.daoState.daoStateInSync=Ihr lokaler DAO Status stimmt mit dem des Netzwerk überein +dao.monitor.daoState.daoStateNotInSync=Ihr lokaler DAO Status stimmt nicht mit dem des Netzwerk überein. Bitte synchronisieren Sie Ihren DAO Status neu. +dao.monitor.daoState.table.headline=Kette von DAO Status Hashs +dao.monitor.daoState.table.blockHeight=Blockhöhe +dao.monitor.daoState.table.hash=Hash des DAO Status +dao.monitor.daoState.table.prev=Vorheriger Hash +dao.monitor.daoState.conflictTable.headline=DAO Status Hashs der Peers im Konflikt + +dao.monitor.proposal.headline=Vorschlag Status +dao.monitor.proposal.daoStateInSync=Ihr lokaler Vorschlag Status stimmt mit dem des Netzwerk überein +dao.monitor.proposal.daoStateNotInSync=Ihr lokaler Vorschlagstatus stimmt nicht mit dem des Netzwerks überein. Bitte starten Sie Ihre Anwendung neu. +dao.monitor.proposal.table.headline=Kette von Vorschlag Status Hashs +dao.monitor.proposal.conflictTable.headline=Vorschlag Status Hashs der Peers im Konflikt + +dao.monitor.proposal.table.hash=Hash des Vorschlag Status +dao.monitor.proposal.table.prev=Vorheriger Hash +dao.monitor.proposal.table.numProposals=Keine Vorschläge + + +dao.monitor.blindVote.headline=Geheime Wahl-Status +dao.monitor.blindVote.daoStateInSync=Ihr lokaler geheime Wahl Status stimmt mit dem des Netzwerk überein +dao.monitor.blindVote.daoStateNotInSync=Ihr lokaler geheime Wahl Status stimmt nicht mit dem des Netzwerks überein. Bitte starten Sie Ihre Anwendung neu. +dao.monitor.blindVote.table.headline=Kette von geheime Wahl Status Hashs +dao.monitor.blindVote.conflictTable.headline=Geheime Wahl Status Hashs der Peers im Konflikt +dao.monitor.blindVote.table.hash=Hash des geheimen Wahl Status +dao.monitor.blindVote.table.prev=Vorheriger Hash +dao.monitor.blindVote.table.numBlindVotes=Anzahl geheimer Wahlen + +dao.factsAndFigures.menuItem.supply=Angebot an BSQ +dao.factsAndFigures.menuItem.transactions=BSQ Transaktionen + +dao.factsAndFigures.dashboard.marketPrice=Marktdaten +dao.factsAndFigures.dashboard.price=Aktueller BSQ/BTC Handelspreis (in Bisq) +dao.factsAndFigures.dashboard.marketCap=Marktkapitalisierung (basierend auf Handelspreis) +dao.factsAndFigures.dashboard.availableAmount=Insgesamt verfügbare BSQ + +dao.factsAndFigures.supply.issued=Ausgestellte BSQ +dao.factsAndFigures.supply.genesisIssueAmount=Ausgestellte BSQ in Genesis-Transaktion +dao.factsAndFigures.supply.compRequestIssueAmount=Ausgestellte BSQ für Entlohnungsanträge +dao.factsAndFigures.supply.reimbursementAmount=Ausgestellte BSQ für Rückerstattungsanträge + +dao.factsAndFigures.supply.burnt=Verbrannte BSQ (Gebühren und Proof-of-Burn) + +dao.factsAndFigures.supply.locked=Globaler Zustand der gesperrten BSQ +dao.factsAndFigures.supply.totalLockedUpAmount=In Pfänden gesperrt +dao.factsAndFigures.supply.totalUnlockingAmount=BSQ von Pfand auslösen +dao.factsAndFigures.supply.totalUnlockedAmount=BSQ von Pfand ausgelöst +dao.factsAndFigures.supply.totalConfiscatedAmount=Konfiszierte BSQ von Pfänden +dao.factsAndFigures.supply.burntAmount=Verbrannte BSQ (Gebühren) + +dao.factsAndFigures.transactions.genesis=Ursprungstransaktion +dao.factsAndFigures.transactions.genesisBlockHeight=Genesisblock-Höhe +dao.factsAndFigures.transactions.genesisTxId=Genesis-Transaktions-ID +dao.factsAndFigures.transactions.txDetails=BSQ Transationsstatistiken +dao.factsAndFigures.transactions.allTx=Anzahl aller BSQ-Transaktionen +dao.factsAndFigures.transactions.utxo=Anzahl aller nicht ausgegebenen Transaktionsausgängen +dao.factsAndFigures.transactions.compensationIssuanceTx=Anzahl aller Transaktionen von Entlohnungsanfragen +dao.factsAndFigures.transactions.reimbursementIssuanceTx=Anzahl aller Transaktionen von Entschädigungsanfragen +dao.factsAndFigures.transactions.burntTx=Anzahl aller Transaktionen von bezahlten Gebühren #################################################################### # Windows @@ -1615,16 +1762,16 @@ disputeSummaryWindow.close.button=Ticket schließen disputeSummaryWindow.close.msg=Ticket geschlossen am {0}\n\nZusammenfassung:\n{1} übermittelte, manipulatoinssichere Beweise: {2}\n{3} hat Ausweisüberprüfung geleistet: {4}\n{5} hat Bildschirmmitschnitt oder Video erstellt: {6}\nAuszahlungsbetrag für BTC-Käufer: {7}\nAuszahlungsbetrag für BTC-Verkäufer: {8}\n\nZusammenfassende Hinweise:\n{9} disputeSummaryWindow.close.closePeer=Sie müssen auch das Ticket des Handelspartners schließen! -emptyWalletWindow.headline={0} Notfall-Brieftaschen-Werkzeug +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 Brieftaschen-Guthaben +emptyWalletWindow.balance=Ihr verfügbares Wallets-Guthaben emptyWalletWindow.bsq.btcBalance=Guthaben von nicht-BSQ Satoshis emptyWalletWindow.address=Ihre Zieladresse emptyWalletWindow.button=Alle Gelder senden -emptyWalletWindow.openOffers.warn=Sie haben offene Angebote, die entfernt werden, wenn Sie die Brieftasche leeren.\nSind Sie sicher, dass Sie Ihre Brieftasche leeren wollen? +emptyWalletWindow.openOffers.warn=Sie haben offene Angebote, die entfernt werden, wenn Sie die Wallet leeren.\nSind Sie sicher, dass Sie Ihre Wallet leeren wollen? emptyWalletWindow.openOffers.yes=Ja, ich bin sicher. -emptyWalletWindow.sent.success=Das Guthaben Ihrer Brieftasche wurde erfolgreich überwiesen. +emptyWalletWindow.sent.success=Das Guthaben Ihrer Wallet wurde erfolgreich überwiesen. enterPrivKeyWindow.headline=Die Registrierung ist nur für eingeladene Vermittler verfügbar @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Gefilterte Seed-Knoten (Komma getr. Onion-Adressen) filterWindow.priceRelayNode=Gefilterte Preisrelais Knoten (Komma getr. Onion-Adressen) filterWindow.btcNode=Gefilterte Bitcoinknoten (Komma getr. Adresse + Port) filterWindow.preventPublicBtcNetwork=Nutzung des öffentlichen Bitcoin-Netzwerks verhindern +filterWindow.disableDao=DAO deaktivieren filterWindow.add=Filter hinzufügen filterWindow.remove=Filter entfernen @@ -1660,11 +1808,11 @@ 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-Brieftasche von einer externen Brieftasche. +qRCodeWindow.msg=Bitte nutzen Sie diesen QR-Code zum Finanzieren Ihrer Bisq-Wallet von einer externen Wallet. qRCodeWindow.request=Zahlungsanfrage:\n{0} selectDepositTxWindow.headline=Kautionstransaktion für Konflikt auswählen -selectDepositTxWindow.msg=Die Kautionstransaktion wurde nicht im Handel gespeichert.\nBitte wählen Sie die existierenden MultiSig-Transaktionen aus Ihrer Brieftasche, die die Kautionstransaktion für den fehlgeschlagenen Handel war.\n\nSie können die korrekte Transaktion finden, indem Sie das Handelsdetail-Fenster öffnen (klicken Sie auf die Handels-ID in der Liste) und dem Transaktions-Output der Handelsgebührenzahlung zur nächsten Transaktion folgen, wo Sie die MultiSig-Kautionstransaktion sehen (die Adresse beginnt mit einer 3). Diese Transaktions-ID sollte in der dargestellten Liste auftauchen. Sobald Sie die korrekte Transaktion gefunden haben, wählen Sie diese Transaktion hier aus und fahren fort.\n\nEntschuldigen Sie die Unannehmlichkeiten, aber dieser Fehler sollte sehr selten auftreten und wir werden in Zukunft versuchen bessere Wege zu finden, ihn zu lösen. +selectDepositTxWindow.msg=Die Kautionstransaktion wurde nicht im Handel gespeichert.\nBitte wählen Sie die existierenden MultiSig-Transaktionen aus Ihrer Wallet, die die Kautionstransaktion für den fehlgeschlagenen Handel war.\n\nSie können die korrekte Transaktion finden, indem Sie das Handelsdetail-Fenster öffnen (klicken Sie auf die Handels-ID in der Liste) und dem Transaktions-Output der Handelsgebührenzahlung zur nächsten Transaktion folgen, wo Sie die MultiSig-Kautionstransaktion sehen (die Adresse beginnt mit einer 3). Diese Transaktions-ID sollte in der dargestellten Liste auftauchen. Sobald Sie die korrekte Transaktion gefunden haben, wählen Sie diese Transaktion hier aus und fahren fort.\n\nEntschuldigen Sie die Unannehmlichkeiten, aber dieser Fehler sollte sehr selten auftreten und wir werden in Zukunft versuchen bessere Wege zu finden, ihn zu lösen. selectDepositTxWindow.select=Kautionstransaktion auswählen selectBaseCurrencyWindow.headline=Marktauswahl @@ -1684,7 +1832,7 @@ sendPrivateNotificationWindow.privateNotification=Private Benachrichtigung sendPrivateNotificationWindow.enterNotification=Benachrichtigung eingeben sendPrivateNotificationWindow.send=Private Benachrichtigung senden -showWalletDataWindow.walletData=Brieftasche-Daten +showWalletDataWindow.walletData=Wallet-Daten showWalletDataWindow.includePrivKeys=Private Schlüssel einbeziehen # We do not translate the tac because of the legal nature. We would need translations checked by lawyers @@ -1748,7 +1896,7 @@ popup.headline.error=Fehler popup.doNotShowAgain=Nicht erneut anzeigen popup.reportError.log=Protokolldatei öffnen popup.reportError.gitHub=Auf GitHub-Issue-Tracker melden -popup.reportError={0}\n\nUm uns bei der Verbesserung der Software zu helfen, erstellen Sie bitte einen Fehlerbericht auf unserem Issue-Tracker auf GitHub (https://github.com/bisq-network/bisq/issues).\nDie Fehlermeldung wird in die Zwischenablage kopiert, wenn Sie auf die Schaltflächen unten klicken.\nEs wird das Debuggen einfacher machen, wenn Sie die bisq.log Datei anfügen. +popup.reportError={0}\n\nUm uns bei der Verbesserung der Software zu helfen, erstellen Sie bitte einen Fehler-Bericht auf https://github.com/bisq-network/bisq/issues.\nDie Fehlermeldung wird in die Zwischenablage kopiert, wenn Sie auf einen der Knöpfe unten klicken.\nEs wird das Debuggen einfacher machen, wenn Sie die bisq.log Datei anfügen indem Sie "Logdatei öffnen" klicken, eine Kopie speichern und diese dem Fehler-Bericht anfügen. popup.error.tryRestart=Versuchen Sie bitte Ihre Anwendung neu zu starten und überprüfen Sie Ihre Netzwerkverbindung um zu sehen, ob Sie das Problem beheben können. popup.error.takeOfferRequestFailed=Es ist ein Fehler aufgetreten, als jemand versuchte eins Ihrer Angebote anzunehmen:\n{0} @@ -1756,7 +1904,7 @@ popup.error.takeOfferRequestFailed=Es ist ein Fehler aufgetreten, als jemand ver error.spvFileCorrupted=Beim Einlesen der SPV-Kettendatei ist ein Fehler aufgetreten.\nDie SPV-Kettendatei ist möglicherweise beschädigt.\n\nFehlermeldung: {0}\n\nMöchten Sie diese löschen und neu synchronisieren? error.deleteAddressEntryListFailed=Konnte AddressEntryList-Datei nicht löschen.\nFehler: {0} -popup.warning.walletNotInitialized=Die Brieftasche ist noch nicht initialisiert +popup.warning.walletNotInitialized=Die Wallet ist noch nicht initialisiert 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. @@ -1764,9 +1912,9 @@ popup.warning.cryptoTestFailed=Es scheint so, als würden Sie selbst kompilierte popup.warning.tradePeriod.halfReached=Ihr Handel mit der ID {0} hat die Hälfte der maximal erlaubten Handelsdauer erreicht und ist immer noch nicht abgeschlossen.\n\nDie Handelsdauer endet am {1}\n\nBitte überprüfen Sie den Status Ihres Handels unter \"Portfolio/Offene Händel\" für weitere Informationen. popup.warning.tradePeriod.ended=Ihr Handel mit der ID {0} hat die maximal erlaubte Handelsdauer erreicht und ist nicht abgeschlossen.\n\nDie Handelsdauer endete am {1}\n\nBitte überprüfen Sie den Status Ihres Handels unter \"Portfolio/Offene Händel\" um den Vermittler zu kontaktieren. popup.warning.noTradingAccountSetup.headline=Sie haben kein Handelskonto eingerichtet -popup.warning.noTradingAccountSetup.msg=Sie müssen ein nationales Währungs- oder Altcoin-Konto einrichten, bevor Sie ein Angebot erstellen können.\nMöchten Sie ein Konto einrichten? +popup.warning.noTradingAccountSetup.msg=Sie müssen ein nationales Währung- oder Altcoin-Konto einrichten, bevor Sie ein Angebot erstellen können.\nMöchten Sie ein Konto einrichten? popup.warning.noArbitratorsAvailable=Momentan sind keine Vermittler verfügbar. -popup.warning.notFullyConnected=Sie müssen warten, bis Sie vollständig mit dem Netzwerk verbunden sind.\nDas kann bis ungefähr zwei Minuten nach dem Start dauern. +popup.warning.notFullyConnected=Sie müssen warten, bis Sie vollständig mit dem Netzwerk verbunden sind.\nDas kann bis ungefähr 2 Minuten nach dem Start dauern. popup.warning.notSufficientConnectionsToBtcNetwork=Sie müssen warten, bis Sie wenigstens {0} Verbindungen zum Bitcoinnetzwerk haben. popup.warning.downloadNotComplete=Sie müssen warten bis der Download der fehlenden Bitcoinblöcke abgeschlossen ist. popup.warning.removeOffer=Sind Sie sicher, dass Sie das Angebot entfernen wollen?\nDie Erstellergebühr von {0} geht verloren, wenn Sie des Angebot entfernen. @@ -1774,9 +1922,9 @@ popup.warning.tooLargePercentageValue=Es kann kein Prozentsatz von 100% oder meh popup.warning.examplePercentageValue=Bitte geben sei einen Prozentsatz wie folgt ein \"5.4\" für 5.4% popup.warning.noPriceFeedAvailable=Es ist kein Marktpreis für diese Währung verfügbar. Sie können keinen auf Prozent basierenden Preis verwenden.\nBitte wählen Sie den Festpreis. popup.warning.sendMsgFailed=Das Senden der Nachricht an Ihren Handelspartner ist fehlgeschlagen.\nVersuchen Sie es bitte erneut und falls es weiter fehlschlägt, erstellen Sie bitte einen Fehlerbericht. -popup.warning.insufficientBtcFundsForBsqTx=Sie haben nicht genügend BTC-Gelder, um die Mining-Gebühr für diese Transaktion zu bezahlen.\nBitte finanzieren Sie Ihre BTC-Brieftasche.\nFehlende Gelder: {0} +popup.warning.insufficientBtcFundsForBsqTx=Sie haben nicht genügend BTC-Gelder, um die Mining-Gebühr für diese Transaktion zu bezahlen.\nBitte finanzieren Sie Ihre BTC-Wallet.\nFehlende Gelder: {0} -popup.warning.insufficientBsqFundsForBtcFeePayment=Sie haben nicht genügend BSQ-Gelder, um die Handels-Gebühr in BSQ zu bezahlen.\nSie können die Gebühr in BTC bezahlen, oder müssen Ihre BSQ-Brieftasche füllen. Sie können BSQ in Bisq kaufen.\n\nFehlende BSQ Gelder: {0} +popup.warning.insufficientBsqFundsForBtcFeePayment=Sie haben nicht genügend BSQ-Gelder, um die Handels-Gebühr in BSQ zu bezahlen.\nSie können die Gebühr in BTC bezahlen, oder müssen Ihre BSQ-Wallet füllen. Sie können BSQ in Bisq kaufen.\n\nFehlende BSQ Gelder: {0} popup.warning.noBsqFundsForBtcFeePayment=Ihre BSQ-Wallet hat keine ausreichenden Gelder, um die Handels-Gebühr in BSQ zu bezahlen. 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 eingesperrte Gelder von einem fehlgeschlagenen Handel.\nEingesperrtes Guthaben: {0} \nTx-Adresse der Kaution: {1}\nHandels-ID: {2}.\n\nBitte öffnen Sie ein Support-Ticket indem Sie den Handel unter "\"Ausstehende Händel\" auswählen und \"alt + o\" oder \"option + o\" drücken. @@ -1785,7 +1933,7 @@ popup.warning.nodeBanned=Einer der {0} Knoten wurde gebannt. Bitte starten Sie d popup.warning.priceRelay=Preisrelais popup.warning.seed=Seed -popup.info.securityDepositInfo=Um sicherzustellen, dass beide Händler dem Handelsprotokoll folgen, müssen diese eine Kaution zahlen.\n\nDie Kaution bleibt in Ihrer lokalen Brieftasche, bis das Angebot von einem anderen Händler angenommen wurde.\nSie wird Ihnen zurückerstattet, nachdem der Handel erfolgreich abgeschlossen wurde.\n\nBitte beachten Sie, dass Sie die Anwendung laufen lassen müssen, wenn Sie ein offenes Angebot haben.\nWenn ein anderer Händler Ihr Angebot annehmen möchte ist es notwendig, dass Ihre Anwendung online ist und reagieren kann.\nStellen Sie sicher, dass Sie den Ruhezustand deaktiviert haben, da dieser Ihren Client vom Netzwerk trennen würde (Der Ruhezustand des Monitors ist kein Problem). +popup.info.securityDepositInfo=Um sicherzustellen, dass beide Händler dem Handelsprotokoll folgen, müssen diese eine Kaution zahlen.\n\nDie Kaution bleibt in Ihrer lokalen Wallet, bis das Angebot von einem anderen Händler angenommen wurde.\nSie wird Ihnen zurückerstattet, nachdem der Handel erfolgreich abgeschlossen wurde.\n\nBitte beachten Sie, dass Sie die Anwendung laufen lassen müssen, wenn Sie ein offenes Angebot haben.\nWenn ein anderer Händler Ihr Angebot annehmen möchte ist es notwendig, dass Ihre Anwendung online ist und reagieren kann.\nStellen Sie sicher, dass Sie den Ruhezustand deaktiviert haben, da dieser Ihren Client vom Netzwerk trennen würde (Der Ruhezustand des Monitors ist kein Problem). 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 @@ -1795,7 +1943,7 @@ popup.info.shutDownWithOpenOffers=Bisq wird heruntergefahren, aber Sie haben off popup.privateNotification.headline=Wichtige private Benachrichtigung! popup.securityRecommendation.headline=Wichtige Sicherheitsempfehlung -popup.securityRecommendation.msg=Wir würden Sie gerne daran erinnern, sich zu überlegen, den Passwortschutz Ihrer Brieftasche zu verwenden, falls Sie diesen noch nicht aktiviert haben.\n\nEs wird außerdem dringend empfohlen, dass Sie die Brieftasche-Seed-Wörter aufschreiben. Diese Seed-Wörter sind wie ein Master-Passwort zum Wiederherstellen ihrer Bitcoin-Brieftasche.\nIm \"Brieftasche-Seed\"-Abschnitt finden Sie weitere Informationen.\n\nZusätzlich sollten Sie ein Backup des ganzen Anwendungsdatenordners im \"Backup\"-Abschnitt erstellen. +popup.securityRecommendation.msg=Wir würden Sie gerne daran erinnern, sich zu überlegen, den Passwortschutz Ihrer Wallet zu verwenden, falls Sie diesen noch nicht aktiviert haben.\n\nEs wird außerdem dringend empfohlen, dass Sie die Wallet-Seed-Wörter aufschreiben. Diese Seed-Wörter sind wie ein Master-Passwort zum Wiederherstellen ihrer Bitcoin-Wallet.\nIm \"Wallet-Seed\"-Abschnitt finden Sie weitere Informationen.\n\nZusätzlich sollten Sie ein Backup des ganzen Anwendungsdatenordners im \"Backup\"-Abschnitt erstellen. popup.bitcoinLocalhostNode.msg=Bisq hat einen lokal laufenden Bitcoin-Core-Knoten entdeckt (bei localhost),\nStellen Sie bitte sicher, dass dieser Knoten vollständig synchronisiert ist, bevor Sie Bisq starten und nicht im pruned Modus läuft. @@ -1807,8 +1955,8 @@ popup.attention.forTradeWithId=Der Handel mit der ID {0} benötigt Ihre Aufmerks popup.roundedFiatValues.headline=Neues Privatsphären Feature: Gerundete fiat Beträge popup.roundedFiatValues.msg=Um die Privatsphäre Ihres Handels zu erhöhen, wurde der {0} Betrag gerundet.\n\nAbhängig von der Version des Clients, werden Sie Beträge mit Nachkommastellen oder gerundet erhalten.\n\nBeide Beträge erfüllen von nun an das Handelsprotokoll.\n\nBeachten Sie auch, dass die BTC Beträge automatisch angepasst werden, um dem gerundeten fiat Betrag so genau wie möglich übereinzustimmen. -popup.info.multiplePaymentAccounts.headline=Multiple payment accounts available -popup.info.multiplePaymentAccounts.msg=You have multiple payment accounts available for this offer. Please make sure you've picked the right one. +popup.info.multiplePaymentAccounts.headline=Mehrere Zahlungskonten verfügbar +popup.info.multiplePaymentAccounts.msg=Für dieses Angebot stehen Ihnen mehrere Zahlungskonten zur Verfügung. Bitte stellen Sie sicher, dass Sie das richtige ausgewählt haben. #################################################################### @@ -1824,11 +1972,11 @@ notification.trade.paymentStarted=Der BTC-Käufer hat die Zahlung begonnen. notification.trade.selectTrade=Handel wählen notification.trade.peerOpenedDispute=Ihr Handelspartner hat ein/einen {0} geöffnet. notification.trade.disputeClosed=Der/Das {0} wurde geschlossen. -notification.walletUpdate.headline=Update der Handels-Brieftaschen -notification.walletUpdate.msg=Ihre Handels-Brieftasche ist ausreichend finanziert.\nBetrag: {0} -notification.takeOffer.walletUpdate.msg=Ihre Handels-Brieftasche wurde bereits durch eine früher versuchte Angebotsannahme ausreichend finanziert.\nBetrag: {0} +notification.walletUpdate.headline=Update der Handels-Wallets +notification.walletUpdate.msg=Ihre Handels-Wallet ist ausreichend finanziert.\nBetrag: {0} +notification.takeOffer.walletUpdate.msg=Ihre Handels-Wallet wurde bereits durch eine früher versuchte Angebotsannahme ausreichend finanziert.\nBetrag: {0} notification.tradeCompleted.headline=Handel abgeschlossen -notification.tradeCompleted.msg=Sie können Ihre Gelder jetzt auf eine externe Bitcoin-Brieftasche abheben oder an die Bisq-Brieftasche überweisen. +notification.tradeCompleted.msg=Sie können Ihre Gelder jetzt auf eine externe Bitcoin-Wallet abheben oder an die Bisq-Wallet überweisen. #################################################################### @@ -1846,7 +1994,7 @@ systemTray.tooltip=Bisq: Das dezentrale Handelsnetzwerk # GUI Util #################################################################### -guiUtil.miningFeeInfo=Bitte stellen Sie sicher, dass die Transaktionsgebühr Ihrer externen Brieftasche mindestens {0} Satoshi/Byte ist. Andernfalls kann die Handelstransaktion nicht bestätigt werden und der Handel würde in einem Konflikt enden. +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.accountExport.savedToPath=Handelskonten in Verzeichnis gespeichert:\n{0} guiUtil.accountExport.noAccountSetup=Sie haben kein Handelskonto zum Exportieren eingerichtet. @@ -1879,7 +2027,7 @@ table.placeholder.noData=Momentan sind keine Daten verfügbar peerInfoIcon.tooltip.tradePeer=Handelspartners peerInfoIcon.tooltip.maker=Erstellers -peerInfoIcon.tooltip.trade.traded={0} Onion-Adresse: {1}\nSie haben schon {2} mal mit diesem Partner gehandelt.\n{3} +peerInfoIcon.tooltip.trade.traded={0} Onion-Adresse: {1}\nSie haben schon {2} Mal(e) mit diesem Partner gehandelt.\n{3} peerInfoIcon.tooltip.trade.notTraded={0} Onion-Adresse: {1}\nSie haben noch nicht mit diesem Partner gehandelt.\n{2} peerInfoIcon.tooltip.age=Zahlungskonto vor {0} erstellt. peerInfoIcon.tooltip.unknownAge=Alter des Zahlungskontos unbekannt. @@ -1890,7 +2038,7 @@ tooltip.openBlockchainForTx=Externen Blockchain-Explorer für Transaktion öffne confidence.unknown=Unbekannter Transaktionsstatus confidence.seen=Von {0} Peer(s) gesehen / 0 Bestätigungen -confidence.confirmed=In {0} Blöcken bestätigt +confidence.confirmed=In {0} Blöcken(Block) bestätigt confidence.invalid=Die Transaktion ist ungültig peerInfo.title=Peer-Infos @@ -1900,10 +2048,10 @@ peerInfo.setTag=Markierung für diesen Peer setzen peerInfo.age=Alter des Zahlungskontos peerInfo.unknownAge=Alter unbekannt -addressTextField.openWallet=Ihre Standard-Bitcoin-Brieftasche öffnen +addressTextField.openWallet=Ihre Standard-Bitcoin-Wallet öffnen addressTextField.copyToClipboard=Adresse in Zwischenablage kopieren addressTextField.addressCopiedToClipboard=Die Adresse wurde in die Zwischenablage kopiert -addressTextField.openWallet.failed=Öffnen einer Bitcoin-Brieftasche-Standardanwendung ist fehlgeschlagen. Haben Sie möglicherweise keine installiert? +addressTextField.openWallet.failed=Öffnen einer Bitcoin-Wallet-Standardanwendung ist fehlgeschlagen. Haben Sie möglicherweise keine installiert? peerInfoIcon.tooltip={0}\nMarkierung: {1} @@ -1916,7 +2064,7 @@ txIdTextField.blockExplorerIcon.tooltip=Einen Blockchain-Explorer mit dieser Tra #################################################################### navigation.account=\"Konto\" -navigation.account.walletSeed=\"Konto/Brieftasche-Seed\" +navigation.account.walletSeed=\"Konto/Wallet-Seed\" navigation.funds.availableForWithdrawal=\"Gelder/Gelder senden\" navigation.portfolio.myOpenOffers=\"Portfolio/Meine offenen Angebote\" navigation.portfolio.pending=\"Portfolio/Offene Händel\" @@ -1925,7 +2073,7 @@ navigation.funds.depositFunds=\"Gelder/Gelder erhalten\" navigation.settings.preferences=\"Einstellungen/Voreinstellungen\" navigation.funds.transactions=\"Gelder/Transaktionen\" navigation.support=\"Support\" -navigation.dao.wallet.receive=\"DAO/BSQ-Brieftasche/Erhalten\" +navigation.dao.wallet.receive=\"DAO/BSQ-Wallet/Erhalten\" #################################################################### @@ -1955,6 +2103,10 @@ BTC_MAINNET=Bitcoin-Hauptnetzwerk BTC_TESTNET=Bitcoin-Testnetzwerk # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin-Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin-DAO-Testnetzwerk +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanetz (Bitcoin Hauptnetz) time.year=Jahr time.month=Monat @@ -1976,27 +2128,27 @@ password.enterPassword=Passwort eingeben password.confirmPassword=Passwort bestätigen password.tooLong=Das Passwort muss aus weniger als 500 Zeichen bestehen. password.deriveKey=Schlüssel aus Passwort ableiten -password.walletDecrypted=Die Brieftasche wurde erfolgreich entschlüsselt und der Passwortschutz entfernt. +password.walletDecrypted=Die Wallet wurde erfolgreich entschlüsselt und der Passwortschutz entfernt. password.wrongPw=Sie haben das falsche Passwort eingegeben.\n\nVersuchen Sie bitte Ihr Passwort erneut einzugeben, wobei Sie dies vorsichtig auf Tipp- und Rechtschreibfehler überprüfen sollten. -password.walletEncrypted=Die Brieftasche wurde erfolgreich verschlüsselt und der Passwortschutz aktiviert. -password.walletEncryptionFailed=Brieftasche Passwort konnte nicht gesetzt werden. Sie haben vielleicht Seed-Wörter importiert, die nicht mit der Brieftasche-Datenbank übereinstimmen. Bitte kontaktieren Sie die Entwickler im Bisq-Forum. -password.passwordsDoNotMatch=Die beiden eingegebenen Passwörter stimmen nicht überein. +password.walletEncrypted=Die Wallet wurde erfolgreich verschlüsselt und der Passwortschutz aktiviert. +password.walletEncryptionFailed=Wallet Passwort konnte nicht gesetzt werden. Sie haben vielleicht Seed-Wörter importiert, die nicht mit der Wallet-Datenbank übereinstimmen. Bitte kontaktieren Sie die Entwickler im Bisq-Forum. +password.passwordsDoNotMatch=Die 2 eingegebenen Passwörter stimmen nicht überein. password.forgotPassword=Passwort vergessen? -password.backupReminder=Beachten Sie, dass wenn Sie ein Passwort setzen, alle automatisch erstellten Backups der unverschlüsselten Brieftasche gelöscht werden.\n\nEs wird dringend empfohlen, ein Backup des Anwendungsverzeichnisses zu erstellen und die Seed-Wörter aufzuschreiben, bevor Sie ein Passwort setzen! +password.backupReminder=Beachten Sie, dass wenn Sie ein Passwort setzen, alle automatisch erstellten Backups der unverschlüsselten Wallet gelöscht werden.\n\nEs wird dringend empfohlen, ein Backup des Anwendungsverzeichnisses zu erstellen und die Seed-Wörter aufzuschreiben, bevor Sie ein Passwort setzen! password.backupWasDone=Ich habe schon ein Backup erstellt -seed.seedWords=Seed-Wörter der Brieftasche -seed.enterSeedWords=Seed-Wörter der Brieftasche eingeben -seed.date=Brieftaschen-Datum -seed.restore.title=Brieftaschen aus Seed-Wörtern wiederherstellen -seed.restore=Brieftaschen wiederherstellen +seed.seedWords=Seed-Wörter der Wallet +seed.enterSeedWords=Seed-Wörter der Wallet eingeben +seed.date=Wallets-Datum +seed.restore.title=Wallets aus Seed-Wörtern wiederherstellen +seed.restore=Wallets wiederherstellen seed.creationDate=Erstellungsdatum -seed.warn.walletNotEmpty.msg=Ihre Bitcoin-Brieftasche ist nicht leer.\n\nSie müssen diese Brieftasche leeren, bevor Sie versuchen, eine ältere Brieftasche wiederherzustellen, da das Mischen von Brieftaschen zu ungültigen Backups führen kann.\n\nBitte schließen Sie Ihre Händel ab, schließen Sie all Ihre offenen Angebote und gehen Sie in den Abschnitt \"Gelder\", um Ihre Bitcoins abzuheben.\nSollten Sie nicht auf Ihre Bitcoins zugreifen können, können Sie das Notfallwerkzeug nutzen, um Ihre Brieftasche zu leeren.\nUm das Notfallwerkzeug zu öffnen, drücken Sie \"alt + e\" oder \"option + e\". +seed.warn.walletNotEmpty.msg=Ihre Bitcoin-Wallet ist nicht leer.\n\nSie müssen diese Wallet leeren, bevor Sie versuchen, eine ältere Wallet wiederherzustellen, da das Mischen von Wallets zu ungültigen Backups führen kann.\n\nBitte schließen Sie Ihre Händel ab, schließen Sie all Ihre offenen Angebote und gehen Sie in den Abschnitt \"Gelder\", um Ihre Bitcoins abzuheben.\nSollten Sie nicht auf Ihre Bitcoins zugreifen können, können Sie das Notfallwerkzeug nutzen, um Ihre Wallet zu leeren.\nUm das Notfallwerkzeug zu öffnen, drücken Sie \"alt + e\" oder \"option + e\". seed.warn.walletNotEmpty.restore=Ich möchte trotzdem wiederherstellen -seed.warn.walletNotEmpty.emptyWallet=Ich werde meine Brieftaschen erst leeren -seed.warn.notEncryptedAnymore=Ihre Brieftaschen sind verschlüsselt.\n\nNach einer Wiederherstellung werden die Brieftaschen nicht mehr verschlüsselt sein und Sie werden ein neues Passwort festlegen müssen.\n\nMöchten Sie fortfahren? -seed.restore.success=Brieftaschen mit den neuen Seed-Wörtern erfolgreich wiederhergestellt.\n\nSie müssen die Anwendung herunterfahren und neu starten. -seed.restore.error=Beim Wiederherstellen der Brieftaschen mit den Seed-Wörtern ist ein Fehler aufgetreten.{0} +seed.warn.walletNotEmpty.emptyWallet=Ich werde meine Wallets erst leeren +seed.warn.notEncryptedAnymore=Ihre Wallets sind verschlüsselt.\n\nNach einer Wiederherstellung werden die Wallets nicht mehr verschlüsselt sein und Sie werden ein neues Passwort festlegen müssen.\n\nMöchten Sie fortfahren? +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} #################################################################### @@ -2025,17 +2177,20 @@ payment.country=Land payment.extras=Besondere Erfordernisse payment.email.mobile=E-Mail oder Telefonnummer payment.altcoin.address=Altcoin-Adresse +payment.altcoin.tradeInstantCheckbox=Handeln sie schnell (innerhalb 1 Stunde) mit diesem Altcoin +payment.altcoin.tradeInstant.popup=Für "Schnelles Handeln" müssen beide Handelspartner online sein, um den Handel innerhalb 1 Stunde abschließen zu können.\n\nFalls sie offene Angebote haben jedoch nicht verfügbar sind, deaktivieren sie bitte diese Angebote unter 'Portfolio'. payment.altcoin=Altcoin payment.select.altcoin=Altcoin wählen oder suchen payment.secret=Geheimfrage payment.answer=Antwort -payment.wallet=Brieftaschen-ID +payment.wallet=Wallets-ID payment.uphold.accountId=Nutzername oder Email oder Telefonnr. payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=E-Mail oder Telefonnummer payment.venmo.venmoUserName=Venmo Nutzername payment.popmoney.accountId=E-Mail oder Telefonnummer -payment.revolut.email=E-Mail oder Telefonnummer +payment.revolut.email=E-Mail +payment.revolut.phoneNr=Registrierte Telefonnummer payment.promptPay.promptPayId=Personalausweis/Steuernummer oder Telefonnr. payment.supportedCurrencies=Unterstützte Währungen payment.limitations=Einschränkungen @@ -2082,6 +2237,10 @@ payment.limits.info=Beachten Sie bitte, dass alle Banküberweisungen ein gewisse payment.cashDeposit.info=Bitte bestätigen Sie, dass Ihre Bank Bareinzahlungen in Konten von anderen Personen erlaubt. Zum Beispiel werden diese Einzahlungen bei der Bank of America und Wells Fargo nicht mehr erlaubt. +payment.revolut.info=Bitte vergewissern sie sich, dass die Telefonnummer die sie verwendet haben auch bei Revolut registriert ist. Ansonsten kann der BTC-Käufer ihnen den Betrag nicht überweisen. + +payment.usPostalMoneyOrder.info=Zahlungsanweisungen sind eine der privateren, auf Bisq verfügbaren, Kaufmethoden.\n\nBitte beachten Sie potenziell erhöhte Risiken die mit ihrer Nutzung verbunden sind. Bisq übernimmt keine Haftung, fals eine gesendete Zahlungsanweisung gestohlen wird, und der Vermittler wird in solchen Fällen die BTC an den Sender der Zahlungsanweisung vergeben, falls dieser Tracking-Informationen und Quittungen vorlegen kann. Es kann ratsam sein, dass der Sender den Namen des BTC-Verkäufers in die Zahlungsanweisung schreibt, um das Risiko zu minimieren, dass die Zahlungsanweisung von jemand anders eingelöst wird. + payment.f2f.contact=Kontaktinformationen payment.f2f.contact.prompt=Wie möchten Sie vom Handeslpartner kontaktiert werden? (E-Mailadresse, Telefonnummer,...) payment.f2f.city=Stadt für ein "Angesicht zu Angesicht" Treffen @@ -2128,16 +2287,10 @@ F2F_SHORT=A2A # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altcoins PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins schnell +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Altcoins PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins schnell + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,7 +2416,8 @@ validation.nationalAccountId={0} muss aus {1} Zahlen bestehen. validation.invalidInput=Ungültige Eingabe: {0} validation.accountNrFormat=Die Kontonummer muss folgendes Format haben: {0} validation.altcoin.wrongStructure=Die Adressvalidierung ist fehlgeschlagen, da diese nicht mit der Struktur einer {0}-Adresse übereinstimmt. -validation.altcoin.zAddressesNotSupported=Die ZEC-Adresse muss mit einem t beginnen. Adressen, die mit z beginnen werden nicht unterstützt. +validation.altcoin.ltz.zAddressesNotSupported=Die LTZ-Adressen müssen mit einem L beginnen. Adressen, die mit z beginnen werden nicht unterstützt. +validation.altcoin.zAddressesNotSupported=Die ZEC-Adressen müssen mit einem t beginnen. Adressen, die mit z beginnen werden nicht unterstützt. validation.altcoin.invalidAddress=Die Adresse ist keine gültige {0}-Adresse! {1} validation.bic.invalidLength=Die Eingabelänge ist weder 8 noch 11 validation.bic.letters=Bank- und Ländercode müssen aus Buchstaben bestehen @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Nur Buchstaben, Zahlen, Leerzeichen validation.interacETransfer.invalidAnswer=Muss ein Wort mit Buchstaben, Zahlen und/oder dem Symbol - sein validation.inputTooLarge=Eingabe darf nicht größer als {0} sein validation.inputTooSmall=Eingabe muss größer als {0} sein +validation.inputToBeAtLeast=Eingabe muss mindestens {0} sein validation.amountBelowDust=Beträge unter dem Dust-Limit von {0} sind nicht erlaubt. validation.length=Die Länge muss zwischen {0} und {1} sein validation.pattern=Die Eingabe muss im Format {0} sein validation.noHexString=Die Eingabe ist nicht im HEX-Format. -validation.advancedCash.invalidFormat=Gültige E-Mail-Adresse oder Brieftaschen ID vom Format "X000000000000" benötigt +validation.advancedCash.invalidFormat=Gültige E-Mail-Adresse oder Wallets ID vom Format "X000000000000" benötigt +validation.invalidUrl=Dies ist keine gültige URL +validation.mustBeDifferent=Ihre Eingabe muss vom momentanen Wert abweichen +validation.cannotBeChanged=Parameter kann nicht geändert werden +validation.numberFormatException=Zahlenformat Ausnahme {0} +validation.mustNotBeNegative=Eingabe darf nicht negativ sein diff --git a/core/src/main/resources/i18n/displayStrings_el.properties b/core/src/main/resources/i18n/displayStrings_el.properties index 7298a19b7d5..557cd909b61 100644 --- a/core/src/main/resources/i18n/displayStrings_el.properties +++ b/core/src/main/resources/i18n/displayStrings_el.properties @@ -104,7 +104,7 @@ shared.belowInPercent=Μικρότερο % από τιμή αγοράς shared.aboveInPercent=Μεγαλύτερο % από τιμή αγοράς shared.enterPercentageValue=Εισήγαγε % αξίας shared.OR=Ή -shared.notEnoughFunds=Δεν έχεις επαρκή κεφάλαια στο Bisq πορτοφόλι σου.\nΧρειάζεσαι {0}, αλλά έχεις μόνο {1}.\n\nΧρηματοδότησε τη συναλλαγή από ένα εξωτερικό Bitcoin πορτοφόλι ή χρηματοδότησε το Bisq πορτοφόλι σου στην καρτέλα \"Κεφάλαια/Λήψη Κεφαλαίων\". +shared.notEnoughFunds=Δεν έχεις επαρκή κεφάλαια στο Bisq πορτοφόλι σου.\nΧρειάζεσαι {0} αλλά έχεις μόνο {1} στο Bisq Πορτοφόλι σου.\n\nΧρηματοδότησε τη συναλλαγή από ένα εξωτερικό Bitcoin πορτοφόλι ή χρηματοδότησε το Bisq πορτοφόλι σου στην καρτέλα \"Κεφάλαια/Λήψη Κεφαλαίων\". shared.waitingForFunds=Αναμονή για κεφάλαια... shared.depositTransactionId=Ταυτότητα κατάθεσης shared.TheBTCBuyer=Ο αγοραστής BTC @@ -113,12 +113,13 @@ shared.reasonForPayment=Αιτία πληρωμής shared.sendingConfirmation=Αποστολή επιβεβαίωσης... shared.sendingConfirmationAgain=Ξαναστείλε επιβεβαίωση shared.exportCSV=Export to csv +shared.exportJSON=Export to JSON shared.noDateAvailable=Δεν υπάρχουν διαθέσιμα δεδομένα shared.arbitratorsFee=Αμοιβή διαμεσολαβητή shared.noDetailsAvailable=Δεν υπάρχουν διαθέσιμα στοιχεία shared.notUsedYet=Αχρησιμοποίητο προς το παρόν shared.date=Ημερομηνία -shared.sendFundsDetailsWithFee=Αποστολή: {0}\nΑπό διεύθυνση αποστολής: {1}\nΠρος διεύθυνση παραλαβής: {2}\nΑπαιτούμενη προμήθεια συναλλαγής: {3} ({4} satoshis/byte)\nΜέγεθος συναλλαγής: {5} Kb\n\nΟ παραλήπτης θα λάβει: {6}\n\nΕίσαι σίγουρος πως θέλεις να κάνεις ανάληψη αυτού του ποσού; +shared.sendFundsDetailsWithFee=Αποστολή: {0}\nΑπό διεύθυνση: {1}\nΠρος διεύθυνση παραλαβής: {2}\nΑπαιτούμενη προμήθεια συναλλαγής: {3} ({4} satoshis/byte)\nΜέγεθος συναλλαγής: {5} Kb\n\nΟ παραλήπτης θα λάβει: {6}\n\nΕίσαι σίγουρος πως θέλεις να κάνεις ανάληψη αυτού του ποσού; shared.copyToClipboard=Αντιγραφή στο πρόχειρο shared.language=Γλώσσα shared.country=Χώρα @@ -137,7 +138,7 @@ shared.saveNewAccount=Αποθήκευση νέου λογαριασμού shared.selectedAccount=Επιλεγμένος λογαριασμός shared.deleteAccount=Διαγραφή λογαριασμού shared.errorMessageInline=\nΜήνυμα σφάλματος: {0} -shared.errorMessage=Error message +shared.errorMessage=Μήνυμα σφάλματος shared.information=Πληροφορίες shared.name=Όνομα shared.id=Ταυτότητα @@ -152,19 +153,19 @@ shared.seller=πωλητής shared.buyer=αγοραστής shared.allEuroCountries=Όλες οι χώρες ευρωζώνης shared.acceptedTakerCountries=Αποδεκτές χώρες του/της taker -shared.arbitrator=Selected arbitrator +shared.arbitrator=Επιλεγμένος διαμεσολαβητής shared.tradePrice=Τιμή συναλλαγής shared.tradeAmount=Ποσό συναλλαγής shared.tradeVolume=Όγκος συναλλαγής shared.invalidKey=Το κλειδί που εισήγαγες δεν είναι σωστό. -shared.enterPrivKey=Enter private key to unlock -shared.makerFeeTxId=Maker fee transaction ID -shared.takerFeeTxId=Taker fee transaction ID -shared.payoutTxId=Payout transaction ID -shared.contractAsJson=Contract in JSON format +shared.enterPrivKey=Εισήγαγε προσωπικό κλειδί για ξεκλείδωμα +shared.makerFeeTxId=Ταυτότητα συναλλαγής προμήθειας Maker +shared.takerFeeTxId=Ταυτότητα συναλλαγής προμήθειας Taker +shared.payoutTxId=Ταυτότητα συναλλαγής αποπληρωμής +shared.contractAsJson=Συμβόλαιο σε μορφή JSON shared.viewContractAsJson=Συμβόλαιο σε μορφή JSON shared.contract.title=Συμβόλαιο συναλλαγής με ταυτότητα: {0} -shared.paymentDetails=BTC {0} payment details +shared.paymentDetails=Στοιχεία πληρωμής BTC {0} shared.securityDeposit=Ποσό εγγύησης shared.yourSecurityDeposit=Ποσό εγγύησής σου shared.contract=Συμβόλαιο @@ -172,30 +173,32 @@ shared.messageArrived=Μήνυμα αφίχθη. shared.messageStoredInMailbox=Το μήνυμα αποθηκεύτηκε στο γραμματοκιβώτιο. shared.messageSendingFailed=Αποστολή μηνύματος απέτυχε. Σφάλμα: {0} shared.unlock=Ξεκλείδωσε -shared.toReceive=to receive -shared.toSpend=to spend +shared.toReceive=προς λήψη +shared.toSpend=προς δαπάνη shared.btcAmount=Ποσό BTC -shared.yourLanguage=Your languages +shared.yourLanguage=Οι γλώσσες σου shared.addLanguage=Πρόσθεσε γλώσσα shared.total=Σύνολο -shared.totalsNeeded=Funds needed -shared.tradeWalletAddress=Trade wallet address -shared.tradeWalletBalance=Trade wallet balance +shared.totalsNeeded=Απαιτούμενα κεφάλαια +shared.tradeWalletAddress=Διεύθυνση πορτοφολιού συναλλαγής +shared.tradeWalletBalance=Υπόλοιπο πορτοφολιού συναλλαγής shared.makerTxFee=Maker: {0} shared.takerTxFee=Taker: {0} shared.securityDepositBox.description=Ποσό εγγύησης για BTC {0} shared.iConfirm=Επιβεβαιώνω shared.tradingFeeInBsqInfo=χρήση ισοδύναμου με {0} ως αμοιβή εξόρυξης shared.openURL=Άνοιξε {0} -shared.fiat=Fiat -shared.crypto=Crypto -shared.all=All -shared.edit=Edit -shared.advancedOptions=Advanced options +shared.fiat=Νομίσματα fiat +shared.crypto=Κρυπτονομίσματα +shared.all=Όλα +shared.edit=Διόρθωση +shared.advancedOptions=Προχωρημένες επιλογές shared.interval=Interval -shared.actions=Actions -shared.buyerUpperCase=Buyer -shared.sellerUpperCase=Seller +shared.actions=Ενέργειες +shared.buyerUpperCase=Αγοραστής +shared.sellerUpperCase=Πωλητής +shared.new=ΝΕΟ +shared.blindVoteTxId=Blind vote transaction ID #################################################################### # UI views @@ -215,9 +218,9 @@ mainView.menu.settings=Ρυθμίσεις mainView.menu.account=Λογαριασμός mainView.menu.dao=DAO -mainView.marketPrice.provider=Price by -mainView.marketPrice.label=Market price -mainView.marketPriceWithProvider.label=Market price by {0} +mainView.marketPrice.provider=Τιμή από: +mainView.marketPrice.label=Τιμή αγορών +mainView.marketPriceWithProvider.label=Τιμή αγορών από {0} mainView.marketPrice.bisqInternalPrice=Τιμή τελευταίας συναλλαγής Bisq mainView.marketPrice.tooltip.bisqInternalPrice=Δεν υπάρχει διαθέσιμη τιμή αγοράς από εξωτερική πηγή.\nΗ εμφανιζόμενη τιμή είναι η τελευταία τιμή συναλλαγής αυτού του νομίσματος στο Bisq. mainView.marketPrice.tooltip=Η τιμή αγοράς παρέχεται από το {0}{1}\nΤελευταία ενημέρωση: {2}\nΚόμβος URL παρόχου τιμής: {3} @@ -225,15 +228,16 @@ mainView.marketPrice.tooltip.altcoinExtra=Εάν το altcoin δεν διατί mainView.balance.available=Διαθέσιμο υπόλοιπο mainView.balance.reserved=Δεσμευμένα σε προσφορές mainView.balance.locked=Κλειδωμένα σε συναλλαγές -mainView.balance.reserved.short=Reserved -mainView.balance.locked.short=Locked +mainView.balance.reserved.short=Δεσμευμένα +mainView.balance.locked.short=Κλειδωμένα mainView.footer.usingTor=(με χρήση Tor) mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo=Bitcoin network peers: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Έναρξη -mainView.footer.btcInfo.synchronizedWith=συγχρονισμένο με -mainView.footer.btcInfo.connectingTo=σύνδεση με +mainView.footer.btcInfo.initializing=Σύνδεση με δίκτυο Bitcoin +mainView.footer.bsqInfo.synchronizing=/ Συγχρονισμός DAO +mainView.footer.btcInfo.synchronizedWith=Συγχρονισμένο με +mainView.footer.btcInfo.connectingTo=Σύνδεση με mainView.footer.btcInfo.connectionFailed=σύνδεση απέτυχε mainView.footer.p2pInfo=P2P network peers: {0} @@ -299,8 +303,8 @@ market.trades.tooltip.candle.date=Ημερομηνία: offerbook.createOffer=Δημιουργία προσφοράς offerbook.takeOffer=Αποδοχή προσφοράς -offerbook.takeOfferToBuy=Take offer to buy {0} -offerbook.takeOfferToSell=Take offer to sell {0} +offerbook.takeOfferToBuy=Αποδοχή προσφοράς αγοράς {0} +offerbook.takeOfferToSell=Αποδοχή προσφοράς πώλησης {0} offerbook.trader=Συναλλασσόμενος offerbook.offerersBankId=Ταυτότητα τράπεζας Maker (BIC/SWIFT): {0} offerbook.offerersBankName=Όνομα τράπεζας Maker: {0} @@ -308,7 +312,7 @@ offerbook.offerersBankSeat=Χώρα έδρας τράπεζας του Maker: {0 offerbook.offerersAcceptedBankSeatsEuro=Αποδεκτές χώρες έδρας τράπεζας (Taker): Σύνολο χωρών ευρωζώνης offerbook.offerersAcceptedBankSeats=Αποδεκτές χώρες έδρας τράπεζας (Taker):\n{0} offerbook.availableOffers=Διαθέσιμες προσφορές -offerbook.filterByCurrency=Filter by currency +offerbook.filterByCurrency=Φιλτράρισμα κατά νόμισμα offerbook.filterByPaymentMethod=Φιλτράρισμα κατά μέθοδο πληρωμής offerbook.nrOffers=Πλήθος προσφορών: {0} @@ -349,7 +353,7 @@ offerbook.info.buyBelowMarketPrice=Θα πληρώσεις {0} λιγότερα offerbook.info.buyAtFixedPrice=Θα αγοράσεις σε αυτή την καθορισμένη τιμή. offerbook.info.sellAtFixedPrice=Θα πουλήσεις σε αυτή την καθορισμένη τιμή. offerbook.info.noArbitrationInUserLanguage=Σε περίπτωση διαφωνίας, παρακαλούμε να σημειώσετε ότι η διαιτησία για αυτή την προσφορά θα διεκπεραιωθεί στο {0}. Η γλώσσα είναι αυτήν τη στιγμή ρυθμισμένη στο {1}.\n -offerbook.info.roundedFiatVolume=The amount was rounded to increase the privacy of your trade. +offerbook.info.roundedFiatVolume=Το ποσό στρογγυλοποιήθηκε για να αυξηθεί το απόρρητο της συναλλαγής σου. #################################################################### # Offerbook / Create offer @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=Ποσό BTC προς {0} createOffer.amountPriceBox.buy.volumeDescription=Ποσό σε {0} προς δαπάνη createOffer.amountPriceBox.sell.volumeDescription=Ποσό σε {0} προς λήψη createOffer.amountPriceBox.minAmountDescription=Ελάχιστο ποσό BTC -createOffer.securityDeposit.prompt=Ποσό εγγύησης σε BTC +createOffer.securityDeposit.prompt=Ποσό εγγύησης createOffer.fundsBox.title=Χρηματοδότησε την προσφορά σου. createOffer.fundsBox.offerFee=Προμήθεια συναλλαγής -createOffer.fundsBox.networkFee=Mining fee +createOffer.fundsBox.networkFee=Αμοιβή εξόρυξης createOffer.fundsBox.placeOfferSpinnerInfo=Κοινοποίηση προσφοράς σε εξέλιξη... createOffer.fundsBox.paymentLabel=Συναλλαγή Bisq με ταυτότητα {0} createOffer.fundsBox.fundsStructure=({0} ποσό εγγύησης, {1} προμήθεια συναλλαγής, {2} αμοιβή εξόρυξης) +createOffer.fundsBox.fundsStructure.BSQ=({0} ποσό εγγύησης, {1} αμοιβή εξόρυξης) + {2} προμήθεια συναλλαγής createOffer.success.headline=Η προσφορά σου έχει κοινοποιηθεί createOffer.success.info=Μπορείς να διαχειριστείς τις ανοιχτές προσφορές σου στο \"Χαρτοφυλάκιο/Οι τρέχουσες προσφορές μου\". createOffer.info.sellAtMarketPrice=Θα πουλάς πάντα στην τρέχουσα τιμή αγοράς, καθώς η τιμή της προσφοράς σου θα ενημερώνεται συνεχώς. @@ -378,8 +383,8 @@ createOffer.info.buyBelowMarketPrice=Θα πληρώνεις {0}% λιγότερ createOffer.warning.sellBelowMarketPrice=Θα λαμβάνεις {0}% λιγότερο από την τρέχουσα τιμή αγοράς, καθώς η τιμή της προσφοράς σου θα ενημερώνεται συνεχώς. createOffer.warning.buyAboveMarketPrice=Θα πληρώνεις {0}% περισσότερο από την τρέχουσα τιμή αγοράς, καθώς η τιμή της προσφοράς σου θα ενημερώνεται συνεχώς. createOffer.tradeFee.descriptionBTCOnly=Προμήθεια συναλλαγής -createOffer.tradeFee.descriptionBSQEnabled=Select trade fee currency -createOffer.tradeFee.fiatAndPercent=≈ {0} / {1} of trade amount +createOffer.tradeFee.descriptionBSQEnabled=Επίλεξε νόμισμα προμήθειας συναλλαγής +createOffer.tradeFee.fiatAndPercent=≈ {0} / {1} ποσού συναλλαγής # new entries createOffer.placeOfferButton=Ανασκόπηση: Τοποθέτησε προσφορά σε {0} bitcoin @@ -387,6 +392,7 @@ createOffer.alreadyFunded=Είχες ήδη χρηματοδοτήσει αυτ createOffer.createOfferFundWalletInfo.headline=Χρηματοδότησε την προσφορά σου. # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Ποσό συναλλαγής: {0}\n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} και {1} createOffer.createOfferFundWalletInfo.msg=Πρέπει να καταθέσεις {0} για αυτή την προσφορά.\n\nΤα κεφάλαια αποθηκεύονται στο τοπικό πορτοφόλι σου και κλειδώνονται στη διεύθυνση καταθέσεων πολλαπλών υπογραφών (multisig) μόλις κάποιος δεχτεί την προσφορά σου.\n\nΤο ποσό είναι το σύνολο των:\n{1}- Ποσό εγγύησης: {2}\n- Προμήθεια συναλλαγής: {3}\n- Αμοιβή εξόρυξης: {4}\n\nΜπορείς να επιλέξεις ανάμεσα σε δύο επιλογές για να χρηματοδοτήσεις τη συναλλαγή σου:\n- Χρήση του Bisq πορτοφολιού σου (βολικό, αλλά με πιθανή συσχέτιση των συναλλαγών), ή\n- Μεταφορά από ένα εξωτερικό πορτοφόλι (πιθανώς πιο εχέμυθη επιλογή)\n\nΌλες οι λεπτομέρειες και οι επιλογές χρηματοδότησης θα εμφανιστούν μόλις κλείσεις αυτό το παράθυρο. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=Η τιμή που εισήγαγες είν createOffer.changePrice=Άλλαξε την τιμή createOffer.tac=Τοποθετώντας αυτή την προσφορά συμφωνώ να συναλλαχθώ με οποιονδήποτε ικανοποιεί τις συνθήκες που καθορίζονται σε αυτό το παράθυρο. createOffer.currencyForFee=Προμήθεια συναλλαγής -createOffer.setDeposit=Καθορισμός ποσού εγγύησης αγοραστή +createOffer.setDeposit=Καθορισμός ποσού εγγύησης αγοραστή (%) +createOffer.setDepositAsBuyer=Καθορισμός δικού μου ποσού εγγύησης ως αγοραστής (%) +createOffer.securityDepositInfo=Το ποσό εγγύησης του αγοραστή θα είναι {0} +createOffer.securityDepositInfoAsBuyer=Το δικό σου ποσό εγγύησης ως αγοραστής θα είναι {0} #################################################################### @@ -422,9 +431,9 @@ takeOffer.validation.amountLargerThanOfferAmount=Το ποσό δεν μπορε takeOffer.validation.amountLargerThanOfferAmountMinusFee=Το ποσό που εισήγαγες θα δημιουργήσει ποσότητα BTC μηδαμινής αξίας για τον πωλητή. takeOffer.fundsBox.title=Χρηματοδότησε τη συναλλαγή σου takeOffer.fundsBox.isOfferAvailable=Έλεγχος διαθεσιμότητας προσφοράς... -takeOffer.fundsBox.tradeAmount=Amount to sell +takeOffer.fundsBox.tradeAmount=Ποσό προς πώληση takeOffer.fundsBox.offerFee=Προμήθεια συναλλαγής -takeOffer.fundsBox.networkFee=Total mining fees +takeOffer.fundsBox.networkFee=Σύνολο αμοιβών εξόρυξης takeOffer.fundsBox.takeOfferSpinnerInfo=Αποδοχή προσφοράς σε εξέλιξη... takeOffer.fundsBox.paymentLabel=Συναλλαγή Bisq με ταυτότητα {0} takeOffer.fundsBox.fundsStructure=({0} ποσό εγγύησης, {1} προμήθεια συναλλαγής, {2} αμοιβή εξόρυξης) @@ -516,9 +525,9 @@ portfolio.pending.step2_buyer.postal=Στείλε {0} μέσω \"US Postal Money portfolio.pending.step2_buyer.bank=Συνδέσου στον e-banking λογαριασμό σου και πλήρωσε {0} στον BTC πωλητή.\n\n portfolio.pending.step2_buyer.f2f=Επικοινώνησε με τον πωλητή BTC μέσω του διαθέσιμου αριθμού και κανόνισε συνάντηση για να πληρώσεις {0}.\n\n portfolio.pending.step2_buyer.startPaymentUsing=Ξεκίνα την πληρωμή με χρήση {0} -portfolio.pending.step2_buyer.amountToTransfer=Amount to transfer -portfolio.pending.step2_buyer.sellersAddress=Seller''s {0} address -portfolio.pending.step2_buyer.buyerAccount=Your payment account to be used +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=Δεν έχεις κάνει την πληρωμή {0}!\nΤονίζουμε πως η συναλλαγή θα πρέπει να έχει ολοκληρωθεί μέχρι {1}, διαφορετικά θα διερευνηθεί από τον διαμεσολαβητή. portfolio.pending.step2_buyer.openForDispute=Δεν ολοκλήρωσες την πληρωμή σου!\nΗ μέγιστη χρονική περίοδος για αυτή τη συναλλαγή παρήλθε.\n\nΕπικοινώνησε με τον διαμεσολαβητή ώστε να επιλυθεί η διένεξη. @@ -529,16 +538,16 @@ portfolio.pending.step2_buyer.moneyGramMTCNInfo.msg=Πρέπει να στείλ portfolio.pending.step2_buyer.westernUnionMTCNInfo.headline=Αποστολή MTCN και απόδειξης portfolio.pending.step2_buyer.westernUnionMTCNInfo.msg=Πρέπει να στείλεις τον αριθμό MTCN (αριθμός εντοπισμού) και μία φωτογραφία της απόδειξης μέσω email στον πωλητή BTC.\nΣτην απόδειξη θα πρέπει να διακρίνεται καθαρά το πλήρες όνομα του πωλητή, η πόλη, η χώρα και το ποσό. Το email του πωλητή είναι: {0}.\n\nΈστειλες το MTCN και επικοινώνησες με τον πωλητή; portfolio.pending.step2_buyer.halCashInfo.headline=Αποστολή κωδικού HalCash -portfolio.pending.step2_buyer.halCashInfo.msg=You need to send a text message with the HalCash code as well as the trade ID ({0}) to the BTC seller.\nThe seller''s mobile nr. is {1}.\n\nDid you send the code to the seller? -portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Some banks might require the receiver's name. The UK sort code and account number is sufficient for a Faster Payment transfer and the receivers name is not verified by any of the banks. +portfolio.pending.step2_buyer.halCashInfo.msg=Απαιτείται να στείλεις SMS με τον κωδικό HalCash και την ταυτότητα ({0}) της συναλλαγής στον πωλητή BTC.\nΤο κινητό του πωλητή είναι {1}.\n\nΈστειλες το μήνυμα στον πωλητή; +portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo=Μερικές τράπεζες ίσως ζητούν το όνομα του παραλήπτη. Στο Ηνωμένο Βασίλειο ο sort code και ο αριθμός λογαριασμού αρκούν για συναλλαγές Faster Payment και το όνομα του παραλήπτη δεν επαληθεύεται από τις τράπεζες. portfolio.pending.step2_buyer.confirmStart.headline=Επιβεβαίωσε πως ξεκίνησες την πληρωμή portfolio.pending.step2_buyer.confirmStart.msg=Ξεκίνησες την πληρωμή {0} προς τον έτερο συναλλασσόμενο; portfolio.pending.step2_buyer.confirmStart.yes=Ναι, ξεκίνησα την πληρωμή portfolio.pending.step2_seller.waitPayment.headline=Αναμονή για την πληρωμή -portfolio.pending.step2_seller.f2fInfo.headline=Buyer's contact information +portfolio.pending.step2_seller.f2fInfo.headline=Πληροφορίες επικοινωνίας αγοραστή portfolio.pending.step2_seller.waitPayment.msg=Η κατάθεση έχει τουλάχιστον μία επιβεβαίωση στο blockchain.\nΠρέπει να περιμένεις μέχρι να ξεκινήσει ο αγοραστής BTC την πληρωμή {0}. -portfolio.pending.step2_seller.warn=The BTC buyer still has not done the {0} payment.\nYou need to wait until they have started the payment.\nIf the trade has not been completed on {1} the arbitrator will investigate. +portfolio.pending.step2_seller.warn=Ο αγοραστής BTC δεν έχει κάνει ακόμα την πληρωμή {0}.\nΠρέπει να περιμένεις μέχρι να ξεκινήσει την πληρωμή.\nΑν η συναλλαγή δεν ολοκληρωθεί μέχρι την {1}, ο διαμεσολαβητής θα το διερευνήσει. portfolio.pending.step2_seller.openForDispute=Ο αγοραστής BTC δεν ξεκίνησε τη διαδικασία πληρωμής!\nΗ μέγιστη χρονική περίοδος συναλλαγής παρήλθε.\nΜπορείς να περιμένεις περισσότερο και να δώσεις επιπλέον χρόνο στον έτερο συναλλασσόμενο ή μπορείς να επικοινωνήσεις με τον διαμεσολαβητή και να ξεκινήσεις επίλυση διένεξης. # suppress inspection "UnusedProperty" @@ -556,31 +565,31 @@ message.state.FAILED=Αποτυχία αποστολής μηνύματος portfolio.pending.step3_buyer.wait.headline=Αναμονή για την επιβεβαίωση της πληρωμής από τον πωλητή BTC portfolio.pending.step3_buyer.wait.info=Αναμονή για την επιβεβαίωση της απόδειξης της πληρωμής {0} από τον πωλητή BTC. -portfolio.pending.step3_buyer.wait.msgStateInfo.label=Payment started message status +portfolio.pending.step3_buyer.wait.msgStateInfo.label=Κατάσταση μηνύματος έναρξης πληρωμής portfolio.pending.step3_buyer.warn.part1a=στο blockchain {0} portfolio.pending.step3_buyer.warn.part1b=στον πάροχο υπηρεσιών πληρωμής (π.χ. τράπεζα) portfolio.pending.step3_buyer.warn.part2=Ο πωλητής BTC δεν έχει επιβεβαιώσει ακόμα την πληρωμή σου!\nΈλεγξε {0} αν υπήρξε επιτυχής πληρωμή.\nΑν ο πωλητής BTC δεν επιβεβαιώσει την απόδειξη της πληρωμής σου μέχρι την {1}, η συναλλαγή θα διερευνηθεί από τον διαμεσολαβητή. portfolio.pending.step3_buyer.openForDispute=Ο πωλητής BTC δεν επιβεβαίωσε την πληρωμή σου!\nΗ μέγιστη χρονική περίοδος συναλλαγής παρήλθε.\nΜπορείς να περιμένεις περισσότερο και να δώσεις επιπλέον χρόνο στον έτερο συναλλασσόμενο ή μπορείς να επικοινωνήσεις με τον διαμεσολαβητή και να ξεκινήσεις επίλυση διένεξης. # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.part=Your trading partner has confirmed that they have initiated the {0} payment.\n\n -portfolio.pending.step3_seller.altcoin.explorer=on your favorite {0} blockchain explorer -portfolio.pending.step3_seller.altcoin.wallet=at your {0} wallet -portfolio.pending.step3_seller.altcoin={0}Please check {1} if the transaction to your receiving address\n{2}\nhas already sufficient blockchain confirmations.\nThe payment amount has to be {3}\n\nYou can copy & paste your {4} address from the main screen after closing that popup. +portfolio.pending.step3_seller.part=Ο συναλλασσόμενος επιβεβαίωσε πως ξεκίνησε την {0} πληρωμή.\n +portfolio.pending.step3_seller.altcoin.explorer=στον προτιμώμενο {0} blockchain explorer +portfolio.pending.step3_seller.altcoin.wallet=στο {0} πορτοφόλι σου +portfolio.pending.step3_seller.altcoin={0}Έλεγξε στον {1} αν η συναλλαγή προς τη διεύθυνση παραλαβής\n{2}\nέχει επαρκείς επιβεβαιώσεις στο blockchain.\nΤο ποσό πληρωμής θα πρέπει να είναι {3}\n\nΜπορείς να αντιγράψεις και να επικολλήσεις τη διεύθυνση {4} από την κύρια οθόνη αφού κλείσεις αυτό το παράθυρο. portfolio.pending.step3_seller.postal={0}Έλεγξε αν παρέλαβες {1} μέσω \"US Postal Money Order\" από τον αγοραστή BTC.\n\nΗ ταυτότητα (ID) συναλλαγής (πεδίο \"reason for payment\") είναι: \"{2}\" -portfolio.pending.step3_seller.bank=Your trading partner has confirmed that they have initiated the {0} payment.\n\nPlease go to your online banking web page and check if you have received {1} from the BTC buyer.\n\nThe trade ID (\"reason for payment\" text) of the transaction is: \"{2}\"\n\n -portfolio.pending.step3_seller.cash=Because the payment is done via Cash Deposit the BTC buyer has to write \"NO REFUND\" on the paper receipt, tear it in 2 parts and send you a photo by email.\n\nTo avoid chargeback risk, only confirm if you received the email and if you are sure the paper receipt is valid.\nIf you are not sure, {0} +portfolio.pending.step3_seller.bank=Ο έτερος συναλλασσόμενος επιβεβαίωσε την έναρξη της πληρωμής {0}.\n\nΣυνδέσου στον e-banking λογαριασμό σου και έλεγξε αν έχεις παραλάβει {1} από τον αγοραστή BTC.\n\nΗ ταυτότητα συναλλαγής (πεδίο \"reason for payment\") στη συναλλαγή είναι: \"{2}\"\n +portfolio.pending.step3_seller.cash=Καθώς η πληρωμή έγινε μέσω κατάθεσης μετρητών, ο αγοραστής BTC πρέπει να γράψει \"NO REFUND\" στην έντυπη απόδειξη, να την κόψει σε 2 κομμάτια, και να σου στείλει μια φωτογραφία μέσω email.\n\nΓια να αποφύγεις την πιθανότητα αντιλογισμού χρέωσης, κάνε επιβεβαίωση μονάχα στην περίπτωση που παραλάβεις το email, καθώς επίσης και αν είσαι σίγουρος πως το παραστατικό κατάθεσης είναι έγκυρο.\nΑν δεν είσαι σίγουρος, {0} portfolio.pending.step3_seller.moneyGram=Ο αγοραστής θα πρέπει να σου στείλει τον αριθμό Έγκρισης (Authorisation) και μία φωτογραφία της απόδειξης μέσω email.\nΣτην απόδειξη θα πρέπει να διακρίνεται καθαρά το πλήρες όνομά σου, η χώρα, η πόλη και το ποσό. Έλεγξε αν στα email σου έχεις λάβει τον αριθμό Έγκρισης.\n\nΑφού κλείσεις αυτό το αναδυόμενο παράθυρο θα δεις το όνομα και τη διεύθυνση του αγοραστή BTC, ώστε να λάβεις τα χρήματα από την MoneyGram.\n\nΕπιβεβαίωσε την απόδειξη μονάχα αφού εισπράξεις τα χρήματα! portfolio.pending.step3_seller.westernUnion=Ο αγοραστής θα πρέπει να σου στείλει τον αριθμό MTCN (αριθμός εντοπισμού) και μία φωτογραφία της απόδειξης μέσω email.\nΣτην απόδειξη θα πρέπει να διακρίνεται καθαρά το πλήρες όνομά σου, η πόλη, η χώρα και το ποσό. Έλεγξε αν στα email σου έχεις λάβει το MTCN.\n\nΑφού κλείσεις αυτό το αναδυόμενο παράθυρο θα δεις το όνομα και τη διεύθυνση του αγοραστή BTC, ώστε να λάβεις τα χρήματα από την Western Union.\n\nΕπιβεβαίωσε την απόδειξη μονάχα αφού εισπράξεις τα χρήματα! -portfolio.pending.step3_seller.halCash=The buyer has to send you the HalCash code as text message. Beside that you will receive a message from HalCash with the required information to withdraw the EUR from a HalCash supporting ATM.\n\nAfter you have picked up the money from the ATM please confirm here the receipt of the payment! +portfolio.pending.step3_seller.halCash=Ο αγοραστής πρέπει να σου στείλει SMS με τον κωδικό HalCash. Εκτός από αυτό θα παραλάβεις και μήνυμα από την HalCash με τις απαραίτητες πληροφορίες για την ανάληψη Ευρώ από ATM υποστηριζόμενο από την HalCash.\n\nΑφού παραλάβεις τα χρήματα από το ATM επιβεβαίωσε εδώ το παραστατικό της πληρωμής! portfolio.pending.step3_seller.bankCheck=\n\nΕπιβεβαίωσε επίσης ότι το όνομα αποστολέα στην ειδοποίηση από την τράπεζά σου ταιριάζει με αυτό από τα στοιχεία της συναλλαγής:\nΌνομα αποστολέα: {0}\n\nΑν το όνομα δεν είναι το ίδιο όπως αυτό που παρουσιάζεται εδώ, {1} portfolio.pending.step3_seller.openDispute=μην προχωρήσεις σε επιβεβαίωση, αλλά ξεκίνα την επίλυση διένεξης πατώντας \"alt + o\" ή \"option + o\". portfolio.pending.step3_seller.confirmPaymentReceipt=Επιβεβαίωσε την απόδειξη πληρωμής -portfolio.pending.step3_seller.amountToReceive=Amount to receive -portfolio.pending.step3_seller.yourAddress=Your {0} address -portfolio.pending.step3_seller.buyersAddress=Buyers {0} address -portfolio.pending.step3_seller.yourAccount=Your trading account -portfolio.pending.step3_seller.buyersAccount=Buyers trading account +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.confirmReceipt=Επιβεβαίωσε την απόδειξη πληρωμής portfolio.pending.step3_seller.buyerStartedPayment=Ο αγοραστής BTC ξεκίνησε την πληρωμή {0}.\n{1} portfolio.pending.step3_seller.buyerStartedPayment.altcoin=Έλεγξε τις επιβεβαιώσεις στο altcoin πορτοφόλι σου ή στον block explorer και επιβεβαίωσε την πληρωμή όταν θα έχεις επαρκείς blockchain επιβεβαιώσεις. @@ -601,12 +610,12 @@ portfolio.pending.step3_seller.onPaymentReceived.confirm.yes=Ναι, έλαβα portfolio.pending.step5_buyer.groupTitle=Περίληψη ολοκληρωμένης συναλλαγής portfolio.pending.step5_buyer.tradeFee=Προμήθεια συναλλαγής -portfolio.pending.step5_buyer.makersMiningFee=Mining fee -portfolio.pending.step5_buyer.takersMiningFee=Total mining fees -portfolio.pending.step5_buyer.refunded=Refunded security deposit +portfolio.pending.step5_buyer.makersMiningFee=Αμοιβή εξόρυξης +portfolio.pending.step5_buyer.takersMiningFee=Σύνολο αμοιβών εξόρυξης +portfolio.pending.step5_buyer.refunded=Ποσό εγγύησης που επεστράφη portfolio.pending.step5_buyer.withdrawBTC=Ανάληψη bitcoin -portfolio.pending.step5_buyer.amount=Amount to withdraw -portfolio.pending.step5_buyer.withdrawToAddress=Withdraw to address +portfolio.pending.step5_buyer.amount=Ποσό προς ανάληψη +portfolio.pending.step5_buyer.withdrawToAddress=Μεταφορά στη διεύθυνση portfolio.pending.step5_buyer.moveToBisqWallet=Μετάφερε κεφάλαια στο πορτοφόλι Bisq. portfolio.pending.step5_buyer.withdrawExternal=Μεταφορά σε εξωτερικό ποσρτοφόλι portfolio.pending.step5_buyer.alreadyWithdrawn=Τα κεφάλαιά σου έχουν ήδη αποσυρθεί.\nΈλεγξε το ιστορικό συναλλαγών. @@ -614,17 +623,25 @@ portfolio.pending.step5_buyer.confirmWithdrawal=Επιβεβαίωση αίτη portfolio.pending.step5_buyer.amountTooLow=Το ποσό προς μεταφορά είναι χαμηλότερο από την προμήθεια συναλλαγής και την ελάχιστη δυνατή αξία συναλλαγής (dust). portfolio.pending.step5_buyer.withdrawalCompleted.headline=Ανάληψη ολοκληρώθηκε portfolio.pending.step5_buyer.withdrawalCompleted.msg=Οι ολοκληρωμένες συναλλαγές σου βρίσκονται αποθηκευμένες στο \"Χαρτοφυλάκιο/Ιστορικό\".\nΜπορείς να δεις όλες τις bitcoin συναλλαγές σου στο \"Κεφάλαια/Συναλλαγές\" -portfolio.pending.step5_buyer.bought=You have bought -portfolio.pending.step5_buyer.paid=You have paid +portfolio.pending.step5_buyer.bought=Αγόρασες +portfolio.pending.step5_buyer.paid=Πλήρωσες -portfolio.pending.step5_seller.sold=You have sold -portfolio.pending.step5_seller.received=You have received +portfolio.pending.step5_seller.sold=Πούλησες +portfolio.pending.step5_seller.received=Έλαβες tradeFeedbackWindow.title=Συγχαρητήρια για την ολοκλήρωση της συναλλαγής tradeFeedbackWindow.msg.part1=Θα θέλαμε να μάθουμε για τις εντυπώσεις σου. Θα μας βοηθήσει να βελτιώσουμε το πρόγραμμα και να εξομαλύνουμε τυχόν δυσκολίες. Αν θα σε ενδιέφερε να μας προσφέρεις κάποια σχόλια, συμπλήρωσε την ακόλουθη φόρμα έρευνας (δεν απαιτείται εγγραφή) στο: tradeFeedbackWindow.msg.part2=Αν έχεις απορίες ή αν αντιμετώπισες κάποιο πρόβλημα, μπορείς να επικοινωνήσεις με άλλους χρήστες και συνεργάτες μέσω του Bisq φόρουμ στο: tradeFeedbackWindow.msg.part3=Ευχαριστούμε που χρησιμοποίησες το Bisq! + +daoTestingFeedbackWindow.title=Σ' ευχαριστούμε που δοκίμασες το Bisq DAO +daoTestingFeedbackWindow.msg.part1=Μπορείς να μας δώσεις 3 λεπτά για μία σύντομη δημοσκόπηση; Προσφέρουμε 20 BSQ για ολοκληρωμένες δημοσκοπήσεις.\nΗ γνώμη σου είναι κρίσιμη για την ομαλή έναρξη στο mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Συμμετοχή στη δημοσκόπηση +daoTestingFeedbackWindow.msg.part2=Έχεις ερωτήσεις ή άλλα ζητήματα; Συζήτησέ τα με χρήστες και συνεισφέροντες στο Bisq στο forum: +daoTestingFeedbackWindow.forumLinkLabel=Επίσκεψη στο forum +daoTestingFeedbackWindow.msg.part3=Ευχαριστούμε που χρησιμοποίησες το Bisq! + portfolio.pending.role=Ο ρόλος μου portfolio.pending.tradeInformation=Πληροφορίες συναλλαγής portfolio.pending.remainingTime=Υπολοιπόμενος χρόνος @@ -672,8 +689,8 @@ funds.deposit.usedInTx=Χρησιμοποιήθηκε σε {0} συναλλαγ funds.deposit.fundBisqWallet=Χρηματοδότησε το πορτοφόλι Bisq funds.deposit.noAddresses=Δεν έχει δημιουργηθεί διεύθυνση κατάθεσης μέχρι τώρα funds.deposit.fundWallet=Χρηματοδότησε το πορτοφόλι σου -funds.deposit.withdrawFromWallet=Send funds from wallet -funds.deposit.amount=Amount in BTC (optional) +funds.deposit.withdrawFromWallet=Μετάφερε κεφάλαια από πορτοφόλι +funds.deposit.amount=Ποσό σε BTC (προαιρετικό): funds.deposit.generateAddress=Δημιούργησε νέα διεύθυνση funds.deposit.selectUnused=Προκειμένου να δημιουργήσεις μια νέα, διάλεξε μία αχρησιμοποίητη διεύθυνση από τον ανωτέρω πίνακα. @@ -685,8 +702,8 @@ funds.withdrawal.receiverAmount=Ποσό παραλήπτη funds.withdrawal.senderAmount=Ποσό αποστολέα funds.withdrawal.feeExcluded=Το ποσό δεν περιλαμβάνει την αμοιβή εξόρυξης funds.withdrawal.feeIncluded=Το ποσό περιλαμβάνει την αμοιβή εξόρυξης -funds.withdrawal.fromLabel=Withdraw from address -funds.withdrawal.toLabel=Withdraw to address +funds.withdrawal.fromLabel=Ανάληψη από διεύθυνση: +funds.withdrawal.toLabel=Μεταφορά στη διεύθυνση funds.withdrawal.withdrawButton=Επιλέχθηκε η ανάληψη funds.withdrawal.noFundsAvailable=Δεν υπάρχουν διαθέσιμα κεφάλαια προς ανάληψη funds.withdrawal.confirmWithdrawalRequest=Επιβεβαίωση αίτησης ανάληψης @@ -707,8 +724,8 @@ funds.locked.locked=Κλειδωμένα σε multisig για τη συναλλ funds.tx.direction.sentTo=Αποστολή προς: funds.tx.direction.receivedWith=Παρελήφθη με: -funds.tx.direction.genesisTx=From Genesis tx: -funds.tx.txFeePaymentForBsqTx=Miner fee for BSQ tx +funds.tx.direction.genesisTx=Από Genesis tx: +funds.tx.txFeePaymentForBsqTx=Αμοιβή εξόρυξης για BSQ tx funds.tx.createOfferFee=Προμήθεια maker και συναλλαγής: {0} funds.tx.takeOfferFee=Προμήθεια taker και συναλλαγής: {0} funds.tx.multiSigDeposit=Κατάθεση Multisig: {0} @@ -723,8 +740,8 @@ funds.tx.noTxAvailable=Δεν υπάρχουν διαθέσιμες συναλλ funds.tx.revert=Revert funds.tx.txSent=Η συναλλαγή απεστάλη επιτυχώς σε νέα διεύθυνση στο τοπικό πορτοφόλι Bisq. funds.tx.direction.self=Αποστολή στον εαυτό σου -funds.tx.daoTxFee=Miner fee for DAO tx -funds.tx.reimbursementRequestTxFee=Reimbursement request +funds.tx.daoTxFee=Αμοιβή εξόρυξης για DAO tx +funds.tx.reimbursementRequestTxFee=Αίτημα αποζημίωσης funds.tx.compensationRequestTxFee=Αίτημα αποζημίωσης @@ -735,7 +752,7 @@ funds.tx.compensationRequestTxFee=Αίτημα αποζημίωσης support.tab.support=Αιτήματα υποστήριξης support.tab.ArbitratorsSupportTickets=Αιτήματα υποστήριξης διαμεσολαβητή support.tab.TradersSupportTickets=Αιτήματα υποστήριξης συναλλασσόμενου -support.filter=Filter list +support.filter=Λίστα φίλτρων support.noTickets=Δεν υπάρχουν ανοιχτά αιτήματα support.sendingMessage=Αποστολή μηνύματος... support.receiverNotOnline=Ο παραλήπτης δεν είναι συνδεδεμένος. Το μήνυμα αποθηκεύτηκε στο γραμματοκιβώτιό του. @@ -767,13 +784,13 @@ support.buyerOfferer=Αγοραστής/Maker BTC support.sellerOfferer=Πωλητής/Maker BTC support.buyerTaker=Αγοραστής/Taker BTC support.sellerTaker=Πωλητής/Taker BTC -support.backgroundInfo=Bisq is not a company, so it handles disputes differently.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display an \"Open dispute\" button after the trade period is over for contacting the arbitrator.\n\nIf there is an issue with the application, the software will try to detect it and, if possible, display an \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIf you are having an issue and did not see the \"Open support ticket\" button, you can open a support ticket manually by selecting the trade causing issues under \"Portfolio/Open trades\" and hitting \"alt + o\" or \"option + o\". Please use this method only if you are sure that the software is not working as expected. If you have problems or questions, please review the FAQ on the bisq.network web page or post in the Bisq forum in the Support section. +support.backgroundInfo=Το Bisq δεν είναι εταιρία και γι' αυτό διαχειρίζεται τις διενέξεις διαφορετικά.\n\nΕάν προκύψουν διενέξεις κατά τη διαδικασία συναλλαγής (π.χ. ένας εκ των συναλλασσομένων δεν ακολουθήσει το πρωτόκολλο συναλλαγών), η εφαρμογή εμφανίζει το κουμπί \"Επίλυση διένεξης\" μετά τη λήξη της χρονικής περιόδου της συναλλαγής, με τη χρήση του οποίου ειδοποιείται ο διαμεσολαβητής.\n\nΑν προκύψει πρόβλημα με την εφαρμογή, το λογισμικό θα προσπαθήσει να το εντοπίσει και, εφόσον είναι εφικτό, θα εμφανίσει το κουμπί \"Αίτημα υποστήριξης\", με το οποίο καλείται ο διαμεσολαβητής, ο οποίος προωθεί το ζήτημα στους προγραμματιστές.\n\nΑν αντιμετωπίζεις πρόβλημα και δεν εμφανίζεται το κουμπί \"Αίτημα υποστήριξης\", μπορείς να εκκινήσεις ένα αίτημα υποστήριξης επιλέγοντας τη συναλλαγή που δημιουργεί το πρόβλημα πατώντας \"alt+o\" ή \"option+o\" στην καρτέλα \"Χαρτοφυλάκιο/Ανοιχτές συναλλαγές\". Χρησιμοποίησε αυτή τη μέθοδο μονάχα αν είσαι απολύτως σίγουρη/ος πως το λογισμικό δεν λειτουργεί όπως πρέπει. Για οποιαδήποτε απορία ή πρόβλημα έλεγξε τις FAQ (συχνές ερωτήσεις) στην ιστοσελίδα bisq.network ή θέσε το ερώτημά σου στο τμήμα υποστήριξης του φόρουμ. -support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrator's requests within 2 days.\n2. The maximum period for a dispute is 14 days.\n3. You need to cooperate with the arbitrator and provide the information they request to make your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=Μήνυμα συστήματος: {0} -support.youOpenedTicket=Άνοιξες ένα αίτημα υποστήριξης. -support.youOpenedDispute=Άνοιξες ένα αίτημα για επίλυση διένεξης.\n\n{0} -support.peerOpenedTicket=Ο έτερος συναλλασσόμενος αιτήθηκε υποστήριξης εξαιτίας τεχνικού προβλήματος. Παρακαλώ ανάμενε για περαιτέρω οδηγίες. +support.youOpenedTicket=Άνοιξες ένα αίτημα υποστήριξης.\n\n{0}\n\nΈκδοση Bisq: {1} +support.youOpenedDispute=Άνοιξες ένα αίτημα για επίλυση διένεξης.\n\n{0}\n\nΈκδοση Bisq: {1} +support.peerOpenedTicket=Ο έτερος συναλλασσόμενος αιτήθηκε υποστήριξης εξαιτίας τεχνικού προβλήματος.\n\n{0} support.peerOpenedDispute=Ο έτερος συναλλασσόμενος αιτήθηκε επίλυση διένεξης.\n\n{0} @@ -786,65 +803,65 @@ settings.tab.about=Σχετικά setting.preferences.general=Γενικές προτιμήσεις setting.preferences.explorer=Bitcoin block explorer -setting.preferences.deviation=Max. deviation from market price -setting.preferences.avoidStandbyMode=Avoid standby mode +setting.preferences.deviation=Μέγιστη απόκλιση από τιμή αγορών +setting.preferences.avoidStandbyMode=Ακύρωση κατάστασης αναμονής setting.preferences.deviationToLarge=Δεν επιτρέπονται τιμές υψηλότερες από {0}%. -setting.preferences.txFee=Withdrawal transaction fee (satoshis/byte) +setting.preferences.txFee=Προμήθεια ανάληψης (satoshis/byte) setting.preferences.useCustomValue=Χρησιμοποίησε την προεπιλεγμένη τιμή setting.preferences.txFeeMin=Η προμήθεια συναλλαγής θα πρέπει να είναι τουλάχιστον {0} satoshis/byte setting.preferences.txFeeTooLarge=Εισήγαγες υπέρογκη αξία (>5000 satoshis/byte). Η προμήθεια συναλλαγής συνήθως κυμαίνεται μεταξύ 50-400 satoshis/byte. -setting.preferences.ignorePeers=Ignore peers with onion address (comma sep.) +setting.preferences.ignorePeers=Αγνόησε συναλλασσόμενους με διεύθυνση onion (για πολλαπλές εισαγωγές βάλε κόμμα ανάμεσα) setting.preferences.refererId=Referral ID setting.preferences.refererId.prompt=Optional referral ID setting.preferences.currenciesInList=Νομίσματα στην λίστα τιμών αγοράς -setting.preferences.prefCurrency=Preferred currency -setting.preferences.displayFiat=Display national currencies +setting.preferences.prefCurrency=Προτιμώμενο νόμισμα +setting.preferences.displayFiat=Προβολή εθνικών νομισμάτων setting.preferences.noFiat=Δεν επέλεξες εθνικό νόμισμα setting.preferences.cannotRemovePrefCurrency=Δεν μπορείς να καταργήσεις το επιλεγμένο προτιμώμενο νόμισμα προβολής -setting.preferences.displayAltcoins=Display altcoins +setting.preferences.displayAltcoins=Προβολή altcoins setting.preferences.noAltcoins=Δεν επέλεξες altcoin setting.preferences.addFiat=Πρόσθεσε εθνικό νόμισμα setting.preferences.addAltcoin=Πρόσθεσε altcoin setting.preferences.displayOptions=Προβολή επιλογών -setting.preferences.showOwnOffers=Show my own offers in offer book -setting.preferences.useAnimations=Use animations -setting.preferences.sortWithNumOffers=Sort market lists with no. of offers/trades -setting.preferences.resetAllFlags=Reset all \"Don't show again\" flags +setting.preferences.showOwnOffers=Εμφάνισε τις προσφορές μου στο καθολικό προσφορών +setting.preferences.useAnimations=Χρήση animations +setting.preferences.sortWithNumOffers=Ταξινόμησε τους καταλόγους αγορών σύμφωνα με το πλήθος προσφορών/συναλλαγών +setting.preferences.resetAllFlags=Επανάφερε όλες τις επισημάνσεις \"Να μην επανεμφανιστεί\" setting.preferences.reset=Επαναφορά settings.preferences.languageChange=Για αλλαγή γλώσσας σε όλα τα παράθυρα απαιτείται επανεκκίνηση. settings.preferences.arbitrationLanguageWarning=Σε περίπτωση διαφωνίας, σημειώστε ότι η διαιτησία διεκπεραιώνεται στο {0}. -settings.preferences.selectCurrencyNetwork=Select network -setting.preferences.daoOptions=DAO options -setting.preferences.dao.resync.label=Rebuild DAO state from genesis tx -setting.preferences.dao.resync.button=Resync -setting.preferences.dao.resync.popup=After an application restart the BSQ consensus state will be rebuilt from the genesis transaction. -setting.preferences.dao.isDaoFullNode=Run Bisq as DAO full node -setting.preferences.dao.rpcUser=RPC username -setting.preferences.dao.rpcPw=RPC password -setting.preferences.dao.fullNodeInfo=For running Bisq as DAO full node you need to have Bitcoin Core locally running and configured with RPC and other requirements which are documented in ''{0}''. -setting.preferences.dao.fullNodeInfo.ok=Open docs page -setting.preferences.dao.fullNodeInfo.cancel=No, I stick with lite node mode +settings.preferences.selectCurrencyNetwork=Διάλεξε δίκτυο +setting.preferences.daoOptions=Επιλογές DAO +setting.preferences.dao.resync.label=Ανακατασκευή κατάστασης DAO από genesis tx +setting.preferences.dao.resync.button=Επανασυγχρονισμός +setting.preferences.dao.resync.popup=Η κατάσταση consensus του BSQ θα ανακατασκευαστεί από το genesis transaction μετά την επανεκκίνηση της εφαρμογής. +setting.preferences.dao.isDaoFullNode=Εκτέλεση του Bisq ως πλήρης κόμβος DAO. +setting.preferences.dao.rpcUser=Όνομα χρήστη RPC +setting.preferences.dao.rpcPw=Κωδικός RPC +setting.preferences.dao.fullNodeInfo=Για την εκτέλεση του Bisq ως πλήρους κόμβου DAO χρειάζεται να τρέχεις τοπικά το Bitcoin Core παραμετροποιημένο σύμφωνα με RPC και άλλες απαιτήσεις, οι οποίες είναι καταγραμμένες στο "{0}". +setting.preferences.dao.fullNodeInfo.ok=Άνοιγμα σελίδας κειμένων +setting.preferences.dao.fullNodeInfo.cancel=Όχι, παραμένω σε κατάσταση lite node settings.net.btcHeader=Δίκτυο bitcoin settings.net.p2pHeader=Δίκτυο P2P -settings.net.onionAddressLabel=My onion address +settings.net.onionAddressLabel=Η onion διεύθυνσή μου settings.net.btcNodesLabel=Χρήση προσωπικών επιλογών κόμβων Bitcoin Core -settings.net.bitcoinPeersLabel=Connected peers -settings.net.useTorForBtcJLabel=Use Tor for Bitcoin network -settings.net.bitcoinNodesLabel=Bitcoin Core nodes to connect to +settings.net.bitcoinPeersLabel=Συνδεδεμένοι peers +settings.net.useTorForBtcJLabel=Χρήση Tor για δίκτυο Bitcoin +settings.net.bitcoinNodesLabel=Σύνδεση κόμβων Bitcoin Core με settings.net.useProvidedNodesRadio=Χρήση προτεινόμενων κόμβων Bitcoin Core: settings.net.usePublicNodesRadio=Χρήση δημόσιου δικτύου Bitcoin settings.net.useCustomNodesRadio=Χρήση προσωπικών επιλογών κόμβων Bitcoin Core settings.net.warn.usePublicNodes=Αν χρησιμοποιήσεις το δημόσιο δίκτυο Bitcoin εκτίθεσαι σε σοβαρά προβλήματα απορρήτου, τα οποία προκαλούνται από το σχεδιασμό και την εφαρμογή του φίλτρου bloom σε SPV πορτοφόλια όπως το BitcoinJ (χρησιμοποιείται στο Bisq). Οποιοσδήποτε πλήρης κόμβος στον οποίο είσαι συνδεδεμένος μπορεί να εντοπίσει όλες τις διευθύνσεις πορτοφολιού που ανήκουν στο ίδιο πρόσωπο.\n\nΓια περισσότερες λεπτομέρειες διάβασε: https://bisq.network/blog/privacy-in-bitsquare\n\nΘέλεις σίγουρα να χρησιμοποιήσεις δημόσιους κόμβους; settings.net.warn.usePublicNodes.useProvided=Όχι, χρησιμοποίησε προτεινόμενους κόμβους settings.net.warn.usePublicNodes.usePublic=Ναι, χρησιμοποίησε δημόσιο δίκτυο -settings.net.warn.useCustomNodes.B2XWarning=Please be sure that your Bitcoin node is a trusted Bitcoin Core node!\n\nConnecting to nodes which do not follow the Bitcoin Core consensus rules could corrupt your wallet and cause problems in the trade process.\n\nUsers who connect to nodes that violate consensus rules are responsible for any resulting damage. Any resulting disputes will be decided in favor of the other peer. No technical support will be given to users who ignore this warning and protection mechanisms! -settings.net.localhostBtcNodeInfo=(Background information: If you are running a local Bitcoin node (localhost) you can connect exclusively to it.) -settings.net.p2PPeersLabel=Connected peers +settings.net.warn.useCustomNodes.B2XWarning=Βεβαιώσου πως ο κόμβος Bitcoin που χρησιμοποιείς είναι έμπιστος Bitcoin Core κόμβος!\n\nΣύνδεση με κόμβους που δεν ακολουθούν τα Bitcoin Core πρωτόκολλα μπορούν να διαβάλλουν το πορτοφόλι σου και να προκαλέσουν προβλήματα στη διαδικασία συναλλαγής.\n\nΧρήστες που συνδέονται με κόμβους που παραβιάζουν αυτά τα πρωτόκολλα είναι υπεύθυνοι για οποιαδήποτε ζημιά προκληθεί. Διενέξεις που θα ξεκινήσουν εξαιτίας τους θα επιλύονται προς όφελος του άλλου συναλλασσόμενου. Καμία τεχνική υποστήριξη δεν θα προσφέρεται σε χρήστες που αγνοούν αυτή την προειδοποίηση και τους μηχανισμούς προστασίας! +settings.net.localhostBtcNodeInfo=(Βοηθητική πληροφορία: Αν τρέχεις έναν τοπικό κόμβο Bitcoin (localhost) μπορείς να συνδεθείς αποκλειστικά σε αυτόν.) +settings.net.p2PPeersLabel=Συνδεδεμένοι peers settings.net.onionAddressColumn=Διεύθυνση onion settings.net.creationDateColumn=Καθιερώθηκε settings.net.connectionTypeColumn=Εισερχόμενα/Εξερχόμενα -settings.net.totalTrafficLabel=Total traffic +settings.net.totalTrafficLabel=Σύνολο κίνησης settings.net.roundTripTimeColumn=Roundtrip settings.net.sentBytesColumn=Απεσταλμένα settings.net.receivedBytesColumn=Ληφθέντα @@ -862,28 +879,28 @@ settings.net.inbound=εισερχόμενα settings.net.outbound=εξερχόμενα settings.net.reSyncSPVChainLabel=Επανασυγχρονισμός αλυσίδας SPV settings.net.reSyncSPVChainButton=Διάγραψε το αρχείο SPV και επανασυγχρονίσου -settings.net.reSyncSPVSuccess=The SPV chain file will be deleted on the next startup. You need to restart your application now.\n\nAfter the restart it can take a while to resync with the network and you will only see all transactions once the resync is completed.\n\nPlease restart again after the resync has completed because there are sometimes inconsistencies that result in incorrect balances to display. -settings.net.reSyncSPVAfterRestart=The SPV chain file has been deleted. Please be patient. It can take a while to resync with the network. +settings.net.reSyncSPVSuccess=Το αρχείο αλυσίδας SPV θα διαγραφεί στην επόμενη εκκίνηση. Πρέπει να επανεκκινήσεις την εφαρμογή τώρα.\n\nΜετά την επανεκκίνηση ίσως χρειαστεί λίγη ώρα για τον επανασυγχρονισμό με το δίκτυο και θα δεις όλες τις συναλλαγές μόλις ολοκληρωθεί ο συγχρονισμός.\n\nΕπανεκκίνησε μετά την ολοκλήρωση του συγχρονισμού, καθώς μερικές φορές προκύπτουν ασυνέπειες που οδηγούν σε εσφαλμένη εμφάνιση υπολοίπου. +settings.net.reSyncSPVAfterRestart=Το αρχείο αλυσίδας SPV διαγράφηκε. Παρακαλούμε για την υπομονή σου. Ίσως χρειαστεί λίγη ώρα για τον επανασυγχρονισμό με το δίκτυο. settings.net.reSyncSPVAfterRestartCompleted=Ο επανασυγχρονισμός ολοκληρώθηκε. Επανεκκίνησε την εφαρμογή. settings.net.reSyncSPVFailed=Αποτυχία διαγραφής αρχείου αλυσίδας SPV.\nΣφάλμα: {0} setting.about.aboutBisq=Σχετικά με το Bisq -setting.about.about=Bisq is open-source software which facilitates the exchange of bitcoin with national currencies (and other cryptocurrencies) through a decentralized peer-to-peer network in a way that strongly protects user privacy. Learn more about Bisq on our project web page. +setting.about.about=Το Bisq είναι ένα λογισμικό ανοιχτού κώδικα που εξυπηρετεί την ανταλλαγή bitcoin έναντι εθνικών νομισμάτων (και άλλων κρυπτονομισμάτων) μέσω ενός αποκεντρωμένου peer-to-peer δικτύου, και διαδικασιών που προστατεύει ισχυρά το απόρρητο χρήστη. Μπορείς να μάθεις περισσότερα για το Bisq στην ιστοσελίδα μας. setting.about.web=Ιστοσελίδα bisq setting.about.code=Πηγαίος κώδικας setting.about.agpl=Άδεια AGPL setting.about.support=Υποστήριξε το Bisq -setting.about.def=Bisq is not a company—it is a project open to the community. If you want to participate or support Bisq please follow the links below. +setting.about.def=Το Bisq δεν είναι εταιρία, αλλά ένα εγχείρημα ανοιχτό σε συμμετοχές. Αν θέλεις να συμμετάσχεις ή να υποστηρίξεις το Bisq ακολούθησε τους παρακάτω συνδέσμους. setting.about.contribute=Συνεισφορά setting.about.donate=Δωρεά setting.about.providers=Πάροχοι δεδομένων setting.about.apisWithFee=Η εφαρμογή Bisq χρησιμοποιεί APIs τρίτων για τις τιμές αγοράς των Fiat και Altcoin νομισμάτων, καθώς και για τον υπολογισμό της αμοιβής εξόρυξης. setting.about.apis=Η εφαρμογή Bisq χρησιμοποιεί APIs τρίτων για τις τιμές αγοράς των fiat και altcoin νομισμάτων. -setting.about.pricesProvided=Market prices provided by +setting.about.pricesProvided=Οι τιμές αγοράς παρέχονται από setting.about.pricesProviders={0}, {1} και {2} -setting.about.feeEstimation.label=Mining fee estimation provided by +setting.about.feeEstimation.label=Παροχή υπολογισμού αμοιβής εξόρυξης από setting.about.versionDetails=Λεπτομέρειες έκδοσης -setting.about.version=Application version -setting.about.subsystems.label=Versions of subsystems +setting.about.version=Έκδοση εφαρμογής +setting.about.subsystems.label=Έκδοση υποσυστημάτων setting.about.subsystems.val=Έκδοση δικτύου: {0}. Έκδοση P2P message: {1}. Έκδοση τοπικής ΒΔ: {2}. Έκδοση πρωτοκόλλου συναλλαγών: {3} @@ -903,11 +920,11 @@ account.menu.seedWords=Seed πορτοφολιού account.menu.backup=Αντίγραφο ασφαλείας account.menu.notifications=Ειδοποιήσεις -account.arbitratorRegistration.pubKey=Public key +account.arbitratorRegistration.pubKey=Δημόσιο κλειδί account.arbitratorRegistration.register=Εγγραφή διαμεσολαβητή account.arbitratorRegistration.revoke=Ανάκληση εγγραφής -account.arbitratorRegistration.info.msg=Please note that you need to stay available for 15 days after revoking as there might be trades which are using you as arbitrator. The max. allowed trade period is 8 days and the dispute process might take up to 7 days. +account.arbitratorRegistration.info.msg=Λάβε υπόψιν σου πως απαιτείται να παραμείνεις διαθέσιμος για 15 ημέρες μετά την ανάκλησή σου, καθώς ίσως υπάρχουν συναλλαγές στις οποίες έχεις ρόλο διαμεσολαβητή. Η μέγιστη επιτρεπόμενη περίοδος συναλλαγών είναι 8 ημέρες και η επίλυση διένεξης μπορεί να διαρκέσει μέχρι 7 ημέρες. account.arbitratorRegistration.warn.min1Language=Πρέπει να προσθέσεις τουλάχιστον μία γλώσσα.\nΠροσθέσαμε ήδη την προεπιλεγμένη γλώσσα. account.arbitratorRegistration.removedSuccess=Διέγραψες επιτυχώς τον διαμεσολαβητή σου από το δίκτυο P2P. account.arbitratorRegistration.removedFailed= Δεν ήταν δυνατή η διαγραφή του διαμεσολαβητή.{0} @@ -925,22 +942,24 @@ account.arbitratorSelection.noMatchingLang=Δεν βρέθηκε κοινή γλ account.arbitratorSelection.noLang=Μπορείς να επιλέξεις μονάχα διαμεσολαβητές με τουλάχιστον 1 κοινή γλώσσα. account.arbitratorSelection.minOne=Πρέπει να επιλέξεις τουλάχιστον έναν διαμεσολαβητή. -account.altcoin.yourAltcoinAccounts=Your altcoin accounts -account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe arbitrator is not a {2} specialist and cannot help in such cases. +account.altcoin.yourAltcoinAccounts=Οι altcoin λογαριασμοί σου +account.altcoin.popup.wallet.msg=Βεβαιώσου πως πληρείς τις απαιτήσεις για τη χρήση των {0} πορτοφολιών, όπως αυτές περιγράφονται στην ιστοσελίδα {1}.\nΗ χρήση πορτοφολιών κεντρικών ανταλλακτηρίων, όπου είτε (α) δεν έχεις τον έλεγχο των κλειδιών σου, είτε (β) δεν χρησιμοποιούν πορτοφόλια συμβατού λογισμικού, είναι παρακινδυνευμένη: μπορεί να οδηγήσει σε απώλεια κεφαλαίων!\nΟ διαμεσολαβητής δεν είναι ειδικός {2} και δεν μπορεί να βοηθήσει σε τέτοιες περιπτώσεις. account.altcoin.popup.wallet.confirm=Κατανοώ και επιβεβαιώνω πως γνωρίζω ποιο πορτοφόλι πρέπει να χρησιμοποιήσω. -account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending XMR, you need to use either the official Monero GUI wallet or Monero 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.\nmonero-wallet-cli (use the command get_tx_key)\nmonero-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nIn addition to XMR checktx tool (https://xmr.llcoins.net/checktx.html) verification can also be accomplished in-wallet.\nmonero-wallet-cli : using command (check_tx_key).\nmonero-wallet-gui : on the Advanced > Prove/Check page.\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 XMR sender is responsible for providing verification of the XMR 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 (https://www.getmonero.org/resources/user-guides/prove-payment.html) or the Monero forum (https://forum.getmonero.org) 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 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 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=Συναλλαγές με XMR στο Bisq απαιτούν την κατανόηση και εκπλήρωση των ακόλουθων προαπαιτήσεων:\n\nΓια αποστολή XMR απατείται είτε η χρήση του επίσημου Monero GUI πορτοφολιού, είτε του Monero CLI πορτοφολιού με την επισήμανση store-tx-info ενεργοποιημένη (προεπιλεγμένη στις νεώτερες εκδόσεις). Βεβαιώσου πως έχεις πρόσβαση στο tx key, καθώς σε περίπτωση διένεξης θα χρειαστεί.\nΠορτοφόλι Monero CLI (χρησιμοποίησε την εντολή get_tx_key)\nΠορτοφόλι Monero GUI (πήγαινε στην καρτέλα History και πάτα το πλήκτρο (P) για απόδειξη πληρωμής)\n\nΕπιπροσθέτως του εργαλείου XMR checktx (https://xmr.llcoins.net/checktx.html) επιβεβαίωση μπορεί να επιτευχθεί και εντός των πορτοφολιών.\nΠορτοφόλι Monero CLI: με χρήση της εντολής (check_tx_key).\nΠορτοφόλι Monero GUI: στην καρτέλα Advanced > Prove/Check.\nΣε απλούς block explorers η συναλλαγή δεν είναι επαληθεύσιμη.\n\nΣε περίπτωση διένεξης θα χρειαστεί να προσφέρεις στον διαμεσολαβητή τις ακόλουθες πληροφορίες:\n- Το ιδιωτικό tx κλειδί\n- Το hash της συναλλαγής\n- Τη δημόσια διεύθυνση του παραλήπτη\n\nΕάν δεν μπορέσεις να προσφέρεις τις παραπάνω πληροφορίες ή αν χρησιμοποίησες ένα μη συμβατό πορτοφόλι, θα χάσεις την επίλυση της διένεξης. Ο αποστολέας XMR είναι υπεύθυνος για την επιβεβαίωση της μεταφοράς XMR στον διαμεσολαβητή σε περίπτωση διένεξης.\n\nΔεν απαιτείται η ταυτότητα πληρωμής, παρά μονάχα η δημόσια διεύθυνση.\nΕάν δεν είσαι σίγουρος σχετικά με τη διαδικασία, μπορείς να επισκεφτείς το (https://www.getmonero.org/resources/user-guides/prove-payment.html) ή το φόρουμ του Monero (https://forum.getmonero.org) για περαιτέρω πληροφορίες. 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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=Όταν χρησιμοποιείς {0} μπορείς να χρησιμοποιήσεις μονάχα τη διάφανη διεύθυνση (ξεκινά με t), και όχι την z διεύθυνση (ιδιωτική), καθώς ο διαμεσολαβητής δεν θα μπορεί να επαληθεύσει τη συναλλαγή μέσω της z διεύθυνσης. -account.altcoin.popup.XZC.msg=Όταν χρησιμοποιείς {0} μπορείς να χρησιμοποιήσεις μονάχα τη διάφανη (ανιχνεύσιμη) διεύθυνση, και όχι τη μη ανιχνεύσιμη διεύθυνση, καθώς ο διαμεσολαβητής δεν θα μπορεί να επαληθεύσει τη συναλλαγή με μη ανιχνεύσιμες διευθύνσεις στον block explorer. -account.altcoin.popup.bch=Τα Bitcoin Cash και Bitcoin Clashic δεν διαθέτουν προστασία επανάληψης (replay protection). Αν χρησιμοποιείς αυτά τα νομίσματα απαιτείται να λάβεις επαρκείς προφυλάξεις και να κατανοείς όλες τις πιθανές επιπτώσεις. Η ακούσια αποστολή σε άλλο blockchain κατά την μετακίνηση νομισμάτων, ενδέχεται να οδηγήσει σε απώλειες κεφαλαίων. Καθώς αυτά τα νομίσματα μοιράζονται το ίδιο ιστορικό με το blockchain του Bitcoin, εμπεριέχουν κινδύνους ασφαλείας και απώλειας εχεμύθειας.\n\nΔιάβασε περισσότερα στο φόρουμ του Bisq: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Καθώς το Bitcoin Gold έχει κοινό ιστορικό με το blockchain του Bitcoin, ενέχει συγκεκριμένους κινδύνους ασφάλειας και σημαντικά ρίσκα απώλειας απορρήτου. Αν χρησιμοποιείς το Bitcoin Gold, βεβαιώσου πως λαμβάνεις επαρκεί μέτρα πρόληψης και κατανοείς πλήρως τις πιθανές επιπτώσεις.\n\nΠερισσότερες πληροφορίες μπορείς να διαβάσεις στο Bisq φόρουμ: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.ZEC.msg=Όταν χρησιμοποιείς Zcoin μπορείς να χρησιμοποιήσεις μονάχα τις διαφανείς διευθύνσεις (ξεκινούν με t) και όχι τις z διευθύνσεις (ιδιωτικές), καθώς ο διαμεσολαβητής δεν θα μπορεί να επαληθεύσει τη συναλλαγή μέσω z διευθύνσεων. +account.altcoin.popup.XZC.msg=Όταν χρησιμοποιείς Zcoin μπορείς να χρησιμοποιήσεις μονάχα διαφανείς (ανιχνεύσιμες) διευθύνσεις, και όχι μη ανιχνεύσιμες διευθύνσεις, καθώς ο διαμεσολαβητής δεν θα μπορεί να επαληθεύσει τη συναλλαγή με μη ανιχνεύσιμες διευθύνσεις στον block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. -account.fiat.yourFiatAccounts=Your national currency accounts +account.fiat.yourFiatAccounts=Οι λογαριασμοί σου εθνικών νομισμάτων account.backup.title=Δημιουργία αντιγράφου ασφαλείας πορτοφολιού -account.backup.location=Backup location +account.backup.location=Τοποθεσία αντιγράφου ασφαλείας account.backup.selectLocation=Επιλογή τοποθεσίας αντιγράφου ασφαλείας account.backup.backupNow=Δημιουργία αντιγράφου ασφαλείας τώρα (χωρίς κωδικοποίηση!) account.backup.appDir=Θέση δεδομένων εφαρμογής @@ -954,15 +973,15 @@ account.password.removePw.button=Αφαίρεση κωδικού account.password.removePw.headline=Αφαίρεση κωδικού προστασίας πορτοφολιού account.password.setPw.button=Όρισε κωδικό account.password.setPw.headline=Όρισε κωδικό προστασίας πορτοφολιού -account.password.info=With password protection you'll need to enter your password at application startup, when withdrawing bitcoin out of your wallet, and when restoring your wallet from seed words. +account.password.info=Με κωδικό προστασίας απαιτείται η εισαγωγή του κωδικού με κάθε εκκίνηση της εφαρμογής, για αναλήψεις bitcoin από το πορτοφόλι σου, και όταν θέλεις να επαναφέρεις το πορτοφόλι σου μέσω των λέξεων seed. account.seed.backup.title=Αποθήκευση αντιγράφου ασφαλείας για seed words πορτοφολιού account.seed.info=Please write down both wallet seed words and the date! You can recover your wallet any time with seed words and the date.\nThe same seed words are used for the BTC and BSQ wallet.\n\nYou should write down the seed words on a sheet of paper. Do not save them on your computer.\n\nPlease 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! account.seed.warn.noPw.msg=Δεν έχεις δημιουργήσει κωδικό πορτοφολιού, ο οποίος θα προστατεύσει την εμφάνιση των λέξεων seed.\n\nΘέλεις να εμφανίσεις τις λέξεις seed; account.seed.warn.noPw.yes=Ναι, και να μην ερωτηθώ ξανά account.seed.enterPw=Εισήγαγε κωδικό για την εμφάνιση των λέξεων seed -account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state. -account.seed.restore.ok=Εντάξει, καταλαβαίνω και θέλω να κάνω επαναφορά. +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Εντάξει. Εκτέλεση επαναφοράς και τερματισμός του Bisq #################################################################### @@ -977,14 +996,14 @@ account.notifications.webCamWindow.headline=Σάρωση κωδικού QR μέ account.notifications.webcam.label=Χρήση κάμερας account.notifications.webcam.button=Σάρωση κωδικού QR account.notifications.noWebcam.button=Δεν έχω κάμερα -account.notifications.testMsg.label=Send test notification +account.notifications.testMsg.label=Αποστολή ειδοποίησης δοκιμής account.notifications.testMsg.title=Δοκιμή -account.notifications.erase.label=Clear notifications on phone -account.notifications.erase.title=Clear notifications +account.notifications.erase.label=Απαλοιφή ειδοποιήσεων στο τηλέφωνο +account.notifications.erase.title=Απαλοιφή ειδοποιήσεων account.notifications.email.label=Pairing token account.notifications.email.prompt=Enter pairing token you received by email account.notifications.settings.title=Ρυθμίσεις -account.notifications.useSound.label=Play notification sound on phone +account.notifications.useSound.label=Αναπαραγωγή ήχου ειδοποίησης στο τηλέφωνο account.notifications.trade.label=Receive trade messages account.notifications.market.label=Receive offer alerts account.notifications.price.label=Receive price alerts @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=Η χαμηλότερ # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=Πορτοφόλι BSQ dao.tab.proposals=Governance dao.tab.bonding=Bonding dao.tab.proofOfBurn=Asset listing fee/Proof of burn +dao.tab.monitor=Network monitor +dao.tab.news=News dao.paidWithBsq=πληρώθηκε με BSQ -dao.availableBsqBalance=Available -dao.availableNonBsqBalance=Available non-BSQ balance (BTC) -dao.unverifiedBsqBalance=Unverified (awaiting block confirmation) +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Balance of all verified UTXOs +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) dao.lockedForVoteBalance=Used for voting dao.lockedInBonds=Locked in bonds +dao.availableNonBsqBalance=Available non-BSQ balance (BTC) dao.totalBsqBalance=Συνολικό υπόλοιπο BSQ dao.tx.published.success=Η συναλλαγή σου κοινοποιήθηκε επιτυχώς. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Voting cycle overview dao.cycle.currentPhase=Current phase dao.cycle.currentBlockHeight=Current block height dao.cycle.proposal=Proposal phase +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=Blind vote phase dao.cycle.voteReveal=Vote reveal phase dao.cycle.voteResult=Αποτέλεσμα ψηφοφορίας dao.cycle.phaseDuration={0} blocks (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=Κύκλοι dao.results.cycles.table.header.cycle=Κύκλος @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Αποτέλεσμα ψηφοφορί dao.results.proposals.voting.detail.header=Αποτελέσματα ψηφοφορίας επιλεγμένης πρότασης +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Ακαθόριστο @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=Recipient BTC address # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=Asset listing fee per day # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. trade volume +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Current value: {0} dao.param.blocks={0} blocks -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Διάρκεια {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} block(s) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(προεπιλεγμένη τιμή) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(has been changed in voting) +dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the P2P network.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Ακαθόριστο # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Lockup dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Lockup dao.bond.table.button.unlock=Ξεκλείδωσε dao.bond.table.button.revoke=Revoke +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Ακαθόριστο # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Not bonded yet # suppress inspection "UnusedProperty" @@ -1252,13 +1296,17 @@ dao.bond.bondState.UNLOCKED=Bond unlocked # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Bond confiscated +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Ακαθόριστο # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Bonded reputation # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github admin +dao.bond.bondedRoleType.UNDEFINED=Ακαθόριστο +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin # suppress inspection "UnusedProperty" @@ -1266,24 +1314,30 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Rocket chat admin # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Youtube admin +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq maintainer # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Website operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Forum operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Price node operator +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Btc node operator +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ explorer operator # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domain name holder # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS admin @@ -1291,14 +1345,16 @@ dao.bond.bondedRoleType.DNS_ADMIN=DNS admin dao.bond.bondedRoleType.MEDIATOR=Mediator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Διαμεσολαβητής +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=Asset listing fee +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=Asset listing fee dao.burnBsq.menuItem.proofOfBurn=Proof of burn dao.burnBsq.header=Fee for asset listing dao.burnBsq.selectAsset=Select Asset dao.burnBsq.fee=Fee -dao.burnBsq.trialPeriod=Trial period +dao.burnBsq.trialPeriod=Δοκιμαστική περίοδος dao.burnBsq.payFee=Pay fee dao.burnBsq.allAssets=All assets dao.burnBsq.assets.nameAndCode=Asset name @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=Ημερομηνία dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=Συναλλαγές dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Sign a message with key from proof or burn transaction -dao.proofOfBurn.verify.window.title=Verify a message with key from proof or burn transaction +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction dao.proofOfBurn.copySig=Copy signature to clipboard dao.proofOfBurn.sign=Sign dao.proofOfBurn.message=Message dao.proofOfBurn.sig=Signature dao.proofOfBurn.verify=Verify -dao.proofOfBurn.verify.header=Verify message with key from proof or burn transaction +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=Verification succeeded dao.proofOfBurn.verificationResult.failed=Verification failed @@ -1368,10 +1424,12 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Αποκάλυψη ψήφου # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Αποτέλεσμα ψηφοφορίας +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Ακαθόριστο # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Αίτημα αποζημίωσης # suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Reimbursement request +dao.proposal.type.REIMBURSEMENT_REQUEST=Αίτημα αποζημίωσης # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" @@ -1383,10 +1441,12 @@ dao.proposal.type.GENERIC=Γενική πρόταση # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Proposal for confiscating a bond +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Ακαθόριστο # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Αίτημα αποζημίωσης # suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Reimbursement request +dao.proposal.type.short.REIMBURSEMENT_REQUEST=Αίτημα αποζημίωσης # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=Γενική πρόταση # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Confiscating a bond - dao.proposal.details=Λεπτομέρειες πρότασης dao.proposal.selectedProposal=Επιλεγμένη πρόταση dao.proposal.active.header=Proposals of current cycle dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal?\nThe already paid proposal fee will be lost. dao.proposal.active.remove.doRemove=Yes, remove my proposal dao.proposal.active.remove.failed=Δεν ήταν δυνατή η απόσυρση της πρότασης. +dao.proposal.myVote.title=Ψηφοφορία dao.proposal.myVote.accept=Αποδοχή πρότασης dao.proposal.myVote.reject=Απόρριψη πρότασης dao.proposal.myVote.removeMyVote=Ignore proposal dao.proposal.myVote.merit=Vote weight from earned BSQ dao.proposal.myVote.stake=Vote weight from stake -dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID dao.proposal.myVote.revealTxId=Vote reveal transaction ID -dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0} -dao.proposal.votes.header=Ψήφιση σε όλες τις προτάσεις -dao.proposal.votes.header.voted=Η ψήφος μου -dao.proposal.myVote.button=Ψήφιση σε όλες τις προτάσεις +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Επιλογή τύπου πρότασης +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=Τύπος πρότασης -dao.proposal.create.createNew=Κατάθεση νέας πρότασης -dao.proposal.create.create.button=Κατάθεσε πρόταση +dao.proposal.create.new=Κατάθεση νέας πρότασης +dao.proposal.create.button=Κατάθεσε πρόταση +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=πρόταση dao.proposal.display.type=Τύπος πρότασης dao.proposal.display.name=Name/nickname dao.proposal.display.link=Link to detail info -dao.proposal.display.link.prompt=Link to Github issue +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=Requested amount in BSQ dao.proposal.display.bsqAddress=BSQ address dao.proposal.display.txId=Proposal transaction ID @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=Accepted dao.proposal.display.myVote.rejected=Rejected dao.proposal.display.myVote.ignored=Ignored dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}); +dao.proposal.myVote.invalid=Vote was invalid dao.proposal.voteResult.success=Accepted dao.proposal.voteResult.failed=Rejected @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=Asset to remove dao.blindVote=blind vote dao.blindVote.startPublishing=Publishing blind vote transaction... -dao.blindVote.success=Your blind vote has been successfully published. +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=Αποστολή dao.wallet.menuItem.receive=Λήψη dao.wallet.menuItem.transactions=Συναλλαγές dao.wallet.dashboard.myBalance=My wallet balance -dao.wallet.dashboard.distribution=Distribution of all BSQ -dao.wallet.dashboard.locked=Global state of locked BSQ -dao.wallet.dashboard.market=Market data -dao.wallet.dashboard.genesis=Συναλλαγή genesis -dao.wallet.dashboard.txDetails=BSQ transactions statistics -dao.wallet.dashboard.genesisBlockHeight=Genesis block height -dao.wallet.dashboard.genesisTxId=Genesis transaction ID -dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction -dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests -dao.wallet.dashboard.reimbursementAmount=BSQ issued for reimbursement requests -dao.wallet.dashboard.availableAmount=Total available BSQ -dao.wallet.dashboard.burntAmount=Burned BSQ (fees) -dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds -dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds -dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds -dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds -dao.wallet.dashboard.allTx=No. of all BSQ transactions -dao.wallet.dashboard.utxo=No. of all unspent transaction outputs -dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions -dao.wallet.dashboard.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions -dao.wallet.dashboard.burntTx=No. of all fee payments transactions -dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq) -dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price) - -dao.wallet.receive.fundYourWallet=Χρηματοδότησε το BSQ πορτοφόλι σου -dao.wallet.receive.bsqAddress=BSQ wallet address + +dao.wallet.receive.fundYourWallet=Your BSQ receive address +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=The Bisq DAO +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=Αποστολή κεφαλαίων dao.wallet.send.sendBtcFunds=Send non-BSQ funds (BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=Latest verified block: {0} dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1} dao.wallet.tx.type=Τύπος +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Ακαθόριστο # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Δεν αναγνωρίζεται # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=Αίτημα/έκδοση αποζημίωσης dao.tx.issuanceFromCompReq.tooltip=Αίτημα αποζημίωσης το οποίο οδήγησε σε έκδοση νέων BSQ.\nΗμερομηνία έκδοσης: {0} dao.tx.issuanceFromReimbursement=Reimbursement request/issuance dao.tx.issuanceFromReimbursement.tooltip=Reimbursement request which led to an issuance of new BSQ.\nIssuance date: {0} -dao.proposal.create.missingBsqFunds=Δεν έχεις επαρκή κεφάλαια για τη δημιουργία της πρότασης.\nΥπολείπονται: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=Επιβεβαίωση συναλλαγής {0} dao.feeTx.confirm.details={0} fee: {1}\nMining fee: {2} ({3} Satoshis/byte)\nTransaction size: {4} Kb\n\nAre you sure you want to publish the {5} transaction? +dao.news.bisqDAO.title=THE BISQ DAO +dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. +dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO + +dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ +dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.news.pastContribution.yourAddress=Your BSQ Wallet Address +dao.news.pastContribution.requestNow=Request now + +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. +dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ +dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. +dao.news.DAOOnTestnet.readMoreLink=Read the full documentation + +dao.monitor.daoState=DAO state +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Conflicts +dao.monitor.state=Κατάσταση +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=DAO state +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Block height +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Previous hash +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Previous hash +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Previous hash +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Market data +dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq) +dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price) +dao.factsAndFigures.dashboard.availableAmount=Total available BSQ + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests +dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Global state of locked BSQ +dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds +dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds +dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds +dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds +dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees) + +dao.factsAndFigures.transactions.genesis=Συναλλαγή genesis +dao.factsAndFigures.transactions.genesisBlockHeight=Genesis block height +dao.factsAndFigures.transactions.genesisTxId=Genesis transaction ID +dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics +dao.factsAndFigures.transactions.allTx=No. of all BSQ transactions +dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs +dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions +dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions +dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Filtered seed nodes (comma sep. onion addresses) filterWindow.priceRelayNode=Filtered price relay nodes (comma sep. onion addresses) filterWindow.btcNode=Filtered Bitcoin nodes (comma sep. addresses + port) filterWindow.preventPublicBtcNetwork=Prevent usage of public Bitcoin network +filterWindow.disableDao=Disable DAO filterWindow.add=Προσθήκη φίλτρων filterWindow.remove=Αναίρεση φίλτρων @@ -1697,7 +1845,7 @@ tacWindow.arbitrationSystem=Σύστημα διαμεσολάβησης tradeDetailsWindow.headline=Συναλλαγή tradeDetailsWindow.disputedPayoutTxId=Ταυτότητα αμφισβητούμενης συναλλαγής αποπληρωμής: tradeDetailsWindow.tradeDate=Ημερομηνία συναλλαγής -tradeDetailsWindow.txFee=Mining fee +tradeDetailsWindow.txFee=Αμοιβή εξόρυξης tradeDetailsWindow.tradingPeersOnion=Διεύθυνση onion συναλλασσόμενων peers tradeDetailsWindow.tradeState=Trade state @@ -1955,6 +2103,10 @@ BTC_MAINNET=Bitcoin Mainnet BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=Έτος time.month=Μήνας @@ -2025,6 +2177,8 @@ payment.country=Χώρα payment.extras=Extra requirements payment.email.mobile=Email or mobile no. payment.altcoin.address=Altcoin address +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=Altcoin payment.select.altcoin=Διάλεξε ή αναζήτησε altcoin: payment.secret=Secret question @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=Email or phone no. payment.venmo.venmoUserName=Venmo username payment.popmoney.accountId=Email or phone no. -payment.revolut.email=Email or phone no. +payment.revolut.email=Email +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=Citizen ID/Tax ID or phone no. payment.supportedCurrencies=Supported currencies payment.limitations=Limitations @@ -2082,6 +2237,10 @@ payment.limits.info=Λάβε υπόψιν πως όλες οι συναλλαγ payment.cashDeposit.info=Please confirm your bank allows you to send cash deposits into other peoples' accounts. For example, Bank of America and Wells Fargo no longer allow such deposits. +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=Στοιχεία επινοινωνίας payment.f2f.contact.prompt=Πώς θέλεις να επικοινωνήσει μαζί σου ο έτερος συναλλασσόμενος; (email, αριθμός τηλεφώνου, ...) payment.f2f.city=Πόλη για συνάντηση κατά πρόσωπο. @@ -2128,16 +2287,10 @@ F2F_SHORT=Κατά πρόσωπο (F2F) # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Υποστήριξε # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altcoins PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Υποστήριξε +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Υποστήριξε # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Altcoins PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} θα πρέπει να αποτελείται validation.invalidInput=Μη έγκυρη εισαγωγή: {0} validation.accountNrFormat=Ο αριθμός λογαριασμού πρέπει να είναι της μορφής: {0} validation.altcoin.wrongStructure=Η επαλήθευση διεύθυνσης απέτυχε καθώς δεν ταιριάζει στη δομή μιας διεύθυνσης {0}. +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=Οι διευθύνσεις ZEC πρέπει να ξεκινούν με t. Διευθύνσεις που ξεκινούν με z δεν υποστηρίζονται. validation.altcoin.invalidAddress=Η διεύθυνση δεν είναι έγκυρη {0} διεύθυνση! {1} validation.bic.invalidLength=Το μήκος της εισαγωγής δεν είναι ούτε 8, ούτε 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Must contain only letters, numbers, validation.interacETransfer.invalidAnswer=Must be one word and contain only letters, numbers, and/or the symbol - validation.inputTooLarge=Input must not be larger than {0} validation.inputTooSmall=Input has to be larger than {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=The amount below the dust limit of {0} is not allowed. validation.length=Length must be between {0} and {1} validation.pattern=Input must be of format: {0} validation.noHexString=The input is not in HEX format. validation.advancedCash.invalidFormat=Must be a valid email or wallet id of format: X000000000000 +validation.invalidUrl=This is not a valid URL +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index a921ce9a4a0..22301a0f1a3 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -92,7 +92,7 @@ shared.dontRemoveOffer=No eliminar oferta shared.editOffer=Editar oferta shared.openLargeQRWindow=Abrir ventana grande de código QR shared.tradingAccount=Cuenta de intercambio -shared.faq=Visite el sitio de preguntas frecuentes +shared.faq=Visite el sitio de FAQs shared.yesCancel=Sí, cancelar shared.nextStep=Siguiente paso shared.selectTradingAccount=Selecionar cuenta de intercambio @@ -104,7 +104,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/Depositar fondos\". +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.waitingForFunds=Esperando fondos... shared.depositTransactionId=ID deposito de la transacción shared.TheBTCBuyer=El comprador de BTC @@ -113,12 +113,13 @@ shared.reasonForPayment=Concepto de pago shared.sendingConfirmation=Enviando confirmación... shared.sendingConfirmationAgain=Por favor envíe confirmación de nuevo shared.exportCSV=Exportar a csv +shared.exportJSON=Exportar a JSON shared.noDateAvailable=Sin fecha disponible shared.arbitratorsFee=Tasa de arbitraje shared.noDetailsAvailable=Sin detalles disponibles shared.notUsedYet=Sin usar aún shared.date=Fecha -shared.sendFundsDetailsWithFee=Enviando: {0}\nDesde la dirección: {1}\nA la dirección de destino: {2}.\nEl costo de transacción requerido es: {3} ({4} Satoshis/byte)\nTamaño de la transacción: {5} Kb\n\nEl receptor recibirá: {6}\n\n¿Está seguro que quiere retirar esta cantidad? +shared.sendFundsDetailsWithFee=Enviando: {0}\nDesde la dirección: {1}\nA la dirección receptora: {2}.\nLa tasa de transacción requerida es: {3} ({4} Satoshis/byte)\nTamaño de la transacción: {5} Kb\n\nEl receptor recibirá: {6}\n\nSeguro que quiere retirar esta cantidad? shared.copyToClipboard=Copiar al portapapeles shared.language=Idioma shared.country=País @@ -152,7 +153,7 @@ shared.seller=Vendedor shared.buyer=Comprador shared.allEuroCountries=Todos los países Euro shared.acceptedTakerCountries=Países aceptados como tomador -shared.arbitrator=Árbitro seleccionado +shared.arbitrator=árbitro de transacciones seleccionado shared.tradePrice=Precio de intercambio shared.tradeAmount=Cantidad de intercambio shared.tradeVolume=Volumen de intercambio @@ -187,7 +188,7 @@ shared.securityDepositBox.description=Depósito de seguridad para BTC {0} shared.iConfirm=Confirmo shared.tradingFeeInBsqInfo=equivalente a {0} usado como tasa de minado shared.openURL=Abrir {0} -shared.fiat=Fiatt +shared.fiat=Fiat shared.crypto=Cripto shared.all=Todos shared.edit=Editar @@ -196,6 +197,8 @@ shared.interval=Intervalo shared.actions=Acciones shared.buyerUpperCase=Comprador shared.sellerUpperCase=Vendedor +shared.new=NUEVO +shared.blindVoteTxId=Id de la transacción de voto ciego #################################################################### # UI views @@ -231,7 +234,8 @@ mainView.balance.locked.short=Bloqueado mainView.footer.usingTor=(usando Tor) mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo=Pares de red Bitcoin: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Iniciando +mainView.footer.btcInfo.initializing=Conectando a la red Bitcoin +mainView.footer.bsqInfo.synchronizing=/ Sincronizando DAO mainView.footer.btcInfo.synchronizedWith=Sincronizado con mainView.footer.btcInfo.connectingTo=Conectando a mainView.footer.btcInfo.connectionFailed=Conexión fallida @@ -299,8 +303,8 @@ market.trades.tooltip.candle.date=Fecha: offerbook.createOffer=Crear oferta offerbook.takeOffer=Tomar oferta -offerbook.takeOfferToBuy=Take offer to buy {0} -offerbook.takeOfferToSell=Take offer to sell {0} +offerbook.takeOfferToBuy=Tomar oferta de compra de {0} +offerbook.takeOfferToSell=Tomar oferta de venta de {0} offerbook.trader=Trader offerbook.offerersBankId=ID del banco del creador (BIC/SWIFT): {0} offerbook.offerersBankName=Nombre del banco del creador: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=Cantidad de BTC a {0} createOffer.amountPriceBox.buy.volumeDescription=Cantidad a gastar en {0} createOffer.amountPriceBox.sell.volumeDescription=Cantidad a recibir en {0}. createOffer.amountPriceBox.minAmountDescription=Cantidad mínima de BTC -createOffer.securityDeposit.prompt=Depósito de seguridad en BTC +createOffer.securityDeposit.prompt=Depósito de seguridad createOffer.fundsBox.title=Dote de fondos su oferta. createOffer.fundsBox.offerFee=Tasa de transacción createOffer.fundsBox.networkFee=Tasa de minado createOffer.fundsBox.placeOfferSpinnerInfo=Publicación de oferta en curso... createOffer.fundsBox.paymentLabel=Intercambio Bisq con ID {0} createOffer.fundsBox.fundsStructure=({0} depósito de seguridad, {1} costo de transacción, {2} tarifa de minado) +createOffer.fundsBox.fundsStructure.BSQ=({0} depósito seguridad, {1} tasa de minado) + {2} tasa de intercambio createOffer.success.headline=Su oferta ha sido publicada. createOffer.success.info=Puede gestionar sus ofertas abiertas en \"Portafolio/Mis ofertas abiertas\". createOffer.info.sellAtMarketPrice=Siempre venderá a precio de mercado ya que el precio de su oferta será actualizado continuamente. @@ -387,6 +392,7 @@ createOffer.alreadyFunded=Ya había destinado fondos para esa oferta.\nSus fondo createOffer.createOfferFundWalletInfo.headline=Dote de fondos su trato. # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Cantidad a intercambiar: {0}\n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} y {1} createOffer.createOfferFundWalletInfo.msg=Necesita depositar {0} para completar esta oferta.\n\nEsos fondos son reservados en su cartera local y se bloquearán en la dirección de depósito multifirma una vez alguien tome su oferta.\nLa cantidad es la suma de:\n{1}- Su depósito de seguridad: {2}\n- Cuota de intercambio: {3}\n- Tarifa de mining: {4}\n\nPuede elegir entre dos opciones a la hora de depositar fondos para realizar su intercambio:\n- Usar su cartera Bisq (conveniente, pero las transacciones pueden ser trazables) O también\n- Transferir desde una cartera externa (potencialmente con mayor privacidad)\n\nConocerá todos los detalles y opciones para depositar fondos al cerrar esta ventana. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=El precio que ha introducido está fuera de createOffer.changePrice=Cambiar precio createOffer.tac=Al colocar esta oferta estoy de acuerdo en comerciar con cualquier comerciante que cumpla con las condiciones definidas anteriormente. createOffer.currencyForFee=Tasa de transacción -createOffer.setDeposit=Establecer depósito de seguridad del comprador +createOffer.setDeposit=Establecer depósito de seguridad para el comprador (%) +createOffer.setDepositAsBuyer=Establecer mi depósito de seguridad como comprador (%) +createOffer.securityDepositInfo=El depósito de seguridad del comprador será {0} +createOffer.securityDepositInfoAsBuyer=Su depósito de seguridad como comprador será {0} #################################################################### @@ -507,7 +516,7 @@ portfolio.pending.step2_buyer.cash=Por favor vaya al banco y pague {0} al vended portfolio.pending.step2_buyer.cash.extra=REQUERIMIENTO IMPORTANTE:\nDespués de haber hecho el pago escribe en el recibo de papel: SIN REEMBOLSOS\nLuego divídalo en 2 partes, haga una foto y envíela a la dirección de e-mail del vendedor de BTC. portfolio.pending.step2_buyer.moneyGram=Por favor pague {0} al vendedor de BTC utilizando MoneyGram.\n\n portfolio.pending.step2_buyer.moneyGram.extra=REQUERIMIENTO IMPORTANTE:\nDespués que usted haya realizado el pago, envíe el número de autorización y una foto del recibo al vendedor de BTC por correo electrónico.\nEl recibo debe mostrar claramente el monto, asi como el nombre completo, país y demarcación (departamento,estado, etc.) del vendedor. El correo electrónico del vendedor es: {0}. -portfolio.pending.step2_buyer.westernUnion=Por favor pagar {0} a el vendedor de BTC usando Western Union./n +portfolio.pending.step2_buyer.westernUnion=Por favor, pagar {0} al vendedor de BTC usando Western Union.\n\n portfolio.pending.step2_buyer.westernUnion.extra=Requerimiento importante:/nDespués de haber realizado el pago envíe el MTCN (número de seguimiento) y una foto de el recibo por email a el vendedor BTC./nEl recibo debe mostrar claramente el nombre completo del emisor, la ciudad, país y la cantidad. El email del vendedor es: {0}. # suppress inspection "TrailingSpacesInProperty" @@ -564,7 +573,7 @@ portfolio.pending.step3_buyer.openForDispute=El vendedor de BC aún no ha confir # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.part=La otra parte del intercambio confirma haber iniciado el pago de {0}.\n\n portfolio.pending.step3_seller.altcoin.explorer=en su explorador de cadena de bloques {0} favorito -portfolio.pending.step3_seller.altcoin.wallet=en su monedero {0} +portfolio.pending.step3_seller.altcoin.wallet=en su cartera {0} portfolio.pending.step3_seller.altcoin={0}Por favor compruebe {1} si la transacción a su dirección de recepción\n{2}\ntiene suficientes confirmaciones en la cadena de bloques.\nLa cantidad a pagar tiene que ser {3}\n\nPuede copiar y pegar su dirección {4} desde la pantalla principal después de cerrar este popup. portfolio.pending.step3_seller.postal={0}Por favor, compruebe si ha recibido {1} con \"US Postal Money Order\"desde el comprador de BTC.\n\nLa ID de intercambio (el texto \"reason for payment\") de la transacción es: \"{2}\" portfolio.pending.step3_seller.bank=Su par de intercambio ha confirmado haber iniciado el pago de {0}.\n\nPor favor, vaya a la web de su banco y compruebe si ha recibido {1} desde la cuenta del comprador de BTC.\nLa ID de intercambio (texto \"razón de pago\") de la transacción es: \"{2}\" @@ -592,7 +601,7 @@ portfolio.pending.step3_seller.openForDispute=No ha confirmado el recibo de pago # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.part1=Ha recibido el pago de {0} de su par de intercambio?\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.onPaymentReceived.fiat=La ID de intercambio (texto \"reason for payment\") de la transacción es: \"{0}\"\n\n +portfolio.pending.step3_seller.onPaymentReceived.fiat=La ID de intercambio/transacción (texto \"concepto de pago\") de la transacción es: \"{0}\"\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.name=Por favor, verifica también que el nombre del remitente de su anuncio bancario concuerda con el del contrato de intercambio:\nNombre del emisor: {0}\n\nSi el nombre no es el mismo que el mostrado aquí, por favor no confirme. Abra una disputa pulsando \"cmd + o\" o \"ctrl + o\".\n\n portfolio.pending.step3_seller.onPaymentReceived.note=Por favor, tenga en cuenta que tan pronto como confirme el recibo, la cantidad de intercambio bloqueada se liberará a el comprador de BTC y se le devolverá el depósito de seguridad. @@ -620,11 +629,19 @@ portfolio.pending.step5_buyer.paid=Ha pagado portfolio.pending.step5_seller.sold=Ha vendido portfolio.pending.step5_seller.received=Ha recibido -tradeFeedbackWindow.title=Felicitaciones por completar su transacción +tradeFeedbackWindow.title=Felicitaciones por completar su intercambio/transacción tradeFeedbackWindow.msg.part1=Nos encantaría saber su opinion acerca de su experiencia de usuario. Nos ayudará a mejorar el software y refinar sus características. Si desea enviar sus comentarios, por favor complete esta breve encuesta (sin registro requerido) en: tradeFeedbackWindow.msg.part2=Si tiene alguna pregunta o experimenta algún problema, por favor póngase en contacto con otros usuarios y colaboradores a través del foro Bisq en: tradeFeedbackWindow.msg.part3=Gracias por usar Bisq! + +daoTestingFeedbackWindow.title=Gracias por probar la DAO Bisq +daoTestingFeedbackWindow.msg.part1=Puedes dedicar 3 minutos a completar una pequeña encuesta? Estamos ofreciendo 20 BSQ para quienes completen la encuesta. Tus respuestas son cruciales para asegurar un lanzamiento en mainnet sin fallas. +daoTestingFeedbackWindow.surveyLinkLabel=Completar la encuesta +daoTestingFeedbackWindow.msg.part2=Tienes preguntas u otros comentarios? Puedes discutir con otros usuarios y colaboradores de Bisq en el foro: +daoTestingFeedbackWindow.forumLinkLabel=Visitar el foro +daoTestingFeedbackWindow.msg.part3=Gracias por usar Bisq! + portfolio.pending.role=Mi rol portfolio.pending.tradeInformation=Información de intercambio portfolio.pending.remainingTime=Tiempo requerido @@ -769,11 +786,11 @@ support.buyerTaker=comprador/Tomador BTC support.sellerTaker=vendedor/Tomador BTC support.backgroundInfo=Bisq no es una compañía, con lo que maneja las disputas de una forma diferente.\n\nSi hay disputas en el proceso de intercambio (v.g. un trader no sigue el protocolo de intercambio) la aplicación mostrará un botón de \"Abrir disputa\" después de que el periodo de intecambio haya finalizado para contactar con el árbitro.\n\nSi hay algún problema con la aplicación, el software intentará detectarlo y, si es posible, mostrará el botón \"Abrir ticket de soporte\" para contactar con el árbitro, que enviará el problema a los desarrolladores.\n\nSi tiene algún problema y no ve el botón \"Abrir ticket de soporte\" puede abrir un ticket de soporte manualmente seleccionando el intercambio causante de problemas en \"Portafolio/Intercambios Abiertos\" y pulsando \"alt + o\" o \"option + o\". Por favor use este método solo si está muy seguro de que el software no está funcionando como debería. Si tiene problemas o preguntas, por favor consulte las FAQ en la web bisq.network o postee en el forum Bisq en la sección de Soporte. -support.initialInfo="Por favor, tenga en cuenta las reglas básicas del proceso de disputa:\n1. Necesita responder a los requerimientos del árbitro en 2 días.\n2. El periodo máximo para la disputa es de 14 días.\n3. Necesita cooperar con el árbitro y entregar la información que pueda requerir para su caso.\n4. Acepta las reglas mostradas en la wiki de acuerdo de usuario cuando inició la aplicación por primera vez.\n\nPor favor lea más en detalle acerca del proceso de disputa en nuestra wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Por favor, introduzca una descripción de su problema en el campo de texto inferior. Añada tanta información como sea posible para acelerar el tiempo de resolución de la disputa.\n\nAquí tiene una lista para la información que debería proveer:\n● Si es comprador de BTC: Hizo ela transferencia de fiat o de altcoins? Si es así, hizo click en el botón "pago iniciado" de la aplicación?\n● Si es el vendedor BTC: Recibió el pago fiat o de altcoin? Si es así, hiciste click en el botón "pago recibido" de la aplicación?\n● Qué versión de Bisq está usando?\n● Qué sistema operativo está usando?\n● Si ha encontrado algún problema con transacciones fallidas por favor considere cambiar a un nuevo directorio de datos.\nA veces el directorio de datos se corrompe y lleva a bugs extraños.\nVer: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPor favor asegúrese de familiarizarse con las reglas básicas del proceso de disputa:\n● Tiene que responder a el requerimiento del árbitro en 2 días.\n● El periodo máximo de una disputa es de 14 días.\n● Necesita cooperar con el árbitro y proveer la información que le soliciten para resolver su caso.\n● Ha aceptado las reglas descritas en el documento de disputa en el acuerdo de usuario la primera vez que inició la aplicación.\n\nPuede leer más acerca del proceso de disputa en: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=Mensaje de sistema: {0} -support.youOpenedTicket=Ha abierto una solicitud de soporte. -support.youOpenedDispute=Ha abierto una solicitud de disputa.\n\n{0} -support.peerOpenedTicket=Su pareja de intercambio ha solicitado soporte debido a problemas técnicos. Por favor, espere para más instrucciones. +support.youOpenedTicket=Ha abierto una solicitud de soporte.\n\n{0}\n\nVersión Bisq: {1} +support.youOpenedDispute=Ha abierto una solicitud de disputa.\n\n{0}\n\nVersión Bisq: {1} +support.peerOpenedTicket=Su par de intercambio ha solicitado soporte debido a problemas técnicos.\n\n{0} support.peerOpenedDispute=Su pareja de intercambio ha solicitado una disputa.\n\n{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=Necesita tener al menos un árbitro seleccion account.altcoin.yourAltcoinAccounts=Sus cuentas de altcoin account.altcoin.popup.wallet.msg=Por favor, asegúrese de que sigue los requerimientos para el uso de carteras {0} tal como se describe en la página web {1}.\n¡Usar carteras desde casas de cambio centralizadas donde (a) no tiene control sobre las claves o (b) no usan un compatible es arriesgado: puede llevar a la pérdida de los fondos intercambiados!\nEl árbitro no es un especialista {2} y no puede ayudar en tales casos. account.altcoin.popup.wallet.confirm=Entiendo y confirmo que sé qué monedero tengo que utilizar. +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 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 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=Intercambiar XMR en Bisq, requiere entender y cumplir los siguientes requisitos\n\nPara enviar XMR necesita usar o el monedero oficial Monero GUI o el monedero Monero simple con la bandera store-tx-info habilitada (por defecto en las nuevas versiones). Por favor asegúrese de que puede acceder a la tx key (usar el comando get_tx_key en simplewallet) porque podría requerirse en caso de disputa.\nmonero-wallet-cli (use el comando get_tx_key)\nmonero-wallet-gui (vaya a la pestaña de historia y clique en el botón (P) para prueba de pago)\n\nAdemás de la herramienta XMR checktx (https://xmr.llcoins.net/checktx.html) la verificación también se puede cumplir dentro del monedero.\nmonero-wallet-cli : usando el comando (check_tx_key).\nmonero-wallet-gui : en Avanzado > Probar/Comprobar página.\nEn exploradores de bloque normales la transferencia no es verificable.\n\nNecesita entregar al árbitro la siguiente información en caso de disputa:\n- La clave privada de transacción (tx private key).\n- El hash de transacción.\n- La dirección pública del receptor.\n\nSi no puede proveer la información o si ha usado un monedero incompatible resultaría en la pérdida del caso de disputa. El emisor de XMR es responsable de ser capaz de verificar la transacción XMR a el árbitro en caso de disputa.\n\nNo se requiere la ID de pago, sólo la dirección pública normal.\nSi no está seguro del proceso visite (https://www.getmonero.org/resources/user-guides/prove-payment.html) o el foro Monero (https://forum.getmonero.org) para más información. 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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=Cuando use {0} sólo puede usar direcciones transparentes (que empiecen por t) y no las direcciones-z (privadas), porque el árbitro no sería capaz de verificar la transacción con direcciones-z. -account.altcoin.popup.XZC.msg=Al usar {0} puede solamente usar direcciones transparentes (trazables), porque el árbitro no sería capaz de verificar la transacciones no trazables en el explorador de bloques. -account.altcoin.popup.bch=Bitcoin cash and Bitcoin Clashic sufren de replay protection. Si usa una de estas monedes aseguŕese de que toma suficientes precauciones y entiende las implicaciones. Puede sufrir pérdidas enviando una moneda sin querer enviar las mismas monedeas a la otra cadena de bloques. Debido a que estos "airdrops" comparten la misma historia con la cadena de bloques de Bitcoin hay también riesgos de seguridad y un riesgo considerable de pérdida de privacidad.\n\nPor favor lea en el foro Bisq más acerca de este tema: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bch -account.altcoin.popup.btg=Debido a que Bitcoin Gold comparte la misma historia que la cadena de bloques de Bitcoin se producen ciertos riesgos de seguridad y un riesgo considerable de pérdida de privacidad. Si usa Bitcoin Gold asegúrese que toma suficientes medidas de precaución y entiende las implicaciones.\n\nPor favor lea en el Foro de Bisq más acerca de este tema: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc\n\n +account.altcoin.popup.ZEC.msg=Al usar Zcash solo puede usar las direcciones transparentes (que comienzan por t), no las direcciones-z (privadas), porque el árbitro no sería capaz de verificar la transacción con direcciones-z. +account.altcoin.popup.XZC.msg=Al usar Zcoin puede usar únicamente las direcciones transparentes (trazables) y no las no-trazables, porque el árbitro no sería capaz de verificar la transacción con direcciones no trazables en el explorador de bloques. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=Sus cuentas de moneda\nnacional: @@ -961,8 +980,8 @@ account.seed.info=Por favor apunte en un papel tanto las palabras semilla del mo account.seed.warn.noPw.msg=No ha establecido una contraseña de cartera que proteja la visualización de las palabras semilla.\n\n¿Quiere que se muestren las palabras semilla? account.seed.warn.noPw.yes=Sí, y no preguntar de nuevo account.seed.enterPw=Introducir password para ver las palabras semilla -account.seed.restore.info=Por favor haga una copia de seguridad antes de aplicar la restauración desde las palabras semilla. Tenga en cuenta que la restauración de cartera solo es para casos de emergencia y puede causar problemas con la base de datos interna del monedero.\nNo es el modo de aplicar una restauración de copia de seguridad! Por favor use una copia de seguridad desde el archivo de directorio de la aplicación para restaurar un estado de aplicación anterior. -account.seed.restore.ok=Ok, entiendo y quiero restaurar +account.seed.restore.info=Por favor haga una copia de seguridad antes de aplicar la restauración desde las palabras semilla. Tenga en cuenta que la restauración de cartera solo es para casos de emergencia y puede causar problemas con la base de datos interna del monedero.\nNo es el modo de aplicar una restauración de copia de seguridad! Por favor use una copia de seguridad desde el archivo de directorio de la aplicación para restaurar un estado de aplicación anterior.\n\nDespués de restaurar la aplicación se cerrará automáticamente. Después de reiniciar la aplicacion se resincronizará con la red Bitcoin. Esto puede llevar un tiempo y consumir mucha CPU, especialemente si la cartera es antigua y tiene muchas transacciones. Por favor evite interrumpir este proceso, o podría tener que borrar el archivo de la cadena SPV de nuevo o repetir el proceso de restauración. +account.seed.restore.ok=Ok, adelante con la restauración y el apagado de Bisq. #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=El precio inferior de # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=Monedero BSQ dao.tab.proposals=Gobernancia dao.tab.bonding=Obligaciones dao.tab.proofOfBurn=Tasa de listado de activos/Prueba de quemado +dao.tab.monitor=Monitor de red +dao.tab.news=Noticias dao.paidWithBsq=pagado con BSQ -dao.availableBsqBalance=Disponible -dao.availableNonBsqBalance=Balance no-BSQ disponible (BTC) -dao.unverifiedBsqBalance=No verificado (esperando confirmación) +dao.availableBsqBalance=Disponible para gastar (verificados + outputs de cambio no confirmados) +dao.verifiedBsqBalance=Balance de todas las UTXOs verificadas +dao.unconfirmedChangeBalance=Balance de todos los outputs de cambio no confirmados +dao.unverifiedBsqBalance=Balance d etodas las transacciones no verificadas (esperando una confirmación en bloque) dao.lockedForVoteBalance=Usado para votar dao.lockedInBonds=Bloqueado en bonos +dao.availableNonBsqBalance=Balance no-BSQ disponible (BTC) dao.totalBsqBalance=Balance total BSQ dao.tx.published.success=Su transacción ha sido publicada satisfactoriamente. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Resumen del ciclo de votación dao.cycle.currentPhase=Fase actual dao.cycle.currentBlockHeight=Altura de bloque acutal dao.cycle.proposal=Fase de propuesta +dao.cycle.proposal.next=Fase de siguiente propuesta dao.cycle.blindVote=Fase de votación ciega dao.cycle.voteReveal=Fase de revelado de voto dao.cycle.voteResult=Resultado de la votación dao.cycle.phaseDuration={0} bloques (≈{1}); Bloque {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Bloque {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Transacción de revelado de voto publicada +dao.voteReveal.txPublished=Su transacción de revelado de voto con ID de transacción {0} se publicó satisfactoriamente.\n\nEsto ocurre automáticamente por el software si ha participado en la votación DAO. dao.results.cycles.header=Ciclos dao.results.cycles.table.header.cycle=Ciclo @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Resultado de la votación dao.results.proposals.voting.detail.header=Resultados para la propuesta seleccionada +dao.results.exceptions=Excepción(es) del resultado de votación + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Indefinido @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=Dirección BTC del receptor # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=Tasa de listado de activo por día # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Volumen mínimo de intercambio +dao.param.ASSET_MIN_VOLUME=Volumen mínimo de intercambio para activos + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Tiempo límite para un pago de transacción alternativo +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Tasa de arbitraje en BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Límite máximo de intercambio en BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Unidad de factor de rol bonificado en BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Valor actual: {0} dao.param.blocks={0} bloques -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Duración de {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} bloque(s) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(valor por defecto) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(ha sido cambiado en la votación) +dao.results.invalidVotes=Hemos tenido votos inválidos en este ciclo de votaciones. Esto puede pasar si un voto no se distribuyó bien en la red P2P.\n\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Indefinido # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Bloquear dao.bond.reputation.lockup.headline=Confirmar transacción de bloqueo -dao.bond.reputation.lockup.details=Cantidad bloqueada: {0}\nTiempo de bloqueo: {1} bloque(s)\n\nEstá seguro de que quiere proceder? +dao.bond.reputation.lockup.details=Cantidad bloqueada: {0}\nTiempo de desbloqueo: {1} bloque(s) (≈{2})\n\nTasa de minado: {3} ({4} Satoshis/byte)\nTamaño de la transacción: {5} Kb\n\nSeguro que quiere proceder? dao.bond.reputation.unlock.headline=Confirmar desbloqueo de transacción -dao.bond.reputation.unlock.details=Cantidad a desbloquear: {0}\nTiempo de bloqueo: {1} bloque(s)\n\nEstá seguro de que quiere proceder? +dao.bond.reputation.unlock.details=Cantidad de desbloqueo: {0}\nTiempo de desbloqueo: {1} bloque(s) (≈{2})\n\nTasa de minado: {3} ({4} Satoshis/byte)\nTamaño de transacción: {5} Kb\n\nSeguro que quiere proceder? dao.bond.allBonds.header=Todos los bonos @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Bloquear dao.bond.table.button.unlock=Desbloquear dao.bond.table.button.revoke=Revocar +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=No abonado aún # suppress inspection "UnusedProperty" @@ -1252,11 +1296,15 @@ dao.bond.bondState.UNLOCKED=Bono desbloqueada # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Obligación confiscada +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Rol depositado # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Reputación abonada +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.GITHUB_ADMIN=Admin Github # suppress inspection "UnusedProperty" @@ -1270,13 +1318,17 @@ dao.bond.bondedRoleType.YOUTUBE_ADMIN=Admin Youtube # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Mantenedor Bisq # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=Mantenedor BitcoinJ-fork +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Mantenedor Netlayer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Operador de la web # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Operador Foro # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Operador de nodo semilla # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Operador nodo de precio +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Operador nodo transmisión de datos # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Operador de nodo BTC # suppress inspection "UnusedProperty" @@ -1284,6 +1336,8 @@ dao.bond.bondedRoleType.MARKETS_OPERATOR=Operador de mercados # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Operador explorador BSQ # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Operador transmisión de notificaciones móvil +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Titular del nombre de dominio # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=Admin DNS @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=Admin DNS dao.bond.bondedRoleType.MEDIATOR=Mediador # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Árbitro +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=Propietario de dirección de donaciones BTC -dao.burnBsq.assetFee=Tasa de listado de activo +dao.burnBsq.assetFee=Listado de activos dao.burnBsq.menuItem.assetFee=Tasa de listado de activo dao.burnBsq.menuItem.proofOfBurn=Prueba de quemado dao.burnBsq.header=Tasa por listar de activo @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=Fecha dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=Transacciones dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Firmar un mensaje con una clave de un periodo de prueba o transacción de quemado -dao.proofOfBurn.verify.window.title=Verifica un mensaje con la clave de la transacción de prueba de quemado. +dao.proofOfBurn.signature.window.title=Firmar un mensaje con clave de transacción de quemado +dao.proofOfBurn.verify.window.title=Verificar un mensaje con clave de transacción de quemado. dao.proofOfBurn.copySig=Copiar firma al portapapeles dao.proofOfBurn.sign=Firma dao.proofOfBurn.message=Mensaje dao.proofOfBurn.sig=Firma dao.proofOfBurn.verify=Verificar -dao.proofOfBurn.verify.header=Verificar mensaje de una clave desde prueba o transacción de quemado +dao.proofOfBurn.verify.header=Verificar mensaje con clave de transacción de quemado dao.proofOfBurn.verificationResult.ok=Verificación exitosa dao.proofOfBurn.verificationResult.failed=Verificación fallida @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Revelar voto # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Resultado de la votación +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Petición de compensación # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=Propuesta genérica # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Propuesta para confiscar un bono +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Petición de compensación # suppress inspection "UnusedProperty" @@ -1398,28 +1458,30 @@ dao.proposal.type.short.GENERIC=Propuesta genérica # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Confiscar un bono - dao.proposal.details=Detalles de propuesta dao.proposal.selectedProposal=Propuesta seleccionada dao.proposal.active.header=Propuestas del ciclo actual dao.proposal.active.remove.confirm=Está seguro que quiere eliminar esta propuesta?\nLas tasas de propuesta pagadas se perderán. dao.proposal.active.remove.doRemove=Sí, eliminar mi propuesta dao.proposal.active.remove.failed=No se pudo eliminar la propuesta +dao.proposal.myVote.title=Votando dao.proposal.myVote.accept=Aceptar propuesta dao.proposal.myVote.reject=Eliminar propuesta dao.proposal.myVote.removeMyVote=Ignorar propuesta dao.proposal.myVote.merit=Peso del voto de BSQ adquiridos dao.proposal.myVote.stake=Peso de voto desde stake -dao.proposal.myVote.blindVoteTxId=Id de la transacción de voto ciego dao.proposal.myVote.revealTxId=ID de transacción de revelado de voto -dao.proposal.myVote.stake.prompt=Balance máximo disponible para votar: {0} -dao.proposal.votes.header=otar en todas las propuestas -dao.proposal.votes.header.voted=Mi voto -dao.proposal.myVote.button=otar en todas las propuestas +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Seleccionar tipo de propuesta +dao.proposal.create.phase.inactive=Por favor espere a la próxima fase de propuesta dao.proposal.create.proposalType=Tipo de propuesta -dao.proposal.create.createNew=Hacer una nueva propuesta -dao.proposal.create.create.button=Hacer propuesta +dao.proposal.create.new=Hacer una nueva propuesta +dao.proposal.create.button=Hacer propuesta +dao.proposal.create.publish=Publicar propuesta +dao.proposal.create.publishing=Publicación de propuesta en progreso... dao.proposal=propuesta dao.proposal.display.type=Tipo de propuesta dao.proposal.display.name=Nombre/nick @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=Aceptado dao.proposal.display.myVote.rejected=Rechazado dao.proposal.display.myVote.ignored=Ignorado dao.proposal.myVote.summary=Votado: {0}; Peso del voto: {1} (obtenido: {2} + stake: {3}); +dao.proposal.myVote.invalid=Votación inválida dao.proposal.voteResult.success=Aceptado dao.proposal.voteResult.failed=Rechadazo @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=Activo a eliminar dao.blindVote=Voto ciego dao.blindVote.startPublishing=Publicando transacción de voto ciego.... -dao.blindVote.success=Su voto ciego ha sido publicado con éxito. +dao.blindVote.success=Su voto ciego ha sido publicado satisfactoriamente.\n\nPor favor tenga en cuenta que tiene que estar onloine en la fase de revelado de votos para que la aplicación Bisq pueda publicar la transacción de revelado de voto. Sin la transacción de revelado de voto, su voto será inválido!ª dao.wallet.menuItem.send=Enviar dao.wallet.menuItem.receive=Recibir dao.wallet.menuItem.transactions=Transacciones dao.wallet.dashboard.myBalance=Balance de mi cartera -dao.wallet.dashboard.distribution=Distribución de todo BSQ -dao.wallet.dashboard.locked=Estado global de BSQ bloqueados -dao.wallet.dashboard.market=Datos de mercad -dao.wallet.dashboard.genesis=Transacción génesis -dao.wallet.dashboard.txDetails=Estadísticas de transacción BSQ -dao.wallet.dashboard.genesisBlockHeight=Altura de bloque génesis -dao.wallet.dashboard.genesisTxId=ID transacción génesis -dao.wallet.dashboard.genesisIssueAmount=BSQ emitidos en la transacción génesis -dao.wallet.dashboard.compRequestIssueAmount=BSQ emitidos para solicitudes de compensación -dao.wallet.dashboard.reimbursementAmount=BSQ emitidos para solicitudes de reembolso -dao.wallet.dashboard.availableAmount=BSQ totales disponibles -dao.wallet.dashboard.burntAmount=BSQ quemados (tasas) -dao.wallet.dashboard.totalLockedUpAmount=Bloqueados en obligaciones -dao.wallet.dashboard.totalUnlockingAmount=Desbloqueando BSQ de bonos -dao.wallet.dashboard.totalUnlockedAmount=BSQ desbloqueados de bonos -dao.wallet.dashboard.totalConfiscatedAmount=BSQ confiscados de bonos -dao.wallet.dashboard.allTx=Número de todas las transacciones BSQ -dao.wallet.dashboard.utxo=Número de todos los outputs de transacciones no gastadas -dao.wallet.dashboard.compensationIssuanceTx=Número de todas las transacciones emitidas de solicitudes de compensación -dao.wallet.dashboard.reimbursementIssuanceTx=Número de todas las transacciones emitidas de solicitud de reembolso -dao.wallet.dashboard.burntTx=Número de todas las transacciones de tasa de pago -dao.wallet.dashboard.price=Último precio intercambio BSQ/BTC (en Bisq) -dao.wallet.dashboard.marketCap=Capitalización de mercado (basado en el precio de intercambio) - -dao.wallet.receive.fundYourWallet=Dotar de fondos su monedero -dao.wallet.receive.bsqAddress=Dirección cartera BSQ + +dao.wallet.receive.fundYourWallet=Su dirección para recibir BSQ +dao.wallet.receive.bsqAddress=Dirección monedero BSQ (Dirección fresca, sin usar) + +dao.wallet.receive.dao.headline=La DAO Bisq +dao.wallet.receive.daoInfo=TAl como el exchange Bisq es descentralizado y resistente a la censura, lo es su modelo de governanza - y la DAO BISQ y el token BSQ son herramientas que lo hacen posible. +dao.wallet.receive.daoInfo.button=Aprender más acerca de la DAO Bisq +dao.wallet.receive.daoTestnetInfo=La red principal de la DAO Bisq aún no se ha lanzado pero puede aprender acerca de la DAO ejecutándola en la testnet. +dao.wallet.receive.daoTestnetInfo.button=Como ejecutar la DAO BIsq en testnet +dao.wallet.receive.daoContributorInfo=Si ha contribuido a Bisq por favor use la dirección BSQ de abajo y haga una solicitud por haber participado en la distribución de BSQ génesis. +dao.wallet.receive.daoContributorInfo.button=Cómo ser parte de la distribución génesis de BSQ dao.wallet.send.sendFunds=Enviar fondos dao.wallet.send.sendBtcFunds=Enviar fondos no-BSQ (BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=Último bloque verificado: {0} dao.wallet.chainHeightSyncing=Esperando bloques... {0} bloques verificados de {1} dao.wallet.tx.type=Tipo +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=No reconocido # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=Solicitud/emisión de compensación dao.tx.issuanceFromCompReq.tooltip=Solicitud de compensación que lleva a emitir nuevos BSQ.\nFecha de emisión: {0} dao.tx.issuanceFromReimbursement=Solicitud de reembolso/emisión dao.tx.issuanceFromReimbursement.tooltip=Solicitud de reembolso que lleva a una emisión de nuevos BSQ.\nFecha de emisión: {0} -dao.proposal.create.missingBsqFunds=No tiene suficientes fondos para crear la propuesta.\nFaltan: {0} +dao.proposal.create.missingBsqFunds=No tiene suficientes fondos para crear la propuesta. Si tiene una transacción BSQ no confirmada necesita esperar a una confirmación porque los BSQ se validan solo si están incluidos en un bloque.\nSe necesitan: {0} + +dao.proposal.create.missingBsqFundsForBond=No tiene suficientes fondos BSQ para este rol. Aún puede publicar esta propuesta, pero necesitará la cantidad BSQ requerida para este rol si es aceptada.\nSe necesitan: {0} + +dao.proposal.create.missingMinerFeeFunds=No tiene suficientes fondos BTC para crear la propuesta de transacción. Cualquier transacción BSQ requiere también una tasa de minado en BTC.\nSe necesitan: {0} dao.feeTx.confirm=Confirmar transacción {0} dao.feeTx.confirm.details={0} tasa: {1}\nTasa de minado: {2} ({3} Satoshis/byte)\nTamaño de la transacción: {4} Kb\n\nEstá seguro de que quire publicar la transacción {5}? +dao.news.bisqDAO.title=La DAO BISQ +dao.news.bisqDAO.description=Tal como el exchange Bisq es descentralizado y resistente a la censura, lo es su modelo de governanza - y la DAO BISQ y el token BSQ son herramientas que lo hacen posible. +dao.news.bisqDAO.readMoreLink=Aprender más acerca de la DAO Bisq. + +dao.news.pastContribution.title=Hiciste contribuciones en el pasado? Solicita BSQ +dao.news.pastContribution.description=Si ha contribuido a Bisq por favor use la dirección BSQ de abajo y haga una solicitud por haber participado en la distribución de BSQ génesis. +dao.news.pastContribution.yourAddress=Su dirección de monedero BSQ +dao.news.pastContribution.requestNow=Solicitar ahora + +dao.news.DAOOnTestnet.title=CORRER LA DAO BISQ EN TESTNET +dao.news.DAOOnTestnet.description=La red principal de la DAO Bisq aún no se ha lanzado pero puede aprender acerca de la DAO ejecutándola en la testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Cambiar a Modo Testnet +dao.news.DAOOnTestnet.firstSection.content=Cambiar a la testnet desde la pantalla de Configuración +dao.news.DAOOnTestnet.secondSection.title=2. Adquirir algunos BSQ +dao.news.DAOOnTestnet.secondSection.content=Solicitar BSQ en Slack o comprar BSQ en Bisq +dao.news.DAOOnTestnet.thirdSection.title=3. Participar en un ciclo de votación +dao.news.DAOOnTestnet.thirdSection.content=Realizar propuestas y votar propuestas para cambiar diversos aspectos de Bisq +dao.news.DAOOnTestnet.fourthSection.title=4. Explorar un explorador de bloques BSQ +dao.news.DAOOnTestnet.fourthSection.content=Como BSQ es simplemente bitcoin, puede ver las transacciones BSQ en cualquier explorador de bloques de bitcoin +dao.news.DAOOnTestnet.readMoreLink=Leer toda la documentación + +dao.monitor.daoState=Estado DAO +dao.monitor.proposals=Estado de propuestas +dao.monitor.blindVotes=Estado de votos a ciegas + +dao.monitor.table.peers=Pares +dao.monitor.table.conflicts=Conflictos +dao.monitor.state=Estado +dao.monitor.requestAlHashes=Solicitar todos los hashes +dao.monitor.resync=Resincronizar estado DAO +dao.monitor.table.header.cycleBlockHeight=Ciclo / altura de bloque +dao.monitor.table.cycleBlockHeight=Ciclo {0} / bloque {1} + +dao.monitor.daoState.headline=Estado DAO +dao.monitor.daoState.daoStateInSync=Su estado local DAO está en consenso con la red +dao.monitor.daoState.daoStateNotInSync=Su estado local DAO no está en consenso con la red. Por favor, resincronice su estado DAO. +dao.monitor.daoState.table.headline=Cadena de hashes de estado de DAO +dao.monitor.daoState.table.blockHeight=Altura de bloque +dao.monitor.daoState.table.hash=Estado de hashes de DAO +dao.monitor.daoState.table.prev=Hash previo +dao.monitor.daoState.conflictTable.headline=Estado de hashes DAO desde pares en conflicto + +dao.monitor.proposal.headline=Estado de propuestas +dao.monitor.proposal.daoStateInSync=El estado de sus propuestas local está en consenso con la red +dao.monitor.proposal.daoStateNotInSync=Su estado local de propuestas no está en consenso con la red. Por avor reinicie su aplicación. +dao.monitor.proposal.table.headline=Estado de hashes de cadena de propuesta +dao.monitor.proposal.conflictTable.headline=Hashes de estado de propuesta desde pares en conflicto + +dao.monitor.proposal.table.hash=Estado de hash de propuesta +dao.monitor.proposal.table.prev=Hash previo +dao.monitor.proposal.table.numProposals=Número de propuestas + + +dao.monitor.blindVote.headline=Estado de votos a ciegas +dao.monitor.blindVote.daoStateInSync=Su estado de votos a ciegas local esta en consenso con la red +dao.monitor.blindVote.daoStateNotInSync=Su estado de votos a ciegas local no está en consenso con la red. Por favor reincie su aplicación +dao.monitor.blindVote.table.headline=Estado de hashes de cadena de voto a ciegas +dao.monitor.blindVote.conflictTable.headline=Estado de hashes de voto a ciegas desde pares en conflicto +dao.monitor.blindVote.table.hash=Estado de hash de voto a ciegas +dao.monitor.blindVote.table.prev=Hash previo +dao.monitor.blindVote.table.numBlindVotes=Número de votos a ciegas + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Datos de mercad +dao.factsAndFigures.dashboard.price=Último precio intercambio BSQ/BTC (en Bisq) +dao.factsAndFigures.dashboard.marketCap=Capitalización de mercado (basado en el precio de intercambio) +dao.factsAndFigures.dashboard.availableAmount=BSQ totales disponibles + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ emitidos en la transacción génesis +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ emitidos para solicitudes de compensación +dao.factsAndFigures.supply.reimbursementAmount=BSQ emitidos para solicitudes de reembolso + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Estado global de BSQ bloqueados +dao.factsAndFigures.supply.totalLockedUpAmount=Bloqueados en obligaciones +dao.factsAndFigures.supply.totalUnlockingAmount=Desbloqueando BSQ de bonos +dao.factsAndFigures.supply.totalUnlockedAmount=BSQ desbloqueados de bonos +dao.factsAndFigures.supply.totalConfiscatedAmount=BSQ confiscados de bonos +dao.factsAndFigures.supply.burntAmount=BSQ quemados (tasas) + +dao.factsAndFigures.transactions.genesis=Transacción génesis +dao.factsAndFigures.transactions.genesisBlockHeight=Altura de bloque génesis +dao.factsAndFigures.transactions.genesisTxId=ID transacción génesis +dao.factsAndFigures.transactions.txDetails=Estadísticas de transacción BSQ +dao.factsAndFigures.transactions.allTx=Número de todas las transacciones BSQ +dao.factsAndFigures.transactions.utxo=Número de todos los outputs de transacciones no gastadas +dao.factsAndFigures.transactions.compensationIssuanceTx=Número de todas las transacciones emitidas de solicitudes de compensación +dao.factsAndFigures.transactions.reimbursementIssuanceTx=Número de todas las transacciones emitidas de solicitud de reembolso +dao.factsAndFigures.transactions.burntTx=Número de todas las transacciones de tasa de pago #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Nodos semilla filtrados (direcciones onion separadas por c filterWindow.priceRelayNode=nodos de retransmisión de precio filtrados (direcciones onion separadas por coma) filterWindow.btcNode=Nodos Bitcoin filtrados (direcciones + puerto separadas por coma) filterWindow.preventPublicBtcNetwork=Prevenir uso público de la red Bitcoin +filterWindow.disableDao=Deshabilitar DAO filterWindow.add=Añadir filtro filterWindow.remove=Eliminar filtro @@ -1748,7 +1896,7 @@ popup.headline.error=Error popup.doNotShowAgain=No mostrar de nuevo popup.reportError.log=Abrir archivo de registro popup.reportError.gitHub=Reportar al rastreador de problemas de Github -popup.reportError={0}\n\nTo help us to improve the software please report this bug by opening a new issue at https://github.com/bisq-network/bisq/issues.\nThe above error message will be copied to the clipboard when you click either of the buttons below.\nIt will make debugging easier if you include the bisq.log file by pressing "Open log file", saving a copy, and attaching it to your bug report. +popup.reportError={0}\n\nPara ayudarnos a mejorar el software por favor reporte el fallo en nuestro rastreador de fallos en https://github.com/bisq-network/bisq/issues.\nEl mensaje de error será copiado al portapapeles cuando clique cualquiera de los botones inferiores.\nHará el depurado de fallos más fácil si puede adjuntar el archivo bisq.log presionando "abrir archivo de log", guardando una copia y adjuntándola en su informe de errores. popup.error.tryRestart=Por favor pruebe a reiniciar la aplicación y comprobar su conexión a la red para ver si puede resolver el problema. popup.error.takeOfferRequestFailed=Un error ocurrió cuando alguien intentó tomar una de sus ofertas:/n{0} @@ -1805,10 +1953,10 @@ popup.shutDownInProgress.msg=Cerrrar la aplicación puede llevar unos segundos.\ popup.attention.forTradeWithId=Se requiere atención para el intercambio con ID {0} popup.roundedFiatValues.headline=Nueva característica de privacidad: Valores redondeados a fiat -popup.roundedFiatValues.msg=Para incrementar la privacidad de sus intercambios la cantidad de {0} se redondeó.\n\nDependiendo de la versión del cliente pagará o recibirá valores con decimales o redondeados.\n\nAmbos valores serán cumplirán con el protocolo de intercambio.\n\nTambién tenga en cuenta que los valores BTC cambian automáticamente para igualar la cantidad fiat redondeada tan ajustada como sea posible. +popup.roundedFiatValues.msg=Para incrementar la privacidad de sus intercambios la cantidad de {0} se redondeó.\n\nDependiendo de la versión del cliente pagará o recibirá valores con decimales o redondeados.\n\nAmbos valores cumplen a partir de ahora con el protocolo de intercambio.\n\nTambién tenga en cuenta que los valores BTC cambian automáticamente para igualar la cantidad fiat redondeada tan ajustada como sea posible. -popup.info.multiplePaymentAccounts.headline=Multiple payment accounts available -popup.info.multiplePaymentAccounts.msg=You have multiple payment accounts available for this offer. Please make sure you've picked the right one. +popup.info.multiplePaymentAccounts.headline=Múltiples cuentas de pago disponibles +popup.info.multiplePaymentAccounts.msg=Tiene múltiples cuentes de pago disponibles para esta oferta. Por favor, asegúrese de que ha elegido la correcta. #################################################################### @@ -1955,6 +2103,10 @@ BTC_MAINNET=Red principal de Bitcoin BTC_TESTNET=Red de prueba de Bitcoin # suppress inspection "UnusedProperty" BTC_REGTEST=Regtest Bitcoin +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Red de prueba de Bitcoin +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=Año time.month=Mes @@ -2025,6 +2177,8 @@ payment.country=País payment.extras=Requerimientos extra payment.email.mobile=Email o número de móvil payment.altcoin.address=Dirección altcoi +payment.altcoin.tradeInstantCheckbox=Intercambio instantáneo (en una hora) con esta altcoin +payment.altcoin.tradeInstant.popup=Para intercambios instantáneos se requiere que ambos pares estén en linea para poder completar el intercambio en menos de 1 hora.\n\nSi tiene ofertas abiertas y no está disponible, por favor deshabilite esas ofertas en la pantall 'Portafolio'. payment.altcoin=Altcoin payment.select.altcoin=Seleccione o busque altcoin payment.secret=Pregunta secreta @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=Correo electrónico o núm. de telefóno payment.venmo.venmoUserName=Nombre de usuario Venmo payment.popmoney.accountId=Correo electrónico o núm. de telefóno -payment.revolut.email=Correo electrónico o núm. de telefóno +payment.revolut.email=Email +payment.revolut.phoneNr=Número de teléfono registrado payment.promptPay.promptPayId=Citizen ID/Tax ID o número de teléfono payment.supportedCurrencies=Monedas soportadas payment.limitations=Límitaciones: @@ -2082,6 +2237,10 @@ payment.limits.info=Por favor, tenga en cuenta que todas las transferencias banc 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=Por favor asegúrese de que el número de teléfono que ha usado para la cuenta Revolut está registrada en Revolut, de lo contrario el comprador BTC no podrá enviarle los fondos. + +payment.usPostalMoneyOrder.info=Los giros postales son uno de los métodos de adquisición de fiat más privados disponibles en Bisq\n\nAún así, por favor tenga en cuenta el elevado riesgo potencial asociado a su uso. Bisq no contrae ninguna responsabilidad en caso de que el dinero enviado sea robado, y los árbitros en estos casos adjudiquen los BTC a el emisor del giro postal, siempre que puedan mostrar recibos e información de seguimiento. Es aconsejable par el emisor escribir el nombre del emisor en el giro postal, para minimizar el riesgo de que el giro postal sea retirado por otra persona. + payment.f2f.contact=Información de contacto payment.f2f.contact.prompt=Cómo quiere ser contactado por el par de intercambio? (dirección email, número de teléfono...) payment.f2f.city=Ciudad para la reunión 'cara a cara' @@ -2128,16 +2287,10 @@ F2F_SHORT=F2F # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altcoins PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Altcoins PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} debe consistir de {1} número(s). validation.invalidInput=Entrada inválida: {0} validation.accountNrFormat=El número de cuenta debe ser del formato: {0} validation.altcoin.wrongStructure=La validación de dirección falló porque no se corresponde con la estructura de una dirección {0}. +validation.altcoin.ltz.zAddressesNotSupported=Dirección LTZ tiene que empezar con L. Las direcciones que empiezan por z no se soportan. validation.altcoin.zAddressesNotSupported=La dirección ZEC tiene que empezar con t. Las direcciones que comienzan con z no están soportadas. validation.altcoin.invalidAddress=La dirección no es una dirección {0} válida! {1} validation.bic.invalidLength=La longitud de la entrada no es 8 ni 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Debe contener solamente letras, núm validation.interacETransfer.invalidAnswer=Debe ser una palabra y contener solamente letras, números, y/o el símbolo - validation.inputTooLarge=El valor introducido no debe ser mayor que {0} validation.inputTooSmall=Lo introducido tiene que ser mayor que {0} +validation.inputToBeAtLeast=El valor introducido tiene que ser al menos {0} validation.amountBelowDust=No se permiten cantidades por debajo del límite dust de {0} validation.length=La longitud debe estar entre {0} y {1} validation.pattern=Lo introducido debe ser de formato: {0} validation.noHexString=Lo introducido no es un formato HEX. validation.advancedCash.invalidFormat=Tiene que ser un formato de email o ID de cartera válido: X000000000000 +validation.invalidUrl=No es una URL válida +validation.mustBeDifferent=Su valor introducido debe ser diferente a el valor actual +validation.cannotBeChanged=Este parámetro no se puede cambiar +validation.numberFormatException=Excepción en formato de número {0} +validation.mustNotBeNegative=El valor introducido no debe ser negativo diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 75ef08bc506..1854bc075b4 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -104,7 +104,7 @@ shared.belowInPercent= ٪ زیر قیمت بازار shared.aboveInPercent= ٪ بالای قیمت بازار shared.enterPercentageValue=ارزش ٪ را وارد کنید shared.OR=یا -shared.notEnoughFunds=شما وجه کافی در کیف Bisq خود ندارید. \nبه {0} نیاز دارید، اما فقط {1} در کیف Bisq خود دارید. \n\nلطفاً وجه موردنیاز معامله را از کیف پولی دیگر یا از کیف Bisq در بخش \"وجوه/دریافت وجوه\"، تأمین نمایید. +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.waitingForFunds=در انتظار دریافت وجه... shared.depositTransactionId=شناسه تراکنش وجه دریافتی shared.TheBTCBuyer=خریدار بیتکوین @@ -113,12 +113,13 @@ shared.reasonForPayment=دلیل پرداخت shared.sendingConfirmation=در حال ارسال تاییدیه... shared.sendingConfirmationAgain=لطفاً تاییدیه را دوباره ارسال نمایید shared.exportCSV=به csv خروجی بگیرید +shared.exportJSON=Export to JSON shared.noDateAvailable=تاریخ موجود نیست shared.arbitratorsFee=هزینه داور shared.noDetailsAvailable=جزئیاتی در دسترس نیست shared.notUsedYet=هنوز مورد استفاده قرار نگرفته shared.date=تاریخ -shared.sendFundsDetailsWithFee=ارسال {0}:\nاز آدرس: {1}\nبه آدرس گیرنده: {2}.\nهزینه موردنیاز تراکنش: {3} ( {4} ساتوشی بر بایت) است\nسایز تراکنش: {5} کیلوبایت\nمقدار دریافتی گیرنده: {6}\n\nآیا از برداشت این مبلغ مطمئن هستید؟ +shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired transaction 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.copyToClipboard=کپی در کلیپ‌بورد shared.language=زبان shared.country=کشور @@ -196,6 +197,8 @@ shared.interval=دوره shared.actions=عملیات shared.buyerUpperCase=خریدار shared.sellerUpperCase=فروشنده +shared.new=NEW +shared.blindVoteTxId=شناسه تراکنش رای ناشناس #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=قفل شده mainView.footer.usingTor=(استفاده از Tor) mainView.footer.localhostBitcoinNode=(لوکال هاست) mainView.footer.btcInfo=همتایان شبکه بیتکوین: {0} / {1} {2} -mainView.footer.btcInfo.initializing= راه اندازی اولیه -mainView.footer.btcInfo.synchronizedWith=همگام شده با -mainView.footer.btcInfo.connectingTo=در حال ایجاد ارتباط با +mainView.footer.btcInfo.initializing=Connecting to Bitcoin network +mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO +mainView.footer.btcInfo.synchronizedWith=Synchronized with +mainView.footer.btcInfo.connectingTo=Connecting to mainView.footer.btcInfo.connectionFailed=ارتباط ناموفق بود mainView.footer.p2pInfo=همتایان شبکه P2P: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=مقدار BTC برای {0} createOffer.amountPriceBox.buy.volumeDescription=مقدار در {0} به منظور خرج کردن createOffer.amountPriceBox.sell.volumeDescription=مقدار در {0} به منظور دریافت نمودن createOffer.amountPriceBox.minAmountDescription=حداقل مقدار بیتکوین -createOffer.securityDeposit.prompt=سپرده‌ی اطمینان در بیتکوین +createOffer.securityDeposit.prompt=سپرده‌ی اطمینان createOffer.fundsBox.title=پیشنهاد خود را تامین وجه نمایید createOffer.fundsBox.offerFee=کارمزد معامله createOffer.fundsBox.networkFee=کارمزد استخراج createOffer.fundsBox.placeOfferSpinnerInfo=انتشار پیشنهاد در حال انجام است ... createOffer.fundsBox.paymentLabel=معامله Bisq با شناسه‌ی {0} createOffer.fundsBox.fundsStructure=({0} سپرده‌ی اطمینان، {1} هزینه‌ی معامله، {2} هزینه‌ی تراکنش شبکه) +createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=پیشنهاد شما، منتشر شد. createOffer.success.info=شما می توانید پیشنهادهای باز خود را در \"سبد سهام/پیشنهادهای باز من\" مدیریت نمایید. createOffer.info.sellAtMarketPrice=شما همیشه به نرخ روز بازار خواهید فروخت، زیرا قیمت پیشنهادتان به طور مداوم به روزرسانی خواهد شد. @@ -387,6 +392,7 @@ createOffer.alreadyFunded=در حال حاضر آن پیشنهاد را تامی createOffer.createOfferFundWalletInfo.headline=پیشنهاد خود را تامین وجه نمایید # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=مقدار معامله:{0}\n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} and {1} createOffer.createOfferFundWalletInfo.msg=شما باید {0} برای این پیشنهاد، سپرده بگذارید.\nآن وجوه در کیف پول محلی شما ذخیره شده اند و هنگامی که کسی پیشنهاد شما را دریافت می کند، به آدرس سپرده چند امضایی قفل خواهد شد.\n\nمقدار مذکور، مجموع موارد ذیل است:\n{1} - سپرده‌ی اطمینان شما: {2}\n-هزینه معامله: {3}\n-هزینه تراکنش شبکه: {4}\nشما هنگام تامین مالی معامله‌ی خود، می‌توانید بین دو گزینه انتخاب کنید:\n- از کیف پول Bisq خود استفاده کنید (این روش راحت است، اما ممکن است تراکنش‌ها قابل رصد شوند)، یا\n- از کیف پول خارجی انتقال دهید (به طور بالقوه‌ای این روش ایمن‌تر و محافظ حریم خصوصی شما است)\n\nشما تمام گزینه‌ها و جزئیات تامین مالی را پس از بستن این پنجره، خواهید دید. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=قیمتی که شما وارد کرده ا createOffer.changePrice=تغییر قیمت createOffer.tac=با انتشار این پیشنهاد، می‌پذیرم که با هر معامله گری که شرایط تعیین شده در این صفحه را دارا می‌باشد، معامله کنم. createOffer.currencyForFee=هزینه‌ی معامله -createOffer.setDeposit=تنظیم سپرده‌ی اطمینان خریدار +createOffer.setDeposit=Set buyer's security deposit (%) +createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) +createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} #################################################################### @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=دوست داریم تجربه شما را بشن tradeFeedbackWindow.msg.part2=اگر سوالی دارید یا مشکلی را تجربه کرده‌اید، لطفا با سایر کاربران و شرکت کننده ها از طریق انجمن Bisq که در ذیل ارائه شده، به اشتراک بگذارید: tradeFeedbackWindow.msg.part3=بابت استفاده از Bisq، از شما متشکریم! + +daoTestingFeedbackWindow.title=Thank you for testing the Bisq DAO +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Visit forum +daoTestingFeedbackWindow.msg.part3=بابت استفاده از Bisq، از شما متشکریم! + portfolio.pending.role=نقش من portfolio.pending.tradeInformation=اطلاعات معامله portfolio.pending.remainingTime=زمان باقیمانده @@ -769,11 +786,11 @@ support.buyerTaker=خریدار/پذیرنده‌ی بیتکوین support.sellerTaker=فروشنده/پذیرنده‌ی بیتکوین support.backgroundInfo=Bisq یک شرکت نیست، درنتیجه روش مواجه آن با احتلافات متفاوت است.\n\nاگر اختلافی در روند معامله به وجود بیاید (مثلا یکی از معامله گران از پروتکل معامله پیروی نکند) نرم‌افزار، بعد از اتمام زمان معامله، دکمه \"ایجاد اختلاف\" را برای تماس با داور معامله نمایش می‌دهد.\n\nاگر مشکلی برای خود نرم‌افزار به وجود بیاید، خودش سعی می‌کند آن را شناسایی کند و اگر امکانش باشد دکمه \"ایجاد تیکت پشتیبانی\" را برای تماس با داور معامله نمایش می‌دهد تا او مشکل را به توسعه‌دهندگان منتقل کند.\n\nاگر مشکلی برای نرم‌افزار به وجود آمد و شما دکمه \"ایجاد تیکت پشتیبانی\" را ندیدید، برای ایجاد یک تیکت پشتیبانی به صورت دستی، معامله‌ای که ایجاد مشکل کرده است را از طریق \"سبد سهام/معاملات باز\" و فشردن کلیدهای \"alt + o\" یا \"option + o\" انتخاب کنید. لطفا تنها در صورتی از این روش استفاده کنید که مطمئن هستید نرم‌افزار آنگونه که باید عمل نکرده است. اگر مشکل یا سوالی دارید، لطفا بخش سوالات متداول را در سایت bisq.network بررسی کنید و یا آن را در تالار گفتگوی Bisq در بخش پشتیبانی مطرح کنید. -support.initialInfo=لطفا این قوانین پایه‌ای را برای فرآیند اختلاف در نظر داشته باشید:\n1. شما باید حداکثر تا ۲ روز به درخواست داور پاسخ بدهید.\n2. حدکاثر دوره زمانی یک اختلاف 14 روز است.\n3. شما باید با تحویل شواهد خودتان، آنچه را که داور درخواست می‌کند برآورده سازید.\n4. هنگامی که شما برای اولین بار اقدام به استفاده از نرم‌افزار کردید، قوانین ذکر شده در ویکی در موافقتنامه کاربر را پذیرفته‌اید.\n\nلطفا جزئیات بیشتر در مورد فرآیند اختلاف را در ویکی ما بخوانید:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=پیغام سیستم: {0} -support.youOpenedTicket=درخواستی برای پشتیبانی باز کردید. -support.youOpenedDispute=درخواستی برای مناقشه باز کردید.\n\n{0} -support.peerOpenedTicket=همتای معامله‌ی شما به دلیل مشکلات فنی درخواست پشتیبانی کرده است. لطفاً منتظر دستورالعمل‌های بعدی باشید. +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=همتای معامله‌ی شما درخواست مناقشه کرده است.\n\n{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=شما باید حداقل یک داور را account.altcoin.yourAltcoinAccounts=حساب‌های آلت‌کوین شما account.altcoin.popup.wallet.msg=لطفا مطمئن شوید که الزامات استفاده از کیف پول {0} را همان طور که در صفحه {1} شرح داده شده است، رعایت می‌کنید.\nاستفاده از کیف‌پول صرافی‌های متمرکز که در آن (الف) کلید های خود را تحت کنترل ندارید و یا (ب) از یک نرم افزار کیف پول ناسازگار می‌کند می تواند منجر به از دست رفتن وجوه معامله شده شود!\nداور یک متخصص {2} نیست و در چنین مواردی نمی تواند کمک کند. account.altcoin.popup.wallet.confirm=من می فهمم و تأیید می کنم که می دانم از کدام کیف پول باید استفاده کنم. +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 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 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=اگر میخواهید XMR را در Bisq معامله کنید، لطفا مطمئن شوید که شرایط زیر را درک کرده و انجام می دهید:\n\nبرای ارسال XMR، شما باید یا از کیف پول Monero GUI یا کیف پول Monero CLI که store-tx-info در آن فعال است (به طور پیش فرض در نسخه‌های جدید فعال است) استفاده کنید. لطفا مطمئن شوید که به کلید تراکنش دسترسی دارید چرا که در صورت بروز اختلاف به آن نیاز دارید.\nدر monero-wallet-cli از دستور(get_tx_key) استفاده کنید\n در monero-wallet-gui برای دیدن اثبات پرداخت به برگه (تاریخچه) بروید و روی دکمه (P) بزنید\nعلاوه بر ابزار XMR checktx به آدرس (https://xmr.llcoins.net/checktx.html)  تایید تراکنش از داخل کیف‌پول هم قابل انجام است.\nدر monero-wallet-cli از طریق دستور (check_tx_key) \nدر monero-wallet-gui از طریق منوی Advanced > Prove/Check\nدر کاوشگرهای بلاک عادی، انتقال قابل تایید نیست.\n\nدر صورت بروز اختلاف، باید داده های زیر را در اختیار داور بگذارید:\n- کلید خصوصی تراکنش\n- هش تراکنش\n- آدرس عمومی گیرنده\n\nاگر شما نمی توانید داده های بالا را ارائه نموده یا اگر از کیف پول ناسازگار استفاده کرده اید، این موضوع می تواند منجر به محکوم شدن در یک اختلاف شود. فرستنده ی XMR باید بتواند در صورت بروز اختلاف انتقال XMR را به داوراثبات کند.\n\nشناسه پرداخت لازم نیست، فقط آدرس عمومی معمولی.\n\nاگر شما درباره این فرآیند مطمئن نیستید به (https://www.getmonero.org/resources/user-guides/prove-payment.html) سر بزنید و یا برای کسب اطلاعات بیشتر به تالار گفتگوی مونرو (https://forum.getmonero.org) مراجعه نمایید. account.altcoin.popup.blur.msg=اگر می‌خواهید BLUR را در Bisq معامله کنید لطفا مطمئن شوید که پیشنیازهای زیر را می‌فهمید و آنها را انجام داده‌اید:\n\nبرای ارسال BLUR باید از کیف‌پول CLI یا GUI مربوط به Blur Network استفاده کنید.\n\nاگر از یک کیف‌پول CLI استفاده می‌کنید، بعد از یک انتقال یک هش تراکنش (شناسه تراکنش) برای شما به نمایش در می‌آید. شما باید این اطلاعات را ذخیره کنید. بلافاصله بعد از ارسال یک انتقال، باید با دستور 'get_tx_key' کلید خصوصی تراکنش را بدست بیاورید. اگر موفق به انجام این مرحله نشوید، بعدا نمی‌توانید کلید را بدست بیاورید.\n\nاگر از کیف پول Blur Network GUI استفاده می‌کنید، کلید خصوصی تراکنش و شناسه تراکنش به راحتی در برگه «تاریخچه» در دسترس هستند. بلافاصله بعد از ارسال، تراکنش مورد نظر را پیدا کنید و روی نماد «؟» ذر گوشه جعبه نمایشی حاوی تراکنش کلیک کنید.\n\nاگر زمانی داوری مورد نیاز بود، باید اطلاعات زیر را به داور ارائه بدهید: 1.) شناسه تراکنش، 2.) کلید خصوصی تراکنش و 3.) آدرس گیرنده. بعد از آن، داور، انتقال را از طریق نمایشگر تراکنش Blur به آدرس (https://blur.cash/#tx-viewer) تایید می‌کند.\n\nاگر نتوانید اطلاعات مورد نیاز را به داور ارائه دهید، اختلاف به وجود آمده به ضرر شما تمام خواهد شد. در هر نوعی اختلافی که به وجود بیاید، 100% مسئولیت تایید کردن تراکنش به عهده ارسال کننده BLUR می‌باشد.\n\nاگر از این پیشنیازها سر در نمی‌آورید در Bisq معامله انجام ندهید. قبل از هر چیز در Blur Network Discord به آدرس (https://discord.gg/dMWaqVW) کمک بگیرید. -account.altcoin.popup.ccx.msg=اگر می‌خواهید CCX را در Bisq معامله کنید لطفا مطمئن شوید که پیشنیازهای زیر را متوجه شده‌اید:\n\nبرای ارسال CCX باید از یک کیف‌پول رسمی Conceal استفاده کنید که یا به صورت CLI است و یا به صورت GUI. بعد از انجام یک پرداخت، کیف‌پول‌ها کلید محرمانه تراکنش را نمایش می‌دهند.\nشما باید این کلید را به همراه هش تراکنش (شناسه) و آدرس عمومی گیرنده در جایی ذخیره کنید تا در صورت بروز اختلاف از آن استفاده کنید.\n در صورت بروز اختلاف باید هر سه این اطلاعات را به داور ارسال کنید\nتا او به کمک نمایشگر تراکنش Conceal به آدرس (https://explorer.conceal.network/txviewer) بتواند انتقال CCX را تایید کند.\nاز آنجایی که Conceal یک کوین است که سعی در حفظ حریم خصوصی دارد، کاوشگرهای بلاک قادر به تایید انتقال‌های آن نیستند.\n\nاگر نتوانید اطلاعات مورد نیاز را در اختیار داور بگذارید، در صورت بروز اختلاف، به ضرر شما تمام خواهد شد.\nاگر کلید محرمانه تراکنش را بلافاصله بعد از انتقال CCX در جایی ذخیره نکنید بعدا نمی‌توانید آن را بازیابی کنید.\nاگر از این پیشنیازها سر در نمی‌آورید از Conceal Discord به آدرس (http://discord.conceal.network) کمک بگیرید. +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=معامله Dragonglass در Bisq نیازمند این است که پیشنیازهای زیر را بفهمید و انجام دهید:\n\nبا توجه به ویژگی‌های حریم خصوصی که Dragonglass در اختیار می‌گذارد تراکنش‌ها قابل تایید بر روی بلاکچین عمومی نیستند. اگر نیازبه این کار داشتید می‌توانید پرداحت خود را از طریق کلید خصوصی TXN اثبات کنید.\nکلید خصوصی TXN کلیدی است که فقط یکبار به صورت خودکار برای هر تراکنش ساخته می‌شود و فقط می‌توانید از طریق کیف پول DRGL به آن دسترسی پیدا کنید.\nیا از طریق کیف پول DRGL GUI (داخل پنجره جزئیات تراکنش) و یا از طریق کیف‌پول Dragonglass CLI simplewallet (از طریق دستور "get_tx_key").\n\nنسخه 'Oathkeeper' مربوط به DRGL یا بالاتر در هر دوی کیف‌پول‌ها مورد نیاز است.\n\nدر صورت بروز اختلاف، شما باید اطلاعات زیر را به داور ارائه بدهید:\n- کلید خصوصی TXN\n- هش تراکنش \n- آدرس عمومی گیرنده\n\nتایید پرداخت با استفاده از اطلاعات بالا از طریق (http://drgl.info/#check_txn) امکان پذیر است.\n\nاگر نتوانید اطلاعات بالا را به داور ارائه بدهید و یا اینکه از یک کیف‌پول غیر سازگار استفاده کرده باشید، اگر اختلافی رخ بدهد به ضرر شما تمام خواهد شد. فرستنده Dragonglass مسئول این است که بتواند در صورت بروز اختلاف، انتقال را به داور اثبات کند. نیازی به استفاده از PaymentID نیست.\n\nاگر در مورد هر بخش از این فرآیند مطمئن نیستید از Dragonglass درDiscord به آدرس (http://discord.drgl.info) کمک بگیرید. -account.altcoin.popup.ZEC.msg=هنگام استفاده از {0} شما فقط می توانید آدرس های شفاف (که با t شروع می شوند) و نه z-addresses (خصوصی) را استفاده کنید، زیرا داور قادر به تأیید معامله با z-addresses نخواهد بود. -account.altcoin.popup.XZC.msg=هنگام استفاده از {0} شما فقط می توانید از آدرس های شفاف (قابل ردیابی) استفاده کنید نه آدرس های بی نشان، زیرا داور قادر نخواهد بود تا معامله را با آدرس های غیر قابل کشف در یک مرورگر مسدود تأیید کند. -account.altcoin.popup.bch=Bitcoin Cash و Bitcoin Clasics در معرض حملات بازپخش هستند. اگر از این کوین ها استفاده می کنید حتما اقدامات احتیاطی لازم را انجام دهید و همه پیامدها را متوجه شوید. شما می توانید از طریق ارسال یک کوین و ارسال ناخواسته کوین های مشابه در بلاک چین دیگر، متحمل ضرر شوید. از آنجایی که کوین های ایردراپ یا همان جایزه سابقه مشابهی با بلاک چین بیتکوین دارند، باز هم خطرات امنیتی و یک ریسک قابل ملاحظه برای از دست دادن حریم خصوصی وجود دارد.\n\nلطفا در مورد این موضوع در تالار گفتگو Bisq بیشتر بخوانید: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=از آنجایی که Bitcoin Gold سابقه مشابهی با بلاک چین بیتکوین دارند، باز هم خطرات امنیتی و یک ریسک قابل ملاحظه برای از دست دادن حریم خصوصی وجود دارد. اگر شما از Bitcoin Gold استفاده می کنید حتما اقدامات احتیاطی لازم را انجام دهید و همه پیامدها را متوجه شوید. \n\nلطفا در مورد این موضوع در تالار گفتگو Bisq بیشتر بخوانید: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.ZEC.msg=When using Zcash you can only use the transparent addresses (starting with t) not the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. +account.altcoin.popup.XZC.msg=When using Zcoin you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=حساب‌های ارزهای ملی شما @@ -961,8 +980,8 @@ account.seed.info=لطفا هم کلمات seed و هم تاریخ را یادد account.seed.warn.noPw.msg=شما یک رمز عبور کیف پول تنظیم نکرده اید که از نمایش کلمات رمز خصوصی محافظت کند.\n\nآیا می خواهید کلمات رمز خصوصی نشان داده شود؟ account.seed.warn.noPw.yes=بلی، و دوباره از من نپرس account.seed.enterPw=وارد کردن رمز عبور به منظور مشاهده ی کلمات رمز خصوصی -account.seed.restore.info=لطفا قبل از اجرای بازگردانی از طریق کلمات seed یک نسخه پشتیبان تهیه کنید. در نظر داشته باشید بازگردانی کیف‌پول فقط برای موارد اضطراری است و ممکن است باعث به وجود آمدن مشکلاتی در پایگاه داده داخلی شود.\nبا این روش یک پشتیبان را برنگردانید! لطفا از نسخه پشتیبان از پوشه داده‌های برنامه برای بازگردانی وضعیت قبلی برنامه استفاده کنید. -account.seed.restore.ok=خوب، من می فهمم و می خواهم بازگردانی کنم +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Ok, do the restore and shut down Bisq #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=قیمت پایین # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=کیف پول BSQ  dao.tab.proposals=حکمرانی dao.tab.bonding=ضمانت dao.tab.proofOfBurn=کارمزد ثبت دارایی/اثبات امحا +dao.tab.monitor=Network monitor +dao.tab.news=News dao.paidWithBsq=پرداخت شده با BSQ -dao.availableBsqBalance=در دسترس -dao.availableNonBsqBalance=موجودی غیر BSQ در دسترس (BTC) -dao.unverifiedBsqBalance=تایید نشده (منتظر تاییدیه بر اساس بلاک) +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Balance of all verified UTXOs +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) dao.lockedForVoteBalance=در حال استفاده برای رای دادن dao.lockedInBonds=قفل شده در ضمانت +dao.availableNonBsqBalance=موجودی غیر BSQ در دسترس (BTC) dao.totalBsqBalance=مجموع موجودی BSQ dao.tx.published.success=تراکنش شما به طور موفقیت آمیز منتشر شد. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=مرور دوره رای‌گیری dao.cycle.currentPhase=مرحله فعلی dao.cycle.currentBlockHeight=طول بلاک فعلی dao.cycle.proposal=مرحله طرح پیشنهادی +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=مرحله رای‌گیری ناشناس dao.cycle.voteReveal=مرحله آشکار کردن رای dao.cycle.voteResult=تنیجه را‌ی‌گیری dao.cycle.phaseDuration={0} بلاک (≈{1})؛ بلاک {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=دوره‌ها dao.results.cycles.table.header.cycle=دوره @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=تنیجه را‌ی‌گیری dao.results.proposals.voting.detail.header=نتایج رای‌گیری برای طرح پیشنهادی انتخاب شده +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=تعریف نشده @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=آدرس BTC گیرنده # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=کارمزد ثبت دارایی در روز # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=حداقل حجم معامله +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=مقدار فعلی: {0} dao.param.blocks={0} بلاک -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=زمان {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} بلاک -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(مقدار پیش‌فرض) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(در رای گیری تغییر کرده است) +dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the P2P network.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=تعریف نشده # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=داده تصادفی dao.bond.reputation.hash=تابع درهم ساز (هش) dao.bond.reputation.lockupButton=قفل کردن dao.bond.reputation.lockup.headline=تایید تراکنش قفل کردن وجه -dao.bond.reputation.lockup.details=مقدار وجه قفل شده: {0}\nمدت زمان قفل ماندن: ({1}) بلاک\n\nآیا از ادامه دادن مطمئنید؟ +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=تایید تراکنش رها کردن وجه -dao.bond.reputation.unlock.details=مقدار رها شدن وجه: {0}\nمدت زمان قفل شدن: ({1}) بلاک\n\nآیا از ادامه دادن مطمئنید؟ +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=همه ضمانت‌ها @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=قفل کردن dao.bond.table.button.unlock=باز کردن dao.bond.table.button.revoke=ابطال +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=تعریف نشده # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=هنوز ضمانت نشده # suppress inspection "UnusedProperty" @@ -1252,13 +1296,17 @@ dao.bond.bondState.UNLOCKED=وجه ضمانت شده رها شده # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=ضمانت مصادره شده +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=تعریف نشده # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=نقش ضمانت شده # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=اعتبار ضمانت شده # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=مدیر Github +dao.bond.bondedRoleType.UNDEFINED=تعریف نشده +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=مدیر تالار # suppress inspection "UnusedProperty" @@ -1266,24 +1314,30 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=مدیر توئیتر # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=مدیر Rocket chat # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=مدیر یوتیوب +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=نگهدارنده Bisq # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=گرداننده سایت # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=گرداننده تالار # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=گرداننده گره seed # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=گرداننده گره قیمت +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=گرداننده گره BTC +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=گرداننده بازارها # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=گرداننده کاوشگر BSQ # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=صاحب نام دامنه # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=مدیر DNS @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=مدیر DNS dao.bond.bondedRoleType.MEDIATOR=واسط # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=داور +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=کارمزد ثبت دارایی +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=کارمزد ثبت دارایی dao.burnBsq.menuItem.proofOfBurn=اثبات امحا dao.burnBsq.header=کارمزد ثبت دارایی @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=تاریخ dao.proofOfBurn.hash=هش dao.proofOfBurn.txs=تراکنش‌ها dao.proofOfBurn.pubKey=کلید عمومی -dao.proofOfBurn.signature.window.title=امضا کردن یک با کلید تراکنش اثبات امحا -dao.proofOfBurn.verify.window.title=تایید کردن یک با کلید تراکنش اثبات امحا +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction dao.proofOfBurn.copySig=کپی کردن امضا به حافظه موقت dao.proofOfBurn.sign=امضا کردن dao.proofOfBurn.message=پیام dao.proofOfBurn.sig=امضا dao.proofOfBurn.verify=تایید کردن -dao.proofOfBurn.verify.header=پیام را با کلید تراکنش اثبات امحا تایید کنید +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=تایید با موفقیت انجام شد dao.proofOfBurn.verificationResult.failed=تایید نا موفق بود @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=آشکارسازی رأی # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=تنیجه را‌ی‌گیری +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=تعریف نشده # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=درخواست خسارت # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=پیشنهاد عمومی # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=طرح پیشنهادی برای مصادره کردن یک ضمانت +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=تعریف نشده # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=درخواست خسارت # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=پیشنهاد کلی # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=مصادره کردن یک ضمانت - dao.proposal.details=جزئیات پیشنهاد dao.proposal.selectedProposal=پیشنهادهای انتخاب شده dao.proposal.active.header=طرح‌های پیشنهادی برای دوره فعلی dao.proposal.active.remove.confirm=آیا از حذف کردن آن طرح پیشنهادی مطمئنید؟\nکارمزد طرح پیشنهادی که قبلا پرداخت شده است از بین خواهد رفت. dao.proposal.active.remove.doRemove=بله، طرح پیشنهادی من را حذف کن dao.proposal.active.remove.failed=نمی توان پیشنهاد را حذف کرد. +dao.proposal.myVote.title=رأی گیری dao.proposal.myVote.accept=قبول پیشنهاد dao.proposal.myVote.reject=رد پیشنهاد dao.proposal.myVote.removeMyVote=نادیده گرفتن طرح پیشنهادی dao.proposal.myVote.merit=وزن رای به سبب مقدار BSQ بدست آورده dao.proposal.myVote.stake=وزن رای به سبب سهام -dao.proposal.myVote.blindVoteTxId=شناسه تراکنش رای ناشناس dao.proposal.myVote.revealTxId=شناسه تراکنش آشکاری سازی رای -dao.proposal.myVote.stake.prompt=حداکثر موجودی در دسترس برای رای دادن: {0} -dao.proposal.votes.header=رأی به تمام طرح‌های پیشنهادی -dao.proposal.votes.header.voted=رای من -dao.proposal.myVote.button=رأی به تمام پیشنهادها +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=انتخاب نوع پیشنهاد +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=نوع پیشنهاد -dao.proposal.create.createNew=ارائه ی پیشنهاد جدید -dao.proposal.create.create.button=ارائه ی پیشنهاد +dao.proposal.create.new=ارائه ی پیشنهاد جدید +dao.proposal.create.button=ارائه ی پیشنهاد +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=طرح پیشنهادی dao.proposal.display.type=نوع طرح پیشنهادی dao.proposal.display.name=نام/نام مستعار dao.proposal.display.link=پیوند به اطلاعات جزئی -dao.proposal.display.link.prompt=پیوند به Github issue +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=مبلغ درخواستی به BSQ dao.proposal.display.bsqAddress=آدرس BSQ dao.proposal.display.txId=شناسه تراکنش طرح پیشنهادی @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=قبول شده dao.proposal.display.myVote.rejected=رد شده dao.proposal.display.myVote.ignored=نادیده گرفته شده dao.proposal.myVote.summary=رای: {0}؛ وزن رای: {1} (بدست آمده: {2} + سهام: {3})؛ +dao.proposal.myVote.invalid=Vote was invalid dao.proposal.voteResult.success=قبول شده dao.proposal.voteResult.failed=رد شده @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=دارایی مورد حذف dao.blindVote=رای ناشناس dao.blindVote.startPublishing=در حال انتشار تراکنش رای ناشناس... -dao.blindVote.success=رای ناشناس شما با موفقیت منتشر شد. +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=ارسال dao.wallet.menuItem.receive=دریافت dao.wallet.menuItem.transactions=تراکنش ها dao.wallet.dashboard.myBalance=موجودی کیف‌پول من -dao.wallet.dashboard.distribution=توزیع تمام BSQ -dao.wallet.dashboard.locked=وضعیت جهانی BSQ های قفل شده -dao.wallet.dashboard.market=داده‌های بازار -dao.wallet.dashboard.genesis=تراکنش پیدایش -dao.wallet.dashboard.txDetails=آمار تراکنش‌های BSQ -dao.wallet.dashboard.genesisBlockHeight=طول بلاک پیدایش -dao.wallet.dashboard.genesisTxId=شناسه تراکنش پیدایش -dao.wallet.dashboard.genesisIssueAmount=BSQ صادر شده در تراکنش پیدایش -dao.wallet.dashboard.compRequestIssueAmount=BSQ صادر شده برای درخواست‌های مصادره -dao.wallet.dashboard.reimbursementAmount=BSQ صادر شده برای درخواست‌های بازپرداخت -dao.wallet.dashboard.availableAmount=مجموع BSQ در دسترس -dao.wallet.dashboard.burntAmount=BSQ امحا شده (کارمزدها) -dao.wallet.dashboard.totalLockedUpAmount=قفل شده در ضمانت‌ها -dao.wallet.dashboard.totalUnlockingAmount=رها کردن BSQ از ضمانت‌ها -dao.wallet.dashboard.totalUnlockedAmount=BSQ رها شده از ضمانت‌ها -dao.wallet.dashboard.totalConfiscatedAmount=BSQ مصادره شده از ضمانت‌ها -dao.wallet.dashboard.allTx=تعداد تمام تراکنش‌های BSQ -dao.wallet.dashboard.utxo=تعداد تمام خروجی تراکنش‌های خرج نشده -dao.wallet.dashboard.compensationIssuanceTx=تعداد تمام تراکنش‌های صدور درخواست مصادره -dao.wallet.dashboard.reimbursementIssuanceTx=تعداد تمام تراکنش‌های صدور درخواست بازپرداخت -dao.wallet.dashboard.burntTx=تعداد تمام تراکنش‌های کارمزد پرداخت -dao.wallet.dashboard.price=آخرین قیمت معاملاتی BSQ/BTC (به Bisq) -dao.wallet.dashboard.marketCap=ارزش بازار (بر مبنای قیمت معاملاتی) - -dao.wallet.receive.fundYourWallet=تأمین مالی کیف پول BSQ خودتان -dao.wallet.receive.bsqAddress=آدرس کیف‌پول BSQ + +dao.wallet.receive.fundYourWallet=Your BSQ receive address +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=The Bisq DAO +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=ارسال وجوه dao.wallet.send.sendBtcFunds=ارسال وجوه غیر BSQ (به BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=آخرین بلاک تایید شده: {0} dao.wallet.chainHeightSyncing=منتظر بلاک‌ها... {0} تا از {1} بلاک تایید شده است dao.wallet.tx.type=نوع +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=تعریف نشده # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=شناسایی نشده # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=درخواست/صدور خسارت dao.tx.issuanceFromCompReq.tooltip=درخواست خسارت که منجر به صدور BSQ جدید می‌شود.\nتاریخ صدور: {0} dao.tx.issuanceFromReimbursement=درخواست/صدور بازپرداخت dao.tx.issuanceFromReimbursement.tooltip=درخواست بازپرداختی که منجر به صدور BSQ جدید می‌شود.\nتاریخ صدور: {0} -dao.proposal.create.missingBsqFunds=شما وجوه کافی برای ایجاد پیشنهاد را ندارید.\nمقدار مورد نیاز: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=تایید {0} تراکنش dao.feeTx.confirm.details=کارمزد {0}: {1}\nکارمزد استخراج: {2} ({3} ساتوشی بر بایت)\nاندازه تراکنش: {4} Kb\n\nآیا از انتشار تراکنش {5} اطمینان دارید؟ +dao.news.bisqDAO.title=THE BISQ DAO +dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. +dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO + +dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ +dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.news.pastContribution.yourAddress=Your BSQ Wallet Address +dao.news.pastContribution.requestNow=Request now + +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. +dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ +dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. +dao.news.DAOOnTestnet.readMoreLink=Read the full documentation + +dao.monitor.daoState=DAO state +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Conflicts +dao.monitor.state=وضعیت +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=DAO state +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Block height +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Previous hash +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Previous hash +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Previous hash +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=داده‌های بازار +dao.factsAndFigures.dashboard.price=آخرین قیمت معاملاتی BSQ/BTC (به Bisq) +dao.factsAndFigures.dashboard.marketCap=ارزش بازار (بر مبنای قیمت معاملاتی) +dao.factsAndFigures.dashboard.availableAmount=مجموع BSQ در دسترس + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ صادر شده در تراکنش پیدایش +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ صادر شده برای درخواست‌های مصادره +dao.factsAndFigures.supply.reimbursementAmount=BSQ صادر شده برای درخواست‌های بازپرداخت + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=وضعیت جهانی BSQ های قفل شده +dao.factsAndFigures.supply.totalLockedUpAmount=قفل شده در ضمانت‌ها +dao.factsAndFigures.supply.totalUnlockingAmount=رها کردن BSQ از ضمانت‌ها +dao.factsAndFigures.supply.totalUnlockedAmount=BSQ رها شده از ضمانت‌ها +dao.factsAndFigures.supply.totalConfiscatedAmount=BSQ مصادره شده از ضمانت‌ها +dao.factsAndFigures.supply.burntAmount=BSQ امحا شده (کارمزدها) + +dao.factsAndFigures.transactions.genesis=تراکنش پیدایش +dao.factsAndFigures.transactions.genesisBlockHeight=طول بلاک پیدایش +dao.factsAndFigures.transactions.genesisTxId=شناسه تراکنش پیدایش +dao.factsAndFigures.transactions.txDetails=آمار تراکنش‌های BSQ +dao.factsAndFigures.transactions.allTx=تعداد تمام تراکنش‌های BSQ +dao.factsAndFigures.transactions.utxo=تعداد تمام خروجی تراکنش‌های خرج نشده +dao.factsAndFigures.transactions.compensationIssuanceTx=تعداد تمام تراکنش‌های صدور درخواست مصادره +dao.factsAndFigures.transactions.reimbursementIssuanceTx=تعداد تمام تراکنش‌های صدور درخواست بازپرداخت +dao.factsAndFigures.transactions.burntTx=تعداد تمام تراکنش‌های کارمزد پرداخت #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=گره های seed فیلتر شده (آدرس های Onio filterWindow.priceRelayNode=گره های رله قیمت فیلترشده (آدرس های Onion جدا شده با ویرگول) filterWindow.btcNode=گره‌های بیت‌کوین فیلترشده (آدرس + پورت جدا شده با ویرگول) filterWindow.preventPublicBtcNetwork=جلوگیری از استفاده ازشبکه عمومی بیت‌کوین +filterWindow.disableDao=Disable DAO filterWindow.add=افزودن فیلتر filterWindow.remove=حذف فیلتر @@ -1955,6 +2103,10 @@ BTC_MAINNET=Bitcoin Mainnet BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=سال time.month=ماه @@ -2025,6 +2177,8 @@ payment.country=کشور payment.extras=الزامات اضافی payment.email.mobile=ایمیل یا شماره موبایل payment.altcoin.address=آدرس آلت‌کوین +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=آلت‌کوین payment.select.altcoin=انتخاب یا جستجوی آلت کوین payment.secret=سوال محرمانه @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=ایمیل یا شماره تلفن payment.venmo.venmoUserName=نام کاربری Venmo payment.popmoney.accountId=ایمیل یا شماره تلفن -payment.revolut.email=ایمیل یا شماره تلفن +payment.revolut.email=ایمیل +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=شناسه شهروندی/شناسه مالیاتی یا شماره تلفن payment.supportedCurrencies=ارزهای مورد حمایت payment.limitations=محدودیت‌ها @@ -2082,6 +2237,10 @@ payment.limits.info=لطفا توجه داشته باشید که تمام انت payment.cashDeposit.info=لطفا مطمئن شوید که بانک شما اجازه پرداخت سپرده نفد به حساب دیگر افراد را می‌دهد. برای مثال، Bank of America و Wells Fargo دیگر اجازه چنین پرداخت‌هایی را نمی‌دهند. +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=اطلاعات تماس payment.f2f.contact.prompt=از چه طریقی می‌خواهید توسط همتای معاملاتی با شما تماس حاصل شود؟ (آدرس ایمیل، شماره تلفن، ...) payment.f2f.city=شهر جهت ملاقات 'رو در رو' @@ -2128,16 +2287,10 @@ F2F_SHORT=رو در رو # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=درخواست نقدی -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=آلت کوین ها PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=درخواست نقدی # suppress inspection "UnusedProperty" -CASH_APP_SHORT=درخواست نقدی +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo\n +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney\n # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=آلت کوین ها PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=درخواست نقدی +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} باید شامل {1} عدد باشد. validation.invalidInput=ورودی نامعتبر: {0} validation.accountNrFormat=شماره حساب باید از فرمت {0} باشد validation.altcoin.wrongStructure=تأیید آدرس ناموفق بود زیرا آن با ساختار یک آدرس {0} مطابقت ندارد. +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=آدرس ZEC باید با t شروع شود. آدرس هایی که با z می شوند، پشتیبانی نمی شوند. validation.altcoin.invalidAddress=آدرس یک آدرس {0} معتبر نیست! {1} validation.bic.invalidLength=طول ورودی نه 8 و نه 11 است @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=باید فقط شامل حروف، validation.interacETransfer.invalidAnswer=باید یک کلمه باشد و فقط شامل حروف، اعداد و یا نماد - باشد validation.inputTooLarge=ورودی نباید بزرگتر از {0} باشد validation.inputTooSmall=ورودی باید بزرگتر از {0} باشد +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=مقادیر کمتر از {0} مجاز نیستند. validation.length=طول باید بین {0} و {1} باشد validation.pattern=ورودی باید در این قالب باشد: {0} validation.noHexString=ورودی در قالب HEX نیست validation.advancedCash.invalidFormat=باید یک ایمیل درست باشد و یا یک شناسه کیف‌پول در قالب: X000000000000 +validation.invalidUrl=This is not a valid URL +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index bf9851fc54a..36c7963f2a1 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -104,7 +104,7 @@ shared.belowInPercent=% sous le prix du marché shared.aboveInPercent=% au-dessus du prix du marché shared.enterPercentageValue=Entrez la valeur du % shared.OR=OU -shared.notEnoughFunds=Pas assez de fonds dans votre portefeuille Bisq.\nRequis : {0} mais vous possédez seulement : {1} dans votre portefeuille.\n\nVeuillez svp payer cette transaction à partir d'un portefeuille Bitcoin externe, ou ajouter des fonds à votre portefeuille Bisq via l'onglet \"Fonds/Recevoir Fonds\". +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.waitingForFunds=En attente des fonds... shared.depositTransactionId=ID du dépôt de transaction shared.TheBTCBuyer=L'acheteur de BTC @@ -113,12 +113,13 @@ shared.reasonForPayment=Motif du paiement shared.sendingConfirmation=Envoi de la confirmation... shared.sendingConfirmationAgain=SVP envoyez la confirmation à nouveau shared.exportCSV=Exporter au format csv +shared.exportJSON=Export to JSON shared.noDateAvailable=Pas de date disponible shared.arbitratorsFee=Frais d'arbitrage shared.noDetailsAvailable=Pas de détails disponibles shared.notUsedYet=Pas encore utilisé shared.date=Date -shared.sendFundsDetailsWithFee=Envoi: {0}\nDepuis l'adresse: {1}\nÀ l'adresse de réception: {2}\nFrais de transaction requis: {3} ({4} satoshis/byte)\nVolume de transaction: {5} Kb\n\nLe destinataire recevra: {6}\n\nÊtes-vous sûr de vouloir retirer ce montant? +shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired transaction 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.copyToClipboard=Copier dans le presse-papiers shared.language=Langue shared.country=Pays @@ -196,6 +197,8 @@ shared.interval=Intervalle shared.actions=Actions shared.buyerUpperCase=Acheteur shared.sellerUpperCase=Vendeur +shared.new=NEW +shared.blindVoteTxId=Blind vote transaction ID #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=Vérouillé mainView.footer.usingTor=(utilisant Tor) mainView.footer.localhostBitcoinNode=Ordinateur local mainView.footer.btcInfo=Pairs du réseau Bitcoin: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Démarrage -mainView.footer.btcInfo.synchronizedWith=synchronisé avec -mainView.footer.btcInfo.connectingTo=connecté à +mainView.footer.btcInfo.initializing=Connecting to Bitcoin network +mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO +mainView.footer.btcInfo.synchronizedWith=Synchronized with +mainView.footer.btcInfo.connectingTo=Connecting to mainView.footer.btcInfo.connectionFailed=échec de la connexion mainView.footer.p2pInfo=Pairs du réseau P2P: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=Nombre de bitcoin à {0} createOffer.amountPriceBox.buy.volumeDescription=Montant en {0} à envoyer createOffer.amountPriceBox.sell.volumeDescription=Montant en {0} à recevoir createOffer.amountPriceBox.minAmountDescription=Montant minimum de BTC -createOffer.securityDeposit.prompt=Dépôt de garantie en BTC +createOffer.securityDeposit.prompt=Dépôt de garantie createOffer.fundsBox.title=Ajouter des fonds à votre offre createOffer.fundsBox.offerFee=Frais de transaction createOffer.fundsBox.networkFee=Frais de minage createOffer.fundsBox.placeOfferSpinnerInfo=Publication de l'offre en cours... createOffer.fundsBox.paymentLabel=Transaction Bisq avec l'ID {0} createOffer.fundsBox.fundsStructure=({0} dépôt de garantie, {1} frais de transaction, {2} frais de minage) +createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=Votre offre a été publiée createOffer.success.info=Vous pouvez gérer vos offres ouvertes dans \"Portfolio/Mes offres\". createOffer.info.sellAtMarketPrice=Vous vendrez toujours au prix du marché alors que le prix de votre offre sera continuellement mis à jour. @@ -387,6 +392,7 @@ createOffer.alreadyFunded=Vous aviez déjà payer cette offre.\nVos fonds ont é createOffer.createOfferFundWalletInfo.headline=Payer votre offre # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=Montant de l'échange: {0}\n\n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} and {1} createOffer.createOfferFundWalletInfo.msg=Un dépôt de {0} vous est nécessaire pour cette offre.\n\nCes fonds sont mis en réserve dans votre portefeuille local et seront vérouillés dans l'adresse de dépôt multisig une fois que quelqu'un prendra votre offre.\n\nCe montant est la somme de:\n{1}- Votre dépôt de garantie: {2}\n- Frais de transaction: {3}\n- Frais de minage: {4}\n\nVous pouvez choisir parmi deux options pour financer votre transaction:\n- Utiliser votre portefeuille Bisq (pratique, mais les transactions peuvent être retrouvables) OU\n- Faire un transfert depuis un portefeuille externe (potentiellement plus privé)\n\nVous pourrez voir à nouveau toutes les options de financement et leurs détails après avoir fermé ce popup. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=Le prix que vous avez entré est en dehors d createOffer.changePrice=Modifiez le prix createOffer.tac=En plaçant cette offre vous acceptez de faire des transactions avec n'importe quel trader remplissant les conditions ci-dessus. createOffer.currencyForFee=Frais de transaction -createOffer.setDeposit=Etablir le dépôt de garantie de l'acheteur +createOffer.setDeposit=Set buyer's security deposit (%) +createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) +createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} #################################################################### @@ -539,7 +548,7 @@ portfolio.pending.step2_seller.waitPayment.headline=En attente du paiement portfolio.pending.step2_seller.f2fInfo.headline=Fiche de contact acheteur portfolio.pending.step2_seller.waitPayment.msg=La transaction de dépôt a au moins une confirmation de la blockchain.\nVous devez attendre que l'acheteur de BTC débute le {0} payment. portfolio.pending.step2_seller.warn=L'acheteur de BTC n'a toujours pas effectué le paiement {0}.\nVeuillez attendre qu'il effectue celui-ci.\nSi la transaction n'est pas effectuée le {1}, un arbitre enquêtera. -portfolio.pending.step2_seller.openForDispute=L'acheteur de BTC n'a pas démarré son paiement!\nLe temps max. alloué pour la transaction est écoulé.\nVous pouvez attendre encore pour laisser plus de temps à l'autre participant, ou contacter l'arbitre pour ouvrir une dispute. +portfolio.pending.step2_seller.openForDispute=L'acheteur de BTC n'a pas démarré son paiement!\nLe temps max. alloué pour la transaction est écoulé.\nVous pouvez attendre encore pour laisser plus de temps à l'autre participant, ou contacter l'arbitre pour ouvrir une dispute. # suppress inspection "UnusedProperty" message.state.UNDEFINED=Indéfini @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=Nous aimerions votre retour à propos de votre exp tradeFeedbackWindow.msg.part2=Si vous avez la moindre question, ou rencontrez le moindre problème, veuillez s'il vous plaît vous mettre en relation avec les autres utilisateurs et contributeurs via le Bisq forum à: tradeFeedbackWindow.msg.part3=Merci d'utiliser Bisq! + +daoTestingFeedbackWindow.title=Thank you for testing the Bisq DAO +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Visit forum +daoTestingFeedbackWindow.msg.part3=Merci d'utiliser Bisq! + portfolio.pending.role=Mon rôle portfolio.pending.tradeInformation=Information de l'échange portfolio.pending.remainingTime=Temps restant @@ -769,11 +786,11 @@ support.buyerTaker=Acheteur BTC/Taker support.sellerTaker=Vendeur BTC/Taker support.backgroundInfo=Bisq is not a company, so it handles disputes differently.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display an \"Open dispute\" button after the trade period is over for contacting the arbitrator.\n\nIf there is an issue with the application, the software will try to detect it and, if possible, display an \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIf you are having an issue and did not see the \"Open support ticket\" button, you can open a support ticket manually by selecting the trade causing issues under \"Portfolio/Open trades\" and hitting \"alt + o\" or \"option + o\". Please use this method only if you are sure that the software is not working as expected. If you have problems or questions, please review the FAQ on the bisq.network web page or post in the Bisq forum in the Support section. -support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrator's requests within 2 days.\n2. The maximum period for a dispute is 14 days.\n3. You need to cooperate with the arbitrator and provide the information they request to make your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=Message du système: {0} -support.youOpenedTicket=Vous avez ouvert une demande d'assistance. -support.youOpenedDispute=Vous avez ouvert une dispute.\n\n{0} -support.peerOpenedTicket=Your trading peer has requested support due technical problems. Please wait for further instructions. +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=L'autre trader a ouvert une dispute\n{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=Vous devez sélectionner un arbitre. account.altcoin.yourAltcoinAccounts=Your altcoin accounts account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe arbitrator is not a {2} specialist and cannot help in such cases. account.altcoin.popup.wallet.confirm=Je comprends et confirme que je sais quel portefeuille je dois utiliser. +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 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 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\nFor sending XMR, you need to use either the official Monero GUI wallet or Monero 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.\nmonero-wallet-cli (use the command get_tx_key)\nmonero-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nIn addition to XMR checktx tool (https://xmr.llcoins.net/checktx.html) verification can also be accomplished in-wallet.\nmonero-wallet-cli : using command (check_tx_key).\nmonero-wallet-gui : on the Advanced > Prove/Check page.\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 XMR sender is responsible for providing verification of the XMR 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 (https://www.getmonero.org/resources/user-guides/prove-payment.html) or the Monero forum (https://forum.getmonero.org) to find more information. 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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=When using {0} you can only use the transparent addresses (starting with t) not the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. -account.altcoin.popup.XZC.msg=When using {0} you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. -account.altcoin.popup.bch=Bitcoin Cash and Bitcoin Clashic suffer from replay protection. If you use those coins be sure you take sufficient precautions and understand all implications.You can suffer losses by sending one coin and unintentionally send the same coins on the other block chain.Because those "airdrop coins" share the same history with the Bitcoin blockchain there are also security risks and a considerable risk for losing privacy.\n\nPlease read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Because Bitcoin Gold shares the same history as the Bitcoin blockchain it comes with certain security risks and a considerable risk for losing privacy.If you use Bitcoin Gold be sure you take sufficient precautions and understand all implications.\n\nPlease read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.ZEC.msg=When using Zcash you can only use the transparent addresses (starting with t) not the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. +account.altcoin.popup.XZC.msg=When using Zcoin you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=Your national currency accounts @@ -961,8 +980,8 @@ account.seed.info=Please write down both wallet seed words and the date! You can account.seed.warn.noPw.msg=Vous n'avez pas défini un mot de passe portefeuille qui protègerais l'affichage des graines.\n\nVoulez vous afficher les graines? account.seed.warn.noPw.yes=Oui, et ne plus me demander à l'avenir account.seed.enterPw=Entrer le mot de passe pour voir les graines -account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state. -account.seed.restore.ok=Ok, j'ai compris et je veux restaurer +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Ok, do the restore and shut down Bisq #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=The lower price must # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=Portefeuille BSQ dao.tab.proposals=Governance dao.tab.bonding=Bonding dao.tab.proofOfBurn=Asset listing fee/Proof of burn +dao.tab.monitor=Network monitor +dao.tab.news=News dao.paidWithBsq=payé en BSQ -dao.availableBsqBalance=Available -dao.availableNonBsqBalance=Available non-BSQ balance (BTC) -dao.unverifiedBsqBalance=Unverified (awaiting block confirmation) +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Balance of all verified UTXOs +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) dao.lockedForVoteBalance=Used for voting dao.lockedInBonds=Locked in bonds +dao.availableNonBsqBalance=Available non-BSQ balance (BTC) dao.totalBsqBalance=Total balance BSQ dao.tx.published.success=Votre transaction a été publiée avec succès. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Voting cycle overview dao.cycle.currentPhase=Current phase dao.cycle.currentBlockHeight=Current block height dao.cycle.proposal=Proposal phase +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=Blind vote phase dao.cycle.voteReveal=Vote reveal phase dao.cycle.voteResult=Vote result dao.cycle.phaseDuration={0} blocks (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=Cycles dao.results.cycles.table.header.cycle=Cycle @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Vote result dao.results.proposals.voting.detail.header=Vote results for selected proposal +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Indéfini @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=Recipient BTC address # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=Asset listing fee per day # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. trade volume +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Current value: {0} dao.param.blocks={0} blocks -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Duration of {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} block(s) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(default value) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(has been changed in voting) +dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the P2P network.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Indéfini # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Lockup dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Lockup dao.bond.table.button.unlock=Déverrouiller dao.bond.table.button.revoke=Revoke +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Indéfini # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Not bonded yet # suppress inspection "UnusedProperty" @@ -1252,13 +1296,17 @@ dao.bond.bondState.UNLOCKED=Bond unlocked # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Bond confiscated +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Indéfini # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Bonded reputation # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github admin +dao.bond.bondedRoleType.UNDEFINED=Indéfini +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin # suppress inspection "UnusedProperty" @@ -1266,24 +1314,30 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Rocket chat admin # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Youtube admin +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq maintainer # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Website operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Forum operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Price node operator +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Btc node operator +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ explorer operator # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domain name holder # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS admin @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=DNS admin dao.bond.bondedRoleType.MEDIATOR=Mediator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Arbitrator +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=Asset listing fee +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=Asset listing fee dao.burnBsq.menuItem.proofOfBurn=Proof of burn dao.burnBsq.header=Fee for asset listing @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=Date dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=Transactions dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Sign a message with key from proof or burn transaction -dao.proofOfBurn.verify.window.title=Verify a message with key from proof or burn transaction +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction dao.proofOfBurn.copySig=Copy signature to clipboard dao.proofOfBurn.sign=Sign dao.proofOfBurn.message=Message dao.proofOfBurn.sig=Signature dao.proofOfBurn.verify=Verify -dao.proofOfBurn.verify.header=Verify message with key from proof or burn transaction +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=Verification succeeded dao.proofOfBurn.verificationResult.failed=Verification failed @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Vote reveal # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Vote result +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Indéfini # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Requête de compensation # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=Generic proposal # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Proposal for confiscating a bond +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Indéfini # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Requête de compensation # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=Generic proposal # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Confiscating a bond - dao.proposal.details=Proposal details dao.proposal.selectedProposal=Selected proposal dao.proposal.active.header=Proposals of current cycle dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal?\nThe already paid proposal fee will be lost. dao.proposal.active.remove.doRemove=Yes, remove my proposal dao.proposal.active.remove.failed=Could not remove proposal. +dao.proposal.myVote.title=Vote dao.proposal.myVote.accept=Accept proposal dao.proposal.myVote.reject=Reject proposal dao.proposal.myVote.removeMyVote=Ignore proposal dao.proposal.myVote.merit=Vote weight from earned BSQ dao.proposal.myVote.stake=Vote weight from stake -dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID dao.proposal.myVote.revealTxId=Vote reveal transaction ID -dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0} -dao.proposal.votes.header=Vote on all proposals -dao.proposal.votes.header.voted=My vote -dao.proposal.myVote.button=Vote on all proposals +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Select proposal type +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=Proposal type -dao.proposal.create.createNew=Make new proposal -dao.proposal.create.create.button=Make proposal +dao.proposal.create.new=Make new proposal +dao.proposal.create.button=Make proposal +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=proposal dao.proposal.display.type=Proposal type dao.proposal.display.name=Name/nickname dao.proposal.display.link=Link to detail info -dao.proposal.display.link.prompt=Link to Github issue +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=Requested amount in BSQ dao.proposal.display.bsqAddress=BSQ address dao.proposal.display.txId=Proposal transaction ID @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=Accepted dao.proposal.display.myVote.rejected=Rejected dao.proposal.display.myVote.ignored=Ignored dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}); +dao.proposal.myVote.invalid=Vote was invalid dao.proposal.voteResult.success=Accepted dao.proposal.voteResult.failed=Rejected @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=Asset to remove dao.blindVote=blind vote dao.blindVote.startPublishing=Publishing blind vote transaction... -dao.blindVote.success=Your blind vote has been successfully published. +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=Envoyer dao.wallet.menuItem.receive=Recevoir dao.wallet.menuItem.transactions=Transactions dao.wallet.dashboard.myBalance=My wallet balance -dao.wallet.dashboard.distribution=Distribution of all BSQ -dao.wallet.dashboard.locked=Global state of locked BSQ -dao.wallet.dashboard.market=Market data -dao.wallet.dashboard.genesis=Genesis transaction -dao.wallet.dashboard.txDetails=BSQ transactions statistics -dao.wallet.dashboard.genesisBlockHeight=Genesis block height -dao.wallet.dashboard.genesisTxId=Genesis transaction ID -dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction -dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests -dao.wallet.dashboard.reimbursementAmount=BSQ issued for reimbursement requests -dao.wallet.dashboard.availableAmount=Total available BSQ -dao.wallet.dashboard.burntAmount=Burned BSQ (fees) -dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds -dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds -dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds -dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds -dao.wallet.dashboard.allTx=No. of all BSQ transactions -dao.wallet.dashboard.utxo=No. of all unspent transaction outputs -dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions -dao.wallet.dashboard.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions -dao.wallet.dashboard.burntTx=No. of all fee payments transactions -dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq) -dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price) - -dao.wallet.receive.fundYourWallet=Financer votre portefeuille BSQ -dao.wallet.receive.bsqAddress=BSQ wallet address + +dao.wallet.receive.fundYourWallet=Your BSQ receive address +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=The Bisq DAO +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=Envoyer des fonds dao.wallet.send.sendBtcFunds=Send non-BSQ funds (BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=Latest verified block: {0} dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1} dao.wallet.tx.type=Type +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Indéfini # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Not recognized # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=Compensation request/issuance dao.tx.issuanceFromCompReq.tooltip=Compensation request which led to an issuance of new BSQ.\nIssuance date: {0} dao.tx.issuanceFromReimbursement=Reimbursement request/issuance dao.tx.issuanceFromReimbursement.tooltip=Reimbursement request which led to an issuance of new BSQ.\nIssuance date: {0} -dao.proposal.create.missingBsqFunds=You don''t have sufficient funds for creating the proposal.\nMissing: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=Confirm {0} transaction dao.feeTx.confirm.details={0} fee: {1}\nMining fee: {2} ({3} Satoshis/byte)\nTransaction size: {4} Kb\n\nAre you sure you want to publish the {5} transaction? +dao.news.bisqDAO.title=THE BISQ DAO +dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. +dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO + +dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ +dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.news.pastContribution.yourAddress=Your BSQ Wallet Address +dao.news.pastContribution.requestNow=Request now + +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. +dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ +dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. +dao.news.DAOOnTestnet.readMoreLink=Read the full documentation + +dao.monitor.daoState=DAO state +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Conflicts +dao.monitor.state=Statut +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=DAO state +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Block height +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Previous hash +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Previous hash +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Previous hash +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Market data +dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq) +dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price) +dao.factsAndFigures.dashboard.availableAmount=Total available BSQ + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests +dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Global state of locked BSQ +dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds +dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds +dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds +dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds +dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees) + +dao.factsAndFigures.transactions.genesis=Genesis transaction +dao.factsAndFigures.transactions.genesisBlockHeight=Genesis block height +dao.factsAndFigures.transactions.genesisTxId=Genesis transaction ID +dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics +dao.factsAndFigures.transactions.allTx=No. of all BSQ transactions +dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs +dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions +dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions +dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Filtered seed nodes (comma sep. onion addresses) filterWindow.priceRelayNode=Filtered price relay nodes (comma sep. onion addresses) filterWindow.btcNode=Filtered Bitcoin nodes (comma sep. addresses + port) filterWindow.preventPublicBtcNetwork=Prevent usage of public Bitcoin network +filterWindow.disableDao=Disable DAO filterWindow.add=Ajouter un filtre filterWindow.remove=Retirer un filtre @@ -1955,6 +2103,10 @@ BTC_MAINNET=Bitcoin Mainnet BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=Année time.month=Mois @@ -2025,6 +2177,8 @@ payment.country=Pays payment.extras=Extra requirements payment.email.mobile=Email or mobile no. payment.altcoin.address=Altcoin address +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=Altcoin payment.select.altcoin=Sélectionner ou chercher un altcoin payment.secret=Secret question @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=Email or phone no. payment.venmo.venmoUserName=Venmo username payment.popmoney.accountId=Email or phone no. -payment.revolut.email=Email or phone no. +payment.revolut.email=Email +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=Citizen ID/Tax ID or phone no. payment.supportedCurrencies=Supported currencies payment.limitations=Limitations @@ -2082,6 +2237,10 @@ payment.limits.info=Please be aware that all bank transfers carry a certain amou payment.cashDeposit.info=Please confirm your bank allows you to send cash deposits into other peoples' accounts. For example, Bank of America and Wells Fargo no longer allow such deposits. +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=Contact info payment.f2f.contact.prompt=How you want to get contacted by the trading peer? (email address, phone number,...) payment.f2f.city=City for 'Face to face' meeting @@ -2128,16 +2287,10 @@ F2F_SHORT=F2F # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altcoins PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Altcoins PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} doit se composer de {1} nombres. validation.invalidInput=Invalid input: {0} validation.accountNrFormat=Account number must be of format: {0} validation.altcoin.wrongStructure=Address validation failed because it does not match the structure of a {0} address. +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=L'adresse ZEC doit commencer par un t. Les adresses commençant par un z ne sont pas gérées. validation.altcoin.invalidAddress=L'adresse n'est pas un adresse valide {0}! {1} validation.bic.invalidLength=Input length is neither 8 nor 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Must contain only letters, numbers, validation.interacETransfer.invalidAnswer=Must be one word and contain only letters, numbers, and/or the symbol - validation.inputTooLarge=Input must not be larger than {0} validation.inputTooSmall=Input has to be larger than {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=The amount below the dust limit of {0} is not allowed. validation.length=Length must be between {0} and {1} validation.pattern=Input must be of format: {0} validation.noHexString=The input is not in HEX format. validation.advancedCash.invalidFormat=Must be a valid email or wallet id of format: X000000000000 +validation.invalidUrl=This is not a valid URL +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_hu.properties b/core/src/main/resources/i18n/displayStrings_hu.properties index a129f833aac..f81188c94e9 100644 --- a/core/src/main/resources/i18n/displayStrings_hu.properties +++ b/core/src/main/resources/i18n/displayStrings_hu.properties @@ -104,7 +104,7 @@ shared.belowInPercent=Below % from market price shared.aboveInPercent=Above % from market price shared.enterPercentageValue=Írja be % értékét shared.OR=VAGY -shared.notEnoughFunds=Nincs elég pénzed a Bisq pénztárcádban.\nSzükséged van {0}, de csak {1} van a Bisq pénztárcádban.\n\nKérjük finanszírozzd ezt a tranzakciót egy külső Bitcoin tárcából, vagy finanszírozd a Bisq tárcádat a \"Pénzeszközök/Pénzeszközök részesedése\"-nél. +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.waitingForFunds=Várakozás finanszírozásra... shared.depositTransactionId=Befizetési tranzakció azonosító shared.TheBTCBuyer=A BTC vásárló @@ -113,12 +113,13 @@ shared.reasonForPayment=Fizetés indoka shared.sendingConfirmation=Visszaigazolás küldése... shared.sendingConfirmationAgain=Kérjük, küldd újra a jóváhagyást shared.exportCSV=Exportálás CSV-be +shared.exportJSON=Export to JSON shared.noDateAvailable=Nincs elérhető dátum shared.arbitratorsFee=Bíró díja shared.noDetailsAvailable=Nincs adat shared.notUsedYet=Még nincs használatban shared.date=Dátum -shared.sendFundsDetailsWithFee=Küldött: {0}\nCímről: {1}\nFogadó címe: {2}.\nSzükséges tranzakciós díj: {3} ({4} Satoshi/bájt)\nTranzakció mérete: {5} Kb\n\nCímzett kapni fog: {6}\n\nBiztosan szeretné visszavonni az összeget? +shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired transaction 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.copyToClipboard=Másolja a vágólapra shared.language=Nyelv shared.country=Ország @@ -196,6 +197,8 @@ shared.interval=Interval shared.actions=Actions shared.buyerUpperCase=Buyer shared.sellerUpperCase=Seller +shared.new=NEW +shared.blindVoteTxId=Blind vote transaction ID #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=Locked mainView.footer.usingTor=(Tor használatával) mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo=Bitcoin hálózat társak: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Inicializálás -mainView.footer.btcInfo.synchronizedWith=szinkronizálva -mainView.footer.btcInfo.connectingTo=csatlakozás +mainView.footer.btcInfo.initializing=Connecting to Bitcoin network +mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO +mainView.footer.btcInfo.synchronizedWith=Synchronized with +mainView.footer.btcInfo.connectingTo=Connecting to mainView.footer.btcInfo.connectionFailed=sikeretlen kapcsolat mainView.footer.p2pInfo=P2P hálózat társak: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=BTC összeg {0}-ra createOffer.amountPriceBox.buy.volumeDescription=Költenivaló {0} összeg createOffer.amountPriceBox.sell.volumeDescription=Kapnivaló {0} összeg createOffer.amountPriceBox.minAmountDescription=Minimális BTC összeg -createOffer.securityDeposit.prompt=BTC óvadék +createOffer.securityDeposit.prompt=Kaució createOffer.fundsBox.title=Finanszírozd ajánlatodat createOffer.fundsBox.offerFee=Tranzakció díj createOffer.fundsBox.networkFee=Mining fee createOffer.fundsBox.placeOfferSpinnerInfo=Az ajánlat közzététele folyamatban van ... createOffer.fundsBox.paymentLabel=Bisq tranzakció {0} azonosítóval createOffer.fundsBox.fundsStructure=({0} óvadék, {1} tranzakció díj, {2} bányászati díj) +createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=Ajánlata nyilvánosítottva createOffer.success.info=Nyitott ajánlataidat a következő helyen kezelheted: \"Portfolió/Nyílt ajánlataim\". createOffer.info.sellAtMarketPrice=You will always sell at market price as the price of your offer will be continuously updated. @@ -387,6 +392,7 @@ createOffer.alreadyFunded=Már finanszírozta ezt az ajánlatot.\nA pénzeszköz createOffer.createOfferFundWalletInfo.headline=Finanszírozd ajánlatodat # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Váltott összeg: {0} \n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} and {1} createOffer.createOfferFundWalletInfo.msg=Le kell raknia {0} előleget az ajánlat elfogadásához.\n\nEzeket az összegek a helyi pénztárcájában vannak lefoglalva, és a multisig címére lesznek lezárva, amint valaki elfogadja ajánlatát\n\nThe amount is the sum of:\n{1}- Kaució: {2}\n- Tranzakciói díj: {3}\n- Bányászati díj: {4}\n\nKét lehetőség közül választhat a tranzakció finanszírozásakor:\n- Használja a Bisq pénztárcáját (könnyebb, viszont a tranzakciók összekapcsolhatók) VAGY\n- Átutalás külső pénztárcából (lehetőleg privátabb)\n\nAz összes finanszírozási lehetőséget és részleteket a felugró bezárása után láthatja. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=A megadott ár a megengedett piaci árnál l createOffer.changePrice=Árváltozás createOffer.tac=Ez ajánlat közzétételével egyetértek azzal, hogy minden olyan kereskedővel vállalok tranzakciózni, aki teljesíti a képernyőn meghatározott feltételeket. createOffer.currencyForFee=Tranzakció díj -createOffer.setDeposit=Állítsa be a vevő biztonsági letétjét +createOffer.setDeposit=Set buyer's security deposit (%) +createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) +createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} #################################################################### @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=We'd love to hear back from you about your experie tradeFeedbackWindow.msg.part2=If you have any questions, or experienced any problems, please get in touch with other users and contributors via the Bisq forum at: tradeFeedbackWindow.msg.part3=Thanks for using Bisq! + +daoTestingFeedbackWindow.title=Thank you for testing the Bisq DAO +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Visit forum +daoTestingFeedbackWindow.msg.part3=Thanks for using Bisq! + portfolio.pending.role=Az én szerepem portfolio.pending.tradeInformation=Tranzakció információ portfolio.pending.remainingTime=Hátralévő idő @@ -769,11 +786,11 @@ support.buyerTaker=BTC vásárló/Vevő support.sellerTaker=BTC eladó/Vevő support.backgroundInfo=Bisq is not a company, so it handles disputes differently.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display an \"Open dispute\" button after the trade period is over for contacting the arbitrator.\n\nIf there is an issue with the application, the software will try to detect it and, if possible, display an \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIf you are having an issue and did not see the \"Open support ticket\" button, you can open a support ticket manually by selecting the trade causing issues under \"Portfolio/Open trades\" and hitting \"alt + o\" or \"option + o\". Please use this method only if you are sure that the software is not working as expected. If you have problems or questions, please review the FAQ on the bisq.network web page or post in the Bisq forum in the Support section. -support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrator's requests within 2 days.\n2. The maximum period for a dispute is 14 days.\n3. You need to cooperate with the arbitrator and provide the information they request to make your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=Rendszerüzenet: {0} -support.youOpenedTicket=Nyitottál egy támogatási kérelmet. -support.youOpenedDispute=Megindítottál egy vitarendezési kérelmet.\n\n{0} -support.peerOpenedTicket=Váltótársa támogatást kért technikai problémák miatt. Kérjük, várjon további utasításokra. +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=Váltótársa vitát kérelmezett.\n\n{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=Legalább egy bírót kell választania. account.altcoin.yourAltcoinAccounts=Your altcoin accounts account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe arbitrator is not a {2} specialist and cannot help in such cases. account.altcoin.popup.wallet.confirm=Megértem és alátámasztom, hogy tudom, melyik pénztárcát kell használnom. +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 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 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\nFor sending XMR, you need to use either the official Monero GUI wallet or Monero 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.\nmonero-wallet-cli (use the command get_tx_key)\nmonero-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nIn addition to XMR checktx tool (https://xmr.llcoins.net/checktx.html) verification can also be accomplished in-wallet.\nmonero-wallet-cli : using command (check_tx_key).\nmonero-wallet-gui : on the Advanced > Prove/Check page.\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 XMR sender is responsible for providing verification of the XMR 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 (https://www.getmonero.org/resources/user-guides/prove-payment.html) or the Monero forum (https://forum.getmonero.org) to find more information. 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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=A {0} használatakor csak átlátható címeket használhat (t-vel kezdődve) nem pedig a z-címeket (privát), mivel hogy a bíró nem tudja ellenőrizni a z-címekkel kezdődő tranzakciókat. -account.altcoin.popup.XZC.msg=A {0} használatakor csak átlátható (nyomon követhető) címeket használhat, nem pedig lenyomozhatatlan címeket, mivel hogy a bíró nem tudja ellenőrizni a tranzakciót lenyomozhatatlan címmel a blokkböngészőben. -account.altcoin.popup.bch=Bitcoin Cash és Bitcoin Clashic ismételt védelemben részesül. Ha ezeket az érméket használja, győződjön meg róla, hogy elegendő óvintézkedést és minden következtetést megért. Veszteségeket szenvedhet úgy, hogy egy érmét küld és szándék nélkül ugyanazokat az érméket küldi a másik blokkláncon. Mivel ezek a "légpénzes érmék" ugyanazt a történelmet osztják a Bitcoin blokkhálózattal, szintén vannak biztonsági kockázatok és jelentős rizikót jelent a magánélet elvesztését illetően.\n\nKérjük olvasson többet erről a témáról a Bisq fórumon: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Mivel hogy Bitcoin Gold ugyanazt a történetet osztja a Bitcoin blokkhálózatal, ez bizonyos biztonsági kockázatokkal jár és jelentős rizikót jelent a magánélet elvesztésében. Ha Bitcoin Gold-ot használ, győződjön meg róla hogy elegendő óvintézkedést és minden lehetséges következtetést megért.\n\nKérjük olvasson többet erről a témáról a Bisq fórumon: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.ZEC.msg=When using Zcash you can only use the transparent addresses (starting with t) not the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. +account.altcoin.popup.XZC.msg=When using Zcoin you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=Your national currency accounts @@ -961,8 +980,8 @@ account.seed.info=Please write down both wallet seed words and the date! You can account.seed.warn.noPw.msg=Nem állított be olyan pénztárca jelszót, amely megvédené a magszavak megjelenítését.\n\nMeg szeretné jeleníteni a magszavakat? account.seed.warn.noPw.yes=Igen, és ne kérdezz többé account.seed.enterPw=Írd be a jelszót a magszavak megtekintéséhez -account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state. -account.seed.restore.ok=Oké, értem és vissza szeretném állítani +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Ok, do the restore and shut down Bisq #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=The lower price must # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=BSQ pénztárca dao.tab.proposals=Governance dao.tab.bonding=Bonding dao.tab.proofOfBurn=Asset listing fee/Proof of burn +dao.tab.monitor=Network monitor +dao.tab.news=News dao.paidWithBsq=BSQ-vel fizetve -dao.availableBsqBalance=Available -dao.availableNonBsqBalance=Available non-BSQ balance (BTC) -dao.unverifiedBsqBalance=Unverified (awaiting block confirmation) +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Balance of all verified UTXOs +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) dao.lockedForVoteBalance=Used for voting dao.lockedInBonds=Locked in bonds +dao.availableNonBsqBalance=Available non-BSQ balance (BTC) dao.totalBsqBalance=Teljes BSQ egyenleg dao.tx.published.success=A tranzakciód sikeresen nyilvánosságra volt hozva. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Voting cycle overview dao.cycle.currentPhase=Current phase dao.cycle.currentBlockHeight=Current block height dao.cycle.proposal=Proposal phase +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=Blind vote phase dao.cycle.voteReveal=Vote reveal phase dao.cycle.voteResult=Vote result dao.cycle.phaseDuration={0} blocks (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=Cycles dao.results.cycles.table.header.cycle=Cycle @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Vote result dao.results.proposals.voting.detail.header=Vote results for selected proposal +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Határozatlan @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=Recipient BTC address # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=Asset listing fee per day # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. trade volume +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Current value: {0} dao.param.blocks={0} blocks -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Duration of {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} block(s) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(default value) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(has been changed in voting) +dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the P2P network.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Határozatlan # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Lockup dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Lockup dao.bond.table.button.unlock=Felnyit dao.bond.table.button.revoke=Revoke +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Határozatlan # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Not bonded yet # suppress inspection "UnusedProperty" @@ -1252,13 +1296,17 @@ dao.bond.bondState.UNLOCKED=Bond unlocked # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Bond confiscated +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Határozatlan # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Bonded reputation # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github admin +dao.bond.bondedRoleType.UNDEFINED=Határozatlan +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin # suppress inspection "UnusedProperty" @@ -1266,24 +1314,30 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Rocket chat admin # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Youtube admin +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq maintainer # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Website operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Forum operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Price node operator +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Btc node operator +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ explorer operator # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domain name holder # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS admin @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=DNS admin dao.bond.bondedRoleType.MEDIATOR=Mediator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Arbitrator +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=Asset listing fee +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=Asset listing fee dao.burnBsq.menuItem.proofOfBurn=Proof of burn dao.burnBsq.header=Fee for asset listing @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=Dátum dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=Tranzakciók dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Sign a message with key from proof or burn transaction -dao.proofOfBurn.verify.window.title=Verify a message with key from proof or burn transaction +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction dao.proofOfBurn.copySig=Copy signature to clipboard dao.proofOfBurn.sign=Sign dao.proofOfBurn.message=Message dao.proofOfBurn.sig=Signature dao.proofOfBurn.verify=Verify -dao.proofOfBurn.verify.header=Verify message with key from proof or burn transaction +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=Verification succeeded dao.proofOfBurn.verificationResult.failed=Verification failed @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Vote reveal # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Vote result +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Határozatlan # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Kártérítési kérelem # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=Generic proposal # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Proposal for confiscating a bond +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Határozatlan # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Kártérítési kérelem # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=Generic proposal # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Confiscating a bond - dao.proposal.details=Proposal details dao.proposal.selectedProposal=Kiválasztott kártérítési kérelmek dao.proposal.active.header=Proposals of current cycle dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal?\nThe already paid proposal fee will be lost. dao.proposal.active.remove.doRemove=Yes, remove my proposal dao.proposal.active.remove.failed=A kártérítési kérelmet nem sikerült eltávolítani. +dao.proposal.myVote.title=Szavazás dao.proposal.myVote.accept=Accept proposal dao.proposal.myVote.reject=Reject proposal dao.proposal.myVote.removeMyVote=Ignore proposal dao.proposal.myVote.merit=Vote weight from earned BSQ dao.proposal.myVote.stake=Vote weight from stake -dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID dao.proposal.myVote.revealTxId=Vote reveal transaction ID -dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0} -dao.proposal.votes.header=Vote on all proposals -dao.proposal.votes.header.voted=My vote -dao.proposal.myVote.button=Vote on all proposals +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Select proposal type +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=Proposal type -dao.proposal.create.createNew=Új kártérítési kérelem létrehozása -dao.proposal.create.create.button=Kártérítési kérelem létrehozása +dao.proposal.create.new=Új kártérítési kérelem létrehozása +dao.proposal.create.button=Kérelem létrehozása +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=proposal dao.proposal.display.type=Proposal type dao.proposal.display.name=Name/nickname dao.proposal.display.link=Link to detail info -dao.proposal.display.link.prompt=Link to Github issue +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=Requested amount in BSQ dao.proposal.display.bsqAddress=BSQ address dao.proposal.display.txId=Proposal transaction ID @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=Accepted dao.proposal.display.myVote.rejected=Rejected dao.proposal.display.myVote.ignored=Ignored dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}); +dao.proposal.myVote.invalid=Vote was invalid dao.proposal.voteResult.success=Accepted dao.proposal.voteResult.failed=Rejected @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=Asset to remove dao.blindVote=blind vote dao.blindVote.startPublishing=Publishing blind vote transaction... -dao.blindVote.success=Your blind vote has been successfully published. +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=Küldés dao.wallet.menuItem.receive=Fogadd: dao.wallet.menuItem.transactions=Tranzakciók dao.wallet.dashboard.myBalance=My wallet balance -dao.wallet.dashboard.distribution=Distribution of all BSQ -dao.wallet.dashboard.locked=Global state of locked BSQ -dao.wallet.dashboard.market=Market data -dao.wallet.dashboard.genesis=Genesis tranzakció -dao.wallet.dashboard.txDetails=BSQ transactions statistics -dao.wallet.dashboard.genesisBlockHeight=Genesis block height -dao.wallet.dashboard.genesisTxId=Genesis transaction ID -dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction -dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests -dao.wallet.dashboard.reimbursementAmount=BSQ issued for reimbursement requests -dao.wallet.dashboard.availableAmount=Total available BSQ -dao.wallet.dashboard.burntAmount=Burned BSQ (fees) -dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds -dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds -dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds -dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds -dao.wallet.dashboard.allTx=No. of all BSQ transactions -dao.wallet.dashboard.utxo=No. of all unspent transaction outputs -dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions -dao.wallet.dashboard.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions -dao.wallet.dashboard.burntTx=No. of all fee payments transactions -dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq) -dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price) - -dao.wallet.receive.fundYourWallet=Finanszírozd BSQ pénztárcádat -dao.wallet.receive.bsqAddress=BSQ wallet address + +dao.wallet.receive.fundYourWallet=Your BSQ receive address +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=The Bisq DAO +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=Pénzátutalás dao.wallet.send.sendBtcFunds=Send non-BSQ funds (BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=Latest verified block: {0} dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1} dao.wallet.tx.type=Típus +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Határozatlan # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Nem ismeretes # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=Compensation request/issuance dao.tx.issuanceFromCompReq.tooltip=Compensation request which led to an issuance of new BSQ.\nIssuance date: {0} dao.tx.issuanceFromReimbursement=Reimbursement request/issuance dao.tx.issuanceFromReimbursement.tooltip=Reimbursement request which led to an issuance of new BSQ.\nIssuance date: {0} -dao.proposal.create.missingBsqFunds=Nem rendelkezik elegendő összegekkel a kártérítési kérelem létrehozásához.\nHiányzó: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=Confirm {0} transaction dao.feeTx.confirm.details={0} fee: {1}\nMining fee: {2} ({3} Satoshis/byte)\nTransaction size: {4} Kb\n\nAre you sure you want to publish the {5} transaction? +dao.news.bisqDAO.title=THE BISQ DAO +dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. +dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO + +dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ +dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.news.pastContribution.yourAddress=Your BSQ Wallet Address +dao.news.pastContribution.requestNow=Request now + +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. +dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ +dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. +dao.news.DAOOnTestnet.readMoreLink=Read the full documentation + +dao.monitor.daoState=DAO state +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Conflicts +dao.monitor.state=Állapot +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=DAO state +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Block height +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Previous hash +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Previous hash +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Previous hash +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Market data +dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq) +dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price) +dao.factsAndFigures.dashboard.availableAmount=Total available BSQ + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests +dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Global state of locked BSQ +dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds +dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds +dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds +dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds +dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees) + +dao.factsAndFigures.transactions.genesis=Genesis tranzakció +dao.factsAndFigures.transactions.genesisBlockHeight=Genesis block height +dao.factsAndFigures.transactions.genesisTxId=Genesis transaction ID +dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics +dao.factsAndFigures.transactions.allTx=No. of all BSQ transactions +dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs +dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions +dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions +dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Filtered seed nodes (comma sep. onion addresses) filterWindow.priceRelayNode=Filtered price relay nodes (comma sep. onion addresses) filterWindow.btcNode=Filtered Bitcoin nodes (comma sep. addresses + port) filterWindow.preventPublicBtcNetwork=Prevent usage of public Bitcoin network +filterWindow.disableDao=Disable DAO filterWindow.add=Szűrő hozzáadása filterWindow.remove=Szűrő eltávolítása @@ -1955,6 +2103,10 @@ BTC_MAINNET=Bitcoin Mainnet BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=Év time.month=Hónap @@ -2025,6 +2177,8 @@ payment.country=Ország payment.extras=Extra requirements payment.email.mobile=Email or mobile no. payment.altcoin.address=Altcoin address +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=Altcoin payment.select.altcoin=Válassz vagy keress altérmét payment.secret=Secret question @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=Email or phone no. payment.venmo.venmoUserName=Venmo username payment.popmoney.accountId=Email or phone no. -payment.revolut.email=Email or phone no. +payment.revolut.email=Email +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=Citizen ID/Tax ID or phone no. payment.supportedCurrencies=Supported currencies payment.limitations=Limitations @@ -2082,6 +2237,10 @@ payment.limits.info=Kérjük vegye figyelembe, hogy minden banki átutalás bizo payment.cashDeposit.info=Please confirm your bank allows you to send cash deposits into other peoples' accounts. For example, Bank of America and Wells Fargo no longer allow such deposits. +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=Contact info payment.f2f.contact.prompt=How you want to get contacted by the trading peer? (email address, phone number,...) payment.f2f.city=City for 'Face to face' meeting @@ -2128,16 +2287,10 @@ F2F_SHORT=F2F # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altérmék PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Altérmék PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} {1} számokból kell állnia. validation.invalidInput=Érvénytelen bevitel: {0} validation.accountNrFormat=A fiók számának formátuma: {0} validation.altcoin.wrongStructure=A címellenőrzés sikertelen volt, mert nem egyezik a {0} cím szerkezetével. +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=A ZEC címét t-vel kell kezdeni. Az z által indított címek nem támogatottak. validation.altcoin.invalidAddress=A cím nem érvényes {0} address! {1} validation.bic.invalidLength=A bevitel hossza sem 8, sem 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Must contain only letters, numbers, validation.interacETransfer.invalidAnswer=Must be one word and contain only letters, numbers, and/or the symbol - validation.inputTooLarge=Input must not be larger than {0} validation.inputTooSmall=Input has to be larger than {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=The amount below the dust limit of {0} is not allowed. validation.length=Length must be between {0} and {1} validation.pattern=Input must be of format: {0} validation.noHexString=The input is not in HEX format. validation.advancedCash.invalidFormat=Must be a valid email or wallet id of format: X000000000000 +validation.invalidUrl=This is not a valid URL +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 534d350e8a7..5a17ab10479 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -53,14 +53,14 @@ shared.oneOffer=oferta shared.multipleOffers=ofertas shared.Offer=Oferta shared.openOffers=abrir ofertas -shared.trade=negociar +shared.trade=negociação shared.trades=negociações shared.openTrades=abrir negociações shared.dateTime=Data/Hora shared.price=Preço shared.priceWithCur=Preço em {0} shared.priceInCurForCur=Preço em {0} para 1 {1} -shared.fixedPriceInCurForCur=Preço fixo em {0} para 1 {1} +shared.fixedPriceInCurForCur=Preço em {0} fixo para 1 {1} shared.amount=Quantidade shared.amountWithCur=Quantidade em {0} shared.volumeWithCur=Volume em {0} @@ -96,15 +96,15 @@ shared.faq=Visite a página de ajuda shared.yesCancel=Sim, cancelar shared.nextStep=Próximo passo shared.selectTradingAccount=Selecionar conta de negociação -shared.fundFromSavingsWalletButton=Pagar com a carteira Bisq +shared.fundFromSavingsWalletButton=Transferir fundos da carteira Bisq shared.fundFromExternalWalletButton=Abrir sua carteira externa para prover fundos -shared.openDefaultWalletFailed=Erro ao abrir a carteira padrão de Bitcoin. Talvez você não possua uma instalada. +shared.openDefaultWalletFailed=Falha na abertura de uma carteira padrão de Bitcoin. Talvez você não possua uma instalada? 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=OR -shared.notEnoughFunds=Você não possui saldo suficiente em sua carteira bisq.\nSão necessários {0}, porém você possui apenas {1}.\n\nPor favor, transfira mais bitcoins para a sua carteira bisq em \"Fundos/Receber fundos\" ou realize o pagamento usando uma carteira externa. +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.waitingForFunds=Aguardando pagamento... shared.depositTransactionId=ID da Transação de depósito shared.TheBTCBuyer=O comprador de BTC @@ -113,8 +113,9 @@ 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.noDateAvailable=Sem dado disponível -shared.arbitratorsFee=Taxa de arbitragem +shared.exportJSON=Exportar para JSON +shared.noDateAvailable=Sem data disponível +shared.arbitratorsFee=Taxa do árbitro shared.noDetailsAvailable=Sem detalhes disponíveis shared.notUsedYet=Ainda não usado shared.date=Data @@ -136,7 +137,7 @@ shared.createNewAccount=Criar nova conta shared.saveNewAccount=Salvar nova conta shared.selectedAccount=Conta selecionada shared.deleteAccount=Apagar conta -shared.errorMessageInline=\nMensagem do erro: {0} +shared.errorMessageInline=\nMensagem de erro: {0} shared.errorMessage=Mensagem de erro shared.information=Informação shared.name=Nome @@ -178,11 +179,11 @@ shared.btcAmount=Quantidade de BTC shared.yourLanguage=Seus idiomas shared.addLanguage=Adicionar idioma shared.total=Total -shared.totalsNeeded=Quantia necessária -shared.tradeWalletAddress=Endereço da negociação -shared.tradeWalletBalance=Saldo da negociação +shared.totalsNeeded=Fundos necessária +shared.tradeWalletAddress=Endereço da carteira de negociação +shared.tradeWalletBalance=Saldo da carteira de negociação shared.makerTxFee=Ofertante: {0} -shared.takerTxFee=Aceitador da oferta: {0} +shared.takerTxFee=Aceitador: {0} shared.securityDepositBox.description=Depósito de segurança para BTC {0} shared.iConfirm=Eu confirmo shared.tradingFeeInBsqInfo=equivalente a {0} utilizado como taxa de mineração @@ -192,10 +193,12 @@ shared.crypto=Cripto shared.all=Todos shared.edit=Editar shared.advancedOptions=Opções avançadas -shared.interval=Interval +shared.interval=Intervalo shared.actions=Ações shared.buyerUpperCase=Comprador shared.sellerUpperCase=Vendedor +shared.new=NOVO +shared.blindVoteTxId=Blind vote transaction ID #################################################################### # UI views @@ -225,15 +228,16 @@ mainView.marketPrice.tooltip.altcoinExtra=Se a altcoin não estiver disponível mainView.balance.available=Saldo disponível mainView.balance.reserved=Reservado em ofertas mainView.balance.locked=Travado em negociações -mainView.balance.reserved.short=Reservados +mainView.balance.reserved.short=Reservado mainView.balance.locked.short=Travados mainView.footer.usingTor=(usando Tor) mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo=Pares na rede Bitcoin: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Iniciando -mainView.footer.btcInfo.synchronizedWith=sincronizado com -mainView.footer.btcInfo.connectingTo=conectando à +mainView.footer.btcInfo.initializing=Conectando-se à rede Bitcoin +mainView.footer.bsqInfo.synchronizing=/ Sincronizando DAO +mainView.footer.btcInfo.synchronizedWith=Sincronizado com +mainView.footer.btcInfo.connectingTo=Conectando-se a mainView.footer.btcInfo.connectionFailed=falha na conexão mainView.footer.p2pInfo=Pares na rede P2P: {0} @@ -271,8 +275,8 @@ market.offerBook.buyAltcoin=Comprar {0} (vender {1}) market.offerBook.sellAltcoin=Vender {0} (comprar {1}) market.offerBook.buyWithFiat=Comprar {0} market.offerBook.sellWithFiat=Vender {0} -market.offerBook.sellOffersHeaderLabel=Ofertas de compra de {0} -market.offerBook.buyOffersHeaderLabel=Ofertas de venda de {0} +market.offerBook.sellOffersHeaderLabel=Vender {0} para +market.offerBook.buyOffersHeaderLabel=Comprar {0} de market.offerBook.buy=Eu quero comprar bitcoin market.offerBook.sell=Eu quero vender bitcoin @@ -298,9 +302,9 @@ market.trades.tooltip.candle.date=Data: #################################################################### offerbook.createOffer=Criar oferta -offerbook.takeOffer=Aceitar -offerbook.takeOfferToBuy=Take offer to buy {0} -offerbook.takeOfferToSell=Take offer to sell {0} +offerbook.takeOffer=Aceitar oferta +offerbook.takeOfferToBuy=Comprar {0} +offerbook.takeOfferToSell=Vender {0} offerbook.trader=Trader offerbook.offerersBankId=ID do banco do ofertante (BIC/SWIFT): {0} offerbook.offerersBankName=Nome do banco do ofertante: {0} @@ -349,26 +353,27 @@ offerbook.info.buyBelowMarketPrice=Você irá pagar {0} a menos do que o atual p offerbook.info.buyAtFixedPrice=Você irá comprar nesse preço fixo. offerbook.info.sellAtFixedPrice=Você irá vender neste preço fixo. offerbook.info.noArbitrationInUserLanguage=O idioma do Bisq atualmente está definido para {1}. No entanto, em caso de disputa, a arbitragem para essa oferta será realizada em {0}. -offerbook.info.roundedFiatVolume=O montante foi arredondado para aumentar a privacidade do seu comércio. +offerbook.info.roundedFiatVolume=O valor foi arredondado para aumentar a privacidade da sua negociação. #################################################################### # Offerbook / Create offer #################################################################### createOffer.amount.prompt=Insira o valor em BTC -createOffer.price.prompt=Insira aqui o preço -createOffer.volume.prompt=Insira aqui o valor em {0} +createOffer.price.prompt=Insira o preço +createOffer.volume.prompt=Insira o valor em {0} createOffer.amountPriceBox.amountDescription=Quantia em BTC para {0} createOffer.amountPriceBox.buy.volumeDescription=Valor em {0} a ser gasto createOffer.amountPriceBox.sell.volumeDescription=Valor em {0} a ser recebido createOffer.amountPriceBox.minAmountDescription=Quantia mínima de BTC -createOffer.securityDeposit.prompt=Depósito de segurança em BTC +createOffer.securityDeposit.prompt=Depósito de segurança createOffer.fundsBox.title=Financiar sua oferta createOffer.fundsBox.offerFee=Taxa da negociação createOffer.fundsBox.networkFee=Taxa de mineração createOffer.fundsBox.placeOfferSpinnerInfo=Sua oferta está sendo publicada... createOffer.fundsBox.paymentLabel=negociação bisq com ID {0} createOffer.fundsBox.fundsStructure=({0} para o depósito de segurança, {1} para a taxa de transação e {2} para a taxa de mineração) +createOffer.fundsBox.fundsStructure.BSQ=({0} depósito de segurança, {1} taxa de mineração) + {2} taxa de transação createOffer.success.headline=Sua oferta foi publicada createOffer.success.info=Você pode administrar suas ofertas abertas em \"Portfolio/Minhas ofertas\". createOffer.info.sellAtMarketPrice=Você irá sempre vender a preço de mercado e o preço de sua oferta será atualizado constantemente. @@ -387,7 +392,8 @@ createOffer.alreadyFunded=Você já havia financiado aquela oferta.\nSeus fundos createOffer.createOfferFundWalletInfo.headline=Financiar sua oferta # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Quantia da negociação: {0} \n -createOffer.createOfferFundWalletInfo.msg=Você precisa depositar {0} para esta oferta.\n\nEsses fundos ficam reservados na sua carteira local e ficarão travados no ednereço de depósito multisig quando alguém aceitar a sua oferta.\n\nA quantia equivale a soma de:\n{1}- Seu depósito de segurança: {2}\n- Taxa de negociação: {3}\n- Taxa de mineração: {4}\n\nVocê pode escolher entre duas opções para financiar sua negociação:\n- Usar a sua carteira Bisq (conveniente, mas transações poderão ser associadas entre si) OU\n- Transferir a partir de uma carteira externa (potencialmente mais privado)\n\nVocê verá todas as opções de financiamento e detalhes após fechar esta janela. +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} e {1} +createOffer.createOfferFundWalletInfo.msg=Você precisa depositar {0} para esta oferta.\n\nEsses fundos ficam reservados na sua carteira local e ficarão travados no endereço de depósito multisig quando alguém aceitar a sua oferta.\n\nA quantia equivale à soma de:\n{1}- Seu depósito de segurança: {2}\n- Taxa de negociação: {3}\n- Taxa de mineração: {4}\n\nVocê pode financiar sua negociação das seguintes maneiras:\n- Usando a sua carteira Bisq (conveniente, mas transações poderão ser associadas entre si) OU\n- Usando uma carteira externa (maior privacidade)\n\nVocê verá todas as opções de financiamento e detalhes após fechar esta janela. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=Um erro ocorreu ao emitir uma oferta:\n\n{0}\n\nNenhum fundo foi retirado de sua carteira até agora.\nPor favor, reinicie o programa e verifique sua conexão de internet. @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=O preço submetido está fora do desvio máx createOffer.changePrice=Alterar preço createOffer.tac=Ao publicar essa oferta, eu concordo em negociar com qualquer trader que preencher as condições definidas nesta tela. createOffer.currencyForFee=Taxa da negociação -createOffer.setDeposit=Definir o depósito de segurança do comprador +createOffer.setDeposit=Definir o depósito de segurança do comprador (%) +createOffer.setDepositAsBuyer=Definir o meu depósito de segurança como comprador (%) +createOffer.securityDepositInfo=O seu depósito de segurança do comprador será {0} +createOffer.securityDepositInfoAsBuyer=O seu depósito de segurança como comprador será {0} #################################################################### @@ -478,14 +487,14 @@ portfolio.tab.history=Histórico portfolio.tab.failed=Falha portfolio.tab.editOpenOffer=Editar oferta -portfolio.pending.step1.waitForConf=Esperando confirmação do protocolo +portfolio.pending.step1.waitForConf=Aguardar confirmação da blockchain portfolio.pending.step2_buyer.startPayment=Iniciar pagamento -portfolio.pending.step2_seller.waitPaymentStarted=Aguardar até que o pagamento se inicie -portfolio.pending.step3_buyer.waitPaymentArrived=Aguardar até que o pagamento chegue -portfolio.pending.step3_seller.confirmPaymentReceived=Confirmar pagamento recebido +portfolio.pending.step2_seller.waitPaymentStarted=Aguardar pagamento +portfolio.pending.step3_buyer.waitPaymentArrived=Aguardar recebimento do pagamento +portfolio.pending.step3_seller.confirmPaymentReceived=Confirmar recebimento do pagamento portfolio.pending.step5.completed=Concluído -portfolio.pending.step1.info=A transação de depósito foi publicada.\nApós aguardar uma confirmação da blockchain, {0} poderá iniciar o pagamento. +portfolio.pending.step1.info=A transação de depósito foi publicada.\nApós aguardar uma confirmação da blockchain, o {0} poderá realizar 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 a partir de uma carteira externa de um dos negociadores foi muito baixa. portfolio.pending.step1.openForDispute=A transação do depósito ainda não foi confirmada.\nIsto raramente pode ocorrer quando um dos negociadores envia uma taxa de financiamento muito baixa através de uma carteira externa.\nO período máximo para a negociação já se esgotou.\n\nPor favor, aguarde mais um pouco ou entre em contato com o árbitro para abrir uma disputa. @@ -504,11 +513,11 @@ portfolio.pending.step2_buyer.fees=Caso seu banco cobre taxas você terá que co portfolio.pending.step2_buyer.altcoin=Transfira com a sua carteira {0} externa\n{1} para o vendedor de BTC.\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.cash=Vá ao banco e pague {0} ao vendedor de BTC.\n\n -portfolio.pending.step2_buyer.cash.extra=REQUESITO IMPORTANTE:\nApós executar o pagamento escrever no recibo: NÃO ACEITO RESTITUIÇÃO\nEntão rasgue-o em 2 partes, tire uma foto e envia-a para o email do vendedor de BTC. +portfolio.pending.step2_buyer.cash.extra=REQUESITO IMPORTANTE:\nApós executar o pagamento, escreva no comprovante de depósito: SEM REEMBOLSO\nEntão rasgue-o em 2 partes, tire uma foto e envie-a para o e-mail do vendedor de BTC. portfolio.pending.step2_buyer.moneyGram=Por favor, pague {0} ao vendedor de BTC usando MoneyGram.\n\n -portfolio.pending.step2_buyer.moneyGram.extra=IMPORTANTE:\nApós você ter feito o pagamento, envie o número de autorização e uma foto do recibo por e-mail para o vendedor de BTC.\nO recibo deve exibir claramente o nome completo, o país e o estado do vendedor, assim como a quantia. O e-mail do vendedor é: {0}. +portfolio.pending.step2_buyer.moneyGram.extra=IMPORTANTE:\nApós você ter feito o pagamento, envie o número de autorização e uma foto do comprovante por e-mail para o vendedor de BTC.\nO comprovante deve exibir claramente o nome completo, o país e o estado do vendedor, assim como a quantia. O e-mail do vendedor é: {0}. portfolio.pending.step2_buyer.westernUnion=Por favor, pague {0} ao vendedor de BTC através da Western Union.\n\n -portfolio.pending.step2_buyer.westernUnion.extra=IMPORTANTE:\nApós você ter feito o pagamento, envie o número de rastreamento (MTCN) e uma foto do recibo por e-mail para o vendedor de BTC.\nO recibo deve exibir claramente o nome completo, o país e o estado do vendedor, assim como a quantia. O e-mail do vendedor é: {0}. +portfolio.pending.step2_buyer.westernUnion.extra=IMPORTANTE:\nApós você ter feito o pagamento, envie o número de rastreamento (MTCN) e uma foto do comprovante por e-mail para o vendedor de BTC.\nO comprovante deve exibir claramente o nome completo, o país e o estado do vendedor, assim como a quantia. O e-mail do vendedor é: {0}. # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step2_buyer.postal=Envie {0} através de \"US Postal Money Order\" para o vendedor de BTC.\n\n @@ -522,11 +531,11 @@ portfolio.pending.step2_buyer.buyerAccount=A sua conta de pagamento a ser usada portfolio.pending.step2_buyer.paymentStarted=Pagamento iniciado portfolio.pending.step2_buyer.warn=Você ainda não realizou o seu pagamento de {0}!\nEssa negociação deve ser completada até {1}, caso contrário ela será investigada pelo árbitro. portfolio.pending.step2_buyer.openForDispute=Você ainda não concluiu o pagamento!\nO período máximo para a negociação já passou.\n\nFavor entrar em contato com o árbitro para abrir uma disputa. -portfolio.pending.step2_buyer.paperReceipt.headline=Você enviou o papel de recibo para o vendedor de BTC? -portfolio.pending.step2_buyer.paperReceipt.msg=Lembre-se:\nVocê deve escrever no recibo: SEM RESTITUIÇÃO\nDepois deve rasgá-lo em 2 partes, tirar uma foto e enviar para o email do vendedor. -portfolio.pending.step2_buyer.moneyGramMTCNInfo.headline=Enviar o número de autorização e o recibo -portfolio.pending.step2_buyer.moneyGramMTCNInfo.msg=You need to send the Authorisation number and a photo of the receipt by email to the BTC seller.\nThe receipt must clearly show the seller''s full name, country, state and the amount. The seller''s email is: {0}.\n\nDid you send the Authorisation number and contract to the seller? -portfolio.pending.step2_buyer.westernUnionMTCNInfo.headline=Enviar MTCN e recibo +portfolio.pending.step2_buyer.paperReceipt.headline=Você enviou o comprovante de depósito para o vendedor de BTC? +portfolio.pending.step2_buyer.paperReceipt.msg=Lembre-se:\nVocê deve escrever no comprovante de depósito: SEM REEMBOLSO\nA seguir, rasgue-o em duas partes, tire uma foto e envie-a para o e-mail do vendedor de BTC. +portfolio.pending.step2_buyer.moneyGramMTCNInfo.headline=Enviar o número de autorização e o comprovante de depósito +portfolio.pending.step2_buyer.moneyGramMTCNInfo.msg=Você previsa enviar por-email para o vendedor BTC o número de autorização e uma foto com o comprovante de depósito.\nO comprovante deve mostrar claramente o nome completo, o país e o estado do vendedor, assim como a quantia. O e-mail do vendedor é: {0}.\n\nVocê enviou o número de autorização e o contrato ao vendedor? +portfolio.pending.step2_buyer.westernUnionMTCNInfo.headline=Enviar MTCN e comprovante portfolio.pending.step2_buyer.westernUnionMTCNInfo.msg=You need to send the MTCN (tracking number) and a photo of the receipt by email to the BTC seller.\nThe receipt must clearly show the seller''s full name, city, country and the amount. The seller''s email is: {0}.\n\nDid you send the MTCN and contract to the seller? portfolio.pending.step2_buyer.halCashInfo.headline=Enviar código HalCash portfolio.pending.step2_buyer.halCashInfo.msg=You need to send a text message with the HalCash code as well as the trade ID ({0}) to the BTC seller.\nThe seller''s mobile nr. is {1}.\n\nDid you send the code to the seller? @@ -535,10 +544,10 @@ portfolio.pending.step2_buyer.confirmStart.headline=Confirme que você iniciou o 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_seller.waitPayment.headline=Aguardar o pagamento +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}. -portfolio.pending.step2_seller.warn=The BTC buyer still has not done the {0} payment.\nYou need to wait until they have started the payment.\nIf the trade has not been completed on {1} the arbitrator will investigate. +portfolio.pending.step2_seller.warn=O comprador de BTC ainda não fez o pagamento de {0}.\nVocê precisa esperar até que ele inicie o pagamento.\nCaso a negociação não conclua em {1}, o árbitro irá investigar. portfolio.pending.step2_seller.openForDispute=O comprador de BTC ainda não iniciou o pagamento!\nO período máximo permitido para a negociação expirou.\nVocê pode aguardar mais um pouco, dando mais tempo para o seu parceiro de negociação, ou você pode entrar em contato com o árbitro para abrir uma disputa. # suppress inspection "UnusedProperty" @@ -555,33 +564,33 @@ message.state.ACKNOWLEDGED=O destinário confirmou o recebimento da mensagem message.state.FAILED=Erro ao enviar a mensagem portfolio.pending.step3_buyer.wait.headline=Aguarde confirmação de pagamento do vendedor de BTC. -portfolio.pending.step3_buyer.wait.info=Aguardando confirmação do vendedor de BTC para o recibo do pagamento de {0}. -portfolio.pending.step3_buyer.wait.msgStateInfo.label=Payment started message status +portfolio.pending.step3_buyer.wait.info=Aguardando o vendedor de BTC confirmar o recebimento do pagamento de {0}. +portfolio.pending.step3_buyer.wait.msgStateInfo.label=Status da mensagem de pagamento iniciado portfolio.pending.step3_buyer.warn.part1a=na blockchain {0} portfolio.pending.step3_buyer.warn.part1b=com seu provedor de pagamentos (por exemplo seu banco) portfolio.pending.step3_buyer.warn.part2=O vendedor de BTC ainda não confirmou o pagamento!\nPor favor, verifique em {0} se o pagamento foi enviado com sucesso.\nCaso o vendedor não confirme o recebimento do pagamento até {1}, a negociação será investigada pelo árbitro. portfolio.pending.step3_buyer.openForDispute=O vendedor de BTC não confirmou o seu pagamento!\nO período máximo para essa negociação expirou..\nVocê pode aguardar mais um pouco, dando mais tempo para o seu parceiro de negociação, ou você pode entrar em contato com um árbitro para abrir uma disputa. # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.part=Your trading partner has confirmed that they have initiated the {0} payment.\n\n +portfolio.pending.step3_seller.part=Seu parceiro de negociação confirmou que iniciou o pagamento de {0}.\n\n portfolio.pending.step3_seller.altcoin.explorer=em seu explorador preferido da blockchain do {0} portfolio.pending.step3_seller.altcoin.wallet=em sua carteira {0} portfolio.pending.step3_seller.altcoin={0}Verifique em {1} se a transação para o seu endereço de recebimento\n{2}\njá tem confirmações suficientes na blockchain.\nA quantia do pagamento deve ser {3}\n\nVocê pode copiar e colar seu endereço {4} na janela principal, após fechar aquele popup. portfolio.pending.step3_seller.postal={0}Por gentileza verifique se recebeu {1} como \"US Postal Money Order\" do comprador de BTC.\n\nO ID de negociação (texto \"razão do pagamento\") da transação é: \"{2}\" -portfolio.pending.step3_seller.bank=Your trading partner has confirmed that they have initiated the {0} payment.\n\nPlease go to your online banking web page and check if you have received {1} from the BTC buyer.\n\nThe trade ID (\"reason for payment\" text) of the transaction is: \"{2}\"\n\n -portfolio.pending.step3_seller.cash=\n\nComo o pagamento é realizado através de depósito de dinheiro, o comprador de BTC deve escrever \"SEM RESTITUIÇÃO\" no recibo de papel, rasgá-lo em duas partes e enviar uma foto do recibo para você por e-mail.\n\nPara evitar risco de restituição, apenas confirme caso você tenha recebido o e-mail e tem certeza de que o recibo é válido.\nSe não tiver certeza, {0} +portfolio.pending.step3_seller.bank=Seu parceiro de negociação confirmou que iniciou o pagamento de {0}.\n\nAcesse sua conta bancária online e verifique se você recebeu {1} do comprador de BTC.\n\nO ID de negociação (texto \"motivo do pagamento\") da transação é: \"{2}\" +portfolio.pending.step3_seller.cash=Como o pagamento é realizado através de depósito de dinheiro em espécie, o comprador de BTC obrigatoriamente deve escrever \"SEM REEMBOLSO\" no comprovante de depósito, rasgá-lo em duas partes e enviar uma foto do comprovante para você por e-mail.\n\nPara reduzir a chance de um reembolso (restituição do valor depositado para o comprador), confirme apenas se você tiver recebido o e-mail e tiver certeza de que o comprovante de depósito é autêntico.\nSe você não tiver certeza, {0} portfolio.pending.step3_seller.moneyGram=The buyer has to send you the Authorisation number and a photo of the receipt by email.\nThe receipt must clearly show your full name, country, state and the amount. Please check your email if you received the Authorisation number.\n\nAfter closing that popup you will see the BTC buyer's name and address for picking up the money from MoneyGram.\n\nOnly confirm receipt after you have successfully picked up the money! portfolio.pending.step3_seller.westernUnion=The buyer has to send you the MTCN (tracking number) and a photo of the receipt by email.\nThe receipt must clearly show your full name, city, country and the amount. Please check your email if you received the MTCN.\n\nAfter closing that popup you will see the BTC buyer's name and address for picking up the money from Western Union.\n\nOnly confirm receipt after you have successfully picked up the money! portfolio.pending.step3_seller.halCash=The buyer has to send you the HalCash code as text message. Beside that you will receive a message from HalCash with the required information to withdraw the EUR from a HalCash supporting ATM.\n\nAfter you have picked up the money from the ATM please confirm here the receipt of the payment! portfolio.pending.step3_seller.bankCheck=\n\nPor favor, verifique também que o nome do remetente no seu extrato bancário confere com o do contrato de negociação:\nNome do remetente: {0}\n\nSe o nome do extrato bancário não for o mesmo que o exibido acima, {1} portfolio.pending.step3_seller.openDispute=por favor, não confirme e abra uma disputa pressionando \"alt + o\" ou \"option + o\". -portfolio.pending.step3_seller.confirmPaymentReceipt=Confirmar recibo de pagamento +portfolio.pending.step3_seller.confirmPaymentReceipt=Confirmar recebimento do pagamento 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.confirmReceipt=Confirmar recibo de pagamento +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 houverem confirmações suficientes. portfolio.pending.step3_seller.buyerStartedPayment.fiat=Verifique em sua conta de negociação (por exemplo, sua conta bancária) e confirme que recebeu o pagamento. @@ -590,13 +599,13 @@ portfolio.pending.step3_seller.warn.part1b=em seu provedor de pagamentos (por ex portfolio.pending.step3_seller.warn.part2=Você ainda não confirmou o recebimento do pagamento!\nPor favor, verifique em {0} se você recebeu o pagamento.\nCaso você não confirme o recebimento até {1}, a negociação será investigada pelo árbitro. portfolio.pending.step3_seller.openForDispute=Você ainda não confirmou o recebimento do pagamento!\nO período máximo para a negociação expirou.\nPor favor, confirme o recebimento do pagamento ou entre em contato com o árbitro para abrir uma disputa. # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.onPaymentReceived.part1=Você recebeu o pagamento {0} de seu parceiro de negociação?\n\n +portfolio.pending.step3_seller.onPaymentReceived.part1=Você recebeu o pagamento em {0} de seu parceiro de negociação?\n\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.fiat=O ID de negociação (texto \"razão do pagamento\") da transação é: \"{0}\"\n # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.name=Verifique também no extrato bancário se o nome do remetente confere com o nome que foi fornecido no contrato da negociação:\nNome do remetente: {0}\n\nSe o nome do extrato não for o mesmo que o nome exibido acima, não confirme e abra uma disputa pressionando \"alt + o\" ou \"option + o\".\n\n -portfolio.pending.step3_seller.onPaymentReceived.note=Tenha em mente que, assim que confirmar o recibo, o valor reservado será liberado para o comprador de BTC e o depósito de segurança será devolvido. -portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Confirme que recebeu o pagamento +portfolio.pending.step3_seller.onPaymentReceived.note=Assim que você confirmar o recebimento do pagamento, o valor da transação será liberado para o comprador de BTC e os depósitos de segurança serão devolvidos para você e para o comprador. +portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Confirmar recebimento do pagamento portfolio.pending.step3_seller.onPaymentReceived.confirm.yes=Sim, eu recebi o pagamento portfolio.pending.step5_buyer.groupTitle=Resumo da negociação @@ -625,12 +634,20 @@ tradeFeedbackWindow.msg.part1=Nós adoramos saber como está sendo a sua experi tradeFeedbackWindow.msg.part2=Se você tem dúvidas ou está tendo problemas, por favor entre em contato com outros usuários e contribuidores através do fórum Bisq em: tradeFeedbackWindow.msg.part3=Obrigado por usar Bisq! + +daoTestingFeedbackWindow.title=Obrigado por testar o DAO da Bisq +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Acessar fórum +daoTestingFeedbackWindow.msg.part3=Obrigado por usar Bisq! + portfolio.pending.role=Minha função portfolio.pending.tradeInformation=Informação da negociação portfolio.pending.remainingTime=Tempo restante portfolio.pending.remainingTimeDetail={0} (até {1}) portfolio.pending.tradePeriodInfo=O período de negociação irá se iniciar após a primeira confirmação na blockchain. O período de negociação máximo irá variar de acordo com o método de pagamento utilizado. -portfolio.pending.tradePeriodWarning=Se o período expirar os dois negociantes poderão abrir uma disputa. +portfolio.pending.tradePeriodWarning=Se o período expirar, os dois negociantes poderão abrir uma disputa. portfolio.pending.tradeNotCompleted=Negociação não completada a tempo (até {0}) portfolio.pending.tradeProcess=Processo de negociação portfolio.pending.openAgainDispute.msg=Se você não tem certeza de que o árbitro recebeu a mensagem (por exemplo, se você não receber nenhuma resposta após 1 dia), sinta-se livre em abrir uma disputa novamente. @@ -724,7 +741,7 @@ funds.tx.revert=Reverter funds.tx.txSent=Transação enviada com sucesso para um novo endereço em sua carteira Bisq local. funds.tx.direction.self=Enviar para você mesmo funds.tx.daoTxFee=Miner fee for DAO tx -funds.tx.reimbursementRequestTxFee=Reimbursement request +funds.tx.reimbursementRequestTxFee=Solicitar reembolso funds.tx.compensationRequestTxFee=Pedido de compensação @@ -736,7 +753,7 @@ support.tab.support=Tickets de suporte support.tab.ArbitratorsSupportTickets=Tickets de suporte do árbitro support.tab.TradersSupportTickets=Tickets de suporte do negociador support.filter=Lista de filtragem -support.noTickets=Não há tickets abertos +support.noTickets=Não há tickets de suporte abertos support.sendingMessage=Enviando mensagem... support.receiverNotOnline=Recebedor não está conectado. A mensagem foi gravada na caixa postal. support.sendMessageError=Erro ao enviar a mensagem: {0} @@ -767,13 +784,13 @@ support.buyerOfferer=Comprador de BTC / Ofetante support.sellerOfferer=Vendedor de BTC / Ofertante support.buyerTaker=Comprador de BTC / Aceitador da oferta support.sellerTaker=Vendedor de BTC / Aceitador da oferta -support.backgroundInfo=Bisq is not a company, so it handles disputes differently.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display an \"Open dispute\" button after the trade period is over for contacting the arbitrator.\n\nIf there is an issue with the application, the software will try to detect it and, if possible, display an \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIf you are having an issue and did not see the \"Open support ticket\" button, you can open a support ticket manually by selecting the trade causing issues under \"Portfolio/Open trades\" and hitting \"alt + o\" or \"option + o\". Please use this method only if you are sure that the software is not working as expected. If you have problems or questions, please review the FAQ on the bisq.network web page or post in the Bisq forum in the Support section. +support.backgroundInfo=Como a Bisq não é uma empresa, as disputas são resolvidas de maneira diferente.\n\nSe surgir alguma disputa no processo de negociação (ex: se você não receber um pagamento), o aplicativo irá exibir um botão \"Abrir disputa\" após o período de negociação ter terminado. Esse botão permitirá que você entre em contato com árbitro da negociação.\n\nSe surgir algum problema/bug no aplicativo, o próprio aplicativo tentará detectá-lo e, se possível, irá exibir um botão \"Abrir ticket de suporte\". Este ticket será enviado para o árbitro, que encaminhará o problema detectado para os desenvolvedores do aplicativo.\n\nSe surgr algum problema/bug no aplicativo e o botão \"Abrir ticket de suporte\" não for exibido, você poderá abrir um ticket manualmente. Para fazer isso, acesse \"Portfolio/Negociações em aberto\", selecione a negociação em que o problema surgiu e use a combinação de teclas \"alt + o\" ou \"option + o\" no seu teclado. Por favor, utilize esse método somente se você tiver certeza de que o software não está funcionando como o esperado. Se você tiver problemas ou dúvidas, leia as dúvidas comuns (FAQ) no site https://bisq.network ou faça uma postagem na seção Suporte do fórum do Bisq. -support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrator's requests within 2 days.\n2. The maximum period for a dispute is 14 days.\n3. You need to cooperate with the arbitrator and provide the information they request to make your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=Mensagem do sistema: {0} -support.youOpenedTicket=Você abriu uma solicitação para suporte. -support.youOpenedDispute=Você abriu uma solicitação para disputa.\n\n{0} -support.peerOpenedTicket=Seu parceiro de negociação solicitou suporte devido a problemas técnicos. Por favor aguarde novas instruções. +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=Seu parceiro de negociação solicitou uma disputa.\n\n{0} @@ -793,7 +810,7 @@ setting.preferences.txFee=Taxa da transação de retirada (satoshis/byte) setting.preferences.useCustomValue=Usar valor personalizado setting.preferences.txFeeMin=A taxa de transação precisa ter pelo menos {0} satoshis/byte setting.preferences.txFeeTooLarge=Seu valor está muito alto (>5.000 satoshis/byte). A taxa de transação normalmente fica na faixa de 50-400 satoshis/byte. -setting.preferences.ignorePeers=Ignorar par(es) com endereço(s) onion (ex:end1,end2) +setting.preferences.ignorePeers=Ignorar o(s) endereço(s) onion (ex:end1,end2) setting.preferences.refererId=Referral ID setting.preferences.refererId.prompt=ID de referência opcional setting.preferences.currenciesInList=Moedas na lista de preços de mercado @@ -806,10 +823,10 @@ setting.preferences.noAltcoins=Não há altcoins selecionadas setting.preferences.addFiat=Adicionar moeda nacional setting.preferences.addAltcoin=Adicionar altcoin setting.preferences.displayOptions=Opções de exibição -setting.preferences.showOwnOffers=Exibir minhas próprias ofertas no livro de ofertas +setting.preferences.showOwnOffers=Exibir minhas ofertas no livro de ofertas setting.preferences.useAnimations=Usar animações setting.preferences.sortWithNumOffers=Ordenar pelo nº de ofertas/negociações -setting.preferences.resetAllFlags=Esquecer todas marcações \"Não exibir novamente\" +setting.preferences.resetAllFlags=Esquecer marcações \"Não exibir novamente\" setting.preferences.reset=Limpar settings.preferences.languageChange=Para aplicar a mudança de língua em todas as telas requer uma reinicialização. settings.preferences.arbitrationLanguageWarning=Em caso de disputa, a arbitragem será realizada em {0}. @@ -838,8 +855,8 @@ settings.net.useCustomNodesRadio=Usar nodos personalizados do Bitcoin Core settings.net.warn.usePublicNodes=Ao usar a rede pública do Bitcoin, você estará se expondo a um problema grave de privacidade que é causado por uma falha de projeto e de implementação do filtro bloom, o qual é usado em carteiras SPV como a BitcoinJ (a carteira SPV usada no Bisq). Qualquer nodo completo a que você se conectar será capaz de descobrir que todos os endereços da sua carteira pertencem a uma mesma pessoa/entidade.\n\nPor favor, leia mais sobre os detalhes em: https://bisq.network/blog/privacy-in-bitsquare.\n\nVocê tem certeza de que realmente quer usar os nodos públicos? settings.net.warn.usePublicNodes.useProvided=Não, usar os nodos fornecidos settings.net.warn.usePublicNodes.usePublic=Sim, usar rede pública -settings.net.warn.useCustomNodes.B2XWarning=Please be sure that your Bitcoin node is a trusted Bitcoin Core node!\n\nConnecting to nodes which do not follow the Bitcoin Core consensus rules could corrupt your wallet and cause problems in the trade process.\n\nUsers who connect to nodes that violate consensus rules are responsible for any resulting damage. Any resulting disputes will be decided in favor of the other peer. No technical support will be given to users who ignore this warning and protection mechanisms! -settings.net.localhostBtcNodeInfo=(Background information: If you are running a local Bitcoin node (localhost) you can connect exclusively to it.) +settings.net.warn.useCustomNodes.B2XWarning=Certifique-se de que o seu nodo Bitcoin é um nodo Bitcoin Core confiável!\n\nAo se conectar a nodos que não estão seguindo as regras de consenso do Bitcoin Core, você pode corromper a sua carteira e causar problemas no processo de negociação.\n\nOs usuários que se conectam a nodos que violam as regras de consenso são responsáveis pelos danos que forem criados por isso. As disputas causadas por esse motivo serão decididas a favor do outro negociante. Nenhum suporte técnico será fornecido para os usuários que ignorarem esse aviso e os mecanismos de proteção! +settings.net.localhostBtcNodeInfo=(Nota: se você estiver rodando um nodo Bitcoin local (localhost), você pode conectar-se exclusivamente a ele.) settings.net.p2PPeersLabel=Pares conectados settings.net.onionAddressColumn=Endereço onion settings.net.creationDateColumn=Conexão @@ -863,16 +880,16 @@ settings.net.outbound=sainte settings.net.reSyncSPVChainLabel=Ressincronizar SPV chain settings.net.reSyncSPVChainButton=Remover arquivo SPV e ressincronizar settings.net.reSyncSPVSuccess=The SPV chain file will be deleted on the next startup. You need to restart your application now.\n\nAfter the restart it can take a while to resync with the network and you will only see all transactions once the resync is completed.\n\nPlease restart again after the resync has completed because there are sometimes inconsistencies that result in incorrect balances to display. -settings.net.reSyncSPVAfterRestart=The SPV chain file has been deleted. Please be patient. It can take a while to resync with the network. +settings.net.reSyncSPVAfterRestart=O arquivo SPV chain foi removido. Por favor, tenha paciência, pois a ressincronização com a rede pode demorar. settings.net.reSyncSPVAfterRestartCompleted=A resincronização concluiu. Favor reiniciar o programa. settings.net.reSyncSPVFailed=Não foi possível apagar o arquivo da SPV chain.\nErro: {0} setting.about.aboutBisq=Sobre Bisq -setting.about.about=Bisq is open-source software which facilitates the exchange of bitcoin with national currencies (and other cryptocurrencies) through a decentralized peer-to-peer network in a way that strongly protects user privacy. Learn more about Bisq on our project web page. -setting.about.web=página da web do Bisq +setting.about.about=Bisq é um software de código aberto que facilita a troca de Bitcoin por moedas nacionais (e outras criptomoedas) através de uma rede ponto-a-ponto descentralizada, protegendo a privacidade dos usuários. Descubra mais sobre o Bisq no site do projeto. +setting.about.web=Site do Bisq setting.about.code=Código fonte setting.about.agpl=Licença AGPL setting.about.support=Suporte Bisq -setting.about.def=Bisq is not a company—it is a project open to the community. If you want to participate or support Bisq please follow the links below. +setting.about.def=Bisq não é uma empresa — é um projeto aberto à participação da comunidade. Se você tem interesse em participar do projeto ou apoiá-lo, visite os links abaixo. setting.about.contribute=Contribuir setting.about.donate=Fazer doação setting.about.providers=Provedores de dados @@ -894,7 +911,7 @@ setting.about.subsystems.val=Versão da rede: {0}; Versão de mensagens P2P: {1} account.tab.arbitratorRegistration=Registro de árbitro account.tab.account=Conta account.info.headline=Bem vindo à sua conta Bisq -account.info.msg=Here you can add trading accounts for national currencies & altcoins, select arbitrators, and create a backup of your wallet & account data.\n\nA new Bitcoin wallet was created the first time you started Bisq.\n\nWe strongly recommend that you write down your Bitcoin wallet seed words (see tab on the top) and consider adding a password before funding. Bitcoin deposits and withdrawals are managed in the \"Funds\" section.\n\nPrivacy & security note: because Bisq is a decentralized exchange, all your data is kept on your computer. There are no servers, so we have no access to your personal info, your funds, or even your IP address. Data such as bank account numbers, altcoin & Bitcoin addresses, etc are only shared with your trading partner to fulfill trades you initiate (in case of a dispute the arbitrator will see the same data as your trading peer). +account.info.msg=Nesta seção você pode criar e administrar contas para moedas nacionais e altcoins, escolher árbitros e fazer backup de sua carteira e dos dados da sua conta.\n\nUma carteira Bitcoin vazia foi criada na primeira vez que você iniciou o Bisq.\n\nRecomendamos que você anote as palavras da semente da sua carteira Bitcoin (na seção \"Semente da carteira\") e considere adicionar uma senha antes de transferir fundos para ela. Os depósitos e retiradas em Bitcoin são administrados na seção \"Fundos\".\n\nPrivacidade & Segurança:\nO Bisq é uma exchange descentralizada – ou seja, todos os seus dados pessoais são mantidos em seu computador. Nenhum servidor e nenhuma pessoa tem acesso às suas informações pessoais, seus fundos ou mesmo ao seu endereço IP. Dados como nº da conta corrente, endereços de Bitcoin e altcoins, etc. são compartilhados somente com os usuários que você iniciar uma negociação (caso surgir uma disputa, o árbitro irá ver os mesmos dados que o seu parceiro de negociação consegue ver). account.menu.paymentAccount=Contas de moedas nacionais account.menu.altCoinsAccountView=Contas de altcoins @@ -907,7 +924,7 @@ account.arbitratorRegistration.pubKey=Chave pública account.arbitratorRegistration.register=Registrar árbitro account.arbitratorRegistration.revoke=Revogar registro -account.arbitratorRegistration.info.msg=Please note that you need to stay available for 15 days after revoking as there might be trades which are using you as arbitrator. The max. allowed trade period is 8 days and the dispute process might take up to 7 days. +account.arbitratorRegistration.info.msg=Note que você precisa estar disponível por 15 dias após a revogação, pois podem existir negociações que estão utilizando você como árbitro. A duração máxima de uma negociação é de 8 dias e o processo de disputa pode levar até 7 dias. account.arbitratorRegistration.warn.min1Language=Você precisa escolher pelo menos 1 língua.\nNós adicionamos a língua padrão para você. account.arbitratorRegistration.removedSuccess=Você removeu com sucesso seu árbitro da rede P2P. account.arbitratorRegistration.removedFailed=Não foi possível remover árbitro.{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=Você precisa selecionar ao menos um árbitro account.altcoin.yourAltcoinAccounts=Suas contas de altcoins account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe arbitrator is not a {2} specialist and cannot help in such cases. account.altcoin.popup.wallet.confirm=Eu entendo e confirmo que eu sei qual carteira que preciso usar. +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 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 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\nFor sending XMR, you need to use either the official Monero GUI wallet or Monero 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.\nmonero-wallet-cli (use the command get_tx_key)\nmonero-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nIn addition to XMR checktx tool (https://xmr.llcoins.net/checktx.html) verification can also be accomplished in-wallet.\nmonero-wallet-cli : using command (check_tx_key).\nmonero-wallet-gui : on the Advanced > Prove/Check page.\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 XMR sender is responsible for providing verification of the XMR 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 (https://www.getmonero.org/resources/user-guides/prove-payment.html) or the Monero forum (https://forum.getmonero.org) to find more information. 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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=Quando usar {0} você pode apenas utilizar endereços transparentes (começando com t) não os endereços z (privados), pois o árbitro não conseguiria verificar a transação com endereços z. -account.altcoin.popup.XZC.msg=Ao usar {0}, você obrigatoriamente precisa usar endereços transparentes (rastreáveis), caso contrário o árbitro não conseguiria verificar em um explorador de blocos uma transação com endereço privado (irrastreável). -account.altcoin.popup.bch=Bitcoin Cash and Bitcoin Clashic suffer from replay protection. If you use those coins be sure you take sufficient precautions and understand all implications.You can suffer losses by sending one coin and unintentionally send the same coins on the other block chain.Because those "airdrop coins" share the same history with the Bitcoin blockchain there are also security risks and a considerable risk for losing privacy.\n\nPlease read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Because Bitcoin Gold shares the same history as the Bitcoin blockchain it comes with certain security risks and a considerable risk for losing privacy.If you use Bitcoin Gold be sure you take sufficient precautions and understand all implications.\n\nPlease read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.ZEC.msg=When using Zcash you can only use the transparent addresses (starting with t) not the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. +account.altcoin.popup.XZC.msg=When using Zcoin you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=Suas contas de moeda nacional @@ -954,15 +973,15 @@ account.password.removePw.button=Remover senha account.password.removePw.headline=Remover proteção com senha da carteira account.password.setPw.button=Definir senha account.password.setPw.headline=Definir proteção de senha da carteira -account.password.info=With password protection you'll need to enter your password at application startup, when withdrawing bitcoin out of your wallet, and when restoring your wallet from seed words. +account.password.info=Ao proteger a carteira com uma senha, você precisará digitá-la sempre que abrir o programa, retirar bitcoins da carteira e restaurar a carteira a partir da semente. account.seed.backup.title=Fazer backup da carteira -account.seed.info=Please write down both wallet seed words and the date! You can recover your wallet any time with seed words and the date.\nThe same seed words are used for the BTC and BSQ wallet.\n\nYou should write down the seed words on a sheet of paper. Do not save them on your computer.\n\nPlease 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! +account.seed.info=Por favor, anote em um papel as palavras da semente da carteira e a data! Com essas informações, você poderá recuperar sua carteira a qualquer momento.\nA semente exibida é usada tanto para a carteira BTC quanto para a carteira BSQ.\n\nVocê deve anotá-las em uma folha de papel. Jamais anote as palavras em um arquivo no seu computador ou em seu e-mail.\n\nNote que a semente da carteira NÃO substitui um backup.\nPara fazer isso, você precisa fazer backup da pasta do Bisq na seção \"Conta/Backup\".\nA importação da semente da carteira só é recomendada em casos de emergência. O programa não funcionará corretamente se você não recuperá-lo através de um backup! account.seed.warn.noPw.msg=Você não definiu uma senha para carteira, que protegeria a exibição das palavras da semente.\n\nGostaria de exibir as palavras da semente? account.seed.warn.noPw.yes=Sim, e não me pergunte novamente -account.seed.enterPw=Digite a senha para ver palavras semente -account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state. -account.seed.restore.ok=Ok, eu entendi e quero restaurar +account.seed.enterPw=Digite a senha para ver a semente da carteira +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Ok, quero restaurar e desligar o Bisq #################################################################### @@ -977,11 +996,11 @@ account.notifications.webCamWindow.headline=Escanear código QR do celular account.notifications.webcam.label=Usar webcam account.notifications.webcam.button=Escanear código QR account.notifications.noWebcam.button=Eu não tenho uma webcam -account.notifications.testMsg.label=Send test notification +account.notifications.testMsg.label=Enviar notificação de teste account.notifications.testMsg.title=Testar account.notifications.erase.label=Limpar notificações no celular account.notifications.erase.title=Limpar notificações -account.notifications.email.label=Pairing token +account.notifications.email.label=Token de pareamento account.notifications.email.prompt=Insira o token de pareamento que você recebeu por e-mail account.notifications.settings.title=Configurações account.notifications.useSound.label=Reproduzir som de notificação no celular @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=O preço mais baixo d # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=Carteira BSQ dao.tab.proposals=Governança dao.tab.bonding=Bonding dao.tab.proofOfBurn=Asset listing fee/Proof of burn +dao.tab.monitor=Monitor da rede +dao.tab.news=Notícias dao.paidWithBsq=pago com BSQ -dao.availableBsqBalance=Disponível -dao.availableNonBsqBalance=Available non-BSQ balance (BTC) -dao.unverifiedBsqBalance=Unverified (awaiting block confirmation) -dao.lockedForVoteBalance=Used for voting +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Saldo de todas UTXOs verificadas +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) +dao.lockedForVoteBalance=Usado para votação dao.lockedInBonds=Locked in bonds +dao.availableNonBsqBalance=Available non-BSQ balance (BTC) dao.totalBsqBalance=Saldo total de BSQ dao.tx.published.success=Sua transação foi publicada com sucesso. @@ -1053,15 +1077,20 @@ dao.proposal.menuItem.make=Fazer proposta dao.proposal.menuItem.browse=Browse open proposals dao.proposal.menuItem.vote=Vota em propostas dao.proposal.menuItem.result=Resultados dos votos -dao.cycle.headline=Voting cycle -dao.cycle.overview.headline=Voting cycle overview +dao.cycle.headline=Ciclo de votação +dao.cycle.overview.headline=Visão geral do ciclo de votação dao.cycle.currentPhase=Fase atual dao.cycle.currentBlockHeight=Altura do bloco atual dao.cycle.proposal=Fase de proposta +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=Fase de voto cego dao.cycle.voteReveal=Fase de revelação dos votos dao.cycle.voteResult=Resultado dos votos -dao.cycle.phaseDuration={0} blocks (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDuration={0} blocos (≈{1}); Bloco {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Bloco {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=Ciclos dao.results.cycles.table.header.cycle=Ciclo @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Resultado dos votos dao.results.proposals.voting.detail.header=Resultados dos votos para a proposta selecionada +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Indefinido @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=Endereço BTC do recipiente # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=Asset listing fee per day # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. trade volume +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Valor atual: {0} dao.param.blocks={0} blocos -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Duração de {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} bloco(s) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(valor padrão) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(foi modificado em votação) +dao.results.invalidVotes=Nós tivemos votos inválidos neste ciclo de votação. Isso pode ocorrer quando um voto não é bem distribuído na rede P2P.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Indefinido # suppress inspection "UnusedProperty" @@ -1203,11 +1245,11 @@ dao.bond.reputation.amount=Amount of BSQ to lockup dao.bond.reputation.time=Unlock time in blocks dao.bond.reputation.salt=Sal dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=Lockup +dao.bond.reputation.lockupButton=Travar dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds @@ -1231,10 +1273,12 @@ dao.bond.table.column.bondState=Bond state dao.bond.table.column.lockTime=Lock time dao.bond.table.column.lockupDate=Travado em -dao.bond.table.button.lockup=Lockup +dao.bond.table.button.lockup=Travar dao.bond.table.button.unlock=Destravar dao.bond.table.button.revoke=Revoke +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Not bonded yet # suppress inspection "UnusedProperty" @@ -1252,60 +1296,72 @@ dao.bond.bondState.UNLOCKED=Bond unlocked # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Bond confiscated +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Bonded reputation # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github admin +dao.bond.bondedRoleType.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin +dao.bond.bondedRoleType.GITHUB_ADMIN=Admin do GitHub # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin +dao.bond.bondedRoleType.FORUM_ADMIN=Admin do Fórum # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Rocket chat admin +dao.bond.bondedRoleType.TWITTER_ADMIN=Admin do Twitter # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Youtube admin +dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Admin do Rocket chat # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq maintainer +dao.bond.bondedRoleType.YOUTUBE_ADMIN=Admin do YouTube # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Website operator +dao.bond.bondedRoleType.BISQ_MAINTAINER=Mantenedor Bisq # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Forum operator +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.WEBSITE_OPERATOR=Operador do Website +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.FORUM_OPERATOR=Operador do Fórum # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Price node operator +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Btc node operator +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ explorer operator # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domain name holder # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS admin # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Mediator +dao.bond.bondedRoleType.MEDIATOR=Mediador # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Arbitrator +dao.bond.bondedRoleType.ARBITRATOR=Árbitro +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=Asset listing fee +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=Asset listing fee -dao.burnBsq.menuItem.proofOfBurn=Proof of burn +dao.burnBsq.menuItem.proofOfBurn=Prova de queima dao.burnBsq.header=Fee for asset listing -dao.burnBsq.selectAsset=Select Asset +dao.burnBsq.selectAsset=Escolher Ativo dao.burnBsq.fee=Taxa dao.burnBsq.trialPeriod=Período de testes dao.burnBsq.payFee=Pagar taxa -dao.burnBsq.allAssets=All assets -dao.burnBsq.assets.nameAndCode=Asset name +dao.burnBsq.allAssets=Todos ativos +dao.burnBsq.assets.nameAndCode=Nome do ativo dao.burnBsq.assets.state=Estado dao.burnBsq.assets.tradeVolume=Volume de negociação -dao.burnBsq.assets.lookBackPeriod=Verification period -dao.burnBsq.assets.trialFee=Fee for trial period +dao.burnBsq.assets.lookBackPeriod=Período de verificação +dao.burnBsq.assets.trialFee=Taxa para o período de testes dao.burnBsq.assets.totalFee=Total de taxas pagas dao.burnBsq.assets.days={0} dias dao.burnBsq.assets.toFewDays=The asset fee is too low. The min. amount of days for the trial period is {0}. @@ -1313,34 +1369,34 @@ dao.burnBsq.assets.toFewDays=The asset fee is too low. The min. amount of days f # suppress inspection "UnusedProperty" dao.assetState.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=In trial period +dao.assetState.IN_TRIAL_PERIOD=Em período de testes # suppress inspection "UnusedProperty" dao.assetState.ACTIVELY_TRADED=Actively traded # suppress inspection "UnusedProperty" dao.assetState.DE_LISTED=De-listed due to inactivity # suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Removed by voting +dao.assetState.REMOVED_BY_VOTING=Removido por votação -dao.proofOfBurn.header=Proof of burn +dao.proofOfBurn.header=Prova de queima dao.proofOfBurn.amount=Quantia dao.proofOfBurn.preImage=Pre-image dao.proofOfBurn.burn=Queimar -dao.proofOfBurn.allTxs=All proof of burn transactions -dao.proofOfBurn.myItems=My proof of burn transactions +dao.proofOfBurn.allTxs=Todas provas de transações queimadas +dao.proofOfBurn.myItems=Minhas provas de transações queimadas dao.proofOfBurn.date=Data dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=Transações dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Sign a message with key from proof or burn transaction -dao.proofOfBurn.verify.window.title=Verify a message with key from proof or burn transaction -dao.proofOfBurn.copySig=Copy signature to clipboard -dao.proofOfBurn.sign=Sign +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction +dao.proofOfBurn.copySig=Copiar assinatura para a área de trabalho +dao.proofOfBurn.sign=Assinar dao.proofOfBurn.message=Mensagem dao.proofOfBurn.sig=Assinatura dao.proofOfBurn.verify=Verificar -dao.proofOfBurn.verify.header=Verify message with key from proof or burn transaction +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=Verificação realizada com sucesso -dao.proofOfBurn.verificationResult.failed=Verification failed +dao.proofOfBurn.verificationResult.failed=Erro na verificação # suppress inspection "UnusedProperty" dao.phase.UNDEFINED=Indefinido @@ -1368,10 +1424,12 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Revelar voto # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Resultado dos votos +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Pedido de compensação # suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Reimbursement request +dao.proposal.type.REIMBURSEMENT_REQUEST=Solicitar reembolso # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" @@ -1383,10 +1441,12 @@ dao.proposal.type.GENERIC=Proposta genérica # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Proposal for confiscating a bond +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Pedido de compensação # suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Reimbursement request +dao.proposal.type.short.REIMBURSEMENT_REQUEST=Solicitar reembolso # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=Proposta genérica # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Confiscating a bond - dao.proposal.details=Detalhes da proposta dao.proposal.selectedProposal=Propostas selecionadas dao.proposal.active.header=Proposals of current cycle dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal?\nThe already paid proposal fee will be lost. -dao.proposal.active.remove.doRemove=Yes, remove my proposal +dao.proposal.active.remove.doRemove=Sim, remover minha proposta dao.proposal.active.remove.failed=Não foi possível remover a proposta. +dao.proposal.myVote.title=Votaçã dao.proposal.myVote.accept=Aceitar proposta dao.proposal.myVote.reject=Rejeitar proposta dao.proposal.myVote.removeMyVote=Ignorar proposta dao.proposal.myVote.merit=Vote weight from earned BSQ dao.proposal.myVote.stake=Vote weight from stake -dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID dao.proposal.myVote.revealTxId=Vote reveal transaction ID -dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0} -dao.proposal.votes.header=Votar em todas as propostas -dao.proposal.votes.header.voted=Meu voto -dao.proposal.myVote.button=Votar em todas as propostas +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Escolha o tipo de proposta +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=Tipo de proposta -dao.proposal.create.createNew=Fazer nova proposta -dao.proposal.create.create.button=Fazer proposta +dao.proposal.create.new=Fazer nova proposta +dao.proposal.create.button=Fazer proposta +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=proposta dao.proposal.display.type=Tipo de proposta dao.proposal.display.name=Name/nickname dao.proposal.display.link=Link to detail info -dao.proposal.display.link.prompt=Link to Github issue +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=Requested amount in BSQ dao.proposal.display.bsqAddress=BSQ address dao.proposal.display.txId=Proposal transaction ID @@ -1434,84 +1496,72 @@ dao.proposal.display.voteResult=Vote result summary dao.proposal.display.bondedRoleComboBox.label=Bonded role type dao.proposal.display.requiredBondForRole.label=Required bond for role dao.proposal.display.tickerSymbol.label=Ticker Symbol -dao.proposal.display.option=Option +dao.proposal.display.option=Opção dao.proposal.table.header.proposalType=Tipo de proposta dao.proposal.table.header.link=Link dao.proposal.table.header.myVote=Meu voto dao.proposal.table.header.remove=Remover -dao.proposal.table.icon.tooltip.removeProposal=Remove my proposal -dao.proposal.table.icon.tooltip.changeVote=Current vote: ''{0}''. Change vote to: ''{1}'' +dao.proposal.table.icon.tooltip.removeProposal=Remover minha proposta +dao.proposal.table.icon.tooltip.changeVote=Voto atual: ''{0}''. Mudar voto para: ''{1}'' dao.proposal.display.myVote.accepted=Aceito dao.proposal.display.myVote.rejected=Rejeitado dao.proposal.display.myVote.ignored=Ignorado dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}); +dao.proposal.myVote.invalid=O voto era inválido dao.proposal.voteResult.success=Aceito dao.proposal.voteResult.failed=Rejeitado dao.proposal.voteResult.summary=Result: {0}; Threshold: {1} (required > {2}); Quorum: {3} (required > {4}) -dao.proposal.display.paramComboBox.label=Select parameter to change +dao.proposal.display.paramComboBox.label=Escolha parâmetro para modificar dao.proposal.display.paramValue=Parameter value dao.proposal.display.confiscateBondComboBox.label=Choose bond -dao.proposal.display.assetComboBox.label=Asset to remove +dao.proposal.display.assetComboBox.label=Ativo para remover dao.blindVote=voto cego dao.blindVote.startPublishing=Publishing blind vote transaction... -dao.blindVote.success=Your blind vote has been successfully published. +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=Enviar dao.wallet.menuItem.receive=Receber dao.wallet.menuItem.transactions=Transações -dao.wallet.dashboard.myBalance=My wallet balance -dao.wallet.dashboard.distribution=Distribution of all BSQ -dao.wallet.dashboard.locked=Global state of locked BSQ -dao.wallet.dashboard.market=Market data -dao.wallet.dashboard.genesis=Transação gênesis -dao.wallet.dashboard.txDetails=BSQ transactions statistics -dao.wallet.dashboard.genesisBlockHeight=Genesis block height -dao.wallet.dashboard.genesisTxId=Genesis transaction ID -dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction -dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests -dao.wallet.dashboard.reimbursementAmount=BSQ issued for reimbursement requests -dao.wallet.dashboard.availableAmount=Total de BSQ disponível -dao.wallet.dashboard.burntAmount=Burned BSQ (fees) -dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds -dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds -dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds -dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds -dao.wallet.dashboard.allTx=No. of all BSQ transactions -dao.wallet.dashboard.utxo=No. of all unspent transaction outputs -dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions -dao.wallet.dashboard.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions -dao.wallet.dashboard.burntTx=No. of all fee payments transactions -dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq) -dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price) - -dao.wallet.receive.fundYourWallet=Financiar sua carteira BSQ -dao.wallet.receive.bsqAddress=BSQ wallet address +dao.wallet.dashboard.myBalance=Meu saldo de carteira + +dao.wallet.receive.fundYourWallet=Seu endereço de recebimento BSQ +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=DAO da Bisq +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Ler mais sobre o DAO da Bisq +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=Se você já contribuiu para a Bisq, use o endereço BSQ abaixo e faça uma solicitação para fazer parte da distribuição gênese do BSQ. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=Enviar fundos dao.wallet.send.sendBtcFunds=Send non-BSQ funds (BTC) -dao.wallet.send.amount=Amount in BSQ +dao.wallet.send.amount=Quantia em BSQ dao.wallet.send.btcAmount=Amount in BTC (non-BSQ funds) dao.wallet.send.setAmount=Definir quantia a retirar (quantia mínima é {0}) dao.wallet.send.setBtcAmount=Set amount in BTC to withdraw (min. amount is {0}) dao.wallet.send.receiverAddress=Receiver's BSQ address -dao.wallet.send.receiverBtcAddress=Receiver's BTC address +dao.wallet.send.receiverBtcAddress=Endereço BTC do recipiente dao.wallet.send.setDestinationAddress=Preencha seu endereço de destino dao.wallet.send.send=Enviar fundos BSQ dao.wallet.send.sendBtc=Send BTC funds dao.wallet.send.sendFunds.headline=Confirmar solicitação de retirada. dao.wallet.send.sendFunds.details=Enviando: {0}\nPara o endereço: {1}\nTaxa de transação: {2} ({3} satoshis/byte)\nTamanho da transação: {4} Kb\n\nO destinatário receberá: {5}\n\nTem certeza de que deseja retirar essa quantia? dao.wallet.chainHeightSynced=Latest verified block: {0} -dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1} +dao.wallet.chainHeightSyncing=Aguardando blocos... Verificados {0} blocos de {1} dao.wallet.tx.type=Tipo +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Não reconhecida # suppress inspection "UnusedProperty" @@ -1545,16 +1595,113 @@ dao.tx.type.enum.UNLOCK=Unlock bond # suppress inspection "UnusedProperty" dao.tx.type.enum.ASSET_LISTING_FEE=Asset listing fee # suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Proof of burn +dao.tx.type.enum.PROOF_OF_BURN=Prova de queima dao.tx.issuanceFromCompReq=Compensation request/issuance dao.tx.issuanceFromCompReq.tooltip=Compensation request which led to an issuance of new BSQ.\nIssuance date: {0} dao.tx.issuanceFromReimbursement=Reimbursement request/issuance dao.tx.issuanceFromReimbursement.tooltip=Reimbursement request which led to an issuance of new BSQ.\nIssuance date: {0} -dao.proposal.create.missingBsqFunds=Você não tem saldo suficiente para criar a proposta.\nFaltam: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=Confirmar transação {0} dao.feeTx.confirm.details=Taxa de {0}: {1}\nTaxa de mineração: {2} ({3} satoshis/byte)\nTamanho da transação: {4} Kb\n\nTem certeza de que deseja publicar a transação {5}? +dao.news.bisqDAO.title=DAO DA BISQ +dao.news.bisqDAO.description=O modelo de governança da Bisq é assim como a exchange - descentralizado e resistente à censura. As ferramentas que tornam isso realidade são a DAO da BISQ e o token BSQ. +dao.news.bisqDAO.readMoreLink=Ler mais sobre o DAO da Bisq + +dao.news.pastContribution.title=JÁ FEZ CONTRIBUIÇÕES? SOLICITE BSQ +dao.news.pastContribution.description=Se você já contribuiu para a Bisq, use o endereço BSQ abaixo e faça uma solicitação para fazer parte da distribuição gênese do BSQ. +dao.news.pastContribution.yourAddress=Seu Endereço de Carteira BSQ +dao.news.pastContribution.requestNow=Solicitar agora + +dao.news.DAOOnTestnet.title=RODE O DAO DA BISQ EM NOSSA TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Mude para o Modo Testnet da DAO +dao.news.DAOOnTestnet.firstSection.content=Mude para o Testnet da DAO na seção Configurações. +dao.news.DAOOnTestnet.secondSection.title=2. Adquira alguns BSQ +dao.news.DAOOnTestnet.secondSection.content=Solicite BSQ no Slack ou Compre BSQ na Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participe de um Ciclo de Votação +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Use um Explorador de Blocos de BSQ +dao.news.DAOOnTestnet.fourthSection.content=Como o BSQ é bitcoin, você pode ver as transações BSQ em nosso explorador de blocos de bitcoin. +dao.news.DAOOnTestnet.readMoreLink=Leia a documentação completa + +dao.monitor.daoState=Estado da DAO +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Pares +dao.monitor.table.conflicts=Conflitos +dao.monitor.state=Status +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=Estado da DAO +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Altura do bloco +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Hash prévio +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Hash prévio +dao.monitor.proposal.table.numProposals=Nº de propostas + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Hash prévio +dao.monitor.blindVote.table.numBlindVotes=Nº de votos cegos + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Dados de mercado +dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq) +dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price) +dao.factsAndFigures.dashboard.availableAmount=Total de BSQ disponível + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests +dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Global state of locked BSQ +dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds +dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds +dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds +dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds +dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees) + +dao.factsAndFigures.transactions.genesis=Transação gênesis +dao.factsAndFigures.transactions.genesisBlockHeight=Altura do bloco gênese +dao.factsAndFigures.transactions.genesisTxId=ID da transação gênese +dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics +dao.factsAndFigures.transactions.allTx=Nº de todas as transações BSQ +dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs +dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions +dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions +dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions #################################################################### # Windows @@ -1589,7 +1736,7 @@ displayUpdateDownloadWindow.download.openDir=Abrir pasta de download disputeSummaryWindow.title=Resumo disputeSummaryWindow.openDate=Data da abertura do ticket disputeSummaryWindow.role=Trader's role -disputeSummaryWindow.evidence=Evidence +disputeSummaryWindow.evidence=Evidência disputeSummaryWindow.evidence.tamperProof=Evidência à prova de adulteração disputeSummaryWindow.evidence.id=Verificação de ID disputeSummaryWindow.evidence.video=Vídeo/Screencast @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Filtered seed nodes (comma sep. onion addresses) filterWindow.priceRelayNode=Filtered price relay nodes (comma sep. onion addresses) filterWindow.btcNode=Filtered Bitcoin nodes (comma sep. addresses + port) filterWindow.preventPublicBtcNetwork=Prevent usage of public Bitcoin network +filterWindow.disableDao=Desativar DAO filterWindow.add=Adicionar filtro filterWindow.remove=Remover filtro @@ -1660,7 +1808,7 @@ offerDetailsWindow.creationDate=Criada em offerDetailsWindow.makersOnion=Endereço onion do ofertante qRCodeWindow.headline=Código QR -qRCodeWindow.msg=Por favor, utilize esse código QR para realizar depósitos em sua carteira bisq a partir de uma carteira externa. +qRCodeWindow.msg=Utilize esse código QR para realizar depósitos em sua carteira Bisq a partir de uma carteira externa. qRCodeWindow.request=Solicitação de pagamento:\n{0} selectDepositTxWindow.headline=Selecionar transação de depósito para disputa @@ -1704,7 +1852,7 @@ tradeDetailsWindow.tradeState=Estado da negociação walletPasswordWindow.headline=Digite senha para abrir: torNetworkSettingWindow.header=Configurações de rede do Tor -torNetworkSettingWindow.noBridges=Não usar bridges +torNetworkSettingWindow.noBridges=Não usar pontes torNetworkSettingWindow.providedBridges=Conectar com as pontes fornecidas torNetworkSettingWindow.customBridges=Adicionar pontes personalizadas torNetworkSettingWindow.transportType=Tipo de transporte @@ -1712,13 +1860,13 @@ torNetworkSettingWindow.obfs3=obfs3 torNetworkSettingWindow.obfs4=obfs4 (recomendado) torNetworkSettingWindow.meekAmazon=meek-amazon torNetworkSettingWindow.meekAzure=meek-azure -torNetworkSettingWindow.enterBridge=Enter one or more bridge relays (one per line) -torNetworkSettingWindow.enterBridgePrompt=type address:port +torNetworkSettingWindow.enterBridge=Insira um ou mais relés de pontes (um por linha) +torNetworkSettingWindow.enterBridgePrompt=digite endereço:porta torNetworkSettingWindow.restartInfo=Você precisa reiniciar o programa para aplicar as modificações torNetworkSettingWindow.openTorWebPage=Abrir site do projeto Tor torNetworkSettingWindow.deleteFiles.header=Problemas de conexão? torNetworkSettingWindow.deleteFiles.info=Caso você está tendo problemas de conexão durante a inicialização, tente deletar os arquivos desatualizados do Tor. Para fazer isso, clique no botão abaixo e reinicialize o programa em seguida. -torNetworkSettingWindow.deleteFiles.button=Apagar arquivos do Tor desatualizados e desligar +torNetworkSettingWindow.deleteFiles.button=Apagar arquivos desatualizados do Tor e desligar torNetworkSettingWindow.deleteFiles.progress=Desligando Tor... torNetworkSettingWindow.deleteFiles.success=Os arquivos desatualizados do Tor foram deletados com sucesso. Por favor, reinicie o aplicativo. torNetworkSettingWindow.bridges.header=O Tor está bloqueado? @@ -1748,7 +1896,7 @@ popup.headline.error=Erro popup.doNotShowAgain=Não mostrar novamente popup.reportError.log=Abrir arquivo de log popup.reportError.gitHub=Relatar à lista de problemas do GitHub -popup.reportError={0}\n\nTo help us to improve the software please report this bug by opening a new issue at https://github.com/bisq-network/bisq/issues.\nThe above error message will be copied to the clipboard when you click either of the buttons below.\nIt will make debugging easier if you include the bisq.log file by pressing "Open log file", saving a copy, and attaching it to your bug report. +popup.reportError={0}\n\nPara nos ajudar a melhorar o programa, reporte o problema criando um relato (Issue) em nossa página do GitHub em https://github.com/bisq-network/bisq/issues.\n\nA mensagem de erro exibida acima será copiada para a área de transferência quando você clicar qualquer um dos botões abaixo.\nO resolvimento de problemas será mais fácil se você anexar o arquivo bisq.log ao clicar em "Abrir arquivo de log", salvando uma cópia e incluindo-a em seu relato do problema (Issue) no GitHub. popup.error.tryRestart=Por favor, reinicie o aplicativo e verifique sua conexão de Internet para ver se o problema foi resolvido. popup.error.takeOfferRequestFailed=Um erro ocorreu quando alguém tentou aceitar uma de suas ofertas:\n{0} @@ -1757,7 +1905,7 @@ error.spvFileCorrupted=Um erro ocorreu ao ler o arquivo SPV chain.\nPode ser que error.deleteAddressEntryListFailed=Não foi possível apagar o arquivo AddressEntryList.\nErro: {0} popup.warning.walletNotInitialized=A carteira ainda não foi inicializada -popup.warning.wrongVersion=You probably have the wrong Bisq version for this computer.\nYour computer''s architecture is: {0}.\nThe Bisq binary you installed is: {1}.\nPlease shut down and re-install the correct version ({2}). +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 reinstale a versão correta ({2}). popup.warning.incompatibleDB=We detected incompatible data base files!\n\nThose database file(s) are not compatible with our current code base:\n{0}\n\nWe made a backup of the corrupted file(s) and applied the default values to a new database version.\n\nThe backup is located at:\n{1}/db/backup_of_corrupted_data.\n\nPlease check if you have the latest version of Bisq installed.\nYou can download it at:\nhttps://bisq.network/downloads\n\nPlease restart the application. popup.warning.startupFailed.twoInstances=O Bisq já está sendo executado. Você não pode executar duas instâncias do Bisq. popup.warning.cryptoTestFailed=Seems that you use a self compiled binary and have not following the build instructions in https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nIf that is not the case and you use the official Bisq binary, please file a bug report to the GitHub page.\nError={0} @@ -1777,7 +1925,7 @@ popup.warning.sendMsgFailed=O envio da mensagem para seu parceiro de negociaçã popup.warning.insufficientBtcFundsForBsqTx=Você não possui fundos BTC suficientes para pagar a taxa de mineração para essa transação.\nPor favor, deposite BTC em sua carteira.\nFundos faltando: {0} popup.warning.insufficientBsqFundsForBtcFeePayment=You don''t have sufficient BSQ funds for paying the trade fee in BSQ. You can pay the fee in BTC or you need to fund your BSQ wallet. You can buy BSQ in Bisq.\n\nMissing BSQ funds: {0} -popup.warning.noBsqFundsForBtcFeePayment=Your BSQ wallet does not have sufficient funds for paying the trade fee in BSQ. +popup.warning.noBsqFundsForBtcFeePayment=Sua carteira BSQ não possui fundos suficientes para pagar a taxa de transação em BSQ. popup.warning.messageTooLong=Sua mensagem excede o tamanho máximo permitido. Favor enviá-la em várias partes ou subir utilizando um serviço como 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 pending trades screen and clicking \"alt + o\" or \"option + o\"." @@ -1785,11 +1933,11 @@ popup.warning.nodeBanned=Um dos {0} nodos foi banido. Por favor, reinicie o prog popup.warning.priceRelay=price relay popup.warning.seed=semente -popup.info.securityDepositInfo=To ensure both traders follow the trade protocol, both traders need to pay a security deposit.\n\nThis deposit is kept in your trade wallet until your trade has been successfully completed, and then it's refunded to you.\n\nPlease note: if you're creating a new offer, Bisq needs to be running for another trader to take it. To keep your offers online, keep Bisq running and make sure this computer remains online too (i.e., make sure it doesn't switch to standby mode...monitor standby is fine). +popup.info.securityDepositInfo=Para garantir que ambas as partes sigam o protocolo de negociação, tanto o vendedor quanto o comprador precisam realizar um depósito de segurança.\n\nEste depósito permanecerá em sua carteira local até que a negociação seja concluída com sucesso. A seguir, ele será devolvido para você.\n\nAtenção: se você está criando uma nova oferta, é necessário que você mantenha o programa aberto, para que outro usuário possa aceitar a sua oferta. Para manter suas ofertas online, mantenha o Bisq sempre aberto e certifique-se de que as funções de economia de energia do seu computador estão desativadas (desative a função standby do seu computador). -popup.info.cashDepositInfo=Please be sure that you have a bank branch in your area to be able to make the cash deposit.\nThe bank ID (BIC/SWIFT) of the seller''s bank is: {0}. +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 eu posso fazer o depósito -popup.info.shutDownWithOpenOffers=Bisq is being shut down, but there are open offers. \n\nThese offers won't be available on the P2P network while Bisq is shut down, but they will be re-published to the P2P network the next time you start Bisq.\n\nTo keep your offers online, keep Bisq running and make sure this computer remains online too (i.e., make sure it doesn't go into standby mode...monitor standby is not a problem). +popup.info.shutDownWithOpenOffers=O Bisq está sendo desligado, mas existem ofertas abertas.\n\nEstas ofertas não ficaram disponíveis na rede P2P enquanto o Bisq estiver desligado, mas elas serão republicadas na rede na próxima vez que você iniciar o programa.\n\nPara manter suas ofertas online, mantenha o Bisq aberto e certifique-se de que o seu computador permanece online (ex: certifique-se de que o computador não está entrando em modo de hibernação).\n popup.privateNotification.headline=Notificação privada importante! @@ -1804,8 +1952,8 @@ popup.shutDownInProgress.msg=O desligamento do programa pode levar alguns segund popup.attention.forTradeWithId=Atenção para a negociação com ID {0} -popup.roundedFiatValues.headline=Nova funcionalidade de privacidade: valores arredondados em moeda fiduciária -popup.roundedFiatValues.msg=Para aumentar a privacidade da sua transação, a quantia {0} foi arredondada.\n\nDependendo da versão do cliente, você irá pagar ou receber valores com decimais ou valores arredondados.\n\nAmbos os valores se enquadram no protocolo de negociação.\n\nPerceba também que os valores em BTC são modificados automaticamente para corresponder o mais próximo possível à quantia em fiat arredondada. +popup.roundedFiatValues.headline=Valores arredondados em moeda fiduciária +popup.roundedFiatValues.msg=Para aumentar a privacidade da sua transação, a quantia em {0} foi arredondada.\n\nDependendo da versão do cliente, você poderá fazer transações com valores arredondados ou não.\n\nTanto os valores arredondados quanto os não arredondados são aceitos como válidos no protocolo de negociação.\n\nOs valores em BTC serão modificados automaticamente para corresponderem o mais próximo possível à quantia arredondada em moeda fiduciária. popup.info.multiplePaymentAccounts.headline=Multiple payment accounts available popup.info.multiplePaymentAccounts.msg=You have multiple payment accounts available for this offer. Please make sure you've picked the right one. @@ -1846,7 +1994,7 @@ systemTray.tooltip=Bisq: A exchange descentralizada # GUI Util #################################################################### -guiUtil.miningFeeInfo=Please be sure that the mining fee used at your external wallet is at least {0} satoshis/byte. Otherwise the trade transactions cannot be confirmed and a trade would end up in a dispute. +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 em uma disputa. guiUtil.accountExport.savedToPath=Contas de negociação salvas na pasta:\n{0} guiUtil.accountExport.noAccountSetup=Você não tem contas de negociação prontas para exportar. @@ -1860,8 +2008,8 @@ guiUtil.accountExport.selectExportPath=Selecionar pasta para exportar guiUtil.accountImport.imported=Conta de negociação importada da pasta:\n{0}\n\nContas importadas:\n{1} guiUtil.accountImport.noAccountsFound=Nenhuma conta de negociação exportada foi encontrada no caminho: {0}.\nNome do arquivo é {1}." guiUtil.openWebBrowser.warning=Você abrirá uma página da web em seu navegador padrão.\nDeseja abrir a página agora?\n\nSe você não estiver usando o \"Tor Browser\" como seu navegador padrão você conectará à página na rede aberta (clear net).\n\nURL: \"{0}\" -guiUtil.openWebBrowser.doOpen=Abrir a página da web e não perguntar novamente -guiUtil.openWebBrowser.copyUrl=Copiar URL e cancelar +guiUtil.openWebBrowser.doOpen=Abrir a página e não perguntar novamente +guiUtil.openWebBrowser.copyUrl=Copiar URL e fechar guiUtil.ofTradeAmount=da quantia da negociação @@ -1955,6 +2103,10 @@ BTC_MAINNET=Mainnet do Bitcoin BTC_TESTNET=Testnet do Bitcoin # suppress inspection "UnusedProperty" BTC_REGTEST=Regtest do Bitcoin +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=Ano time.month=Mês @@ -1975,14 +2127,14 @@ time.seconds=segundos password.enterPassword=Insira a senha password.confirmPassword=Confirme a senha password.tooLong=A senha deve ter menos de 500 caracteres. -password.deriveKey=Derivar chave a partir da senha +password.deriveKey=Derivando chave a partir da senha password.walletDecrypted=A carteira foi decriptada com sucesso e a proteção por senha removida password.wrongPw=Você digitou a senha incorreta.\n\nFavor tentar novamente, verificando com cuidado erros de digitação ou ortografia. -password.walletEncrypted=Carteira encriptada com sucesso e proteção por senha ativada. +password.walletEncrypted=A carteira foi criptografada e a proteção por senha foi ativada com sucesso. password.walletEncryptionFailed=Wallet password could not be set. You might have imported seed words which are not matching the wallet database. Please contact the developers on the Bisq Forum. password.passwordsDoNotMatch=As 2 senhas inseridas não são iguais. password.forgotPassword=Esqueceu a senha? -password.backupReminder=Please note that when setting a wallet password all automatically created backups from the unencrypted wallet will be deleted.\n\nIt is highly recommended to make a backup of the application directory and write down your seed words before setting a password! +password.backupReminder=Ao definir uma senha para a carteira, todos os backups criados automaticamente a partir da carteira não criptografada serão apagados.\n\nAntes de definir uma senha, é altamente recomendável que você faça um backup da pasta do Bisq e anote a semente da carteira em um papel. password.backupWasDone=Eu já fiz um backup seed.seedWords=Semente da carteira @@ -2025,6 +2177,8 @@ payment.country=País payment.extras=Requerimentos adicionais payment.email.mobile=E-mail ou celular payment.altcoin.address=Endereço altcoin +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=Altcoin payment.select.altcoin=Selecionar ou buscar altcoin payment.secret=Pergunta secreta @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag: payment.moneyBeam.accountId=E-mail ou nº de telefone payment.venmo.venmoUserName=Nome do usuário do Venmo payment.popmoney.accountId=E-mail ou nº de telefone -payment.revolut.email=E-mail ou nº de telefone +payment.revolut.email=E-mail +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=Citizen ID/Tax ID or phone no. payment.supportedCurrencies=Moedas suportadas payment.limitations=Limites @@ -2082,6 +2237,10 @@ payment.limits.info=Por favor, esteja ciente de que todas as transferências ban payment.cashDeposit.info=Certifique-se de que o seu banco permite a realização de depósitos em espécie na conta de terceiros. +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=Informações para contato payment.f2f.contact.prompt=Como você gostaria de ser contatado pelo seu parceiro de negociação? (e-mail, nº de telefone, ...) payment.f2f.city=Cidade para se encontrar pessoalmente @@ -2128,16 +2287,10 @@ F2F_SHORT=F2F # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altcoins PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,22 +2370,33 @@ BLOCK_CHAINS_SHORT=Altcoins PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation #################################################################### -validation.empty=Campo vazio não é permitido +validation.empty=Obrigatório validation.NaN=Número inválido -validation.notAnInteger=A entrada não é um valor inteiro. +validation.notAnInteger=A quantia não é um valor inteiro. validation.zero=Número 0 não é permitido validation.negative=Valores negativos não são permitidos. -validation.fiat.toSmall=Entrada menor do que a quantia mínima permitida. -validation.fiat.toLarge=Entrada maior do que a quantia máxima permitida. -validation.btc.fraction=A entrada resulta em um valor de bitcoin menor do que a unidade mínima (satoshi). -validation.btc.toLarge=Entrada maior que {0} não é permitida. -validation.btc.toSmall=Entrada menor que {0} não é permitida. -validation.securityDeposit.toSmall=Entrada menor que {0} não é permitida. +validation.fiat.toSmall=Quantia menor do que a mínima permitida. +validation.fiat.toLarge=Quantia maior do que a máxima permitida. +validation.btc.fraction=A quantia resulta em um valor de bitcoin menor do que a unidade mínima (satoshi). +validation.btc.toLarge=Quantia máx. permitida: {0} +validation.btc.toSmall=Quantia mín. permitida: {0} +validation.securityDeposit.toSmall=Quantia mín. permitida: {0} validation.passwordTooShort=A senha inserida é muito curta. É necessário conter no mínimo 8 caracteres validation.passwordTooLong=A senha inserida é muito longa. Não pode ser maior do que 50 caracteres validation.sortCodeNumber={0} deve consistir de {1} números. @@ -2244,9 +2413,10 @@ validation.bsq.amountBelowMinAmount=A quantia mínima é {0} validation.nationalAccountId={0} deve consistir de {1 números. #new -validation.invalidInput=Entrada inválida: {0} +validation.invalidInput=Quantia inválida: {0} validation.accountNrFormat=O número da conta deve estar no formato: {0} validation.altcoin.wrongStructure=Validação do endereço falhou pois este não é compatível com a estrutura de um endereço {0}. +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=Endereços ZEC precisam iniciar com t. Endereços iniciando com z não são suportados. validation.altcoin.invalidAddress=Endereço não é um endereço {0} válido! {1} validation.bic.invalidLength=Comprimento da entrada não é 8 ou 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Deve conter somente letras, números validation.interacETransfer.invalidAnswer=Must be one word and contain only letters, numbers, and/or the symbol - validation.inputTooLarge=Não deve ser maior do que {0} validation.inputTooSmall=Deve ser maior do que {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=Não é permitido uma quantia menor do que o limite pó de {0}. validation.length=Deve ser entre {0} e {1} validation.pattern=Deve ser no formato: {0} validation.noHexString=Não está no formato hexadecimal validation.advancedCash.invalidFormat=Deve ser um e-mail válido ou uma ID de carteira no formato: X000000000000 +validation.invalidUrl=Essa URL não é válida +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_ro.properties b/core/src/main/resources/i18n/displayStrings_ro.properties index 2efa867dddd..931ce06f3f8 100644 --- a/core/src/main/resources/i18n/displayStrings_ro.properties +++ b/core/src/main/resources/i18n/displayStrings_ro.properties @@ -104,7 +104,7 @@ shared.belowInPercent=Below % from market price shared.aboveInPercent=Above % from market price shared.enterPercentageValue=Introdu % valoarea shared.OR=SAU -shared.notEnoughFunds=Nu ai suficiente fonduri în portofelul tău Bisq.\nAi nevoie de {0}, dar ai doar {1} în portofelul tău Bisq.\n\nTe rugăm să finanțezi tranzacția dintr-un portofel extern de Bitcoin sau să-ți finanțezi portofelul Bisq la \"Fonduri/Incasare fonduri\". +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.waitingForFunds=În așteptare de fonduri... shared.depositTransactionId=Codul tranzacției de depozit shared.TheBTCBuyer=Cumpărătorul de BTC @@ -113,12 +113,13 @@ shared.reasonForPayment=Motivul plății shared.sendingConfirmation=Se trimite confirmarea... shared.sendingConfirmationAgain=Te rugăm trimite confirmarea din nou shared.exportCSV=Exportare în format csv +shared.exportJSON=Export to JSON shared.noDateAvailable=Nicio dată disponibilă shared.arbitratorsFee=Comisionul arbitrului shared.noDetailsAvailable=Niciun detaliu disponibil shared.notUsedYet=Încă nefolosit shared.date=Data -shared.sendFundsDetailsWithFee=Trimitem: {0}\nDinspre adresa: {1}\nCătre adresa de primire: {2}.\nComisionul necesar tranzacției: {3} ({4} Satoshi/octet)\nMărimea tranzacției: {5} Kb\n\nDestinatarul va primi: {6}\n\nEști sigur că dorești să retragi suma specificată? +shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired transaction 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.copyToClipboard=Copiază în clipboard shared.language=Limba shared.country=Țara @@ -196,6 +197,8 @@ shared.interval=Interval shared.actions=Actions shared.buyerUpperCase=Buyer shared.sellerUpperCase=Seller +shared.new=NEW +shared.blindVoteTxId=Blind vote transaction ID #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=Locked mainView.footer.usingTor=(folosind Tor) mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo=Utilizatori ai rețelei Bitcoin: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Inițializare -mainView.footer.btcInfo.synchronizedWith=sincronizat cu -mainView.footer.btcInfo.connectingTo=conectare la +mainView.footer.btcInfo.initializing=Connecting to Bitcoin network +mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO +mainView.footer.btcInfo.synchronizedWith=Synchronized with +mainView.footer.btcInfo.connectingTo=Connecting to mainView.footer.btcInfo.connectionFailed=conectare eșuată mainView.footer.p2pInfo=Utilizatori ai rețelei P2P: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=Suma de BTC în {0} createOffer.amountPriceBox.buy.volumeDescription=Suma în {0} de cheltuit createOffer.amountPriceBox.sell.volumeDescription=Suma în {0} de încasat createOffer.amountPriceBox.minAmountDescription=Suma minimă de BTC -createOffer.securityDeposit.prompt=Depozitul de securitate în BTC +createOffer.securityDeposit.prompt=Depozit de securitate createOffer.fundsBox.title=Finanțează-ți oferta createOffer.fundsBox.offerFee=Comisionul tranzacției createOffer.fundsBox.networkFee=Mining fee createOffer.fundsBox.placeOfferSpinnerInfo=Publicare ofertei în desfășurare ... createOffer.fundsBox.paymentLabel=Tranzacția Bisq cu codul {0} createOffer.fundsBox.fundsStructure=({0} cauțiune, {1} comision tranzacție, {2} comision minier) +createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=Oferta ta a fost publicată createOffer.success.info=Îți poți gestiona ofertele deschise la \"Portofoliu/Tranzacții deschise\". createOffer.info.sellAtMarketPrice=You will always sell at market price as the price of your offer will be continuously updated. @@ -387,6 +392,7 @@ createOffer.alreadyFunded=Ai finanțat deja această ofertă.\nFondurile tale au createOffer.createOfferFundWalletInfo.headline=Finanțează-ți oferta # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Valoarea tranzacției: {0} \n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} and {1} createOffer.createOfferFundWalletInfo.msg=Trebuie să depui {0} către această ofertă.\n\nAceste fonduri sunt rezervate în portofelul tău local și vor fi blocate în adresa de depozit multisig odată ce cineva îți va accepta oferta.\n\nSuma este în valoare de:\n{1}- Depozitul de securitate: {2}\n- Comision de tranzacționare: {3}\n- Comision minier: {4}\n\nPoți alege între două variante pentru finanțarea tranzacției:\n- Folosești portofelului tău Bisq (convenient, însă tranzacțiile vor putea fi oricând urmărite) SAU\n- Transfer dintr-un portofel extern (potențial mai confidențial)\n\nVei vedea toate opțiunile și detaliile de finanțare după închiderea acestui pop-up. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=Prețul pe care l-ai introdus este în afara createOffer.changePrice=Schimbă preț createOffer.tac=Prin publicarea acestei oferte, sunt de acord să tranzacționez cu orice comerciant care îndeplinește condițiile definite în acest ecran. createOffer.currencyForFee=Comisionul tranzacției -createOffer.setDeposit=Setarea depozitului de securitate al cumpărătorului +createOffer.setDeposit=Set buyer's security deposit (%) +createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) +createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} #################################################################### @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=We'd love to hear back from you about your experie tradeFeedbackWindow.msg.part2=If you have any questions, or experienced any problems, please get in touch with other users and contributors via the Bisq forum at: tradeFeedbackWindow.msg.part3=Thanks for using Bisq! + +daoTestingFeedbackWindow.title=Thank you for testing the Bisq DAO +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Visit forum +daoTestingFeedbackWindow.msg.part3=Thanks for using Bisq! + portfolio.pending.role=Rolul meu portfolio.pending.tradeInformation=Informații tranzacții portfolio.pending.remainingTime=Timp rămas @@ -769,11 +786,11 @@ support.buyerTaker=Acceptant/Cumpărător BTC support.sellerTaker=Acceptant/Vânzător BTC support.backgroundInfo=Bisq is not a company, so it handles disputes differently.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display an \"Open dispute\" button after the trade period is over for contacting the arbitrator.\n\nIf there is an issue with the application, the software will try to detect it and, if possible, display an \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIf you are having an issue and did not see the \"Open support ticket\" button, you can open a support ticket manually by selecting the trade causing issues under \"Portfolio/Open trades\" and hitting \"alt + o\" or \"option + o\". Please use this method only if you are sure that the software is not working as expected. If you have problems or questions, please review the FAQ on the bisq.network web page or post in the Bisq forum in the Support section. -support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrator's requests within 2 days.\n2. The maximum period for a dispute is 14 days.\n3. You need to cooperate with the arbitrator and provide the information they request to make your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=Mesaj de sistem: {0} -support.youOpenedTicket=Ați deschis o solicitare de asistență. -support.youOpenedDispute=Ai deschis o cerere pentru dispută.\n\n{0} -support.peerOpenedTicket=Partenerul tău de tranzacționare a solicitat asistență din cauza unor probleme tehnice. Așteaptă te rog instrucțiuni suplimentare. +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=Partenerul tău de tranzacționare a solicitat o dispută.\n\n{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=Trebuie să ai ales cel puțin un arbitru. account.altcoin.yourAltcoinAccounts=Your altcoin accounts account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe arbitrator is not a {2} specialist and cannot help in such cases. account.altcoin.popup.wallet.confirm=Înțeleg și confirm că știu ce portofel trebuie să folosesc. +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 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 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\nFor sending XMR, you need to use either the official Monero GUI wallet or Monero 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.\nmonero-wallet-cli (use the command get_tx_key)\nmonero-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nIn addition to XMR checktx tool (https://xmr.llcoins.net/checktx.html) verification can also be accomplished in-wallet.\nmonero-wallet-cli : using command (check_tx_key).\nmonero-wallet-gui : on the Advanced > Prove/Check page.\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 XMR sender is responsible for providing verification of the XMR 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 (https://www.getmonero.org/resources/user-guides/prove-payment.html) or the Monero forum (https://forum.getmonero.org) to find more information. 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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=Atunci când folosești {0}, poți utiliza doar adresele transparente (începând cu t) nu adresele z (private), deoarece arbitrul nu va putea verifica tranzacția cu adrese z. -account.altcoin.popup.XZC.msg=Când folosești {0}, poți folosi numai adresele transparente (ce pot fi urmărite), nu adresele indetectabile, deoarece arbitrul nu va putea verifica tranzacția executată cu adrese ce nu pot fi citite de către un explorator de bloc. -account.altcoin.popup.bch=Bitcoin Cash și Bitcoin Clashic suferă de protecție împotriva reluării. Dacă folosești aceste monede, asigură-te că ai luat măsurile necesare de precauție și înțelegi toate implicațiile. Poți suferi pierderi prin trimiterea unei monezi și neintenționat să trimiți aceeași monedă pe celălalt blockchain. Deoarece acele "monede airdrop" au aceeași istorie cu blockchainul Bitcoin există și riscuri de securitate cât și un risc considerabil în pierderea confidențialității.\n\nTe invităm să citești mai multe despre acest subiect pe forumul Bisq: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Deoarece Bitcoin Gold împărtășește același istoric ca și blocukchainul Bitcoin, acesta vine atașat cu anumite riscuri de securitate și un risc considerabil crescut privind pierderea confidențialității. Dacă folosești Bitcoin Gold asigură-te că ai luat măsurile de precauție necesare și înțelegi toate implicațiile.\n\nTe invităm să citești mai multe despre acest subiect pe forumul Bisq: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.ZEC.msg=When using Zcash you can only use the transparent addresses (starting with t) not the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. +account.altcoin.popup.XZC.msg=When using Zcoin you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=Your national currency accounts @@ -961,8 +980,8 @@ account.seed.info=Please write down both wallet seed words and the date! You can account.seed.warn.noPw.msg=Nu ai creat o parolă pentru portofel care să protejeze afișarea cuvintelor-nucleu.\n\nDorești afișarea cuvintelor-nucleu? account.seed.warn.noPw.yes=Da, și nu mă mai întreba vreodată account.seed.enterPw=Introdu parola pentru a vizualiza cuvintele-nucleu -account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state. -account.seed.restore.ok=Da, înțeleg și doresc să restaurez +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Ok, do the restore and shut down Bisq #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=The lower price must # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=Portofel BSQ dao.tab.proposals=Governance dao.tab.bonding=Bonding dao.tab.proofOfBurn=Asset listing fee/Proof of burn +dao.tab.monitor=Network monitor +dao.tab.news=News dao.paidWithBsq=plătit cu BSQ -dao.availableBsqBalance=Available -dao.availableNonBsqBalance=Available non-BSQ balance (BTC) -dao.unverifiedBsqBalance=Unverified (awaiting block confirmation) +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Balance of all verified UTXOs +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) dao.lockedForVoteBalance=Used for voting dao.lockedInBonds=Locked in bonds +dao.availableNonBsqBalance=Available non-BSQ balance (BTC) dao.totalBsqBalance=Soldul total BSQ dao.tx.published.success=Tranzacția ta a fost publicată cu succes. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Voting cycle overview dao.cycle.currentPhase=Current phase dao.cycle.currentBlockHeight=Current block height dao.cycle.proposal=Proposal phase +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=Blind vote phase dao.cycle.voteReveal=Vote reveal phase dao.cycle.voteResult=Vote result dao.cycle.phaseDuration={0} blocks (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=Cycles dao.results.cycles.table.header.cycle=Cycle @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Vote result dao.results.proposals.voting.detail.header=Vote results for selected proposal +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Nedefinit @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=Recipient BTC address # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=Asset listing fee per day # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. trade volume +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Current value: {0} dao.param.blocks={0} blocks -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Duration of {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} block(s) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(default value) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(has been changed in voting) +dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the P2P network.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Nedefinit # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Lockup dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Lockup dao.bond.table.button.unlock=Deblocare dao.bond.table.button.revoke=Revoke +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Nedefinit # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Not bonded yet # suppress inspection "UnusedProperty" @@ -1252,13 +1296,17 @@ dao.bond.bondState.UNLOCKED=Bond unlocked # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Bond confiscated +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Nedefinit # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Bonded reputation # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github admin +dao.bond.bondedRoleType.UNDEFINED=Nedefinit +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin # suppress inspection "UnusedProperty" @@ -1266,24 +1314,30 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Rocket chat admin # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Youtube admin +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq maintainer # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Website operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Forum operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Price node operator +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Btc node operator +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ explorer operator # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domain name holder # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS admin @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=DNS admin dao.bond.bondedRoleType.MEDIATOR=Mediator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Arbitrator +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=Asset listing fee +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=Asset listing fee dao.burnBsq.menuItem.proofOfBurn=Proof of burn dao.burnBsq.header=Fee for asset listing @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=Data dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=Tranzacții dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Sign a message with key from proof or burn transaction -dao.proofOfBurn.verify.window.title=Verify a message with key from proof or burn transaction +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction dao.proofOfBurn.copySig=Copy signature to clipboard dao.proofOfBurn.sign=Sign dao.proofOfBurn.message=Message dao.proofOfBurn.sig=Signature dao.proofOfBurn.verify=Verify -dao.proofOfBurn.verify.header=Verify message with key from proof or burn transaction +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=Verification succeeded dao.proofOfBurn.verificationResult.failed=Verification failed @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Vote reveal # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Vote result +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Nedefinit # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Solicitare de despăgubire # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=Generic proposal # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Proposal for confiscating a bond +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Nedefinit # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Solicitare de despăgubire # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=Generic proposal # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Confiscating a bond - dao.proposal.details=Proposal details dao.proposal.selectedProposal=Solicitare de despăgubire selectată dao.proposal.active.header=Proposals of current cycle dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal?\nThe already paid proposal fee will be lost. dao.proposal.active.remove.doRemove=Yes, remove my proposal dao.proposal.active.remove.failed=Solicitarea de despăgubire nu a putut fi înlăturată. +dao.proposal.myVote.title=Vot dao.proposal.myVote.accept=Accept proposal dao.proposal.myVote.reject=Reject proposal dao.proposal.myVote.removeMyVote=Ignore proposal dao.proposal.myVote.merit=Vote weight from earned BSQ dao.proposal.myVote.stake=Vote weight from stake -dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID dao.proposal.myVote.revealTxId=Vote reveal transaction ID -dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0} -dao.proposal.votes.header=Vote on all proposals -dao.proposal.votes.header.voted=My vote -dao.proposal.myVote.button=Vote on all proposals +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Select proposal type +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=Proposal type -dao.proposal.create.createNew=Creează o nouă solicitare de despăgubire -dao.proposal.create.create.button=Creează solicitare de despăgubire +dao.proposal.create.new=Creează o nouă solicitare de despăgubire +dao.proposal.create.button=Creează solicitare de despăgubire +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=proposal dao.proposal.display.type=Proposal type dao.proposal.display.name=Name/nickname dao.proposal.display.link=Link to detail info -dao.proposal.display.link.prompt=Link to Github issue +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=Requested amount in BSQ dao.proposal.display.bsqAddress=BSQ address dao.proposal.display.txId=Proposal transaction ID @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=Accepted dao.proposal.display.myVote.rejected=Rejected dao.proposal.display.myVote.ignored=Ignored dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}); +dao.proposal.myVote.invalid=Vote was invalid dao.proposal.voteResult.success=Accepted dao.proposal.voteResult.failed=Rejected @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=Asset to remove dao.blindVote=blind vote dao.blindVote.startPublishing=Publishing blind vote transaction... -dao.blindVote.success=Your blind vote has been successfully published. +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=Trimite dao.wallet.menuItem.receive=Încasează dao.wallet.menuItem.transactions=Tranzacții dao.wallet.dashboard.myBalance=My wallet balance -dao.wallet.dashboard.distribution=Distribution of all BSQ -dao.wallet.dashboard.locked=Global state of locked BSQ -dao.wallet.dashboard.market=Market data -dao.wallet.dashboard.genesis=Tranzacție-geneză -dao.wallet.dashboard.txDetails=BSQ transactions statistics -dao.wallet.dashboard.genesisBlockHeight=Genesis block height -dao.wallet.dashboard.genesisTxId=Genesis transaction ID -dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction -dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests -dao.wallet.dashboard.reimbursementAmount=BSQ issued for reimbursement requests -dao.wallet.dashboard.availableAmount=Total available BSQ -dao.wallet.dashboard.burntAmount=Burned BSQ (fees) -dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds -dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds -dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds -dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds -dao.wallet.dashboard.allTx=No. of all BSQ transactions -dao.wallet.dashboard.utxo=No. of all unspent transaction outputs -dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions -dao.wallet.dashboard.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions -dao.wallet.dashboard.burntTx=No. of all fee payments transactions -dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq) -dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price) - -dao.wallet.receive.fundYourWallet=Finanțează-ți portofelul BSQ -dao.wallet.receive.bsqAddress=BSQ wallet address + +dao.wallet.receive.fundYourWallet=Your BSQ receive address +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=The Bisq DAO +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=Trimite fonduri dao.wallet.send.sendBtcFunds=Send non-BSQ funds (BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=Latest verified block: {0} dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1} dao.wallet.tx.type=Tip +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Nedefinit # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Nerecunoscut # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=Compensation request/issuance dao.tx.issuanceFromCompReq.tooltip=Compensation request which led to an issuance of new BSQ.\nIssuance date: {0} dao.tx.issuanceFromReimbursement=Reimbursement request/issuance dao.tx.issuanceFromReimbursement.tooltip=Reimbursement request which led to an issuance of new BSQ.\nIssuance date: {0} -dao.proposal.create.missingBsqFunds=Nu ai suficiente fonduri pentru crearea solicitării de despăgubire.\nLipsesc: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=Confirm {0} transaction dao.feeTx.confirm.details={0} fee: {1}\nMining fee: {2} ({3} Satoshis/byte)\nTransaction size: {4} Kb\n\nAre you sure you want to publish the {5} transaction? +dao.news.bisqDAO.title=THE BISQ DAO +dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. +dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO + +dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ +dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.news.pastContribution.yourAddress=Your BSQ Wallet Address +dao.news.pastContribution.requestNow=Request now + +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. +dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ +dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. +dao.news.DAOOnTestnet.readMoreLink=Read the full documentation + +dao.monitor.daoState=DAO state +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Conflicts +dao.monitor.state=Statut +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=DAO state +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Block height +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Previous hash +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Previous hash +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Previous hash +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Market data +dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq) +dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price) +dao.factsAndFigures.dashboard.availableAmount=Total available BSQ + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests +dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Global state of locked BSQ +dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds +dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds +dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds +dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds +dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees) + +dao.factsAndFigures.transactions.genesis=Tranzacție-geneză +dao.factsAndFigures.transactions.genesisBlockHeight=Genesis block height +dao.factsAndFigures.transactions.genesisTxId=Genesis transaction ID +dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics +dao.factsAndFigures.transactions.allTx=No. of all BSQ transactions +dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs +dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions +dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions +dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Filtered seed nodes (comma sep. onion addresses) filterWindow.priceRelayNode=Filtered price relay nodes (comma sep. onion addresses) filterWindow.btcNode=Filtered Bitcoin nodes (comma sep. addresses + port) filterWindow.preventPublicBtcNetwork=Prevent usage of public Bitcoin network +filterWindow.disableDao=Disable DAO filterWindow.add=Adaugă filtru filterWindow.remove=Înlătură filtru @@ -1955,6 +2103,10 @@ BTC_MAINNET=Bitcoin Mainnet BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=An time.month=Lună @@ -2025,6 +2177,8 @@ payment.country=Țara payment.extras=Extra requirements payment.email.mobile=Email or mobile no. payment.altcoin.address=Altcoin address +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=Altcoin payment.select.altcoin=Selectează sau caută altcoin payment.secret=Secret question @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=Email or phone no. payment.venmo.venmoUserName=Venmo username payment.popmoney.accountId=Email or phone no. -payment.revolut.email=Email or phone no. +payment.revolut.email=Email +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=Citizen ID/Tax ID or phone no. payment.supportedCurrencies=Supported currencies payment.limitations=Limitations @@ -2082,6 +2237,10 @@ payment.limits.info=Rețineți că toate transferurile bancare au un anumit risc payment.cashDeposit.info=Please confirm your bank allows you to send cash deposits into other peoples' accounts. For example, Bank of America and Wells Fargo no longer allow such deposits. +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=Contact info payment.f2f.contact.prompt=How you want to get contacted by the trading peer? (email address, phone number,...) payment.f2f.city=City for 'Face to face' meeting @@ -2128,16 +2287,10 @@ F2F_SHORT=F2F # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altcoinuri PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Altcoinuri PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} trebuie să fie format din {1} numere. validation.invalidInput=Intrarea nu este validă: {0} validation.accountNrFormat=Numărul de cont trebuie sa fie sub formatul: {0} validation.altcoin.wrongStructure=Validarea adresei a eșuat deoarece nu corespunde structurii adresei {0}. +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=Adresa de ZEC trebuie să înceapă cu t. Adresele ce încep cu z nu sunt acceptate. validation.altcoin.invalidAddress=Adresa nu este validă {0} address! {1} validation.bic.invalidLength=Lungimea introdusă nu e nici de 8 nici de 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Must contain only letters, numbers, validation.interacETransfer.invalidAnswer=Must be one word and contain only letters, numbers, and/or the symbol - validation.inputTooLarge=Input must not be larger than {0} validation.inputTooSmall=Input has to be larger than {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=The amount below the dust limit of {0} is not allowed. validation.length=Length must be between {0} and {1} validation.pattern=Input must be of format: {0} validation.noHexString=The input is not in HEX format. validation.advancedCash.invalidFormat=Must be a valid email or wallet id of format: X000000000000 +validation.invalidUrl=This is not a valid URL +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index a4ddbf9522f..d57126f6a7e 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -104,7 +104,7 @@ shared.belowInPercent=% ниже рыночного курса shared.aboveInPercent=% выше рыночного курса shared.enterPercentageValue=Ввести % величину shared.OR=ИЛИ -shared.notEnoughFunds=Недостаточно средств в Вашем Bisq кошельке.\nНеобходимо {0} а только {1} в Вашем Bisq кошельке. \n\nПросьба обеспечить эту сделку из внешнего Биткойн кошелька или пополнением своего кошелька Bisq в разделе \"Средства/Получить средства\". +shared.notEnoughFunds=Недостаточно средств в Вашем Bisq кошельке.\nНеобходимо {0} а только {1} имеется в Вашем Bisq кошельке. \n\nПросьба обеспечить эту сделку из внешнего Биткойн кошелька или пополнением своего кошелька Bisq в разделе \"Средства/Получить средства\". shared.waitingForFunds=Ожидание средств... shared.depositTransactionId=Идентификатор зачисления на счёт shared.TheBTCBuyer=Покупатель ВТС @@ -113,6 +113,7 @@ shared.reasonForPayment=Назначение платежа shared.sendingConfirmation=Отправка подтверждения... shared.sendingConfirmationAgain=Просьба выслать подтверждение повторно shared.exportCSV=Экспорт в csv +shared.exportJSON=Экспорт в JSON shared.noDateAvailable=Дата не указана shared.arbitratorsFee=Комиссия арбитра shared.noDetailsAvailable=Подробности не указаны @@ -196,6 +197,8 @@ shared.interval=Интервал shared.actions=Действия shared.buyerUpperCase=Покупатель shared.sellerUpperCase=Продавец +shared.new=НОВОЕ +shared.blindVoteTxId=Идент. транзакции слепого голосования #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=Заперто mainView.footer.usingTor=(используется Tor) mainView.footer.localhostBitcoinNode=(локальный узел) mainView.footer.btcInfo=Пэров сети Биткойн: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Инициализация -mainView.footer.btcInfo.synchronizedWith=синхронизировано с -mainView.footer.btcInfo.connectingTo=подключение к +mainView.footer.btcInfo.initializing=Подключение к сети Биткойн +mainView.footer.bsqInfo.synchronizing=/ Синхронизация DAO +mainView.footer.btcInfo.synchronizedWith=Синхронизировано с +mainView.footer.btcInfo.connectingTo=Подключение к mainView.footer.btcInfo.connectionFailed=соединение не удалось mainView.footer.p2pInfo=Пэров сети P2P: {0} @@ -299,8 +303,8 @@ market.trades.tooltip.candle.date=Дата: offerbook.createOffer=Создать предложение offerbook.takeOffer=Принять предложение -offerbook.takeOfferToBuy=Take offer to buy {0} -offerbook.takeOfferToSell=Take offer to sell {0} +offerbook.takeOfferToBuy=Принять предложение купить {0} +offerbook.takeOfferToSell=Принять предложение продать {0} offerbook.trader=Трейдер offerbook.offerersBankId=Идент. банка (BIC/SWIFT) создателя: {0} offerbook.offerersBankName=Название банка создателя: {0} @@ -369,6 +373,7 @@ createOffer.fundsBox.networkFee=Комиссия майнера createOffer.fundsBox.placeOfferSpinnerInfo=Публикация предложения... createOffer.fundsBox.paymentLabel=Сделка Bisq с идентификатором {0} createOffer.fundsBox.fundsStructure=({0} залоговый депозит, {1} торговый сбор, {2} комиссия майнера) +createOffer.fundsBox.fundsStructure.BSQ=({0} залоговый депозит, {1} комиссия майнера) + {2} торговый сбор createOffer.success.headline=Ваше предложение опубликовано createOffer.success.info=Вы можете управлять текущими предложениями в разделе \"Папка/Мои текущие предложения\". createOffer.info.sellAtMarketPrice=Вы всегда будете продавать по рыночной цене, а курс вашего предложения будет постоянно обновляться. @@ -387,6 +392,7 @@ createOffer.alreadyFunded=Вы уже обеспечили это предлож createOffer.createOfferFundWalletInfo.headline=Обеспечить своё предложение # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Сумма сделки: {0} \n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} и {1} createOffer.createOfferFundWalletInfo.msg=Вы должны внести {0} для обеспечения этого предложения.\n\nЭти средства зарезервированы в Вашем локальном кошельке, и будут заперты в депозитном адресе multisig, когда кто-то примет Ваше предложение.\n\nСумма состоит из:\n{1}- Вашего залогового депозита: {2}\n- Торгового сбора: {3}\n- Общей комиссии майнера: {4}\n\nВы можете выбрать один из двух вариантов финасирования сделки:\n - Использовать свой Bisq кошелёк (удобно, но связь между сделками может быть вычислена посторонними) ИЛИ\n - Перевести из внешнего кошелька (потенциально более анонимно)\n\nВы увидите все варианты обеспечения и подробности после закрытия этого окна. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=Введенный курс превышае createOffer.changePrice=Изменить курс createOffer.tac=Опубликовав предложение, я согласен торговать с любым трейдером, совместимым по условиям обозначенным на экране. createOffer.currencyForFee=Оплата сделки -createOffer.setDeposit=Установить залоговый депозит покупателя +createOffer.setDeposit=Установить залоговую сумму для покупателя (%) +createOffer.setDepositAsBuyer=Установить свой залоговый депозит в роли покупателя (%) +createOffer.securityDepositInfo=Залоговая сумма для покупателя: {0} +createOffer.securityDepositInfoAsBuyer=Ваш залоговый депозит в роли покупателя будет {0} #################################################################### @@ -424,7 +433,7 @@ takeOffer.fundsBox.title=Обеспечить свою сделку takeOffer.fundsBox.isOfferAvailable=Проверка доступности предложения... takeOffer.fundsBox.tradeAmount=Количество для продажи takeOffer.fundsBox.offerFee=Торговый сбор -takeOffer.fundsBox.networkFee=Итого комиссия майнера +takeOffer.fundsBox.networkFee=Oбщая комиссия майнера takeOffer.fundsBox.takeOfferSpinnerInfo=Принятие предложения... takeOffer.fundsBox.paymentLabel=Bisq сделка с идентификатором {0} takeOffer.fundsBox.fundsStructure=({0} залоговый депозит, {1} торговый сбор, {2} комиссия майнера) @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=Приветствуем Ваши отзывы. О tradeFeedbackWindow.msg.part2=Если у Вас возникли вопросы или проблемы, просьба связаться с другими пользователями и участниками через форум Bisq по адресу: https://bisq.community tradeFeedbackWindow.msg.part3=Благодарим Вас за пользование Bisq! + +daoTestingFeedbackWindow.title=Спасибо, что принимаете участие в тестировании ДАО Bisq +daoTestingFeedbackWindow.msg.part1=Не могли бы вы пройти небольшой опрос? Это займет всего 3 минуты, и вы получите 20 BSQ.\nВаш отзыв очень важен для благополучного запуска основной сети. +daoTestingFeedbackWindow.surveyLinkLabel=Пройти опрос +daoTestingFeedbackWindow.msg.part2=Возникли вопросы или сложности? Обсудите их с пользователями и членами сообщества Bisq на форуме: +daoTestingFeedbackWindow.forumLinkLabel=Посетить форум +daoTestingFeedbackWindow.msg.part3=Благодарим Вас за пользование Bisq! + portfolio.pending.role=Моя роль portfolio.pending.tradeInformation=Информация о сделке portfolio.pending.remainingTime=Оставшееся время @@ -769,11 +786,11 @@ support.buyerTaker=Покупатель ВТС/Получатель support.sellerTaker=Продавец BTC/Получатель support.backgroundInfo=Bisq не является компанией, и поэтому справляется со спорами по другому.\n\n Если в процессе торговли возникают споры (напр. трейдер не соблюдает торговый протокол) приложение покажет кнопку \"Открыть спор\" для обращения к арбитру после окончания торгового срока.\n\nВ случае неполадок с приложением, оно попытается их обнаружить, и если это возможно, покажет кнопку \"Открыть билет поддержки\" чтобы связаться с арбитром который передаст проблему разработчикам. \n\nЕсли у Вас возникли проблемы и не появилась кнопка \"Открыть билет поддержки\", откройте его вручную выбрав данную сделку в разделе \"Папка/Текущие сделки\" и нажав \"alt + o\" или \"option + o\". Просьба использовать это только если Вы уверены что приложение не работает как следует. Если у Вас возникли проблемы или вопросы, просьба пересмотреть Часто Задаваемые Вопросы на странице bisq.network или спросить на Bisq форуме в разделе поддержки. -support.initialInfo=Учтите основные правила процесса рассмотрения спора:\n1. Вы обязаны ответитть на запросы арбитра в течении 2 дней.\n2. Максимальный срок отведенный на спор: 14 дней.\n3. Вы обязаны содействовать арбитру, и предоставлять запрашиваемую информацию по Вашему делу.\n4. Вы согласились с правилами указанными в wiki в соглашении пользователя, когда первый раз запускали приложение.\n\nПросьба прочесть детали процесса спора в нашем wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Просьба ввести описание проблемы в поле ниже, и добавить как можно больше информации, чтобы ускорить разрешение спора. \n\nВот перечень информации, которую Вы должны предоставить:\n\t● Если Вы покупатель BTC: Вы перевели нац. валюту или альткойны? Если да, то нажали ли Вы кнопку "Оплата началась" в приложении?\n\t ● Если Вы продавец BTC: Вы получили платеж нац. валюты или альткойнов? Если да, то нажали ли Вы кнопку "Оплата получена" в приложении?\n\t ● Какую версию Bisq Вы используете?\n\t ● Какую операционную систему Вы используете?\n\t ● Если транзакций не удаются, попробуйте переключиться на новый каталог данных.\n\t Иногда каталог данных повреждается и вызывает странные неисправности.\n\t См.: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nПросьба ознакомиться с основными правилами процесса разрешения споров:\n\t● Вы обязаны ответить на запросы арбитра в течении 2 дней.\n\t ● Максимальный срок отведенный на спор: 14 дней. \n\t ● Вы обязаны содействовать арбитру, и предоставлять запрашиваемую информацию по Вашему делу.\n\t ● Вы приняли правила, изложенные в пользовательском соглашении, при первом запуске приложения.\n\nВы можете прочесть о процессе споров подробнее: https://bisq.network/docs/exchange/arbitration-system\n support.systemMsg=Системное сообщение: {0} -support.youOpenedTicket=Вы открыли запрос поддержки. -support.youOpenedDispute=Вы запросили начать спор.\n\n{0} -support.peerOpenedTicket=Ваш контрагент запросил поддержку по техническим причинам. Просьба дождаться дальнейших инструкций. +support.youOpenedTicket=Вы запросили поддержку.\n\n{0}\n\nВерсия Bisq: {1} +support.youOpenedDispute=Вы сделали запрос на начало спора.\n\n{0}\n\nВерсия Bisq: {1} +support.peerOpenedTicket=Ваш контрагент запросил поддержку по техническим причинам.\n\n{0} support.peerOpenedDispute=Ваш контрагент запросил спор.\n\n{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=Необходимо выбрать хотя account.altcoin.yourAltcoinAccounts=Ваши алткойн-счета account.altcoin.popup.wallet.msg=Убедитесь, что соблюдаете требования пользования кошельками {0}, описанных на веб-странице {1}.\nИспользование кошельков централизованных обменников рискованно, где (а) Ваши ключи не под Вашим контролем, или (б) используют несовместимый кошелёк; может привести к потере Ваших торговых средств!\nАрбитр не является специалистом {2} и не может помочь в таких случаях. account.altcoin.popup.wallet.confirm=Я понимаю и подтверждаю, что знаю какой кошелёк нужно использовать. +account.altcoin.popup.arq.msg=Для торговли ARQ на Bisq необходимо понять и выполнить следующие требования:\n\nДля отправки ARQ необходимо использовать официальный кошелёк ArQmA с графическим интерфейсом (GUI) или командной строкой (CLI) с установленным флажком store-tx-info (данная функция включена автоматически в новых версиях кошелька). Убедитесь, что у вас есть доступ к ключу транзакции, так как он потребуется в случае возникновения спора.\narqma-wallet-cli (используйте команду get_tx_key)\narqma-wallet-gui (перейдите на вкладку «История» (History) и нажмите на кнопку P, чтобы получить доказательство платежа)\n\nПеревод невозможно подтвердить с помощью обычного обозревателя блоков.\n\nВ случае возникновения спора арбитру необходимо предоставить следующие данные:\n- закрытый ключ транзакции;\n- хеш транзакции;\n- публичный адрес получателя.\n\nНепредоставление вышеуказанных данных или использование несовместимого кошелька приведут к тому, что спор будет решен не в вашу пользу. Отправитель ARQ несет ответственность за предоставление подтверждения перевода ARQ арбитру в случае возникновения спора.\n\nВам необходимо предоставить не идентификатор платежа, а обычный публичный адрес.\nДля получения дополнительной информации о том, как найти указанные данные, посетите канал ArQmA в Discord (https://discord.gg/s9BQpJT) или форум ArQmA (https://labs.arqma.com). account.altcoin.popup.xmr.msg=Для торговли XMR на Bisq, Вам необходимо понять и следовать следующим требованиям:\n\nДля отправления XMR, необходимы официальные кошельки Monero GUI или Monero CLI с включённым флажком store-tx-info (включено в новых версиях). Удостоверьтесь, что Вам доступен ключ транзакций, необходимый в случае спора.\nmonero-wallet-cli (используйте команду get_tx_key)\nmonero-wallet-gui (перейдите в раздел history и нажмите на кнопку (P) для подтверждения оплаты)\n\n В дополнение к инструменту проверки XMR (https://xmr.llcoins.net/checktx.html), верификация также может быть выполнена в кошельке.\nmonero-wallet-cli: используя команду (check_tx_key).\nmonero-wallet-gui: разделе Advanced > Prove/Check.\nПереводы не проверяются обычными исследователями блоков.\n\nЕсли возникнет спор, арбитру потребуются от Вас следующие данные:\n- Личный ключ транзакции\n- Хеш(hash) транзакции\n- Публичный адрес получателя\n\nЕсли Вы не предоставите эти данные, либо используете несоответствующие кошельки, Вы проиграете спор. Посылающий XMR ответственен перед арбитром за подтверждение перевода XMR в случае спора.\n\nИдентификатор оплаты не нужен - только обычный публичный адрес.\nЕсли Вам не знаком этот процесс, посетите (https://www.getmonero.org/resources/user-guides/prove-payment.html) или осведомитесь на форуме Monero (https://forum.getmonero.org). -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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). -account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=При использовании {0} допустимо использовать только прозрачные адреса (начиная с t) а не z-адреса (личные), потому что арбитр не сможет проверить транзакцию с z-адресами. -account.altcoin.popup.XZC.msg=При использовании {0} можно использовать только прозрачные (отслеживаемые) адреса, а не неотслеживаемые адреса, поскольку арбитр не сможет проверить транзакцию с неотслеживаемыми адресами в обозревателе блоков цепи / вlockchain. -account.altcoin.popup.bch=Bitcoin Cash и Bitcoin Classic страдают отсутствием защиты воспроизведения. Если вы используете эти монеты, убедитесь, что вы принимаете достаточные меры предосторожности и понимаете все последствия. Возможно понести убытки, отправив одну монету и непреднамеренно отправить те же монеты на другую блокцепь. Поскольку эти монеты были выпущены процессом "airdrop" и разделяют исторические данные с блокчейном Биткойн, существует также риск безопасности и значительный риск потери конфиденциальности. \n\nПросьба прочесть подробнее на форуме Bisq: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Поскольку Bitcoin Gold разделяет историческую блокцепь с Bitcoin, она несёт определенный риск безопасности и значительный риск потери конфиденциальности. Если Вы используете Bitcoin Gold, убедитесь, что вы принимаете достаточные меры предосторожности и понимаете все последствия. \n\nПросьба прочесть подробнее на форуме Bisq: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.blur.msg=Для торговли BLUR на Bisq необходимо понять и выполнить следующие требования:\n\nДля отправки BLUR необходимо использовать кошелёк Blur Network с командой строкой (CLI) или графическим интерфейсом (GUI). \n\nЕсли вы используете кошелёк с командной строкой, после отправки перевода вы увидите хеш транзакции (идентификатор транзакции). Вы должны сохранить его. Сразу после отправки перевода необходимо воспользоваться командой get_tx_key, чтобы получить закрытый ключ транзакции. Если вы этого не сделаете, вы можете утратить доступ к ключу навсегда. \n\nЕсли вы используете кошелёк Blur Network с графическим интерфейсом, закрытый ключ транзакции и ее идентификатор можно найти во вкладке «История» (History). Найдите нужную транзакцию сразу после отправки перевода. Нажмите на «?» в нижнем правом углу поля транзакции и сохраните информацию о ней. \n\nВ случае если потребуется вмешательство арбитра, вы должны предоставить ему следующие данные: 1) Идентификатор транзакции. 2) Закрытый ключ транзакции. 3.) Адрес получателя. Арбитр удостоверит перевод BLUR с помощью средства просмотра транзакций Blur (https://blur.cash/#tx-viewer).\n\nНепредоставление необходимой информации арбитру приведет к тому, что спор будет решен не в вашу пользу. При любых спорах отправитель BLUR несет полную ответственность за подтверждение транзакций перед арбитром. \n\nЕсли вам непонятны данные требования, воздержитесь от торговли на Bisq и сначала обратитесь за помощью в Discord Blur Network (https://discord.gg/dMWaqVW). +account.altcoin.popup.cash2.msg=Для торговли CASH2 на Bisq необходимо понять и выполнить следующие требования:\n\nДля отправки CASH2 необходимо использовать кошелёк Cash2 версии 3 или выше. \n\nПосле отправки перевода вы увидите идентификатор транзакции. Вы должны сохранить его. Сразу после отправки транзакции вы должны воспользоваться командой getTxKey в simplewallet для получения секретного ключа транзакции. \n\nВ случае если потребуется вмешательство арбитра, вы должны предоставить ему следующие данные: 1) Идентификатор транзакции. 2) Секретный ключ транзакции. 3) Адрес получателя Cash2. Арбитр удостоверит перевод CASH2 с помощью обозревателя блоков Cash2 (https://blocks.cash2.org).\n\nНепредоставление необходимой информации арбитру приведет к тому, что спор будет решен не в вашу пользу. При любых спорах отправитель CASH2 несет полную ответственность за подтверждение транзакций перед арбитром.\n\nЕсли вам непонятны данные требования, воздержитесь от торговли на Bisq и сначала обратитесь за помощью в Discord Cash2 (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Для торговли Qwertycoin на Bisq необходимо понять и выполнить следующие требования:\n\nДля отправки QWC необходимо использовать официальный кошелёк QWC версии 5.1.3 или выше. \n\nПосле отправки перевода вы увидите идентификатор транзакции. Вы должны сохранить его. Сразу после отправки транзакции вы должны воспользоваться командой get_Tx_Key в simplewallet для получения секретного ключа транзакции. \n\nВ случае если потребуется вмешательство арбитра, вы должны предоставить ему следующие данные: 1) Идентификатор транзакции. 2) Секретный ключ транзакции. 3) Адрес получателя QWC. Арбитр удостоверит перевод QWC с помощью обозревателя блоков QWC (https://explorer.qwertycoin.org).\n\nНепредоставление необходимой информации арбитру приведет к тому, что спор будет решен не в вашу пользу. При любых спорах отправитель QWC несет полную ответственность за подтверждение транзакций перед арбитром.\n\nЕсли вам непонятны данные требования, воздержитесь от торговли на Bisq и сначала обратитесь за помощью в Discord QWC (https://discord.gg/rUkfnpC). +account.altcoin.popup.drgl.msg=Для торговли Dragonglass на Bisq необходимо понять и выполнить следующие требования:\n\nИз-за обеспечиваемой Dragonglass конфиденциальности транзакцию в публичном блокчейне подтвердить невозможно. Подтвердить платеж можно с помощью закрытого ключа транзакции (TXN-Private-Key).\nЗакрытый ключ транзакции — это одноразовый ключ, автоматически генерируемый для каждой транзакции, доступ к которому можно получить из вашего кошелька DRGL.\nКлюч можно найти в кошельке DRGL с графическим интерфейсом (GUI), открыв окно с информацией о транзакции, или в кошельке Dragonglass с командной строкой (CLI simplewallet), воспользовавшись командой get_tx_key.\n\nДля получения закрытого ключа транзакции требуется DRGL версии Oathkeeper или выше.\n\nВ случае возникновения спора необходимо предоставить арбитру следующие данные:\n- Закрытый ключ транзакции (TXN-Private-Key).\n- Хеш транзакции.\n- Публичный адрес получателя.\n\nУдостоверить факт платежа можно с помощью вышеуказанных данных на сайте http://drgl.info/#check_txn.\n\nНепредоставление указанных выше данных или использование несовместимого кошелька приведут к тому, что спор будет решен не в вашу пользу. Отправитель Dragonglass несет ответственность за предоставление подтверждения перевода DRGL арбитру в случае возникновения спора. Использование идентификатора платежа (PaymentID) не требуется.\n\nЕсли вас есть какие-либо вопросы относительно указанных выше действий, посетите канал Dragonglass в Discord (http://discord.drgl.info). +account.altcoin.popup.ZEC.msg=При торговле Zcash вы можете использовать только прозрачные адреса (начинающиеся с t), а не z-адреса (скрытые), так как арбитр не сможет удостоверить транзакцию с z-адресами. +account.altcoin.popup.XZC.msg=При торговле Zcoin вы можете использовать только прозрачные (отслеживаемые) адреса, а не неотслеживаемые адреса, так как арбитр не сможет удостоверить транзакцию с неотслеживаемыми адресами в обозревателе блоков. +account.altcoin.popup.grin.msg=При создании транзакции в GRIN требуется взаимодействие в реальном времени между отправителем и получателем. Следуйте инструкциям на веб-сайте проекта GRIN, чтобы узнать, как отправлять и получать GRIN (получатель должен находиться в сети в момент отправки перевода или в течение определенного периода). \n\nBisq поддерживает кошельки только в формате Grinbox (Wallet713). \n\nОтправитель GRIN должен предоставить доказательство успешной отправки перевода. Если отправитель не сможет предоставить это доказательство, потенциальный спор будет решен в пользу получателя GRIN. Убедитесь, что используете последнюю версию Grinbox с поддержкой доказательства транзакций и что понимаете, как нужно отправлять и получать GRIN, а также как создавать доказательство перевода. \n\nЧтобы узнать подробности работы с инструментом доказательства транзакции в Grinbox, см. https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only. +account.altcoin.popup.beam.msg=При создании транзакции в BEAM требуется взаимодействие в реальном времени между отправителем и получателем. \n\nСледуйте инструкциям на веб-сайте проекта BEAM, чтобы узнать, как отправлять и получать BEAM (получатель должен находиться в сети в момент отправки перевода или в течение определенного периода). \n\nОтправитель BEAM должен предоставить доказательство успешной отправки перевода. Используйте кошелёк, позволяющий получить доказательство перевода. Если отправитель не сможет предоставить это доказательство, потенциальный спор будет решен в пользу получателя BEAM. account.fiat.yourFiatAccounts=Ваши счета нац. валют @@ -961,8 +980,8 @@ account.seed.info=Просьба записать кодовые слова ко account.seed.warn.noPw.msg=Вы не установили пароль кошелька, защищающий его кодовые слова.\n\nЖелаете показать на экране кодовые слова? account.seed.warn.noPw.yes=Да, и не спрашивать снова account.seed.enterPw=Введите пароль, чтобы увидеть кодовые слова -account.seed.restore.info=Просьба создать резервную копию перед восстановлением из кодовых слов. Помните, что восстановление кошелька предназначено для экстренных случаев и может вызвать неполадки внутренней базы данных кошелька.\nЭто не способ резервного копирования! Используйте резервную копию из каталога данных приложения для восстановления его предыдущего состояния. -account.seed.restore.ok=Хорошо, я понимаю и хочу восстановить +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Восстановить и закрыть Bisq #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=Нижняя цена # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=BSQ кошелёк dao.tab.proposals=Руководство dao.tab.bonding=Гарантийный депозит dao.tab.proofOfBurn=Сбор за листинг активов/Proof of burn +dao.tab.monitor=Монитор сети +dao.tab.news=Новости dao.paidWithBsq=выплачено в BSQ -dao.availableBsqBalance=Доступно -dao.availableNonBsqBalance=Доступный баланс (ВТС) исключая BSQ -dao.unverifiedBsqBalance=Непроверенно (ожидает подтверждения блока) +dao.availableBsqBalance=Доступно для расходов (проверенные + неподтвержденные суммы) +dao.verifiedBsqBalance=Баланс всех проверенных UTXO +dao.unconfirmedChangeBalance=Остаток всех неподтвержденных сумм +dao.unverifiedBsqBalance=Баланс всех непроверенных транзакций (ожидающих подтверждения блока) dao.lockedForVoteBalance=Использовано для голосования dao.lockedInBonds=Заперто в гарантийных депозитах +dao.availableNonBsqBalance=Доступный баланс (ВТС) исключая BSQ dao.totalBsqBalance=Общий баланс BSQ dao.tx.published.success=Ваша транзакция опубликована. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Обзор цикла голосования dao.cycle.currentPhase=Текущий этап dao.cycle.currentBlockHeight=Номер текущего блока dao.cycle.proposal=Этап предложения +dao.cycle.proposal.next=Cледующий этап предложения dao.cycle.blindVote=Этап слепого голосования dao.cycle.voteReveal=Этап выявления голосов dao.cycle.voteResult=Результат голосования dao.cycle.phaseDuration={0} блоков (≈{1}); Блок {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Блок {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Транзакция выявления голоса опубликована +dao.voteReveal.txPublished=Ваша транзакция выявления голоса с идент. {0} была успешно опубликована. \n\nЭто происходит автоматически, если Вы участвовали в голосовании DAO. dao.results.cycles.header=Цыклы dao.results.cycles.table.header.cycle=Цыкл @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Результат голосован dao.results.proposals.voting.detail.header=Результаты голосования по выбранному предложению +dao.results.exceptions=Исключение(я) результата голосования + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Неопределено @@ -1129,7 +1160,7 @@ dao.param.QUORUM_REMOVE_ASSET=Необходимый кворум BSQ для у # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Необходимый кворум BSQ для запроса конфискации # suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Требуемый кворум в BSQ для запросов роли требующей залога +dao.param.QUORUM_ROLE=Требуемый кворум в BSQ для запросов обеспеченной роли # suppress inspection "UnusedProperty" dao.param.THRESHOLD_GENERIC=Требуемый порог в % для обычного предложения @@ -1150,22 +1181,33 @@ dao.param.THRESHOLD_ROLE=Требуемый порог в % для запрос dao.param.RECIPIENT_BTC_ADDRESS=BTC адрес получателя # suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Стоимость листинга актива в день +dao.param.ASSET_LISTING_FEE_PER_DAY=Сбор за листинг актива в день +# suppress inspection "UnusedProperty" +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Срок блокировки для альтернативных торговых выплат # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Мин. торговый объём +dao.param.ARBITRATOR_FEE=Комиссия арбитра в ВТС + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Макс. торговый предел в BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Текущее значение {0} dao.param.blocks={0} блоков -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Продолжительность {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} блок(ов) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(значение по умолчанию) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(были изменены при голосовании) +dao.results.invalidVotes=В этом цикле голосования возникли недействительные голоса. Это может произойти, если голосование было плохо распределено в сети P2P.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Неопределено # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Соль dao.bond.reputation.hash=Хеш dao.bond.reputation.lockupButton=Запереть dao.bond.reputation.lockup.headline=Подтвердить транзакцию блокировки -dao.bond.reputation.lockup.details=Запереть сумму: {0}\nВремя блокировки: {1} блок(ов) \n\nДействительно желаете продолжить? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Подтвердить транзакцию разблокировки -dao.bond.reputation.unlock.details=Отпереть сумму: {0}\nВремя блокировки: {1} блок(ов)\n\nДействительно желаете продолжить? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=Все гарантийные депозиты @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Запереть dao.bond.table.button.unlock=Разблокировать dao.bond.table.button.revoke=Аннулировать +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Неопределено # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Ещё не обеспечено # suppress inspection "UnusedProperty" @@ -1252,11 +1296,15 @@ dao.bond.bondState.UNLOCKED=Гарантийный депозит разблок # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Гарантийный депозит конфискован +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Неопределено # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Обеспеченная роль # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Обеспеченная репутация +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.UNDEFINED=Неопределено # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.GITHUB_ADMIN=Github админ # suppress inspection "UnusedProperty" @@ -1266,9 +1314,13 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter админ # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Rocket чат админ # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Youtube админ +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube админ +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BISQ_MAINTAINER=Мейнтейнер Bisq # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Мэйнтейнер Bisq +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Оператор вэбсайта # suppress inspection "UnusedProperty" @@ -1276,7 +1328,7 @@ dao.bond.bondedRoleType.FORUM_OPERATOR=Оператор форума # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Оператор исходного узла # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Оператор узла данных курса +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Оператор Биткойн узла # suppress inspection "UnusedProperty" @@ -1284,6 +1336,8 @@ dao.bond.bondedRoleType.MARKETS_OPERATOR=Оператор узла данных # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Оператор BSQ обозревателя # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Владелец имени домена # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS админ @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=DNS админ dao.bond.bondedRoleType.MEDIATOR=Посредник # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Арбитр +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=Сбор за листинг актива +dao.burnBsq.assetFee=Листинг актива dao.burnBsq.menuItem.assetFee=Сбор за листинг актива dao.burnBsq.menuItem.proofOfBurn=Proof of burn dao.burnBsq.header=Сбор за листинг актива @@ -1308,7 +1364,7 @@ dao.burnBsq.assets.lookBackPeriod=Период проверки dao.burnBsq.assets.trialFee=Сбор за испытательный срок dao.burnBsq.assets.totalFee=Итого уплачен сбор dao.burnBsq.assets.days={0} дней -dao.burnBsq.assets.toFewDays=Взнос за актив слишком низок. Мин. количество дней пробного периода {0}. +dao.burnBsq.assets.toFewDays=Взнос за актив слишком низок. Мин. количество дней для пробного периода: {0}. # suppress inspection "UnusedProperty" dao.assetState.UNDEFINED=Неопределено @@ -1332,7 +1388,7 @@ dao.proofOfBurn.hash=Хеш dao.proofOfBurn.txs=Транзакции dao.proofOfBurn.pubKey=Общедоступный ключ dao.proofOfBurn.signature.window.title=Подписать сообщение ключом из транзакции proof or burn -dao.proofOfBurn.verify.window.title=Проверьте сообщение ключом из транзакции proof of burn +dao.proofOfBurn.verify.window.title=Проверить сообщение ключом из транзакции proof or burn dao.proofOfBurn.copySig=Скопировать подпись в буфер dao.proofOfBurn.sign=Подписать dao.proofOfBurn.message=Сообщение @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Показать голосование # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Результат голосования +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Неопределено # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Запрос компенсации # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=Общее предложение # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Предложение о конфискации гарантийного депозита +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Неопределено # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Запрос компенсации # suppress inspection "UnusedProperty" @@ -1398,28 +1458,30 @@ dao.proposal.type.short.GENERIC=Общее предложение # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Гарантийный депозит конфискуется - dao.proposal.details=Подробности предложения dao.proposal.selectedProposal=Избранное предложение dao.proposal.active.header=Предложения текущего цикла dao.proposal.active.remove.confirm=Действительно хотите удалить это предложение?\nВзнос создателя предложения будет утерян. dao.proposal.active.remove.doRemove=Да, удалить мое предложение dao.proposal.active.remove.failed=Не удалось удалить предложение. +dao.proposal.myVote.title=Голосование dao.proposal.myVote.accept=Принять предложение dao.proposal.myVote.reject=Отклонить предложение dao.proposal.myVote.removeMyVote=Игнорировать предложение dao.proposal.myVote.merit=Вес голоса от заслуженного BSQ dao.proposal.myVote.stake=Вес голоса от доли -dao.proposal.myVote.blindVoteTxId=Идент. транзакции слепого голосования dao.proposal.myVote.revealTxId=Идент. транзакции выявления голоса -dao.proposal.myVote.stake.prompt=Макс. доступный баланс для голосования: {0} -dao.proposal.votes.header=Голосование по всем предложениям -dao.proposal.votes.header.voted=Мой голос -dao.proposal.myVote.button=Голосование по всем предложениям +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Выберите тип предложения +dao.proposal.create.phase.inactive=Просьба дождаться следующего этапа предложения dao.proposal.create.proposalType=Тип предложения -dao.proposal.create.createNew=Создать новое предложение -dao.proposal.create.create.button=Создать предложение +dao.proposal.create.new=Создать новое предложение +dao.proposal.create.button=Создать предложение +dao.proposal.create.publish=Опубликовать предложение +dao.proposal.create.publishing=Публикация предложения... dao.proposal=предложение dao.proposal.display.type=Тип предложения dao.proposal.display.name=Имя/ник @@ -1432,7 +1494,7 @@ dao.proposal.display.proposalFee=Сбор за предложение dao.proposal.display.myVote=Мой голос dao.proposal.display.voteResult=Сводка итогов голосования dao.proposal.display.bondedRoleComboBox.label=Тип обеспеченной роли -dao.proposal.display.requiredBondForRole.label=Необходимый гарантийный депозит роли +dao.proposal.display.requiredBondForRole.label=Требуемый гарантийный депозит для роли dao.proposal.display.tickerSymbol.label=Тикер dao.proposal.display.option=Вариант @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=Принято dao.proposal.display.myVote.rejected=Отклонено dao.proposal.display.myVote.ignored=Проигнорировано dao.proposal.myVote.summary=Проголосовано: {0}; Вес голоса: {1} (заслужено: {2} + доля: {3}); +dao.proposal.myVote.invalid=Голосование было недействительным dao.proposal.voteResult.success=Принято dao.proposal.voteResult.failed=Отклонено @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=Актив для удаления dao.blindVote=слепое голосование dao.blindVote.startPublishing=Транзакция слепого голосования публикуется... -dao.blindVote.success=Ваш слепой голос успешно опубликован. +dao.blindVote.success=Ваша транзакция слепого голосованию успешно опубликована.\n\nОбратите внимание, что Вы должны быть онлайн при стадии выявления голосов, для того что-бы Ваше приложение Bisq смогло опубликовать транзакцию выявления Вашего голосоа. Без этого Ваш голос не будет зачислен! dao.wallet.menuItem.send=Послать dao.wallet.menuItem.receive=Получить dao.wallet.menuItem.transactions=Транзакции dao.wallet.dashboard.myBalance=Баланс моего кошелька -dao.wallet.dashboard.distribution=Распределение всего BSQ -dao.wallet.dashboard.locked=Глобальное состояние запертого BSQ -dao.wallet.dashboard.market=Данные рынка -dao.wallet.dashboard.genesis=Исходная транзакция -dao.wallet.dashboard.txDetails=Статистика транзакций BSQ -dao.wallet.dashboard.genesisBlockHeight=Номер исходного блока -dao.wallet.dashboard.genesisTxId=Идент. исходной транзакции -dao.wallet.dashboard.genesisIssueAmount=BSQ выданные в исходной транзакции -dao.wallet.dashboard.compRequestIssueAmount=BSQ выданные на запросы компенсации -dao.wallet.dashboard.reimbursementAmount=BSQ выданные на запросы возмещения -dao.wallet.dashboard.availableAmount=Итого доступных BSQ -dao.wallet.dashboard.burntAmount=Уничтоженные BSQ (сборы) -dao.wallet.dashboard.totalLockedUpAmount=Заперто в гарантийных депозитах -dao.wallet.dashboard.totalUnlockingAmount=Разблокировка BSQ из гарантийных депозитов -dao.wallet.dashboard.totalUnlockedAmount=BSQ разблокировано из гарантийных депозитов -dao.wallet.dashboard.totalConfiscatedAmount=BSQ конфисковано из гарантийных депозитов -dao.wallet.dashboard.allTx=Общее количество транзакций BSQ -dao.wallet.dashboard.utxo=Общее кол-во неизрасходованных (UTXO) -dao.wallet.dashboard.compensationIssuanceTx=Общее кол-во транзакций выдачи запроса компенсации -dao.wallet.dashboard.reimbursementIssuanceTx=Общее кол-во транзакций выдачи запроса возмещения -dao.wallet.dashboard.burntTx=Общее кол-во транзакций оплаты сборов -dao.wallet.dashboard.price=Текущий курс BSQ/BTC (в Bisq) -dao.wallet.dashboard.marketCap=Рыночная капитализация (на основе торговой цены) - -dao.wallet.receive.fundYourWallet=Ввести средства на Ваш BSQ кошелёк -dao.wallet.receive.bsqAddress=Адрес кошелька BSQ + +dao.wallet.receive.fundYourWallet=Ваш BSQ адрес +dao.wallet.receive.bsqAddress=Адрес кошелька BSQ (ранее неиспользованный адрес) + +dao.wallet.receive.dao.headline=Bisq DAO +dao.wallet.receive.daoInfo=Подобно тому, как обменник Bisq децентрализован и защищён от цензуры, так и в его способе управления Bisq DAО и токен BSQ позволяют это осуществить. +dao.wallet.receive.daoInfo.button=Подробнее о Bisq DAO +dao.wallet.receive.daoTestnetInfo=Основная сеть Bisq DAO еще не запущена, но Вы можете узнать о ней подробнее, запустив её на testnet. +dao.wallet.receive.daoTestnetInfo.button=Как запустить Bisq DAO на нашей testnet +dao.wallet.receive.daoContributorInfo=Если Вы внесли свой вклад в Bisq, используйте свой адрес BSQ ниже и запросите участия в первоначальном распределении BSQ. +dao.wallet.receive.daoContributorInfo.button=Как принять участие в первоначальном распределении BSQ dao.wallet.send.sendFunds=Послать средства dao.wallet.send.sendBtcFunds=Отправить средства (BTC), исключая BSQ @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=Новейший проверенный блок: { dao.wallet.chainHeightSyncing=Ожидание блоков... Проверено {0} блоков из {1} dao.wallet.tx.type=Тип +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Неопределено # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Не признано # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=Запрос/выдача компенсации dao.tx.issuanceFromCompReq.tooltip=Запрос компенсации, который привел к выпуску новых BSQ.\nДата выпуска: {0} dao.tx.issuanceFromReimbursement=Запрос/выдача возмещения dao.tx.issuanceFromReimbursement.tooltip=Запрос возмещения, который привел к выпуску новых BSQ.\nДата выпуска: {0} -dao.proposal.create.missingBsqFunds=У Вас недостаточно средств для создания предложения.\nНехватает: {0} +dao.proposal.create.missingBsqFunds=У Вас недостаточно средств BSQ для создания предложения. Если у Вас есть неподтвержденная транз. BSQ, необходимо дождаться подтверждения в блокцепи потому, что BSQ проверяется только при включении в блок.\nНехватает: {0} + +dao.proposal.create.missingBsqFundsForBond=У Вас недостаточно BSQ для этой роли. Вы все же можете опубликовать это предложение, но Вам понадобится полная сумма BSQ, необходимая для этой роли, если оно будет принято.\nНехватает: {0} + +dao.proposal.create.missingMinerFeeFunds=У Вас недостаточно средств для создания транзакции предложения. Любая транз. BSQ требует оплаты комиссии майнера в ВТС.\nНехватает: {0} dao.feeTx.confirm=Подтвердить транзакцию {0} dao.feeTx.confirm.details={0} сбор: {1}\nкомиссия майнера: {2} ({3} сатоши/байт)\nРазмер транзакиции: {4} Кб\n\nДействительно хотите опубликовать транзакцию {5}? +dao.news.bisqDAO.title=BISQ DAO +dao.news.bisqDAO.description=Подобно тому, как обменник Bisq децентрализован и защищён от цензуры, так и в его способе управления Bisq DAО и токен BSQ позволяют это осуществить. +dao.news.bisqDAO.readMoreLink=Подробнее о Bisq DAO + +dao.news.pastContribution.title=СОДЕЙСТВОВАЛИ В ПРОШЛОМ? ЗАПРОСИТЬ BSQ +dao.news.pastContribution.description=Если Вы внесли свой вклад в Bisq, используйте свой адрес BSQ ниже и запросите участия в первоначальном распределении BSQ. +dao.news.pastContribution.yourAddress=Адрес Вашего кошелька BSQ +dao.news.pastContribution.requestNow=Запросить сейчас + +dao.news.DAOOnTestnet.title=ЗАПУСТИТЬ BISQ DAO НА НАШЕЙ TESTNET +dao.news.DAOOnTestnet.description=Основная сеть Bisq DAO еще не запущена, но Вы можете узнать о Bisq DAO подробнее, запустив её на нашей testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Переключить в режим DAO Testnet +dao.news.DAOOnTestnet.firstSection.content=Переключитесь на DAO Testnet в настройках. +dao.news.DAOOnTestnet.secondSection.title=2. Приобрести BSQ +dao.news.DAOOnTestnet.secondSection.content=Запросить BSQ на Slack или купить BSQ в Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Принять участие в цикле голосования +dao.news.DAOOnTestnet.thirdSection.content=Внесение предложений и голосование по предложениям об изменении различных аспектов Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Ознакомьтесь с обозревателем блоков BSQ +dao.news.DAOOnTestnet.fourthSection.content=Поскольку BSQ тоже биткойны, транзакции BSQ видны в нашем обозревателе блоков Биткойн. +dao.news.DAOOnTestnet.readMoreLink=Ознакомиться с полной документацией + +dao.monitor.daoState=Состояние ДАО +dao.monitor.proposals=Состояние выдвижения предложений +dao.monitor.blindVotes=Состояние слепого голосования + +dao.monitor.table.peers=Пиры +dao.monitor.table.conflicts=Конфликты +dao.monitor.state=Статус +dao.monitor.requestAlHashes=Запросить все хеши +dao.monitor.resync=Повторная синхронизация состояния ДАО +dao.monitor.table.header.cycleBlockHeight=Номер блока/цикла +dao.monitor.table.cycleBlockHeight=Цикл {0} / блок {1} + +dao.monitor.daoState.headline=Состояние ДАО +dao.monitor.daoState.daoStateInSync=Состояние ДАО на вашем компьютере достигло консенсуса с сетью +dao.monitor.daoState.daoStateNotInSync=Состояние ДАО на вашем компьютере не достигло консенсуса с сетью. Требуется повторная синхронизация состояния ДАО. +dao.monitor.daoState.table.headline=Цепочка хешей состояния ДАО +dao.monitor.daoState.table.blockHeight=Номер блока +dao.monitor.daoState.table.hash=Хеш состояния ДАО +dao.monitor.daoState.table.prev=Предыдущий хеш +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Состояние выдвижения предложений +dao.monitor.proposal.daoStateInSync=Состояние выдвижения предложений на вашем компьютере достигло консенсуса с сетью +dao.monitor.proposal.daoStateNotInSync=Состояние выдвижения предложений на вашем компьютере не достигло консенсуса с сетью. Требуется перезапуск приложения. +dao.monitor.proposal.table.headline=Цепочка хешей состояния выдвижения предложений +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Хеш состояния выдвижения предложений +dao.monitor.proposal.table.prev=Предыдущий хеш +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Состояние слепого голосования +dao.monitor.blindVote.daoStateInSync=Состояние слепого голосования на вашем компьютере достигло консенсуса с сетью +dao.monitor.blindVote.daoStateNotInSync=Состояние слепого голосования на вашем компьютере не достигло консенсуса с сетью. Требуется перезапуск приложения. +dao.monitor.blindVote.table.headline=Цепочка хешей состояния слепого голосования +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Хеш состояния слепого голосования +dao.monitor.blindVote.table.prev=Предыдущий хеш +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Данные рынка +dao.factsAndFigures.dashboard.price=Текущий курс BSQ/BTC (в Bisq) +dao.factsAndFigures.dashboard.marketCap=Рыночная капитализация (на основе торговой цены) +dao.factsAndFigures.dashboard.availableAmount=Итого доступных BSQ + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ выданные в исходной транзакции +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ выданные на запросы компенсации +dao.factsAndFigures.supply.reimbursementAmount=BSQ выданные на запросы возмещения + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Глобальное состояние запертого BSQ +dao.factsAndFigures.supply.totalLockedUpAmount=Заперто в гарантийных депозитах +dao.factsAndFigures.supply.totalUnlockingAmount=Разблокировка BSQ из гарантийных депозитов +dao.factsAndFigures.supply.totalUnlockedAmount=BSQ разблокировано из гарантийных депозитов +dao.factsAndFigures.supply.totalConfiscatedAmount=BSQ конфисковано из гарантийных депозитов +dao.factsAndFigures.supply.burntAmount=Уничтоженные BSQ (сборы) + +dao.factsAndFigures.transactions.genesis=Исходная транзакция +dao.factsAndFigures.transactions.genesisBlockHeight=Номер исходного блока +dao.factsAndFigures.transactions.genesisTxId=Идент. исходной транзакции +dao.factsAndFigures.transactions.txDetails=Статистика транзакций BSQ +dao.factsAndFigures.transactions.allTx=Общее количество транзакций BSQ +dao.factsAndFigures.transactions.utxo=Общее кол-во неизрасходованных выводов (UTXO) +dao.factsAndFigures.transactions.compensationIssuanceTx=Общее кол-во транзакций выдачи запроса компенсации +dao.factsAndFigures.transactions.reimbursementIssuanceTx=Общее кол-во транзакций выдачи запроса возмещения +dao.factsAndFigures.transactions.burntTx=Общее кол-во транзакций оплаты сборов #################################################################### # Windows @@ -1590,7 +1737,7 @@ disputeSummaryWindow.title=Сводка disputeSummaryWindow.openDate=Дата открытия билета поддержки disputeSummaryWindow.role=Роль трейдера disputeSummaryWindow.evidence=Доказательство -disputeSummaryWindow.evidence.tamperProof=Подтверждение доказательств +disputeSummaryWindow.evidence.tamperProof=Неподдельное доказательство disputeSummaryWindow.evidence.id=Проверка идентификации disputeSummaryWindow.evidence.video=Видео/Экранная демонстрация disputeSummaryWindow.payout=Выплата суммы сделки @@ -1630,15 +1777,16 @@ enterPrivKeyWindow.headline=Регистрация открыта только filterWindow.headline=Изменить список фильтров filterWindow.offers=Отфильтрованные предложения (через запят.) -filterWindow.onions=Отфильтрованные onion-адреса (через запят.) +filterWindow.onions=Фильтрованные onion-адреса (через запят.) filterWindow.accounts=Отфильтрованные данные торгового счёта:\nФормат: список, через запятые [идентификатор способа оплаты | поле данных | стоимость] -filterWindow.bannedCurrencies=Отфильтрованные коды валют (через запят.) +filterWindow.bannedCurrencies=Фильтрованные коды валют (через запят.) filterWindow.bannedPaymentMethods=Отфильтрованные идент. способов оплаты (через запят.) filterWindow.arbitrators=Фильтрованные арбитры (onion-адреса через запят.) filterWindow.seedNode=Фильтрованные исходные узлы (onion-адреса через запят.) filterWindow.priceRelayNode=Фильтрованные ретрансляторы курса (onion-адреса через запят.) filterWindow.btcNode=Фильтрованные узлы Биткойн (адреса + порты через запят.) filterWindow.preventPublicBtcNetwork=Не использовать общедоступную Bitcoin сеть +filterWindow.disableDao=Отключить DAO filterWindow.add=Добавить фильтр filterWindow.remove=Удалить фильтр @@ -1785,7 +1933,7 @@ popup.warning.nodeBanned=Один из узлов {0} был запрещен/з popup.warning.priceRelay=ретранслятор курса popup.warning.seed=кодовые слова -popup.info.securityDepositInfo=Чтобы гарантировать следование торговому протоколу трейдерами, обоим необходимо заплатить залоговый депозит.\n\nДепозит остаётся в Вашем торговом кошельке до успешного завершения сделки, и затем будет возвращен Вам.\n\nУчтите, что если Вы создаёте новое предложение, Bisq должен быть подключён к сети для принятия предложения другими трейдерами. Чтобы Ваши предложения были доступны в сети, компьютер и Bisq должны быть включёны и подключены к сети. (т. е. убедитесь, что компьютер не впал в режим ожидания...монитор в режиме ожидания не проблема). +popup.info.securityDepositInfo=Чтобы гарантировать следование торговому протоколу трейдерами, обоим необходимо заплатить залоговый депозит.\n\nДепозит остаётся в Вашем торговом кошельке до успешного завершения сделки, и затем будет возвращен Вам.\n\nУчтите, что если Вы создаёте новое предложение, Bisq должен быть подключён к сети для принятия предложения другими трейдерами. Чтобы Ваши предложения были доступны в сети, компьютер и Bisq должны быть включены и подключены к сети. (т. е. убедитесь, что компьютер не впал в режим ожидания...монитор в режиме ожидания не проблема). popup.info.cashDepositInfo=Просьба убедиться, что в Вашем районе существует отделение банка, где возможно внести депозит наличными.\nИдентификатор (BIC/SWIFT) банка продавца: {0}. popup.info.cashDepositInfo.confirm=Я подтверждаю, что могу внести депозит @@ -1807,8 +1955,8 @@ popup.attention.forTradeWithId=Обратите внимание на сделк popup.roundedFiatValues.headline=Новая функция конфиденциальности: Округленные значения нац. валют popup.roundedFiatValues.msg=Для повышения конфиденциальности Вашей сделки, сумма {0} была округлена.\n\nВ зависимости от версии приложения, Вы заплатите или получите либо значения с десятичными знаками, либо округленные.\n\nОтныне, оба значения соответствуют торговому протоколу.\n\nТакже имейте в виду, что значения BTC изменяются автоматически, чтобы предельно соответствовать округленной сумме нац. валюты. -popup.info.multiplePaymentAccounts.headline=Multiple payment accounts available -popup.info.multiplePaymentAccounts.msg=You have multiple payment accounts available for this offer. Please make sure you've picked the right one. +popup.info.multiplePaymentAccounts.headline=Доступно несколько платёжных счетов +popup.info.multiplePaymentAccounts.msg=У Вас есть несколько платёжных счетов, доступных для этого предложения. Просьба убедиться, что Вы выбрали правильный. #################################################################### @@ -1877,7 +2025,7 @@ table.placeholder.noItems=Сейчас нет доступных {0} table.placeholder.noData=Сейчас данные недоступны -peerInfoIcon.tooltip.tradePeer=контрагента +peerInfoIcon.tooltip.tradePeer=Kонтрагента peerInfoIcon.tooltip.maker=Создателя peerInfoIcon.tooltip.trade.traded=Onion/Tor адрес {0}: {1}\nВы уже торговали {2} раз(a) с данным трейдером\n{3} peerInfoIcon.tooltip.trade.notTraded=Onion/Tor адрес {0}: {1}\nВы ещё не торговали с этим трейдером.\n {2} @@ -1955,6 +2103,10 @@ BTC_MAINNET=Биткойн Основная сеть BTC_TESTNET=Биткойн Тест-сеть # suppress inspection "UnusedProperty" BTC_REGTEST=Биткойн режим регрессионного тестирования +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Биткойн DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Бета-сеть ДАО Биткойн (основная сеть Биткойн) time.year=Год time.month=Месяц @@ -2024,7 +2176,9 @@ payment.email=Э-почта payment.country=Страна payment.extras=Дополнительные требования payment.email.mobile=Э-почта или номер моб. тел. -payment.altcoin.address=Алткойн-адрес +payment.altcoin.address=Альткойн-адрес +payment.altcoin.tradeInstantCheckbox=Ускорить сделку (в течение 1 часа) с этой алткойн +payment.altcoin.tradeInstant.popup=Для ускоренной торговли требуется, чтобы оба контрагента были онлайн, и могли завершить сделку менее чем за 1 час.\n\nЕсли у Вас есть текущие предложения, и Вы не доступны, просьба отключить эти предложения в разделе "Папка". payment.altcoin=Алткойны payment.select.altcoin=Выберите или поищите алткойн payment.secret=Секретный вопрос @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=Э-почта или тел. номер payment.venmo.venmoUserName=Логин Venmo payment.popmoney.accountId=Э-почта или тел. номер -payment.revolut.email=Э-почта или тел. номер +payment.revolut.email=Э-почта +payment.revolut.phoneNr=Зарегистрированный номер телефона payment.promptPay.promptPayId=Удостовер. гражданства / налог. идент. или номер телефона. payment.supportedCurrencies=Поддерживаемые валюты payment.limitations=Ограничения @@ -2082,6 +2237,10 @@ payment.limits.info=Учтите, что все банковские перев payment.cashDeposit.info=Просьба удостовериться, что Ваш банк позволяет отправлять денежные депозиты на счета других. Например, Bank of America и Wells Fargo больше не разрешают такие депозиты. +payment.revolut.info=Просьба убедиться, что номер телефона, который Вы использовали для своего счета Revolut, зарегистрирован в Revolut. Иначе, покупатель BTC не сможет отправить Вам средства. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=Контактная информация payment.f2f.contact.prompt=Как Вы хотите связаться с контрагентом? (адрес электронной почты, номер телефона...) payment.f2f.city=Город для встречи "лицом к лицу" @@ -2124,20 +2283,14 @@ MONEY_GRAM_SHORT=MoneyGram # suppress inspection "UnusedProperty" WESTERN_UNION_SHORT=Western Union # suppress inspection "UnusedProperty" -F2F_SHORT=Лицом к лицу +F2F_SHORT=Лицом к лицу # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Алткойны PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Алткойны Мгновенно +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Алткойны PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Алткойны Мгновенно + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} должен состоять из {1} цифе validation.invalidInput=Недействительный ввод: {0} validation.accountNrFormat=Номер счёта должен быть в формате: {0} validation.altcoin.wrongStructure=Сбой проверки адреса, поскольку она не соответствует структуре адреса {0}. +validation.altcoin.ltz.zAddressesNotSupported=Адрес LTZ должен начинаться с L. Адреса, начинающиеся с z, не поддерживаются. validation.altcoin.zAddressesNotSupported=Адрес ZEC должен начинаться с "t". Адреса, начинающиеся с "z", не поддерживаются. validation.altcoin.invalidAddress=Адрес не является действительным адресом {0}! {1} validation.bic.invalidLength=Длина ввода ни 8, ни 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Должно содержать то validation.interacETransfer.invalidAnswer=Должно быть одно слово, содержащее только буквы, цифры и/или символ - validation.inputTooLarge=Данные должны быть не более {0} validation.inputTooSmall=Данные должны быть более {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=Сумма ниже предела ("пыли") {0} не допускается. validation.length=Длина должна быть между {0} и {1} validation.pattern=Данные должны быть в формате: {0} validation.noHexString=Данные не в шестнадцатеричном формате. validation.advancedCash.invalidFormat=Должен быть действительный идент. э-почты или кошелька в формате: x000000000000 +validation.invalidUrl=Это недопустимый URL-адрес +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_sr.properties b/core/src/main/resources/i18n/displayStrings_sr.properties index e59b753ffc0..3a925fdc160 100644 --- a/core/src/main/resources/i18n/displayStrings_sr.properties +++ b/core/src/main/resources/i18n/displayStrings_sr.properties @@ -104,7 +104,7 @@ shared.belowInPercent=Below % from market price shared.aboveInPercent=Above % from market price shared.enterPercentageValue=Unesi % vrednost shared.OR=ILI -shared.notEnoughFunds=Nemate dovoljno sredstava u vašem Bisq novčaniku,\nTreba vam {0} ali vi imate samo {1} u vašem Bisq novčaniku.\n\nMolimo vas finansirajte trgovinu iz eksternog Bitkoin novčanika ili finansirajte vaš Bisq novčanik pod \"Sredstva/Primi sredstava". +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.waitingForFunds=Čekanje sredstava... shared.depositTransactionId=ID transakcije depozita shared.TheBTCBuyer=BTC kupac @@ -113,12 +113,13 @@ shared.reasonForPayment=Svrha uplate shared.sendingConfirmation=Slanje potvrde... shared.sendingConfirmationAgain=Molimo ponovo pošaljite potvrdu shared.exportCSV=Izvezi u csv +shared.exportJSON=Export to JSON shared.noDateAvailable=Datum nije dostupan shared.arbitratorsFee=Provizija arbitra shared.noDetailsAvailable=Detalji nisu dostupni shared.notUsedYet=Još nije korišćeno shared.date=Datum -shared.sendFundsDetailsWithFee=Slanje: {0}\nSa adrese: {1}\nNa adresu: {2}\nPotrebna provizija transakcije je: {3} ({4} Satošija/bajt)\nVeličina transakcije: {5} Kb\n\nPrimalac će dobiti: {6}\n\nDa li ste sigurni da želite podići taj iznos? +shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired transaction 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.copyToClipboard=Kopiraj u klipbord shared.language=Jezik shared.country=Država @@ -196,6 +197,8 @@ shared.interval=Interval shared.actions=Actions shared.buyerUpperCase=Buyer shared.sellerUpperCase=Seller +shared.new=NEW +shared.blindVoteTxId=Blind vote transaction ID #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=Locked mainView.footer.usingTor=(korišćenje Tor-a) mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo=Pirovi Bitkoin mreže: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Inicijalizacija -mainView.footer.btcInfo.synchronizedWith=sinhronizovano sa -mainView.footer.btcInfo.connectingTo=povezivanje sa +mainView.footer.btcInfo.initializing=Connecting to Bitcoin network +mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO +mainView.footer.btcInfo.synchronizedWith=Synchronized with +mainView.footer.btcInfo.connectingTo=Connecting to mainView.footer.btcInfo.connectionFailed=veza nije uspostavljena mainView.footer.p2pInfo=Pirovi P2P mreže: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=Iznos bitkoina da se {0} createOffer.amountPriceBox.buy.volumeDescription=Iznos u {0} da se potroši createOffer.amountPriceBox.sell.volumeDescription=Iznos u {0} koji se dobija createOffer.amountPriceBox.minAmountDescription=Minimalni iznos BTC -createOffer.securityDeposit.prompt=Bezbednosni depozit u BTC. +createOffer.securityDeposit.prompt=Bezbednosni depozit createOffer.fundsBox.title=Finansirajte vašu ponudu createOffer.fundsBox.offerFee=Provizija prihvatanja createOffer.fundsBox.networkFee=Mining fee createOffer.fundsBox.placeOfferSpinnerInfo=Postavljanje ponude je u toku ... createOffer.fundsBox.paymentLabel=Bisq trgovina sa ID {0} createOffer.fundsBox.fundsStructure=({0} security deposit, {1} trade fee, {2} mining fee) +createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=Vaša ponuda je objavljena createOffer.success.info=Možete upravljati sa vašim otvorenim ponudama u \"Portfolio/Moje otvorene ponude". createOffer.info.sellAtMarketPrice=You will always sell at market price as the price of your offer will be continuously updated. @@ -387,6 +392,7 @@ createOffer.alreadyFunded=Već ste finansirali tu ponudu.\nVaša novčana sredst createOffer.createOfferFundWalletInfo.headline=Finansirajte vašu ponudu # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Iznos trgovine: {0}\n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} and {1} createOffer.createOfferFundWalletInfo.msg=Morate deponovati {0} ovoj ponudi.\n\nTa sredstva su rezervisana u vašem lokalnom novčaniku i biće zaključana u multisig depozit adresi kad neko prihvati ponudu.\n\nIznos je suma:\n{1}- Bezbednosnog depozita: {2}\n- Provizije trgovanja: {3}\n- Provizije rudara: {4}\n\nMožete birati izmedju dve opcije pri finansiranju vaše trgovine:\n- Koristite vaš Bisq novčanik (pogodno, ali transakcije možda mogu biti povezive) ILI\n- Prenesite iz vašeg eksternog novčanika (potencijalno veća privatnost)\n\nVidećete sve opcije finansiranja i detalje kad zatvorite ovaj popup. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=Cena koju ste uneli je van maks. dozvoljenog createOffer.changePrice=Izmeni cenu createOffer.tac=Sa objavljivanjem te ponude slažem se da trgujem sa bilo kojim trgovcem koji ispunjava uslove definisane na tom ekranu. createOffer.currencyForFee=Provizija pravljenja -createOffer.setDeposit=Set buyer's security deposit +createOffer.setDeposit=Set buyer's security deposit (%) +createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) +createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} #################################################################### @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=We'd love to hear back from you about your experie tradeFeedbackWindow.msg.part2=If you have any questions, or experienced any problems, please get in touch with other users and contributors via the Bisq forum at: tradeFeedbackWindow.msg.part3=Thanks for using Bisq! + +daoTestingFeedbackWindow.title=Thank you for testing the Bisq DAO +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Visit forum +daoTestingFeedbackWindow.msg.part3=Thanks for using Bisq! + portfolio.pending.role=Moja uloga portfolio.pending.tradeInformation=Informacije trgovine portfolio.pending.remainingTime=Preostalo vreme @@ -769,11 +786,11 @@ support.buyerTaker=BTC kupac/Uzimalac support.sellerTaker=BTC prodavac/Tvorac support.backgroundInfo=Bisq is not a company, so it handles disputes differently.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display an \"Open dispute\" button after the trade period is over for contacting the arbitrator.\n\nIf there is an issue with the application, the software will try to detect it and, if possible, display an \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIf you are having an issue and did not see the \"Open support ticket\" button, you can open a support ticket manually by selecting the trade causing issues under \"Portfolio/Open trades\" and hitting \"alt + o\" or \"option + o\". Please use this method only if you are sure that the software is not working as expected. If you have problems or questions, please review the FAQ on the bisq.network web page or post in the Bisq forum in the Support section. -support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrator's requests within 2 days.\n2. The maximum period for a dispute is 14 days.\n3. You need to cooperate with the arbitrator and provide the information they request to make your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=Poruka sistema: {0} -support.youOpenedTicket=Otvorili ste zahtev za podršku. -support.youOpenedDispute=You opened a request for a dispute.\n\n{0} -support.peerOpenedTicket=Vaš trgovinski partner je zatražio podršku zbog tehničkih problema. Molimo sačekajte dalje instrukcije. +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=Your trading peer has requested a dispute.\n\n{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=Potrebno je da imate bar jednog arbitra izabr account.altcoin.yourAltcoinAccounts=Your altcoin accounts account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe arbitrator is not a {2} specialist and cannot help in such cases. account.altcoin.popup.wallet.confirm=Razumem i potvrđujem da znam koji novčanik trebam da koristim. +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 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 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\nFor sending XMR, you need to use either the official Monero GUI wallet or Monero 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.\nmonero-wallet-cli (use the command get_tx_key)\nmonero-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nIn addition to XMR checktx tool (https://xmr.llcoins.net/checktx.html) verification can also be accomplished in-wallet.\nmonero-wallet-cli : using command (check_tx_key).\nmonero-wallet-gui : on the Advanced > Prove/Check page.\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 XMR sender is responsible for providing verification of the XMR 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 (https://www.getmonero.org/resources/user-guides/prove-payment.html) or the Monero forum (https://forum.getmonero.org) to find more information. 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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=Tokom korišćenja {0} možete koristiti samo transparentne adrese (počinju sa t) a ne z-adrese (privatne), jer arbitar ne bi bio u stanju da proveri transakciju sa z-adresom. -account.altcoin.popup.XZC.msg=When using {0} you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. -account.altcoin.popup.bch=Bitcoin Cash and Bitcoin Clashic suffer from replay protection. If you use those coins be sure you take sufficient precautions and understand all implications.You can suffer losses by sending one coin and unintentionally send the same coins on the other block chain.Because those "airdrop coins" share the same history with the Bitcoin blockchain there are also security risks and a considerable risk for losing privacy.\n\nPlease read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Because Bitcoin Gold shares the same history as the Bitcoin blockchain it comes with certain security risks and a considerable risk for losing privacy.If you use Bitcoin Gold be sure you take sufficient precautions and understand all implications.\n\nPlease read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.ZEC.msg=When using Zcash you can only use the transparent addresses (starting with t) not the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. +account.altcoin.popup.XZC.msg=When using Zcoin you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=Your national currency accounts @@ -961,8 +980,8 @@ account.seed.info=Please write down both wallet seed words and the date! You can account.seed.warn.noPw.msg=You have not setup a wallet password which would protect the display of the seed words.\n\nDo you want to display the seed words? account.seed.warn.noPw.yes=Da, ne pitaj me ponovo account.seed.enterPw=Unesite lozinku da bi videli sid reči -account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state. -account.seed.restore.ok=Ok, I understand and want to restore +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Ok, do the restore and shut down Bisq #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=The lower price must # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=BSQ novčanik dao.tab.proposals=Governance dao.tab.bonding=Bonding dao.tab.proofOfBurn=Asset listing fee/Proof of burn +dao.tab.monitor=Network monitor +dao.tab.news=News dao.paidWithBsq=paid with BSQ -dao.availableBsqBalance=Available -dao.availableNonBsqBalance=Available non-BSQ balance (BTC) -dao.unverifiedBsqBalance=Unverified (awaiting block confirmation) +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Balance of all verified UTXOs +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) dao.lockedForVoteBalance=Used for voting dao.lockedInBonds=Locked in bonds +dao.availableNonBsqBalance=Available non-BSQ balance (BTC) dao.totalBsqBalance=Ukupno BSQ stanje dao.tx.published.success=Vaša ponuda je uspešno objavljena. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Voting cycle overview dao.cycle.currentPhase=Current phase dao.cycle.currentBlockHeight=Current block height dao.cycle.proposal=Proposal phase +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=Blind vote phase dao.cycle.voteReveal=Vote reveal phase dao.cycle.voteResult=Vote result dao.cycle.phaseDuration={0} blocks (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=Cycles dao.results.cycles.table.header.cycle=Cycle @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Vote result dao.results.proposals.voting.detail.header=Vote results for selected proposal +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Undefined @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=Recipient BTC address # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=Asset listing fee per day # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. trade volume +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Current value: {0} dao.param.blocks={0} blocks -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Duration of {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} block(s) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(default value) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(has been changed in voting) +dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the P2P network.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Undefined # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Lockup dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Lockup dao.bond.table.button.unlock=Otključaj dao.bond.table.button.revoke=Revoke +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Not bonded yet # suppress inspection "UnusedProperty" @@ -1252,13 +1296,17 @@ dao.bond.bondState.UNLOCKED=Bond unlocked # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Bond confiscated +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Bonded reputation # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github admin +dao.bond.bondedRoleType.UNDEFINED=Undefined +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin # suppress inspection "UnusedProperty" @@ -1266,24 +1314,30 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Rocket chat admin # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Youtube admin +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq maintainer # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Website operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Forum operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Price node operator +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Btc node operator +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ explorer operator # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domain name holder # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS admin @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=DNS admin dao.bond.bondedRoleType.MEDIATOR=Mediator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Arbitrator +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=Asset listing fee +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=Asset listing fee dao.burnBsq.menuItem.proofOfBurn=Proof of burn dao.burnBsq.header=Fee for asset listing @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=Datum dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=Transakcije dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Sign a message with key from proof or burn transaction -dao.proofOfBurn.verify.window.title=Verify a message with key from proof or burn transaction +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction dao.proofOfBurn.copySig=Copy signature to clipboard dao.proofOfBurn.sign=Sign dao.proofOfBurn.message=Message dao.proofOfBurn.sig=Signature dao.proofOfBurn.verify=Verify -dao.proofOfBurn.verify.header=Verify message with key from proof or burn transaction +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=Verification succeeded dao.proofOfBurn.verificationResult.failed=Verification failed @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Vote reveal # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Vote result +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Zahtev za nadoknadu # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=Generic proposal # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Proposal for confiscating a bond +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Zahtev za nadoknadu # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=Generic proposal # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Confiscating a bond - dao.proposal.details=Proposal details dao.proposal.selectedProposal=Izabrani zahtevi za nadoknadu dao.proposal.active.header=Proposals of current cycle dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal?\nThe already paid proposal fee will be lost. dao.proposal.active.remove.doRemove=Yes, remove my proposal dao.proposal.active.remove.failed=Could not remove proposal. +dao.proposal.myVote.title=Glasanje dao.proposal.myVote.accept=Accept proposal dao.proposal.myVote.reject=Reject proposal dao.proposal.myVote.removeMyVote=Ignore proposal dao.proposal.myVote.merit=Vote weight from earned BSQ dao.proposal.myVote.stake=Vote weight from stake -dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID dao.proposal.myVote.revealTxId=Vote reveal transaction ID -dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0} -dao.proposal.votes.header=Vote on all proposals -dao.proposal.votes.header.voted=My vote -dao.proposal.myVote.button=Vote on all proposals +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Select proposal type +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=Proposal type -dao.proposal.create.createNew=Napravi novi zahtev za nadoknadu -dao.proposal.create.create.button=Napravi zahtev za nadoknadu +dao.proposal.create.new=Napravi novi zahtev za nadoknadu +dao.proposal.create.button=Napravi zahtev +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=proposal dao.proposal.display.type=Proposal type dao.proposal.display.name=Name/nickname dao.proposal.display.link=Link to detail info -dao.proposal.display.link.prompt=Link to Github issue +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=Requested amount in BSQ dao.proposal.display.bsqAddress=BSQ address dao.proposal.display.txId=Proposal transaction ID @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=Accepted dao.proposal.display.myVote.rejected=Rejected dao.proposal.display.myVote.ignored=Ignored dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}); +dao.proposal.myVote.invalid=Vote was invalid dao.proposal.voteResult.success=Accepted dao.proposal.voteResult.failed=Rejected @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=Asset to remove dao.blindVote=blind vote dao.blindVote.startPublishing=Publishing blind vote transaction... -dao.blindVote.success=Your blind vote has been successfully published. +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=Pošalji dao.wallet.menuItem.receive=Primi dao.wallet.menuItem.transactions=Transakcije dao.wallet.dashboard.myBalance=My wallet balance -dao.wallet.dashboard.distribution=Distribution of all BSQ -dao.wallet.dashboard.locked=Global state of locked BSQ -dao.wallet.dashboard.market=Market data -dao.wallet.dashboard.genesis=Transakcija postanka -dao.wallet.dashboard.txDetails=BSQ transactions statistics -dao.wallet.dashboard.genesisBlockHeight=Genesis block height -dao.wallet.dashboard.genesisTxId=Genesis transaction ID -dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction -dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests -dao.wallet.dashboard.reimbursementAmount=BSQ issued for reimbursement requests -dao.wallet.dashboard.availableAmount=Total available BSQ -dao.wallet.dashboard.burntAmount=Burned BSQ (fees) -dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds -dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds -dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds -dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds -dao.wallet.dashboard.allTx=No. of all BSQ transactions -dao.wallet.dashboard.utxo=No. of all unspent transaction outputs -dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions -dao.wallet.dashboard.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions -dao.wallet.dashboard.burntTx=No. of all fee payments transactions -dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq) -dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price) - -dao.wallet.receive.fundYourWallet=Finansirajte vaš BSQ novčanik -dao.wallet.receive.bsqAddress=BSQ wallet address + +dao.wallet.receive.fundYourWallet=Your BSQ receive address +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=The Bisq DAO +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=Pošalji sredstva dao.wallet.send.sendBtcFunds=Send non-BSQ funds (BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=Latest verified block: {0} dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1} dao.wallet.tx.type=Tip +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Not recognized # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=Compensation request/issuance dao.tx.issuanceFromCompReq.tooltip=Compensation request which led to an issuance of new BSQ.\nIssuance date: {0} dao.tx.issuanceFromReimbursement=Reimbursement request/issuance dao.tx.issuanceFromReimbursement.tooltip=Reimbursement request which led to an issuance of new BSQ.\nIssuance date: {0} -dao.proposal.create.missingBsqFunds=You don''t have sufficient funds for creating the proposal.\nMissing: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=Confirm {0} transaction dao.feeTx.confirm.details={0} fee: {1}\nMining fee: {2} ({3} Satoshis/byte)\nTransaction size: {4} Kb\n\nAre you sure you want to publish the {5} transaction? +dao.news.bisqDAO.title=THE BISQ DAO +dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. +dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO + +dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ +dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.news.pastContribution.yourAddress=Your BSQ Wallet Address +dao.news.pastContribution.requestNow=Request now + +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. +dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ +dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. +dao.news.DAOOnTestnet.readMoreLink=Read the full documentation + +dao.monitor.daoState=DAO state +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Conflicts +dao.monitor.state=Status +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=DAO state +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Block height +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Previous hash +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Previous hash +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Previous hash +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Market data +dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq) +dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price) +dao.factsAndFigures.dashboard.availableAmount=Total available BSQ + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests +dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Global state of locked BSQ +dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds +dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds +dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds +dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds +dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees) + +dao.factsAndFigures.transactions.genesis=Transakcija postanka +dao.factsAndFigures.transactions.genesisBlockHeight=Genesis block height +dao.factsAndFigures.transactions.genesisTxId=Genesis transaction ID +dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics +dao.factsAndFigures.transactions.allTx=No. of all BSQ transactions +dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs +dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions +dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions +dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Filtered seed nodes (comma sep. onion addresses) filterWindow.priceRelayNode=Filtered price relay nodes (comma sep. onion addresses) filterWindow.btcNode=Filtered Bitcoin nodes (comma sep. addresses + port) filterWindow.preventPublicBtcNetwork=Prevent usage of public Bitcoin network +filterWindow.disableDao=Disable DAO filterWindow.add=Dodaj filter filterWindow.remove=Ukloni filter @@ -1955,6 +2103,10 @@ BTC_MAINNET=Bitkoin Mejnnet BTC_TESTNET=Bitkoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitkoin Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=Godina time.month=Mesec @@ -2025,6 +2177,8 @@ payment.country=Država payment.extras=Extra requirements payment.email.mobile=Email or mobile no. payment.altcoin.address=Altcoin address +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=Altcoin payment.select.altcoin=Izaberi ili traži altkoin payment.secret=Secret question @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=Email or phone no. payment.venmo.venmoUserName=Venmo username payment.popmoney.accountId=Email or phone no. -payment.revolut.email=Email or phone no. +payment.revolut.email=Email +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=Citizen ID/Tax ID or phone no. payment.supportedCurrencies=Supported currencies payment.limitations=Limitations @@ -2082,6 +2237,10 @@ payment.limits.info=Please be aware that all bank transfers carry a certain amou payment.cashDeposit.info=Please confirm your bank allows you to send cash deposits into other peoples' accounts. For example, Bank of America and Wells Fargo no longer allow such deposits. +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=Contact info payment.f2f.contact.prompt=How you want to get contacted by the trading peer? (email address, phone number,...) payment.f2f.city=City for 'Face to face' meeting @@ -2128,16 +2287,10 @@ F2F_SHORT=F2F # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altkoini PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Altkoini PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} mora da se sastoji od {1} brojeva. validation.invalidInput=Nevažeći unos: {0} validation.accountNrFormat=Broj računa mora biti formata: {0} validation.altcoin.wrongStructure=Provera adrese nije uspela zato što ne odgovara strukturi {0} adrese. +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=ZEC adresa mora da počinje sa t. Adrese koje počinju sa z nisu podržane. validation.altcoin.invalidAddress=Adresa nije važeća {0} adresa! {1} validation.bic.invalidLength=Dužina unosa nije ni 8 ni 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Must contain only letters, numbers, validation.interacETransfer.invalidAnswer=Must be one word and contain only letters, numbers, and/or the symbol - validation.inputTooLarge=Input must not be larger than {0} validation.inputTooSmall=Input has to be larger than {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=The amount below the dust limit of {0} is not allowed. validation.length=Length must be between {0} and {1} validation.pattern=Input must be of format: {0} validation.noHexString=The input is not in HEX format. validation.advancedCash.invalidFormat=Must be a valid email or wallet id of format: X000000000000 +validation.invalidUrl=This is not a valid URL +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index eec8134b032..b9d0edb93cd 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -104,7 +104,7 @@ shared.belowInPercent=ต่ำกว่า % จากราคาตลาด shared.aboveInPercent=สูงกว่า % จากราคาตาด shared.enterPercentageValue=เข้าสู่ % ตามมูลค่า shared.OR=หรือ -shared.notEnoughFunds=คุณมีเงินไม่เพียงพอใน Bisq wallet \nคุณต้องมี {0} แต่คุณมีเพียง {1} ใน Bisq wallet ของคุณ\n\nโปรดโอนเงินจากการ Bitcoin wallet ภายนอกหรือโอนเงินเข้า Bisq wallet ของคุณที่ \"เงิน / รับเงิน \" +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.waitingForFunds=กำลังรอเงิน ... shared.depositTransactionId=รหัสธุรกรรมการฝากเงิน (transaction ID) shared.TheBTCBuyer=ผู้ซื้อ BTC @@ -113,12 +113,13 @@ shared.reasonForPayment=เหตุผลในการชำระเงิ shared.sendingConfirmation=กำลังส่งการยืนยัน ... shared.sendingConfirmationAgain=โปรดยืนยันการส่งอีกครั้ง shared.exportCSV=ส่งไปที่ csv +shared.exportJSON=Export to JSON shared.noDateAvailable=ไม่มีวันที่ให้แสดง shared.arbitratorsFee=ค่าธรรมเนียมสำหรับการอนุญาโตตุลาการหรือการเจรจาในการไกล่เกลี่ย shared.noDetailsAvailable=ไม่มีรายละเอียด shared.notUsedYet=ยังไม่ได้ใช้งาน shared.date=วันที่ -shared.sendFundsDetailsWithFee=กำลังส่ง: {0} \nจากที่อยู่: {1} \nไปยังที่อยู่คนรับ: {2} .\nค่าธรรมเนียมการทำธุรกรรมที่จำเป็นคือ: {3} ({4} satoshis / ไบต์) \nขนาดของธุรกรรม: {5} Kb\n\nผู้รับจะได้รับ: {6} \n\nคุณแน่ใจหรือไม่ว่าคุณต้องการถอนจำนวนเงินดังกล่าว +shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired transaction 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.copyToClipboard=คัดลอกไปที่คลิปบอร์ด shared.language=ภาษา shared.country=ประเทศ @@ -196,6 +197,8 @@ shared.interval=ระยะห่าง shared.actions=การปฏิบัติการ shared.buyerUpperCase=ผู้ซื้อ shared.sellerUpperCase=ผู้ขาย +shared.new=NEW +shared.blindVoteTxId=ID การทำธุรกรรมการลงคะแนนเสียงแบบไม่ระบุตัวตน #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=ถูกล็อคไว้ mainView.footer.usingTor=(ใช้งาน Tor) mainView.footer.localhostBitcoinNode=(แม่ข่ายเฉพาะที่) mainView.footer.btcInfo=Bitcoin network peers (เครือข่ายแบบเพียร์): {0} / {1} {2} -mainView.footer.btcInfo.initializing=เริ่มต้น -mainView.footer.btcInfo.synchronizedWith=ตรงกับ -mainView.footer.btcInfo.connectingTo=เชื่อมต่อไปยัง +mainView.footer.btcInfo.initializing=Connecting to Bitcoin network +mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO +mainView.footer.btcInfo.synchronizedWith=Synchronized with +mainView.footer.btcInfo.connectingTo=Connecting to mainView.footer.btcInfo.connectionFailed=การเชื่อมต่อล้มเหลว mainView.footer.p2pInfo=P2P เครือข่ายแบบเพียร์: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=ยอดจำนวน BTC ถึ createOffer.amountPriceBox.buy.volumeDescription=ยอดจำนวน {0} ที่ต้องจ่าย createOffer.amountPriceBox.sell.volumeDescription=จำนวนเงิน {0} ที่ได้รับ createOffer.amountPriceBox.minAmountDescription=จำนวนเงินขั้นต่ำของ BTC -createOffer.securityDeposit.prompt=เงินประกันใน BTC +createOffer.securityDeposit.prompt=เงินประกัน createOffer.fundsBox.title=เงินทุนสำหรับข้อเสนอของคุณ createOffer.fundsBox.offerFee=ค่าธรรมเนียมการซื้อขาย createOffer.fundsBox.networkFee=ค่าธรรมเนียมการขุด createOffer.fundsBox.placeOfferSpinnerInfo=การประกาศข้อเสนออยู่ระหว่างดำเนินการ ... createOffer.fundsBox.paymentLabel=การซื้อขาย Bisq ด้วย ID {0} createOffer.fundsBox.fundsStructure=({0} เงินประกัน {1} ค่าธรรมเนียมการซื้อขาย {2} ค่าธรรมเนียมการขุด) +createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=ข้อเสนอของคุณได้รับการเผยแพร่แล้ว createOffer.success.info=คุณสามารถจัดการข้อเสนอแบบเปิดของคุณได้ที่ \"Portfolio (แฟ้มผลงาน) / My open offers (ข้อเสนอแบบเปิดของฉัน) \" createOffer.info.sellAtMarketPrice=คุณจะขายในราคาตลาดเสมอ เนื่องจากราคาข้อเสนอของคุณจะได้รับการอัพเดตอย่างต่อเนื่อง @@ -387,6 +392,7 @@ createOffer.alreadyFunded=คุณได้รับเงินจากข้ createOffer.createOfferFundWalletInfo.headline=เงินทุนสำหรับข้อเสนอของคุณ # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- ปริมาณการซื้อขาย: {0} +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} and {1} createOffer.createOfferFundWalletInfo.msg=คุณต้องวางเงินมัดจำ {0} ข้อเสนอนี้\n\nเงินเหล่านั้นจะถูกสงวนไว้ใน wallet ภายในประเทศของคุณและจะถูกล็อคไว้ในที่อยู่ที่ฝากเงิน multisig เมื่อมีคนรับข้อเสนอของคุณ\n\nผลรวมของจำนวนของ: \n{1} - เงินประกันของคุณ: {2} \n- ค่าธรรมเนียมการซื้อขาย: {3} \n- ค่าขุด: {4} \n\nคุณสามารถเลือกระหว่างสองตัวเลือกเมื่อมีการระดุมทุนการซื้อขายของคุณ: \n- ใช้กระเป๋าสตางค์ Bisq ของคุณ (สะดวก แต่ธุรกรรมอาจเชื่อมโยงกันได้) หรือ\n- โอนเงินจากเงินภายนอกเข้ามา (อาจเป็นส่วนตัวมากขึ้น) \n\nคุณจะเห็นตัวเลือกและรายละเอียดการระดมทุนทั้งหมดหลังจากปิดป๊อปอัปนี้ # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=ราคาที่คุณป้อน createOffer.changePrice=เปลี่ยนราคา createOffer.tac=ด้วยการเผยแพร่ข้อเสนอพิเศษนี้ ฉันยอมรับการซื้อขายกับผู้ค้ารายย่อยที่ปฏิบัติตามเงื่อนไขที่กำหนดไว้บนหน้าจอนี้ createOffer.currencyForFee=ค่าธรรมเนียมการซื้อขาย -createOffer.setDeposit=ตั้งค่าเงินประกันของผู้ซื้อ +createOffer.setDeposit=Set buyer's security deposit (%) +createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) +createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} #################################################################### @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=เรายินดีที่จะฟัง tradeFeedbackWindow.msg.part2=หากคุณมีข้อสงสัยหรือประสบปัญหาใด ๆ โปรดติดต่อกับผู้ใช้และผู้สนับสนุนคนอื่น ๆ ผ่านทางฟอรัม Bisq ที่: tradeFeedbackWindow.msg.part3=ขอบคุณที่ใช้ Bisq! + +daoTestingFeedbackWindow.title=Thank you for testing the Bisq DAO +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Visit forum +daoTestingFeedbackWindow.msg.part3=ขอบคุณที่ใช้ Bisq! + portfolio.pending.role=บทบาทของฉัน portfolio.pending.tradeInformation=ข้อมูลทางการซื้อขาย portfolio.pending.remainingTime=เวลาที่เหลือ @@ -705,10 +722,10 @@ funds.reserved.reserved=สำรองใน wallet ท้องถิ่นเ funds.locked.noFunds=ไม่มีเงินถูกล็อคในการซื้อขาย funds.locked.locked=ถูกล็อคใน multisig สำหรับการซื้อขายด้วย ID: {0} -funds.tx.direction.sentTo=ส่งไปยัง: +funds.tx.direction.sentTo=ส่งไปยัง: funds.tx.direction.receivedWith=ได้รับโดย: funds.tx.direction.genesisTx=จากการทำธุรกรรมทั่วไป : -funds.tx.txFeePaymentForBsqTx=ค่าธรรมเนียมของนักขุดบิทคอยน์สำหรับการทำธุรกรรม BSQ +funds.tx.txFeePaymentForBsqTx=ค่าธรรมเนียมของนักขุดบิทคอยน์สำหรับการทำธุรกรรม BSQ funds.tx.createOfferFee=ผู้สร้างและค่าธรรมเนียมการทำธุรกรรม: {0} funds.tx.takeOfferFee=ค่าธรรมเนียมของผู้รับและการทำธุรกรรม: {0} funds.tx.multiSigDeposit=เงินฝาก Multisig (การรองรับหลายลายเซ็น): {0} @@ -769,11 +786,11 @@ support.buyerTaker=BTC ผู้ซื้อ / ผู้รับ support.sellerTaker=BTC ผู้ขาย / ผู้รับ support.backgroundInfo=Bisq ไม่ใช่ บริษัท และไม่ได้รับดำเนินการใด ๆ ของการสนับสนุนและช่วยเหลือลูกค้า\n\nหากมีข้อพิพาทในกระบวนการทางการค้ซื้อขาย (เช่น ผู้ค้ารายหนึ่งไม่ปฏิบัติตามโปรโตคอลทางการค้า) แอปพลิเคชันจะแสดงปุ่ม \"เปิดข้อโต้แย้ง \" หลังจากสิ้นสุดระยะเวลาการค้าสำหรับการติดต่อผู้ไกล่เกลี่ย\nในกรณีที่มีข้อบกพร่องของซอฟต์แวร์หรือปัญหาอื่น ๆ ที่ตรวจพบ โดยโปรแกรมจะแสดงปุ่ม \"เปิดการช่วยเหลือและสนับสนุน \" เพื่อติดต่อกับผู้ไกล่เกลี่ย\n\nในกรณีที่ผู้ใช้มีข้อบกพร่องโดยไม่มีการแสดงปุ่ม \"เปิดการช่วยเหลือและสนับสนุน \" คุณสามารถเปิดปุ่มสนับสนุนด้วยตนเองโดยใช้ปุ่มลัดพิเศษ\n\nโปรดใช้เฉพาะในกรณีที่คุณมั่นใจว่าซอฟต์แวร์ไม่ทำงานอย่างที่คาดไว้ หากคุณมีปัญหาในการใช้ Bisq หรือคำถามใด ๆ โปรดอ่าน FAQ ที่หน้าเว็บ bisq.network หรือโพสต์ในฟอรัม Bisq ที่ส่วนการสนับสนุนและช่วยเหลือ\n\nหากคุณแน่ใจว่าต้องการเปิดปุ่มช่วยเหลือและสนับสนุน โปรดเลือกการซื้อขายซึ่งเป็นสาเหตุของปัญหาภายใต้ \"แฟ้มผลงาน/ เปิดการซื้อขาย \" และพิมพ์คีย์ผสม \"alt + o \" หรือ \"option + o \" เพื่อเปิดปุ่มช่วยเหลือและสนับสนุน -support.initialInfo=โปรดทราบกฎพื้นฐานสำหรับกระบวนการพิพาท: \n1. คุณต้องตอบคำขอผู้ไกล่เกลี่ยภายใน 2 วัน\n2. ระยะเวลาสูงสุดในการพิพาทคือ 14 วัน\n3. คุณต้องปฏิบัติตามสิ่งที่ผู้ไกล่เกลี่ยขอจากคุณเพื่อให้หลักฐานสำหรับปัญหาข้อพิพาทของคุณ\n4. คุณได้ยอมรับกฎที่ระบุไว้ใน wiki ในข้อตกลงของผู้ใช้เมื่อคุณเริ่มใช้งานแอพพลิเคชัน\n\nโปรดอ่านรายละเอียดเพิ่มเติมเกี่ยวกับกระบวนการพิพาทในวิกิพีเดียของเรา: \nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=ระบบข้อความ: {0} -support.youOpenedTicket=คุณได้เปิดคำขอการสนับสนุนแล้ว -support.youOpenedDispute=คุณเปิดคำขอให้มีการพิพาท\n\n{0} -support.peerOpenedTicket=คู่ค้าของคุณได้ร้องขอการสนับสนุนและความช่วยเหลือเนื่องจากปัญหาทางเทคนิค โปรดรอสำหรับคำแนะนำเพิ่มเติม +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=คู่ค้าของคุณได้ร้องขอข้อพิพาท\n\n{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=คุณต้องมีผู้ไกล account.altcoin.yourAltcoinAccounts=บัญชี altcoin (เหรียญทางเลือก) ของคุณ account.altcoin.popup.wallet.msg=โปรดตรวจสอบว่าคุณทำตามข้อกำหนดสำหรับการใช้ {0} wallet ตามที่อธิบายไว้ใน {1} หน้าเว็บเพจ\nการใช้ wallet จากการแลกเปลี่ยนแบบส่วนกลางที่คุณไม่ได้รับคีย์ภายใต้การควบคุมของคุณ หรือ ใช้ซอฟต์แวร์ wallet ที่ไม่สามารถใช้งานร่วมกันได้อาจทำให้สูญเสียเงินได้!\nผู้ไกล่เกลี่ยไม่ได้เป็น {2} ผู้เชี่ยวชาญและไม่สามารถช่วยในกรณีดังกล่าวได้ account.altcoin.popup.wallet.confirm=ฉันเข้าใจและยืนยันว่าฉันรู้ว่า wallet ใดที่ฉันต้องการใช้ +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 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 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=การซื้อขาย XMR บน Bisq ประสงค์ให้คุณเข้าใจและปฏิบัติตามข้อกำหนดดังต่อไปนี้:\n\nสำหรับการส่ง XMR, คุณจำเป็นต้องใช้กระเป๋าสตางค์ทางการ Monero GUI wallet หรือกระเป๋าสตางค์ Monero CLI ด้วย store-tx-info flag (ตั้งค่าในเวอร์ชั่นใหม่). โปรดมั่นใจว่าคุณสามารถเข้าถึงคีย์ธุรกรรมในกรณีเกิดเหตุการณ์ข้อพิพาท\nmonero-wallet-cli (ใข้คำสั่ง get_tx_key)\nmonero-wallet-gui (ไปที่หน้าประวัติและคลิกบนปุ่ม (P) สำหรับหลักฐานการชำระเงิน)\n\nนอกจากนี้ การใช้วิธีการทำธุรกรรม XMR ในการพิสูจน์หลักฐาน (https://xmr.llcoins.net/checktx.html) ยังสามารถทำด้วยวิธีกระเป๋าสตางค์ภายใน in-wallet ได้เช่นกัน\nmonero-wallet-cli : กำลังใช้คำสั่ง (check_tx_key).\nmonero-wallet-gui : บน the Advanced > หน้าพิสูจน์/ตรวจสอบ\nสำหรับผู้สำรวจบล็อกทั่วไป การโอนเงินไม่สามารถพิสูจน์หลักฐานได้ \n\nคุณสามารถนำข้อมูลดังต่อไปนี้ยื่นให้แก่ผู้ไกล่เกลี่ยในกรณีข้อพิพาทได้:\n- คีย์ส่วนตัวของการทำธุรกรรม\n- แฮชของธุรกรรม\n- ที่อยู่สาธารณะของผู้รับ\n\nความล้มเหลวในการยื่นข้อมูลข้างต้น หรือเมื่อคุณใช้กระเป๋าสตางค์ที่ไม่เหมาะสมจะส่งผลให้คุณสูญเสียกรณีการพิพาทได้ ผู้ส่ง XMR มีหน้าที่ยื่นหลักฐานพิสูจน์การโอนเงิน XMR ให้กับผู้ไกล่เกลี่ยเมื่อมีกรณีข้อพิพาทเกิดขึ้น\n\nไม่จำเป็นต้องมี ID การชำระเงิน เพียงแค่ที่อยู่สาธารณะตามปกติก็สามารถดำเนินการได้หากคุณไม่แน่ใจเกี่ยวกับกระบวนการดังกล่าวโปรดไปที่ฟอรัม (https://www.getmonero.org/resources/user-guides/prove-payment.html) หรือฟอรัม Monero (https://forum.getmonero.org) เพื่อค้นหาข้อมูลเพิ่มเติม account.altcoin.popup.blur.msg=หากคุณต้องการที่จะเทรด BLUR บน Bisq โปรดมั่นใจว่าคุณเข้าใจและทำตามข้อกำหนดดังกล่าว:ในการส่ง BLUR คุณต้องใช้ Blur Network CLI or GUI Wallet. หากคุณกำลังใช้งาน CLI wallet, แฮชธุรกรรม (tx ID) จะมีการแสดงผลหลังจากการโอนย้ายได้ถูกส่งแล้ว คุณต้องบันทึกข้อมูลนี้ไว้ และเมื่อคุณมีการโอนย้าย ณ เดี๋ยวนั้น คุณต้องใช้คำสั่ง 'get_tx_key' ในการเรียกคืนการทำธุรกรรมคีย์ส่วนตัว หากคุณไม่ได้ปฏิบัติในขั้นตอนนี้ คุณอาจไม่สามารถเรียกคืนข้อมูลดังกล่าวในภายหลังได้หากคุณกำลังใช้งาน Blur Network GUI Wallet, ธุรกรรม คีย์ส่วนตัวและธุรกรรม ID สามารถถูกค้นหาได้ง่ายในหน้า "ประวัติ" หลังจากที่ส่ง ณ เดี๋ยวนั้น ระบุตำแหน่งรายการที่น่าสนใจ คลิกสัญลักษณ์ "?" ในมุมขวาด้านล่างของกล่องที่มีรายการอยู่ และคุณต้องทำการบันทึกข้อมูลนี้ไว้ในรายการที่จำเป็นต้องมีการไกล่เกลี่ย คุณต้องเสนอรายการดังต่อไปนี้ต่อผู้ไกล่เกลี่ย: 1.) ID การทำธุรกรรม 2.) คีย์ส่วนตัวในการทำธุรกรรม และ 3.) ที่อยู่ของผู้รับ ผู้ไกล่เกลี่ยจะมีการตรวจสอบข้อมูลการโอนถ่ายโดยการใช้งาน Blur Transaction Viewer (https://blur.cash/#tx-viewer) หากคุณไม่สามารถปฏิบัติตามข้อกำหนดต่อผู้ไกล่เกลี่ย คุณอาจไม่สามารถชนะข้อพิพาทได้ ในกรณีเคสของการพิพาททั้งหมด ผู้ส่ง BLUR สามารถค้ำประกันให้ได้ถึง 100% ในการพิสูจน์หลักฐานทางธุรกรรมให้กับผู้ไกล่เกลี่ยหากคุณไม่เข้าใจในเรื่องข้อควรปฏิบัติเหล่านี้ ได้โปรดอย่าทำการเทรดบน Bisq อันดับแรก คุณสามารถรขอความช่วยเหลือได้ทาง Blur Network Discord (https://discord.gg/dMWaqVW). -account.altcoin.popup.ccx.msg=การค้า CCX บน Bisq ประสงค์ให้คุณเข้าใจและปฏิบัติตามข้อกำหนดดังต่อไปนี้:\n\nในการส่ง CCX คุณต้องใช้กระเป๋าสตางค์ทางการ Conceal หรือ CLI or GUI หลังจากการส่งการชำระเงินแล้ว กระเป๋าสตางค์\nแสดงให้เห็นคีย์ลับธุรกรรม คุณต้องใช้มันพร้อมกันแฮชธุรกรรม (ID) และที่อยู่สาธารณะของผู้รับ\nในกรณีเมื่อมีอนุญาโตตุลาการมาเกี่ยวข้อง ซึ่งคุณจะต้องใช้ข้อมูลทั้งสามอย่างแก่ผู้ไกล่เกลี่ยผู้ซึ่ง\nพิสูจน์การโอนเงิน CCX ภายหลังโดยการใช้การตรวจสอบ Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nเนื่องจาก Conceal เป็นเหรียญส่วนตัว ผู้สำรวจบล็อกไม่สามารถพิสูจน์หลักฐานการโอนเงินได้\n\nหากไม่สามารถยื่นข้อมูลดังกล่าวต่อผู้ไกล่เกลี่ยจะส่งผลต่อการสูญเสียการชนะกรณีการพิพาท\nหากคุณไม่สามารถบันทึกคีย์ลับธุรกรรมในทันทีหลังจากการโอนถ่าย CCX มันจะไม่สามารถกู้คืนข้อมูลได้ในภายหลัง\nหากคุณไม่เข้าใจข้อกำหนดเหล่านี้ สามารถขอความช่วยเหลือได้ที่ Conceal discord (http://discord.conceal.network). +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=การค้า Dragonglass บน Bisq ประสงค์ให้คุณเข้าใจและปฏิบัติตามข้อกำหนดดังต่อไปนี้:\n\nเนื่องจากความเป็นส่วนตัวของ Dragonglass ธุรกรรมจะไม่สามารถพิสูจน์หลักฐานได้บนบล็อกเชนสาธารณะ หากแต่มีความประสงค์ คุณสามารถตรวจสอบหลักฐานการชำระเงินของคุณได้โดยการใช้คีย์ส่วนตัว TXN\nคีย์ส่วนตัว TXN เป็นคีย์แบบใช้ครั้งเดียวอัตโนมัติที่สร้างขึ้นสำหรับทุกการทำธุรกรรมที่มีการเข้าใช้งานจากกระเป๋าสตางค์ DRGL ของคุณ\nไม่ว่าจะเป็นกระเป๋าสตางค์ DRGL-wallet GUI (ไดอาล็อกรายละเอียดการธุรกรรมที่อยู่ภายใน) หรือโดยกระเป๋าสตางค์แบบปกติอย่าง Dragonglass CLI (ใช้คำสั่งเดียวกัน "get_tx_key").\n\nเวอร์ชั่น DRGL 'Oathkeeper' และแบบขั้นกว่ามีความจำเป็นอย่างมาก\n\nในกรณีข้อพิพาท คุณต้องยื่นข้อมูลดังต่อไปนี้ต่อผู้ไกล่เกลี่ย:\n- คีย์ส่วนตัว TXN\n- แฮชธุรกรรม\n- ที่อยู่สาธารณะของผู้รับ\nการพิสูจน์หลักฐานการชำระเงินจะมีผลสมบูรณ์ได้โดยการใข้ข้อมูลข้างต้นในการป้องข้อมูลเข้าที่ (http://drgl.info/#check_txn).\n\nความล้มเหลวในการยื่นข้อมูลข้างตน หรือการใช้กระเป๋าสตางค์ที่ไม่เหมาะสม จะส่งผลให้คุณสูญเสียการชนะการพิพาทได้ ผู้ส่ง Dragonglass มีหน้าที่แสดงหลักฐานของการโอนเงิน DRGL ต่อผู้ไกล่เกลี่ยในกรณีเกิดข้อพิพาม ไม่เป็นต้องใช้งานการชำเงินแบบ PaymentID \n\nหากคุณมีคำถามหรือข้อสงสัยวิธีการการใช้งาน สามารถขอความช่วยเหลือได้ที่ Dragonglass on Discord (http://discord.drgl.info) -account.altcoin.popup.ZEC.msg=เมื่อใช้ {0} คุณสามารถใช้ที่อยู่ที่ถูกทำให้มองไม่เห็น (เริ่มต้นด้วย t) ไม่ใช่ที่อยู่ z (ส่วนตัว) เนื่องจากผู้ไกล่เกลี่ยจะไม่สามารถตรวจสอบธุรกรรมกับที่อยู่ z ได้ -account.altcoin.popup.XZC.msg=เมื่อใช้ {0} คุณสามารถใช้ที่อยู่ที่ถูกทำให้มองเห็น (สามารถตรวจสอบย้อนกลับได้) ไม่ใช่ที่อยู่ที่ไม่สามารถระบุได้เนื่องจากผู้ไกล่เกลี่ยจะไม่สามารถตรวจสอบการทำรายการกับที่อยู่ที่ไม่สามารถเข้าถึงได้ที่ block explorer (นักสำรวจบล็อก) -account.altcoin.popup.bch=Bitcoin Cash และ Bitcoin Clashic ได้รับความเสียหายจากการป้องกันการเล่นซ้ำ หากคุณใช้เหรียญเหล่านี้ ให้แน่ใจว่าคุณใช้ความระมัดระวังเพียงพอและเข้าใจผลกระทบทั้งหมด คุณอาจเผชิญกับความสูญเสียโดยการส่งเหรียญหนึ่งเหรียญโดยไม่ได้ตั้งใจและส่งเหรียญเดียวกันในห่วงโซ่อุปทานอื่น ๆ เนื่องจากเหล่า "airdrop coins" ใช้ฐานประวัติเดียวกันกับ blockchain Bitcoin นอกจากนี้ยังมีความเสี่ยงด้านความปลอดภัยและความเสี่ยงที่จะสูญเสียความเป็นส่วนตัวมากขึ้น\n\nโปรดอ่านที่ Bisq Forum เพิ่มเติมเกี่ยวกับหัวข้อ: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=เนื่องจาก Bitcoin Gold ใช้ประวัติเดียวกับ the Bitcoin blockchain จึงมีความเสี่ยงด้านความปลอดภัยและความเสี่ยงในการสูญเสียความเป็นส่วนตัวหากคุณใช้ Bitcoin Gold จงแน่ใจว่าคุณใช้งานด้วยความระมัดระวังเพียงพอและเข้าใจความหมายทั้งหมด\n\nโปรดอ่านที่ Bisq Forum เพิ่มเติมเกี่ยวกับหัวข้อ: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.ZEC.msg=When using Zcash you can only use the transparent addresses (starting with t) not the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. +account.altcoin.popup.XZC.msg=When using Zcoin you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=บัญชีสกุลเงินของคุณ @@ -961,8 +980,8 @@ account.seed.info=โปรดเขียนรหัสสำรองข้ account.seed.warn.noPw.msg=คุณยังไม่ได้ตั้งรหัสผ่าน wallet ซึ่งจะช่วยป้องกันการแสดงผลของรหัสสำรองข้อมูล wallet \n\nคุณต้องการแสดงรหัสสำรองข้อมูล wallet หรือไม่ account.seed.warn.noPw.yes=ใช่ และไม่ต้องถามฉันอีก account.seed.enterPw=ป้อนรหัสผ่านเพื่อดูรหัสสำรองข้อมูล wallet -account.seed.restore.info=โปรดสร้างตัวสำรองข้อมูลก่อนกระทำการกู้ Seed words ควรพึงระวังว่าการกู้คืนจากกระเป๋าสตางค์ใช้ในกรณีฉุกเฉินเท่านั้น และอาจทำให้เกิดปัญหาหลายประการกับฐานข้อมูลกระเป๋าสตางค์ภายใน -account.seed.restore.ok=ตกลง ฉันเข้าใจและต้องการกู้คืนข้อมูล +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Ok, do the restore and shut down Bisq #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=ราคาที่ # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=กระเป๋าสตางค์ BSQ dao.tab.proposals=การกำกับดูแลกิจการ dao.tab.bonding=ค้ำประกัน -dao.tab.proofOfBurn=ค่าธรรมเนียมในการลงบันทึกรายการทรัพย์สิน/หลักฐานในการทำลายทิ้ง +dao.tab.proofOfBurn=ค่าธรรมเนียมในการลงบันทึกรายการทรัพย์สิน/หลักฐานในการทำลายทิ้ง +dao.tab.monitor=Network monitor +dao.tab.news=News dao.paidWithBsq=จ่ายโดย BSQ -dao.availableBsqBalance=พร้อมใช้งาน -dao.availableNonBsqBalance=ยอดคงเหลือที่ไม่ใช่ BSQ (BTC) ซึ่งใช้งานได้ -dao.unverifiedBsqBalance=ไม่ได้รับการตรวจสอบ (รอการยืนยันการบล็อก) +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Balance of all verified UTXOs +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) dao.lockedForVoteBalance=ถูกใช้สำหรับการโหวต dao.lockedInBonds=ถูกล็อคไว้ในการค้ำประกัน +dao.availableNonBsqBalance=ยอดคงเหลือที่ไม่ใช่ BSQ (BTC) ซึ่งใช้งานได้ dao.totalBsqBalance=ยอด BSQ คงเหลือทั้งหมด dao.tx.published.success=การทำธุรกรรมของคุณได้รับการเผยแพร่เรียบร้อยแล้ว @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=ภาพรวมรอบการโหวด dao.cycle.currentPhase=ระยะปัจจุบัน dao.cycle.currentBlockHeight=ความสูงของบล็อกปัจจุบัน dao.cycle.proposal=ระยะเสนอ +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=ขั้นตอนการลงคะแนนเสียงแบบไม่ระบุตัวตน dao.cycle.voteReveal=ขั้นตอนการประกาศผลโหวต dao.cycle.voteResult=ผลโหวต dao.cycle.phaseDuration={0} บล็อก (≈{1}); บล็อก {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=รอบ dao.results.cycles.table.header.cycle=วงจร @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=ผลโหวต dao.results.proposals.voting.detail.header=ผลโหวตสำหรับข้อเสนอที่เลือก +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=ไม่ได้กำหนด @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=ที่อยู่ BTC ของผู้ร # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=ค่าธรรมเนียมในการลงบันทึกรายการทรัพย์สินต่อวัน # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=ขนาดการเทรดขั้นต่ำ +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=มูลค่าปัจจุบัน: {0} dao.param.blocks={0} บล็อก -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=ระยะเวลา {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} บล็อก -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(มูลค่าเริ่มต้น) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(มีการเปลี่ยนแปลงในการลงคะแนนเสียง) +dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the P2P network.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=ไม่ได้กำหนด # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=ข้อมูลแบบสุ่ม dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=ล็อค dao.bond.reputation.lockup.headline=ยืนยันล็อคการทำรายการ -dao.bond.reputation.lockup.details=ล็อคจำนวน: {0}\nล็อคเวลา: {1} บล็อก\n\nคุณแน่ใจหรือไม่ว่าต้องการดำเนินการต่อ? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=ยืนยันการปลดล็อกธุรกรรม -dao.bond.reputation.unlock.details=จำนวนที่ปลดล็อค: {0}\nเวลาในการล็อค: {1} บล็อก (s)\n\nคุณแน่ใจหรือไม่ว่าต้องการดำเนินการต่อ +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=การค้ำประกันทั้งหมด @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=ล็อค dao.bond.table.button.unlock=ปลดล็อค dao.bond.table.button.revoke=เพิกถอน +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=ไม่ได้กำหนด # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=ยังไม่ได้ประกัน # suppress inspection "UnusedProperty" @@ -1252,38 +1296,48 @@ dao.bond.bondState.UNLOCKED=การประกันปลดล็อคแ # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=หลักค้ำประกันถูกยึด +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=ไม่ได้กำหนด # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=บทบาทการประกัน # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=กิตติศัพท์ในการประกัน # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=แอดมิน Github +dao.bond.bondedRoleType.UNDEFINED=ไม่ได้กำหนด +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=แอดมินฟอรั่ม # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.TWITTER_ADMIN=แอดมินทวิตเตอร์ # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=แอดมิน Rocket chat +dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=แอดมิน Rocket chat # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=แอดมินยูทูป +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=ผู้ดูแล Bisq # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=ผู้ดำเนินงานของเว็บไซต์ # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=ผู้ดำเนินการฟอรั่ม # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=ตัวดำเนินการแหล่งข้อมูลในโหนด # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=ผู้ดำเนินการโหนดด้านราคา +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=ผู้ดำเนินการโหนด BTC +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=ผู้ดำเนินการด้านตลาด # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=ผู้ดำเนินการส่วนสำรวจ BSQ # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=เจ้าของชื่อโดเมน # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=แอดมิน DNS @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=แอดมิน DNS dao.bond.bondedRoleType.MEDIATOR=ผู้ไกล่เกลี่ย # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=ผู้ไกล่เกลี่ย +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=ค่าธรรมเนียมในการลงบันทึกรายการทรัพย์สิน +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=ค่าธรรมเนียมในการลงบันทึกรายการทรัพย์สิน dao.burnBsq.menuItem.proofOfBurn=พยานหลักฐานในการทำลายทิ้ง (Proof of burn) dao.burnBsq.header=ค่าธรรมเนียมสำหรับการลงบันทึกรายการทรัพย์สิน @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=วันที่ dao.proofOfBurn.hash=แฮช dao.proofOfBurn.txs=การทำธุรกรรม dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=ลงนามข้อความด้วยคีย์จากหลักฐานที่พิสูจน์ได้ (key from proof) หรือธุรกรรมที่ทำลายทิ้ง (burn transaction) -dao.proofOfBurn.verify.window.title=ตรวจสอบข้อความด้วยคีย์จากหลักฐานที่พิสูจน์ได้ (key from proof) หรือธุรกรรมที่ทำลายทิ้ง (burn transaction) +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction dao.proofOfBurn.copySig=คัดลอกลายเซ็นไปยังคลิปบอร์ด dao.proofOfBurn.sign=ลงนาม dao.proofOfBurn.message=ข้อความ dao.proofOfBurn.sig=ลายเซ็น dao.proofOfBurn.verify=ยืนยัน -dao.proofOfBurn.verify.header=ตรวจสอบข้อความด้วยคีย์จากหลักฐานที่พิสูจน์ได้ (key from proof) หรือธุรกรรมที่ทำลายทิ้ง (burn transaction) +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=การตรวจสอบดำเนินการเรียบร้อยแล้ว dao.proofOfBurn.verificationResult.failed=การตรวจสอบล้มเหลว @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=การประกาศผลโห # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=ผลโหวต +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=ไม่ได้กำหนด # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=คำขอสำหรับค่าสินไหมตอบแทน # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=ข้อเสนอทั่วไป # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=ข้อเสนอในการริบการประกัน +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=ไม่ได้กำหนด # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=คำขอค่าสินไหมทดแทน # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=ข้อเสนอทั่วไป # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=การริบตัวประกัน - dao.proposal.details=รายละเอียดข้อเสนอ dao.proposal.selectedProposal=ข้อเสนอที่เลือก dao.proposal.active.header=ข้อเสนอของวงจรปัจจุบัน dao.proposal.active.remove.confirm=คุณแน่ใจหรือไม่ที่จะลบข้อเสนอนั้น ข้อเสนอดังกล่าวที่มีการชำระค่าธรรมเนียมแล้วจะสูญหายหลังจากการลบข้อเสนอ dao.proposal.active.remove.doRemove=ใช่ ลบข้อเสนอของฉัน dao.proposal.active.remove.failed=ไม่สามารถลบข้อเสนอได้ +dao.proposal.myVote.title=การโหวต dao.proposal.myVote.accept=รับข้อเสนอ dao.proposal.myVote.reject=ปฏิเสธข้อเสนอ dao.proposal.myVote.removeMyVote=ละเว้นข้อเสนอ dao.proposal.myVote.merit=น้ำหนักผลคะแนนเสียงจาก BSQ ที่ได้รับ dao.proposal.myVote.stake=น้ำหนักผลการลงคะแนนเสียงจาก Stake (เหรียญที่ล็อคไว้สำหรับสิทธิ์ในการโหวต) -dao.proposal.myVote.blindVoteTxId=ID การทำธุรกรรมการลงคะแนนเสียงแบบไม่ระบุตัวตน dao.proposal.myVote.revealTxId=ID ธุรกรรมการแสดงผลการลงคะแนน -dao.proposal.myVote.stake.prompt=ยอดที่ใช้งานได้ขั้นสูงสุดสำหรับการโหวต: {0} -dao.proposal.votes.header=โหวตในข้อเสนอทั้งหมด -dao.proposal.votes.header.voted=การลงคะแนนของฉัน -dao.proposal.myVote.button=โหวตในข้อเสนอทั้งหมด +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=เลือกประเภทข้อเสนอ +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=ประเภทข้อเสนอ -dao.proposal.create.createNew=สร้างข้อเสนอใหม่ -dao.proposal.create.create.button=เสนอคำขอ +dao.proposal.create.new=สร้างข้อเสนอใหม่ +dao.proposal.create.button=เสนอคำขอ +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=ข้อเสนอ dao.proposal.display.type=ประเภทข้อเสนอ dao.proposal.display.name=ชื่อ / ชื่อเล่น dao.proposal.display.link=ลิงก์ไปยังรายละเอียดข้อมูล -dao.proposal.display.link.prompt=เชื่อมต่อไปยังคำสั่ง Github +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=จำนวนที่ต้องการใน BSQ dao.proposal.display.bsqAddress=ที่อยู่ BSQ dao.proposal.display.txId=รหัสธุรกรรมของข้อเสนอ @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=ได้รับการยืนยั dao.proposal.display.myVote.rejected=ปฏิเสธ dao.proposal.display.myVote.ignored=ละเว้น dao.proposal.myVote.summary=โหวต: {0}; น้ำหนักผลการลงคะแนนเสียง: {1} (รายได้: {2} + เงินเดิมพัน: {3}); +dao.proposal.myVote.invalid=Vote was invalid dao.proposal.voteResult.success=ได้รับการยืนยัน dao.proposal.voteResult.failed=ปฏิเสธ @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=ทรัพย์สินที่จ dao.blindVote=การลงคะแนนเสียงแบบไม่ระบุตัวตน dao.blindVote.startPublishing=กำลังเผยแพร่การทำธุรกรรมโหวตแบบไม่ระบุตัวตน ... -dao.blindVote.success=การโหวตที่ไม่ระบุตัวตนของคุณได้รับจัดทำการเรียบร้อยแล้ว +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=ส่ง dao.wallet.menuItem.receive=รับ dao.wallet.menuItem.transactions=การทำธุรกรรม dao.wallet.dashboard.myBalance=ยอดคงเหลือในกระเป๋าสตางค์ของฉัน -dao.wallet.dashboard.distribution=การแบ่งหน่วยของ BSQ ทั้งหมด -dao.wallet.dashboard.locked=สถานะทั่วโลกของ BSQ ที่ล็อกไว้ -dao.wallet.dashboard.market=ข้อมูลทางการตลาด -dao.wallet.dashboard.genesis=ธุรกรรมต้นกำเนิด -dao.wallet.dashboard.txDetails=สถิติในการทำธุรกรรม BSQ -dao.wallet.dashboard.genesisBlockHeight=ความสูงของบล็อกต้นกำเนิด (Genesis block) -dao.wallet.dashboard.genesisTxId=ID การทำธุรกรรมต้นกำเนิด -dao.wallet.dashboard.genesisIssueAmount=BSQ ที่ได้ดำเนินการในส่วนธุรกรรมทั่วไป -dao.wallet.dashboard.compRequestIssueAmount=BSQ ที่ได้ดำเนินการสำหรับการเรียกร้องค่าชดเชย -dao.wallet.dashboard.reimbursementAmount=BSQ ที่ได้ดำเนินการสำหรับการเรียกร้องการชำระเงินคืน -dao.wallet.dashboard.availableAmount=BSQ ที่ใช้งานได้ทั้งหมด -dao.wallet.dashboard.burntAmount=BSQ ที่ทำลายทิ้ง (ค่าธรรมเนียม) -dao.wallet.dashboard.totalLockedUpAmount=ถูกล็อคไว้ในการประกัน -dao.wallet.dashboard.totalUnlockingAmount=การปลดล็อค BSQ จากการประกัน -dao.wallet.dashboard.totalUnlockedAmount=ปลดล็อค BSQ จากการประกันไว้แล้ว -dao.wallet.dashboard.totalConfiscatedAmount=ยึด BSQ จากการประกันไว้แล้ว -dao.wallet.dashboard.allTx=หมายเลขจำนวนธุรกรรม BSQ ทั้งหมด -dao.wallet.dashboard.utxo=หมายเลขจำนวนธุรกรรมที่ยังใช้ไม่หมด (เงินทอน) -dao.wallet.dashboard.compensationIssuanceTx=เลขที่ของการทำธุรกรรมสำหรับยื่นคำขอค่าตอบแทนทั้งหมด -dao.wallet.dashboard.reimbursementIssuanceTx=เลขที่ของการทำธุรกรรมสำหรับการยื่นคำขอการชำระเงินคืนทั้งหมด -dao.wallet.dashboard.burntTx=เลขที่การทำธุรกรรมของการชำระค่าธรรมเนียมทั้งหมด -dao.wallet.dashboard.price=ราคาเทรด BSQ/BTC ล่าสุด (in Bisq) -dao.wallet.dashboard.marketCap=โครงสร้างเงินทุนในตลาด (ขึ้นอยู่กับราคาเทรด) - -dao.wallet.receive.fundYourWallet=เติมเงินกระเป๋าสตางค์ BSQ ของคุณ -dao.wallet.receive.bsqAddress=ที่อยู่ของกระเป๋าสตางค์ BSQ + +dao.wallet.receive.fundYourWallet=Your BSQ receive address +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=The Bisq DAO +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=ส่งเงิน dao.wallet.send.sendBtcFunds=ส่งเงินทุนที่ไม่ใช่เครือ BSQ (BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=บล็อกที่ได้รับการ dao.wallet.chainHeightSyncing=บล็อกที่กำลังรอดำเนินการ... ตรวจสอบแล้ว {0} จากบล็อกทั้งหมด {1} dao.wallet.tx.type=หมวด +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=ไม่ได้กำหนด # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=จำไม่ได้ # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=คำขอหรือการออกค่า dao.tx.issuanceFromCompReq.tooltip=คำขอค่าสินไหมทดแทน ซึ่งนำไปสู่การออก BSQ ใหม่\nวันที่ออก: {0} dao.tx.issuanceFromReimbursement=การออกคำสั่ง/การยื่นคำร้องขอการชำระเงินคืน dao.tx.issuanceFromReimbursement.tooltip=การเรียกร้องขอการชำระเงินคืนซึ่งเป็นคำสั่งภายใต้ BSQ ฉบับใหม่\nวันที่เริ่มทำการ: {0} -dao.proposal.create.missingBsqFunds=คุณไม่มีเงินเพียงพอสำหรับการสร้างข้อเสนอ\nขาดไป: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=ยืนยันการทำรายการ {0} dao.feeTx.confirm.details={0} ค่าธรรมเนียม: {1}\nค่าธรรมเนียมการขุด: {2} ({3} Satoshis / byte)\nขนาดของธุรกรรม: {4} Kb\n\nคุณแน่ใจหรือไม่ว่าต้องการเผยแพร่ {5} ธุรกรรม? +dao.news.bisqDAO.title=THE BISQ DAO +dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. +dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO + +dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ +dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.news.pastContribution.yourAddress=Your BSQ Wallet Address +dao.news.pastContribution.requestNow=Request now + +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. +dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ +dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. +dao.news.DAOOnTestnet.readMoreLink=Read the full documentation + +dao.monitor.daoState=DAO state +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Conflicts +dao.monitor.state=สถานะ +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=DAO state +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Block height +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Previous hash +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Previous hash +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Previous hash +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=ข้อมูลทางการตลาด +dao.factsAndFigures.dashboard.price=ราคาเทรด BSQ/BTC ล่าสุด (in Bisq) +dao.factsAndFigures.dashboard.marketCap=โครงสร้างเงินทุนในตลาด (ขึ้นอยู่กับราคาเทรด) +dao.factsAndFigures.dashboard.availableAmount=BSQ ที่ใช้งานได้ทั้งหมด + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ ที่ได้ดำเนินการในส่วนธุรกรรมทั่วไป +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ ที่ได้ดำเนินการสำหรับการเรียกร้องค่าชดเชย +dao.factsAndFigures.supply.reimbursementAmount=BSQ ที่ได้ดำเนินการสำหรับการเรียกร้องการชำระเงินคืน + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=สถานะทั่วโลกของ BSQ ที่ล็อกไว้ +dao.factsAndFigures.supply.totalLockedUpAmount=ถูกล็อคไว้ในการประกัน +dao.factsAndFigures.supply.totalUnlockingAmount=การปลดล็อค BSQ จากการประกัน +dao.factsAndFigures.supply.totalUnlockedAmount=ปลดล็อค BSQ จากการประกันไว้แล้ว +dao.factsAndFigures.supply.totalConfiscatedAmount=ยึด BSQ จากการประกันไว้แล้ว +dao.factsAndFigures.supply.burntAmount=BSQ ที่ทำลายทิ้ง (ค่าธรรมเนียม) + +dao.factsAndFigures.transactions.genesis=ธุรกรรมต้นกำเนิด +dao.factsAndFigures.transactions.genesisBlockHeight=ความสูงของบล็อกต้นกำเนิด (Genesis block) +dao.factsAndFigures.transactions.genesisTxId=ID การทำธุรกรรมต้นกำเนิด +dao.factsAndFigures.transactions.txDetails=สถิติในการทำธุรกรรม BSQ +dao.factsAndFigures.transactions.allTx=หมายเลขจำนวนธุรกรรม BSQ ทั้งหมด +dao.factsAndFigures.transactions.utxo=หมายเลขจำนวนธุรกรรมที่ยังใช้ไม่หมด (เงินทอน) +dao.factsAndFigures.transactions.compensationIssuanceTx=เลขที่ของการทำธุรกรรมสำหรับยื่นคำขอค่าตอบแทนทั้งหมด +dao.factsAndFigures.transactions.reimbursementIssuanceTx=เลขที่ของการทำธุรกรรมสำหรับการยื่นคำขอการชำระเงินคืนทั้งหมด +dao.factsAndFigures.transactions.burntTx=เลขที่การทำธุรกรรมของการชำระค่าธรรมเนียมทั้งหมด #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=แหล่งข้อมูลในโหนดเค filterWindow.priceRelayNode=โหนดผลัดเปลี่ยนราคาที่ได้รับการกรอง (คั่นด้วยเครื่องหมายจุลภาค ที่อยู่ onion) filterWindow.btcNode=โหนด Bitcoin ที่ได้รับการกรองแล้ว (คั่นด้วยเครื่องหมายจุลภาค ที่อยู่ + พอร์ต) filterWindow.preventPublicBtcNetwork=ป้องกันการใช้เครือข่าย Bitcoin สาธารณะ +filterWindow.disableDao=Disable DAO filterWindow.add=เพิ่มตัวกรอง filterWindow.remove=ลบตัวกรอง @@ -1765,7 +1913,7 @@ popup.warning.tradePeriod.halfReached=การซื้อขายของค popup.warning.tradePeriod.ended=การซื้อขายของคุณที่มีรหัส ID {0} มีค่าสูงสุดแล้ว อนุญาตให้ซื้อขายได้และยังไม่สมบูรณ์\n\nช่วงเวลาการซื้อขายสิ้นสุดวันที่ {1}\n\nโปรดตรวจสอบการค้าของคุณที่ \"Portfolio (แฟ้มผลงาน) / เปิดการซื้อขาย \" เพื่อติดต่อกับผู้ไกล่เกลี่ย popup.warning.noTradingAccountSetup.headline=คุณยังไม่ได้ตั้งค่าบัญชีการซื้อขาย popup.warning.noTradingAccountSetup.msg=คุณต้องตั้งค่าสกุลเงินประจำชาติหรือบัญชี altcoin (เหรียญทางเลือก) ก่อนจึงจะสามารถสร้างข้อเสนอได้\nคุณต้องการตั้งค่าบัญชีหรือไม่ -popup.warning.noArbitratorsAvailable=ไม่มีผู้ไกล่เกลี่ยสำหรับทำการ +popup.warning.noArbitratorsAvailable=ไม่มีผู้ไกล่เกลี่ยสำหรับทำการ popup.warning.notFullyConnected=คุณต้องรอจนกว่าคุณจะเชื่อมต่อกับเครือข่ายอย่างสมบูรณ์\nอาจใช้เวลาประมาณ 2 นาทีเมื่อเริ่มต้น popup.warning.notSufficientConnectionsToBtcNetwork=คุณต้องรอจนกว่าจะมีการเชื่อมต่อกับเครือข่าย Bitcoin อย่างน้อย {0} รายการ popup.warning.downloadNotComplete=คุณต้องรอจนกว่าการดาวน์โหลดบล็อค Bitcoin ที่ขาดหายไปจะเสร็จสมบูรณ์ @@ -1955,6 +2103,10 @@ BTC_MAINNET=Bitcoin Mainnet BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=ปี time.month=เดือน @@ -2025,6 +2177,8 @@ payment.country=ประเทศ payment.extras=ข้อกำหนดเพิ่มเติม payment.email.mobile=อีเมลหรือหมายเลขโทรศัพท์มือถือ payment.altcoin.address=ที่อยู่ Altcoin (เหรียญทางเลือก) +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=Altcoin (เหรียญทางเลือก) payment.select.altcoin=เลือกหรือค้นหา altcoin (เหรียญทางเลือก) payment.secret=คำถามลับ @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=อีเมลหรือหมายเลขโทรศัพท์ payment.venmo.venmoUserName=ชื่อผู้ใช้ Venmo payment.popmoney.accountId=อีเมลหรือหมายเลขโทรศัพท์ -payment.revolut.email=อีเมลหรือหมายเลขโทรศัพท์ +payment.revolut.email=อีเมล +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=รหัสบัตรประชาชน/รหัสประจำตัวผู้เสียภาษี หรือเบอร์โทรศัพท์ payment.supportedCurrencies=สกุลเงินที่ได้รับการสนับสนุน payment.limitations=ข้อจำกัด @@ -2080,7 +2235,11 @@ payment.westernUnion.info=เมื่อใช้ Western Union ผู้ซื payment.halCash.info=เมื่อมีการใช้งาน HalCash ผู้ซื้อ BTC จำเป็นต้องส่งรหัส Halcash ให้กับผู้ขายทางข้อความโทรศัพท์มือถือ\n\nโปรดตรวจสอบว่าไม่เกินจำนวนเงินสูงสุดที่ธนาคารของคุณอนุญาตให้คุณส่งด้วย HalCash จำนวนเงินขั้นต่ำในการเบิกถอนคือ 10 EUR และสูงสุดในจำนวนเงิน 600 EUR สำหรับการถอนซ้ำเป็น 3000 EUR ต่อผู้รับและต่อวัน และ 6000 EUR ต่อผู้รับและต่อเดือน โปรดตรวจสอบข้อจำกัดจากทางธนาคารคุณเพื่อให้มั่นใจได้ว่าทางธนาคารได้มีการใช้มาตรฐานข้อกำหนดเดียวกันกับดังที่ระบุไว้ ณ ที่นี่\n\nจำนวนเงินที่ถอนจะต้องเป็นจำนวนเงินหลาย 10 EUR เนื่องจากคุณไม่สามารถถอนเงินอื่น ๆ ออกจากตู้เอทีเอ็มได้ UI ในหน้าจอสร้างข้อเสนอและรับข้อเสนอจะปรับจำนวนเงิน BTC เพื่อให้จำนวนเงิน EUR ถูกต้อง คุณไม่สามารถใช้ราคาตลาดเป็นจำนวนเงิน EUR ซึ่งจะเปลี่ยนแปลงไปตามราคาที่มีการปรับเปลี่ยน\n\nในกรณีที่มีข้อพิพาทผู้ซื้อ BTC ต้องแสดงหลักฐานว่าได้ส่ง EUR แล้ว payment.limits.info=โปรดทราบว่าการโอนเงินผ่านธนาคารทั้งหมดมีความเสี่ยงจากการเรียกเก็บเงินคืน\n\nเพื่อลดความเสี่ยงนี้ Bisq ตั้งค่าขีดจำกัดต่อการซื้อขายขึ้นอยู่กับสองปัจจัยคือ\n\n1. ระดับความเสี่ยงจากการเรียกเก็บเงินคืนโดยประเมินจากวิธีการชำระเงินที่ใช้\n2. อายุของบัญชีสำหรับวิธีการชำระเงินนั้น\n\nขณะนี้บัญชีที่คุณกำลังสร้างใหม่และยังไม่ได้เริ่มอายุการใช้งาน เนื่องจากบัญชีของคุณเติบโตขึ้นเมื่ออายุเกินกว่าระยะเวลาสองเดือน ขีดจำกัดต่อการซื้อขายของคุณจะเติบโตไปพร้อมเช่นกัน: \n\n●ในช่วง 1 เดือนขีดจำกัดต่อการซื้อขายของคุณจะเป็น {0} \n●ในช่วงเดือนที่ 2 ขีดจำกัดต่อการซื้อขายของคุณจะเป็น {1} \n●หลังจากเดือนที่ 2 ขีดจำกัดต่อการซื้อขายของคุณจะเป็น {2} \n\nโปรดทราบว่าไม่มีข้อจำกัดเกี่ยวกับจำนวนครั้งที่คุณสามารถซื้อขายได้ -payment.cashDeposit.info=โปรดยืนยันว่าธนาคารของคุณได้อนุมัติให้คุณสามารถส่งเงินสดให้กับบัญชีบุคคลอื่นได้ ตัวอย่างเช่น บางธนาคารที่ไม่ได้มีการบริการถ่ายโอนเงินสดอย่าง Bank of America และ Wells Fargo +payment.cashDeposit.info=โปรดยืนยันว่าธนาคารของคุณได้อนุมัติให้คุณสามารถส่งเงินสดให้กับบัญชีบุคคลอื่นได้ ตัวอย่างเช่น บางธนาคารที่ไม่ได้มีการบริการถ่ายโอนเงินสดอย่าง Bank of America และ Wells Fargo + +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. payment.f2f.contact=ข้อมูลติดต่อ payment.f2f.contact.prompt=วิธีการที่คุณต้องการได้รับการติดต่อจากการค้าจากระบบ peer (ที่อยู่อีเมล , หมายเลขโทรศัพท์ ... ) @@ -2128,16 +2287,10 @@ F2F_SHORT=F2F # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altcoins PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Altcoins PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} ต้องประกอบด้วย {1} validation.invalidInput=ใส่ข้อมูลไม่ถูกต้อง: {0} validation.accountNrFormat=หมายเลขบัญชีต้องเป็นรูปแบบ: {0} validation.altcoin.wrongStructure=การตรวจสอบความถูกต้องของที่อยู่ล้มเหลวเนื่องจากไม่ตรงกับโครงสร้างของที่อยู่ {0} +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=ที่อยู่ ZEC ต้องเริ่มต้นด้วย t ไม่รองรับที่อยู่ที่ขึ้นต้นด้วย z validation.altcoin.invalidAddress=ที่อยู่ไม่ใช่ที่อยู่ {0} ที่ถูกต้อง! {1} validation.bic.invalidLength=ความยาวของการป้อนข้อมูลนำเข้าไม่ใช่ 8 หรือ 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=ต้องประกอบด้ validation.interacETransfer.invalidAnswer=ต้องเป็นคำเดียว และประกอบด้วยตัวอักษร ตัวเลข และ/หรือ สัญลักษณ์ - เท่านั้น validation.inputTooLarge=ข้อมูลที่ป้อนต้องไม่เป็นจำนวนที่มากกว่า {0} validation.inputTooSmall=การป้อนเข้าจะต้องมีจำนวนมากกว่า {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=จำนวนยอดรวมต่ำกว่าข้อจำกัดของ Dust (dust หน่วยเล็กสุดของ bitcoin) ซึ่งเป็น {0} ไม่ได้รับการยินยอมให้ validation.length=ความยาวจะต้องอยู่ระหว่าง {0} และ {1} validation.pattern=การป้อนเข้าจะต้องเป็นรูปแบบ {0} validation.noHexString=การป้อนเข้านั้นคือไม่ใช่รูปแบบของ HEX validation.advancedCash.invalidFormat=จะต้องเป็นอีเมลหรือรหัสกระเป๋าสตางค์ที่ใช้งานได้: X000000000000 +validation.invalidUrl=This is not a valid URL +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 4454bb34fcc..5212ca166dd 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -104,7 +104,7 @@ 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 ngoài hoặc nộp vào ví Bisq tại \"Funds/Receive funds\". +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.waitingForFunds=Đợi nộp tiền... shared.depositTransactionId=ID giao dịch gửi tiền shared.TheBTCBuyer=Người mua BTC @@ -113,12 +113,13 @@ 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.exportJSON=Export to JSON shared.noDateAvailable=Ngày tháng không hiển thị shared.arbitratorsFee=Phí trọng tài shared.noDetailsAvailable=Không có thông tin shared.notUsedYet=Chưa được sử dụng shared.date=Ngày -shared.sendFundsDetailsWithFee=Đang gửi: {0}\nTừ địa chỉ: {1}\nĐến địa chỉ nhận: {2}.\nPhí giao dịch là: {3} ({4} satoshis/byte)\nDung lượng giao dịch: {5} Kb\n\nNgười nhận sẽ nhận được: {6}\n\nBạn có chắc chắn muốn rút khoản tiền này không? +shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired transaction 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.copyToClipboard=Sao chép đến clipboard shared.language=Ngôn ngữ shared.country=Quốc gia @@ -196,6 +197,8 @@ shared.interval=Khoảng thời gian shared.actions=Hoạt động shared.buyerUpperCase=Người mua shared.sellerUpperCase=Người bán +shared.new=NEW +shared.blindVoteTxId=Mã giao dịch bỏ phiếu mù #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=Bị khóa mainView.footer.usingTor=(sử dụng Tor) mainView.footer.localhostBitcoinNode=(Máy chủ nội bộ) mainView.footer.btcInfo=Các mạng Bitcoin ngang hàng: {0} / {1} {2} -mainView.footer.btcInfo.initializing=Khởi tạo -mainView.footer.btcInfo.synchronizedWith=đồng bộ hóa với -mainView.footer.btcInfo.connectingTo=kết nối với +mainView.footer.btcInfo.initializing=Connecting to Bitcoin network +mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO +mainView.footer.btcInfo.synchronizedWith=Synchronized with +mainView.footer.btcInfo.connectingTo=Connecting to mainView.footer.btcInfo.connectionFailed=không kết nối được mainView.footer.p2pInfo=Các mạng P2P ngang hàng: {0} @@ -299,8 +303,8 @@ market.trades.tooltip.candle.date=Ngày: offerbook.createOffer=Tạo chào giá offerbook.takeOffer=Nhận chào giá -offerbook.takeOfferToBuy=Take offer to buy {0} -offerbook.takeOfferToSell=Take offer to sell {0} +offerbook.takeOfferToBuy=Nhận chào giá mua {0} +offerbook.takeOfferToSell=Nhận chào giá bán {0} offerbook.trader=Trader offerbook.offerersBankId=ID ngân hàng của người tạo (BIC/SWIFT): {0} offerbook.offerersBankName=Tên ngân hàng của người tạo: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=Số tiền BTC đến {0} createOffer.amountPriceBox.buy.volumeDescription=Số tiền {0} để chi trả createOffer.amountPriceBox.sell.volumeDescription=Số tiền {0} để nhận createOffer.amountPriceBox.minAmountDescription=Số tiền BTC nhỏ nhất -createOffer.securityDeposit.prompt=Tiền đặt cọc bằng BTC +createOffer.securityDeposit.prompt=Tiền đặt cọc createOffer.fundsBox.title=Nộp tiền cho chào giá của bạn createOffer.fundsBox.offerFee=Phí giao dịch createOffer.fundsBox.networkFee=Phí đào createOffer.fundsBox.placeOfferSpinnerInfo=Báo giá đang được công bố createOffer.fundsBox.paymentLabel=giao dịch Bisq với ID {0} createOffer.fundsBox.fundsStructure=({0} tiền đặt cọc, {1} phí giao dịch, {2} phí đào) +createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=Chào giá của bạn đã được công bố createOffer.success.info=Bạn có thể quản lý báo giá hiện hành của bạn tại \"Portfolio/ báo giá hiện tại của bạn\". createOffer.info.sellAtMarketPrice=Bạn sẽ luôn bán với giá thị trường vì báo giá của bạn sẽ luôn được cập nhật. @@ -387,6 +392,7 @@ createOffer.alreadyFunded=Bạn đã nộp tiền cho chào giá.\nSố tiền c createOffer.createOfferFundWalletInfo.headline=Nộp tiền cho báo giá của bạn # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Khoản tiền giao dịch: {0} \n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} and {1} createOffer.createOfferFundWalletInfo.msg=Bạn cần đặt cọc {0} cho báo giá này.\n\nCác khoản tiền này sẽ được giữ trong ví nội bộ của bạn và sẽ bị khóa vào địa chỉ đặt cọc multisig khi có người nhận báo giá của bạn.\n\nKhoản tiền này là tổng của:\n{1}- tiền gửi đại lý của bạn: {2}\n- Phí giao dịch: {3}\n- Phí đào: {4}\n\nBạn có thể chọn giữa hai phương án khi nộp tiền cho giao dịch:\n- Sử dụng ví Bisq của bạn (tiện lợi, nhưng giao dịch có thể bị kết nối) OR\n- Chuyển từ ví bên ngoài (riêng tư hơn)\n\nBạn sẽ xem các phương án nộp tiền và thông tin chi tiết sau khi đóng cửa sổ này. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=Giá bạn vừa nhập ngoài sai lệch ch createOffer.changePrice=Thay đổi giá createOffer.tac=Với việc công bố chào giá này, tôi đồng ý giao dịch với bất cứ Thương gia nào đáp ứng các điều kiện nêu rõ trên màn hình này. createOffer.currencyForFee=Phí giao dịch -createOffer.setDeposit=Cài đặt tiền đặt cọc của người mua +createOffer.setDeposit=Set buyer's security deposit (%) +createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) +createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} #################################################################### @@ -538,7 +547,7 @@ portfolio.pending.step2_buyer.confirmStart.yes=Có, tôi đã bắt đầu thanh 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}. -portfolio.pending.step2_seller.warn=The BTC buyer still has not done the {0} payment.\nYou need to wait until they have started the payment.\nIf the trade has not been completed on {1} the arbitrator will investigate. +portfolio.pending.step2_seller.warn=Người mua BTC vẫn chưa thanh toán {0}.\nBạn cần phải đợi cho đến khi người mua bắt đầu thanh toán.\nNếu giao dịch không được hoàn thành vào {1} trọng tài sẽ điều tra. portfolio.pending.step2_seller.openForDispute=Người mua BTC chưa bắt đầu thanh toán!\nThời gian cho phép tối đa cho giao dịch đã hết.\nBạn có thể đợi lâu hơn và cho Đối tác giao dịch thêm thời gian hoặc liên hệ trọng tài để mở khiếu nại. # suppress inspection "UnusedProperty" @@ -562,12 +571,12 @@ portfolio.pending.step3_buyer.warn.part1b=tại nhà cung cấp thanh toán củ portfolio.pending.step3_buyer.warn.part2=Người bán BTC vẫn chưa xác nhận thanh toán của bạn!\nHãy kiểm tra xem {0} việc gửi thanh toán đi đã thành công chưa.\nNếu người bán BTC không xác nhận là đã nhận thanh toán của bạn trước {1} giao dịch sẽ bị điều tra bởi trọng tài. portfolio.pending.step3_buyer.openForDispute=Người bán BTC vẫn chưa xác nhận thanh toán của bạn!\nThời gian tối đa cho giao dịch đã hết.\nBạn có thể đợi lâu hơn và cho đối tác giao dịch thêm thời gian hoặc liên hệ trọng tài để mở khiếu nại. # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.part=Your trading partner has confirmed that they have initiated the {0} payment.\n\n +portfolio.pending.step3_seller.part=Đối tác giao dịch của bạn đã xác nhận rằng họ đã kích hoạt thanh toán {0}.\n\n portfolio.pending.step3_seller.altcoin.explorer=Trên trình duyệt blockchain explorer {0} ưa thích của bạn portfolio.pending.step3_seller.altcoin.wallet=Trên ví {0} của bạn portfolio.pending.step3_seller.altcoin={0}Vui lòng kiểm tra {1} xem giao dịch tới địa chỉ nhận của bạn \n{2}\nđã nhận được đủ xác nhận blockchain hay chưa.\nSố tiền thanh toán phải là {3}\n\nBạn có thể copy & paste địa chỉ {4} của bạn từ màn hình chính sau khi đóng cửa sổ này. portfolio.pending.step3_seller.postal={0}Hãy kiểm tra xem bạn đã nhận được {1} \"Thư chuyển tiền US\" từ người mua BTC chưa.\n\nID giao dịch (nội dung \"lý do thanh toán\") của giao dịch là: \"{2}\" -portfolio.pending.step3_seller.bank=Your trading partner has confirmed that they have initiated the {0} payment.\n\nPlease go to your online banking web page and check if you have received {1} from the BTC buyer.\n\nThe trade ID (\"reason for payment\" text) of the transaction is: \"{2}\"\n\n +portfolio.pending.step3_seller.bank=Đối tác giao dịch của bạn đã xác nhận rằng họ đã kích hoạt thanh toán {0}.\n\nHãy truy cập trang web ngân hàng online và kiểm tra xem bạn đã nhận được {1} từ người mua BTC chưa.\n\nID giao dịch (\"lý do thanh toán\" text) của giao dịch là: \"{2}\"\n\n portfolio.pending.step3_seller.cash=Vì thanh toán được thực hiện qua Tiền gửi tiền mặt nên người mua BTC phải viết rõ \"KHÔNG HOÀN LẠI\" trên giấy biên nhận, xé làm 2 phần và gửi ảnh cho bạn qua email.\n\nĐể tránh bị đòi tiền lại, chỉ xác nhận bạn đã nhận được email và bạn chắc chắn giấy biên nhận là có hiệu lực.\nNếu bạn không chắc chắn, {0} portfolio.pending.step3_seller.moneyGram=Người mua phải gửi mã số xác nhận và ảnh chụp của hoá đơn qua email.\nHoá đơn cần ghi rõ họ tên đầy đủ, quốc gia, tiêu bang và số lượng. Vui lòng kiểm tra email nếu bạn nhận được số xác thực.\n\nSau khi popup đóng, bạn sẽ thấy tên người mua BTC và địa chỉ để nhận tiền từ MoneyGram.\n\nChỉ xác nhận hoá đơn sau khi bạn hoàn thành việc nhận tiền. portfolio.pending.step3_seller.westernUnion=Người mua phải gửi cho bạn MTCN (số theo dõi) và ảnh giấy biên nhận qua email.\nGiấy biên nhận phải ghi rõ họ tên của bạn, thành phố, quốc gia và số tiền. Hãy kiểm tra email xem bạn đã nhận được MTCN chưa.\n\nSau khi đóng cửa sổ này, bạn sẽ thấy tên và địa chỉ của người mua BTC để nhận tiền từ Western Union.\n\nChỉ xác nhận giấy biên nhận sau khi bạn đã nhận tiền thành công! @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=Chúng tôi rất vui lòng được nghe phản h tradeFeedbackWindow.msg.part2=nếu bạn có câu hỏi hay vấn đề, vui lòng liên hệ với người dùng khác và các nhàn đóng góp qua Bisq forum ở: tradeFeedbackWindow.msg.part3=Cám ơn bạn đã sử dụng Bisq! + +daoTestingFeedbackWindow.title=Thank you for testing the Bisq DAO +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Visit forum +daoTestingFeedbackWindow.msg.part3=Cám ơn bạn đã sử dụng Bisq! + portfolio.pending.role=Vai trò của tôi portfolio.pending.tradeInformation=Thông tin giao dịch portfolio.pending.remainingTime=Thời gian còn lại @@ -767,13 +784,13 @@ support.buyerOfferer=Người mua BTC/Người tạo support.sellerOfferer=Người bán BTC/Người tạo support.buyerTaker=Người mua BTC/Người nhận support.sellerTaker=Người bán BTC/Người nhận -support.backgroundInfo=Bisq is not a company, so it handles disputes differently.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display an \"Open dispute\" button after the trade period is over for contacting the arbitrator.\n\nIf there is an issue with the application, the software will try to detect it and, if possible, display an \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIf you are having an issue and did not see the \"Open support ticket\" button, you can open a support ticket manually by selecting the trade causing issues under \"Portfolio/Open trades\" and hitting \"alt + o\" or \"option + o\". Please use this method only if you are sure that the software is not working as expected. If you have problems or questions, please review the FAQ on the bisq.network web page or post in the Bisq forum in the Support section. +support.backgroundInfo=Bisq không phải là một công ty, vậy nên chúng tôi giải quyết khiếu nại một cách khác biệt.\n\nNếu có khiếu nại trong quá trình giao dịch (VD: một người tham gia giao dịch không tuân thủ giao thức giao dịch) ứng dụng sẽ hiển thị nút \"Mở khiếu nại\" sau khi hết thời gian giao dịch để liên hệ với trọng tài.\n\nTrong trường hợp có sự cố với ứng dụng, phầm mềm sẽ tìm cách phát hiện và, nếu có thể, sẽ hiển thị nút \"Mở vé hỗ trợ\" để liên hệ với trọng tài nhằm chuyển tiếp vấn đề cho lập trình viên.\n\nTrong trường hợp người dùng gặp sự cố nhưng nút \"Mở vé hỗ trợ\" không hiển thị, bạn có thể mở vé hỗ trợ thủ công bằng cách chọn giao dịch gây ra vấn đề thông qua \"Danh mục/Các giao dịch mở\" và gõ tổ hợp phím \"alt + o\" hoặc \"option + o\". Vui lòng chỉ sử dụng khi bạn chắc chắn phần mềm không làm việc như bình thường. Nếu bạn gặp vấn đề khi sử dụng Bisq hoặc có bất cứ thắc mắc nào, vui lòng xem lại FAQ tại trang web bisq.network hoặc đăng lên diễn đàn Bisq ở phần hỗ trợ. -support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrator's requests within 2 days.\n2. The maximum period for a dispute is 14 days.\n3. You need to cooperate with the arbitrator and provide the information they request to make your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=Tin nhắn hệ thống: {0} -support.youOpenedTicket=Bạn đã mở yêu cầu hỗ trợ. -support.youOpenedDispute=Bạn đã mở yêu cầu khiếu nại.\n\n{0} -support.peerOpenedTicket=Đối tác giao dịch của bạn đã yêu cầu hỗ trợ do sự cố kỹ thuật. Vui lòng đợi hướng dẫn thêm. +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=Đối tác giao dịch của bạn đã yêu cầu khiếu nại.\n\n{0} @@ -838,8 +855,8 @@ settings.net.useCustomNodesRadio=Sử dụng nút Bitcoin Core thông dụng settings.net.warn.usePublicNodes=Nếu bạn sử dụng mạng Bitcoin công cộng, bạn có thể gặp vấn đề về quyền riêng tư do thiết kế bộ lọc bloom bị hư hỏng và quá trình thực hiện áp dụng cho ví SPV như BitcoinJ (sử dụng trong Bisq). Bất cứ nút nào mà bạn kết nối cũng có thể thấy địa chỉ ví của bạn thuộc về một tổ chức.\n\nVui lòng đọc thêm thông tin chi tiết tại: https://bisq.network/blog/privacy-in-bitsquare.\n\nBạn có chắc muốn sử dụng nút công cộng? settings.net.warn.usePublicNodes.useProvided=Không, sử dụng nút được cung cấp settings.net.warn.usePublicNodes.usePublic=Vâng, sử dụng nút công cộng -settings.net.warn.useCustomNodes.B2XWarning=Please be sure that your Bitcoin node is a trusted Bitcoin Core node!\n\nConnecting to nodes which do not follow the Bitcoin Core consensus rules could corrupt your wallet and cause problems in the trade process.\n\nUsers who connect to nodes that violate consensus rules are responsible for any resulting damage. Any resulting disputes will be decided in favor of the other peer. No technical support will be given to users who ignore this warning and protection mechanisms! -settings.net.localhostBtcNodeInfo=(Background information: If you are running a local Bitcoin node (localhost) you can connect exclusively to it.) +settings.net.warn.useCustomNodes.B2XWarning=Vui lòng chắc chắn rằng nút Bitcoin của bạn là nút Bitcoin Core đáng tin cậy!\n\nKết nối với nút không tuân thủ nguyên tắc đồng thuận Bitcoin Core có thể làm hỏng ví của bạn và gây ra các vấn đề trong quá trình giao dịch.\n\nNgười dùng kết nối với nút vi phạm nguyên tắc đồng thuận chịu trách nhiệm đối với các thiệt hại mà việc này gây ra. Các khiếu nại do điều này gây ra sẽ được quyết định theo hướng có lợi cho đối tác bên kia. Sẽ không có hỗ trợ về mặt kỹ thuật nào cho người dùng không tuân thủ cơ chế cảnh báo và bảo vệ của chúng tôi! +settings.net.localhostBtcNodeInfo=(Thông tin cơ sở: Nếu bạn đang chạy nút Bitcoin nội bộ (máy chủ nội bộ) bạn có thể kết nối độc quyền với nút này.) settings.net.p2PPeersLabel=Các đối tác được kết nối settings.net.onionAddressColumn=Địa chỉ onion settings.net.creationDateColumn=Đã thiết lập @@ -862,17 +879,17 @@ settings.net.inbound=chuyến về settings.net.outbound=chuyến đi settings.net.reSyncSPVChainLabel=Đồng bộ hóa lại SPV chain settings.net.reSyncSPVChainButton=Xóa file SPV và đồng bộ hóa lại -settings.net.reSyncSPVSuccess=The SPV chain file will be deleted on the next startup. You need to restart your application now.\n\nAfter the restart it can take a while to resync with the network and you will only see all transactions once the resync is completed.\n\nPlease restart again after the resync has completed because there are sometimes inconsistencies that result in incorrect balances to display. -settings.net.reSyncSPVAfterRestart=The SPV chain file has been deleted. Please be patient. It can take a while to resync with the network. +settings.net.reSyncSPVSuccess=File chuỗi SPV sẽ được xóa ở lần khởi động sau. Bạn cần khởi động lại ứng dụng bây giờ.\n\nSau khi khởi động lại có thể mất một lúc để đồng bộ hóa với mạng và bạn chỉ xem được tất cả giao dịch khi đồng bộ hóa xong.\n\nVui lòng khởi động lại sau khi đã đồng bộ hóa xong vì đôi khi sẽ có những chỗ không nhất quán dẫn tới hiển thị số dư không đúng. +settings.net.reSyncSPVAfterRestart=File chuỗi SPV đã được xóa. Vui lòng đợi, có thể mất một lúc để đồng bộ hóa với mạng. settings.net.reSyncSPVAfterRestartCompleted=Đồng bộ hóa đã xong. Vui lòng khởi động lại ứng dụng. settings.net.reSyncSPVFailed=Không thể xóa SPV chain file.\nLỗi: {0} setting.about.aboutBisq=Về Bisq -setting.about.about=Bisq is open-source software which facilitates the exchange of bitcoin with national currencies (and other cryptocurrencies) through a decentralized peer-to-peer network in a way that strongly protects user privacy. Learn more about Bisq on our project web page. +setting.about.about=Bisq là một phần mềm mã nguồn mở nhằm hỗ trợ quá trình trao đổi giữa bitcoin và tiền tệ quốc gia (và các loại tiền crypto khác) thông qua một mạng lưới ngang hàng phi tập trung hoạt động trên cơ sở bảo vệ tối đa quyền riêng tư của người dùng. Vui lòng tìm hiểu thêm về Bisq trên trang web dự án của chúng tôi. setting.about.web=Trang web Bisq setting.about.code=Mã nguồn setting.about.agpl=Giấy phép AGPL setting.about.support=Hỗ trợ Bisq -setting.about.def=Bisq is not a company—it is a project open to the community. If you want to participate or support Bisq please follow the links below. +setting.about.def=Bisq không phải là một công ty mà là một dự án mở cho cả cộng đồng. Nếu bạn muốn tham gia hoặc hỗ trợ Bisq, vui lòng truy cập link dưới đây. setting.about.contribute=Góp vốn setting.about.donate=Tặng setting.about.providers=Nhà cung cấp dữ liệu @@ -894,7 +911,7 @@ setting.about.subsystems.val=Phiên bản mạng: {0}; Phiên bản tin nhắn P account.tab.arbitratorRegistration=Đăng ký trọng tài account.tab.account=Tài khoản account.info.headline=Chào mừng đến với tài khoản Bisq của bạn -account.info.msg=Here you can add trading accounts for national currencies & altcoins, select arbitrators, and create a backup of your wallet & account data.\n\nA new Bitcoin wallet was created the first time you started Bisq.\n\nWe strongly recommend that you write down your Bitcoin wallet seed words (see tab on the top) and consider adding a password before funding. Bitcoin deposits and withdrawals are managed in the \"Funds\" section.\n\nPrivacy & security note: because Bisq is a decentralized exchange, all your data is kept on your computer. There are no servers, so we have no access to your personal info, your funds, or even your IP address. Data such as bank account numbers, altcoin & Bitcoin addresses, etc are only shared with your trading partner to fulfill trades you initiate (in case of a dispute the arbitrator will see the same data as your trading peer). +account.info.msg=Ở đây bạn có thể thiết lập tài khoản giao dịch cho tiền tệ quốc gia & altcoin, lựa chọn trọng tài và sao lưu dữ liệu ví & tài khoản của bạn.\n\nMột ví Bitcoin mới được tạo khi bạn khởi động Bisq lần đầu tiên.\nChúng tôi khuyến khích bạn viết lại các từ khóa khởi tạo ví Bitcoin của bạn (xem thẻ bên trên) và cân nhắc để thêm mật khẩu trước khi nộp tiền. Tiền gửi và rút Bitcoin được quản lý trong phần \"Số tiền\".\n\nGhi chú về Quyền riêng tư & Bảo mật:\nBởi vì Bisq là một sàn giao dịch phi tập trung, tất cả dữ liệu của bạn được lưu trữ trong máy tính. Không có máy chủ vì vậy chúng tôi không có quyền truy cập thông tin cá nhân, số tiền hay thậm chí địa chỉ IP của bạn. Các dữ liệu như số tài khoản ngân hàng, địa chỉ altcoin & Bitcoin, vv... chỉ được chia sẻ với Đối tác giao dịch của bạn để thực hiện giao dịch bạn khởi tạo (trong trường hợp có khiếu nại, trọng tài sẽ xem dữ liệu tương tự như Đối tác giao dịch của bạn). account.menu.paymentAccount=Tài khoản tiền tệ quốc gia account.menu.altCoinsAccountView=Tài khoản Altcoin @@ -907,7 +924,7 @@ account.arbitratorRegistration.pubKey=Public key (địa chỉ ví) account.arbitratorRegistration.register=Đăng ký trọng tài account.arbitratorRegistration.revoke=Hủy bỏ đăng ký -account.arbitratorRegistration.info.msg=Please note that you need to stay available for 15 days after revoking as there might be trades which are using you as arbitrator. The max. allowed trade period is 8 days and the dispute process might take up to 7 days. +account.arbitratorRegistration.info.msg=Lưu ý rằng bạn cần phải luôn sẵn sàng trong 15 ngày sau khi hủy vì có thể có giao dịch mà bạn đóng vai trò là trọng tài. Thời gian cho phép tối đa cho giao dịch là 8 ngày và quá trình khiếu nại có thể mất 7 ngày. account.arbitratorRegistration.warn.min1Language=Bạn cần cài đặt ít nhất 1 ngôn ngữ.\nChúng tôi thêm ngôn ngữ mặc định cho bạn. account.arbitratorRegistration.removedSuccess=Bạn đã gỡ bỏ thành công trọng tài của bạn ra khỏi mạng P2P. account.arbitratorRegistration.removedFailed=Không thể gỡ bỏ trọng tài.{0} @@ -926,16 +943,18 @@ account.arbitratorSelection.noLang=Bạn chỉ có thể chọn trọng tài nó account.arbitratorSelection.minOne=Bạn cần có ít nhất một trọng tài được chọn. account.altcoin.yourAltcoinAccounts=Tài khoản altcoin của bạn -account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe arbitrator is not a {2} specialist and cannot help in such cases. +account.altcoin.popup.wallet.msg=Hãy chắc chắn bạn tuân thủ các yêu cầu để sử dụng ví {0} như quy định tại trang web {1}.\nSử dụng ví từ tổng đài giao dịch phân quyền nơi bạn (a) không được kiểm soát khóa của bạn hoặc (b) những sàn không sử dụng phần mềm ví tương thích đều rất mạo hiểm: việc này có thể dẫn đến mất số tiền đã giao dịch!\nTrọng tài không phải là một chuyên gia {2} và không thể giúp bạn trong những trường hợp như thế này. 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.xmr.msg=Trading XMR on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending XMR, you need to use either the official Monero GUI wallet or Monero 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.\nmonero-wallet-cli (use the command get_tx_key)\nmonero-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nIn addition to XMR checktx tool (https://xmr.llcoins.net/checktx.html) verification can also be accomplished in-wallet.\nmonero-wallet-cli : using command (check_tx_key).\nmonero-wallet-gui : on the Advanced > Prove/Check page.\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 XMR sender is responsible for providing verification of the XMR 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 (https://www.getmonero.org/resources/user-guides/prove-payment.html) or the Monero forum (https://forum.getmonero.org) to find more information. -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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). -account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=Khi sử dụng {0} bạn chỉ có thể sử dụng địa chỉ rõ ràng (bắt đầu bằng t) không phải địa chỉ z (riêng tư), vì trọng tài sẽ không thể xác minh giao dịch với địa chỉ z. -account.altcoin.popup.XZC.msg=Khi sử dụng {0} bạn chỉ có thể sử dụng địa chỉ rõ ràng (có thể theo dõi) chứ không phải địa chỉ không thể theo dõi, vì trọng tài không thể xác minh giao dịch với địa chỉ không thể theo dõi tại một block explorer. -account.altcoin.popup.bch=Bitcoin Cash và Bitcoin Clashic được bảo vệ không chào lại. Nếu bạn sử dụng các coin này, phải chắc chắn bạn đã thực hiện đủ biện pháp phòng ngừa và hiểu rõ tất cả nội dung. Bạn có thể bị mất khi gửi một coin và vô tình gửi coin như vậy cho block chain khác. Do các "airdrop coins" này có cùng lịch sử với Bitcoin blockchain do đó có một số nguy cơ mất an toàn và quyền riêng tư.\n\nVui lòng đọc tại diễn đàn Bisq để biết thêm về chủ đề này: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Do Bitcoin Gold có cùng lịch sử với Bitcoin blockchain nên có nguy cơ mất bảo mật và quyền riêng tư. Nếu bạn sử dụng Bitcoin Gold chắc chắn bạn đã thực hiện đủ biện pháp phòng ngừa và hiểu rõ tất cả nội dung.\n\nVui lòng đọc tại Diễn đàn Bisq để biết thêm về chủ đề này: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +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 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 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=Để giao dịch XMR trên Bisq, bạn phải hiểu và tuân thủ các yêu cầu sau:\n\nĐể gửi XMR, bạn cần sử dụng hoặc là ví Monero GUI chính thức hoặc ví Monero CLI với nhãn luu-thongtin-tx được kích hoạt (mặc định trong phiên bản mới). Chắc chắn rằng bạn có thể truy cập khóa tx vì điều này là cần thiết trong trường hợp khiếu nại.\nví-monero-cli (dùng lệnh get_tx_key) \nví-monero-gui (đi đến tab lịch sử và nhấp vào nút (P) để lấy bằng chứng thanh toán)\n\nNgoài công cụ kiểm tra giao dịch XMR (https://xmr.llcoins.net/checktx.html) việc xác minh cũng có thể được thực hiện ngay trên ví.\nví-monero-cli: dùng lệnh (check_tx_key).\nví-monero-gui: ở trên Nâng cao>trang Chứng minh/Kiểm tra.\nVới những block explorer thông thường, chuyển khoản không thể xác minh được.\n\nBạn cần cung cấp cho trọng tài các dữ liệu sau trong trường hợp khiếu nại:\n- Khóa tx riêng\n- Hash của giao dịch\n- Địa chỉ công cộng của người nhận\n\nNếu không cung cấp các dữ liệu trên hoặc nếu bạn sử dụng ví không thích hợp sẽ dẫn tới thua trong vụ khiếu nại. Người gửi XMR chịu trách nhiệm xác minh chuyển khoản XMR cho trọng tài trong trường hợp khiếu nại.\n\nKhông cần có ID thanh toán, chỉ cần địa chỉ công cộng thông thường.\nNếu bạn không chắc chắn về quy trình này, truy cập (https://www.getmonero.org/resources/user-guides/prove-payment.html) hoặc diễn đàn Monero (https://forum.getmonero.org) để tìm thêm thông tin. +account.altcoin.popup.blur.msg=Để giao dịch BLUR trên Bisq, bạn phải hiểu và tuân thủ các yêu cầu sau:\n\nĐể gửi BLUR, bạn cần sử dụng ví CLI hoặc GUI của mạng BLUR. \n\nNếu bạn sử dụng ví CLI, sau khi chuyển tiền, ví sẽ hiển thị một mã giao dịch (tx ID). Bạn phải lưu thông tin này lại. Ngay sau khi chuyển tiền, bạn phải dùng lệnh 'get_tx_key' để lấy khóa riêng của giao dịch. Nếu bạn không thực hiện bước này, rất có thể sau này bạn sẽ không lấy được khóa giao dịch nữa. \n\nNếu bạn sử dụng ví GUI của mạng BLUR, khóa riêng giao dịch và tx ID sẽ được hiển thị ngay trên tab "Lịch sử". Ngay sau khi chuyển tiền, xác định vị trí của giao dịch mà bạn quan tâm. Nhấp chuột vào biểu tượng "?" ở góc phải bên dưới của hộp chứa giao dịch. Bạn cần phải lưu thông tin này lại. \n\nTrường hợp tranh chấp, bạn phải đưa ra cho trọng tài những thông tin sau:1.) mã giao dịch, 2.) khóa riêng của giao dịch, 3.) địa chỉ ví của người nhận. Trọng tài lúc đó sẽ sử dụng Blur Transaction Viewer (https://blur.cash/#tx-viewer) để xác minh giao dịch chuyển BLUR .\n\nNếu không cung cấp các thông tin yêu cầu, bạn sẽ thua trong lần tranh chấp này. Trong tất cả mọi trường hợp tranh chấp, người chuyển BLUR chịu 100% trách nhiệm xác minh giao dịch với trọng tài.\n\nNếu bạn vẫn chưa hiểu rõ về những yêu cầu này, vui lòng vào Discord của BLUR (https://discord.gg/dMWaqVW) để được hỗ trợ. +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). +account.altcoin.popup.drgl.msg=Để giao dịch Dragonglass trên Bisq, bạn cần phải hiểu và tuân thủ các yêu cầu sau:\n\nĐể đảm bảo tính riêng tư, những giao dịch do Dragonglass cung cấp không thể xác minh bằng blockchain công cộng. Nếu được yêu cầu, bạn có thể chứng minh giao dịch thanh toán của bạn bằng cách sử dụng khóa-riêng-TXN.\nKhóa riêng-TXN là khóa dùng một lần được tạo tự động cho mỗi giao dịch và chỉ có thể truy cập thông qua ví DRGL của bạn. \nDù là giao dịch được thực hiện trên ví DRGL GUI (trong hội thoại chi tiết giao dịch) hay bằng ví giản đơn Dragonglass CLI (dùng lệnh "get_tx_key"), thì đều cần phải có phiên bản Dragonglass 'Oathkeeper' hoặc cao hơn.\n\nTrường hợp tranh chấp, bạn cần phải cung cấp cho trọng tài những dữ liệu sau:\n- Khóa riêng-TXN\n- Mã giao dịch\n- Địa chỉ công cộng của người nhận\n\nViệc xác minh có thể được thực hiện bằng cách sử dụng những thông tin ở trên làm dữ liệu nhập tại (http://drgl.info/#check_txn).\n\nNếu không cung cấp các thông tin trên, hoặc nếu bạn sử dụng một ví không hợp lệ, có thể khiến bạn thua trong lần tranh chấp này. Người gửi Dragonglass chịu trách nhiệm xác minh giao dịch chuyển DRGL cho trọng tài trong trường hợp có tranh chấp. Việc sử dụng ID thanh toán không được yêu cầu. \n\nNếu như bạn vẫn chưa chắc chắn về bất cứ phần nào trong quá trình này,liên hệ Dragonglass trên Discord (http://discord.drgl.info) để được hỗ trợ. +account.altcoin.popup.ZEC.msg=Khi sử dụng Zcash bạn chỉ có thể sử dụng địa chỉ rõ ràng (bắt đầu bằng t) không phải địa chỉ z (riêng tư), vì trọng tài sẽ không thể xác minh giao dịch với địa chỉ z. +account.altcoin.popup.XZC.msg=Khi sử dụng Zcoin bạn chỉ có thể sử dụng địa chỉ rõ ràng (có thể theo dõi) chứ không phải địa chỉ không thể theo dõi, vì trọng tài không thể xác minh giao dịch với địa chỉ không thể theo dõi tại một block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=Các tài khoản tiền tệ quốc gia của bạn @@ -954,15 +973,15 @@ account.password.removePw.button=Gỡ bỏ mật khẩu account.password.removePw.headline=Gỡ bỏ bảo vệ mật khẩu cho ví account.password.setPw.button=Cài đặt mật khẩu account.password.setPw.headline=Cài đặt bảo vệ mật khẩu cho ví -account.password.info=With password protection you'll need to enter your password at application startup, when withdrawing bitcoin out of your wallet, and when restoring your wallet from seed words. +account.password.info=Bằng cách bảo vệ với mật khẩu, bạn cần nhập mật khẩu khi khởi động ứng dụng, khi rút bitcoin ra khỏi ví hoặc khi bạn muốn khôi phục ví từ các từ khởi tạo. account.seed.backup.title=Sao lưu dự phòng từ khởi tạo ví của bạn -account.seed.info=Please write down both wallet seed words and the date! You can recover your wallet any time with seed words and the date.\nThe same seed words are used for the BTC and BSQ wallet.\n\nYou should write down the seed words on a sheet of paper. Do not save them on your computer.\n\nPlease 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! +account.seed.info=Hãy viết ra từ khởi tạo ví của bạn và ngày! Bạn có thể khôi phục ví của bạn bất cứ lúc nào với các từ khởi tạo và ngày này.\nTừ khởi tạo được sử dụng chung cho cả ví BTC và BSQ.\n\nBạn nên viết các từ khởi tạo ra tờ giấy. Không được lưu trên máy tính.\n\nLưu ý rằng từ khởi tạo KHÔNG PHẢI là phương án thay thế cho sao lưu dự phòng.\nBạn cần sao lưu dự phòng toàn bộ thư mục của ứng dụng tại màn hình \"Tài khoản/Sao lưu dự phòng\" để khôi phục trạng thái và dữ liệu ứng dụng.\nNhập từ khởi tạo chỉ được thực hiện trong tình huống khẩn cấp. Ứng dụng sẽ không hoạt động mà không có dự phòng các file dữ liệu và khóa phù hợp! account.seed.warn.noPw.msg=Bạn đã tạo mật khẩu ví để bảo vệ tránh hiển thị Seed words.\n\nBạn có muốn hiển thị Seed words? account.seed.warn.noPw.yes=Có và không hỏi lại account.seed.enterPw=Nhập mật khẩu để xem seed words -account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state. -account.seed.restore.ok=Ok, tôi hiểu và muốn khôi phục +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Ok, do the restore and shut down Bisq #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=Giá thấp hơn ph # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=Ví BSQ dao.tab.proposals=Đề xuất dao.tab.bonding=Tài sản đảm bảo dao.tab.proofOfBurn=Phí niêm yết tài sản/ Bằng chứng đốt +dao.tab.monitor=Network monitor +dao.tab.news=News dao.paidWithBsq=thanh toán bằng BSQ -dao.availableBsqBalance=Hiện có -dao.availableNonBsqBalance=Số dư không phải BSQ có sẵn (tính bằng BTC) -dao.unverifiedBsqBalance=Chưa xác minh(đang đợi xác nhận blockchain) +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Balance of all verified UTXOs +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) dao.lockedForVoteBalance=Dùng để bỏ phiếu dao.lockedInBonds=Bị khóa trong hợp đồng +dao.availableNonBsqBalance=Số dư không phải BSQ có sẵn (tính bằng BTC) dao.totalBsqBalance=Tổng số dư BSQ dao.tx.published.success=Giao dịch của bạn đã được công bố thành công. @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Tổng quan vòng bỏ phiếu dao.cycle.currentPhase=Giai đoạn hiện tại dao.cycle.currentBlockHeight=Chiều cao khối hiện tại dao.cycle.proposal=Giai đoạn đề xuất +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=Mở để bỏ phiếu dao.cycle.voteReveal=Mở để công khai khóa bỏ phiếu dao.cycle.voteResult=Kết quả bỏ phiếu dao.cycle.phaseDuration={0} khối(≈{1}); Khối{2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=Các vòng dao.results.cycles.table.header.cycle=Vòng @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Kết quả bầu chọn dao.results.proposals.voting.detail.header=Kết quả của đề xuất được chọn +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Không xác định @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=Địa chỉ BTC của người nhận # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=Phí niêm yết tài sản/ ngày # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Khối lượng giao dịch tối thiểu +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Giá trị hiện tại: {0} dao.param.blocks={0} khối -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Thời lượng {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} (các) khối -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(giá trị mặc định) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(đã được thay đổi qua bỏ phiếu) +dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the P2P network.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Không xác định # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Khóa dao.bond.reputation.lockup.headline=Xác nhận giao dịch khóa -dao.bond.reputation.lockup.details=Số lượng khóa: {0}\nThời gian khóa: {1} khối\n\nBạn có thực sự muốn tiếp tục? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Xác nhận giao dịch mở khóa -dao.bond.reputation.unlock.details=Số lượng mở khóa: {0}\nThời gian mở khóa: {1} khối\n\nBạn có thực sự muốn tiếp tục? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=Tất cả cách tài sản đảm bảo @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Khóa dao.bond.table.button.unlock=Mở khóa dao.bond.table.button.revoke=Hủy +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Không xác định # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Chưa có tài sản đảm bảo # suppress inspection "UnusedProperty" @@ -1252,13 +1296,17 @@ dao.bond.bondState.UNLOCKED=Tài sản đảm bảo đã mở khóa # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Tài sản đảm bảo đã bị tịch thu +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Không xác định # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Vai trò đảm bảo # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Danh tiếng tài sản đảm bảo # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Quản trị Github +dao.bond.bondedRoleType.UNDEFINED=Không xác định +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=Quản trị diễn đàn # suppress inspection "UnusedProperty" @@ -1266,24 +1314,30 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=Quản trị Twitter # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Quản trị Rocket chat # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Quản trị Youtube +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Nhân viên bảo trì Bisq # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Điều hành website # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Điều hành diễn đàn # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Người chạy seed node # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Điều hành giá node +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Điều hành node btc +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" 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 # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Người giữ tên miền # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=Quản trị DNS @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=Quản trị DNS dao.bond.bondedRoleType.MEDIATOR=Người hòa giải # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Trọng tài +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=Phí niêm yết tài sản +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=Phí niêm yết tài sản dao.burnBsq.menuItem.proofOfBurn=Bằng chứng đốt dao.burnBsq.header=Phí để niêm yết tài sản @@ -1308,7 +1364,7 @@ dao.burnBsq.assets.lookBackPeriod=Giai đoạn xác minh dao.burnBsq.assets.trialFee=Phí cho giai đoạn dùng thử dao.burnBsq.assets.totalFee=Tổng số phí đã trả dao.burnBsq.assets.days={0} ngày -dao.burnBsq.assets.toFewDays=The asset fee is too low. The min. amount of days for the trial period is {0}. +dao.burnBsq.assets.toFewDays=Phí tài sản quá thấp. Số ngày tối thiểu cho giai đoạn dùng thử là {0}. # suppress inspection "UnusedProperty" dao.assetState.UNDEFINED=Không xác định @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=Ngày dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=Giao dịch dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Ký một tin nhắn dùng chìa khóa từ bằng chứng của giao dịch dốt -dao.proofOfBurn.verify.window.title=Xác minh một tin nhắn dùng chìa khóa từ bằng chứng của giao dịch dốt +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction dao.proofOfBurn.copySig=Sao chép chữ ký tới clipboard dao.proofOfBurn.sign=Ký dao.proofOfBurn.message=Tin nhắn dao.proofOfBurn.sig=Chữ ký dao.proofOfBurn.verify=Xác minh -dao.proofOfBurn.verify.header=Xác minh tin nhắn dùng chìa khóa từ bằng chứng của giao dịch dốt +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=Xác minh thành công dao.proofOfBurn.verificationResult.failed=Xác minh thất bại @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Công khai phiếu bầu # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Kết quả bỏ phiếu +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Không xác định # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=Yêu cầu bồi thường # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=Đề xuất chung # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Đề xuất tịch thu tài sản đảm bảo +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Không xác định # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=Yêu cầu bồi thường # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=Đề xuất chung # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Tịch thu tài sản đảm bảo - dao.proposal.details=Thông tin về đề xuất dao.proposal.selectedProposal=Đề xuất được chọn dao.proposal.active.header=Các đề xuất có hiệu lực dao.proposal.active.remove.confirm=Bạn có chắc bạn muốn gỡ bỏ đề xuất này?\nPhí đề xuất đã trả sẽ bị mất. dao.proposal.active.remove.doRemove=Vâng, xin hãy gở đề xuất của tôi dao.proposal.active.remove.failed=Không thể gỡ bỏ đề xuất. +dao.proposal.myVote.title=Bỏ phiếu dao.proposal.myVote.accept=Chấp nhận đề xuất dao.proposal.myVote.reject=Từ chối đề xuất dao.proposal.myVote.removeMyVote=Bỏ qua đề xuất dao.proposal.myVote.merit=Trọng lượng phiếu bầu từ BSQ kiếm được dao.proposal.myVote.stake=Tiền đầu tư BSQ để bỏ phiếu -dao.proposal.myVote.blindVoteTxId=Mã giao dịch bỏ phiếu mù dao.proposal.myVote.revealTxId=Mã giao dịch công khai phiếu bầu -dao.proposal.myVote.stake.prompt=Số dư còn lại tối đa để bỏ phiếu: {0} -dao.proposal.votes.header=Các phiếu bầu -dao.proposal.votes.header.voted=Phiếu bầu của tôi -dao.proposal.myVote.button=Bỏ phiếu trên tất cả đề xuất +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Chọn kiểu đề xuất +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=Kiểu đề xuất -dao.proposal.create.createNew=Tạo đề xuất mới -dao.proposal.create.create.button=Tạo đề xuất +dao.proposal.create.new=Tạo đề xuất mới +dao.proposal.create.button=Tạo đề nghị +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=đề xuất dao.proposal.display.type=Kiểu đề xuất dao.proposal.display.name=Tên/nickname dao.proposal.display.link=Link đến thông tin chi tiết -dao.proposal.display.link.prompt=Link đến vấn đề Github +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=Số lượng yêu cầu tính theo BSQ dao.proposal.display.bsqAddress=Địa chỉ BSQ dao.proposal.display.txId=ID giao dịch đề xuất @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=Chấp nhận dao.proposal.display.myVote.rejected=Từ chối dao.proposal.display.myVote.ignored=Bỏ qua dao.proposal.myVote.summary=Đã bầu: {0}; Sức nặng phiếu bầu: {1} (kiếm được: {2} + cược: {3}); +dao.proposal.myVote.invalid=Vote was invalid dao.proposal.voteResult.success=Chấp nhận dao.proposal.voteResult.failed=Từ chối @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=Tài sản cần gỡ bỏ dao.blindVote=bỏ phiếu mù dao.blindVote.startPublishing=Công bố giao dịch bỏ phiếu mù... -dao.blindVote.success=Giao dịch bỏ phiếu mù của bạn đã công bố thành công +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=Gửi dao.wallet.menuItem.receive=Nhận dao.wallet.menuItem.transactions=Giao dịch dao.wallet.dashboard.myBalance=Số dư trong ví của tôi -dao.wallet.dashboard.distribution=Sự phân phối của tất cả BSQ -dao.wallet.dashboard.locked=Trạng thái toàn cầu của BSQ đã khóa -dao.wallet.dashboard.market=Dữ liệu thị trường -dao.wallet.dashboard.genesis=Giao dịch chung -dao.wallet.dashboard.txDetails=Thống kê giao dịch BSQ -dao.wallet.dashboard.genesisBlockHeight=Chiều cao khối Genesis -dao.wallet.dashboard.genesisTxId=ID giao dịch Genesis -dao.wallet.dashboard.genesisIssueAmount=Lượng BSQ phát hành tại giao dịch Genesis -dao.wallet.dashboard.compRequestIssueAmount=Lượng BSQ phát hành dành cho yêu cầu bồi thường -dao.wallet.dashboard.reimbursementAmount=Lượng BSQ phát hành dành cho yêu cầu bồi hoàn -dao.wallet.dashboard.availableAmount=Tổng lượng BSQ hiện có -dao.wallet.dashboard.burntAmount=Lượng BSQ đã đốt (các loại phí) -dao.wallet.dashboard.totalLockedUpAmount=Bị khóa làm tải sản đảm bảo -dao.wallet.dashboard.totalUnlockingAmount=Đang mở khóa BSQ từ tài sản đảm bảo -dao.wallet.dashboard.totalUnlockedAmount=BSQ đã được mở khóa từ tài sản đảm bảo -dao.wallet.dashboard.totalConfiscatedAmount=Lượng BSQ đã tịch thu từ tài sản đảm bảo -dao.wallet.dashboard.allTx=Tổng số giao dịch BSQ -dao.wallet.dashboard.utxo=Tổng đầu ra giao dịch chưa sử dụng -dao.wallet.dashboard.compensationIssuanceTx=Tổng số lượng giao dịch phát hành yêu cầu bồi thường -dao.wallet.dashboard.reimbursementIssuanceTx=Tổng số lượng giao dịch phát hành yêu cầu bồi hoàn -dao.wallet.dashboard.burntTx=Tổng số lượng giao dịch thanh toán phí -dao.wallet.dashboard.price=Giá giao dịch BSQ/BTC gần nhất (tính bằng BSQ) -dao.wallet.dashboard.marketCap=Vốn hóa thị trường (dựa trên giá giao dịch) - -dao.wallet.receive.fundYourWallet=Nộp tiền ví BSQ của bạn -dao.wallet.receive.bsqAddress=Địa chỉ ví BSQ + +dao.wallet.receive.fundYourWallet=Your BSQ receive address +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=The Bisq DAO +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=Gửi vốn dao.wallet.send.sendBtcFunds=Gửi các loại tiền không phải BSQ (BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=Khối đã xác minh mới nhất: {0} dao.wallet.chainHeightSyncing=Đang chờ khối mới... Đã xác nhận{0} / {1} khối dao.wallet.tx.type=Loại +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Không xác định # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Không xác định # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=Yêu cầu bồi thường/ban hành dao.tx.issuanceFromCompReq.tooltip=Yêu cầu bồi thường dẫn đến ban hành BSQ mới.\nNgày ban hành: {0} dao.tx.issuanceFromReimbursement=Yêu cầu/ Phát hành bồi hoàn dao.tx.issuanceFromReimbursement.tooltip=Yêu cầu bồi hoàn dẫn đến ban hành BSQ mới.\nNgày ban hành: {0} -dao.proposal.create.missingBsqFunds=Bạn không có đủ tiền để tạo đề xuất.\nThiếu: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=Xác nhận {0} giao dịch dao.feeTx.confirm.details={0} phí: {1}\nPhí đào: {2} ({3} Satoshis/byte)\nKích thước giao dịch: {4} Kb\n\nBạn có chắc là muốn công bố giao dịch {5}? +dao.news.bisqDAO.title=THE BISQ DAO +dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. +dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO + +dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ +dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.news.pastContribution.yourAddress=Your BSQ Wallet Address +dao.news.pastContribution.requestNow=Request now + +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. +dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ +dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. +dao.news.DAOOnTestnet.readMoreLink=Read the full documentation + +dao.monitor.daoState=DAO state +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Conflicts +dao.monitor.state=Trạng thái +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=DAO state +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Block height +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Previous hash +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Previous hash +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Previous hash +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Dữ liệu thị trường +dao.factsAndFigures.dashboard.price=Giá giao dịch BSQ/BTC gần nhất (tính bằng BSQ) +dao.factsAndFigures.dashboard.marketCap=Vốn hóa thị trường (dựa trên giá giao dịch) +dao.factsAndFigures.dashboard.availableAmount=Tổng lượng BSQ hiện có + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=Lượng BSQ phát hành tại giao dịch Genesis +dao.factsAndFigures.supply.compRequestIssueAmount=Lượng BSQ phát hành dành cho yêu cầu bồi thường +dao.factsAndFigures.supply.reimbursementAmount=Lượng BSQ phát hành dành cho yêu cầu bồi hoàn + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Trạng thái toàn cầu của BSQ đã khóa +dao.factsAndFigures.supply.totalLockedUpAmount=Bị khóa làm tải sản đảm bảo +dao.factsAndFigures.supply.totalUnlockingAmount=Đang mở khóa BSQ từ tài sản đảm bảo +dao.factsAndFigures.supply.totalUnlockedAmount=BSQ đã được mở khóa từ tài sản đảm bảo +dao.factsAndFigures.supply.totalConfiscatedAmount=Lượng BSQ đã tịch thu từ tài sản đảm bảo +dao.factsAndFigures.supply.burntAmount=Lượng BSQ đã đốt (các loại phí) + +dao.factsAndFigures.transactions.genesis=Giao dịch chung +dao.factsAndFigures.transactions.genesisBlockHeight=Chiều cao khối Genesis +dao.factsAndFigures.transactions.genesisTxId=ID giao dịch Genesis +dao.factsAndFigures.transactions.txDetails=Thống kê giao dịch BSQ +dao.factsAndFigures.transactions.allTx=Tổng số giao dịch BSQ +dao.factsAndFigures.transactions.utxo=Tổng đầu ra giao dịch chưa sử dụng +dao.factsAndFigures.transactions.compensationIssuanceTx=Tổng số lượng giao dịch phát hành yêu cầu bồi thường +dao.factsAndFigures.transactions.reimbursementIssuanceTx=Tổng số lượng giao dịch phát hành yêu cầu bồi hoàn +dao.factsAndFigures.transactions.burntTx=Tổng số lượng giao dịch thanh toán phí #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Node cung cấp thông tin đã lọc (địa chỉ onion filterWindow.priceRelayNode=nút rơle giá đã lọc (địa chỉ onion cách nhau bằng dấu phẩy) 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=Disable DAO filterWindow.add=Thêm bộ lọc filterWindow.remove=Gỡ bỏ bộ lọc @@ -1748,7 +1896,7 @@ popup.headline.error=Lỗi popup.doNotShowAgain=Không hiển thị lại popup.reportError.log=Mở log file popup.reportError.gitHub=Báo cáo cho người theo dõi vấn đề GitHub -popup.reportError={0}\n\nTo help us to improve the software please report this bug by opening a new issue at https://github.com/bisq-network/bisq/issues.\nThe above error message will be copied to the clipboard when you click either of the buttons below.\nIt will make debugging easier if you include the bisq.log file by pressing "Open log file", saving a copy, and attaching it to your bug report. +popup.reportError={0}\n\nĐể giúp chúng tôi cải tiến phần mềm, vui lòng báo cáo lỗi này bằng cách mở một thông báo vấn đề mới tại https://github.com/bisq-network/bisq/issues.\nTin nhắn lỗi phía trên sẽ được sao chép tới clipboard khi bạn ấn vào một nút bên dưới.\nSự cố sẽ được xử lý dễ dàng hơn nếu bạn đính kèm bisq.log file bằng cách nhấn "Mở log file", lưu bản sao, và đính kèm vào báo cáo lỗi. popup.error.tryRestart=Hãy khởi động lại ứng dụng và kiểm tra kết nối mạng để xem bạn có thể xử lý vấn đề này hay không. popup.error.takeOfferRequestFailed=Có lỗi xảy ra khi ai đó cố gắng để nhận một trong các chào giá của bạn:\n{0} @@ -1785,7 +1933,7 @@ popup.warning.nodeBanned=Một trong {0} Node đã bị chấm. Vui lòng khởi popup.warning.priceRelay=rơle giá popup.warning.seed=seed -popup.info.securityDepositInfo=To ensure both traders follow the trade protocol, both traders need to pay a security deposit.\n\nThis deposit is kept in your trade wallet until your trade has been successfully completed, and then it's refunded to you.\n\nPlease note: if you're creating a new offer, Bisq needs to be running for another trader to take it. To keep your offers online, keep Bisq running and make sure this computer remains online too (i.e., make sure it doesn't switch to standby mode...monitor standby is fine). +popup.info.securityDepositInfo=Để đảm bảo cả hai người giao dịch đều tuân thủ giao thức giao dịch, cả hai cần phải trả một khoản tiền cọc. \n\nSố tiền cọc này được giữ ở ví giao dịch cho đến khi giao dịch của bạn được hoàn thành, sau đó nó sẽ được trả lại cho bạn. \nXin lưu ý: Nếu bạn tạo một chào giá mới, ứng dụng Bisq cần phải chạy để người giao dịch khác có thể nhận chào giá đó. Để giữ cho chào giá của bạn online, để Bisq chạy và đảm bảo là máy tính của bạn cũng online (nghĩa là đảm bảo là máy tính của bạn không chuyển qua chế độ standby, nếu màn hình chuyển qua chế độ standby thì không sao). 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 @@ -1807,8 +1955,8 @@ popup.attention.forTradeWithId=Cần chú ý khi giao dịch có ID {0} popup.roundedFiatValues.headline=Tính năng bảo mật mới: Giá trị tiền pháp định đã làm tròn popup.roundedFiatValues.msg=Để tăng tính bảo mật, lượng {0} đã được làm tròn.\n\nTùy vào bạn đang sử dụng phiên bản nào của ứng dụng mà bạn sẽ phải trả hay nhận được hoặc là giá trị thập phân hoặc là giá trị đã làm tròn. \n\nCả hai giá trị này từ đây sẽ tuân theo giao thức giao dịch.\n\nCũng nên lưu ý là giá trị BTC sẽ tự động thay đổi sao cho khớp nhất có thể với lượng tiền pháp định đã được làm tròn. -popup.info.multiplePaymentAccounts.headline=Multiple payment accounts available -popup.info.multiplePaymentAccounts.msg=You have multiple payment accounts available for this offer. Please make sure you've picked the right one. +popup.info.multiplePaymentAccounts.headline=Có sẵn nhiều tài khoản thanh toán +popup.info.multiplePaymentAccounts.msg=Bạn có sẵn nhiều tài khoản thanh toán cho chào giá này. Vui lòng đảm bảo là bạn chọn đúng tài khoản. #################################################################### @@ -1955,6 +2103,10 @@ BTC_MAINNET=Bitcoin Mainnet BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=Năm time.month=Tháng @@ -2025,6 +2177,8 @@ payment.country=Quốc gia payment.extras=Yêu cầu thêm payment.email.mobile=Email hoặc số điện thoại payment.altcoin.address=Địa chỉ Altcoin +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=Altcoin payment.select.altcoin=Chọn hoặc tìm altcoin payment.secret=Câu hỏi bí mật @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=Email hoặc số điện thoại payment.venmo.venmoUserName=Tên người dùng Venmo payment.popmoney.accountId=Email hoặc số điện thoại -payment.revolut.email=Email hoặc số điện thoại +payment.revolut.email=Email +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=ID công dân/ ID thuế hoặc số điện thoại payment.supportedCurrencies=Tiền tệ hỗ trợ payment.limitations=Hạn chế @@ -2077,11 +2232,15 @@ payment.clearXchange.info=Chắc chắc bạn đáp ứng các yêu cầu khi s payment.moneyGram.info=Khi dùng MoneyGram người mua BTC phải gửi số xác nhận và ảnh chụp hoá đơn đến emailnguoiwf bán. Hoá đơn phải chỉ rõ tên đầy đủ, quốc gia, tiểu bang và số lượng. Người mua sẽ trình email của người bán ra trong quá trình giao dịch payment.westernUnion.info=Khi sử dụng Western Union, người mua BTC phải gửi MTCN (số theo dõi) và ảnh giấy biên nhận bằng email cho người bán BTC. Giấy biên nhận phải nêu rõ họ tên, thành phố, quốc gia của người bán và số tiền. Người mua sẽ được hiển thị email người bán trong quá trình giao dịch. -payment.halCash.info=When using HalCash the BTC buyer needs to send the BTC seller the HalCash code via a text message from their mobile phone.\n\nPlease make sure to not exceed the maximum amount your bank allows you to send with HalCash. The min. amount per withdrawal is 10 EUR and the max. amount is 600 EUR. For repeated withdrawals it is 3000 EUR per receiver per day and 6000 EUR per receiver per month. Please cross check those limits with your bank to be sure they use the same limits as stated here.\n\nThe withdrawal amount must be a multiple of 10 EUR as you cannot withdraw other amounts from an ATM. The UI in the create-offer and take-offer screen will adjust the BTC amount so that the EUR amount is correct. You cannot use market based price as the EUR amount would be changing with changing prices.\n\nIn case of a dispute the BTC buyer needs to provide the proof that they sent the EUR. +payment.halCash.info=Khi sử dụng HalCash người mua BTC cần phải gửi cho người bán BTC mã HalCash bằng tin nhắn điện thoại.\n\nVui lòng đảm bảo là lượng tiền này không vượt quá số lượng tối đa mà ngân hàng của bạn cho phép gửi khi dùng HalCash. Số lượng rút tối thiểu là 10 EUR và tối đa là 600 EUR. Nếu rút nhiều lần thì giới hạn sẽ là 3000 EUR/ người nhận/ ngày và 6000 EUR/người nhận/tháng. Vui lòng kiểm tra chéo những giới hạn này với ngân hàng của bạn để chắc chắn là họ cũng dùng những giới hạn như ghi ở đây.\n\nSố tiền rút phải là bội số của 10 EUR vì bạn không thể rút các mệnh giá khác từ ATM. Giao diện người dùng ở phần 'tạo chào giá' và 'chấp nhận chào giá' sẽ điều chỉnh lượng btc sao cho lượng EUR tương ứng sẽ chính xác. Bạn không thể dùng giá thị trường vì lượng EUR có thể sẽ thay đổi khi giá thay đổi.\n\nTrường hợp tranh chấp, người mua BTC cần phải cung cấp bằng chứng chứng minh mình đã gửi EUR. payment.limits.info=Hãy hiểu rằng tất cả giao dịch chuyển khoản ngân hàng đều có thể có rủi ro bị đòi tiền lại.\n\nĐể hạn chế rủi ro này, Bisq đã đặt giới hạn trên mỗi giao dịch dựa trên hai yếu tố:\n\n1. Mức rủi ro đòi tiền lại ước tính cho mỗi phương thức thanh toán áp dụng\n2. Tuổi tài khoản của bạn đối với phương thức thanh toán\n\nTài khoản bạn đang tạo là mới và tuổi bằng không. Sau khi tài khoản của bạn được hai tháng tuổi, giới hạn trên mỗi giao dịch của bạn sẽ tăng lên theo:\n\n● Trong tháng đầu tiên, giới hạn trên mỗi giao dịch của bạn là {0}\n● Trong tháng thứ hai, giới hạn trên mỗi giao dịch của bạn là {1}\n● Sau hai tháng, giới hạn trên mỗi giao dịch của bạn là {2}\n\nLưu ý không có giới hạn trên tổng số lần bạn có thể giao dịch. payment.cashDeposit.info=Vui lòng xác nhận rằng ngân hàng của bạn cho phép nạp tiền mặt vào tài khoản của người khác. Chẳng hạn, Ngân Hàng Mỹ và Wells Fargo không còn cho phép nạp tiền như vậy nữa. +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=thông tin liên hệ payment.f2f.contact.prompt=Bạn muốn liên hệ với đối tác giao dịch qua đâu? (email, địa chỉ, số điện thoại,....) payment.f2f.city=Thành phố để gặp mặt trực tiếp @@ -2090,7 +2249,7 @@ payment.f2f.optionalExtra=Thông tin thêm tuỳ chọn. payment.f2f.extra=thông tin thêm payment.f2f.extra.prompt=Người tạo có thể đặt ‘ các điều khoản’ hoặc thêm thông tin liên hệ mở, để hiểnnthij trong báo giá -payment.f2f.info='Face to Face' trades have different rules and come with different risks than online transactions.\n\nThe main differences are:\n● The trading peers need to exchange information about the meeting location and time by using their provided contact details.\n● The trading peers need to bring their laptops and do the confirmation of 'payment sent' and 'payment received' at the meeting place.\n● If a maker has special 'terms and conditions' they must state those in the 'Additional information' text field in the account.\n● By taking an offer the taker agrees to the maker's stated 'terms and conditions'.\n● In case of a dispute the arbitrator cannot help much as it is usually hard to get tamper proof evidence of what happened at the meeting. In such cases the BTC funds might get locked indefinitely or until the trading peers come to an agreement.\n\nTo be sure you fully understand the differences with 'Face to Face' trades please read the instructions and recommendations at: 'https://docs.bisq.network/trading-rules.html#f2f-trading' +payment.f2f.info=Những giao dịch ‘mặt đối mặt’ đi kèm những quy tắc và rủi ro khác với giao dịch trực tuyến.\n\nNhững khác biệt chính đó là:\n● Bên mua và bán cần trao đổi thông tin về thời gian và địa điểm gặp mặt sử dụng những thông tin liên lạc mà họ cung cấp. \n● Bên mua và bán cần phải mang theo laptop và thực hiện việc xác nhận ‘đã gửi tiền’ và ‘đã nhận tiền’ tại nơi gặp.\n● Nếu bên chào giá có những ‘điều khoản và điều kiện’ đặc biệt, họ sẽ phải nêu ra ở phần ‘thông tin thêm’ trong tài khoản.\n● Chấp nhận chào giá tức là bên chấp nhận đã đồng ý với các ‘điều khoản và điều kiện’ của bên chào giá.\n● Trong trường hợp tranh chấp, trọng tài sẽ không giúp được gì nhiều bởi vì để cung cấp các bằng chứng không thể thay đổi về những việc xảy ra tại nơi gặp mặt thường là rất khó. Trong những trường hợp như vậy, số BTC này có thể sẽ bị khóa vô thời hạn hoặc khóa đến khi bên mua và bán đạt được thỏa thuận. \n\nĐể chắc chắn là bạn hiểu rõ sự khác nhau của các giao dịch ‘mặt đối mặt’vui lòng đọc hướng dẫn và khuyến nghị tại: 'https://docs.bisq.network/trading-rules.html#f2f-trading' payment.f2f.info.openURL=Mở trang web payment.f2f.offerbook.tooltip.countryAndCity=Đất nước và thành phố:{0}/{1} payment.f2f.offerbook.tooltip.extra=Thông tin thêm: {0} @@ -2128,16 +2287,10 @@ F2F_SHORT=F2F # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=Altcoins PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=Altcoins PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0} phải có {1} số. validation.invalidInput=Giá trị nhập không hợp lệ: {0} validation.accountNrFormat=Số tài khoản phải có định dạng : {0} validation.altcoin.wrongStructure=Xác nhận địa chỉ không thành công vì không khớp với cấu trúc của {0} địa chỉ. +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=Địa chỉ ZEC phải bắt đầu bằng t. Địa chỉ bắng đầu bằng z không được hỗ trợ. validation.altcoin.invalidAddress=Địa chỉ không phải là địa chỉ {0} hợp lệ! {1} validation.bic.invalidLength=Độ dài giá trị nhập không được là 8 hoặc 11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Chỉ được chứa chữ cái, s validation.interacETransfer.invalidAnswer=Phải được viết liền và chỉ bao gồm chữ cái, số, và/hoặc ký tự - validation.inputTooLarge=Giá trị nhập không được lớn hơn {0} validation.inputTooSmall=Giá trị nhập phải lớn hơn {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=Không cho phép giá trị nhỏ hơn giới hạn dust limit {0} validation.length=Chiều dài phải nằm trong khoảng từ {0} đến {1} validation.pattern=Giá trị nhập phải có định dạng: {0} validation.noHexString=Giá trị nhập không ở định dạng HEX validation.advancedCash.invalidFormat=Phải là một địa chỉ email hợp lệ hoặc là ID ví với định dạng: X000000000000 +validation.invalidUrl=This is not a valid URL +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/i18n/displayStrings_zh.properties b/core/src/main/resources/i18n/displayStrings_zh.properties index e8b874ccd2a..16898e0d645 100644 --- a/core/src/main/resources/i18n/displayStrings_zh.properties +++ b/core/src/main/resources/i18n/displayStrings_zh.properties @@ -104,7 +104,7 @@ shared.belowInPercent=低于市场价格% shared.aboveInPercent=高于市场价格% shared.enterPercentageValue=输入 % 值 shared.OR=或者 -shared.notEnoughFunds=您没有足够的资金在您的 Bisq 钱包.\n您需要 {0} 但是您只有 {1} 在您的 Bisq 钱包.\n\n请从外部比特币钱包注入资金或在 \"资金/存款\"充值到您的 Bisq 钱包. +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.waitingForFunds=等待资金充值... shared.depositTransactionId=存款交易ID shared.TheBTCBuyer=BTC 买家 @@ -113,12 +113,13 @@ shared.reasonForPayment=付款原因 shared.sendingConfirmation=发送确认... shared.sendingConfirmationAgain=请再次发送确认 shared.exportCSV=导出保存为.csv +shared.exportJSON=Export to JSON shared.noDateAvailable=没有可用数据 shared.arbitratorsFee=仲裁员费用 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.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired transaction 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.copyToClipboard=复制到剪贴板 shared.language=语言 shared.country=国家 @@ -196,6 +197,8 @@ shared.interval=Interval shared.actions=Actions shared.buyerUpperCase=Buyer shared.sellerUpperCase=Seller +shared.new=NEW +shared.blindVoteTxId=Blind vote transaction ID #################################################################### # UI views @@ -231,9 +234,10 @@ mainView.balance.locked.short=Locked mainView.footer.usingTor=(使用 Tor 中) mainView.footer.localhostBitcoinNode=(主机) mainView.footer.btcInfo=比特币网络节点: {0} / {1} {2} -mainView.footer.btcInfo.initializing=初始化 -mainView.footer.btcInfo.synchronizedWith=同步至 -mainView.footer.btcInfo.connectingTo=连接至 +mainView.footer.btcInfo.initializing=Connecting to Bitcoin network +mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO +mainView.footer.btcInfo.synchronizedWith=Synchronized with +mainView.footer.btcInfo.connectingTo=Connecting to mainView.footer.btcInfo.connectionFailed=连接失败 mainView.footer.p2pInfo=P2P 网络节点: {0} @@ -362,13 +366,14 @@ createOffer.amountPriceBox.amountDescription=比特币数量{0} createOffer.amountPriceBox.buy.volumeDescription=花费{0}数量 createOffer.amountPriceBox.sell.volumeDescription=接收 {0} 数量 createOffer.amountPriceBox.minAmountDescription=最小BTC数量 -createOffer.securityDeposit.prompt=BTC 保证金 +createOffer.securityDeposit.prompt=保证金 createOffer.fundsBox.title=为您的委托充值 createOffer.fundsBox.offerFee=挂单费 createOffer.fundsBox.networkFee=Mining fee createOffer.fundsBox.placeOfferSpinnerInfo=正在发布委托中... createOffer.fundsBox.paymentLabel=Bisq交易ID {0} createOffer.fundsBox.fundsStructure=({0} 保证金, {1} 交易费, {2} 采矿费) +createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=你的委托已经发布 createOffer.success.info=你可以在 \"业务/未完成委托\"页面内管理您的未完成委托. createOffer.info.sellAtMarketPrice=由于您的价格是持续更新的,因此您将始终以市场价格进行出售。 @@ -387,6 +392,7 @@ createOffer.alreadyFunded=您已经为该委托充值了.\n您的资金已经划 createOffer.createOfferFundWalletInfo.headline=为您的委托充值 # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- 交易数量: {0}\n +createOffer.createOfferFundWalletInfo.feesWithBSQ={0} and {1} createOffer.createOfferFundWalletInfo.msg=这个委托您需要{0} 作为保证金。\n\n这些资金保留在您的本地钱包并会被冻结到多重签名保证金地址直到委托交易成功。\n\n总数量:{1}\n- 保证金: {2}\n- 挂单费: {3}\n- 矿工手续费: {4}\n\n您有两种选项可以充值您的交易:\n- 使用您的 Bisq 钱包 (方便,但交易可能会被链接 ) 或者\n- 从外部钱包转入(或许这样更隐秘一些)\n\n关闭此弹出窗口后,您将看到所有资金选项和详细信息。 # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) @@ -404,7 +410,10 @@ createOffer.priceOutSideOfDeviation=您输入的价格超过了市场价差价 createOffer.changePrice=改变价格 createOffer.tac=发布该委托,我同意与满足该定义的条件的任何交易者进行交易。 createOffer.currencyForFee=挂单费 -createOffer.setDeposit=设置买家的保证金 +createOffer.setDeposit=Set buyer's security deposit (%) +createOffer.setDepositAsBuyer=Set my security deposit as buyer (%) +createOffer.securityDepositInfo=Your buyer''s security deposit will be {0} +createOffer.securityDepositInfoAsBuyer=Your security deposit as buyer will be {0} #################################################################### @@ -625,6 +634,14 @@ tradeFeedbackWindow.msg.part1=We'd love to hear back from you about your experie tradeFeedbackWindow.msg.part2=If you have any questions, or experienced any problems, please get in touch with other users and contributors via the Bisq forum at: tradeFeedbackWindow.msg.part3=Thanks for using Bisq! + +daoTestingFeedbackWindow.title=Thank you for testing the Bisq DAO +daoTestingFeedbackWindow.msg.part1=Can you spare 3 minutes to do a quick survey? We're offering 20 BSQ for completed surveys.\nYour feedback is crucial to ensuring a smooth launch on mainnet. +daoTestingFeedbackWindow.surveyLinkLabel=Do survey +daoTestingFeedbackWindow.msg.part2=Questions, or other issues? Discuss with Bisq users and contributors on the forum: +daoTestingFeedbackWindow.forumLinkLabel=Visit forum +daoTestingFeedbackWindow.msg.part3=Thanks for using Bisq! + portfolio.pending.role=我的角色 portfolio.pending.tradeInformation=交易信息 portfolio.pending.remainingTime=剩余时间 @@ -769,11 +786,11 @@ support.buyerTaker=BTC 买家/买单者 support.sellerTaker=BTC 卖家/买单者 support.backgroundInfo=Bisq is not a company, so it handles disputes differently.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display an \"Open dispute\" button after the trade period is over for contacting the arbitrator.\n\nIf there is an issue with the application, the software will try to detect it and, if possible, display an \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIf you are having an issue and did not see the \"Open support ticket\" button, you can open a support ticket manually by selecting the trade causing issues under \"Portfolio/Open trades\" and hitting \"alt + o\" or \"option + o\". Please use this method only if you are sure that the software is not working as expected. If you have problems or questions, please review the FAQ on the bisq.network web page or post in the Bisq forum in the Support section. -support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrator's requests within 2 days.\n2. The maximum period for a dispute is 14 days.\n3. You need to cooperate with the arbitrator and provide the information they request to make your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system +support.initialInfo=Please enter a description of your problem in the text field below. Add as much information as possible to speed up dispute resolution time.\n\nHere is a check list for information you should provide:\n\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' button in the application?\n\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' button in the application?\n\t● Which version of Bisq are you using?\n\t● Which operating system are you using?\n\t● If you encountered an issue with failed transactions please consider switching to a new data directory.\n\t Sometimes the data directory gets corrupted and leads to strange bugs. \n\t See: https://docs.bisq.network/backup-recovery.html#switch-to-a-new-data-directory\n\nPlease make yourself familiar with the basic rules for the dispute process:\n\t● You need to respond to the arbitrator's requests within 2 days.\n\t● The maximum period for a dispute is 14 days.\n\t● You need to cooperate with the arbitrator and provide the information they request to make your case.\n\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\nYou can read more about the dispute process at: https://bisq.network/docs/exchange/arbitration-system support.systemMsg=系统消息: {0} -support.youOpenedTicket=您创建了帮助请求 -support.youOpenedDispute=您创建了一个纠纷请求\n\n{0} -support.peerOpenedTicket=您的交易对象已要求技术问题帮助。 请等待进一步的指示。 +support.youOpenedTicket=You opened a request for support.\n\n{0}\n\nBisq version: {1} +support.youOpenedDispute=You opened a request for a dispute.\n\n{0}\n\nBisq version: {1} +support.peerOpenedTicket=Your trading peer has requested support due technical problems.\n\n{0} support.peerOpenedDispute=您的交易对象创建了一个纠纷\n\n{0} @@ -928,14 +945,16 @@ account.arbitratorSelection.minOne=您至少需要选择一名仲裁员 account.altcoin.yourAltcoinAccounts=Your altcoin accounts account.altcoin.popup.wallet.msg=Please be sure that you follow the requirements for the usage of {0} wallets as described on the {1} web page.\nUsing wallets from centralized exchanges where (a) you don''t control your keys or (b) which don''t use compatible wallet software is risky: it can lead to loss of the traded funds!\nThe arbitrator is not a {2} specialist and cannot help in such cases. account.altcoin.popup.wallet.confirm=我了解并确定我知道我需要哪种钱包。 +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 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 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\nFor sending XMR, you need to use either the official Monero GUI wallet or Monero 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.\nmonero-wallet-cli (use the command get_tx_key)\nmonero-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nIn addition to XMR checktx tool (https://xmr.llcoins.net/checktx.html) verification can also be accomplished in-wallet.\nmonero-wallet-cli : using command (check_tx_key).\nmonero-wallet-gui : on the Advanced > Prove/Check page.\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 XMR sender is responsible for providing verification of the XMR 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 (https://www.getmonero.org/resources/user-guides/prove-payment.html) or the Monero forum (https://forum.getmonero.org) to find more information. 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 arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The 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 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 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). -account.altcoin.popup.ccx.msg=Trading CCX on Bisq requires that you understand the following requirements:\n\nTo send CCX you must use an official Conceal wallet, either CLI or GUI. After sending a transfer payment, the wallets\ndisplay the transaction secret key. You must save it along with the transaction hash (ID) and the recipient's public\naddress in case arbitration is necessary. In such a case, you must give all three to the arbitrator, who will then\nverify the CCX transfer using the Conceal Transaction Viewer (https://explorer.conceal.network/txviewer).\nBecause Conceal is a privacy coin, block explorers cannot verify transfers.\n\nFailure to provide the required data to the arbitrator will result in losing the dispute case.\nIf you do not save the transaction secret key immediately after transferring CCX, it cannot be recovered later.\nIf you do not understand these requirements, seek help at the Conceal discord (http://discord.conceal.network). +account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN). +account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC). account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help. -account.altcoin.popup.ZEC.msg=当使用{0}时,您只能使用透明地址(以t开头)而不是z开头地址(私有),因为仲裁者无法使用z地址验证事务。 -account.altcoin.popup.XZC.msg=When using {0} you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. -account.altcoin.popup.bch=Bitcoin Cash and Bitcoin Clashic suffer from replay protection. If you use those coins be sure you take sufficient precautions and understand all implications.You can suffer losses by sending one coin and unintentionally send the same coins on the other block chain.Because those "airdrop coins" share the same history with the Bitcoin blockchain there are also security risks and a considerable risk for losing privacy.\n\nPlease read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc -account.altcoin.popup.btg=Because Bitcoin Gold shares the same history as the Bitcoin blockchain it comes with certain security risks and a considerable risk for losing privacy.If you use Bitcoin Gold be sure you take sufficient precautions and understand all implications.\n\nPlease read at the Bisq Forum more about that topic: https://forum.bisq.io/t/airdrop-coins-information-thread-bch-btg-bchc +account.altcoin.popup.ZEC.msg=When using Zcash you can only use the transparent addresses (starting with t) not the z-addresses (private), because the arbitrator would not be able to verify the transaction with z-addresses. +account.altcoin.popup.XZC.msg=When using Zcoin you can only use the transparent (traceable) addresses not the untraceable addresses, because the arbitrator would not be able to verify the transaction with untraceable addresses at a block explorer. +account.altcoin.popup.grin.msg=GRIN requires an interactive process between the sender and receiver to create the transaction. Be sure to follow the instructions from the GRIN project web page to reliably send and receive GRIN (the receiver needs to be online or at least be online during a certain time frame). \n\nBisq supports only the Grinbox (Wallet713) wallet URL format. \n\nThe GRIN sender is required to provide proof that they have sent GRIN successfully. If the wallet cannot provide that proof, a potential dispute will be resolved in favor of the GRIN receiver. Please be sure that you use the latest Grinbox software which supports the transaction proof and that you understand the process of transferring and receiving GRIN as well as how to create the proof. \n\nSee https://github.com/vault713/wallet713/blob/master/docs/usage.md#transaction-proofs-grinbox-only for more information about the Grinbox proof tool. +account.altcoin.popup.beam.msg=BEAM requires an interactive process between the sender and receiver to create the transaction. \n\nBe sure to follow the instructions from the BEAM project web page to reliably send and receive BEAM (the receiver needs to be online or at least be online during a certain time frame). \n\nThe BEAM sender is required to provide proof that they sent BEAM successfully. Be sure to use wallet software which can produce such a proof. If the wallet cannot provide the proof a potential dispute will be resolved in favor of the BEAM receiver. account.fiat.yourFiatAccounts=Your national currency accounts @@ -961,8 +980,8 @@ account.seed.info=Please write down both wallet seed words and the date! You can account.seed.warn.noPw.msg=您还没有设置一个可以保护还原密钥显示的钱包密码\n\n要显示还原密钥吗? account.seed.warn.noPw.yes=是的,不要再问我 account.seed.enterPw=输入密码查看还原密钥 -account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state. -account.seed.restore.ok=好的,我了解并想要恢复 +account.seed.restore.info=Please make a backup before applying restore from seed words. Be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database.\nIt is not a way for applying a backup! Please use a backup from the application data directory for restoring a previous application state.\n\nAfter restoring the application will shut down automatically. After you have restarted the application it will resync with the Bitcoin network. This can take a while and can consume a lot of CPU, especially if the wallet was older and had many transactions. Please avoid interrupting that process, otherwise you might need to delete the SPV chain file again or repeat the restore process. +account.seed.restore.ok=Ok, do the restore and shut down Bisq #################################################################### @@ -1035,17 +1054,22 @@ account.notifications.priceAlert.warning.lowerPriceTooHigh=The lower price must # DAO #################################################################### +dao.tab.factsAndFigures=Facts & Figures dao.tab.bsqWallet=BSQ 钱包 dao.tab.proposals=Governance dao.tab.bonding=Bonding dao.tab.proofOfBurn=Asset listing fee/Proof of burn +dao.tab.monitor=Network monitor +dao.tab.news=News dao.paidWithBsq=paid with BSQ -dao.availableBsqBalance=Available -dao.availableNonBsqBalance=Available non-BSQ balance (BTC) -dao.unverifiedBsqBalance=Unverified (awaiting block confirmation) +dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) +dao.verifiedBsqBalance=Balance of all verified UTXOs +dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs +dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) dao.lockedForVoteBalance=Used for voting dao.lockedInBonds=Locked in bonds +dao.availableNonBsqBalance=Available non-BSQ balance (BTC) dao.totalBsqBalance=BSQ 总额 dao.tx.published.success=你的交易已经成功发布 @@ -1058,10 +1082,15 @@ dao.cycle.overview.headline=Voting cycle overview dao.cycle.currentPhase=Current phase dao.cycle.currentBlockHeight=Current block height dao.cycle.proposal=Proposal phase +dao.cycle.proposal.next=Next proposal phase dao.cycle.blindVote=Blind vote phase dao.cycle.voteReveal=Vote reveal phase dao.cycle.voteResult=Vote result dao.cycle.phaseDuration={0} blocks (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) +dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) + +dao.voteReveal.txPublished.headLine=Vote reveal transaction published +dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. dao.results.cycles.header=Cycles dao.results.cycles.table.header.cycle=Cycle @@ -1080,6 +1109,8 @@ dao.results.proposals.table.header.result=Vote result dao.results.proposals.voting.detail.header=Vote results for selected proposal +dao.results.exceptions=Vote result exception(s) + # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Undefined @@ -1152,20 +1183,31 @@ dao.param.RECIPIENT_BTC_ADDRESS=Recipient BTC address # suppress inspection "UnusedProperty" dao.param.ASSET_LISTING_FEE_PER_DAY=Asset listing fee per day # suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. trade volume +dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets + +# suppress inspection "UnusedProperty" +dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx +# suppress inspection "UnusedProperty" +dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC + +# suppress inspection "UnusedProperty" +dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC + +# suppress inspection "UnusedProperty" +dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ +# suppress inspection "UnusedProperty" +dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ dao.param.currentValue=Current value: {0} dao.param.blocks={0} blocks -# suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Duration of {0} -# suppress inspection "UnusedProperty" dao.results.cycle.duration.value={0} block(s) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.isDefaultValue=(default value) -# suppress inspection "UnusedProperty" dao.results.cycle.value.postFix.hasChanged=(has been changed in voting) +dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the P2P network.\n{0} + # suppress inspection "UnusedProperty" dao.phase.PHASE_UNDEFINED=Undefined # suppress inspection "UnusedProperty" @@ -1205,9 +1247,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Lockup dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nLockup time: {1} block(s)\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds @@ -1235,6 +1277,8 @@ dao.bond.table.button.lockup=Lockup dao.bond.table.button.unlock=解锁 dao.bond.table.button.revoke=Revoke +# suppress inspection "UnusedProperty" +dao.bond.bondState.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.bond.bondState.READY_FOR_LOCKUP=Not bonded yet # suppress inspection "UnusedProperty" @@ -1252,13 +1296,17 @@ dao.bond.bondState.UNLOCKED=Bond unlocked # suppress inspection "UnusedProperty" dao.bond.bondState.CONFISCATED=Bond confiscated +# suppress inspection "UnusedProperty" +dao.bond.lockupReason.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.bond.lockupReason.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" dao.bond.lockupReason.REPUTATION=Bonded reputation # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github admin +dao.bond.bondedRoleType.UNDEFINED=Undefined +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin # suppress inspection "UnusedProperty" @@ -1266,24 +1314,30 @@ dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Rocket chat admin # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Youtube admin +dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq maintainer # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.WEBSITE_OPERATOR=Website operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.FORUM_OPERATOR=Forum operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.PRICE_NODE_OPERATOR=Price node operator +dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Data relay node operator # suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Btc node operator +dao.bond.bondedRoleType.BTC_NODE_OPERATOR=BTC node operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ explorer operator # suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator +# suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domain name holder # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.DNS_ADMIN=DNS admin @@ -1291,8 +1345,10 @@ dao.bond.bondedRoleType.DNS_ADMIN=DNS admin dao.bond.bondedRoleType.MEDIATOR=Mediator # suppress inspection "UnusedProperty" dao.bond.bondedRoleType.ARBITRATOR=Arbitrator +# suppress inspection "UnusedProperty" +dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner -dao.burnBsq.assetFee=Asset listing fee +dao.burnBsq.assetFee=Asset listing dao.burnBsq.menuItem.assetFee=Asset listing fee dao.burnBsq.menuItem.proofOfBurn=Proof of burn dao.burnBsq.header=Fee for asset listing @@ -1331,14 +1387,14 @@ dao.proofOfBurn.date=日期 dao.proofOfBurn.hash=Hash dao.proofOfBurn.txs=交易记录 dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Sign a message with key from proof or burn transaction -dao.proofOfBurn.verify.window.title=Verify a message with key from proof or burn transaction +dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction +dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction dao.proofOfBurn.copySig=Copy signature to clipboard dao.proofOfBurn.sign=Sign dao.proofOfBurn.message=Message dao.proofOfBurn.sig=Signature dao.proofOfBurn.verify=Verify -dao.proofOfBurn.verify.header=Verify message with key from proof or burn transaction +dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction dao.proofOfBurn.verificationResult.ok=Verification succeeded dao.proofOfBurn.verificationResult.failed=Verification failed @@ -1368,6 +1424,8 @@ dao.phase.separatedPhaseBar.VOTE_REVEAL=Vote reveal # suppress inspection "UnusedProperty" dao.phase.separatedPhaseBar.RESULT=Vote result +# suppress inspection "UnusedProperty" +dao.proposal.type.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.proposal.type.COMPENSATION_REQUEST=赔偿要求 # suppress inspection "UnusedProperty" @@ -1383,6 +1441,8 @@ dao.proposal.type.GENERIC=一般提议 # suppress inspection "UnusedProperty" dao.proposal.type.CONFISCATE_BOND=Proposal for confiscating a bond +# suppress inspection "UnusedProperty" +dao.proposal.type.short.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.proposal.type.short.COMPENSATION_REQUEST=赔偿要求 # suppress inspection "UnusedProperty" @@ -1398,33 +1458,35 @@ dao.proposal.type.short.GENERIC=一般提议 # suppress inspection "UnusedProperty" dao.proposal.type.short.CONFISCATE_BOND=Confiscating a bond - dao.proposal.details=提议细节 dao.proposal.selectedProposal=选定赔偿要求 dao.proposal.active.header=Proposals of current cycle dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal?\nThe already paid proposal fee will be lost. dao.proposal.active.remove.doRemove=Yes, remove my proposal dao.proposal.active.remove.failed=Could not remove proposal. +dao.proposal.myVote.title=投票 dao.proposal.myVote.accept=接受提议 dao.proposal.myVote.reject=Reject proposal dao.proposal.myVote.removeMyVote=Ignore proposal dao.proposal.myVote.merit=Vote weight from earned BSQ dao.proposal.myVote.stake=Vote weight from stake -dao.proposal.myVote.blindVoteTxId=Blind vote transaction ID dao.proposal.myVote.revealTxId=Vote reveal transaction ID -dao.proposal.myVote.stake.prompt=Max. available balance for voting: {0} -dao.proposal.votes.header=为全部提议投票 -dao.proposal.votes.header.voted=My vote -dao.proposal.myVote.button=为全部提议投票 +dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} +dao.proposal.votes.header=Set stake for voting and publish your votes +dao.proposal.myVote.button=Publish votes +dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. dao.proposal.create.selectProposalType=Select proposal type +dao.proposal.create.phase.inactive=Please wait until the next proposal phase dao.proposal.create.proposalType=提议类型 -dao.proposal.create.createNew=创建新的赔偿要求 -dao.proposal.create.create.button=创建赔偿要求 +dao.proposal.create.new=创建新的赔偿要求 +dao.proposal.create.button=创建赔偿要求 +dao.proposal.create.publish=Publish proposal +dao.proposal.create.publishing=Proposal publishing is in progress ... dao.proposal=proposal dao.proposal.display.type=提议类型 dao.proposal.display.name=Name/nickname dao.proposal.display.link=Link to detail info -dao.proposal.display.link.prompt=Link to Github issue +dao.proposal.display.link.prompt=Link to GitHub issue dao.proposal.display.requestedBsq=Requested amount in BSQ dao.proposal.display.bsqAddress=BSQ address dao.proposal.display.txId=Proposal transaction ID @@ -1447,6 +1509,7 @@ dao.proposal.display.myVote.accepted=Accepted dao.proposal.display.myVote.rejected=Rejected dao.proposal.display.myVote.ignored=Ignored dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}); +dao.proposal.myVote.invalid=Vote was invalid dao.proposal.voteResult.success=Accepted dao.proposal.voteResult.failed=Rejected @@ -1461,39 +1524,24 @@ dao.proposal.display.assetComboBox.label=Asset to remove dao.blindVote=blind vote dao.blindVote.startPublishing=Publishing blind vote transaction... -dao.blindVote.success=Your blind vote has been successfully published. +dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! dao.wallet.menuItem.send=发送 dao.wallet.menuItem.receive=接收 dao.wallet.menuItem.transactions=交易记录 dao.wallet.dashboard.myBalance=My wallet balance -dao.wallet.dashboard.distribution=Distribution of all BSQ -dao.wallet.dashboard.locked=Global state of locked BSQ -dao.wallet.dashboard.market=Market data -dao.wallet.dashboard.genesis=创始交易 -dao.wallet.dashboard.txDetails=BSQ transactions statistics -dao.wallet.dashboard.genesisBlockHeight=Genesis block height -dao.wallet.dashboard.genesisTxId=Genesis transaction ID -dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction -dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests -dao.wallet.dashboard.reimbursementAmount=BSQ issued for reimbursement requests -dao.wallet.dashboard.availableAmount=Total available BSQ -dao.wallet.dashboard.burntAmount=Burned BSQ (fees) -dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds -dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds -dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds -dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds -dao.wallet.dashboard.allTx=No. of all BSQ transactions -dao.wallet.dashboard.utxo=No. of all unspent transaction outputs -dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions -dao.wallet.dashboard.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions -dao.wallet.dashboard.burntTx=No. of all fee payments transactions -dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq) -dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price) - -dao.wallet.receive.fundYourWallet=充值您的 Bisq 钱包 -dao.wallet.receive.bsqAddress=BSQ wallet address + +dao.wallet.receive.fundYourWallet=Your BSQ receive address +dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) + +dao.wallet.receive.dao.headline=The Bisq DAO +dao.wallet.receive.daoInfo=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model — and the Bisq DAO and BSQ token are the tools that make it possible. +dao.wallet.receive.daoInfo.button=Learn more about the Bisq DAO +dao.wallet.receive.daoTestnetInfo=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on testnet. +dao.wallet.receive.daoTestnetInfo.button=How to run the Bisq DAO on our testnet +dao.wallet.receive.daoContributorInfo=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.wallet.receive.daoContributorInfo.button=How to be part of the BSQ genesis distribution dao.wallet.send.sendFunds=提现 dao.wallet.send.sendBtcFunds=Send non-BSQ funds (BTC) @@ -1512,6 +1560,8 @@ dao.wallet.chainHeightSynced=Latest verified block: {0} dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1} dao.wallet.tx.type=类型 +# suppress inspection "UnusedProperty" +dao.tx.type.enum.UNDEFINED=Undefined # suppress inspection "UnusedProperty" dao.tx.type.enum.UNDEFINED_TX_TYPE=Not recognized # suppress inspection "UnusedProperty" @@ -1551,10 +1601,107 @@ dao.tx.issuanceFromCompReq=补偿请求/发行 dao.tx.issuanceFromCompReq.tooltip=导致新BSQ发行的补偿请求\n发行日期: {0} dao.tx.issuanceFromReimbursement=Reimbursement request/issuance dao.tx.issuanceFromReimbursement.tooltip=Reimbursement request which led to an issuance of new BSQ.\nIssuance date: {0} -dao.proposal.create.missingBsqFunds=You don''t have sufficient funds for creating the proposal.\nMissing: {0} +dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} + +dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} + +dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. Any BSQ transaction require also a miner fee in BTC.\nMissing: {0} dao.feeTx.confirm=Confirm {0} transaction dao.feeTx.confirm.details={0} fee: {1}\nMining fee: {2} ({3} Satoshis/byte)\nTransaction size: {4} Kb\n\nAre you sure you want to publish the {5} transaction? +dao.news.bisqDAO.title=THE BISQ DAO +dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. +dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO + +dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ +dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. +dao.news.pastContribution.yourAddress=Your BSQ Wallet Address +dao.news.pastContribution.requestNow=Request now + +dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET +dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. +dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode +dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. +dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ +dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. +dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle +dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. +dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer +dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. +dao.news.DAOOnTestnet.readMoreLink=Read the full documentation + +dao.monitor.daoState=DAO state +dao.monitor.proposals=Proposals state +dao.monitor.blindVotes=Blind votes state + +dao.monitor.table.peers=Peers +dao.monitor.table.conflicts=Conflicts +dao.monitor.state=状态 +dao.monitor.requestAlHashes=Request all hashes +dao.monitor.resync=Resync DAO state +dao.monitor.table.header.cycleBlockHeight=Cycle / block height +dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} + +dao.monitor.daoState.headline=DAO state +dao.monitor.daoState.daoStateInSync=Your local DAO state is in consensus with the network +dao.monitor.daoState.daoStateNotInSync=Your local DAO state is not in consensus with the network. Please resync your DAO state. +dao.monitor.daoState.table.headline=Chain of DAO state hashes +dao.monitor.daoState.table.blockHeight=Block height +dao.monitor.daoState.table.hash=Hash of DAO state +dao.monitor.daoState.table.prev=Previous hash +dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict + +dao.monitor.proposal.headline=Proposals state +dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network +dao.monitor.proposal.daoStateNotInSync=Your local proposals state is not in consensus with the network. Please restart your application. +dao.monitor.proposal.table.headline=Chain of proposal state hashes +dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict + +dao.monitor.proposal.table.hash=Hash of proposal state +dao.monitor.proposal.table.prev=Previous hash +dao.monitor.proposal.table.numProposals=No. proposals + + +dao.monitor.blindVote.headline=Blind votes state +dao.monitor.blindVote.daoStateInSync=Your local blind votes state is in consensus with the network +dao.monitor.blindVote.daoStateNotInSync=Your local blind votes state is not in consensus with the network. Please restart your application. +dao.monitor.blindVote.table.headline=Chain of blind vote state hashes +dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict +dao.monitor.blindVote.table.hash=Hash of blind vote state +dao.monitor.blindVote.table.prev=Previous hash +dao.monitor.blindVote.table.numBlindVotes=No. blind votes + +dao.factsAndFigures.menuItem.supply=BSQ Supply +dao.factsAndFigures.menuItem.transactions=BSQ Transactions + +dao.factsAndFigures.dashboard.marketPrice=Market data +dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq) +dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price) +dao.factsAndFigures.dashboard.availableAmount=Total available BSQ + +dao.factsAndFigures.supply.issued=BSQ issued +dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction +dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests +dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests + +dao.factsAndFigures.supply.burnt=BSQ burnt + +dao.factsAndFigures.supply.locked=Global state of locked BSQ +dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds +dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds +dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds +dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds +dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees) + +dao.factsAndFigures.transactions.genesis=创始交易 +dao.factsAndFigures.transactions.genesisBlockHeight=Genesis block height +dao.factsAndFigures.transactions.genesisTxId=Genesis transaction ID +dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics +dao.factsAndFigures.transactions.allTx=No. of all BSQ transactions +dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs +dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions +dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions +dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions #################################################################### # Windows @@ -1639,6 +1786,7 @@ filterWindow.seedNode=Filtered seed nodes (comma sep. onion addresses) filterWindow.priceRelayNode=Filtered price relay nodes (comma sep. onion addresses) filterWindow.btcNode=Filtered Bitcoin nodes (comma sep. addresses + port) filterWindow.preventPublicBtcNetwork=Prevent usage of public Bitcoin network +filterWindow.disableDao=Disable DAO filterWindow.add=添加过滤 filterWindow.remove=移除过滤 @@ -1955,6 +2103,10 @@ BTC_MAINNET=比特币主干网络 BTC_TESTNET=比特币测试网络 # suppress inspection "UnusedProperty" BTC_REGTEST=比特币回归测试 +# suppress inspection "UnusedProperty" +BTC_DAO_TESTNET=Bitcoin DAO Testnet +# suppress inspection "UnusedProperty" +BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) time.year=年线 time.month=月线 @@ -2025,6 +2177,8 @@ payment.country=国家 payment.extras=Extra requirements payment.email.mobile=Email or mobile no. payment.altcoin.address=Altcoin address +payment.altcoin.tradeInstantCheckbox=Trade instant (within 1 hour) with this Altcoin +payment.altcoin.tradeInstant.popup=For instant trading it is required that both trading peers are online to be able to complete the trade in less than 1 hour.\n\nIf you have offers open and you are not available please disable those offers under the 'Portfolio' screen. payment.altcoin=Altcoin payment.select.altcoin=选择或搜索数字货币 payment.secret=Secret question @@ -2035,7 +2189,8 @@ payment.cashApp.cashTag=$Cashtag payment.moneyBeam.accountId=Email or phone no. payment.venmo.venmoUserName=Venmo username payment.popmoney.accountId=Email or phone no. -payment.revolut.email=Email or phone no. +payment.revolut.email=Email +payment.revolut.phoneNr=Registered phone no. payment.promptPay.promptPayId=Citizen ID/Tax ID or phone no. payment.supportedCurrencies=Supported currencies payment.limitations=Limitations @@ -2082,6 +2237,10 @@ payment.limits.info=Please be aware that all bank transfers carry a certain amou payment.cashDeposit.info=Please confirm your bank allows you to send cash deposits into other peoples' accounts. For example, Bank of America and Wells Fargo no longer allow such deposits. +payment.revolut.info=Please be sure that the phone number you used for your Revolut account is registered at Revolut otherwise the BTC buyer cannot send you the funds. + +payment.usPostalMoneyOrder.info=Money orders are one of the more private fiat purchase methods available on Bisq.\n\nHowever, please be aware of potentially increased risks associated with their use. Bisq will not bear any responsibility in case a sent money order is stolen, and the arbitrators will in such cases award the BTC to the sender of the money order, provided they can produce tracking information and receipts. It may be advisable for the sender to write the BTC seller's name on the money order, in order to minimize the risk that the money order is cashed by someone else. + payment.f2f.contact=Contact info payment.f2f.contact.prompt=How you want to get contacted by the trading peer? (email address, phone number,...) payment.f2f.city=City for 'Face to face' meeting @@ -2128,16 +2287,10 @@ F2F_SHORT=F2F # Do not translate brand names # suppress inspection "UnusedProperty" -OK_PAY=OKPay -# suppress inspection "UnusedProperty" UPHOLD=Uphold # suppress inspection "UnusedProperty" -CASH_APP=Cash App -# suppress inspection "UnusedProperty" MONEY_BEAM=MoneyBeam (N26) # suppress inspection "UnusedProperty" -VENMO=Venmo -# suppress inspection "UnusedProperty" POPMONEY=Popmoney # suppress inspection "UnusedProperty" REVOLUT=Revolut @@ -2169,17 +2322,22 @@ BLOCK_CHAINS=数字货币 PROMPT_PAY=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT=Altcoins Instant +# Deprecated: Cannot be deleted as it would break old trade history entries # suppress inspection "UnusedProperty" -OK_PAY_SHORT=OKPay +OK_PAY=OKPay # suppress inspection "UnusedProperty" -UPHOLD_SHORT=Uphold +CASH_APP=Cash App # suppress inspection "UnusedProperty" -CASH_APP_SHORT=Cash App +VENMO=Venmo + + # suppress inspection "UnusedProperty" -MONEY_BEAM_SHORT=MoneyBeam (N26) +UPHOLD_SHORT=Uphold # suppress inspection "UnusedProperty" -VENMO_SHORT=Venmo +MONEY_BEAM_SHORT=MoneyBeam (N26) # suppress inspection "UnusedProperty" POPMONEY_SHORT=Popmoney # suppress inspection "UnusedProperty" @@ -2212,6 +2370,17 @@ BLOCK_CHAINS_SHORT=数字货币 PROMPT_PAY_SHORT=PromptPay # suppress inspection "UnusedProperty" ADVANCED_CASH_SHORT=Advanced Cash +# suppress inspection "UnusedProperty" +BLOCK_CHAINS_INSTANT_SHORT=Altcoins Instant + +# Deprecated: Cannot be deleted as it would break old trade history entries +# suppress inspection "UnusedProperty" +OK_PAY_SHORT=OKPay +# suppress inspection "UnusedProperty" +CASH_APP_SHORT=Cash App +# suppress inspection "UnusedProperty" +VENMO_SHORT=Venmo + #################################################################### # Validation @@ -2247,6 +2416,7 @@ validation.nationalAccountId={0}必须由{1}个数字组成。 validation.invalidInput=输入无效:{0} validation.accountNrFormat=帐号必须是格式:{0} validation.altcoin.wrongStructure=地址验证失败,因为它与{0}地址的结构不匹配。 +validation.altcoin.ltz.zAddressesNotSupported=LTZ address need to start with L. Addresses starting with z are not supported. validation.altcoin.zAddressesNotSupported=ZEC地址需要从t开始。 不支持以z开头的地址。 validation.altcoin.invalidAddress=这个地址不是有效的{0}地址!{1} validation.bic.invalidLength=输入长度既不是8也不是11 @@ -2268,8 +2438,14 @@ validation.interacETransfer.invalidQuestion=Must contain only letters, numbers, validation.interacETransfer.invalidAnswer=Must be one word and contain only letters, numbers, and/or the symbol - validation.inputTooLarge=Input must not be larger than {0} validation.inputTooSmall=Input has to be larger than {0} +validation.inputToBeAtLeast=Input has to be at least {0} validation.amountBelowDust=The amount below the dust limit of {0} is not allowed. validation.length=Length must be between {0} and {1} validation.pattern=Input must be of format: {0} validation.noHexString=The input is not in HEX format. validation.advancedCash.invalidFormat=Must be a valid email or wallet id of format: X000000000000 +validation.invalidUrl=This is not a valid URL +validation.mustBeDifferent=Your input must be different to the current value +validation.cannotBeChanged=Parameter cannot be changed +validation.numberFormatException=Number format exception {0} +validation.mustNotBeNegative=Input must not be negative diff --git a/core/src/main/resources/wallet/checkpoints.testnet.txt b/core/src/main/resources/wallet/checkpoints.testnet.txt index 088a01cf9f6..43d074b174b 100644 --- a/core/src/main/resources/wallet/checkpoints.testnet.txt +++ b/core/src/main/resources/wallet/checkpoints.testnet.txt @@ -1,6 +1,6 @@ TXT CHECKPOINTS 1 0 -715 +722 AAAAAAAAB+EH4QfhAAAH4AEAAAApmwX6UCEnJcYIKTa7HO3pFkqqNhAzJVBMdEuGAAAAAPSAvVCBUypCbBW/OqU0oIF7ISF84h2spOqHrFCWN9Zw6r6/T///AB0E5oOO AAAAAAAAD8QPxA/EAAAPwAEAAADHtJ8Nq3z30grJ9lTH6bLhKSHX+MxmkZn8z5wuAAAAAK0gXcQFtYSj/IB2KZ38+itS1Da0Dn/3XosOFJntz7A8OsC/T8D/Pxwf0no+ AAAAAAAALUAtQC1AAAAXoAEAAABwvpBfmfp76xvcOzhdR+OPnJ2aLD5znGpD8LkJAAAAALkv0fxOJYZ1dMLCyDV+3AB0y+BW8lP5/8xBMMqLbX7u+gPDT/D/DxwDvhrh @@ -716,3 +716,10 @@ AAAAxxJ0ZWoA4/uHABXnAAAAwCCKR6UgtjsOrnX1HvJH/FoNnpfkLzW0ui29LAAAAAAAAJpd9uSq/3j/ AAAAx43Eddvf0QeNABXu4AAAACDSFr3nppzBhYuEWZcAns8ebsuzFXpcKod6AQAAAAAAALSDdy83dtx8ZP0KmUh/VELym7QtBJi0bGSMWpKglK6+VwTCW7gGBBrueE4v AAAAyXyz7RsncUHcABX2wAAAACCeYiQAjdOgVmyMP/1hEGqWTIVJKwMWkuetAQAAAAAAAMroGwX8byHCCoYXeVYCibBBgKS8/Iy17slpxWkyHCLl/MfIW2B5ARr5DwGu AAAAzZo/VC+xAlpGABX+oAAAACB2zWT80WB/FL1JNnu5LWR11vj56d3k37iQAAAAAAAAALes4/dEB4rtAanw3oheqL3mKhPISR54Hs5jwvlwoUvpjlTaW6hmARqduvWQ +AAAA0gWa1z4P92XmABYGgAAAACDsQETGfwYx4dxq9yLlsV3EG62Hw28NX+0JAQAAAAAAAO6QS6IRlqpQNbCPo3jYvh4IMhEaOxyYnuwzaeuAXBNRGzXsWz1bARrbZ+Zn +AAAA1rgew2B1HfndABYOYAAAACCnZK1c/5qHBpn1CFb/Ms1mtEx7jgsPZQUxAAAAAAAAAL4EpUhK6O04jBkLxnbhVi6PAuJZaQVscg9HZglb7o4ohR/9Wzg+ARqrbbre +AAAA203OQQZsu26SABYWQADg/z/YhSCLJeX+Ly06iYHqwCjpTzqqqBkkJtQTAQAAAAAAAJ99n1NG8ZKu30eEVfFx9Hl0w9LzH4N0ab9jtk8/u6O60cQQXGlSARoyZV/d +AAAA4CKvRBrPcX/HABYeIAAAACAo/4JlJkhVf3rvL9pWeFpL0Wg1HGsZilvxAAAAAAAAAASo0MRrIXRJZx838aOoZKUuOdMPpPZbflAFpMQdWZs9bGIiXMlCARqkvxOx +AAAA5QVluk2AfW0cABYmAAAAACD/HX1J+PY9GG0mmtGJ8KTRexT3tciRnvs0AAAAAAAAANEFJTL/6nve9E2i1B3wU8FTvLWu2xn2GE9R2SMZXS3wHOI0XGtDARppK7hd +AAAA6h3mrc5teECeABYt4AAAACDgAWOvyEChDxJrqF5flEu7GrGKngyArVFeAAAAAAAAAOZeIyud/MIv9ZmYTCC5H5W88Elg3VTx34RdARlTcQueWodGXAM1ARoUsWQq +AAAA7zhnKDgYb8nvABY1wAAA/z/Ciq9H4ldNuGvC2vKhDjjVL3OK0C8A8gPpAAAAAAAAAL6kirvBq5n9SXwkIJcwqsfbJn5PLWyxl3ZWyR2it9KCAiVYXMImARrsiAXX diff --git a/core/src/main/resources/wallet/checkpoints.txt b/core/src/main/resources/wallet/checkpoints.txt index 9939ae7cd9b..1dc3042319b 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 -273 +279 AAAAAAAAB+EH4QfhAAAH4AEAAABjl7tqvU/FIcDT9gcbVlA4nwtFUbxAtOawZzBpAAAAAKzkcK7NqciBjI/ldojNKncrWleVSgDfBCCn3VRrbSxXaw5/Sf//AB0z8Bkv AAAAAAAAD8EPwQ/BAAAPwAEAAADfP83Sx8MZ9RsrnZCvqzAwqB2Ma+ZesNAJrTfwAAAAACwESaNKhvRgz6WuE7UFdFk1xwzfRY/OIdIOPzX5yaAdjnWUSf//AB0GrNq5 AAAAAAAAF6EXoRehAAAXoAEAAADonWzAaUAKd30XT3NnHKobZMnLOuHdzm/xtehsAAAAAD8cUJA6NBIHHcqPHLc4IrfHw+6mjCGu3e+wRO81EvpnMVqrSf//AB1ffy8G @@ -274,3 +274,9 @@ A3Ua0mxdHmZ6i9CgAAhOQAAAACDihzv+Fzl2xvyjuUOjjOhGzJ13p7hhDAAAAAAAAAAAAAa0s5HbL10Z A6p/xq47klb8dUKnAAhWIAAAACDGSqyFPFpIQQ3a7mcBZCu5oKbb/qnbAwAAAAAAAAAAABrjq2aDGqfl7vaTs/7YYWxgGRjhdjRyh791ZinAmqQHG+/IW70vJxeCTESF A93yCPQZPI+uNBqlAAheAAAAACB72/e2Vw4+TvIo3pk/iVWSFrZ+7VvHGgAAAAAAAAAAAH3IV1wi9LLm56mnIQsX+BbEzUTE2/VXs8aaXzOM5jIJ+GfbW5ItJxcEKAsy BBFmqAAfbeNmLFCnAAhl4AAAACBmF+NNB4n+fX0oF/2E3qKoyM3F6ZZXDgAAAAAAAAAAANfsscIddXjoVx+x2mAvgTWqjkMmq0LNUHDuzFOMrwTs/FfvWy9OKhdhIu7X +BEENCGeaYj2soniZAAhtwAAAACDirLPnHk5EOvSOgdOB3qfTXi6NXmn+FQAAAAAAAAAAAH8q2iJNxK+6bKNwELCZwCMiy13yT87bD/W4f7PKZO6uoBoFXHzZMRdx8oYe +BGl9nUzOC8OwGd2XAAh1oAAAQCDTstfWGtLZX/vVVtngDweHdCNgCo2gFQAAAAAAAAAAANGSdDosGQp0Ifkv7+klBVede47aVoys7hOyV1GscExmnYMZXPQeNxchuuPn +BI4RD4GFzbbLp2U9AAh9gAAAACASCzJkVi1J31nEAKDydkSNsqmqS/b0CAAAAAAAAAAAAFy0tSFQ/n3sIXt020JORC74skEFwkTrrrWfY425xI7zyU8qXKUYMhe0EqUw +BLZPYwRzFg1IUIWVAAiFYADA/y9lULXa52VZWJ4+PhNSNwcra8SYlJ2mKAAAAAAAAAAAAFmIeDQ19QbSzPuttITlbW8dXf3UgGUKyuHjtD00ZOpzyvE7XDPWLxcdUI/b +BOB0BjYhODsJmRMKAAiNQAAAACBrBb0sSgaz2FA6AzwlkzlqJaeeHcrbFAAAAAAAAAAAABsI3z1CzZo42LZq353F60ZPUDYzvYYQhf//cjY0UxWWoaJOXDVoMBe/Z7cq +BQoZ1ZncIovd5BnrAAiVIAAAACCuVddkC3OOHBYJHMc2ZlJuf6Eq9mwEGQAAAAAAAAAAAPeCX+BxQnX+VFIfZuiYz3Q+1D3ZPxhctijfmVgj5O4tfVhgXIhvLhdtCFpM diff --git a/core/src/test/java/bisq/core/dao/governance/ballot/BallotListServiceTest.java b/core/src/test/java/bisq/core/dao/governance/ballot/BallotListServiceTest.java index ff908fb5bfe..56a48de49c5 100644 --- a/core/src/test/java/bisq/core/dao/governance/ballot/BallotListServiceTest.java +++ b/core/src/test/java/bisq/core/dao/governance/ballot/BallotListServiceTest.java @@ -1,18 +1,22 @@ package bisq.core.dao.governance.ballot; -import bisq.common.storage.Storage; import bisq.core.dao.governance.ballot.BallotListService.BallotListChangeListener; import bisq.core.dao.governance.period.PeriodService; import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.ProposalValidator; +import bisq.core.dao.governance.proposal.ProposalValidatorProvider; import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; + +import bisq.common.storage.Storage; + import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import org.junit.Test; -import org.junit.runner.RunWith; + import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; @@ -20,7 +24,7 @@ import static org.mockito.Mockito.when; @RunWith(PowerMockRunner.class) -@PrepareForTest({PeriodService.class, ProposalPayload.class}) +@PrepareForTest({PeriodService.class, ProposalPayload.class, ProposalValidatorProvider.class}) public class BallotListServiceTest { @Test @SuppressWarnings("unchecked") @@ -32,7 +36,7 @@ public void testAddListenersWhenNewPayloadAdded() { when(proposalService.getProposalPayloads()).thenReturn(payloads); BallotListService service = new BallotListService(proposalService, mock(PeriodService.class), - mock(ProposalValidator.class), mock(Storage.class)); + mock(ProposalValidatorProvider.class), mock(Storage.class)); BallotListChangeListener listener = mock(BallotListChangeListener.class); service.addListener(listener); diff --git a/core/src/test/java/bisq/core/dao/governance/proposal/param/ChangeParamValidatorTest.java b/core/src/test/java/bisq/core/dao/governance/proposal/param/ChangeParamValidatorTest.java new file mode 100644 index 00000000000..55d2d0822d0 --- /dev/null +++ b/core/src/test/java/bisq/core/dao/governance/proposal/param/ChangeParamValidatorTest.java @@ -0,0 +1,60 @@ +/* + * 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.dao.governance.proposal.param; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ChangeParamValidatorTest { + + @Test + public void testGetChangeValidationResult() { + assertEquals(ChangeParamValidator.Result.SAME, + ChangeParamValidator.getChangeValidationResult(0, 0, 2, 2)); + assertEquals(ChangeParamValidator.Result.NO_CHANGE_POSSIBLE, + ChangeParamValidator.getChangeValidationResult(0, 1, 2, 2)); + assertEquals(ChangeParamValidator.Result.NO_CHANGE_POSSIBLE, + ChangeParamValidator.getChangeValidationResult(0, -1, 2, 2)); + assertEquals(ChangeParamValidator.Result.NO_CHANGE_POSSIBLE, + ChangeParamValidator.getChangeValidationResult(-1, 0, 2, 2)); + assertEquals(ChangeParamValidator.Result.NO_CHANGE_POSSIBLE, + ChangeParamValidator.getChangeValidationResult(1, 0, 2, 2)); + + assertEquals(ChangeParamValidator.Result.SAME, + ChangeParamValidator.getChangeValidationResult(0, 0, 0, 0)); + assertEquals(ChangeParamValidator.Result.OK, + ChangeParamValidator.getChangeValidationResult(0, 1, 0, 0)); + assertEquals(ChangeParamValidator.Result.OK, + ChangeParamValidator.getChangeValidationResult(0, -1, 0, 0)); + assertEquals(ChangeParamValidator.Result.OK, + ChangeParamValidator.getChangeValidationResult(-1, 0, 0, 0)); + assertEquals(ChangeParamValidator.Result.OK, + ChangeParamValidator.getChangeValidationResult(1, 0, 0, 0)); + + assertEquals(ChangeParamValidator.Result.OK, + ChangeParamValidator.getChangeValidationResult(2, 4, 2, 2)); + assertEquals(ChangeParamValidator.Result.TOO_HIGH, + ChangeParamValidator.getChangeValidationResult(2, 4, 2, 1.1)); + assertEquals(ChangeParamValidator.Result.OK, + ChangeParamValidator.getChangeValidationResult(4, 2, 2, 2)); + assertEquals(ChangeParamValidator.Result.TOO_LOW, + ChangeParamValidator.getChangeValidationResult(4, 2, 1.5, 2)); + + } +} diff --git a/core/src/test/java/bisq/core/dao/state/DaoStateServiceTest.java b/core/src/test/java/bisq/core/dao/state/DaoStateServiceTest.java index 0d9eff411b8..9b2dee42c8f 100644 --- a/core/src/test/java/bisq/core/dao/state/DaoStateServiceTest.java +++ b/core/src/test/java/bisq/core/dao/state/DaoStateServiceTest.java @@ -21,6 +21,8 @@ import bisq.core.dao.state.model.blockchain.Block; import bisq.core.util.BsqFormatter; +import org.bitcoinj.core.Coin; + import org.junit.Assert; import org.junit.Test; @@ -29,7 +31,7 @@ public class DaoStateServiceTest { public void testIsBlockHashKnown() { DaoStateService stateService = new DaoStateService( new DaoState(), - new GenesisTxInfo("fakegenesistxid", 100), + new GenesisTxInfo("fakegenesistxid", 100, Coin.parseCoin("2.5").value), new BsqFormatter()); Assert.assertEquals( "Unknown block should not exist.", @@ -38,6 +40,7 @@ public void testIsBlockHashKnown() { ); Block block = new Block(0, 1534800000, "fakeblockhash0", null); + stateService.onNewBlockHeight(0); stateService.onNewBlockWithEmptyTxs(block); Assert.assertEquals( "Block has to be genesis block to get added.", @@ -52,10 +55,13 @@ public void testIsBlockHashKnown() { ); block = new Block(1, 1534800001, "fakeblockhash1", null); + stateService.onNewBlockHeight(1); stateService.onNewBlockWithEmptyTxs(block); block = new Block(2, 1534800002, "fakeblockhash2", null); + stateService.onNewBlockHeight(2); stateService.onNewBlockWithEmptyTxs(block); block = new Block(3, 1534800003, "fakeblockhash3", null); + stateService.onNewBlockHeight(3); stateService.onNewBlockWithEmptyTxs(block); Assert.assertEquals( "Block that was never added should still not exist after adding more blocks.", diff --git a/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java b/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java index 7941e7bb5a2..d2730b7c303 100644 --- a/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java +++ b/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java @@ -18,6 +18,7 @@ package bisq.core.dao.state; import bisq.core.dao.governance.period.CycleService; +import bisq.core.dao.monitoring.DaoStateMonitoringService; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -33,7 +34,7 @@ import static org.powermock.api.mockito.PowerMockito.mock; @RunWith(PowerMockRunner.class) -@PrepareForTest({DaoStateService.class, GenesisTxInfo.class, CycleService.class, DaoStateStorageService.class}) +@PrepareForTest({DaoStateService.class, GenesisTxInfo.class, CycleService.class, DaoStateStorageService.class, DaoStateMonitoringService.class}) @PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*"}) public class DaoStateSnapshotServiceTest { @@ -44,7 +45,8 @@ public void setup() { daoStateSnapshotService = new DaoStateSnapshotService(mock(DaoStateService.class), mock(GenesisTxInfo.class), mock(CycleService.class), - mock(DaoStateStorageService.class)); + mock(DaoStateStorageService.class), + mock(DaoStateMonitoringService.class)); } @Test diff --git a/core/src/test/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepositoryTest.java b/core/src/test/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepositoryTest.java new file mode 100644 index 00000000000..85b00a44fc6 --- /dev/null +++ b/core/src/test/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepositoryTest.java @@ -0,0 +1,48 @@ +/* + * 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.network.p2p.seed; + +import bisq.core.app.BisqEnvironment; + +import bisq.network.p2p.NodeAddress; + +import org.springframework.core.env.PropertySource; + +import org.junit.Assert; +import org.junit.Test; + +public class DefaultSeedNodeRepositoryTest { + + @Test + public void getSeedNodes() { + DefaultSeedNodeRepository DUT = new DefaultSeedNodeRepository(new BisqEnvironment(new PropertySource.StubPropertySource("name")), null); + Assert.assertFalse(DUT.getSeedNodeAddresses().isEmpty()); + } + + @Test + public void manualSeedNodes() { + String seed1 = "asdf:8001"; + String seed2 = "fdsa:6001"; + String seedNodes = seed1 + "," + seed2; + DefaultSeedNodeRepository DUT = new DefaultSeedNodeRepository(new BisqEnvironment(new PropertySource.StubPropertySource("name")), seedNodes); + Assert.assertFalse(DUT.getSeedNodeAddresses().isEmpty()); + Assert.assertEquals(2, DUT.getSeedNodeAddresses().size()); + Assert.assertTrue(DUT.getSeedNodeAddresses().contains(new NodeAddress(seed1))); + Assert.assertTrue(DUT.getSeedNodeAddresses().contains(new NodeAddress(seed2))); + } +} diff --git a/core/src/test/java/bisq/core/network/p2p/seed/ImmutableSetDecoratorTest.java b/core/src/test/java/bisq/core/network/p2p/seed/ImmutableSetDecoratorTest.java deleted file mode 100644 index b1cab1649d6..00000000000 --- a/core/src/test/java/bisq/core/network/p2p/seed/ImmutableSetDecoratorTest.java +++ /dev/null @@ -1,65 +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.network.p2p.seed; - -import com.google.common.collect.Sets; - -import java.util.Set; - -import org.junit.Test; - -import static org.junit.Assert.assertTrue; - -public class ImmutableSetDecoratorTest { - @Test(expected = UnsupportedOperationException.class) - public void testAdd() { - Set original = Sets.newHashSet(1, 2, 3); - Set decorator = new ImmutableSetDecorator<>(original); - decorator.add(4); - } - - @Test(expected = UnsupportedOperationException.class) - public void testRemove() { - Set original = Sets.newHashSet(1, 2, 3); - Set decorator = new ImmutableSetDecorator<>(original); - decorator.remove(3); - } - - @Test(expected = UnsupportedOperationException.class) - public void testClear() { - Set original = Sets.newHashSet(1, 2, 3); - Set decorator = new ImmutableSetDecorator<>(original); - decorator.clear(); - } - - @Test(expected = UnsupportedOperationException.class) - public void testRemoveWithIterator() { - Set original = Sets.newHashSet(1, 2, 3); - Set decorator = new ImmutableSetDecorator<>(original); - decorator.iterator().remove(); - } - - @Test - public void testBackingCollection() { - Set original = Sets.newHashSet(1, 2, 3); - Set decorator = new ImmutableSetDecorator<>(original); - - original.remove(2); - assertTrue(decorator.contains(2)); - } -} diff --git a/core/src/test/java/bisq/core/network/p2p/seed/SeedNodeAddressLookupTest.java b/core/src/test/java/bisq/core/network/p2p/seed/SeedNodeAddressLookupTest.java deleted file mode 100644 index 8f63daefd82..00000000000 --- a/core/src/test/java/bisq/core/network/p2p/seed/SeedNodeAddressLookupTest.java +++ /dev/null @@ -1,60 +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.network.p2p.seed; - -import bisq.core.app.BisqEnvironment; - -import bisq.network.p2p.NodeAddress; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import java.security.Security; - -import java.util.Collections; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.mock; - -public class SeedNodeAddressLookupTest { - - - @Test - public void testResolveNodeAddressesWhenLocalAddressSpecified() { - SeedNodeAddressLookup lookup = new SeedNodeAddressLookup( - mock(BisqEnvironment.class), false, 1, "192.168.0.1:1234", - "192.168.0.1:1234, 192.168.0.2:9897"); - - Set actual = lookup.resolveNodeAddresses(); - Set expected = Collections.singleton(new NodeAddress("192.168.0.2:9897")); - assertEquals(expected, actual); - } - - @Test - public void testResolveNodeAddressesWhenSeedNodesAreNull() { - SeedNodeAddressLookup lookup = new SeedNodeAddressLookup( - mock(BisqEnvironment.class), false, 1, "192.168.0.1:1234", null); - - Set actual = lookup.resolveNodeAddresses(); - assertFalse(actual.isEmpty()); - } -} diff --git a/core/src/test/java/bisq/core/network/p2p/seed/SeedNodeAddressesTest.java b/core/src/test/java/bisq/core/network/p2p/seed/SeedNodeAddressesTest.java deleted file mode 100644 index b036f98faff..00000000000 --- a/core/src/test/java/bisq/core/network/p2p/seed/SeedNodeAddressesTest.java +++ /dev/null @@ -1,102 +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.network.p2p.seed; - -import bisq.network.p2p.NodeAddress; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import java.security.Security; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class SeedNodeAddressesTest { - - - @Test - public void testCollector() { - List addresses = Lists.newArrayList( - new NodeAddress("192.168.0.1:1111"), - new NodeAddress("192.168.0.1:1111"), - new NodeAddress("192.168.0.2:2222")); - Set expected = new HashSet<>(addresses); - - SeedNodeAddresses actual = addresses.stream() - .collect(SeedNodeAddresses.collector()); - - assertEquals(expected, actual); - } - - @Test - public void testExcludeByFullAddress() { - Set delegate = Sets.newHashSet( - new NodeAddress("192.168.0.1:1111"), - new NodeAddress("192.168.0.2:2222")); - SeedNodeAddresses addresses = new SeedNodeAddresses(delegate); - SeedNodeAddresses actual = addresses.excludeByFullAddress("192.168.0.1:1111"); - - assertEquals(1, actual.size()); - } - - @Test - public void testExcludeByHost() { - Set delegate = Sets.newHashSet( - new NodeAddress("aaa:1111"), - new NodeAddress("aaa:2222"), - new NodeAddress("bbb:1111"), - new NodeAddress("bbb:2222"), - new NodeAddress("ccc:1111"), - new NodeAddress("ccc:2222")); - SeedNodeAddresses addresses = new SeedNodeAddresses(delegate); - - Set hosts = Sets.newHashSet("aaa", "bbb"); - SeedNodeAddresses actual = addresses.excludeByHost(hosts); - - Set expected = Sets.newHashSet( - new NodeAddress("ccc:1111"), - new NodeAddress("ccc:2222")); - - assertEquals(expected, actual); - } - - @Test - public void testFromString() { - Set expected = Sets.newHashSet( - new NodeAddress("192.168.0.1:1111"), - new NodeAddress("192.168.0.2:2222")); - SeedNodeAddresses actual = SeedNodeAddresses.fromString("192.168.0.1:1111, 192.168.0.2:2222"); - assertEquals(expected, actual); - } - - @Test - public void testFromEmptyString() { - SeedNodeAddresses nodeAddresses = SeedNodeAddresses.fromString(""); - assertTrue(nodeAddresses.isEmpty()); - } -} diff --git a/core/src/test/java/bisq/core/trade/TradableListTest.java b/core/src/test/java/bisq/core/trade/TradableListTest.java index 2de76604e47..928898f916a 100644 --- a/core/src/test/java/bisq/core/trade/TradableListTest.java +++ b/core/src/test/java/bisq/core/trade/TradableListTest.java @@ -33,6 +33,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +//TODO cannot be run in IntelliJ IDE as parameter is not supported. OfferPayload is final so it is not so trivial to +// replace that. public class TradableListTest { @Test diff --git a/core/src/test/resources/mainnet.seednodes b/core/src/test/resources/mainnet.seednodes new file mode 100644 index 00000000000..27a6f28feb9 --- /dev/null +++ b/core/src/test/resources/mainnet.seednodes @@ -0,0 +1,9 @@ +# nodeaddress.onion:port [(@owner)] +5quyxpxheyvzmb2d.onion:8000 (@miker) +s67qglwhkgkyvr74.onion:8000 (@emzy) +ef5qnzx6znifo3df.onion:8000 (@freimair) +jhgcy2won7xnslrb.onion:8000 (@freimair) +3f3cu2yw7u457ztq.onion:8000 (@manfredkarrer) +723ljisnynbtdohi.onion:8000 (@manfredkarrer) +rm7b56wbrcczpjvl.onion:8000 (@manfredkarrer) +fl3mmribyxgrv63c.onion:8000 (@manfredkarrer) diff --git a/core/src/test/resources/regtest.seednodes b/core/src/test/resources/regtest.seednodes new file mode 100644 index 00000000000..58f66ba9cc3 --- /dev/null +++ b/core/src/test/resources/regtest.seednodes @@ -0,0 +1,10 @@ +# For development you need to change that to your local onion addresses +# 1. Run a seed node with prog args: --bitcoinNetwork=regtest --nodePort=8002 --appName=bisq_seed_node_rxdkppp3vicnbgqt.onion_8002 +# 2. Find your local onion address in bisq_seed_node_rxdkppp3vicnbgqt.onion_8002/regtest/tor/hiddenservice/hostname +# 3. Shut down the seed node +# 4. Rename the directory with your local onion address +# 5. Edit here your found onion address (new NodeAddress("YOUR_ONION.onion:8002") + +# nodeaddress.onion:port [(@owner)] +rxdkppp3vicnbgqt.onion:8002 +4ie52dse64kaarxw.onion:8002 diff --git a/core/src/test/resources/testnet.seednodes b/core/src/test/resources/testnet.seednodes new file mode 100644 index 00000000000..77b6af07e63 --- /dev/null +++ b/core/src/test/resources/testnet.seednodes @@ -0,0 +1,7 @@ +# nodeaddress.onion:port [(@owner)] +snenz4mea65wigen.onion:8001 +fjr5w4eckjghqtnu.onion:8001 +3d56s6acbi3vk52v.onion:8001 +74w2sttlo4qk6go3.onion:8001 +gtif46mfxirv533z.onion:8001 +jmc5ajqvtnzqaggm.onion:8001 diff --git a/desktop/package/linux/Dockerfile b/desktop/package/linux/Dockerfile index d44f2079e2e..ae1b484a19a 100644 --- a/desktop/package/linux/Dockerfile +++ b/desktop/package/linux/Dockerfile @@ -8,7 +8,7 @@ # pull base image FROM openjdk:8-jdk -ENV version 0.9.3-SNAPSHOT +ENV version 0.9.5-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 4135e4f0702..ae83450a820 100755 --- a/desktop/package/linux/package.sh +++ b/desktop/package/linux/package.sh @@ -6,12 +6,14 @@ # - Update version below # - Ensure JAVA_HOME below is pointing to OracleJDK 10 directory -version=0.9.3-SNAPSHOT -if [ ! -f "$JAVA_HOME/bin/javapackager" ] && [ -d "/usr/lib/jvm/jdk-10.0.2" ]; then - JAVA_HOME=/usr/lib/jvm/jdk-10.0.2 -else - echo Javapackager not found. Update JAVA_HOME variable to point to OracleJDK. - exit 1 +version=0.9.5-SNAPSHOT +if [ ! -f "$JAVA_HOME/bin/javapackager" ]; then + if [ -d "/usr/lib/jvm/jdk-10.0.2" ]; then + JAVA_HOME=/usr/lib/jvm/jdk-10.0.2 + else + echo Javapackager not found. Update JAVA_HOME variable to point to OracleJDK. + exit 1 + fi fi base_dir=$( cd "$(dirname "$0")" ; pwd -P )/../../.. @@ -117,7 +119,7 @@ fi echo Generating rpm package $JAVA_HOME/bin/javapackager \ -deploy \ - -BappVersion=$version_base \ + -BappVersion=$version \ -Bcategory=Network \ -Bemail=contact@bisq.network \ -BlicenseType=GPLv3 \ @@ -134,8 +136,8 @@ $JAVA_HOME/bin/javapackager \ -outfile Bisq-$version \ -v -if [ ! -f "$base_dir/desktop/package/linux/bisq-$version_base-1.x86_64.rpm" ]; then - echo No rpm file found at $base_dir/desktop/package/linux/bisq-$version_base-1.x86_64.rpm +if [ ! -f "$base_dir/desktop/package/linux/bisq-$version-1.x86_64.rpm" ]; then + echo No rpm file found at $base_dir/desktop/package/linux/bisq-$version-1.x86_64.rpm exit 3 fi @@ -148,10 +150,11 @@ mv $base_dir/desktop/package/linux/bisq-$version.deb $base_dir/desktop/package/l echo SHA256 of $base_dir/desktop/package/linux/Bisq-$version.deb: shasum -a256 $base_dir/desktop/package/linux/Bisq-$version.deb | awk '{print $1}' | tee $base_dir/desktop/package/linux/Bisq-$version.deb.txt -if [ -f "$base_dir/desktop/package/linux/Bisq-$version_base-1.x86_64.rpm" ]; then - rm "$base_dir/desktop/package/linux/Bisq-$version_base-1.x86_64.rpm" -fi -mv $base_dir/desktop/package/linux/bisq-$version_base-1.x86_64.rpm $base_dir/desktop/package/linux/Bisq-$version.rpm +# FIXME: My Ubuntu somehow also deletes the lower case file +# if [ -f "$base_dir/desktop/package/linux/Bisq-$version-1.x86_64.rpm" ]; then +# rm "$base_dir/desktop/package/linux/Bisq-$version-1.x86_64.rpm" +# fi +mv $base_dir/desktop/package/linux/bisq-$version-1.x86_64.rpm $base_dir/desktop/package/linux/Bisq-$version.rpm echo SHA256 of $base_dir/desktop/package/linux/Bisq-$version.rpm: shasum -a256 $base_dir/desktop/package/linux/Bisq-$version.rpm | awk '{print $1}' | tee $base_dir/desktop/package/linux/Bisq-$version.rpm.txt diff --git a/desktop/package/linux/release.sh b/desktop/package/linux/release.sh index 71edf917fcc..e4f1547d438 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=0.9.3-SNAPSHOT +version=0.9.5-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/Bisq-background.png b/desktop/package/macosx/Bisq-background.png index ea7ef576e8d..8edd4b2e041 100644 Binary files a/desktop/package/macosx/Bisq-background.png and b/desktop/package/macosx/Bisq-background.png differ diff --git a/desktop/package/macosx/Info.plist b/desktop/package/macosx/Info.plist index 16e81166099..52b244a0567 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 0.9.3 + 0.9.5 CFBundleShortVersionString - 0.9.3 + 0.9.5 CFBundleExecutable Bisq diff --git a/desktop/package/macosx/background/Bisq-background.png b/desktop/package/macosx/background/Bisq-background.png deleted file mode 100644 index 1c0e5a8a3fb..00000000000 Binary files a/desktop/package/macosx/background/Bisq-background.png and /dev/null differ diff --git a/desktop/package/macosx/background/Bisq-background@2x.png b/desktop/package/macosx/background/Bisq-background@2x.png deleted file mode 100644 index ecd40d1f2e8..00000000000 Binary files a/desktop/package/macosx/background/Bisq-background@2x.png and /dev/null differ diff --git a/desktop/package/macosx/copy_dbs.sh b/desktop/package/macosx/copy_dbs.sh index e6215c0f44e..1dee6a8e16a 100755 --- a/desktop/package/macosx/copy_dbs.sh +++ b/desktop/package/macosx/copy_dbs.sh @@ -11,10 +11,11 @@ cp "$dbDir/TradeStatistics2Store" "$resDir/TradeStatistics2Store_BTC_MAINNET" cp "$dbDir/AccountAgeWitnessStore" "$resDir/AccountAgeWitnessStore_BTC_MAINNET" # Testnet -dbDir=~/Library/Application\ Support/bisq-BTC_PRODTEST/btc_testnet/db +dbDir=~/Library/Application\ Support/Bisq/btc_dao_testnet/db -cp "$dbDir/AccountAgeWitnessStore" "$resDir/AccountAgeWitnessStore_BTC_TESTNET" -cp "$dbDir/BlindVoteStore" "$resDir/BlindVoteStore_BTC_TESTNET" -cp "$dbDir/DaoStateStore" "$resDir/DaoStateStore_BTC_TESTNET" -cp "$dbDir/ProposalStore" "$resDir/ProposalStore_BTC_TESTNET" -cp "$dbDir/TempProposalStore" "$resDir/TempProposalStore_BTC_TESTNET" +cp "$dbDir/AccountAgeWitnessStore" "$resDir/AccountAgeWitnessStore_BTC_DAO_TESTNET" +cp "$dbDir/BlindVoteStore" "$resDir/BlindVoteStore_BTC_DAO_TESTNET" +cp "$dbDir/DaoStateStore" "$resDir/DaoStateStore_BTC_DAO_TESTNET" +cp "$dbDir/ProposalStore" "$resDir/ProposalStore_BTC_DAO_TESTNET" +cp "$dbDir/TempProposalStore" "$resDir/TempProposalStore_BTC_DAO_TESTNET" +cp "$dbDir/TradeStatistics2Store" "$resDir/TradeStatistics2Store_BTC_DAO_TESTNET" diff --git a/desktop/package/macosx/create_app.sh b/desktop/package/macosx/create_app.sh index 1c20b3ce345..433e39dbcb0 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="0.9.3-SNAPSHOT" +version="0.9.5-SNAPSHOT" cd .. ./gradlew :desktop:build -x test shadowJar diff --git a/desktop/package/macosx/finalize.sh b/desktop/package/macosx/finalize.sh index 5839c53faf5..2a1204283d0 100755 --- a/desktop/package/macosx/finalize.sh +++ b/desktop/package/macosx/finalize.sh @@ -2,7 +2,7 @@ cd ../../ -version="0.9.3-SNAPSHOT" +version="0.9.5-SNAPSHOT" target_dir="releases/$version" @@ -37,6 +37,10 @@ deb="Bisq-$version.deb" deb64="Bisq-64bit-$version.deb" cp "$linux64/$deb" "$target_dir/$deb64" +rpm="Bisq-$version.rpm" +rpm64="Bisq-64bit-$version.rpm" +cp "$linux64/$rpm" "$target_dir/$rpm64" + exe="Bisq-$version.exe" exe64="Bisq-64bit-$version.exe" cp "$win64/$exe" "$target_dir/$exe64" @@ -46,11 +50,13 @@ cd "$target_dir" echo Create signatures gpg --digest-algo SHA256 --local-user $BISQ_GPG_USER --output $dmg.asc --detach-sig --armor $dmg gpg --digest-algo SHA256 --local-user $BISQ_GPG_USER --output $deb64.asc --detach-sig --armor $deb64 +gpg --digest-algo SHA256 --local-user $BISQ_GPG_USER --output $rpm64.asc --detach-sig --armor $rpm64 gpg --digest-algo SHA256 --local-user $BISQ_GPG_USER --output $exe64.asc --detach-sig --armor $exe64 echo Verify signatures gpg --digest-algo SHA256 --verify $dmg{.asc*,} gpg --digest-algo SHA256 --verify $deb64{.asc*,} +gpg --digest-algo SHA256 --verify $rpm64{.asc*,} gpg --digest-algo SHA256 --verify $exe64{.asc*,} cp -r . $win64/$version diff --git a/desktop/package/macosx/insert_snapshot_version.sh b/desktop/package/macosx/insert_snapshot_version.sh new file mode 100755 index 00000000000..3d2c22f65ff --- /dev/null +++ b/desktop/package/macosx/insert_snapshot_version.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +cd $(dirname $0)/../../../ + +version=0.9.5 + +find . -type f \( -name "finalize.sh" \ +-o -name "create_app.sh" \ +-o -name "build.gradle" \ +-o -name "release.bat" \ +-o -name "package.bat" \ +-o -name "release.sh" \ +-o -name "package.sh" \ +-o -name "version.txt" \ +-o -name "Dockerfile" \ +\) -exec sed -i '' s/$version/"$version-SNAPSHOT"/ {} + diff --git a/desktop/package/macosx/replace_version_number.sh b/desktop/package/macosx/replace_version_number.sh new file mode 100755 index 00000000000..25807f7a0ac --- /dev/null +++ b/desktop/package/macosx/replace_version_number.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +cd $(dirname $0)/../../../ + +oldVersion=0.9.4 +newVersion=0.9.5 + +find . -type f \( -name "finalize.sh" \ +-o -name "create_app.sh" \ +-o -name "build.gradle" \ +-o -name "release.bat" \ +-o -name "package.bat" \ +-o -name "release.sh" \ +-o -name "package.sh" \ +-o -name "version.txt" \ +-o -name "Dockerfile" \ +\) -exec sed -i '' s/"$oldVersion-SNAPSHOT"/$newVersion/ {} + + +find . -type f \( -name "Info.plist" \ +-o -name "SeedNodeMain.java" \ +-o -name "Version.java" \ +\) -exec sed -i '' s/$oldVersion/$newVersion/ {} + + diff --git a/desktop/package/windows/Bisq.iss b/desktop/package/windows/Bisq.iss index e38e77c7269..9b70352d4da 100644 --- a/desktop/package/windows/Bisq.iss +++ b/desktop/package/windows/Bisq.iss @@ -17,7 +17,7 @@ AppComments={cm:AppComments} AppCopyright=Copyright (C) {#AppCopyrightYear} AppPublisherURL=https://bisq.network AppSupportURL=https://bisq.community -;AppUpdatesURL=https://github.com/bisq-network/bisq/releases +;AppUpdatesURL=https://bisq.network/downloads VersionInfoVersion={#FileVersion} VersionInfoDescription=Bisq Setup VersionInfoCopyright=Copyright (C) {#AppCopyrightYear} diff --git a/desktop/package/windows/package.bat b/desktop/package/windows/package.bat index 432884ae6f2..e48db8a32fc 100644 --- a/desktop/package/windows/package.bat +++ b/desktop/package/windows/package.bat @@ -8,7 +8,7 @@ @echo off -set version=0.9.3-SNAPSHOT +set version=0.9.5-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 4d45972e656..aaab643daf3 100644 --- a/desktop/package/windows/release.bat +++ b/desktop/package/windows/release.bat @@ -6,7 +6,7 @@ @echo off -set version=0.9.3-SNAPSHOT +set version=0.9.5-SNAPSHOT set release_dir=%~dp0..\..\..\releases\%version% set package_dir=%~dp0.. diff --git a/desktop/src/main/java/bisq/desktop/app/BisqApp.java b/desktop/src/main/java/bisq/desktop/app/BisqApp.java index 6ebb4b912f6..12328a1b61a 100644 --- a/desktop/src/main/java/bisq/desktop/app/BisqApp.java +++ b/desktop/src/main/java/bisq/desktop/app/BisqApp.java @@ -40,6 +40,7 @@ import bisq.core.dao.governance.voteresult.MissingDataRequestService; import bisq.core.filter.FilterManager; import bisq.core.locale.Res; +import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOfferManager; import bisq.core.user.Preferences; @@ -211,7 +212,14 @@ public void handleUncaughtException(Throwable throwable, boolean doShutDown) { /////////////////////////////////////////////////////////////////////////////////////////// private Scene createAndConfigScene(MainView mainView, Injector injector) { - Rectangle maxWindowBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds(); + Rectangle maxWindowBounds = new Rectangle(); + try { + maxWindowBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds(); + } catch (IllegalArgumentException e) { + // Multi-screen environments may encounter IllegalArgumentException (Window must not be zero) + // Just ignore the exception and continue, which means the window will use the minimum window size below + // since we are unable to determine if we can use a larger size + } Scene scene = new Scene(mainView.getRoot(), maxWindowBounds.width < INITIAL_WINDOW_WIDTH ? (maxWindowBounds.width < MIN_WINDOW_WIDTH ? MIN_WINDOW_WIDTH : maxWindowBounds.width) : @@ -238,10 +246,9 @@ private void setupStage(Scene scene) { // configure the primary stage String appName = injector.getInstance(Key.get(String.class, Names.named(AppOptionKeys.APP_NAME_KEY))); - if (BisqEnvironment.getBaseCurrencyNetwork().isTestnet()) - appName += " [TESTNET]"; - else if (BisqEnvironment.getBaseCurrencyNetwork().isRegtest()) - appName += " [REGTEST]"; + if (!BisqEnvironment.getBaseCurrencyNetwork().isMainnet()) + appName += " [" + Res.get(BisqEnvironment.getBaseCurrencyNetwork().name()) + "]"; + stage.setTitle(appName); stage.setScene(scene); stage.setMinWidth(MIN_WINDOW_WIDTH); @@ -320,7 +327,14 @@ private void addSceneKeyEventHandler(Scene scene, Injector injector) { } private void shutDownByUser() { - if (injector.getInstance(OpenOfferManager.class).getObservableList().isEmpty()) { + boolean hasOpenOffers = false; + for (OpenOffer openOffer : injector.getInstance(OpenOfferManager.class).getObservableList()) { + if (openOffer.getState().equals(OpenOffer.State.AVAILABLE)) { + hasOpenOffers = true; + break; + } + } + if (!hasOpenOffers) { // No open offers, so no need to show the popup. stop(); return; diff --git a/desktop/src/main/java/bisq/desktop/app/SystemTray.java b/desktop/src/main/java/bisq/desktop/app/SystemTray.java index 6aa2edd929c..61f4c83221a 100644 --- a/desktop/src/main/java/bisq/desktop/app/SystemTray.java +++ b/desktop/src/main/java/bisq/desktop/app/SystemTray.java @@ -52,6 +52,8 @@ public class SystemTray { private static final String ICON_HI_RES = "/images/system_tray_icon@2x.png"; private static final String ICON_LO_RES = "/images/system_tray_icon.png"; + private static final String ICON_HI_RES_WHITE = "/images/system_tray_icon@2x_white.png"; + private static final String ICON_LO_RES_WHITE = "/images/system_tray_icon_white.png"; private static final String ICON_WINDOWS_LO_RES = "/images/system_tray_icon_windows.png"; private static final String ICON_WINDOWS_HI_RES = "/images/system_tray_icon_windows@2x.png"; private static final String ICON_LINUX = "/images/system_tray_icon_linux.png"; @@ -97,7 +99,11 @@ private void init() { String path; if (Utilities.isOSX()) - path = ImageUtil.isRetina() ? ICON_HI_RES : ICON_LO_RES; + if (Utilities.isMacMenuBarDarkMode()) + path = ImageUtil.isRetina() ? ICON_HI_RES_WHITE : ICON_LO_RES_WHITE; + else + path = ImageUtil.isRetina() ? ICON_HI_RES : ICON_LO_RES; + else if (Utilities.isWindows()) path = ImageUtil.isRetina() ? ICON_WINDOWS_HI_RES : ICON_WINDOWS_LO_RES; else @@ -106,11 +112,16 @@ else if (Utilities.isWindows()) try { BufferedImage trayIconImage = ImageIO.read(getClass().getResource(path)); TrayIcon trayIcon = new TrayIcon(trayIconImage); - // On Windows and Linux the icon needs to be scaled + // On Windows and Linux the icon needs to be resized // On macOS we get the correct size from the provided image if (!Utilities.isOSX()) { - int trayIconWidth = trayIcon.getSize().width; - trayIcon = new TrayIcon(trayIconImage.getScaledInstance(trayIconWidth, -1, Image.SCALE_SMOOTH)); + if (ImageUtil.isRetina()) { + // Using auto sizing provides better results with high resolution + trayIcon.setImageAutoSize(true); + } else { + // Using scaling provides better results with low resolution + trayIcon = new TrayIcon(trayIconImage.getScaledInstance(trayIcon.getSize().width, -1, Image.SCALE_SMOOTH)); + } } trayIcon.setPopupMenu(popupMenu); diff --git a/desktop/src/main/java/bisq/desktop/bisq.css b/desktop/src/main/java/bisq/desktop/bisq.css index dd5f2f9c5be..b2ff670a49d 100644 --- a/desktop/src/main/java/bisq/desktop/bisq.css +++ b/desktop/src/main/java/bisq/desktop/bisq.css @@ -530,7 +530,7 @@ bg color of non edit textFields: fafafa -fx-pref-width: 30; } -.jfx-badge .label { +.jfx-badge .badge-pane .label { -fx-font-weight: bold; -fx-font-size: 0.692em; -fx-text-fill: -bs-rd-white; diff --git a/desktop/src/main/java/bisq/desktop/components/InfoInputTextField.java b/desktop/src/main/java/bisq/desktop/components/InfoInputTextField.java index 09a353041a5..9247b13d3d2 100644 --- a/desktop/src/main/java/bisq/desktop/components/InfoInputTextField.java +++ b/desktop/src/main/java/bisq/desktop/components/InfoInputTextField.java @@ -157,25 +157,27 @@ public final StringProperty textProperty() { private void setActionHandlers(Node node) { - currentIcon.setManaged(true); - currentIcon.setVisible(true); - - // As we don't use binding here we need to recreate it on mouse over to reflect the current state - currentIcon.setOnMouseEntered(e -> { - hidePopover = false; - showPopOver(node); - }); - currentIcon.setOnMouseExited(e -> { - if (popover != null) - popover.hide(); - hidePopover = true; - UserThread.runAfter(() -> { - if (hidePopover) { + if (node != null) { + currentIcon.setManaged(true); + currentIcon.setVisible(true); + + // As we don't use binding here we need to recreate it on mouse over to reflect the current state + currentIcon.setOnMouseEntered(e -> { + hidePopover = false; + showPopOver(node); + }); + currentIcon.setOnMouseExited(e -> { + if (popover != null) popover.hide(); - hidePopover = false; - } - }, 250, TimeUnit.MILLISECONDS); - }); + hidePopover = true; + UserThread.runAfter(() -> { + if (hidePopover) { + popover.hide(); + hidePopover = false; + } + }, 250, TimeUnit.MILLISECONDS); + }); + } } private void showPopOver(Node node) { diff --git a/desktop/src/main/java/bisq/desktop/components/NewBadge.java b/desktop/src/main/java/bisq/desktop/components/NewBadge.java new file mode 100644 index 00000000000..80a1d48a0fb --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/components/NewBadge.java @@ -0,0 +1,34 @@ +package bisq.desktop.components; + +import bisq.core.locale.Res; +import bisq.core.user.Preferences; + +import com.jfoenix.controls.JFXBadge; + +import javafx.scene.Node; + +import javafx.collections.MapChangeListener; + +public class NewBadge extends JFXBadge { + + private final String key; + + public NewBadge(Node control, String key, Preferences preferences) { + super(control); + + this.key = key; + + setText(Res.get("shared.new")); + getStyleClass().add("new"); + + setEnabled(!preferences.getDontShowAgainMap().containsKey(key)); + refreshBadge(); + + preferences.getDontShowAgainMapAsObservable().addListener((MapChangeListener) change -> { + if (change.getKey().equals(key)) { + setEnabled(!change.wasAdded()); + refreshBadge(); + } + }); + } +} diff --git a/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java b/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java index 54925b87c60..10158f3beb6 100644 --- a/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java +++ b/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java @@ -147,7 +147,7 @@ private void openBlockExplorer(String txId) { BlockChainExplorer blockChainExplorer = isBsq ? preferences.getBsqBlockChainExplorer() : preferences.getBlockChainExplorer(); - GUIUtil.openWebPage(blockChainExplorer.txUrl + txId); + GUIUtil.openWebPage(blockChainExplorer.txUrl + txId, false); } } diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java similarity index 60% rename from desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java rename to desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java index 5b962b5faf2..76bad08bbed 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java @@ -18,6 +18,8 @@ package bisq.desktop.components.paymentmethods; import bisq.desktop.components.InputTextField; +import bisq.desktop.components.NewBadge; +import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.util.FormBuilder; import bisq.desktop.util.Layout; @@ -27,11 +29,13 @@ import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; import bisq.core.payment.AccountAgeWitnessService; -import bisq.core.payment.CryptoCurrencyAccount; +import bisq.core.payment.AssetAccount; +import bisq.core.payment.InstantCryptoCurrencyAccount; import bisq.core.payment.PaymentAccount; -import bisq.core.payment.payload.CryptoCurrencyAccountPayload; +import bisq.core.payment.payload.AssetsAccountPayload; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.payment.validation.AltCoinAddressValidator; +import bisq.core.user.Preferences; import bisq.core.util.BSFormatter; import bisq.core.util.validation.InputValidator; @@ -39,11 +43,17 @@ import org.apache.commons.lang3.StringUtils; +import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; import javafx.scene.layout.VBox; +import javafx.geometry.Insets; +import javafx.geometry.Pos; + import javafx.collections.FXCollections; import javafx.util.StringConverter; @@ -52,40 +62,49 @@ import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField; import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon; +import static bisq.desktop.util.FormBuilder.addLabelCheckBox; import static bisq.desktop.util.FormBuilder.addTopLabelTextField; import static bisq.desktop.util.GUIUtil.getComboBoxButtonCell; -public class CryptoCurrencyForm extends PaymentMethodForm { - private final CryptoCurrencyAccount cryptoCurrencyAccount; +public class AssetsForm extends PaymentMethodForm { + public static final String INSTANT_TRADE_NEWS = "instantTradeNews0.9.5"; + private final AssetAccount assetAccount; private final AltCoinAddressValidator altCoinAddressValidator; private final AssetService assetService; private final FilterManager filterManager; + private final Preferences preferences; private InputTextField addressInputTextField; + private CheckBox tradeInstantCheckBox; + private boolean tradeInstant; public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountPayload paymentAccountPayload, String labelTitle) { addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow, labelTitle, - ((CryptoCurrencyAccountPayload) paymentAccountPayload).getAddress()); + ((AssetsAccountPayload) paymentAccountPayload).getAddress()); return gridRow; } - public CryptoCurrencyForm(PaymentAccount paymentAccount, - AccountAgeWitnessService accountAgeWitnessService, - AltCoinAddressValidator altCoinAddressValidator, - InputValidator inputValidator, - GridPane gridPane, - int gridRow, - BSFormatter formatter, - AssetService assetService, - FilterManager filterManager) { + public AssetsForm(PaymentAccount paymentAccount, + AccountAgeWitnessService accountAgeWitnessService, + AltCoinAddressValidator altCoinAddressValidator, + InputValidator inputValidator, + GridPane gridPane, + int gridRow, + BSFormatter formatter, + AssetService assetService, + FilterManager filterManager, + Preferences preferences) { super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter); - this.cryptoCurrencyAccount = (CryptoCurrencyAccount) paymentAccount; + this.assetAccount = (AssetAccount) paymentAccount; this.altCoinAddressValidator = altCoinAddressValidator; this.assetService = assetService; this.filterManager = filterManager; + this.preferences = preferences; + + tradeInstant = paymentAccount instanceof InstantCryptoCurrencyAccount; } @Override @@ -94,12 +113,36 @@ public void addFormForAddAccount() { addTradeCurrencyComboBox(); currencyComboBox.setPrefWidth(250); + + tradeInstantCheckBox = addLabelCheckBox(gridPane, ++gridRow, + Res.get("payment.altcoin.tradeInstantCheckbox"), 10); + tradeInstantCheckBox.setSelected(tradeInstant); + tradeInstantCheckBox.setOnAction(e -> { + tradeInstant = tradeInstantCheckBox.isSelected(); + if (tradeInstant) + new Popup<>().information(Res.get("payment.altcoin.tradeInstant.popup")).show(); + }); + + // add new badge for this new feature for this release + // TODO: remove it with 0.9.6+ + gridPane.getChildren().remove(tradeInstantCheckBox); + tradeInstantCheckBox.setPadding(new Insets(0, 40, 0, 0)); + + NewBadge instantTradeNewsBadge = new NewBadge(tradeInstantCheckBox, INSTANT_TRADE_NEWS, preferences); + instantTradeNewsBadge.setAlignment(Pos.CENTER_LEFT); + instantTradeNewsBadge.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); + + GridPane.setRowIndex(instantTradeNewsBadge, gridRow); + GridPane.setHgrow(instantTradeNewsBadge, Priority.NEVER); + GridPane.setMargin(instantTradeNewsBadge, new Insets(10, 0, 0, 0)); + gridPane.getChildren().add(instantTradeNewsBadge); + addressInputTextField = FormBuilder.addInputTextField(gridPane, ++gridRow, Res.get("payment.altcoin.address")); addressInputTextField.setValidator(altCoinAddressValidator); addressInputTextField.textProperty().addListener((ov, oldValue, newValue) -> { - cryptoCurrencyAccount.setAddress(newValue); + assetAccount.setAddress(newValue); updateFromInputs(); }); @@ -107,11 +150,28 @@ public void addFormForAddAccount() { addAccountNameTextFieldWithAutoFillToggleButton(); } + @Override + public PaymentAccount getPaymentAccount() { + if (tradeInstant) { + InstantCryptoCurrencyAccount instantCryptoCurrencyAccount = new InstantCryptoCurrencyAccount(); + instantCryptoCurrencyAccount.init(); + instantCryptoCurrencyAccount.setAccountName(paymentAccount.getAccountName()); + instantCryptoCurrencyAccount.setSaltAsHex(paymentAccount.getSaltAsHex()); + instantCryptoCurrencyAccount.setSalt(paymentAccount.getSalt()); + instantCryptoCurrencyAccount.setSingleTradeCurrency(paymentAccount.getSingleTradeCurrency()); + instantCryptoCurrencyAccount.setSelectedTradeCurrency(paymentAccount.getSelectedTradeCurrency()); + instantCryptoCurrencyAccount.setAddress(assetAccount.getAddress()); + return instantCryptoCurrencyAccount; + } else { + return paymentAccount; + } + } + @Override public void updateFromInputs() { - if (addressInputTextField != null && cryptoCurrencyAccount.getSingleTradeCurrency() != null) + if (addressInputTextField != null && assetAccount.getSingleTradeCurrency() != null) addressInputTextField.setPromptText(Res.get("payment.altcoin.address.dyn", - cryptoCurrencyAccount.getSingleTradeCurrency().getName())); + assetAccount.getSingleTradeCurrency().getName())); super.updateFromInputs(); } @@ -131,14 +191,14 @@ protected void autoFillNameTextField() { public void addFormForDisplayAccount() { gridRowFrom = gridRow; addTopLabelTextField(gridPane, gridRow, Res.get("payment.account.name"), - cryptoCurrencyAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); + assetAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.paymentMethod"), - Res.get(cryptoCurrencyAccount.getPaymentMethod().getId())); + Res.get(assetAccount.getPaymentMethod().getId())); Tuple3 tuple2 = addCompactTopLabelTextField(gridPane, ++gridRow, - Res.get("payment.altcoin.address"), cryptoCurrencyAccount.getAddress()); + Res.get("payment.altcoin.address"), assetAccount.getAddress()); TextField field = tuple2.second; field.setMouseTransparent(false); - final TradeCurrency singleTradeCurrency = cryptoCurrencyAccount.getSingleTradeCurrency(); + final TradeCurrency singleTradeCurrency = assetAccount.getSingleTradeCurrency(); final String nameAndCode = singleTradeCurrency != null ? singleTradeCurrency.getNameAndCode() : ""; addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.altcoin"), nameAndCode); @@ -147,12 +207,12 @@ public void addFormForDisplayAccount() { @Override public void updateAllInputsValid() { - TradeCurrency selectedTradeCurrency = cryptoCurrencyAccount.getSelectedTradeCurrency(); + TradeCurrency selectedTradeCurrency = assetAccount.getSelectedTradeCurrency(); if (selectedTradeCurrency != null) { altCoinAddressValidator.setCurrencyCode(selectedTradeCurrency.getCode()); allInputsValid.set(isAccountNameValid() - && altCoinAddressValidator.validate(cryptoCurrencyAccount.getAddress()).isValid - && cryptoCurrencyAccount.getSingleTradeCurrency() != null); + && altCoinAddressValidator.validate(assetAccount.getAddress()).isValid + && assetAccount.getSingleTradeCurrency() != null); } } diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java index bf602956c81..61d965a7084 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java @@ -31,7 +31,7 @@ import bisq.core.locale.TradeCurrency; import bisq.core.offer.Offer; import bisq.core.payment.AccountAgeWitnessService; -import bisq.core.payment.CryptoCurrencyAccount; +import bisq.core.payment.AssetAccount; import bisq.core.payment.PaymentAccount; import bisq.core.util.BSFormatter; import bisq.core.util.validation.InputValidator; @@ -166,7 +166,7 @@ else if (paymentAccount.getSelectedTradeCurrency() != null) else if (!paymentAccount.getTradeCurrencies().isEmpty()) tradeCurrency = paymentAccount.getTradeCurrencies().get(0); else - tradeCurrency = paymentAccount instanceof CryptoCurrencyAccount ? + tradeCurrency = paymentAccount instanceof AssetAccount ? CurrencyUtil.getAllSortedCryptoCurrencies().get(0) : CurrencyUtil.getDefaultTradeCurrency(); @@ -174,7 +174,7 @@ else if (!paymentAccount.getTradeCurrencies().isEmpty()) final boolean isAddAccountScreen = paymentAccount.getAccountName() == null; final long accountAge = !isAddAccountScreen ? accountAgeWitnessService.getMyAccountAge(paymentAccount.getPaymentAccountPayload()) : 0L; - final String limitationsText = paymentAccount instanceof CryptoCurrencyAccount ? + final String limitationsText = paymentAccount instanceof AssetAccount ? Res.get("payment.maxPeriodAndLimitCrypto", getTimeText(hours), formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrency.getCode())))) diff --git a/desktop/src/main/java/bisq/desktop/main/MainView.java b/desktop/src/main/java/bisq/desktop/main/MainView.java index aad57c654bd..c71bdede63c 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainView.java +++ b/desktop/src/main/java/bisq/desktop/main/MainView.java @@ -40,6 +40,7 @@ import bisq.desktop.util.Transitions; import bisq.core.exceptions.BisqException; +import bisq.core.locale.GlobalSettings; import bisq.core.locale.Res; import bisq.core.util.BSFormatter; @@ -80,11 +81,17 @@ import javafx.geometry.Orientation; import javafx.geometry.Pos; +import javafx.beans.binding.ObjectBinding; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.ChangeListener; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +import java.util.Locale; + import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -186,6 +193,11 @@ protected void initialize() { JFXBadge daoButtonWithBadge = new JFXBadge(daoButton); daoButtonWithBadge.getStyleClass().add("new"); + Locale locale = GlobalSettings.getLocale(); + DecimalFormat currencyFormat = (DecimalFormat) NumberFormat.getNumberInstance(locale); + currencyFormat.setMinimumFractionDigits(2); + currencyFormat.setMaximumFractionDigits(2); + root.sceneProperty().addListener((observable, oldValue, newValue) -> { if (newValue != null) { newValue.addEventHandler(KeyEvent.KEY_RELEASED, keyEvent -> { @@ -231,15 +243,75 @@ protected void initialize() { Tuple2 availableBalanceBox = getBalanceBox(Res.get("mainView.balance.available")); availableBalanceBox.first.textProperty().bind(model.getAvailableBalance()); availableBalanceBox.first.setPrefWidth(100); - availableBalanceBox.first.setTooltip(new Tooltip(Res.get("mainView.balance.available"))); + availableBalanceBox.first.tooltipProperty().bind(new ObjectBinding<>() { + { + bind(model.getAvailableBalance()); + bind(model.getMarketPrice()); + } + + @Override + protected Tooltip computeValue() { + String tooltipText = Res.get("mainView.balance.available"); + try { + double availableBalance = Double.parseDouble( + model.getAvailableBalance().getValue().replace("BTC", "")); + double marketPrice = Double.parseDouble(model.getMarketPrice().getValue()); + tooltipText += "\n" + currencyFormat.format(availableBalance * marketPrice) + + " " + model.getPreferences().getPreferredTradeCurrency().getCode(); + } catch (NullPointerException | NumberFormatException e) { + // Either the balance or market price is not available yet + } + return new Tooltip(tooltipText); + } + }); Tuple2 reservedBalanceBox = getBalanceBox(Res.get("mainView.balance.reserved.short")); reservedBalanceBox.first.textProperty().bind(model.getReservedBalance()); - reservedBalanceBox.first.setTooltip(new Tooltip(Res.get("mainView.balance.reserved"))); + reservedBalanceBox.first.tooltipProperty().bind(new ObjectBinding<>() { + { + bind(model.getReservedBalance()); + bind(model.getMarketPrice()); + } + + @Override + protected Tooltip computeValue() { + String tooltipText = Res.get("mainView.balance.reserved"); + try { + double reservedBalance = Double.parseDouble( + model.getReservedBalance().getValue().replace("BTC", "")); + double marketPrice = Double.parseDouble(model.getMarketPrice().getValue()); + tooltipText += "\n" + currencyFormat.format(reservedBalance * marketPrice) + + " " + model.getPreferences().getPreferredTradeCurrency().getCode(); + } catch (NullPointerException | NumberFormatException e) { + // Either the balance or market price is not available yet + } + return new Tooltip(tooltipText); + } + }); Tuple2 lockedBalanceBox = getBalanceBox(Res.get("mainView.balance.locked.short")); lockedBalanceBox.first.textProperty().bind(model.getLockedBalance()); - lockedBalanceBox.first.setTooltip(new Tooltip(Res.get("mainView.balance.locked"))); + lockedBalanceBox.first.tooltipProperty().bind(new ObjectBinding<>() { + { + bind(model.getLockedBalance()); + bind(model.getMarketPrice()); + } + + @Override + protected Tooltip computeValue() { + String tooltipText = Res.get("mainView.balance.locked"); + try { + double lockedBalance = Double.parseDouble( + model.getLockedBalance().getValue().replace("BTC", "")); + double marketPrice = Double.parseDouble(model.getMarketPrice().getValue()); + tooltipText += "\n" + currencyFormat.format(lockedBalance * marketPrice) + + " " + model.getPreferences().getPreferredTradeCurrency().getCode(); + } catch (NullPointerException | NumberFormatException e) { + // Either the balance or market price is not available yet + } + return new Tooltip(tooltipText); + } + }); HBox primaryNav = new HBox(marketButton, getNavigationSeparator(), buyButton, getNavigationSeparator(), sellButton, getNavigationSeparator(), portfolioButtonWithBadge, getNavigationSeparator(), fundsButton); @@ -538,11 +610,6 @@ private VBox createSplashScreen() { Timer showTorNetworkSettingsTimer = UserThread.runAfter(() -> { showTorNetworkSettingsButton.setVisible(true); showTorNetworkSettingsButton.setManaged(true); - if (btcSyncIndicator.progressProperty().getValue() <= 0) { - // If no progress has been made, hide the BTC status since tor is not working - btcSyncIndicator.setVisible(false); - btcSplashInfo.setVisible(false); - } }, SHOW_TOR_SETTINGS_DELAY_SEC); splashP2PNetworkIconIdListener = (ov, oldValue, newValue) -> { diff --git a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java index 0a0b47aa18d..fec7833fcf4 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java @@ -97,6 +97,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupCompleteList private final DaoPresentation daoPresentation; private final P2PService p2PService; private final TradeManager tradeManager; + @Getter private final Preferences preferences; private final PrivateNotificationManager privateNotificationManager; private final WalletPasswordWindow walletPasswordWindow; @@ -591,6 +592,10 @@ IntegerProperty getMarketPriceUpdated() { return marketPricePresentation.getMarketPriceUpdated(); } + StringProperty getMarketPrice() { + return marketPricePresentation.getMarketPrice(); + } + public ObservableList getPriceFeedComboBoxItems() { return marketPricePresentation.getPriceFeedComboBoxItems(); } diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java index d431bcddab1..11fbec3a87b 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsDataModel.java @@ -25,9 +25,8 @@ import bisq.core.locale.TradeCurrency; import bisq.core.offer.OpenOfferManager; import bisq.core.payment.AccountAgeWitnessService; -import bisq.core.payment.CryptoCurrencyAccount; +import bisq.core.payment.AssetAccount; import bisq.core.payment.PaymentAccount; -import bisq.core.payment.payload.PaymentMethod; import bisq.core.trade.TradeManager; import bisq.core.user.Preferences; import bisq.core.user.User; @@ -83,7 +82,7 @@ protected void activate() { private void fillAndSortPaymentAccounts() { if (user.getPaymentAccounts() != null) { paymentAccounts.setAll(user.getPaymentAccounts().stream() - .filter(paymentAccount -> paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.BLOCK_CHAINS_ID)) + .filter(paymentAccount -> paymentAccount.getPaymentMethod().isAsset()) .collect(Collectors.toList())); paymentAccounts.sort((o1, o2) -> o1.getCreationDate().compareTo(o2.getCreationDate())); } @@ -117,7 +116,7 @@ public void onSaveNewAccount(PaymentAccount paymentAccount) { }); } - if (!(paymentAccount instanceof CryptoCurrencyAccount)) + if (!(paymentAccount instanceof AssetAccount)) accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload()); } @@ -143,7 +142,7 @@ public void onSelectAccount(PaymentAccount paymentAccount) { public void exportAccounts(Stage stage) { if (user.getPaymentAccounts() != null) { ArrayList accounts = new ArrayList<>(user.getPaymentAccounts().stream() - .filter(paymentAccount -> paymentAccount instanceof CryptoCurrencyAccount) + .filter(paymentAccount -> paymentAccount instanceof AssetAccount) .collect(Collectors.toList())); GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver); } diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java index 732e1a150ae..0ca30300bcf 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java @@ -19,7 +19,7 @@ import bisq.desktop.common.view.FxmlView; import bisq.desktop.components.TitledGroupBg; -import bisq.desktop.components.paymentmethods.CryptoCurrencyForm; +import bisq.desktop.components.paymentmethods.AssetsForm; import bisq.desktop.components.paymentmethods.PaymentMethodForm; import bisq.desktop.main.account.content.PaymentAccountsView; import bisq.desktop.main.overlays.popups.Popup; @@ -37,6 +37,7 @@ import bisq.core.payment.PaymentAccountFactory; import bisq.core.payment.payload.PaymentMethod; import bisq.core.payment.validation.AltCoinAddressValidator; +import bisq.core.user.Preferences; import bisq.core.util.BSFormatter; import bisq.core.util.validation.InputValidator; @@ -60,6 +61,7 @@ import java.util.Optional; +import static bisq.desktop.components.paymentmethods.AssetsForm.INSTANT_TRADE_NEWS; import static bisq.desktop.util.FormBuilder.add2ButtonsAfterGroup; import static bisq.desktop.util.FormBuilder.add3ButtonsAfterGroup; import static bisq.desktop.util.FormBuilder.addTitledGroupBg; @@ -74,6 +76,7 @@ public class AltCoinAccountsView extends PaymentAccountsView().warning(Res.get("shared.accountNameAlreadyUsed")).show(); } + + preferences.dontShowAgain(INSTANT_TRADE_NEWS, true); } } private void onCancelNewAccount() { removeNewAccountForm(); + + preferences.dontShowAgain(INSTANT_TRADE_NEWS, true); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -221,14 +230,14 @@ protected void onSelectAccount(PaymentAccount paymentAccount) { /////////////////////////////////////////////////////////////////////////////////////////// private PaymentMethodForm getPaymentMethodForm(PaymentMethod paymentMethod) { - final PaymentAccount paymentAccount = PaymentAccountFactory.getPaymentAccount(paymentMethod); + PaymentAccount paymentAccount = PaymentAccountFactory.getPaymentAccount(paymentMethod); paymentAccount.init(); return getPaymentMethodForm(paymentAccount); } private PaymentMethodForm getPaymentMethodForm(PaymentAccount paymentAccount) { - return new CryptoCurrencyForm(paymentAccount, accountAgeWitnessService, altCoinAddressValidator, - inputValidator, root, gridRow, formatter, assetService, filterManager); + return new AssetsForm(paymentAccount, accountAgeWitnessService, altCoinAddressValidator, + inputValidator, root, gridRow, formatter, assetService, filterManager, preferences); } private void removeNewAccountForm() { diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java index 88afc51720b..a60cc95e076 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsDataModel.java @@ -26,9 +26,8 @@ import bisq.core.locale.TradeCurrency; import bisq.core.offer.OpenOfferManager; import bisq.core.payment.AccountAgeWitnessService; -import bisq.core.payment.CryptoCurrencyAccount; +import bisq.core.payment.AssetAccount; import bisq.core.payment.PaymentAccount; -import bisq.core.payment.payload.PaymentMethod; import bisq.core.trade.TradeManager; import bisq.core.user.Preferences; import bisq.core.user.User; @@ -85,7 +84,7 @@ protected void activate() { private void fillAndSortPaymentAccounts() { if (user.getPaymentAccounts() != null) { List list = user.getPaymentAccounts().stream() - .filter(paymentAccount -> !paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.BLOCK_CHAINS_ID)) + .filter(paymentAccount -> !paymentAccount.getPaymentMethod().isAsset()) .collect(Collectors.toList()); paymentAccounts.setAll(list); paymentAccounts.sort(Comparator.comparing(PaymentAccount::getCreationDate)); @@ -146,7 +145,7 @@ public void onSelectAccount(PaymentAccount paymentAccount) { public void exportAccounts(Stage stage) { if (user.getPaymentAccounts() != null) { ArrayList accounts = new ArrayList<>(user.getPaymentAccounts().stream() - .filter(paymentAccount -> !(paymentAccount instanceof CryptoCurrencyAccount)) + .filter(paymentAccount -> !(paymentAccount instanceof AssetAccount)) .collect(Collectors.toList())); GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver); } diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java index b3564ae0b19..2360dc15fb8 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java @@ -80,6 +80,7 @@ import bisq.core.payment.PaymentAccount; import bisq.core.payment.PaymentAccountFactory; import bisq.core.payment.RevolutAccount; +import bisq.core.payment.USPostalMoneyOrderAccount; import bisq.core.payment.WesternUnionAccount; import bisq.core.payment.payload.PaymentMethod; import bisq.core.util.BSFormatter; @@ -276,6 +277,13 @@ private void onSaveNewAccount(PaymentAccount paymentAccount) { .actionButtonText(Res.get("shared.iConfirm")) .onAction(() -> doSaveNewAccount(paymentAccount)) .show(); + } else if (paymentAccount instanceof USPostalMoneyOrderAccount) { + new Popup<>().information(Res.get("payment.usPostalMoneyOrder.info")) + .width(700) + .closeButtonText(Res.get("shared.cancel")) + .actionButtonText(Res.get("shared.iUnderstand")) + .onAction(() -> doSaveNewAccount(paymentAccount)) + .show(); } else { doSaveNewAccount(paymentAccount); } @@ -334,7 +342,7 @@ protected void addNewAccount() { paymentMethodComboBox.setVisibleRowCount(11); paymentMethodComboBox.setPrefWidth(250); List list = PaymentMethod.getPaymentMethods().stream() - .filter(paymentMethod -> !paymentMethod.getId().equals(PaymentMethod.BLOCK_CHAINS_ID)) + .filter(paymentMethod -> !paymentMethod.isAsset()) .collect(Collectors.toList()); paymentMethodComboBox.setItems(FXCollections.observableArrayList(list)); paymentMethodComboBox.setConverter(new StringConverter<>() { diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/seedwords/SeedWordsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/seedwords/SeedWordsView.java index a6d50afa69b..bb615aa8153 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/seedwords/SeedWordsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/seedwords/SeedWordsView.java @@ -52,6 +52,7 @@ import java.time.Instant; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.Month; import java.time.ZoneId; import java.time.ZoneOffset; @@ -253,8 +254,13 @@ private void doRestore() { // If no date was specified, use Bisq 0.5 release date (no current Bisq wallet could have been created before that date). value = LocalDate.of(2017, Month.JUNE, 28); } - //TODO Is ZoneOffset correct? - long date = value.atStartOfDay().toEpochSecond(ZoneOffset.UTC); + + // We subtract 1 day to be sure to not have any issues with timezones. Even if we can be sure that the timezone + // is handled correctly it could be that the user created the wallet in one timezone and make a restore at + // a different timezone which could lead in the worst case that he miss the first day of the wallet transactions. + LocalDateTime localDateTime = value.atStartOfDay().minusDays(1); + long date = localDateTime.toEpochSecond(ZoneOffset.UTC); + DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(seedWordsTextArea.getText()), null, "", date); GUIUtil.restoreSeedWords(seed, walletsManager, storageDir); } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java index d6a62dde6a4..745a0e24d44 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java @@ -24,6 +24,8 @@ import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.util.GUIUtil; +import bisq.core.btc.exceptions.TransactionVerificationException; +import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.setup.WalletsSetup; import bisq.core.dao.DaoFacade; import bisq.core.dao.governance.bond.lockup.LockupReason; @@ -31,14 +33,17 @@ import bisq.core.dao.governance.bond.reputation.MyReputationListService; import bisq.core.dao.governance.bond.role.BondedRolesRepository; import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.governance.BondedRoleType; import bisq.core.dao.state.model.governance.Role; +import bisq.core.dao.state.model.governance.RoleProposal; import bisq.core.locale.Res; +import bisq.core.util.BSFormatter; import bisq.core.util.BsqFormatter; +import bisq.core.util.CoinUtil; import bisq.network.p2p.P2PService; import bisq.common.app.DevEnv; +import bisq.common.util.Tuple2; import org.bitcoinj.core.Coin; import org.bitcoinj.core.InsufficientMoneyException; @@ -80,9 +85,10 @@ public BondingViewUtils(P2PService p2PService, } public void lockupBondForBondedRole(Role role, Consumer resultHandler) { - BondedRoleType bondedRoleType = role.getBondedRoleType(); - Coin lockupAmount = Coin.valueOf(bondedRoleType.getRequiredBond()); - int lockupTime = bondedRoleType.getUnlockTimeInBlocks(); + Optional roleProposal = getAcceptedBondedRoleProposal(role); + checkArgument(roleProposal.isPresent(), "roleProposal must be present"); + Coin lockupAmount = Coin.valueOf(roleProposal.get().getRequiredBond()); + int lockupTime = roleProposal.get().getUnlockTime(); if (!bondedRolesRepository.isBondedAssetAlreadyInBond(role)) { lockupBond(role.getHash(), lockupAmount, lockupTime, LockupReason.BONDED_ROLE, resultHandler); } else { @@ -100,15 +106,29 @@ private void lockupBond(byte[] hash, Coin lockupAmount, int lockupTime, LockupRe Consumer resultHandler) { if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) { if (!DevEnv.isDevMode()) { - new Popup<>().headLine(Res.get("dao.bond.reputation.lockup.headline")) - .confirmation(Res.get("dao.bond.reputation.lockup.details", - bsqFormatter.formatCoinWithCode(lockupAmount), - lockupTime - )) - .actionButtonText(Res.get("shared.yes")) - .onAction(() -> publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler)) - .closeButtonText(Res.get("shared.cancel")) - .show(); + try { + Tuple2 miningFeeAndTxSize = daoFacade.getMiningFeeAndTxSize(lockupAmount); + Coin miningFee = miningFeeAndTxSize.first; + int txSize = miningFeeAndTxSize.second; + BSFormatter formatter = new BSFormatter(); + String duration = formatter.formatDurationAsWords(lockupTime * 10 * 60 * 1000L, false, false); + new Popup<>().headLine(Res.get("dao.bond.reputation.lockup.headline")) + .confirmation(Res.get("dao.bond.reputation.lockup.details", + bsqFormatter.formatCoinWithCode(lockupAmount), + lockupTime, + duration, + formatter.formatCoinWithCode(miningFee), + CoinUtil.getFeePerByte(miningFee, txSize) + )) + .actionButtonText(Res.get("shared.yes")) + .onAction(() -> publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler)) + .closeButtonText(Res.get("shared.cancel")) + .show(); + } catch (WalletException | InsufficientMoneyException | TransactionVerificationException e) { + log.error(e.toString()); + e.printStackTrace(); + new Popup<>().warning(e.getMessage()).show(); + } } else { publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler); } @@ -133,6 +153,10 @@ private void publishLockupTx(byte[] hash, Coin lockupAmount, int lockupTime, Loc ); } + public Optional getAcceptedBondedRoleProposal(Role role) { + return bondedRolesRepository.getAcceptedBondedRoleProposal(role); + } + public void unLock(String lockupTxId, Consumer resultHandler) { if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) { Optional lockupTxOutput = daoFacade.getLockupTxOutput(lockupTxId); @@ -143,10 +167,18 @@ public void unLock(String lockupTxId, Consumer resultHandler) { try { if (!DevEnv.isDevMode()) { + Tuple2 miningFeeAndTxSize = daoFacade.getMiningFeeAndTxSize(unlockAmount); + Coin miningFee = miningFeeAndTxSize.first; + int txSize = miningFeeAndTxSize.second; + BSFormatter formatter = new BSFormatter(); + String duration = formatter.formatDurationAsWords(lockTime * 10 * 60 * 1000L, false, false); new Popup<>().headLine(Res.get("dao.bond.reputation.unlock.headline")) .confirmation(Res.get("dao.bond.reputation.unlock.details", bsqFormatter.formatCoinWithCode(unlockAmount), - lockTime + lockTime, + duration, + formatter.formatCoinWithCode(miningFee), + CoinUtil.getFeePerByte(miningFee, txSize) )) .actionButtonText(Res.get("shared.yes")) .onAction(() -> publishUnlockTx(lockupTxId, resultHandler)) diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.java index e263a6a2b34..39e3559adb8 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.java @@ -93,7 +93,7 @@ private BondsView(BsqFormatter bsqFormatter, @Override public void initialize() { - tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.allBonds.header")); + tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.allBonds.header"), "last"); tableView.setItems(sortedList); addColumns(); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.java index 1d0ae8ea84f..32054288fdd 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.java @@ -42,7 +42,7 @@ private BondingDashboardView(BsqBalanceUtil bsqBalanceUtil) { public void initialize() { gridRow = bsqBalanceUtil.addGroup(root, gridRow); - gridRow = bsqBalanceUtil.addBondBalanceGroup(root, gridRow); + gridRow = bsqBalanceUtil.addBondBalanceGroup(root, gridRow, "last"); } @Override diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.java index 795f1278881..b1cae497365 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.java @@ -144,7 +144,7 @@ public void initialize() { lockupButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.bond.reputation.lockupButton")); - tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.reputation.table.header"), 20); + tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.reputation.table.header"), 20, "last"); createColumns(); tableView.setItems(sortedList); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RoleDetailsWindow.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RoleDetailsWindow.java index f8fd2c55966..54c29fa886b 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RoleDetailsWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RoleDetailsWindow.java @@ -21,6 +21,7 @@ import bisq.desktop.util.FormBuilder; import bisq.core.dao.state.model.governance.BondedRoleType; +import bisq.core.dao.state.model.governance.RoleProposal; import bisq.core.locale.Res; import bisq.core.util.BsqFormatter; @@ -28,17 +29,20 @@ import javafx.geometry.Insets; +import java.util.Optional; + import lombok.extern.slf4j.Slf4j; -//TODO not used atm but keep it as is should be used @Slf4j class RoleDetailsWindow extends Overlay { private final BondedRoleType bondedRoleType; + private final Optional roleProposal; private final BsqFormatter bsqFormatter; - public RoleDetailsWindow(BondedRoleType bondedRoleType, BsqFormatter bsqFormatter) { + RoleDetailsWindow(BondedRoleType bondedRoleType, Optional roleProposal, BsqFormatter bsqFormatter) { this.bondedRoleType = bondedRoleType; + this.roleProposal = roleProposal; this.bsqFormatter = bsqFormatter; width = 968; @@ -77,11 +81,13 @@ private void addContent() { FormBuilder.addTopLabelTextField(gridPane, ++rowIndex, Res.get("dao.bond.details.role"), bondedRoleType.getDisplayString()); + long requiredBond = roleProposal.map(RoleProposal::getRequiredBond).orElse(bondedRoleType.getRequiredBond()); + int unlockTime = roleProposal.map(RoleProposal::getUnlockTime).orElse(bondedRoleType.getUnlockTimeInBlocks()); FormBuilder.addTopLabelTextField(gridPane, ++rowIndex, Res.get("dao.bond.details.requiredBond"), - bsqFormatter.formatCoinWithCode(Coin.valueOf(bondedRoleType.getRequiredBond()))); + bsqFormatter.formatCoinWithCode(Coin.valueOf(requiredBond))); FormBuilder.addTopLabelTextField(gridPane, ++rowIndex, Res.get("dao.bond.details.unlockTime"), - Res.get("dao.bond.details.blocks", bondedRoleType.getUnlockTimeInBlocks())); + Res.get("dao.bond.details.blocks", unlockTime)); FormBuilder.addTopLabelHyperlinkWithIcon(gridPane, ++rowIndex, Res.get("dao.bond.details.link"), bondedRoleType.getLink(), bondedRoleType.getLink(), 0); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.java index 264979523b7..2beb26c5887 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.java @@ -30,6 +30,7 @@ import bisq.core.dao.governance.bond.BondState; import bisq.core.dao.governance.bond.role.BondedRole; import bisq.core.dao.state.model.governance.BondedRoleType; +import bisq.core.dao.state.model.governance.RoleProposal; import bisq.core.locale.Res; import bisq.core.user.Preferences; import bisq.core.util.BsqFormatter; @@ -56,6 +57,7 @@ import javafx.util.Callback; import java.util.Comparator; +import java.util.Optional; import java.util.stream.Collectors; @FxmlView @@ -91,7 +93,7 @@ private RolesView(BsqFormatter bsqFormatter, @Override public void initialize() { int gridRow = 0; - tableView = FormBuilder.addTableViewWithHeader(root, gridRow, Res.get("dao.bond.bondedRoles")); + tableView = FormBuilder.addTableViewWithHeader(root, gridRow, Res.get("dao.bond.bondedRoles"), "last"); createColumns(); tableView.setItems(sortedList); @@ -206,7 +208,8 @@ public void updateItem(final RolesListItem item, boolean empty) { String type = bondedRoleType.getDisplayString(); hyperlink = new Hyperlink(type); hyperlink.setOnAction(event -> { - new RoleDetailsWindow(bondedRoleType, bsqFormatter).show(); + Optional roleProposal = bondingViewUtils.getAcceptedBondedRoleProposal(item.getRole()); + new RoleDetailsWindow(bondedRoleType, roleProposal, bsqFormatter).show(); }); hyperlink.setTooltip(new Tooltip(Res.get("tooltip.openPopupForDetails", type))); setGraphic(hyperlink); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.java index 2adf543518a..2410712cc9d 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.java @@ -141,7 +141,7 @@ public StatefulAsset fromString(String string) { payFeeButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.burnBsq.payFee")); - tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.burnBsq.allAssets"), 20); + tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.burnBsq.allAssets"), 20, "last"); createColumns(); tableView.setItems(sortedList); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.java index 32831864fa4..640ee7894d9 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.java @@ -143,7 +143,7 @@ public void initialize() { createColumnsForMyItems(); myItemsTableView.setItems(myItemsSortedList); - allTxsTableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.proofOfBurn.allTxs"), 30); + allTxsTableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.proofOfBurn.allTxs"), 30, "last"); createColumnsForAllTxs(); allTxsTableView.setItems(allItemsSortedList); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java index 20105df8fd0..0acebf77a24 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java @@ -55,6 +55,7 @@ import bisq.core.dao.state.model.governance.Vote; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; +import bisq.core.user.Preferences; import bisq.core.util.BsqFormatter; import bisq.core.util.validation.InputValidator; import bisq.core.util.validation.UrlInputValidator; @@ -107,6 +108,7 @@ public class ProposalDisplay { @Nullable private final ChangeParamValidator changeParamValidator; private final Navigation navigation; + private final Preferences preferences; @Nullable private TextField proposalFeeTextField, comboBoxValueTextField, requiredBondForRoleTextField; @@ -127,6 +129,7 @@ public class ProposalDisplay { @Getter private int gridRow; private HyperlinkWithIcon linkHyperlinkWithIcon; + private HyperlinkWithIcon txHyperlinkWithIcon; private int gridRowStartIndex; private final List inputChangedListeners = new ArrayList<>(); @Getter @@ -141,13 +144,18 @@ public class ProposalDisplay { private int titledGroupBgRowSpan; private VBox linkWithIconContainer, comboBoxValueContainer, myVoteBox, voteResultBox; - public ProposalDisplay(GridPane gridPane, BsqFormatter bsqFormatter, DaoFacade daoFacade, - @Nullable ChangeParamValidator changeParamValidator, Navigation navigation) { + public ProposalDisplay(GridPane gridPane, + BsqFormatter bsqFormatter, + DaoFacade daoFacade, + @Nullable ChangeParamValidator changeParamValidator, + Navigation navigation, + @Nullable Preferences preferences) { this.gridPane = gridPane; this.bsqFormatter = bsqFormatter; this.daoFacade = daoFacade; this.changeParamValidator = changeParamValidator; this.navigation = navigation; + this.preferences = preferences; // focusOutListener = observable -> inputChangedListeners.forEach(Runnable::run); @@ -193,11 +201,19 @@ public void createAllFields(String title, int gridRowStartIndex, double top, Pro } titledGroupBg = addTitledGroupBg(gridPane, gridRow, titledGroupBgRowSpan, title, top); - double proposalTypeTop = top == Layout.GROUP_DISTANCE ? Layout.FIRST_ROW_AND_GROUP_DISTANCE : Layout.FIRST_ROW_DISTANCE; + double proposalTypeTop; + + if (top == Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR) { + proposalTypeTop = Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE_WITHOUT_SEPARATOR; + } else if (top == Layout.GROUP_DISTANCE) { + proposalTypeTop = Layout.FIRST_ROW_AND_GROUP_DISTANCE; + } else { + proposalTypeTop = Layout.FIRST_ROW_DISTANCE; + } proposalTypeTextField = addTopLabelTextField(gridPane, gridRow, Res.get("dao.proposal.display.type"), proposalType.getDisplayName(), proposalTypeTop).second; - nameTextField = addInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name"), Layout.FIRST_ROW_DISTANCE); + nameTextField = addInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name")); if (isMakeProposalScreen) nameTextField.setValidator(new InputValidator()); inputControls.add(nameTextField); @@ -219,6 +235,14 @@ public void createAllFields(String title, int gridRowStartIndex, double top, Pro linkWithIconContainer.setVisible(false); linkWithIconContainer.setManaged(false); + if (!isMakeProposalScreen) { + Tuple3 uidTuple = addTopLabelHyperlinkWithIcon(gridPane, ++gridRow, + Res.get("dao.proposal.display.txId"), "", "", 0); + txHyperlinkWithIcon = uidTuple.second; + // TODO HyperlinkWithIcon does not scale automatically (button base, -> make anchorpane as base) + txHyperlinkWithIcon.prefWidthProperty().bind(nameTextField.widthProperty()); + } + int comboBoxValueTextFieldIndex = -1; switch (proposalType) { case COMPENSATION_REQUEST: @@ -285,7 +309,10 @@ public Param fromString(String string) { Res.get("dao.proposal.display.bondedRoleComboBox.label")); comboBoxValueTextFieldIndex = gridRow; checkNotNull(bondedRoleTypeComboBox, "bondedRoleTypeComboBox must not be null"); - bondedRoleTypeComboBox.setItems(FXCollections.observableArrayList(BondedRoleType.values())); + List bondedRoleTypes = Arrays.stream(BondedRoleType.values()) + .filter(e -> e != BondedRoleType.UNDEFINED) + .collect(Collectors.toList()); + bondedRoleTypeComboBox.setItems(FXCollections.observableArrayList(bondedRoleTypes)); bondedRoleTypeComboBox.setConverter(new StringConverter<>() { @Override public String toString(BondedRoleType bondedRoleType) { @@ -315,7 +342,7 @@ public BondedRoleType fromString(String string) { comboBoxValueTextFieldIndex = gridRow; checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null"); - confiscateBondComboBox.setItems(FXCollections.observableArrayList(daoFacade.getAllBonds())); + confiscateBondComboBox.setItems(FXCollections.observableArrayList(daoFacade.getAllActiveBonds())); confiscateBondComboBox.setConverter(new StringConverter<>() { @Override public String toString(Bond bond) { @@ -328,7 +355,6 @@ public String toString(Bond bond) { bondType = Res.get("dao.bond.bondedReputation"); bondDetails = Utilities.bytesAsHexString(bond.getBondedAsset().getHash()); } - return bondType + ": " + bondDetails; } @@ -383,12 +409,11 @@ public Asset fromString(String string) { Tuple3 tuple3 = addTopLabelTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.myVote")); myVoteBox = tuple3.third; - myVoteBox.setVisible(false); - myVoteBox.setManaged(false); + setMyVoteBoxVisibility(false); myVoteTextField = tuple3.second; - tuple3 = addTopLabelTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.voteResult")); + tuple3 = addTopLabelReadOnlyTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.voteResult")); voteResultBox = tuple3.third; voteResultBox.setVisible(false); @@ -409,8 +434,7 @@ public void applyBallot(@Nullable Ballot ballot) { } myVoteTextField.setText(myVote); - myVoteBox.setVisible(isNotNull); - myVoteBox.setManaged(isNotNull); + setMyVoteBoxVisibility(isNotNull); } public void applyEvaluatedProposal(@Nullable EvaluatedProposal evaluatedProposal) { @@ -422,9 +446,9 @@ public void applyEvaluatedProposal(@Nullable EvaluatedProposal evaluatedProposal Res.get("dao.proposal.voteResult.failed"); ProposalVoteResult proposalVoteResult = evaluatedProposal.getProposalVoteResult(); String threshold = (proposalVoteResult.getThreshold() / 100D) + "%"; - String requiredThreshold = (evaluatedProposal.getRequiredThreshold() / 100D) + "%"; + String requiredThreshold = (daoFacade.getRequiredThreshold(evaluatedProposal.getProposal()) * 100D) + "%"; String quorum = bsqFormatter.formatCoinWithCode(Coin.valueOf(proposalVoteResult.getQuorum())); - String requiredQuorum = bsqFormatter.formatCoinWithCode(Coin.valueOf(evaluatedProposal.getRequiredQuorum())); + String requiredQuorum = bsqFormatter.formatCoinWithCode(daoFacade.getRequiredQuorum(evaluatedProposal.getProposal())); String summary = Res.get("dao.proposal.voteResult.summary", result, threshold, requiredThreshold, quorum, requiredQuorum); voteResultTextField.setText(summary); @@ -453,8 +477,7 @@ public void applyBallotAndVoteWeight(@Nullable Ballot ballot, long merit, long s } boolean show = ballotIsNotNull && hasVoted; - myVoteBox.setVisible(show); - myVoteBox.setManaged(show); + setMyVoteBoxVisibility(show); } public void setIsVoteIncludedInResult(boolean isVoteIncludedInResult) { @@ -477,6 +500,12 @@ public void applyProposalPayload(Proposal proposal) { linkHyperlinkWithIcon.setOnAction(e -> GUIUtil.openWebPage(proposal.getLink())); } + if (txHyperlinkWithIcon != null) { + txHyperlinkWithIcon.setText(proposal.getTxId()); + txHyperlinkWithIcon.setOnAction(e -> + GUIUtil.openTxInBsqBlockExplorer(proposal.getTxId(), preferences)); + } + if (proposal instanceof CompensationProposal) { CompensationProposal compensationProposal = (CompensationProposal) proposal; checkNotNull(requestedBsqTextField, "requestedBsqTextField must not be null"); @@ -498,7 +527,8 @@ public void applyProposalPayload(Proposal proposal) { Role role = roleProposal.getRole(); bondedRoleTypeComboBox.getSelectionModel().select(role.getBondedRoleType()); comboBoxValueTextField.setText(bondedRoleTypeComboBox.getConverter().toString(role.getBondedRoleType())); - requiredBondForRoleTextField.setText(bsqFormatter.formatCoin(Coin.valueOf(role.getBondedRoleType().getRequiredBond()))); + requiredBondForRoleTextField.setText(bsqFormatter.formatCoin(Coin.valueOf(roleProposal.getRequiredBond()))); + // TODO maybe show also unlock time? } else if (proposal instanceof ConfiscateBondProposal) { ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null"); @@ -508,7 +538,7 @@ public void applyProposalPayload(Proposal proposal) { comboBoxValueTextField.setText(confiscateBondComboBox.getConverter().toString(bond)); comboBoxValueTextField.setOnMouseClicked(e -> navigation.navigateToWithData(bond, MainView.class, DaoView.class, BondingView.class, - BondsView.class)); + BondsView.class)); comboBoxValueTextField.getStyleClass().addAll("hyperlink", "show-hand"); }); } else if (proposal instanceof GenericProposal) { @@ -608,10 +638,6 @@ public int incrementAndGetGridRow() { return ++gridRow; } - public int getGridRow() { - return gridRow; - } - @SuppressWarnings("Duplicates") public ScrollPane getView() { ScrollPane scrollPane = new ScrollPane(); @@ -637,4 +663,13 @@ public ScrollPane getView() { return scrollPane; } + + private void setMyVoteBoxVisibility(boolean visibility) { + myVoteBox.setVisible(visibility); + myVoteBox.setManaged(visibility); + + if (visibility) { + GridPane.setRowSpan(titledGroupBg, titledGroupBgRowSpan); + } + } } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.java index c2a997282b2..c4421984064 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.java @@ -19,6 +19,7 @@ import bisq.desktop.common.view.ActivatableView; import bisq.desktop.common.view.FxmlView; +import bisq.desktop.components.TitledGroupBg; import bisq.desktop.main.dao.governance.PhasesView; import bisq.desktop.util.Layout; @@ -69,7 +70,8 @@ public GovernanceDashboardView(DaoFacade daoFacade, PeriodService periodService, public void initialize() { gridRow = phasesView.addGroup(root, gridRow); - addTitledGroupBg(root, ++gridRow, 6, Res.get("dao.cycle.overview.headline"), Layout.GROUP_DISTANCE); + TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 6, Res.get("dao.cycle.overview.headline"), Layout.GROUP_DISTANCE); + titledGroupBg.getStyleClass().add("last"); currentBlockHeightTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.cycle.currentBlockHeight"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; currentPhaseTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.cycle.currentPhase")).second; diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.java index 47e22437626..ae097c2c61b 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.java @@ -33,13 +33,14 @@ import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.dao.DaoFacade; -import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.bond.Bond; import bisq.core.dao.governance.param.Param; import bisq.core.dao.governance.proposal.ProposalType; +import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.governance.proposal.param.ChangeParamValidator; +import bisq.core.dao.governance.voteresult.VoteResultException; import bisq.core.dao.presentation.DaoUtil; import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.model.blockchain.Block; @@ -87,6 +88,7 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -158,6 +160,7 @@ public void initialize() { gridRow = phasesView.addGroup(root, gridRow); proposalTitledGroup = addTitledGroupBg(root, ++gridRow, 2, proposalGroupTitle.get(), Layout.GROUP_DISTANCE); + proposalTitledGroup.getStyleClass().add("last"); final Tuple3 nextProposalPhaseTuple = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.cycle.proposal.next"), Layout.FIRST_ROW_AND_GROUP_DISTANCE); @@ -184,7 +187,9 @@ public ProposalType fromString(String string) { }; alwaysVisibleGridRowIndex = gridRow + 1; - List proposalTypes = Arrays.asList(ProposalType.values()); + List proposalTypes = Arrays.stream(ProposalType.values()) + .filter(e -> e != ProposalType.UNDEFINED) + .collect(Collectors.toList()); proposalTypeComboBox.setItems(FXCollections.observableArrayList(proposalTypes)); } @@ -257,7 +262,6 @@ public void onParseTxsCompleteAfterBatchProcessing(Block block) { } - /////////////////////////////////////////////////////////////////////////////////////////// // Private /////////////////////////////////////////////////////////////////////////////////////////// @@ -279,11 +283,11 @@ private void publishMyProposal(ProposalType type) { Coin fee = daoFacade.getProposalFee(daoFacade.getChainHeight()); if (type.equals(ProposalType.BONDED_ROLE)) { - final long requiredBond = proposalDisplay.bondedRoleTypeComboBox.getSelectionModel().getSelectedItem().getRequiredBond(); - final long availableBalance = bsqWalletService.getAvailableBalance().value; + long requiredBond = proposalDisplay.bondedRoleTypeComboBox.getSelectionModel().getSelectedItem().getRequiredBond(); + long availableBalance = bsqWalletService.getAvailableBalance().value; if (requiredBond > availableBalance) { - final long missing = requiredBond - availableBalance; + long missing = requiredBond - availableBalance; new Popup<>().warning(Res.get("dao.proposal.create.missingBsqFundsForBond", bsqFormatter.formatCoinWithCode(missing))) .actionButtonText(Res.get("dao.proposal.create.publish")) @@ -291,6 +295,8 @@ private void publishMyProposal(ProposalType type) { showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txSize, fee); }) .show(); + } else { + showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txSize, fee); } } else { showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txSize, fee); @@ -303,8 +309,7 @@ private void publishMyProposal(ProposalType type) { new Popup<>().warning(Res.get("dao.proposal.create.missingMinerFeeFunds", btcFormatter.formatCoinWithCode(e.missing))).show(); } - - } catch (ValidationException e) { + } catch (ProposalValidationException e) { String message; if (e.getMinRequestAmount() != null) { message = Res.get("validation.bsq.amountBelowMinAmount", @@ -313,7 +318,7 @@ private void publishMyProposal(ProposalType type) { message = e.getMessage(); } new Popup<>().warning(message).show(); - } catch (TxException e) { + } catch (Throwable e) { log.error(e.toString()); e.printStackTrace(); new Popup<>().warning(e.toString()).show(); @@ -330,7 +335,6 @@ private void showFeeInfoAndPublishMyProposal(Proposal proposal, Transaction tran } private void doPublishMyProposal(Proposal proposal, Transaction transaction) { - busyLabel.setVisible(true); busyAnimation.play(); makeProposalButton.setDisable(true); @@ -358,14 +362,14 @@ private void doPublishMyProposal(Proposal proposal, Transaction transaction) { } @Nullable - private ProposalWithTransaction getProposalWithTransaction(ProposalType type) - throws InsufficientMoneyException, ValidationException, TxException { + private ProposalWithTransaction getProposalWithTransaction(ProposalType proposalType) + throws InsufficientMoneyException, ProposalValidationException, TxException, VoteResultException.ValidationException { checkNotNull(proposalDisplay, "proposalDisplay must not be null"); String link = proposalDisplay.linkInputTextField.getText(); String name = proposalDisplay.nameTextField.getText(); - switch (type) { + switch (proposalType) { case COMPENSATION_REQUEST: checkNotNull(proposalDisplay.requestedBsqTextField, "proposalDisplay.requestedBsqTextField must not be null"); @@ -385,10 +389,10 @@ private ProposalWithTransaction getProposalWithTransaction(ProposalType type) "proposalDisplay.paramValueTextField must no tbe null"); Param selectedParam = proposalDisplay.paramComboBox.getSelectionModel().getSelectedItem(); if (selectedParam == null) - throw new ValidationException("selectedParam is null"); + throw new ProposalValidationException("selectedParam is null"); String paramValueAsString = proposalDisplay.paramValueTextField.getText(); if (paramValueAsString == null || paramValueAsString.isEmpty()) - throw new ValidationException("paramValue is null or empty"); + throw new ProposalValidationException("paramValue is null or empty"); try { String paramValue = bsqFormatter.parseParamValueToString(selectedParam, paramValueAsString); @@ -415,6 +419,10 @@ private ProposalWithTransaction getProposalWithTransaction(ProposalType type) checkNotNull(proposalDisplay.confiscateBondComboBox, "proposalDisplay.confiscateBondComboBox must not be null"); Bond bond = proposalDisplay.confiscateBondComboBox.getSelectionModel().getSelectedItem(); + + if (!bond.isActive()) + throw new VoteResultException.ValidationException("Bond is not locked and can't be confiscated"); + return daoFacade.getConfiscateBondProposalWithTransaction(name, link, bond.getLockupTxId()); case GENERIC: return daoFacade.getGenericProposalWithTransaction(name, link); @@ -432,9 +440,9 @@ private ProposalWithTransaction getProposalWithTransaction(ProposalType type) private void addProposalDisplay() { if (selectedProposalType != null) { - proposalDisplay = new ProposalDisplay(root, bsqFormatter, daoFacade, changeParamValidator, navigation); + proposalDisplay = new ProposalDisplay(root, bsqFormatter, daoFacade, changeParamValidator, navigation, null); - proposalDisplay.createAllFields(Res.get("dao.proposal.create.new"), alwaysVisibleGridRowIndex, Layout.GROUP_DISTANCE, + proposalDisplay.createAllFields(Res.get("dao.proposal.create.new"), alwaysVisibleGridRowIndex, Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR, selectedProposalType, true); final Tuple4 makeProposalTuple = addButtonBusyAnimationLabelAfterGroup(root, diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java index c0748091064..63ea4be4d42 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java @@ -40,6 +40,7 @@ import bisq.core.btc.listeners.BsqBalanceListener; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.dao.DaoFacade; +import bisq.core.dao.governance.blindvote.BlindVoteConsensus; import bisq.core.dao.governance.myvote.MyVote; import bisq.core.dao.governance.proposal.param.ChangeParamValidator; import bisq.core.dao.state.DaoStateListener; @@ -261,10 +262,12 @@ public void onUpdateBalances(Coin confirmedBalance, Coin lockedForVotingBalance, Coin lockupBondsBalance, Coin unlockingBondsBalance) { - if (isBlindVotePhaseButNotLastBlock()) + Coin blindVoteFee = BlindVoteConsensus.getFee(daoStateService, daoStateService.getChainHeight()); + if (isBlindVotePhaseButNotLastBlock()) { + Coin availableForVoting = confirmedBalance.subtract(blindVoteFee); stakeInputTextField.setPromptText(Res.get("dao.proposal.myVote.stake.prompt", - bsqFormatter.formatCoinWithCode(confirmedBalance))); - else + bsqFormatter.formatCoinWithCode(availableForVoting))); + } else stakeInputTextField.setPromptText(""); } @@ -322,7 +325,7 @@ private void updateListItems() { onSelectProposal(null); } - GUIUtil.setFitToRowsForTableView(tableView, 37, 28, 2, 4); + GUIUtil.setFitToRowsForTableView(tableView, 38, 28, 2, 6); tableView.layout(); root.layout(); } @@ -498,7 +501,7 @@ private void onVote() { Coin stake = bsqFormatter.parseToCoin(stakeInputTextField.getText()); try { // We create a dummy tx to get the miningFee for displaying it at the confirmation popup - Tuple2 miningFeeAndTxSize = daoFacade.getMiningFeeAndTxSize(stake); + Tuple2 miningFeeAndTxSize = daoFacade.getBlindVoteMiningFeeAndTxSize(stake); Coin miningFee = miningFeeAndTxSize.first; int txSize = miningFeeAndTxSize.second; Coin blindVoteFee = daoFacade.getBlindVoteFeeForCycle(); @@ -523,6 +526,7 @@ private void publishBlindVote(Coin stake) { }, exception -> { voteButtonBusyAnimation.stop(); voteButtonInfoLabel.setText(""); + updateViews(); new Popup<>().warning(exception.toString()).show(); }); @@ -677,7 +681,7 @@ private void createProposalsTableView() { private void createEmptyProposalDisplay() { proposalDisplay = new ProposalDisplay(proposalDisplayGridPane, bsqFormatter, daoFacade, - changeParamValidator, navigation); + changeParamValidator, navigation, preferences); proposalDisplayView = proposalDisplay.getView(); GridPane.setMargin(proposalDisplayView, new Insets(0, -10, 0, -10)); GridPane.setRowIndex(proposalDisplayView, ++gridRow); @@ -688,6 +692,7 @@ private void createEmptyProposalDisplay() { private void createVoteView() { voteTitledGroupBg = addTitledGroupBg(root, ++gridRow, 4, Res.get("dao.proposal.votes.header"), 20); + voteTitledGroupBg.getStyleClass().add("last"); voteFields.add(voteTitledGroupBg); Tuple3 meritTuple = addTopLabelTextField(root, gridRow, diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/CycleListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/CycleListItem.java index 31c117f6495..108a993f935 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/CycleListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/CycleListItem.java @@ -46,10 +46,16 @@ public class CycleListItem { } public String getCycle() { - int displayIndex = resultsOfCycle.getCycleIndex() + 1; + return Res.get("dao.results.results.table.item.cycle", getCycleIndex(), getCycleDateTime(true)); + } + + public String getCycleDateTime(boolean useLocaleAndLocalTimezone) { long cycleStartTime = resultsOfCycle.getCycleStartTime(); - String dateTime = cycleStartTime > 0 ? bsqFormatter.formatDateTime(new Date(cycleStartTime)) : Res.get("shared.na"); - return Res.get("dao.results.results.table.item.cycle", displayIndex, dateTime); + return cycleStartTime > 0 ? bsqFormatter.formatDateTime(new Date(cycleStartTime), useLocaleAndLocalTimezone) : Res.get("shared.na"); + } + + public int getCycleIndex() { + return resultsOfCycle.getCycleIndex() + 1; } public String getNumProposals() { diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ProposalListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ProposalListItem.java index c70316e2646..ecd15d4955d 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ProposalListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ProposalListItem.java @@ -105,6 +105,14 @@ public String getColorStyleClass() { } public String getDetails() { + return ProposalListItem.getProposalDetails(evaluatedProposal, bsqFormatter); + } + + public static String getProposalDetails(EvaluatedProposal evaluatedProposal, BsqFormatter bsqFormatter) { + return getProposalDetails(evaluatedProposal, bsqFormatter, true); + } + + public static String getProposalDetails(EvaluatedProposal evaluatedProposal, BsqFormatter bsqFormatter, boolean useDisplayString) { Proposal proposal = evaluatedProposal.getProposal(); switch (proposal.getType()) { case COMPENSATION_REQUEST: @@ -117,11 +125,12 @@ public String getDetails() { return bsqFormatter.formatCoinWithCode(requestedBsq); case CHANGE_PARAM: ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; - return changeParamProposal.getParam().getDisplayString(); + return useDisplayString ? changeParamProposal.getParam().getDisplayString() : changeParamProposal.getParam().name(); case BONDED_ROLE: RoleProposal roleProposal = (RoleProposal) proposal; Role role = roleProposal.getRole(); - return Res.get("dao.bond.bondedRoleType." + role.getBondedRoleType().name()); + String name = role.getBondedRoleType().name(); + return useDisplayString ? Res.get("dao.bond.bondedRoleType." + name) : name; case CONFISCATE_BOND: ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; // TODO add info to bond diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.fxml index 091800d848d..e9004423946 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.fxml @@ -21,6 +21,8 @@ + + implements D private final Preferences preferences; private final BsqFormatter bsqFormatter; private final Navigation navigation; + private MyProposalListService myProposalListService; + private MyBlindVoteListService myBlindVoteListService; + private Button exportButton; private int gridRow = 0; @@ -140,7 +169,9 @@ public VoteResultView(DaoFacade daoFacade, BsqWalletService bsqWalletService, Preferences preferences, BsqFormatter bsqFormatter, - Navigation navigation) { + Navigation navigation, + MyProposalListService myProposalListService, + MyBlindVoteListService myBlindVoteListService) { this.daoFacade = daoFacade; this.phasesView = phasesView; this.daoStateService = daoStateService; @@ -152,14 +183,21 @@ public VoteResultView(DaoFacade daoFacade, this.preferences = preferences; this.bsqFormatter = bsqFormatter; this.navigation = navigation; + this.myProposalListService = myProposalListService; + this.myBlindVoteListService = myBlindVoteListService; } @Override public void initialize() { gridRow = phasesView.addGroup(root, gridRow); + selectedVoteResultListItemListener = (observable, oldValue, newValue) -> onResultsListItemSelected(newValue); createCyclesTable(); + exportButton = FormBuilder.addButton(root, ++gridRow, Res.get("shared.exportJSON")); + GridPane.setMargin(exportButton, new Insets(20, -10, -40, 0)); + GridPane.setColumnSpan(exportButton, 2); + GridPane.setHalignment(exportButton, HPos.RIGHT); } @Override @@ -172,6 +210,10 @@ protected void activate() { cyclesTableView.getSelectionModel().selectedItemProperty().addListener(selectedVoteResultListItemListener); fillCycleList(); + exportButton.setOnAction(event -> { + JsonElement cyclesJsonArray = getVotingHistoryJson(); + GUIUtil.exportJSON("voteResultsHistory.json", cyclesJsonArray, (Stage) root.getScene().getWindow()); + }); } @Override @@ -187,6 +229,7 @@ protected void deactivate() { if (selectedProposalSubscription != null) selectedProposalSubscription.unsubscribe(); + exportButton.setOnAction(null); } @@ -208,8 +251,8 @@ private void onResultsListItemSelected(CycleListItem item) { if (selectedProposalSubscription != null) selectedProposalSubscription.unsubscribe(); - GUIUtil.removeChildrenFromGridPaneRows(root, 2, gridRow); - gridRow = 1; + GUIUtil.removeChildrenFromGridPaneRows(root, 3, gridRow); + gridRow = 2; if (item != null) { resultsOfCycle = item.getResultsOfCycle(); @@ -276,8 +319,8 @@ private void maybeShowVoteResultErrors(Cycle cycle) { private void onSelectProposalResultListItem(ProposalListItem item) { selectedProposalListItem = item; - GUIUtil.removeChildrenFromGridPaneRows(root, 4, gridRow); - gridRow = 2; + GUIUtil.removeChildrenFromGridPaneRows(root, 5, gridRow); + gridRow = 3; if (selectedProposalListItem != null) { @@ -333,7 +376,24 @@ private void fillCycleList() { }); Collections.reverse(cycleListItemList); - GUIUtil.setFitToRowsForTableView(cyclesTableView, 24, 28, 2, 4); + maybeShowDaoTestingFeedbackWindow(); + + GUIUtil.setFitToRowsForTableView(cyclesTableView, 25, 28, 2, 4); + } + + private void maybeShowDaoTestingFeedbackWindow() { + String testingPopupKey = "daoTestingFeedbackPopup"; + if (DontShowAgainLookup.showAgain(testingPopupKey)) { + UserThread.runAfter(() -> { + if (myProposalListService.getList().stream().map(Proposal::getTxId) + .anyMatch(txId -> periodService.isTxInCorrectCycle(txId, daoStateService.getChainHeight())) || + myBlindVoteListService.getMyBlindVoteList().stream().map(BlindVote::getTxId) + .anyMatch(txId -> periodService.isTxInCorrectCycle(txId, daoStateService.getChainHeight()))) + new DaoTestingFeedbackWindow() + .dontShowAgainId(testingPopupKey) + .show(); + }, 4, TimeUnit.SECONDS); + } } @@ -409,7 +469,7 @@ private void createProposalsTable() { ballotByProposalTxIdMap.get(evaluatedProposal.getProposalTxId()), bsqFormatter)) .collect(Collectors.toList())); - GUIUtil.setFitToRowsForTableView(proposalsTableView, 33, 28, 2, 4); + GUIUtil.setFitToRowsForTableView(proposalsTableView, 25, 28, 2, 4); } @@ -420,7 +480,7 @@ private void createProposalsTable() { private ProposalDisplay createProposalDisplay(EvaluatedProposal evaluatedProposal, Ballot ballot) { Proposal proposal = evaluatedProposal.getProposal(); ProposalDisplay proposalDisplay = new ProposalDisplay(new GridPane(), bsqFormatter, - daoFacade, null, navigation); + daoFacade, null, navigation, preferences); ScrollPane proposalDisplayView = proposalDisplay.getView(); GridPane.setMargin(proposalDisplayView, new Insets(0, -10, -15, -10)); @@ -480,7 +540,7 @@ private void createVotesTable() { }); voteListItemList.sort(Comparator.comparing(VoteListItem::getBlindVoteTxId)); - GUIUtil.setFitToRowsForTableView(votesTableView, 33, 28, 2, 4); + GUIUtil.setFitToRowsForTableView(votesTableView, 25, 28, 2, 4); } @@ -633,14 +693,14 @@ public TableCell call( public void updateItem(final ProposalListItem item, boolean empty) { super.updateItem(item, empty); if (item != null) - setText(bsqFormatter.formatDateTime(item.getProposal().getCreationDate())); + setText(bsqFormatter.formatDateTime(item.getProposal().getCreationDateAsDate())); else setText(""); } }; } }); - column.setComparator(Comparator.comparing(o3 -> o3.getProposal().getCreationDate())); + column.setComparator(Comparator.comparing(o3 -> o3.getProposal().getCreationDateAsDate())); column.setSortType(TableColumn.SortType.DESCENDING); votesTableView.getColumns().add(column); votesTableView.getSortOrder().add(column); @@ -922,8 +982,142 @@ public void updateItem(final VoteListItem item, boolean empty) { votesTableView.getColumns().add(column); } - private void openTxInBlockExplorer(String txId) { - if (txId != null) - GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + txId); + private JsonElement getVotingHistoryJson() { + JsonArray cyclesArray = new JsonArray(); + + sortedCycleListItemList.sorted(Comparator.comparing(CycleListItem::getCycleStartTime)).forEach(cycleListItem -> { + JsonObject cycleJson = new JsonObject(); + // No domain data, taken from UI model + // TODO move the data structure needed for UI to core and use as pure domain model and use that here + cycleJson.addProperty("cycleIndex", cycleListItem.getCycleIndex()); + cycleJson.addProperty("cycleDateTime", cycleListItem.getCycleDateTime(false)); + cycleJson.addProperty("votesCount", cycleListItem.getNumVotesAsString()); + cycleJson.addProperty("voteWeight", cycleListItem.getMeritAndStake()); + cycleJson.addProperty("issuance", cycleListItem.getIssuance()); + cycleJson.addProperty("startTime", cycleListItem.getCycleStartTime()); + cycleJson.addProperty("totalAcceptedVotes", cycleListItem.getResultsOfCycle().getNumAcceptedVotes()); + cycleJson.addProperty("totalRejectedVotes", cycleListItem.getResultsOfCycle().getNumRejectedVotes()); + + JsonArray proposalsArray = new JsonArray(); + List evaluatedProposals = cycleListItem.getResultsOfCycle().getEvaluatedProposals(); + evaluatedProposals.sort(Comparator.comparingLong(o -> o.getProposal().getCreationDate())); + + evaluatedProposals.forEach(evaluatedProp -> { + JsonObject proposalJson = new JsonObject(); + proposalJson.addProperty("isAccepted", evaluatedProp.isAccepted() ? "Accepted" : "Rejected"); + + // Proposal + Proposal proposal = evaluatedProp.getProposal(); + proposalJson.addProperty("proposal.name", proposal.getName()); + proposalJson.addProperty("proposal.link", proposal.getLink()); + proposalJson.addProperty("proposal.version", proposal.getVersion()); + proposalJson.addProperty("proposal.creationDate", proposal.getCreationDate()); + proposalJson.addProperty("proposal.txId", proposal.getTxId()); + proposalJson.addProperty("proposal.txType", proposal.getTxType().name()); + proposalJson.addProperty("proposal.quorumParam", proposal.getQuorumParam().name()); + proposalJson.addProperty("proposal.thresholdParam", proposal.getThresholdParam().name()); + proposalJson.addProperty("proposal.proposalType", proposal.getType().name()); + + if (proposal.getExtraDataMap() != null) + proposalJson.addProperty("proposal.extraDataMap", proposal.getExtraDataMap().toString()); + + switch (proposal.getType()) { + case UNDEFINED: + break; + case COMPENSATION_REQUEST: + CompensationProposal compensationProposal = (CompensationProposal) proposal; + proposalJson.addProperty("proposal.requestedBsq", compensationProposal.getRequestedBsq().getValue()); + proposalJson.addProperty("proposal.bsqAddress", compensationProposal.getBsqAddress()); + break; + case REIMBURSEMENT_REQUEST: + ReimbursementProposal reimbursementProposal = (ReimbursementProposal) proposal; + proposalJson.addProperty("proposal.requestedBsq", reimbursementProposal.getRequestedBsq().getValue()); + proposalJson.addProperty("proposal.bsqAddress", reimbursementProposal.getBsqAddress()); + break; + case CHANGE_PARAM: + ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; + Param param = changeParamProposal.getParam(); + proposalJson.addProperty("proposal.param", param.name()); + proposalJson.addProperty("proposal.param.defaultValue", param.getDefaultValue()); + proposalJson.addProperty("proposal.param.type", param.getParamType().name()); + proposalJson.addProperty("proposal.param.maxDecrease", param.getMaxDecrease()); + proposalJson.addProperty("proposal.param.maxIncrease", param.getMaxIncrease()); + proposalJson.addProperty("proposal.paramValue", changeParamProposal.getParamValue()); + break; + case BONDED_ROLE: + RoleProposal roleProposal = (RoleProposal) proposal; + Role role = roleProposal.getRole(); + proposalJson.addProperty("proposal.requiredBond", roleProposal.getRequiredBond()); + proposalJson.addProperty("proposal.unlockTime", roleProposal.getUnlockTime()); + proposalJson.addProperty("proposal.role.uid", role.getUid()); + proposalJson.addProperty("proposal.role.name", role.getName()); + proposalJson.addProperty("proposal.role.link", role.getLink()); + BondedRoleType bondedRoleType = role.getBondedRoleType(); + proposalJson.addProperty("proposal.bondedRoleType", bondedRoleType.name()); + // bondedRoleType enum must not change anyway so we don't print it + break; + case CONFISCATE_BOND: + ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; + proposalJson.addProperty("proposal.lockupTxId", confiscateBondProposal.getLockupTxId()); + break; + case GENERIC: + // No extra fields + break; + case REMOVE_ASSET: + RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal; + proposalJson.addProperty("proposal.tickerSymbol", removeAssetProposal.getTickerSymbol()); + break; + } + + ProposalVoteResult proposalVoteResult = evaluatedProp.getProposalVoteResult(); + proposalJson.addProperty("stakeOfAcceptedVotes", proposalVoteResult.getStakeOfAcceptedVotes()); + proposalJson.addProperty("stakeOfRejectedVotes", proposalVoteResult.getStakeOfRejectedVotes()); + proposalJson.addProperty("numAcceptedVotes", proposalVoteResult.getNumAcceptedVotes()); + proposalJson.addProperty("numRejectedVotes", proposalVoteResult.getNumRejectedVotes()); + proposalJson.addProperty("numIgnoredVotes", proposalVoteResult.getNumIgnoredVotes()); + proposalJson.addProperty("numActiveVotes", proposalVoteResult.getNumActiveVotes()); + proposalJson.addProperty("quorum", proposalVoteResult.getQuorum()); + proposalJson.addProperty("threshold", proposalVoteResult.getThreshold()); + + // Not part of pure domain data, but useful to add here + // required quorum and threshold for cycle for proposal type + proposalJson.addProperty("requiredQuorum", proposalService.getRequiredQuorum(proposal).value); + proposalJson.addProperty("requiredThreshold", proposalService.getRequiredThreshold(proposal)); + + // TODO provide better domain object as now we loop inside the loop. Use lookup map instead.... + JsonArray votesArray = new JsonArray(); + evaluatedProposals.stream() + .filter(evaluatedProposal -> evaluatedProposal.getProposal().equals(proposal)) + .forEach(evaluatedProposal -> { + List decryptedVotesForCycle = cycleListItem.getResultsOfCycle().getDecryptedVotesForCycle(); + // Make sure the votes are sorted so we can easier compare json files from different users + decryptedVotesForCycle.sort(Comparator.comparing(DecryptedBallotsWithMerits::getBlindVoteTxId)); + decryptedVotesForCycle.forEach(decryptedBallotsWithMerits -> { + JsonObject voteJson = new JsonObject(); + // Domain data of decryptedBallotsWithMerits + voteJson.addProperty("hashOfBlindVoteList", Utilities.bytesAsHexString(decryptedBallotsWithMerits.getHashOfBlindVoteList())); + voteJson.addProperty("blindVoteTxId", decryptedBallotsWithMerits.getBlindVoteTxId()); + voteJson.addProperty("voteRevealTxId", decryptedBallotsWithMerits.getVoteRevealTxId()); + voteJson.addProperty("stake", decryptedBallotsWithMerits.getStake()); + + voteJson.addProperty("voteWeight", decryptedBallotsWithMerits.getMerit(daoStateService)); + String voteResult = decryptedBallotsWithMerits.getVote(evaluatedProp.getProposalTxId()) + .map(vote -> vote.isAccepted() ? "Accepted" : "Rejected") + .orElse("Ignored"); + voteJson.addProperty("vote", voteResult); + votesArray.add(voteJson); + }); + }); + + proposalJson.addProperty("numberOfVotes", votesArray.size()); + proposalJson.add("votes", votesArray); + + proposalsArray.add(proposalJson); + }); + cycleJson.addProperty("numberOfProposals", proposalsArray.size()); + cycleJson.add("proposals", proposalsArray); + cyclesArray.add(cycleJson); + }); + return cyclesArray; } } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/BsqBalanceUtil.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/BsqBalanceUtil.java index cdb0942b0af..48ad7a3de20 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/BsqBalanceUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/BsqBalanceUtil.java @@ -92,10 +92,12 @@ public int addGroup(GridPane gridPane, int gridRow) { return gridRow; } - public int addBondBalanceGroup(GridPane gridPane, int gridRow) { - addTitledGroupBg(gridPane, ++gridRow, 2, + public int addBondBalanceGroup(GridPane gridPane, int gridRow, String groupStyle) { + TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 2, Res.get("dao.bond.dashboard.bondsHeadline"), Layout.GROUP_DISTANCE); + if (groupStyle != null) titledGroupBg.getStyleClass().add(groupStyle); + lockupAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(gridPane, gridRow, Res.get("dao.bond.dashboard.lockupAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java index f6027deda74..d0e000f5068 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java @@ -20,6 +20,7 @@ import bisq.desktop.common.view.ActivatableView; import bisq.desktop.common.view.FxmlView; import bisq.desktop.components.HyperlinkWithIcon; +import bisq.desktop.components.TitledGroupBg; import bisq.desktop.main.dao.wallet.BsqBalanceUtil; import bisq.desktop.util.FormBuilder; import bisq.desktop.util.Layout; @@ -97,24 +98,24 @@ public void initialize() { int startRow = gridRow; addTitledGroupBg(root, ++gridRow, 5, Res.get("dao.wallet.dashboard.distribution"), Layout.GROUP_DISTANCE); - genesisIssueAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisIssueAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - compRequestIssueAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compRequestIssueAmount")).second; - reimbursementAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.reimbursementAmount")).second; - burntAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second; - availableAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second; + genesisIssueAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisIssueAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; + compRequestIssueAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compRequestIssueAmount")).second; + reimbursementAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.reimbursementAmount")).second; + burntAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second; + availableAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second; gridRow = startRow; addTitledGroupBg(root, ++gridRow, columnIndex, 5, Res.get("dao.wallet.dashboard.locked"), Layout.GROUP_DISTANCE); - totalLockedUpAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalLockedUpAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - totalUnlockingAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockingAmount")).second; - totalUnlockedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second; - totalConfiscatedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalConfiscatedAmount")).second; + totalLockedUpAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalLockedUpAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; + totalUnlockingAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockingAmount")).second; + totalUnlockedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second; + totalConfiscatedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalConfiscatedAmount")).second; gridRow++; startRow = gridRow; addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.wallet.dashboard.market"), Layout.GROUP_DISTANCE); - priceTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.price"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - marketCapTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second; + priceTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.price"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; + marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second; gridRow = startRow; addTitledGroupBg(root, ++gridRow, columnIndex, 2, Res.get("dao.wallet.dashboard.genesis"), Layout.GROUP_DISTANCE); @@ -131,19 +132,21 @@ public void initialize() { hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", genesisTxId))); startRow = gridRow; - addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.dashboard.txDetails"), Layout.GROUP_DISTANCE); - allTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.allTx"), + TitledGroupBg titledGroupBgTxDetails = addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.dashboard.txDetails"), Layout.GROUP_DISTANCE); + titledGroupBgTxDetails.getStyleClass().add("last"); + allTxTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.allTx"), genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - utxoTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second; - compensationIssuanceTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, + utxoTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second; + compensationIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compensationIssuanceTx")).second; gridRow = startRow; - addTitledGroupBg(root, ++gridRow, columnIndex, 3, "", Layout.GROUP_DISTANCE); - reimbursementIssuanceTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, columnIndex, + TitledGroupBg titledGroupBgReImbursement = addTitledGroupBg(root, ++gridRow, columnIndex, 3, "", Layout.GROUP_DISTANCE); + titledGroupBgReImbursement.getStyleClass().add("last"); + reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.reimbursementIssuanceTx"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - burntTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, + burntTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.burntTx")).second; ++gridRow; @@ -223,7 +226,8 @@ private void updatePrice() { priceTextField.setText(bsqFormatter.formatPrice(bsqPrice) + " BSQ/BTC"); marketCapTextField.setText(bsqFormatter.formatMarketCap(priceFeedService.getMarketPrice("BSQ"), - priceFeedService.getMarketPrice("USD"), availableAmount)); + priceFeedService.getMarketPrice(preferences.getPreferredTradeCurrency().getCode()), + availableAmount)); } else { priceTextField.setText(Res.get("shared.na")); marketCapTextField.setText(Res.get("shared.na")); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/receive/BsqReceiveView.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/receive/BsqReceiveView.java index 14d20010590..343ef09e145 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/receive/BsqReceiveView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/receive/BsqReceiveView.java @@ -67,6 +67,7 @@ public void initialize() { TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 1, Res.get("dao.wallet.receive.fundYourWallet"), Layout.GROUP_DISTANCE); + titledGroupBg.getStyleClass().add("last"); GridPane.setColumnSpan(titledGroupBg, 3); Tuple3 tuple = addLabelBsqAddressTextField(root, gridRow, Res.get("dao.wallet.receive.bsqAddress"), diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/send/BsqSendView.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/send/BsqSendView.java index ac137b1742e..088f9bfc770 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/send/BsqSendView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/send/BsqSendView.java @@ -348,7 +348,7 @@ private void showTxPopup(Coin receiverAmount, walletsManager.publishAndCommitBsqTx(txWithBtcFee, new TxBroadcaster.Callback() { @Override public void onSuccess(Transaction transaction) { - log.debug("Successfully sent tx with id " + txWithBtcFee.getHashAsString()); + log.debug("Successfully sent tx with id {}", txWithBtcFee.getHashAsString()); } @Override diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxView.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxView.java index d89f8edd7d9..b73a1df389d 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxView.java @@ -265,7 +265,8 @@ private void updateList() { bsqWalletService, btcWalletService, daoFacade, - transaction.getUpdateTime(), + // Use tx.getIncludedInBestChainAt() when available, otherwise use tx.getUpdateTime() + transaction.getIncludedInBestChainAt() != null ? transaction.getIncludedInBestChainAt() : transaction.getUpdateTime(), bsqFormatter); }) .collect(Collectors.toList()); @@ -616,12 +617,12 @@ public void updateItem(final BsqTxListItem item, boolean empty) { private void openTxInBlockExplorer(BsqTxListItem item) { if (item.getTxId() != null) - GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + item.getTxId()); + GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + item.getTxId(), false); } private void openAddressInBlockExplorer(BsqTxListItem item) { if (item.getAddress() != null) { - GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().addressUrl + item.getAddress()); + GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().addressUrl + item.getAddress(), false); } } } 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 a9ea060d25f..0cf2230120b 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 @@ -286,7 +286,7 @@ private void updateQRCode() { private void openBlockExplorer(DepositListItem item) { if (item.getAddressString() != null) - GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString()); + GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString(), false); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.java b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.java index e142cc02ffb..38275c3ba23 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.java @@ -181,7 +181,7 @@ private void updateList() { } private void openBlockExplorer(LockedListItem item) { - GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString()); + GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString(), false); } private Optional getTradable(LockedListItem item) { diff --git a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.java b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.java index b22bdce8835..0146d9c14fd 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.java @@ -181,7 +181,7 @@ private void updateList() { } private void openBlockExplorer(ReservedListItem item) { - GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString()); + GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString(), false); } private Optional getTradable(ReservedListItem item) { diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java index 46f595fd1c9..4405a8e28d1 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java @@ -221,8 +221,8 @@ else if (!txFeeForBsqPayment) else if (details.isEmpty()) details = Res.get("funds.tx.txFeePaymentForBsqTx"); } - - date = transaction.getUpdateTime(); + // Use tx.getIncludedInBestChainAt() when available, otherwise use tx.getUpdateTime() + date = transaction.getIncludedInBestChainAt() != null ? transaction.getIncludedInBestChainAt() : transaction.getUpdateTime(); dateString = formatter.formatDateTime(date); } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java index ad94f7cea12..21260550d7e 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java @@ -281,12 +281,12 @@ protected void deactivate() { private void openTxInBlockExplorer(TransactionsListItem item) { if (item.getTxId() != null) - GUIUtil.openWebPage(preferences.getBlockChainExplorer().txUrl + item.getTxId()); + GUIUtil.openWebPage(preferences.getBlockChainExplorer().txUrl + item.getTxId(), false); } private void openAddressInBlockExplorer(TransactionsListItem item) { if (item.getAddressString() != null) { - GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString()); + GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString(), false); } } 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 f9cdee1be9a..f3565535d0a 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 @@ -352,7 +352,7 @@ private void onWithdraw() { @Override public void onSuccess(@javax.annotation.Nullable Transaction transaction) { if (transaction != null) { - log.debug("onWithdraw onSuccess tx ID:" + transaction.getHashAsString()); + log.debug("onWithdraw onSuccess tx ID:{}", transaction.getHashAsString()); } else { log.error("onWithdraw transaction is null"); } @@ -436,7 +436,7 @@ private void selectForWithdrawal(WithdrawalListItem item) { private void openBlockExplorer(WithdrawalListItem item) { if (item.getAddressString() != null) - GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString()); + GUIUtil.openWebPage(preferences.getBlockChainExplorer().addressUrl + item.getAddressString(), false); } 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 1838f519690..e54f9ecdc40 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -46,6 +46,7 @@ import bisq.core.user.Preferences; import bisq.core.user.User; import bisq.core.util.BSFormatter; +import bisq.core.util.CoinUtil; import bisq.network.p2p.P2PService; @@ -62,11 +63,14 @@ import com.google.common.collect.Lists; import javafx.beans.property.BooleanProperty; +import javafx.beans.property.DoubleProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.ReadOnlyDoubleProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; @@ -105,8 +109,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs private final BalanceListener btcBalanceListener; private final SetChangeListener paymentAccountsChangeListener; - private final Coin sellerSecurityDeposit; - protected OfferPayload.Direction direction; protected TradeCurrency tradeCurrency; protected final StringProperty tradeCurrencyCode = new SimpleStringProperty(); @@ -119,7 +121,11 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs protected final ObjectProperty minAmount = new SimpleObjectProperty<>(); protected final ObjectProperty price = new SimpleObjectProperty<>(); protected final ObjectProperty volume = new SimpleObjectProperty<>(); - protected final ObjectProperty buyerSecurityDeposit = new SimpleObjectProperty<>(); + protected final ObjectProperty minVolume = new SimpleObjectProperty<>(); + + // Percentage value of buyer security deposit. E.g. 0.01 means 1% of trade amount + protected final DoubleProperty buyerSecurityDeposit = new SimpleDoubleProperty(); + protected final DoubleProperty sellerSecurityDeposit = new SimpleDoubleProperty(); protected final ObservableList paymentAccounts = FXCollections.observableArrayList(); @@ -174,8 +180,8 @@ public MutableOfferDataModel(OpenOfferManager openOfferManager, addressEntry = btcWalletService.getOrCreateAddressEntry(offerId, AddressEntry.Context.OFFER_FUNDING); useMarketBasedPrice.set(preferences.isUsePercentageBasedPrice()); - buyerSecurityDeposit.set(preferences.getBuyerSecurityDepositAsCoin()); - sellerSecurityDeposit = Restrictions.getSellerSecurityDeposit(); + buyerSecurityDeposit.set(preferences.getBuyerSecurityDepositAsPercent()); + sellerSecurityDeposit.set(Restrictions.getSellerSecurityDepositAsPercent()); btcBalanceListener = new BalanceListener(getAddressEntry().getAddress()) { @Override @@ -230,13 +236,13 @@ private void addListeners() { user.getPaymentAccountsAsObservable().addListener(paymentAccountsChangeListener); } - private void removeListeners() { btcWalletService.removeBalanceListener(btcBalanceListener); bsqWalletService.removeBsqBalanceListener(this); user.getPaymentAccountsAsObservable().removeListener(paymentAccountsChangeListener); } + /////////////////////////////////////////////////////////////////////////////////////////// // API /////////////////////////////////////////////////////////////////////////////////////////// @@ -316,8 +322,8 @@ Offer createAndGetOffer() { String counterCurrencyCode = isCryptoCurrency ? Res.getBaseCurrencyCode() : currencyCode; double marketPriceMarginParam = useMarketBasedPriceValue ? marketPriceMargin : 0; - long amount = this.amount.get() != null ? this.amount.get().getValue() : 0L; - long minAmount = this.minAmount.get() != null ? this.minAmount.get().getValue() : 0L; + long amountAsLong = this.amount.get() != null ? this.amount.get().getValue() : 0L; + long minAmountAsLong = this.minAmount.get() != null ? this.minAmount.get().getValue() : 0L; List acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount); List acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount); @@ -335,7 +341,7 @@ Offer createAndGetOffer() { long lowerClosePrice = 0; long upperClosePrice = 0; String hashOfChallenge = null; - Coin buyerSecurityDepositAsCoin = buyerSecurityDeposit.get(); + Coin makerFeeAsCoin = getMakerFee(); Map extraDataMap = OfferUtil.getExtraDataMap(accountAgeWitnessService, @@ -345,7 +351,7 @@ Offer createAndGetOffer() { OfferUtil.validateOfferData(filterManager, p2PService, - buyerSecurityDepositAsCoin, + buyerSecurityDeposit.get(), paymentAccount, currencyCode, makerFeeAsCoin); @@ -358,8 +364,8 @@ Offer createAndGetOffer() { priceAsLong, marketPriceMarginParam, useMarketBasedPriceValue, - amount, - minAmount, + amountAsLong, + minAmountAsLong, baseCurrencyCode, counterCurrencyCode, Lists.newArrayList(user.getAcceptedArbitratorAddresses()), @@ -376,8 +382,8 @@ Offer createAndGetOffer() { txFeeFromFeeService.value, makerFeeAsCoin.value, isCurrencyForMakerFeeBtc(), - buyerSecurityDepositAsCoin.value, - sellerSecurityDeposit.value, + getBuyerSecurityDepositAsCoin().value, + getSellerSecurityDepositAsCoin().value, maxTradeLimit, maxTradePeriod, useAutoClose, @@ -422,6 +428,7 @@ void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) { void onPaymentAccountSelected(PaymentAccount paymentAccount) { if (paymentAccount != null && !this.paymentAccount.equals(paymentAccount)) { volume.set(null); + minVolume.set(null); price.set(null); marketPriceMargin = 0; preferences.setSelectedPaymentAccountForCreateOffer(paymentAccount); @@ -453,6 +460,7 @@ void onCurrencySelected(TradeCurrency tradeCurrency) { if (tradeCurrency != null) { if (!this.tradeCurrency.equals(tradeCurrency)) { volume.set(null); + minVolume.set(null); price.set(null); marketPriceMargin = 0; } @@ -585,15 +593,11 @@ void calculateVolume() { !amount.get().isZero() && !price.get().isZero()) { try { - Volume volumeByAmount = price.get().getVolumeByAmount(amount.get()); - - // For HalCash we want multiple of 10 EUR - if (isHalCashAccount()) - volumeByAmount = OfferUtil.getAdjustedVolumeForHalCash(volumeByAmount); - else if (CurrencyUtil.isFiatCurrency(tradeCurrencyCode.get())) - volumeByAmount = OfferUtil.getRoundedFiatVolume(volumeByAmount); + Volume volumeByAmount = calculateVolumeForAmount(amount); volume.set(volumeByAmount); + + calculateMinVolume(); } catch (Throwable t) { log.error(t.toString()); } @@ -602,6 +606,33 @@ else if (CurrencyUtil.isFiatCurrency(tradeCurrencyCode.get())) updateBalance(); } + void calculateMinVolume() { + if (price.get() != null && + minAmount.get() != null && + !minAmount.get().isZero() && + !price.get().isZero()) { + try { + Volume volumeByAmount = calculateVolumeForAmount(minAmount); + + minVolume.set(volumeByAmount); + + } catch (Throwable t) { + log.error(t.toString()); + } + } + } + + private Volume calculateVolumeForAmount(ObjectProperty minAmount) { + Volume volumeByAmount = price.get().getVolumeByAmount(minAmount.get()); + + // For HalCash we want multiple of 10 EUR + if (isHalCashAccount()) + volumeByAmount = OfferUtil.getAdjustedVolumeForHalCash(volumeByAmount); + else if (CurrencyUtil.isFiatCurrency(tradeCurrencyCode.get())) + volumeByAmount = OfferUtil.getRoundedFiatVolume(volumeByAmount); + return volumeByAmount; + } + void calculateAmount() { if (volume.get() != null && price.get() != null && @@ -641,7 +672,7 @@ void calculateTotalToPay() { } Coin getSecurityDeposit() { - return isBuyOffer() ? buyerSecurityDeposit.get() : sellerSecurityDeposit; + return isBuyOffer() ? getBuyerSecurityDepositAsCoin() : getSellerSecurityDepositAsCoin(); } public boolean isBuyOffer() { @@ -676,9 +707,9 @@ protected void setVolume(Volume volume) { this.volume.set(volume); } - void setBuyerSecurityDeposit(Coin buyerSecurityDeposit) { - this.buyerSecurityDeposit.set(buyerSecurityDeposit); - preferences.setBuyerSecurityDepositAsLong(buyerSecurityDeposit.value); + void setBuyerSecurityDeposit(double value) { + this.buyerSecurityDeposit.set(value); + preferences.setBuyerSecurityDepositAsPercent(value); } protected boolean isUseMarketBasedPriceValue() { @@ -705,6 +736,10 @@ ReadOnlyObjectProperty getVolume() { return volume; } + ReadOnlyObjectProperty getMinVolume() { + return minVolume; + } + protected void setMinAmount(Coin minAmount) { this.minAmount.set(minAmount); } @@ -717,12 +752,34 @@ ReadOnlyBooleanProperty getUseMarketBasedPrice() { return useMarketBasedPrice; } - ReadOnlyObjectProperty getBuyerSecurityDeposit() { + ReadOnlyDoubleProperty getBuyerSecurityDeposit() { return buyerSecurityDeposit; } - Coin getSellerSecurityDeposit() { - return sellerSecurityDeposit; + protected Coin getBuyerSecurityDepositAsCoin() { + Coin percentOfAmountAsCoin = CoinUtil.getPercentOfAmountAsCoin(buyerSecurityDeposit.get(), amount.get()); + return getBoundedBuyerSecurityDepositAsCoin(percentOfAmountAsCoin); + } + + Coin getSellerSecurityDepositAsCoin() { + Coin amountAsCoin = this.amount.get(); + if (amountAsCoin == null) + amountAsCoin = Coin.ZERO; + + Coin percentOfAmountAsCoin = CoinUtil.getPercentOfAmountAsCoin(sellerSecurityDeposit.get(), amountAsCoin); + return getBoundedSellerSecurityDepositAsCoin(percentOfAmountAsCoin); + } + + private Coin getBoundedBuyerSecurityDepositAsCoin(Coin value) { + // We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the + // MinBuyerSecurityDepositAsCoin from Restrictions. + return Coin.valueOf(Math.max(Restrictions.getMinBuyerSecurityDepositAsCoin().value, value.value)); + } + + private Coin getBoundedSellerSecurityDepositAsCoin(Coin value) { + // We need to ensure that for small amount values we don't get a too low BTC amount. We limit it with using the + // MinSellerSecurityDepositAsCoin from Restrictions. + return Coin.valueOf(Math.max(Restrictions.getMinSellerSecurityDepositAsCoin().value, value.value)); } ReadOnlyObjectProperty totalToPayAsCoinProperty() { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java index e3791252e72..06fa032ad29 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferView.java @@ -28,6 +28,7 @@ import bisq.desktop.components.FundsTextField; import bisq.desktop.components.InfoInputTextField; import bisq.desktop.components.InputTextField; +import bisq.desktop.components.NewBadge; import bisq.desktop.components.TitledGroupBg; import bisq.desktop.main.MainView; import bisq.desktop.main.account.AccountView; @@ -122,6 +123,7 @@ import static javafx.beans.binding.Bindings.createStringBinding; public abstract class MutableOfferView extends ActivatableViewAndModel { + public static final String BUYER_SECURITY_DEPOSIT_NEWS = "buyerSecurityDepositNews0.9.5"; protected final Navigation navigation; private final Preferences preferences; private final Transitions transitions; @@ -136,15 +138,16 @@ public abstract class MutableOfferView extends private BusyAnimation waitingForFundsSpinner; private AutoTooltipButton nextButton, cancelButton1, cancelButton2, placeOfferButton; private Button priceTypeToggleButton; - private InputTextField buyerSecurityDepositInputTextField, fixedPriceTextField, marketBasedPriceTextField; - protected InputTextField amountTextField, minAmountTextField, volumeTextField; + private InputTextField fixedPriceTextField; + private InputTextField marketBasedPriceTextField; + protected InputTextField amountTextField, minAmountTextField, volumeTextField, buyerSecurityDepositInputTextField; private TextField currencyTextField; private AddressTextField addressTextField; private BalanceTextField balanceTextField; private FundsTextField totalToPayTextField; private Label amountDescriptionLabel, priceCurrencyLabel, priceDescriptionLabel, volumeDescriptionLabel, waitingForFundsLabel, marketBasedPriceLabel, percentagePriceDescription, tradeFeeDescriptionLabel, - resultLabel, tradeFeeInBtcLabel, tradeFeeInBsqLabel, xLabel, fakeXLabel; + resultLabel, tradeFeeInBtcLabel, tradeFeeInBsqLabel, xLabel, fakeXLabel, buyerSecurityDepositLabel; protected Label amountBtcLabel, volumeCurrencyLabel, minAmountBtcLabel; private ComboBox paymentAccountsComboBox; private ComboBox currencyComboBox; @@ -161,7 +164,7 @@ public abstract class MutableOfferView extends priceAsPercentageFocusedListener, getShowWalletFundedNotificationListener, tradeFeeInBtcToggleListener, tradeFeeInBsqToggleListener, tradeFeeVisibleListener; private ChangeListener tradeCurrencyCodeListener, errorMessageListener, - marketPriceMarginListener, volumeListener; + marketPriceMarginListener, volumeListener, buyerSecurityDepositInBTCListener; private ChangeListener marketPriceAvailableListener; private EventHandler currencyComboBoxSelectionHandler, paymentAccountsComboBoxSelectionHandler; private OfferView.CloseHandler closeHandler; @@ -169,7 +172,8 @@ public abstract class MutableOfferView extends protected int gridRow = 0; private final List editOfferElements = new ArrayList<>(); private boolean clearXchangeWarningDisplayed, isActivated; - private InfoInputTextField marketBasedPriceInfoInputTextField, volumeInfoInputTextField; + private InfoInputTextField marketBasedPriceInfoInputTextField, volumeInfoInputTextField, + buyerSecurityDepositInfoInputTextField; private AutoTooltipSlideToggleButton tradeFeeInBtcToggle, tradeFeeInBsqToggle; private Text xIcon, fakeXIcon; @@ -246,12 +250,7 @@ protected void doActivate() { balanceTextField.setTargetAmount(model.getDataModel().totalToPayAsCoinProperty().get()); updatePriceToggle(); - if (CurrencyUtil.isFiatCurrency(model.tradeCurrencyCode.get()) && !DevEnv.isDevMode()) { - new Popup<>().headLine(Res.get("popup.roundedFiatValues.headline")) - .information(Res.get("popup.roundedFiatValues.msg", model.tradeCurrencyCode.get())) - .dontShowAgainId("FiatValuesRoundedWarning") - .show(); - } + showFiatRoundingInfoPopup(); boolean currencyForMakerFeeBtc = model.getDataModel().isCurrencyForMakerFeeBtc(); tradeFeeInBtcToggle.setSelected(currencyForMakerFeeBtc); @@ -347,6 +346,15 @@ public void setCloseHandler(OfferView.CloseHandler closeHandler) { // UI actions /////////////////////////////////////////////////////////////////////////////////////////// + private void showFiatRoundingInfoPopup() { + if (CurrencyUtil.isFiatCurrency(model.tradeCurrencyCode.get()) && !DevEnv.isDevMode()) { + new Popup<>().headLine(Res.get("popup.roundedFiatValues.headline")) + .information(Res.get("popup.roundedFiatValues.msg", model.tradeCurrencyCode.get())) + .dontShowAgainId("FiatValuesRoundedWarning") + .show(); + } + } + private void onPlaceOffer() { if (model.isReadyForTxBroadcast()) { if (model.getDataModel().isMakerFeeValid()) { @@ -528,6 +536,8 @@ protected void onPaymentAccountsComboBoxSelected() { model.onPaymentAccountSelected(paymentAccount); model.onCurrencySelected(model.getDataModel().getTradeCurrency()); } + + showFiatRoundingInfoPopup(); } else { currencySelection.setVisible(false); currencySelection.setManaged(false); @@ -574,6 +584,7 @@ private void addBindings() { totalToPayTextField.textProperty().bind(model.totalToPay); addressTextField.amountAsCoinProperty().bind(model.getDataModel().getMissingCoin()); buyerSecurityDepositInputTextField.textProperty().bindBidirectional(model.buyerSecurityDeposit); + buyerSecurityDepositLabel.textProperty().bind(model.buyerSecurityDepositLabel); tradeFeeInBtcLabel.textProperty().bind(model.tradeFeeInBtcWithFiat); tradeFeeInBsqLabel.textProperty().bind(model.tradeFeeInBsqWithFiat); tradeFeeDescriptionLabel.textProperty().bind(model.tradeFeeDescription); @@ -624,6 +635,7 @@ private void removeBindings() { totalToPayTextField.textProperty().unbind(); addressTextField.amountAsCoinProperty().unbind(); buyerSecurityDepositInputTextField.textProperty().unbindBidirectional(model.buyerSecurityDeposit); + buyerSecurityDepositLabel.textProperty().unbind(); tradeFeeInBtcLabel.textProperty().unbind(); tradeFeeInBsqLabel.textProperty().unbind(); tradeFeeDescriptionLabel.textProperty().unbind(); @@ -761,6 +773,15 @@ private void createListeners() { } }; + buyerSecurityDepositInBTCListener = (observable, oldValue, newValue) -> { + if (!newValue.equals("")) { + Label depositInBTCInfo = createPopoverLabel(model.getSecurityDepositPopOverLabel(newValue)); + buyerSecurityDepositInfoInputTextField.setContentForInfoPopOver(depositInBTCInfo); + } else { + buyerSecurityDepositInfoInputTextField.setContentForInfoPopOver(null); + } + }; + volumeListener = (observable, oldValue, newValue) -> { if (!newValue.equals("") && CurrencyUtil.isFiatCurrency(model.tradeCurrencyCode.get())) { volumeInfoInputTextField.setContentForPrivacyPopOver(createPopoverLabel(Res.get("offerbook.info.roundedFiatVolume"))); @@ -860,6 +881,7 @@ private void addListeners() { model.marketPriceMargin.addListener(marketPriceMarginListener); model.volume.addListener(volumeListener); model.isTradeFeeVisible.addListener(tradeFeeVisibleListener); + model.buyerSecurityDepositInBTC.addListener(buyerSecurityDepositInBTCListener); tradeFeeInBtcToggle.selectedProperty().addListener(tradeFeeInBtcToggleListener); tradeFeeInBsqToggle.selectedProperty().addListener(tradeFeeInBsqToggleListener); @@ -892,6 +914,7 @@ private void removeListeners() { model.marketPriceMargin.removeListener(marketPriceMarginListener); model.volume.removeListener(volumeListener); model.isTradeFeeVisible.removeListener(tradeFeeVisibleListener); + model.buyerSecurityDepositInBTC.removeListener(buyerSecurityDepositInBTCListener); tradeFeeInBtcToggle.selectedProperty().removeListener(tradeFeeInBtcToggleListener); tradeFeeInBsqToggle.selectedProperty().removeListener(tradeFeeInBsqToggleListener); @@ -1029,7 +1052,12 @@ private void addOptionsGroup() { GridPane.setMargin(advancedOptionsBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0)); gridPane.getChildren().add(advancedOptionsBox); - advancedOptionsBox.getChildren().addAll(getBuyerSecurityDepositBox(), getTradeFeeFieldsBox()); + // add new badge for this new feature for this release + // TODO: remove it with 0.9.6+ + NewBadge securityDepositBoxWithNewBadge = new NewBadge(getBuyerSecurityDepositBox(), + BUYER_SECURITY_DEPOSIT_NEWS, preferences); + + advancedOptionsBox.getChildren().addAll(securityDepositBoxWithNewBadge, getTradeFeeFieldsBox()); Tuple2 tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, @@ -1099,16 +1127,21 @@ private void showFeeOption() { } private VBox getBuyerSecurityDepositBox() { - Tuple3 tuple = getEditableValueBox( + Tuple3 tuple = getEditableValueBoxWithInfo( Res.get("createOffer.securityDeposit.prompt")); - buyerSecurityDepositInputTextField = tuple.second; - Label buyerSecurityDepositBtcLabel = tuple.third; - - VBox depositBox = getTradeInputBox(tuple.first, Res.get("createOffer.setDeposit")).second; + buyerSecurityDepositInfoInputTextField = tuple.second; + buyerSecurityDepositInputTextField = buyerSecurityDepositInfoInputTextField.getInputTextField(); + Label buyerSecurityDepositPercentageLabel = tuple.third; + // getEditableValueBox delivers BTC, so we overwrite it with % + buyerSecurityDepositPercentageLabel.setText("%"); + + Tuple2 tradeInputBoxTuple = getTradeInputBox(tuple.first, model.getSecurityDepositLabel()); + VBox depositBox = tradeInputBoxTuple.second; + buyerSecurityDepositLabel = tradeInputBoxTuple.first; depositBox.setMaxWidth(310); editOfferElements.add(buyerSecurityDepositInputTextField); - editOfferElements.add(buyerSecurityDepositBtcLabel); + editOfferElements.add(buyerSecurityDepositPercentageLabel); return depositBox; } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java index 075350fa406..67ac5fea469 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java @@ -105,7 +105,8 @@ public abstract class MutableOfferViewModel ext public final StringProperty amount = new SimpleStringProperty(); public final StringProperty minAmount = new SimpleStringProperty(); final StringProperty buyerSecurityDeposit = new SimpleStringProperty(); - final String sellerSecurityDeposit; + final StringProperty buyerSecurityDepositInBTC = new SimpleStringProperty(); + final StringProperty buyerSecurityDepositLabel = new SimpleStringProperty(); // Price in the viewModel is always dependent on fiat/altcoin: Fiat Fiat/BTC, for altcoins we use inverted price. // The domain (dataModel) uses always the same price model (otherCurrencyBTC) @@ -158,7 +159,7 @@ public abstract class MutableOfferViewModel ext private ChangeListener minAmountAsCoinListener; private ChangeListener priceListener; private ChangeListener volumeListener; - private ChangeListener securityDepositAsCoinListener; + private ChangeListener securityDepositAsDoubleListener; private ChangeListener isWalletFundedListener; //private ChangeListener feeFromFundingTxListener; @@ -209,7 +210,6 @@ public MutableOfferViewModel(M dataModel, this.bsqFormatter = bsqFormatter; paymentLabel = Res.get("createOffer.fundsBox.paymentLabel", dataModel.shortOfferId); - sellerSecurityDeposit = btcFormatter.formatCoin(dataModel.getSellerSecurityDeposit()); if (dataModel.getAddressEntry() != null) { addressAsString = dataModel.getAddressEntry().getAddressString(); @@ -223,11 +223,10 @@ public void activate() { if (DevEnv.isDevMode()) { UserThread.runAfter(() -> { amount.set("1"); - price.set("0.0002"); + price.set("0.03"); minAmount.set(amount.get()); onFocusOutPriceAsPercentageTextField(true, false); applyMakerFee(); - updateButtonDisableState(); setAmountToModel(); setMinAmountToModel(); setPriceToModel(); @@ -422,10 +421,13 @@ private void createListeners() { amountAsCoinListener = (ov, oldValue, newValue) -> { - if (newValue != null) + if (newValue != null) { amount.set(btcFormatter.formatCoin(newValue)); - else + buyerSecurityDepositInBTC.set(btcFormatter.formatCoinWithCode(dataModel.getBuyerSecurityDepositAsCoin())); + } else { amount.set(""); + buyerSecurityDepositInBTC.set(""); + } applyMakerFee(); }; @@ -456,11 +458,15 @@ private void createListeners() { applyMakerFee(); }; - securityDepositAsCoinListener = (ov, oldValue, newValue) -> { - if (newValue != null) - buyerSecurityDeposit.set(btcFormatter.formatCoin(newValue)); - else + securityDepositAsDoubleListener = (ov, oldValue, newValue) -> { + if (newValue != null) { + buyerSecurityDeposit.set(btcFormatter.formatToPercent((double) newValue)); + if (dataModel.getAmount().get() != null) + buyerSecurityDepositInBTC.set(btcFormatter.formatCoinWithCode(dataModel.getBuyerSecurityDepositAsCoin())); + } else { buyerSecurityDeposit.set(""); + buyerSecurityDepositInBTC.set(""); + } }; @@ -543,7 +549,7 @@ private void addListeners() { dataModel.getMinAmount().addListener(minAmountAsCoinListener); dataModel.getPrice().addListener(priceListener); dataModel.getVolume().addListener(volumeListener); - dataModel.getBuyerSecurityDeposit().addListener(securityDepositAsCoinListener); + dataModel.getBuyerSecurityDeposit().addListener(securityDepositAsDoubleListener); // dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener); dataModel.getIsBtcWalletFunded().addListener(isWalletFundedListener); @@ -565,7 +571,7 @@ private void removeListeners() { dataModel.getMinAmount().removeListener(minAmountAsCoinListener); dataModel.getPrice().removeListener(priceListener); dataModel.getVolume().removeListener(volumeListener); - dataModel.getBuyerSecurityDeposit().removeListener(securityDepositAsCoinListener); + dataModel.getBuyerSecurityDeposit().removeListener(securityDepositAsDoubleListener); //dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener); dataModel.getIsBtcWalletFunded().removeListener(isWalletFundedListener); @@ -593,7 +599,8 @@ boolean initWithData(OfferPayload.Direction direction, TradeCurrency tradeCurren amountDescription = Res.get("createOffer.amountPriceBox.amountDescription", isBuy ? Res.get("shared.buy") : Res.get("shared.sell")); - buyerSecurityDeposit.set(btcFormatter.formatCoin(dataModel.getBuyerSecurityDeposit().get())); + buyerSecurityDeposit.set(btcFormatter.formatToPercent(dataModel.getBuyerSecurityDeposit().get())); + buyerSecurityDepositLabel.set(getSecurityDepositLabel()); applyMakerFee(); return result; @@ -735,6 +742,18 @@ public void onFocusOutMinAmountTextField(boolean oldValue, boolean newValue) { Coin minAmountAsCoin = dataModel.getMinAmount().get(); syncMinAmountWithAmount = minAmountAsCoin != null && minAmountAsCoin.equals(dataModel.getAmount().get()); setMinAmountToModel(); + + dataModel.calculateMinVolume(); + + if (dataModel.getMinVolume().get() != null) { + InputValidator.ValidationResult minVolumeResult = isVolumeInputValid( + btcFormatter.formatVolume(dataModel.getMinVolume().get())); + + volumeValidationResult.set(minVolumeResult); + + updateButtonDisableState(); + } + this.minAmount.set(btcFormatter.formatCoin(minAmountAsCoin)); if (!dataModel.isMinAmountLessOrEqualAmount()) { @@ -834,22 +853,22 @@ void onFocusOutBuyerSecurityDepositTextField(boolean oldValue, boolean newValue) InputValidator.ValidationResult result = securityDepositValidator.validate(buyerSecurityDeposit.get()); buyerSecurityDepositValidationResult.set(result); if (result.isValid) { - Coin defaultSecurityDeposit = Restrictions.getDefaultBuyerSecurityDeposit(); - String key = "buyerSecurityDepositLowerAsDefault"; - if (preferences.showAgain(key) && - btcFormatter.parseToCoin(buyerSecurityDeposit.get()).compareTo(defaultSecurityDeposit) < 0) { - final String postfix = dataModel.isBuyOffer() ? + double defaultSecurityDeposit = Restrictions.getDefaultBuyerSecurityDepositAsPercent(); + String key = "buyerSecurityDepositIsLowerAsDefault"; + double depositAsDouble = btcFormatter.parsePercentStringToDouble(buyerSecurityDeposit.get()); + if (preferences.showAgain(key) && depositAsDouble < defaultSecurityDeposit) { + String postfix = dataModel.isBuyOffer() ? Res.get("createOffer.tooLowSecDeposit.makerIsBuyer") : Res.get("createOffer.tooLowSecDeposit.makerIsSeller"); new Popup<>() .warning(Res.get("createOffer.tooLowSecDeposit.warning", - btcFormatter.formatCoinWithCode(defaultSecurityDeposit)) + "\n\n" + postfix) + btcFormatter.formatToPercentWithSymbol(defaultSecurityDeposit)) + "\n\n" + postfix) .width(800) .actionButtonText(Res.get("createOffer.resetToDefault")) .onAction(() -> { dataModel.setBuyerSecurityDeposit(defaultSecurityDeposit); ignoreSecurityDepositStringListener = true; - buyerSecurityDeposit.set(btcFormatter.formatCoin(dataModel.getBuyerSecurityDeposit().get())); + buyerSecurityDeposit.set(btcFormatter.formatToPercent(dataModel.getBuyerSecurityDeposit().get())); ignoreSecurityDepositStringListener = false; }) .closeButtonText(Res.get("createOffer.useLowerValue")) @@ -866,7 +885,7 @@ void onFocusOutBuyerSecurityDepositTextField(boolean oldValue, boolean newValue) private void applyBuyerSecurityDepositOnFocusOut() { setBuyerSecurityDepositToModel(); ignoreSecurityDepositStringListener = true; - buyerSecurityDeposit.set(btcFormatter.formatCoin(dataModel.getBuyerSecurityDeposit().get())); + buyerSecurityDeposit.set(btcFormatter.formatToPercent(dataModel.getBuyerSecurityDeposit().get())); ignoreSecurityDepositStringListener = false; } @@ -914,6 +933,15 @@ public String getTradeAmount() { return btcFormatter.formatCoinWithCode(dataModel.getAmount().get()); } + public String getSecurityDepositLabel() { + return dataModel.isBuyOffer() ? Res.get("createOffer.setDepositAsBuyer") : Res.get("createOffer.setDeposit"); + } + + public String getSecurityDepositPopOverLabel(String depositInBTC) { + return dataModel.isBuyOffer() ? Res.get("createOffer.securityDepositInfoAsBuyer", depositInBTC) : + Res.get("createOffer.securityDepositInfo", depositInBTC); + } + public String getSecurityDepositInfo() { return btcFormatter.formatCoinWithCode(dataModel.getSecurityDeposit()) + GUIUtil.getPercentageOfTradeAmount(dataModel.getSecurityDeposit(), dataModel.getAmount().get(), btcFormatter); @@ -1091,13 +1119,12 @@ private void setVolumeToModel() { private void setBuyerSecurityDepositToModel() { if (buyerSecurityDeposit.get() != null && !buyerSecurityDeposit.get().isEmpty()) { - dataModel.setBuyerSecurityDeposit(btcFormatter.parseToCoinWith4Decimals(buyerSecurityDeposit.get())); + dataModel.setBuyerSecurityDeposit(btcFormatter.parsePercentStringToDouble(buyerSecurityDeposit.get())); } else { - dataModel.setBuyerSecurityDeposit(null); + dataModel.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent()); } } - private InputValidator.ValidationResult isBtcInputValid(String input) { return btcValidator.validate(input); } @@ -1151,6 +1178,7 @@ private void updateButtonDisableState() { dataModel.getPrice().get() != null && dataModel.getPrice().get().getValue() != 0 && isVolumeInputValid(volume.get()).isValid && + isVolumeInputValid(btcFormatter.formatVolume(dataModel.getMinVolume().get())).isValid && dataModel.isMinAmountLessOrEqualAmount(); isNextButtonDisabled.set(!inputDataValid); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java index 84313552c81..d2fa0955da6 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java @@ -51,6 +51,8 @@ import java.util.Optional; import java.util.stream.Collectors; +import static bisq.desktop.main.offer.MutableOfferView.BUYER_SECURITY_DEPOSIT_NEWS; + public abstract class OfferView extends ActivatableView { private OfferBookView offerBookView; @@ -271,6 +273,8 @@ private void onCreateOfferViewRemoved() { offerBookView.enableCreateOfferButton(); navigation.navigateTo(MainView.class, this.getClass(), OfferBookView.class); + + preferences.dontShowAgain(BUYER_SECURITY_DEPOSIT_NEWS, true); } private void onTakeOfferViewRemoved() { diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBook.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBook.java index d9754193596..53556eb9481 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBook.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBook.java @@ -113,7 +113,7 @@ public void fillOfferBookListItems() { .map(OfferBookListItem::new) .collect(Collectors.toList())); - log.debug("offerBookListItems.size " + offerBookListItems.size()); + log.debug("offerBookListItems.size {}", offerBookListItems.size()); fillOfferCountMaps(); } catch (Throwable t) { t.printStackTrace(); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index 4b47cedf1c7..4ff8bce652f 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -408,11 +408,12 @@ private void onCreateOffer() { createOfferButton.setDisable(true); offerActionHandler.onCreateOffer(model.getSelectedTradeCurrency()); }) - .closeButtonText(Res.get("offerbook.setupNewAccount")) - .onClose(() -> { + .secondaryActionButtonText(Res.get("offerbook.setupNewAccount")) + .onSecondaryAction(() -> { navigation.setReturnPath(navigation.getCurrentPath()); navigation.navigateTo(MainView.class, AccountView.class, FiatAccountsView.class); }) + .width(725) .show(); } else if (!model.hasAcceptedArbitrators()) { new Popup<>().warning(Res.get("popup.warning.noArbitratorsAvailable")).show(); 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 f6dd5544374..32d497f9a29 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 @@ -488,7 +488,7 @@ void calculateTotalToPay() { totalToPayAsCoin.set(feeAndSecDeposit); updateBalance(); - log.debug("totalToPayAsCoin " + totalToPayAsCoin.get().toFriendlyString()); + log.debug("totalToPayAsCoin {}", totalToPayAsCoin.get().toFriendlyString()); } } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java index 0d0def08011..ae96ae4e39a 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferViewModel.java @@ -380,7 +380,6 @@ void onFocusOutAmountTextField(boolean oldValue, boolean newValue, String userIn /////////////////////////////////////////////////////////////////////////////////////////// private void applyOfferState(Offer.State state) { - log.debug("applyOfferState state = " + state); offerWarning.set(null); // We have 2 situations handled here: @@ -459,8 +458,6 @@ private void applyTradeErrorMessage(@Nullable String errorMessage) { } private void applyTradeState(Trade.State tradeState) { - log.debug("applyTradeState state = " + tradeState); - if (trade.isDepositPublished()) { if (trade.getDepositTx() != null) { if (takeOfferSucceededHandler != null) diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/Overlay.java b/desktop/src/main/java/bisq/desktop/main/overlays/Overlay.java index e8b175c9bfd..3aab67996e9 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/Overlay.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/Overlay.java @@ -172,7 +172,7 @@ protected enum Type { public Overlay() { } - public void show() { + public void show(boolean showAgainChecked) { if (dontShowAgainId == null || DontShowAgainLookup.showAgain(dontShowAgainId)) { createGridPane(); addHeadLine(); @@ -185,12 +185,16 @@ public void show() { addReportErrorButtons(); addButtons(); - addDontShowAgainCheckBox(); + addDontShowAgainCheckBox(showAgainChecked); applyStyles(); onShow(); } } + public void show() { + this.show(false); + } + protected void onShow() { } @@ -374,7 +378,7 @@ public T closeButtonText(String closeButtonText) { public T useReportBugButton() { this.closeButtonText = Res.get("shared.reportBug"); - this.closeHandlerOptional = Optional.of(() -> GUIUtil.openWebPage("https://github.com/bisq-network/bisq-desktop/issues")); + this.closeHandlerOptional = Optional.of(() -> GUIUtil.openWebPage("https://bisq.network/source/bisq/issues")); //noinspection unchecked return (T) this; } @@ -826,7 +830,7 @@ private void addReportErrorButtons() { gitHubButton.setOnAction(event -> { if (message != null) Utilities.copyToClipboard(message); - GUIUtil.openWebPage("https://github.com/bisq-network/bisq-desktop/issues"); + GUIUtil.openWebPage("https://bisq.network/source/bisq/issues"); hide(); }); } @@ -839,19 +843,25 @@ protected void addBusyAnimation() { gridPane.getChildren().add(busyAnimation); } - protected void addDontShowAgainCheckBox() { + protected void addDontShowAgainCheckBox(boolean isChecked) { if (dontShowAgainId != null) { // We might have set it and overridden the default, so we check if it is not set if (dontShowAgainText == null) dontShowAgainText = Res.get("popup.doNotShowAgain"); CheckBox dontShowAgainCheckBox = addCheckBox(gridPane, rowIndex, dontShowAgainText, buttonDistance - 1); + dontShowAgainCheckBox.setSelected(isChecked); + DontShowAgainLookup.dontShowAgain(dontShowAgainId, isChecked); GridPane.setColumnIndex(dontShowAgainCheckBox, 0); GridPane.setHalignment(dontShowAgainCheckBox, HPos.LEFT); dontShowAgainCheckBox.setOnAction(e -> DontShowAgainLookup.dontShowAgain(dontShowAgainId, dontShowAgainCheckBox.isSelected())); } } + protected void addDontShowAgainCheckBox() { + this.addDontShowAgainCheckBox(false); + } + protected void addButtons() { if (!hideCloseButton) { closeButton = new AutoTooltipButton(closeButtonText == null ? Res.get("shared.close") : closeButtonText); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/notifications/NotificationCenter.java b/desktop/src/main/java/bisq/desktop/main/overlays/notifications/NotificationCenter.java index 445dfb2aaa8..ed732eb7180 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/notifications/NotificationCenter.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/notifications/NotificationCenter.java @@ -35,7 +35,6 @@ import bisq.core.user.Preferences; import bisq.common.UserThread; -import bisq.common.app.Log; import com.google.inject.Inject; @@ -217,7 +216,6 @@ else if (trade instanceof SellerTrade && phase.ordinal() == Trade.Phase.FIAT_SEN } private void onDisputeStateChanged(Trade trade, Trade.DisputeState disputeState) { - Log.traceCall(disputeState.toString()); String message = null; if (disputeManager.findOwnDispute(trade.getId()).isPresent()) { String disputeOrTicket = disputeManager.findOwnDispute(trade.getId()).get().isSupportTicket() ? diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/DaoTestingFeedbackWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/DaoTestingFeedbackWindow.java new file mode 100644 index 00000000000..c42e9a7f566 --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/DaoTestingFeedbackWindow.java @@ -0,0 +1,89 @@ +/* + * 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.desktop.main.overlays.windows; + +import bisq.desktop.components.AutoTooltipLabel; +import bisq.desktop.components.HyperlinkWithIcon; +import bisq.desktop.main.overlays.Overlay; + +import bisq.core.locale.Res; + +import com.google.inject.Inject; + +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; + +import javafx.geometry.HPos; +import javafx.geometry.Insets; + +import lombok.extern.slf4j.Slf4j; + +import static bisq.desktop.util.FormBuilder.addHyperlinkWithIcon; + +@Slf4j +public class DaoTestingFeedbackWindow extends Overlay { + @Inject + public DaoTestingFeedbackWindow() { + type = Type.Confirmation; + } + + @Override + public void show() { + headLine(Res.get("daoTestingFeedbackWindow.title")); + message(Res.get("daoTestingFeedbackWindow.msg.part1")); + super.show(true); + } + + @Override + protected void addMessage() { + super.addMessage(); + + HyperlinkWithIcon survey = addHyperlinkWithIcon(gridPane, ++rowIndex, Res.get("daoTestingFeedbackWindow.surveyLinkLabel"), + "https://docs.google.com/forms/d/e/1FAIpQLSdS4YRE9Eox3bvuo4oSJJQCm5Yy54ZclKC_ThUt702PeU4rxw/viewform"); + GridPane.setMargin(survey, new Insets(-6, 0, 10, 0)); + + AutoTooltipLabel messageLabel2 = new AutoTooltipLabel(Res.get("daoTestingFeedbackWindow.msg.part2")); + messageLabel2.setMouseTransparent(true); + messageLabel2.setWrapText(true); + GridPane.setHalignment(messageLabel2, HPos.LEFT); + GridPane.setHgrow(messageLabel2, Priority.ALWAYS); + GridPane.setRowIndex(messageLabel2, ++rowIndex); + GridPane.setColumnIndex(messageLabel2, 0); + GridPane.setColumnSpan(messageLabel2, 2); + gridPane.getChildren().add(messageLabel2); + + HyperlinkWithIcon forum = addHyperlinkWithIcon(gridPane, ++rowIndex, Res.get("daoTestingFeedbackWindow.forumLinkLabel"), + "https://bisq.community", 40); + GridPane.setMargin(forum, new Insets(-6, 0, 10, 0)); + + AutoTooltipLabel messageLabel3 = new AutoTooltipLabel(Res.get("daoTestingFeedbackWindow.msg.part3")); + messageLabel3.setMouseTransparent(true); + messageLabel3.setWrapText(true); + GridPane.setHalignment(messageLabel3, HPos.LEFT); + GridPane.setHgrow(messageLabel3, Priority.ALWAYS); + GridPane.setRowIndex(messageLabel3, ++rowIndex); + GridPane.setColumnIndex(messageLabel3, 0); + GridPane.setColumnSpan(messageLabel3, 2); + gridPane.getChildren().add(messageLabel3); + } + + @Override + protected void onShow() { + display(); + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java index 54756d3a331..d17dba56b0e 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/DisputeSummaryWindow.java @@ -394,21 +394,24 @@ private void applyCustomAmounts(InputTextField inputTextField) { private void addPayoutAmountTextFields() { buyerPayoutAmountInputTextField = new InputTextField(); + buyerPayoutAmountInputTextField.setLabelFloat(true); buyerPayoutAmountInputTextField.setEditable(false); buyerPayoutAmountInputTextField.setPromptText(Res.get("disputeSummaryWindow.payoutAmount.buyer")); sellerPayoutAmountInputTextField = new InputTextField(); + sellerPayoutAmountInputTextField.setLabelFloat(true); sellerPayoutAmountInputTextField.setPromptText(Res.get("disputeSummaryWindow.payoutAmount.seller")); sellerPayoutAmountInputTextField.setEditable(false); isLoserPublisherCheckBox = new AutoTooltipCheckBox(Res.get("disputeSummaryWindow.payoutAmount.invert")); VBox vBox = new VBox(); - vBox.setSpacing(10); + vBox.setSpacing(15); vBox.getChildren().addAll(buyerPayoutAmountInputTextField, sellerPayoutAmountInputTextField, isLoserPublisherCheckBox); - - VBox vBox2 = addTopLabelWithVBox(gridPane, rowIndex, Res.get("disputeSummaryWindow.payout"), vBox, 10).second; - GridPane.setColumnIndex(vBox2, 1); + GridPane.setMargin(vBox, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0)); + GridPane.setRowIndex(vBox, rowIndex); + GridPane.setColumnIndex(vBox, 1); + gridPane.getChildren().add(vBox); } private void addReasonControls() { diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/EmptyWalletWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/EmptyWalletWindow.java index f67e0e8f55b..efd56289938 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/EmptyWalletWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/EmptyWalletWindow.java @@ -238,7 +238,7 @@ private void doEmptyWallet2(KeyParameter aesKey) { }, (errorMessage) -> { emptyWalletButton.setDisable(false); - log.debug("wallet empty failed " + errorMessage); + log.error("wallet empty failed {}", errorMessage); }); } catch (InsufficientMoneyException | AddressFormatException e1) { e1.printStackTrace(); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectBaseCurrencyWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectBaseCurrencyWindow.java deleted file mode 100644 index 3abbc113701..00000000000 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectBaseCurrencyWindow.java +++ /dev/null @@ -1,119 +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.desktop.main.overlays.windows; - -import bisq.desktop.main.overlays.Overlay; -import bisq.desktop.util.FormBuilder; - -import bisq.core.app.BisqEnvironment; -import bisq.core.btc.BaseCurrencyNetwork; -import bisq.core.locale.Res; - -import bisq.common.app.DevEnv; - -import javax.inject.Inject; - -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.layout.GridPane; - -import javafx.geometry.Insets; - -import javafx.collections.FXCollections; - -import javafx.util.StringConverter; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import static bisq.desktop.util.FormBuilder.addMultilineLabel; - -public class SelectBaseCurrencyWindow extends Overlay { - - private ComboBox comboBox; - private Optional> selectHandlerOptional; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Public API - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public SelectBaseCurrencyWindow() { - type = Type.Confirmation; - } - - public void show() { - if (headLine == null) - headLine = Res.get("selectBaseCurrencyWindow.headline"); - - width = 768; - createGridPane(); - addHeadLine(); - addContent(); - addButtons(); - applyStyles(); - display(); - } - - public SelectBaseCurrencyWindow onSelect(Consumer selectHandler) { - this.selectHandlerOptional = Optional.of(selectHandler); - return this; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - private void addContent() { - Label label = addMultilineLabel(gridPane, ++rowIndex, Res.get("selectBaseCurrencyWindow.msg", BisqEnvironment.getBaseCurrencyNetwork().getCurrencyName()), 10); - GridPane.setMargin(label, new Insets(0, 0, 10, 0)); - - comboBox = FormBuilder.addComboBox(gridPane, ++rowIndex, Res.get("selectBaseCurrencyWindow.select")); - comboBox.setPromptText(Res.get("shared.select")); - List baseCurrencyNetworks = Arrays.asList(BaseCurrencyNetwork.values()); - // show ony mainnet in production version - if (!DevEnv.isDevMode()) - baseCurrencyNetworks = baseCurrencyNetworks.stream() - .filter(BaseCurrencyNetwork::isMainnet) - .collect(Collectors.toList()); - comboBox.setItems(FXCollections.observableArrayList(baseCurrencyNetworks)); - - comboBox.setConverter(new StringConverter() { - @Override - public String toString(BaseCurrencyNetwork baseCurrencyNetwork) { - return DevEnv.isDevMode() ? (baseCurrencyNetwork.getCurrencyName() + "_" + baseCurrencyNetwork.getNetwork()) : - baseCurrencyNetwork.getCurrencyName(); - } - - @Override - public BaseCurrencyNetwork fromString(String string) { - return null; - } - }); - - comboBox.setOnAction(event -> { - selectHandlerOptional.get().accept(comboBox.getSelectionModel().getSelectedItem()); - hide(); - }); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/downloadupdate/BisqInstaller.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/downloadupdate/BisqInstaller.java index 62d03e7f9bc..b23c1c9d93d 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/downloadupdate/BisqInstaller.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/downloadupdate/BisqInstaller.java @@ -60,10 +60,10 @@ public class BisqInstaller { private static final String FINGER_PRINT_CHRIS_BEAMS = "5BC5ED73"; private static final String FINGER_PRINT_CHRISTOPH_ATTENEDER = "29CDFD3B"; private static final String PUB_KEY_HOSTING_URL = "https://bisq.network/pubkey/"; - private static final String DOWNLOAD_HOST_URL = "https://github.com/bisq-network/exchange/releases/download/"; + private static final String DOWNLOAD_HOST_URL = "https://bisq.network/downloads/"; public boolean isSupportedOS() { - return Utilities.isOSX() || Utilities.isWindows() || Utilities.isLinux(); + return Utilities.isOSX() || Utilities.isWindows() || Utilities.isDebianLinux() || Utilities.isRedHatLinux(); } public Optional download(String version) { @@ -211,10 +211,12 @@ private FileDescriptor getInstallerDescriptor(String version, String partialUrl) fileName = prefix + version + ".dmg"; else if (Utilities.isWindows()) fileName = prefix + Utilities.getOSArchitecture() + "bit-" + version + ".exe"; - else if (Utilities.isLinux()) + else if (Utilities.isDebianLinux()) fileName = prefix + Utilities.getOSArchitecture() + "bit-" + version + ".deb"; + else if (Utilities.isRedHatLinux()) + fileName = prefix + Utilities.getOSArchitecture() + "bit-" + version + ".rpm"; else - throw new RuntimeException("No suitable OS found, use osCheck before calling this method."); + throw new RuntimeException("No suitable install package available for your OS."); return FileDescriptor.builder() .type(DownloadType.INSTALLER) @@ -306,7 +308,7 @@ public List getSigFileDescriptors(FileDescriptor installerFileDe .loadUrl(installerFileDescriptor.getLoadUrl().concat(suffix)) .build()); } else { - log.debug("We have already a file with the key: " + key.getId()); + log.debug("We have already a file with the key: {}", key.getId()); } } return result; diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java index 6c34457146f..a723a368c7e 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferDataModel.java @@ -39,6 +39,7 @@ import bisq.core.user.Preferences; import bisq.core.user.User; import bisq.core.util.BSFormatter; +import bisq.core.util.CoinUtil; import bisq.network.p2p.P2PService; @@ -98,7 +99,8 @@ public void reset() { minAmount.set(null); price.set(null); volume.set(null); - buyerSecurityDeposit.set(null); + minVolume.set(null); + buyerSecurityDeposit.set(0); paymentAccounts.clear(); paymentAccount = null; marketPriceMargin = 0; @@ -112,7 +114,7 @@ public void applyOpenOffer(OpenOffer openOffer) { CurrencyUtil.getTradeCurrency(offer.getCurrencyCode()) .ifPresent(c -> this.tradeCurrency = c); tradeCurrencyCode.set(offer.getCurrencyCode()); - buyerSecurityDeposit.set(offer.getBuyerSecurityDeposit()); + buyerSecurityDeposit.set(CoinUtil.getAsPercentPerBtc(offer.getBuyerSecurityDeposit())); this.initialState = openOffer.getState(); PaymentAccount tmpPaymentAccount = user.getPaymentAccount(openOffer.getOffer().getMakerPaymentAccountId()); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/BuyerSubView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/BuyerSubView.java index faba914fd1d..df5c4303a08 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/BuyerSubView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/BuyerSubView.java @@ -25,8 +25,6 @@ import bisq.core.locale.Res; -import bisq.common.app.Log; - import org.fxmisc.easybind.EasyBind; public class BuyerSubView extends TradeSubView { @@ -73,7 +71,6 @@ protected void addWizards() { @Override protected void onViewStateChanged(PendingTradesViewModel.State viewState) { if (viewState != null) { - Log.traceCall(viewState.toString()); PendingTradesViewModel.BuyerState buyerState = (PendingTradesViewModel.BuyerState) viewState; step1.setDisabled(); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java index 579f28edf64..702785a835c 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -44,7 +44,6 @@ import bisq.network.p2p.P2PService; -import bisq.common.app.Log; import bisq.common.crypto.KeyRing; import bisq.common.crypto.PubKeyRing; import bisq.common.handlers.ErrorMessageHandler; @@ -348,7 +347,6 @@ public void showNotReadyForTxBroadcastPopups() { /////////////////////////////////////////////////////////////////////////////////////////// private void onListChanged() { - Log.traceCall(); list.clear(); list.addAll(tradeManager.getTradableList().stream().map(PendingTradesListItem::new).collect(Collectors.toList())); @@ -438,7 +436,6 @@ else if (candidates.size() > 1) } private void doOpenDispute(boolean isSupportTicket, Transaction depositTx) { - Log.traceCall("depositTx=" + depositTx); byte[] depositTxSerialized = null; byte[] payoutTxSerialized = null; String depositTxHashAsString = null; diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java index ab41b1ee552..7d7147c1144 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java @@ -25,7 +25,6 @@ import bisq.core.network.MessageState; import bisq.core.offer.Offer; import bisq.core.payment.AccountAgeWitnessService; -import bisq.core.payment.payload.PaymentMethod; import bisq.core.trade.Contract; import bisq.core.trade.Trade; import bisq.core.trade.closed.ClosedTradableManager; @@ -38,7 +37,6 @@ import bisq.common.Clock; import bisq.common.app.DevEnv; -import bisq.common.app.Log; import org.bitcoinj.core.Coin; @@ -294,8 +292,9 @@ public String getTxFee() { } public String getTradeFee() { - if (trade != null && dataModel.getOffer() != null) { - if (dataModel.getOffer().isCurrencyForMakerFeeBtc() && trade.getTradeAmount() != null) { + if (trade != null && dataModel.getOffer() != null && trade.getTradeAmount() != null) { + if (dataModel.isMaker() && dataModel.getOffer().isCurrencyForMakerFeeBtc() || + !dataModel.isMaker() && dataModel.getTrade().isCurrencyForTakerFeeBtc()) { Coin tradeFeeInBTC = dataModel.getTradeFeeInBTC(); String percentage = GUIUtil.getPercentageOfTradeAmount(tradeFeeInBTC, trade.getTradeAmount(), btcFormatter); return btcFormatter.formatCoinWithCode(tradeFeeInBTC) + percentage; @@ -322,7 +321,7 @@ public String getSecurityDeposit() { } public boolean isBlockChainMethod() { - return dataModel.getOffer() != null && dataModel.getOffer().getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS); + return dataModel.getOffer() != null && dataModel.getOffer().getPaymentMethod().isAsset(); } public int getNumPastTrades(Trade trade) { @@ -346,7 +345,6 @@ public int getNumPastTrades(Trade trade) { /////////////////////////////////////////////////////////////////////////////////////////// private void onTradeStateChanged(Trade.State tradeState) { - Log.traceCall(tradeState.toString()); log.debug("UI tradeState={}, id={}", tradeState, trade != null ? trade.getShortId() : "trade is null"); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/SellerSubView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/SellerSubView.java index 9a61ab796cd..aac57aea6c1 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/SellerSubView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/SellerSubView.java @@ -25,8 +25,6 @@ import bisq.core.locale.Res; -import bisq.common.app.Log; - import org.fxmisc.easybind.EasyBind; public class SellerSubView extends TradeSubView { @@ -72,7 +70,6 @@ protected void addWizards() { @Override protected void onViewStateChanged(PendingTradesViewModel.State viewState) { if (viewState != null) { - Log.traceCall(viewState.toString()); PendingTradesViewModel.SellerState sellerState = (PendingTradesViewModel.SellerState) viewState; step1.setDisabled(); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java index 14c64a50b6b..b95d941f12c 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -31,7 +31,6 @@ import bisq.core.user.Preferences; import bisq.common.Clock; -import bisq.common.app.Log; import bisq.common.util.Tuple3; import de.jensd.fx.fontawesome.AwesomeDude; @@ -183,7 +182,6 @@ public void activate() { } public void deactivate() { - Log.traceCall(); if (txIdSubscription != null) txIdSubscription.unsubscribe(); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java index 7d4eb004485..50543eca452 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep2View.java @@ -22,10 +22,10 @@ import bisq.desktop.components.TitledGroupBg; import bisq.desktop.components.paymentmethods.AdvancedCashForm; import bisq.desktop.components.paymentmethods.AliPayForm; +import bisq.desktop.components.paymentmethods.AssetsForm; import bisq.desktop.components.paymentmethods.CashDepositForm; import bisq.desktop.components.paymentmethods.ChaseQuickPayForm; import bisq.desktop.components.paymentmethods.ClearXchangeForm; -import bisq.desktop.components.paymentmethods.CryptoCurrencyForm; import bisq.desktop.components.paymentmethods.F2FForm; import bisq.desktop.components.paymentmethods.FasterPaymentsForm; import bisq.desktop.components.paymentmethods.HalCashForm; @@ -56,8 +56,8 @@ import bisq.core.offer.Offer; import bisq.core.payment.PaymentAccount; import bisq.core.payment.PaymentAccountUtil; +import bisq.core.payment.payload.AssetsAccountPayload; import bisq.core.payment.payload.CashDepositAccountPayload; -import bisq.core.payment.payload.CryptoCurrencyAccountPayload; import bisq.core.payment.payload.F2FAccountPayload; import bisq.core.payment.payload.FasterPaymentsAccountPayload; import bisq.core.payment.payload.HalCashAccountPayload; @@ -162,6 +162,7 @@ public void activate() { break; } } else { + log.warn("confirmButton gets disabled because trade contains error message {}", trade.getErrorMessage()); confirmButton.setDisable(true); statusLabel.setText(""); } @@ -206,7 +207,7 @@ protected void addContent() { Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE).second; field.setCopyWithoutCurrencyPostFix(true); - if (!(paymentAccountPayload instanceof CryptoCurrencyAccountPayload) && + if (!(paymentAccountPayload instanceof AssetsAccountPayload) && !(paymentAccountPayload instanceof F2FAccountPayload)) addTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, Res.get("shared.reasonForPayment"), model.dataModel.getReference(), @@ -281,9 +282,10 @@ protected void addContent() { gridRow = F2FForm.addFormForBuyer(gridPane, gridRow, paymentAccountPayload, model.dataModel.getTrade().getOffer(), 0); break; case PaymentMethod.BLOCK_CHAINS_ID: + case PaymentMethod.BLOCK_CHAINS_INSTANT_ID: String labelTitle = Res.get("portfolio.pending.step2_buyer.sellersAddress", CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode())); - gridRow = CryptoCurrencyForm.addFormForBuyer(gridPane, gridRow, paymentAccountPayload, labelTitle); + gridRow = AssetsForm.addFormForBuyer(gridPane, gridRow, paymentAccountPayload, labelTitle); break; case PaymentMethod.PROMPT_PAY_ID: gridRow = PromptPayForm.addFormForBuyer(gridPane, gridRow, paymentAccountPayload); @@ -495,7 +497,7 @@ private void showPopup() { String id = trade.getShortId(); String paddedId = " " + id + " "; String amount = model.btcFormatter.formatVolumeWithCode(trade.getTradeVolume()); - if (paymentAccountPayload instanceof CryptoCurrencyAccountPayload) { + if (paymentAccountPayload instanceof AssetsAccountPayload) { //noinspection UnusedAssignment message += Res.get("portfolio.pending.step2_buyer.altcoin", CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode()), diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java index 687124788c8..96d94c4c668 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java @@ -44,7 +44,6 @@ import bisq.common.UserThread; import bisq.common.app.DevEnv; -import bisq.common.app.Log; import bisq.common.handlers.FaultHandler; import bisq.common.handlers.ResultHandler; @@ -113,7 +112,6 @@ public void activate() { @Override public void deactivate() { - Log.traceCall(); super.deactivate(); //withdrawAddressTextField.focusedProperty().removeListener(focusedPropertyListener); // withdrawButton.disableProperty().unbind(); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java index 312f32e5238..f4e5798347b 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java @@ -27,9 +27,9 @@ import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; +import bisq.core.payment.payload.AssetsAccountPayload; import bisq.core.payment.payload.BankAccountPayload; import bisq.core.payment.payload.CashDepositAccountPayload; -import bisq.core.payment.payload.CryptoCurrencyAccountPayload; import bisq.core.payment.payload.F2FAccountPayload; import bisq.core.payment.payload.HalCashAccountPayload; import bisq.core.payment.payload.MoneyGramAccountPayload; @@ -132,6 +132,7 @@ public void activate() { break; } } else { + log.warn("confirmButton gets disabled because trade contains error message {}", trade.getErrorMessage()); confirmButton.setDisable(true); statusLabel.setText(""); } @@ -183,9 +184,9 @@ protected void addContent() { if (contract != null) { PaymentAccountPayload myPaymentAccountPayload = contract.getSellerPaymentAccountPayload(); PaymentAccountPayload peersPaymentAccountPayload = contract.getBuyerPaymentAccountPayload(); - if (myPaymentAccountPayload instanceof CryptoCurrencyAccountPayload) { - myPaymentDetails = ((CryptoCurrencyAccountPayload) myPaymentAccountPayload).getAddress(); - peersPaymentDetails = ((CryptoCurrencyAccountPayload) peersPaymentAccountPayload).getAddress(); + if (myPaymentAccountPayload instanceof AssetsAccountPayload) { + myPaymentDetails = ((AssetsAccountPayload) myPaymentAccountPayload).getAddress(); + peersPaymentDetails = ((AssetsAccountPayload) peersPaymentAccountPayload).getAddress(); myTitle = Res.get("portfolio.pending.step3_seller.yourAddress", nameByCode); peersTitle = Res.get("portfolio.pending.step3_seller.buyersAddress", nameByCode); isBlockChain = true; @@ -284,7 +285,7 @@ private void onPaymentReceived() { if (!DevEnv.isDevMode() && DontShowAgainLookup.showAgain(key)) { PaymentAccountPayload paymentAccountPayload = model.dataModel.getSellersPaymentAccountPayload(); String message = Res.get("portfolio.pending.step3_seller.onPaymentReceived.part1", CurrencyUtil.getNameByCode(model.dataModel.getCurrencyCode())); - if (!(paymentAccountPayload instanceof CryptoCurrencyAccountPayload)) { + if (!(paymentAccountPayload instanceof AssetsAccountPayload)) { if (!(paymentAccountPayload instanceof WesternUnionAccountPayload) && !(paymentAccountPayload instanceof HalCashAccountPayload) && !(paymentAccountPayload instanceof F2FAccountPayload)) { @@ -323,8 +324,8 @@ private void showPopup() { String currencyName = CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode()); String part1 = Res.get("portfolio.pending.step3_seller.part", currencyName); String id = trade.getShortId(); - if (paymentAccountPayload instanceof CryptoCurrencyAccountPayload) { - String address = ((CryptoCurrencyAccountPayload) paymentAccountPayload).getAddress(); + if (paymentAccountPayload instanceof AssetsAccountPayload) { + String address = ((AssetsAccountPayload) paymentAccountPayload).getAddress(); String explorerOrWalletString = trade.getOffer().getCurrencyCode().equals("XMR") ? Res.get("portfolio.pending.step3_seller.altcoin.wallet", currencyName) : Res.get("portfolio.pending.step3_seller.altcoin.explorer", currencyName); diff --git a/desktop/src/main/java/bisq/desktop/main/presentation/MarketPricePresentation.java b/desktop/src/main/java/bisq/desktop/main/presentation/MarketPricePresentation.java index 23408f99743..ef6f46dc26f 100644 --- a/desktop/src/main/java/bisq/desktop/main/presentation/MarketPricePresentation.java +++ b/desktop/src/main/java/bisq/desktop/main/presentation/MarketPricePresentation.java @@ -237,4 +237,8 @@ public BooleanProperty getIsPriceAvailable() { public IntegerProperty getMarketPriceUpdated() { return marketPriceUpdated; } + + public StringProperty getMarketPrice() { + return marketPrice; + } } diff --git a/desktop/src/main/java/bisq/desktop/main/settings/about/AboutView.java b/desktop/src/main/java/bisq/desktop/main/settings/about/AboutView.java index 57276af5649..bda8cece3e1 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/about/AboutView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/about/AboutView.java @@ -60,9 +60,9 @@ public void initialize() { GridPane.setHalignment(label, HPos.LEFT); HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.web"), "https://bisq.network"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); - hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.code"), "https://github.com/bisq-network/exchange"); + hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.code"), "https://bisq.network/source/bisq"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); - hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.agpl"), "https://github.com/bisq-network/exchange/blob/master/LICENSE"); + hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.agpl"), "https://bisq.network/source/bisq/blob/master/LICENSE"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); titledGroupBg = addTitledGroupBg(root, ++gridRow, 3, Res.get("setting.about.support"), Layout.GROUP_DISTANCE); diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/P2pNetworkListItem.java b/desktop/src/main/java/bisq/desktop/main/settings/network/P2pNetworkListItem.java index 0c907865a9d..a3e535f4937 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/P2pNetworkListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/P2pNetworkListItem.java @@ -65,7 +65,7 @@ public P2pNetworkListItem(Connection connection, Clock clock, BSFormatter format e -> sentBytes.set(formatter.formatBytes((long) e))); receivedBytesSubscription = EasyBind.subscribe(statistic.receivedBytesProperty(), e -> receivedBytes.set(formatter.formatBytes((long) e))); - onionAddressSubscription = EasyBind.subscribe(connection.peersNodeAddressProperty(), + onionAddressSubscription = EasyBind.subscribe(connection.getPeersNodeAddressProperty(), nodeAddress -> onionAddress.set(nodeAddress != null ? nodeAddress.getFullAddress() : Res.get("settings.net.notKnownYet"))); roundTripTimeSubscription = EasyBind.subscribe(statistic.roundTripTimeProperty(), roundTripTime -> this.roundTripTime.set((int) roundTripTime == 0 ? "-" : roundTripTime + " ms")); diff --git a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java index 3c8f8ae4655..9cc73f18778 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java @@ -225,8 +225,8 @@ private void initializeGeneralOptions() { @Override public String toString(BaseCurrencyNetwork baseCurrencyNetwork) { return baseCurrencyNetwork != null ? - (baseCurrencyNetwork.getCurrencyName() + "_" + baseCurrencyNetwork.getNetwork()) - : ""; + Res.get(baseCurrencyNetwork.name()) : + Res.get("na"); } @Override @@ -590,9 +590,10 @@ private void activateGeneralOptions() { List baseCurrencyNetworks = Arrays.asList(BaseCurrencyNetwork.values()); // We allow switching to testnet to make it easier for users to test the testnet DAO version + // We only show mainnet and dao testnet. Testnet is rather un-usable for application testing when asics + // create 10000s of blocks per day. baseCurrencyNetworks = baseCurrencyNetworks.stream() - .filter(BaseCurrencyNetwork::isBitcoin) - .filter(e -> !e.isRegtest()) + .filter(e -> e.isMainnet() || e.isDaoTestNet() || e.isDaoBetaNet()) .collect(Collectors.toList()); selectBaseCurrencyNetworkComboBox.setItems(FXCollections.observableArrayList(baseCurrencyNetworks)); selectBaseCurrencyNetworkComboBox.setOnAction(e -> onSelectNetwork()); diff --git a/desktop/src/main/java/bisq/desktop/util/FormBuilder.java b/desktop/src/main/java/bisq/desktop/util/FormBuilder.java index 559dd618f2e..d5f9eb11d63 100644 --- a/desktop/src/main/java/bisq/desktop/util/FormBuilder.java +++ b/desktop/src/main/java/bisq/desktop/util/FormBuilder.java @@ -1618,7 +1618,7 @@ public static Tuple3 getNonEditableValueBoxWith public static Tuple2 getTradeInputBox(Pane amountValueBox, String descriptionText) { Label descriptionLabel = new AutoTooltipLabel(descriptionText); descriptionLabel.setId("input-description-label"); - descriptionLabel.setPrefWidth(170); + descriptionLabel.setPrefWidth(190); VBox box = new VBox(); box.setPadding(new Insets(10, 0, 0, 0)); @@ -1743,11 +1743,21 @@ public static Button getIconButton(GlyphIcons icon, String styleClass) { } public static TableView addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText) { - return addTableViewWithHeader(gridPane, rowIndex, headerText, 0); + return addTableViewWithHeader(gridPane, rowIndex, headerText, 0, null); + } + + public static TableView addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText, String groupStyle) { + return addTableViewWithHeader(gridPane, rowIndex, headerText, 0, groupStyle); } public static TableView addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText, int top) { - addTitledGroupBg(gridPane, rowIndex, 1, headerText, top); + return addTableViewWithHeader(gridPane, rowIndex, headerText, top, null); + } + + public static TableView addTableViewWithHeader(GridPane gridPane, int rowIndex, String headerText, int top, String groupStyle) { + TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, rowIndex, 1, headerText, top); + + if (groupStyle != null) titledGroupBg.getStyleClass().add(groupStyle); TableView tableView = new TableView<>(); GridPane.setRowIndex(tableView, rowIndex); diff --git a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java index 53ca7e37c6f..18e3757a8e4 100644 --- a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java @@ -65,6 +65,10 @@ import com.googlecode.jcsv.writer.CSVWriter; import com.googlecode.jcsv.writer.internal.CSVWriterBuilder; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; + import com.google.common.base.Charsets; import org.apache.commons.lang3.StringUtils; @@ -173,7 +177,7 @@ public static void showFeeInfoBeforeExecute(Runnable runnable) { //noinspection UnusedAssignment String key = "miningFeeInfo"; //noinspection ConstantConditions,ConstantConditions - if (!DevEnv.isDevMode() && DontShowAgainLookup.showAgain(key) && BisqEnvironment.getBaseCurrencyNetwork().isBitcoin()) { + if (!DevEnv.isDevMode() && DontShowAgainLookup.showAgain(key)) { new Popup<>().attention(Res.get("guiUtil.miningFeeInfo", String.valueOf(GUIUtil.feeService.getTxFeePerByte().value))) .onClose(runnable) .useIUnderstandButton() @@ -268,6 +272,22 @@ public static void exportCSV(String fileName, CSVEntryConverter headerCon } } + public static void exportJSON(String fileName, JsonElement data, Stage stage) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setInitialFileName(fileName); + File file = fileChooser.showSaveDialog(stage); + if (file != null) { + try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file, false), Charsets.UTF_8)) { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + outputStreamWriter.write(gson.toJson(data)); + } catch (RuntimeException | IOException e) { + e.printStackTrace(); + log.error(e.getMessage()); + new Popup<>().error(Res.get("guiUtil.accountExport.exportFailed", e.getMessage())); + } + } + } + public static String getDirectoryFromChooser(Preferences preferences, Stage stage) { DirectoryChooser directoryChooser = new DirectoryChooser(); File initDir = new File(preferences.getDirectoryChooserPath()); @@ -532,18 +552,16 @@ protected void updateItem(PaymentMethod item, boolean empty) { public static Callback, ListCell> getPaymentMethodCellFactory() { return p -> new ListCell<>() { @Override - protected void updateItem(PaymentMethod item, boolean empty) { - super.updateItem(item, empty); + protected void updateItem(PaymentMethod method, boolean empty) { + super.updateItem(method, empty); - if (item != null && !empty) { - - String id = item.getId(); + if (method != null && !empty) { + String id = method.getId(); HBox box = new HBox(); box.setSpacing(20); - final boolean isBlockchainPaymentMethod = item.equals(PaymentMethod.BLOCK_CHAINS); Label paymentType = new AutoTooltipLabel( - isBlockchainPaymentMethod ? Res.get("shared.crypto") : Res.get("shared.fiat")); + method.isAsset() ? Res.get("shared.crypto") : Res.get("shared.fiat")); paymentType.getStyleClass().add("currency-label-small"); Label paymentMethod = new AutoTooltipLabel(Res.get(id)); @@ -589,10 +607,12 @@ public static void updateConfidence(TransactionConfidence confidence, Tooltip to } } - public static void openWebPage(String target) { + openWebPage(target, true); + } - if (target.contains("bisq.network")) { + public static void openWebPage(String target, boolean useReferrer) { + if (useReferrer && target.contains("bisq.network")) { // add utm parameters target = appendURI(target, "utm_source=desktop-client&utm_medium=in-app-link&utm_campaign=language_" + preferences.getUserLanguage()); @@ -761,9 +781,8 @@ public static void restoreSeedWords(DeterministicSeed seed, WalletsManager walle seed, () -> UserThread.execute(() -> { log.info("Wallets restored with seed words"); - new Popup<>().feedback(Res.get("seed.restore.success")) - .useShutDownButton() - .show(); + new Popup<>().feedback(Res.get("seed.restore.success")).hideCloseButton().show(); + BisqApp.getShutDownHandler().run(); }), throwable -> UserThread.execute(() -> { log.error(throwable.toString()); @@ -974,6 +993,6 @@ protected void updateItem(T item, boolean empty) { public static void openTxInBsqBlockExplorer(String txId, Preferences preferences) { if (txId != null) - GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + txId); + GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + txId, false); } } diff --git a/desktop/src/main/java/bisq/desktop/util/Layout.java b/desktop/src/main/java/bisq/desktop/util/Layout.java index d213eb894a8..888a9f0eecb 100644 --- a/desktop/src/main/java/bisq/desktop/util/Layout.java +++ b/desktop/src/main/java/bisq/desktop/util/Layout.java @@ -28,9 +28,11 @@ public class Layout { public static final double FLOATING_LABEL_DISTANCE = 20d; public static final double GROUP_DISTANCE = 40d; public static final double COMPACT_GROUP_DISTANCE = 30d; + public static final double GROUP_DISTANCE_WITHOUT_SEPARATOR = 20d; public static final double FIRST_ROW_AND_GROUP_DISTANCE = GROUP_DISTANCE + FIRST_ROW_DISTANCE; public static final double COMPACT_FIRST_ROW_AND_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + FIRST_ROW_DISTANCE; public static final double COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + COMPACT_FIRST_ROW_DISTANCE; + public static final double COMPACT_FIRST_ROW_AND_GROUP_DISTANCE_WITHOUT_SEPARATOR = GROUP_DISTANCE_WITHOUT_SEPARATOR + COMPACT_FIRST_ROW_DISTANCE; public static final double TWICE_FIRST_ROW_AND_GROUP_DISTANCE = GROUP_DISTANCE + TWICE_FIRST_ROW_DISTANCE; public static final double PADDING_WINDOW = 20d; public static double PADDING = 10d; diff --git a/desktop/src/main/java/bisq/desktop/util/validation/SecurityDepositValidator.java b/desktop/src/main/java/bisq/desktop/util/validation/SecurityDepositValidator.java index 2ca19b0673b..8dd479f9212 100644 --- a/desktop/src/main/java/bisq/desktop/util/validation/SecurityDepositValidator.java +++ b/desktop/src/main/java/bisq/desktop/util/validation/SecurityDepositValidator.java @@ -21,17 +21,15 @@ import bisq.core.locale.Res; import bisq.core.util.BSFormatter; -import org.bitcoinj.core.Coin; - import javax.inject.Inject; -public class SecurityDepositValidator extends BtcValidator { +public class SecurityDepositValidator extends NumberValidator { + + private final BSFormatter formatter; @Inject public SecurityDepositValidator(BSFormatter formatter) { - super(formatter); - setMaxValue(Restrictions.getMaxBuyerSecurityDeposit()); - setMinValue(Restrictions.getMinBuyerSecurityDeposit()); + this.formatter = formatter; } @@ -46,22 +44,34 @@ public ValidationResult validate(String input) { if (result.isValid) { result = validateIfNotZero(input) .and(validateIfNotNegative(input)) - .and(validateIfNotTooLowBtcValue(input)) - .and(validateIfNotFractionalBtcValue(input)) - .and(validateIfNotExceedsMaxBtcValue(input)); + .and(validateIfNotTooLowPercentageValue(input)) + .and(validateIfNotTooHighPercentageValue(input)); } - return result; } - protected ValidationResult validateIfNotTooLowBtcValue(String input) { + private ValidationResult validateIfNotTooLowPercentageValue(String input) { + try { + double percentage = formatter.parsePercentStringToDouble(input); + double minPercentage = Restrictions.getMinBuyerSecurityDepositAsPercent(); + if (percentage < minPercentage) + return new ValidationResult(false, + Res.get("validation.inputTooSmall", formatter.formatToPercentWithSymbol(minPercentage))); + else + return new ValidationResult(true); + } catch (Throwable t) { + return new ValidationResult(false, Res.get("validation.invalidInput", t.getMessage())); + } + } + + private ValidationResult validateIfNotTooHighPercentageValue(String input) { try { - final Coin coin = Coin.parseCoin(input); - Coin minSecurityDeposit = Restrictions.getMinBuyerSecurityDeposit(); - if (coin.compareTo(minSecurityDeposit) < 0) + double percentage = formatter.parsePercentStringToDouble(input); + double maxPercentage = Restrictions.getMaxBuyerSecurityDepositAsPercent(); + if (percentage > maxPercentage) return new ValidationResult(false, - Res.get("validation.securityDeposit.toSmall", formatter.formatCoinWithCode(minSecurityDeposit))); + Res.get("validation.inputTooLarge", formatter.formatToPercentWithSymbol(maxPercentage))); else return new ValidationResult(true); } catch (Throwable t) { diff --git a/desktop/src/main/resources/images/system_tray_icon@2x_white.png b/desktop/src/main/resources/images/system_tray_icon@2x_white.png new file mode 100644 index 00000000000..555e8cb81b3 Binary files /dev/null and b/desktop/src/main/resources/images/system_tray_icon@2x_white.png differ diff --git a/desktop/src/main/resources/images/system_tray_icon_white.png b/desktop/src/main/resources/images/system_tray_icon_white.png new file mode 100644 index 00000000000..c71aa6189cb Binary files /dev/null and b/desktop/src/main/resources/images/system_tray_icon_white.png differ diff --git a/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferDataModelTest.java b/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferDataModelTest.java index 7098c42637b..1d3d931fa31 100644 --- a/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferDataModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferDataModelTest.java @@ -66,7 +66,7 @@ public void setUp() { when(btcWalletService.getOrCreateAddressEntry(anyString(), any())).thenReturn(addressEntry); when(preferences.isUsePercentageBasedPrice()).thenReturn(true); - when(preferences.getBuyerSecurityDepositAsCoin()).thenReturn(Coin.FIFTY_COINS); + when(preferences.getBuyerSecurityDepositAsPercent()).thenReturn(0.01); model = new CreateOfferDataModel(null, btcWalletService, null, preferences, user, null, diff --git a/docs/autosetup-regtest-dao/bisq/seed_node b/docs/autosetup-regtest-dao/bisq/seed_node index 2cd2dccd131..a9d88416d2a 100755 --- a/docs/autosetup-regtest-dao/bisq/seed_node +++ b/docs/autosetup-regtest-dao/bisq/seed_node @@ -1,4 +1,4 @@ #!/bin/bash . ./config.sh -~/bin/bisq/bisq-seednode --baseCurrencyNetwork=BTC_REGTEST --useLocalhostForP2P=true --useDevPrivilegeKeys=true --nodePort=2002 --myAddress=localhost:2002 --appName=bisq-BTC_REGTEST_Seed_2002 --daoActivated=true --genesisBlockHeight=$genesis_height --genesisTxId=$genesis_tx --fullDaoNode=$dao --rpcUser=$rpcuser --rpcPassword=$rpcpassword --rpcPort=$rpcport --rpcBlockNotificationPort=$blocknotifyport +~/bin/bisq/bisq-seednode --baseCurrencyNetwork=BTC_REGTEST --useLocalhostForP2P=true --useDevPrivilegeKeys=true --nodePort=2002 --appName=bisq-BTC_REGTEST_Seed_2002 --daoActivated=true --genesisBlockHeight=$genesis_height --genesisTxId=$genesis_tx --fullDaoNode=$dao --rpcUser=$rpcuser --rpcPassword=$rpcpassword --rpcPort=$rpcport --rpcBlockNotificationPort=$blocknotifyport diff --git a/docs/build.md b/docs/build.md index a666c60b206..09b5de24c2f 100644 --- a/docs/build.md +++ b/docs/build.md @@ -1,7 +1,7 @@ # Building Bisq _You will need [OpenJDK 10](https://jdk.java.net/10/) installed and configured as the default system JDK to complete the following instructions. See the `scripts` directory for scripts that can be used to install and configure the JDK automatically._ - +> TIP: If you are on MacOS, run the script with this command `. scripts/install_java.sh`. ## Clone diff --git a/docs/data-stores.md b/docs/data-stores.md new file mode 100644 index 00000000000..554efb15889 --- /dev/null +++ b/docs/data-stores.md @@ -0,0 +1,9 @@ +# Data stores + +### Update stores + +With every release we include the latest snapshot of Mainnet and Testnet data from the P2P network within the client. + +* Start your Bisq client on Mainnet and Testnet and let it run until it is fully synced. +* Run [copy_dbs.sh](https://github.com/bisq-network/bisq/blob/master/desktop/package/macosx/copy_dbs.sh) to copy the +required files into the [p2p resources directory](https://github.com/bisq-network/bisq/blob/master/p2p/src/main/resources). diff --git a/docs/dev-setup.md b/docs/dev-setup.md index 638b93bedf9..9911c22e2ba 100644 --- a/docs/dev-setup.md +++ b/docs/dev-setup.md @@ -54,7 +54,6 @@ Here is an overview: - `--baseCurrencyNetwork`: The BTC network to use. Possible values are: `BTC_REGTEST`, `BTC_TESTNET`, `BTC_MAINNET` (default) - `--useLocalhostForP2P`: Uses localhost instead of Tor for Bisq P2P network - `--nodePort`: Port number for localhost mode. For seed nodes there is a convention with the last digit is marking the network type and there is a list of hard coded seed nodes addresses (see: `DefaultSeedNodeAddresses.java`). For regtest: 2002 and 3002. For testnet 2001, 3001 and 4001 and for mainnet: 2000, 3000 and 4000. For normal nodes the port can be chosen freely. - - `--myAddress`: Needed for seed nodes only (e.g.: `localhost:3002`) - `--useDevPrivilegeKeys`: Important for dev testing to allow the developer key for arbitration registration - `--appName`: Custom application name which is used for the data directory. It is important to separate your nodes to not interfere. If not set, it uses the default `Bisq` directory. @@ -63,7 +62,7 @@ Here is an overview: For localhost/regtest mode run the `SeedNodeMain` class or `./bisq-seednode` script in the root project dir with following program arguments: - --baseCurrencyNetwork=BTC_REGTEST --useLocalhostForP2P=true --useDevPrivilegeKeys=true --nodePort=2002 --myAddress=localhost:2002 --appName=bisq-BTC_REGTEST_Seed_2002 + --baseCurrencyNetwork=BTC_REGTEST --useLocalhostForP2P=true --useDevPrivilegeKeys=true --nodePort=2002 --appName=bisq-BTC_REGTEST_Seed_2002 ### Run Bisq arbitrator instance diff --git a/docs/idea-import.md b/docs/idea-import.md index 4bee6f0eae5..1fb149d6ed4 100644 --- a/docs/idea-import.md +++ b/docs/idea-import.md @@ -12,6 +12,7 @@ Most Bisq contributors use IDEA for development. The following instructions have 1. In the `Import Project from Gradle` screen, check the `Use auto-import` option and click `OK` 1. When prompted whether to overwrite the existing `.idea` directory, click `Yes` 1. In the `Project` tool window, right click on the root-level `.idea` folder, select `Git->Revert...` and click OK in the dialog that appears (to restore source-controlled `.idea` configuration files that get overwritten during project import) + 1. If you did not yet setup JDK10 in IntelliJ, Go to `File -> Project Structure -> Project` and under the `Project SDK` option locate your JAVA_HOME folder, then in `Project language level` beneath select `10 - ...`. 1. Go to `Build->Build Project`. Everything should build cleanly. You should be able to run tests, run `main` methods in any component, etc. > TIP: If you encounter compilation errors in IDEA related to the `io.bisq.generated.protobuffer.PB` class, it is probably because you didn't build Bisq at the command line as instructed above. You need to run the `generateProto` task in the `common` project. You can do this via the Gradle tool window in IDEA, or you can do it the command line with `./gradlew :common:generateProto`. Once you've done that, run `Build->Build Project` again and you should have no errors. diff --git a/docs/release-process.md b/docs/release-process.md new file mode 100644 index 00000000000..4b2f6ecb95f --- /dev/null +++ b/docs/release-process.md @@ -0,0 +1,177 @@ +# Release Process + +* Update translations [translation-process.md](translation-process.md#synchronising-translations). +* Update data stores [data-stores.md](data-stores.md#update-stores). +* Write release notes (see below). +* Webpage (Prepare PR) + * Update version number in: + * [_config.yml](https://github.com/bisq-network/bisq-website/blob/master/_config.yml) + * [downloads.html](https://github.com/bisq-network/bisq-website/blob/master/downloads.html) + + * Update currency list in [market_currency_selector.html](https://github.com/bisq-network/bisq-website/blob/master/_includes/market_currency_selector.html) (use [MarketsPrintTool](https://github.com/bisq-network/bisq/blob/master/desktop/src/test/java/bisq/desktop/MarketsPrintTool.java) + to create HTML content). + * Update [roadmap](https://github.com/bisq-network/bisq-website/blob/master/roadmap.md) with new release notes. + + +### Bisq maintainers, suggestion for writing release notes +To be able to create release notes before you make the final release tag, you can temporarily create a local tag and +remove it afterwards again. + + git tag v(new version, e.g. 0.9.4) #create tag + git tag -d v(new version, e.g. 0.9.4) #delete tag + +Write release notes. git shortlog helps a lot, for example: + + git shortlog --no-merges v(current version, e.g. 0.9.3)..v(new version, e.g. 0.9.4) + +Generate list of authors: + + git log --format='- %aN' v(current version, e.g. 0.9.3)..v(new version, e.g. 0.9.4) | sort -fiu + +1. Prepare the release notes with major changes from user perspective. +2. Prepare a short version of the release notes for the in-app update popup + +### Basic preparations +For releasing a new Bisq version you'll need Linux, Windows and macOS. +You can use a virtualization solution like [VirtualBox](https://www.virtualbox.org/wiki/Downloads) for this purpose. + +#### VirtualBox recommended configuration +Although performance of VMs might vary based on your hardware configuration following setup works pretty well on macOS. + +Use VirtualBox < 5.2.22: Using a more recent version makes VMs hardly usable on a MacBook Pro (15-inch, 2016) +with following configuration: + * System > Motherboard > Base Memory: 2048 MB + * System > Processor > Processor(s): 2 CPUs + * System > Processor > Execution Cap: 90% + * Display > Screen > Video Memory: 128 MB + * Display > Screen > Scale Factor: 200% + * Display > Screen > HiDPI Support: Use unscaled HiDPI Output (checked) + * Display > Screen > Acceleration: Enable 3D acceleration (checked) + +##### Windows VM +* Windows 10 64bit +* Recommended virtual disk size: 55 GB + +##### Linux VM +* Ubuntu 16.04.4 64bit +* Recommended virtual disk size: 15 GB + +##### macOS VM +* macOS X 10.11 (El Capitan) 64bit +* Recommended virtual disk size: 40 GB + +#### For every OS + +* Install latest security updates + +#### For Windows + +* Update AntiVirus Software and virus definitions +* Install unicode version of [Inno Tools](http://www.jrsoftware.org/isdl.php) +* Run full AV system scan + +### Build release + +#### macOS + +1. Make sure all version numbers are updated (update version variables and run [replace_version_number.sh](https://github.com/bisq-network/bisq/blob/master/bisq/desktop/package/macosx/replace_version_number.sh)). +2. Set environment variable e.g. export BISQ_GPG_USER=manfred@bitsquare.io to ~/.profile file or the like... (one time effort) +3. Update [vmPath variable](https://github.com/bisq-network/bisq/blob/b4b5d0bb12c36afbe1aa6611dd8451378df6db8c/desktop/package/macosx/create_app.sh#L42) if necessary +4. Run [create_app.sh](https://github.com/bisq-network/bisq/blob/master/desktop/package/macosx/create_app.sh) + +Build output expected in deploy directory (opened after successful build process): + + 1. `Bisq-${NEW-VERSION}.dmg` macOS signed installer + 2. `Bisq-${NEW-VERSION}.jar` Deterministic fat jar + 3. `Bisq-${NEW-VERSION}.jar.txt` sha256 sum of deterministic fat jar + +The build scripts also copies over the deterministic fat jar into the shared folders for the other VMs (Windows & Linux). +Before building the other binaries install the generated Bisq app on macOS and verify that everything works as expected. + +#### Linux + +* Run `desktop/package/linux/package.sh` from the shared VM folder + +Build output expected: + + 1. `Bisq-${NEW-VERSION}.deb` package for distributions that derive from Debian + 2. `Bisq-${NEW-VERSION}.rpm` package for distributions that derive from Redhat based distros + +* Install and run generated package + +#### Windows + +* Run `desktop/package/windows/package.bat` from the shared VM folder + +Build output expected: + + 1. `Bisq-${NEW-VERSION}.exe` Windows unsigned installer + 2. `Bisq-${NEW-VERSION}.exe.txt` sha256 sum of installer + +### Sign release on macOS + +* Run [finalize.sh](https://github.com/bisq-network/bisq/blob/master/bisq/desktop/package/macosx/finalize.sh) + +Build output expected: + + 1. `F379A1C6.asc` Sig key of Manfred Karrer + 2. `5BC5ED73.asc` Sig key of Chris Beams + 3. `29CDFD3B.asc`Sig key of Christoph Atteneder + 4. `signingkey.asc` Fingerprint of key that was used for this builds + 5. `Bisq-${NEW-VERSION}.jar.txt` Sha256 sum of deterministic fat jar + 6. `Bisq-${NEW-VERSION}.dmg` macOS installer + 7. `Bisq-${NEW-VERSION}.dmg.asc` Signature for macOS installer + 8. `Bisq-${NEW-VERSION}.deb` Debian package + 9. `Bisq-${NEW-VERSION}.deb.asc` Signature for Debian package + 10. `Bisq-${NEW-VERSION}.rpm` Redhat based distro package + 11. `Bisq-${NEW-VERSION}.rpm.asc` Signature for Redhat based distro package + 12. `Bisq-${NEW-VERSION}.exe` Windows installer + 13. `Bisq-${NEW-VERSION}.exe.asc` Signature for Windows installer + + * Run a AV scan over all files on the Windows VM where the files got copied over. + +### Final test + + * Make at least one mainnet test trade with some exotic currency to not interfere with real traders. + +### Tag and push release to master + +If all was successful: + + * commit changes of new version number (update version number for release of e.g. v0.9.4) + * create tag for the release +``` + git tag -s v(new version, e.g. 0.9.4) -m"Release v(new version, e.g. 0.9.4)" +``` + * Revert back to SNAPSHOT where necessary (change version variable (e.g. 0.9.4) in shell script [insert_snapshot_version.sh](https://github.com/bisq-network/bisq/blob/master/desktop/package/macosx/insert_snapshot_version.sh) and run it) and commit these changes. + * Push all commits to master including the new tag +``` + git push --tags origin master +``` + +### GitHub + +#### Upload preparations + + * Check the fingerprint of the pgp key which was used for signing in signingkey.asc (e.g. 29CDFD3B for Christoph Atteneder) + * Add all files including signingkey.asc and the gpg pub keys to GitHub release page + * Check all uploaded files with [virustotal.com](https://www.virustotal.com) + * Select the release tag as the source for the GitHub release. + * Release on GitHub + +#### Post GitHub release + * Apply “A newer version is already available! Please don’t use this version anymore.” to old GitHub releases. + * Merge the webpage PR and check if they got deployed properly. + * Start the Alert sender app (CMD + M) remove the old version and send the update message. + Check the checkbox for update, set the version number (e.g. 0.9.4) and add the short version of the release notes. + * After sending the Update message leave it running for about 1 minute to give time for good propagation. + * Make a backup of that alert sender app data directory + +### Announce the release + + * Forum + * Slack (#general channel) + * Twitter + * Optionally reddit /r/Bisq + * Notify @freimair so that he can start updating [the Arch User Repository](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=bisq-git) + * Celebrate diff --git a/docs/testing.md b/docs/testing.md index 61321f807e3..02279cb76a5 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -8,9 +8,7 @@ In order to take part in the testing process, you will need to do the following: - Build Bisq from source (see [build.md](build.md)) - Setup a development/testing environment (see [dev-setup.md](dev-setup.md)) -- Register an account with [TestQuality](https://bisq.testquality.com) - - _**Note:** Once you have registered an account, or if you have trouble registering, contact us in Slack (see below) to be granted permissions to execute test runs._ +- Request access to [TestPad](https://bisq.ontestpad.com) (our test management tool) ## Communication Channels @@ -18,51 +16,58 @@ If you would like to discuss and/or contribute to Bisq's testing effort, join us ## Compensation -Testing activities are eligible for [compensation](https://docs.bisq.network/dao/phase-zero.html#how-to-request-compensation). When submitting a compensation request, please include links to artifacts on TestQuality indicating the activities that were performed (e.g. test plan runs that were executed), as well as any bugs that were discovered and entered as a result of testing. +Testing activities are eligible for [compensation](https://docs.bisq.network/dao/phase-zero.html#how-to-request-compensation). +When submitting a compensation request, please include links to artifacts on TestPad (results/reports) indicating the activities that were performed (e.g. tests that were executed), as well as any bugs that were discovered and entered as a result of testing. ## Testing Process -[TestQuality](https://bisq.testquality.com) is used to manage and track the manual testing process. For specific usage or functionality of TestQuality, please see the guided tour or refer to the help content within TestQuality. +[TestPad](https://bisq.ontestpad.com) is used to manage and track the manual testing process. +For specific usage or functionality of TestPad, please see the flash card introduction within TestPad. ### Definitions -Some definitions within the context of TestQuality: +Some definitions within the context of TestPad and how they apply to our specific testing process: + +- **Project:** Defines a particular testing scope with relevant tests. +- **Script:** Each script is a collection of related tests that are intended to be used to test a particular component. +- **Folder:** Defines a group of scripts for each release. + +### Test Structure -- **Test Case:** a set of conditions under which a tester will determine whether the system under test satisfies requirements or works correctly. -- **Test Suite:** a collection of test cases that are intended to be used to test the system to show that it has some specified set of behaviours. -- **Test Plan:** defines a particular testing scope with testing activities. -- **Test Plan Run:** an occurrence in which a particular test plan is executed. +Tests are written using Behaviour-Driven Development (BDD) style syntax (given/when/then). +- **Given:** This states the preconditions that are assumed for the test. It is not a test step (one that requires a result to be recorded), but instead you must ensure the preconditions are satisfied in order to perform the test. +- **When:** This states the actions to be performed for the test. This also does not require a result to be recorded. +- **Then:** This states the expected results of the test. This requires a result to be recorded. ### Testing Workflow -Once logged in to TestQuality, select the Bisq project from the dashboard. +Once logged in to TestPad, select the `Desktop Client` project from the left navigation menu. -#### Executing a Test Plan Run +Each upcoming release will have a new folder created with applicable scripts that need to be executed. -A new test plan run is created every time a test plan needs to be executed. This allows for tracking the history of test plan’s executions. +#### Executing a Script -To execute a test plan run: +Test runs allow for tracking the results of test execution. Each script may have several test runs created in order to perform the tests on different environments (e.g. operating systems) and assigned to different people. An overview of all test runs for the release can be observed from the main project view, which allows you to quickly find test runs assigned to yourself. -1. Open the test plan run to be executed. +To execute a test run: -1. Select the "Running" state in order to update the start time (used for time tracking purposes). +1. Open the script to be executed. -1. Navigate the test suites and perform each test case. +1. Hover over the applicable test run column and select the play button to start executing the test run. - - Specify a status for each step or for the overall test case and optionally enter the time spent on each step. Select from one of the following statuses: +1. Follow the script and perform each test. - - **Pass:** the test case or step has passed successfully. - - **Pending:** the test case or step has yet to be performed. - - **Fail:** there is an issue (defect) related to the test case or step. - - **Block:** the test case or step is unable to be performed. - - **Retest:** the test case or step needs to be retested. - - **Skip:** the test case or step does not need to be performed. + - Select a status for each test. Select from one of the following statuses: - - If required, use the `Reason for Status` field to add any comments, notes and actual test results. This is especially beneficial to provide details if a test fails. + - **Pass:** the test has passed successfully. + - **Fail:** there is an issue (defect) related to the test. + - **Blocked:** the test cannot be performed for a particular reason. + - **Query:** you are unsure about the test and require further information. + - **Exclude:** the test does not need to be performed for a particular reason. - - If applicable, link an existing or create a new issue (defect) if it was found during the test plan run execution. + - If necessary, use the `Comments` field to add any comments, notes and actual test results. This is especially beneficial to provide details if a test did not pass. -1. Once all test cases within the test plan run have been executed, select the "Complete" state in order to update the end time. + - If applicable, link an existing or create a new issue (defect) if it was found during the test run execution. ### Creating Issues diff --git a/docs/translation-process.md b/docs/translation-process.md new file mode 100644 index 00000000000..14d5351cc2a --- /dev/null +++ b/docs/translation-process.md @@ -0,0 +1,20 @@ +# Translation Process + +### Installing the Transifex client command-line tool + +You'll find a detailed guide [how to install the Transifex client](https://docs.transifex.com/client/installing-the-client) on the Transifex page. + +The Transifex Bisq project config file is included as part of the repository. It can be found at `core/.tx/config`. + +### Synchronising translations + +We've prepared a script to update the translation files with the Transifex client command-line tool. + + * Run [update_translations.sh](https://github.com/bisq-network/bisq/blob/master/core/update_translations.sh) + +Synchronization output expected: + + * All translation files in [i18n directory](https://github.com/bisq-network/bisq/blob/master/core/src/main/resources/i18n) have been updated. + +Go over the changes if there are any obvious issues (Transifex had once a problem which caused a rewrite of certain keys) +and commit and push them to master. diff --git a/gradle/witness/gradle-witness.gradle b/gradle/witness/gradle-witness.gradle index 258b9316ccf..42fa8be0381 100644 --- a/gradle/witness/gradle-witness.gradle +++ b/gradle/witness/gradle-witness.gradle @@ -11,6 +11,7 @@ // // See https://github.com/signalapp/gradle-witness#using-witness for further details. + dependencyVerification { verify = [ 'org.controlsfx:controlsfx:b98f1c9507c05600f80323674b33d15674926c71b0116f70085b62bdacf1e573', @@ -22,8 +23,8 @@ dependencyVerification { 'com.googlecode.jcsv:jcsv:73ca7d715e90c8d2c2635cc284543b038245a34f70790660ed590e157b8714a2', 'com.github.sarxos:webcam-capture:d960b7ea8ec3ddf2df0725ef214c3fccc9699ea7772df37f544e1f8e4fd665f6', 'com.jfoenix:jfoenix:4739e37a05e67c3bc9d5b391a1b93717b5a48fa872992616b0964d3f827f8fe6', - 'com.github.JesusMcCloud.netlayer:tor.native:2d531148cf407b8f6ca3b8f9b87410e5b34c023b8424502e390c8c9682ca0588', - 'com.github.JesusMcCloud.netlayer:tor.external:fa3df5f8cdd49e16c15761b0d4bc7ed5aeef71cf14f33b1a88c2b75ba7f43ce6', + 'com.github.JesusMcCloud.netlayer:tor.native:6f5fbda4e85f773af0d25fe7502007b1fd14e04ddc17b7619219f22ed7d4081e', + 'com.github.JesusMcCloud.netlayer:tor.external:0cef160272a8ba41dd7f7680350e9ad1c003b9b5f41ae552a1086f7a08e5cdf5', 'org.apache.httpcomponents:httpclient:db3d1b6c2d6a5e5ad47577ad61854e2f0e0936199b8e05eb541ed52349263135', 'net.sf.jopt-simple:jopt-simple:6f45c00908265947c39221035250024f2caec9a15c1c8cf553ebeecee289f342', 'org.fxmisc.easybind:easybind:666af296dda6de68751668a62661571b5238ac6f1c07c8a204fc6f902b222aaf', @@ -37,13 +38,13 @@ dependencyVerification { 'com.googlecode.json-simple:json-simple:4e69696892b88b41c55d49ab2fdcc21eead92bf54acc588c0050596c3b75199c', 'org.springframework:spring-core:c451e8417adb2ffb2445636da5e44a2f59307c4100037a1fe387c3fba4f29b52', 'ch.qos.logback:logback-classic:e66efc674e94837344bc5b748ff510c37a44eeff86cbfdbf9e714ef2eb374013', - 'com.github.bisq-network.bitcoinj:bitcoinj-core:d148d9577cf96540f7f5367011f7626ff9c9f148f0bf903b541740d480652969', + 'com.github.bisq-network.bitcoinj:bitcoinj-core:816e976a7efcfb650ff9009059059068b785ead864a6321fe77bba65ebd3d273', 'org.slf4j:slf4j-api:3a4cd4969015f3beb4b5b4d81dbafc01765fb60b8a439955ca64d8476fef553e', 'ch.qos.logback:logback-core:4cd46fa17d77057b39160058df2f21ebbc2aded51d0edcc25d2c1cecc042a005', 'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.guava:guava:36a666e3b71ae7f0f0dca23654b67e086e6c93d192f60ba5dfd5519db6c288c8', 'com.google.inject:guice:9b9df27a5b8c7864112b4137fd92b36c3f1395bfe57be42fedf2f520ead1a93e', - 'com.github.JesusMcCloud.netlayer:tor:75ca62a08621ea532b42ae6b014d9604a856ed572ffc8d2c15c281df817702ba', + 'com.github.JesusMcCloud.netlayer:tor:35cf892e6ce3a8d942cfd2b589cfbde5aed31d49777aee873d6614e134df0b42', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:193ab7813e4d249f2ea4fc1b968fea8c2126bcbeeb5d6127050ce1b93dbaa7c2', 'io.github.microutils:kotlin-logging:4992504fd3c6ecdf9ed10874b9508e758bb908af9e9d7af19a61e9afb6b7e27a', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:877b59bbe466b24a88275a71fd06cd97359d2085420f6f1ac1d766afa8116001', @@ -55,10 +56,10 @@ dependencyVerification { 'org.bouncycastle:bcprov-jdk15on:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', 'com.google.zxing:javase:0ec23e2ec12664ddd6347c8920ad647bb3b9da290f897a88516014b56cc77eb9', 'com.nativelibs4java:bridj:101bcd9b6637e6bc16e56deb3daefba62b1f5e8e9e37e1b3e56e3b5860d659cf', - 'com.github.JesusMcCloud.tor-binary:tor-binary-macos:d143904dee93952576b12afb3c255ffce1b4eb0f8c85b0078c753b5f57fa1d07', - 'com.github.JesusMcCloud.tor-binary:tor-binary-linux32:e863b9e37416890825a80f769ed507b203d0bcc3234aa9922d3f801bdf08987b', - 'com.github.JesusMcCloud.tor-binary:tor-binary-linux64:936bdc1f9e145fbd625766f79d30761529303badd055e69e7bc0d27fcd771e7b', - 'com.github.JesusMcCloud.tor-binary:tor-binary-windows:db2a7af40ded2ccaa12dad26cc301f082bb148322b15ec0155662510609fddfa', + 'com.github.JesusMcCloud.tor-binary:tor-binary-macos:18f7f1a567821dcc22c4b2146db8c4d00a5c6945a556f1a60085b06ad6d61054', + 'com.github.JesusMcCloud.tor-binary:tor-binary-linux32:5620ec4df9649ca1c482cde2bc3edda97906616c210adc0ced918943245a5edb', + 'com.github.JesusMcCloud.tor-binary:tor-binary-linux64:6891fcd90029efbdcc9c58b3a3f09ce50febf7038df6c57ac338cc6c040d68e0', + 'com.github.JesusMcCloud.tor-binary:tor-binary-windows:80ccf0aa44018e7d419d31f570ab87f0d6bd74e4c4a6360b3181d0643d8ef43b', 'com.github.ravn:jsocks:3c71600af027b2b6d4244e4ad14d98ff2352a379410daebefff5d8cd48d742a4', 'org.apache.httpcomponents:httpcore:d7f853dee87680b07293d30855b39b9eb56c1297bd16ff1cd6f19ddb8fa745fb', 'commons-codec:commons-codec:ad19d2601c3abf0b946b5c3a4113e226a8c1e3305e395b90013b78dd94a723ce', @@ -67,8 +68,8 @@ dependencyVerification { 'aopalliance:aopalliance:0addec670fedcd3f113c5c8091d783280d23f75e3acb841b61a9cdb079376a08', 'com.lambdaworks:scrypt:9a82d218099fb14c10c0e86e7eefeebd8c104de920acdc47b8b4b7a686fb73b4', 'com.google.zxing:core:11aae8fd974ab25faa8208be50468eb12349cd239e93e7c797377fa13e381729', - 'com.github.JesusMcCloud.tor-binary:tor-binary-geoip:7340d4a0007b822b2f149e7360cd22443a5976676754bfaad0b8c48858475d5f', - 'com.github.JesusMcCloud:jtorctl:b8be77613eeba899abff797c3cb9159b96361b2f880a3ebcc2280c027b0758e3', + 'com.github.JesusMcCloud.tor-binary:tor-binary-geoip:766e4400e5651cf0b11788ea440cc72721be9b92e42f20809c22d0ff129df83c', + 'com.github.JesusMcCloud:jtorctl:904f7c53332179a3479c64d63fb303afa6a02b6889aabdab5b235f3efc725ca7', 'org.apache.commons:commons-compress:5f2df1e467825e4cac5996d44890c4201c000b43c0b23cffc0782d28a0beb9b0', 'org.tukaani:xz:a594643d73cc01928cf6ca5ce100e094ea9d73af760a5d4fb6b75fa673ecec96', 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', diff --git a/monitor/README.md b/monitor/README.md index 9e170823d36..98ade99b810 100644 --- a/monitor/README.md +++ b/monitor/README.md @@ -2,18 +2,25 @@ The Bisq monitor node collects a set of metrics which are of interest to developers and users alike. These metrics are then made available through reporters. -The *Babysteps* release features these metrics: +The *Settled* release features these metrics: - Tor Startup Time: The time it takes to start Tor starting at a clean system, unpacking the shipped Tor binaries, firing up Tor until Tor is connected to the Tor network and ready to use. - Tor Roundtrip Time: Given a bootstrapped Tor, the roundtrip time of connecting to a hidden service is measured. - Tor Hidden Service Startup Time: Given a bootstrapped Tor, the time it takes to create and announce a freshly created hidden service. +- P2P Round Trip Time: A metric hitchhiking the Ping/Pong messages of the Keep-Alive-Mechanism to determine the Round Trip Time when issuing a Ping to a seed node. +- P2P Seed Node Message Snapshot: Get absolute number and constellation of messages a fresh Bisq client will get on startup. Also reports diffs between seed nodes on a per-message-type basis. +- P2P Network Load: listens to the P2P network and its broadcast messages. Reports every X seconds. +- P2P Market Statistics: a demonstration metric which extracts market information from broadcast messages. This demo implementation reports the number of open offers per market . -The *Babysteps* release features these reporters: + +The *Settled* release features these reporters: - A reporter that simply writes the findings to `System.err` - A reporter that reports the findings to a Graphite/Carbon instance using the [plaintext protocol](https://graphite.readthedocs.io/en/latest/feeding-carbon.html#the-plaintext-protocol) ## Configuration -The *Bisq Network Monitor Node* is to be configured via a Java properties file. The location of the file is to be passed as command line parameter: +The *Bisq Network Monitor Node* is to be configured via a Java properties file. There is a default configuration file shipped with the monitor which reports to the one monitoring service currently up and running. + +If you want to tweak the configuration, you can pass the location of the file as command line parameter: ``` ./bisq-monitor /path/to/your/config.properties @@ -22,6 +29,14 @@ The *Bisq Network Monitor Node* is to be configured via a Java properties file. A sample configuration file looks like follows: ``` +## System configuration + +# true overwrites the reporters picked by the developers (for debugging for example) (defaults to false) +System.useConsoleReporter=true + +# 0 -> BTC_MAINNET, 1 -> BTC_TESTNET (default) +System.baseCurrencyNetwork=0 + ## Each Metric is configured via a set of properties. ## ## The minimal set of properties required to run a Metric is: @@ -29,7 +44,7 @@ A sample configuration file looks like follows: ## YourMetricName.enabled=true|false ## YourMetricName.run.interval=10 [seconds] -#Edit and uncomment the lines below for your liking +#Edit and uncomment the lines below to your liking #TorStartupTime Metric TorStartupTime.enabled=true @@ -48,10 +63,184 @@ TorHiddenServiceStartupTime.run.interval=100 TorHiddenServiceStartupTime.run.localPort=90501 # so that there is no interference with a system Tor TorHiddenServiceStartupTime.run.servicePort=90511 # so that there is no interference with a system Tor +#P2PRoundTripTime Metric +P2PRoundTripTime.enabled=true +P2PRoundTripTime.run.interval=100 +P2PRoundTripTime.run.sampleSize=5 +P2PRoundTripTime.run.hosts=723ljisnynbtdohi.onion:8000, fl3mmribyxgrv63c.onion:8000 +P2PRoundTripTime.run.torProxyPort=9060 + +#P2PNetworkLoad Metric +P2PNetworkLoad.enabled=true +P2PNetworkLoad.run.interval=100 +P2PNetworkLoad.run.torProxyPort=9061 +P2PNetworkLoad.run.historySize=500 + +#P2PNetworkMessageSnapshot Metric +P2PSeedNodeSnapshot.enabled=true +P2PSeedNodeSnapshot.run.interval=24 +P2PSeedNodeSnapshot.run.hosts=3f3cu2yw7u457ztq.onion:8000, 723ljisnynbtdohi.onion:8000, fl3mmribyxgrv63c.onion:8000 +P2PSeedNodeSnapshot.run.torProxyPort=9062 + +#P2PMarketStats Metric +P2PMarketStats.enabled=true +P2PMarketStats.run.interval=37 +P2PMarketStats.run.hosts=ef5qnzx6znifo3df.onion:8000 +P2PMarketStats.run.torProxyPort=9063 + +#PriceNodeStats Metric +PriceNodeStats.enabled=true +PriceNodeStats.run.interval=42 +PriceNodeStats.run.hosts=http://5bmpx76qllutpcyp.onion, http://xc3nh4juf2hshy7e.onion, http://44mgyoe2b6oqiytt.onion, http://62nvujg5iou3vu3i.onion, http://ceaanhbvluug4we6.onion + +#MarketStats Metric +MarketStats.enabled=true +MarketStats.run.interval=191 + ## Reporters are configured via a set of properties as well. ## ## In contrast to Metrics, Reporters do not have a minimal set of properties. #GraphiteReporter -GraphiteReporter.serviceUrl=http://yourHiddenService.onion:2003 +GraphiteReporter.serviceUrl=k6evlhg44acpchtc.onion:2003 + +``` + +## Run + +The distribution ships with a systemd .desktop file. Validate/change the executable/config paths within the shipped `bisq-monitor.service` file and copy/move the file to your systemd directory (something along `/usr/lib/systemd/system/`). Now you can control your *Monitor Node* via the usual systemd start/stop commands + +``` +systemctl start bisq-monitor.service +systemctl stop bisq-monitor.service +``` +and +``` +systemctl enable bisq-monitor.service +``` + +You can reload the configuration without restarting the service by using + +``` +systemctl reload bisq-monitor.service +``` + +Follow the logs created by the service by inspecting + +``` +journalctl --unit bisq-monitor --follow +``` + +# Monitoring Service + +A typical monitoring service consists of a [Graphite](https://graphiteapp.org/) and a [Grafana](https://grafana.com/) instance. +Both are available via Docker-containers. + +## Setting up Graphite + +### Install + +For a docker setup, use + +``` +docker run -d --name graphite --restart=always -p 2003:2003 -p 8080:8080 graphiteapp/graphite-statsd +``` + +- Port 2003 is used for the [plaintext protocol](https://graphite.readthedocs.io/en/latest/feeding-carbon.html#the-plaintext-protocol) mentioned above +- Port 8080 offers an API for user interfaces. + +more information can be found [here](https://graphite.readthedocs.io/en/latest/install.html) + +### Configuration + +For configuration, you must adapt the whisper database schema to suit your needs. First, stop your docker container by running + +``` +docker stop graphite ``` + +Find your config files within the `Source` directory stated in + +``` +docker inspect graphite | grep -C 2 graphite/conf\", +``` + +Edit `storage-schemas.conf` so that the frequency of your incoming data (configured in the monitor configs `interval`) is matched. For example, insert +``` +[bisq] +pattern = ^bisq.* +retentions = 10s:1h,5m:31d,30m:2y,1h:5y +``` +before the `[default...` blocks of the file. This basically says, that every incoming set of data reflects 5 minutes of the time series. Furthermore, every 30 minutes, the data is compressed and thus, takes less memory as it is kept for 2 years. + +Further, edit `storage-aggregation.conf` to configure how your data is compressed. For example, insert +``` +[bisq] +pattern=^bisq.* +xFilesFactor = 0 +aggregationMethod = average +``` +before the `[default...` blocks of the file. With this configuration, whenever data is aggregated, the `average` data is made available given that at least `0%` of the data points (i.e. floor(30 / 5 * 40%) = 2 data points) exist. Otherwise, the aggregated data is dropped. Since we start the first hour with a frequency of 10s but only supply data every 4 to 6 minutes, our aggregated values would get dropped. + +*Please note, that I have not been able to get the whole thing to work without the 10s:1h part yet* + +Finally, update the database. For doing that, go to the storage directory of graphite, the `Source` directory stated in +``` +docker inspect graphite | grep -C 2 graphite/conf\", +``` +Once there, you have two options: +- delete the whisper directory +``` +rm -r whisper +``` +- update the database by doing +``` +find ./ -type f -name '*.wsp' -exec whisper-resize.py --nobackup {} 10s:1h 5m:31d 30m:2y 1h:5y \; +``` + +and finally, restart your graphite container: +``` +docker start graphite +``` + + +Other than that, there is no further configuration necessary. However, you might change your iptables/firewalls to not let anyone access your Graphite instance from the outside. + +### Backup your data + +The metric data is kept in the `Source` directory stated in +``` +docker inspect graphite | grep -C 2 graphite/conf\", +``` +ready to be backed up regularly. + +## Setting up Grafana + +### Install + +For a docker setup, use + +``` +docker run -d --name=grafana -p 3000:3000 grafana/grafana +``` + +- Port 3000 offers the web interface + +more information can be found [here](https://grafana.com/grafana/download?platform=docker) + +### Configuration + +- Once you have Grafana up and running, go to the *Data Source* configuration tab. +- Once there click *Add data source* and select *Graphite*. +- In the HTTP section enter the IP address of your graphite docker container and the port `8080` (as we have configured before). E.g. `http://172.170.1:8080` +- Select `Server (default)` as an *Access* method and hit *Save & Test*. + +You should be all set. You can now proceed to add Dashboards, Panels and finally display the prettiest Graphs you can think of. +A working connection to Graphite should let you add your data series in a *Graph*s *Metrics* tab in a pretty intuitive way. + +- Optional: hide your Grafana instance behind a reverse proxy like nginx and add some TLS. +- Optional: make your Grafana instance accessible via a Tor hidden service. + +### Backup your data + +Grafana stores every dashboard as a JSON model. This model can be accessed (copied/restored) within the dashboards settings and its *JSON Model* tab. Do with the data whatever you want. diff --git a/monitor/bisq-monitor.service b/monitor/bisq-monitor.service new file mode 100644 index 00000000000..892c69ae53b --- /dev/null +++ b/monitor/bisq-monitor.service @@ -0,0 +1,16 @@ +[Unit] +Description=Bisq network monitor +After=network.target + +[Service] +WorkingDirectory=~ +Environment="JAVA_OPTS='-Xmx500M'" +ExecStart=/home/bisq/bisq/bisq-monitor /home/bisq/monitor.properties +ExecReload=/bin/kill -USR1 $MAINPID +Restart=on-failure + +User=bisq +Group=bisq + +[Install] +WantedBy=multi-user.target diff --git a/monitor/src/main/java/bisq/monitor/AvailableTor.java b/monitor/src/main/java/bisq/monitor/AvailableTor.java index bd76620c40a..650425fca52 100644 --- a/monitor/src/main/java/bisq/monitor/AvailableTor.java +++ b/monitor/src/main/java/bisq/monitor/AvailableTor.java @@ -29,7 +29,7 @@ */ public class AvailableTor extends TorMode { - private String hiddenServiceDirectory; + private final String hiddenServiceDirectory; public AvailableTor(File torWorkingDirectory, String hiddenServiceDirectory) { super(torWorkingDirectory); diff --git a/monitor/src/main/java/bisq/monitor/Configurable.java b/monitor/src/main/java/bisq/monitor/Configurable.java index 43212c0d68e..6280f02255b 100644 --- a/monitor/src/main/java/bisq/monitor/Configurable.java +++ b/monitor/src/main/java/bisq/monitor/Configurable.java @@ -66,7 +66,7 @@ protected String getName() { * Set the name used to filter through configuration properties. See * {@link Configurable#configure(Properties)}. * - * @param name + * @param name the name of the configurable */ protected void setName(String name) { this.name = name; diff --git a/monitor/src/main/java/bisq/monitor/Metric.java b/monitor/src/main/java/bisq/monitor/Metric.java index 1417cb29dbc..7ef00f9a397 100644 --- a/monitor/src/main/java/bisq/monitor/Metric.java +++ b/monitor/src/main/java/bisq/monitor/Metric.java @@ -17,10 +17,21 @@ package bisq.monitor; +import bisq.common.app.Capabilities; +import bisq.common.app.Version; + +import java.util.Arrays; import java.util.Properties; +import java.util.Random; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; +import static bisq.core.btc.BtcOptionKeys.BASE_CURRENCY_NETWORK; + /** * Starts a Metric (in its own {@link Thread}), manages its properties and shuts * it down gracefully. Furthermore, configuration updates and execution are done @@ -33,36 +44,24 @@ public abstract class Metric extends Configurable implements Runnable { private static final String INTERVAL = "run.interval"; - private volatile boolean shutdown = false; - - /** - * our reporter - */ + private static ScheduledExecutorService executor; protected final Reporter reporter; - private Thread thread = new Thread(); + private ScheduledFuture scheduler; /** * disable execution */ private void disable() { - shutdown = true; + if (scheduler != null) + scheduler.cancel(false); } /** * enable execution */ private void enable() { - shutdown = false; - - thread = new Thread(this); - - // set human readable name - thread.setName(getName()); - - // set as daemon, so that the jvm does not terminate the thread - thread.setDaemon(true); - - thread.start(); + scheduler = executor.scheduleWithFixedDelay(this, new Random().nextInt(60), + Long.parseLong(configuration.getProperty(INTERVAL)), TimeUnit.SECONDS); } /** @@ -74,12 +73,16 @@ protected Metric(Reporter reporter) { setName(this.getClass().getSimpleName()); - // disable by default - disable(); + if (executor == null) { + executor = new ScheduledThreadPoolExecutor(6); + } } boolean enabled() { - return !shutdown; + if (scheduler != null) + return !scheduler.isCancelled(); + else + return false; } @Override @@ -89,6 +92,8 @@ public void configure(final Properties properties) { super.configure(properties); reporter.configure(properties); + Version.setBaseCryptoNetworkId(Integer.parseInt(properties.getProperty("System." + BASE_CURRENCY_NETWORK, "1"))); // defaults to BTC_TESTNET + // decide whether to enable or disable the task if (configuration.isEmpty() || !configuration.getProperty("enabled", "false").equals("true") || !configuration.containsKey(INTERVAL)) { @@ -114,30 +119,14 @@ else if (!configuration.containsKey(INTERVAL)) @Override public void run() { - while (!shutdown) { - // if not, execute all the things - synchronized (this) { - log.info("{} started", getName()); - execute(); - log.info("{} done", getName()); - } - - if (shutdown) - continue; + Thread.currentThread().setName("Metric: " + getName()); - // make sure our configuration is not changed in the moment we want to query it - String interval; - synchronized (this) { - interval = configuration.getProperty(INTERVAL); - } - - // and go to sleep for the configured amount of time. - try { - Thread.sleep(Long.parseLong(interval) * 1000); - } catch (InterruptedException ignore) { - } + // execute all the things + synchronized (this) { + log.info("{} started", getName()); + execute(); + log.info("{} done", getName()); } - log.info("{} shutdown", getName()); } /** @@ -146,15 +135,17 @@ public void run() { protected abstract void execute(); /** - * Initiate graceful shutdown of the Metric. + * initiate an orderly shutdown on all metrics. Blocks until all metrics are + * shut down or after one minute. */ - public void shutdown() { - log.debug("{} shutdown requested", getName()); - shutdown = true; - } - - void join() throws InterruptedException { - thread.join(); + public static void haltAllMetrics() { + executor.shutdown(); + + try { + if (!Metric.executor.awaitTermination(2, TimeUnit.MINUTES)) + Metric.executor.shutdownNow(); + } catch (InterruptedException e) { + Metric.executor.shutdownNow(); + } } - } diff --git a/monitor/src/main/java/bisq/monitor/Monitor.java b/monitor/src/main/java/bisq/monitor/Monitor.java index 33fb9d06e31..162a5e05952 100644 --- a/monitor/src/main/java/bisq/monitor/Monitor.java +++ b/monitor/src/main/java/bisq/monitor/Monitor.java @@ -17,12 +17,16 @@ package bisq.monitor; +import bisq.monitor.metric.MarketStats; +import bisq.monitor.metric.P2PMarketStats; import bisq.monitor.metric.P2PNetworkLoad; -import bisq.monitor.metric.P2PNetworkMessageSnapshot; +import bisq.monitor.metric.P2PSeedNodeSnapshot; import bisq.monitor.metric.P2PRoundTripTime; +import bisq.monitor.metric.PriceNodeStats; import bisq.monitor.metric.TorHiddenServiceStartupTime; import bisq.monitor.metric.TorRoundTripTime; import bisq.monitor.metric.TorStartupTime; +import bisq.monitor.reporter.ConsoleReporter; import bisq.monitor.reporter.GraphiteReporter; import org.berndpruenster.netlayer.tor.NativeTor; @@ -51,7 +55,7 @@ @Slf4j public class Monitor { - public static final File TOR_WORKING_DIR = new File("monitor/monitor-tor"); + public static final File TOR_WORKING_DIR = new File("monitor/work/monitor-tor"); private static String[] args = {}; public static void main(String[] args) throws Throwable { @@ -76,26 +80,38 @@ private void start() throws Throwable { // assemble Metrics // - create reporters -// ConsoleReporter consoleReporter = new ConsoleReporter(); Reporter graphiteReporter = new GraphiteReporter(); + // only use ConsoleReporter if requested (for debugging for example) + Properties properties = getProperties(); + if ("true".equals(properties.getProperty("System.useConsoleReporter", "false"))) + graphiteReporter = new ConsoleReporter(); + // - add available metrics with their reporters metrics.add(new TorStartupTime(graphiteReporter)); metrics.add(new TorRoundTripTime(graphiteReporter)); metrics.add(new TorHiddenServiceStartupTime(graphiteReporter)); metrics.add(new P2PRoundTripTime(graphiteReporter)); metrics.add(new P2PNetworkLoad(graphiteReporter)); - metrics.add(new P2PNetworkMessageSnapshot(graphiteReporter)); + metrics.add(new P2PSeedNodeSnapshot(graphiteReporter)); + metrics.add(new P2PMarketStats(graphiteReporter)); + metrics.add(new PriceNodeStats(graphiteReporter)); + metrics.add(new MarketStats(graphiteReporter)); // prepare configuration reload // Note that this is most likely only work on Linux - Signal.handle(new Signal("USR1"), signal -> reload()); + Signal.handle(new Signal("USR1"), signal -> { + try { + configure(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + }); // configure Metrics // - which also starts the metrics if appropriate - Properties properties = getProperties(); - for (Metric current : metrics) - current.configure(properties); + configure(); // exit Metrics gracefully on shutdown Runtime.getRuntime().addShutdownHook(new Thread() { @@ -104,43 +120,33 @@ public void run() { // set the name of the Thread for debugging purposes setName("shutdownHook"); - for (Metric current : metrics) { - current.shutdown(); - } + log.info("system shutdown initiated"); - // wait for the metrics to gracefully shut down - for (Metric current : metrics) - try { - current.join(); - } catch (InterruptedException ignore) { - } + log.info("shutting down active metrics..."); + Metric.haltAllMetrics(); - log.info("shutting down tor"); - Tor tor = Tor.getDefault(); - checkNotNull(tor, "tor must not be null"); - tor.shutdown(); + try { + log.info("shutting down tor..."); + Tor tor = Tor.getDefault(); + checkNotNull(tor, "tor must not be null"); + tor.shutdown(); + } catch (Throwable ignore) { + } log.info("system halt"); } }); - - // prevent the main thread to terminate - log.info("joining metrics..."); - for (Metric current : metrics) - current.join(); } /** * Reload the configuration from disk. + * + * @throws Exception if something goes wrong */ - private void reload() { - try { - Properties properties = getProperties(); - for (Metric current : metrics) - current.configure(properties); - } catch (Exception e) { - e.printStackTrace(); - } + private void configure() throws Exception { + Properties properties = getProperties(); + for (Metric current : metrics) + current.configure(properties); } /** @@ -150,13 +156,14 @@ private void reload() { * @throws Exception in case something goes wrong */ private Properties getProperties() throws Exception { - Properties defaults = new Properties(); - defaults.load(Monitor.class.getClassLoader().getResourceAsStream("metrics.properties")); - - Properties result = new Properties(defaults); + Properties result = new Properties(); + // if we have a config file load the config file, else, load the default config + // from the resources if (args.length > 0) result.load(new FileInputStream(args[0])); + else + result.load(Monitor.class.getClassLoader().getResourceAsStream("metrics.properties")); return result; } diff --git a/monitor/src/main/java/bisq/monitor/OnionParser.java b/monitor/src/main/java/bisq/monitor/OnionParser.java index 53e1e5790db..e5a984446fd 100644 --- a/monitor/src/main/java/bisq/monitor/OnionParser.java +++ b/monitor/src/main/java/bisq/monitor/OnionParser.java @@ -34,7 +34,7 @@ public static NodeAddress getNodeAddress(final String current) throws MalformedU if (!nodeAddress.startsWith("http://")) nodeAddress = "http://" + nodeAddress; URL tmp = new URL(nodeAddress); - return new NodeAddress(tmp.getHost(), tmp.getPort()); + return new NodeAddress(tmp.getHost(), tmp.getPort() > 0 ? tmp.getPort() : 80); } public static String prettyPrint(final NodeAddress host) { diff --git a/monitor/src/main/java/bisq/monitor/Reporter.java b/monitor/src/main/java/bisq/monitor/Reporter.java index b38042353bc..5c0202e7f38 100644 --- a/monitor/src/main/java/bisq/monitor/Reporter.java +++ b/monitor/src/main/java/bisq/monitor/Reporter.java @@ -34,15 +34,15 @@ protected Reporter() { /** * Report our findings. * - * @param value + * @param value the value to report */ public abstract void report(long value); /** * Report our findings * - * @param value - * @param prefix + * @param value the value to report + * @param prefix a common prefix to be included in the tag name */ public abstract void report(long value, String prefix); @@ -57,8 +57,18 @@ protected Reporter() { * Report our findings. * * @param values Map - * @param prefix for example "bisq.torStartupTime" + * @param prefix for example "torStartupTime" */ public abstract void report(Map values, String prefix); + /** + * Report our findings one by one. + * + * @param key the metric name + * @param value the value to report + * @param timestamp a unix timestamp in milliseconds + * @param prefix for example "torStartupTime" + */ + public abstract void report(String key, String value, String timestamp, String prefix); + } diff --git a/monitor/src/main/java/bisq/monitor/StatisticsHelper.java b/monitor/src/main/java/bisq/monitor/StatisticsHelper.java index e16c0c84d6b..90064f22781 100644 --- a/monitor/src/main/java/bisq/monitor/StatisticsHelper.java +++ b/monitor/src/main/java/bisq/monitor/StatisticsHelper.java @@ -48,7 +48,7 @@ public static Map process(List samples) { // - p25, median, p75 Integer[] percentiles = new Integer[] { 25, 50, 75 }; for (Integer percentile : percentiles) { - double rank = statistics.getCount() * percentile / 100; + double rank = statistics.getCount() * percentile / 100.0; Long percentileValue; if (samples.size() <= rank + 1) percentileValue = samples.get(samples.size() - 1); diff --git a/monitor/src/main/java/bisq/monitor/metric/MarketStats.java b/monitor/src/main/java/bisq/monitor/metric/MarketStats.java new file mode 100644 index 00000000000..acee92f92fd --- /dev/null +++ b/monitor/src/main/java/bisq/monitor/metric/MarketStats.java @@ -0,0 +1,116 @@ +/* + * 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.monitor.metric; + +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; +import java.util.regex.Pattern; + +import lombok.extern.slf4j.Slf4j; + +/** + * Uses the markets API to retrieve market volume data. + * + * @author Florian Reimair + * + */ +@Slf4j +public class MarketStats extends Metric { + + // poor mans JSON parser + private final Pattern marketPattern = Pattern.compile("\"market\" ?: ?\"([a-z_]+)\""); + private final Pattern amountPattern = Pattern.compile("\"amount\" ?: ?\"([\\d\\.]+)\""); + private final Pattern volumePattern = Pattern.compile("\"volume\" ?: ?\"([\\d\\.]+)\""); + private final Pattern timestampPattern = Pattern.compile("\"trade_date\" ?: ?([\\d]+)"); + + private final String marketApi = "https://markets.bisq.network"; + private Long lastRun = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(15)); + + public MarketStats(Reporter reporter) { + super(reporter); + } + + @Override + protected void execute() { + try { + // for each configured host + Map result = new HashMap<>(); + + // assemble query + Long now = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); + String query = "/api/trades?format=json&market=all×tamp_from=" + lastRun + "×tamp_to=" + now; + lastRun = now; // thought about adding 1 second but what if a trade is done exactly in this one second? + + // connect + URLConnection connection = new URL(marketApi + query).openConnection(); + + // prepare to receive data + BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + + String all = in.readLine(); + in.close(); + + Arrays.stream(all.substring(0, all.length() - 2).split("}")).forEach(trade -> { + Matcher market = marketPattern.matcher(trade); + Matcher amount = amountPattern.matcher(trade); + Matcher timestamp = timestampPattern.matcher(trade); + market.find(); + if (market.group(1).endsWith("btc")) { + amount = volumePattern.matcher(trade); + } + amount.find(); + timestamp.find(); + System.err.println(getName() + ".volume." + market.group(1) + " " + amount.group(1) + " " + timestamp.group(1).substring(0, timestamp.group(1).length() - 3)); + reporter.report("volume." + market.group(1), amount.group(1), timestamp.group(1), getName()); + }); + } 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/P2PMarketStats.java b/monitor/src/main/java/bisq/monitor/metric/P2PMarketStats.java new file mode 100644 index 00000000000..64b246a398e --- /dev/null +++ b/monitor/src/main/java/bisq/monitor/metric/P2PMarketStats.java @@ -0,0 +1,100 @@ +/* + * 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.monitor.metric; + +import bisq.monitor.OnionParser; +import bisq.monitor.Reporter; + +import bisq.core.offer.OfferPayload; + +import bisq.network.p2p.storage.payload.ProtectedStoragePayload; + +import java.util.HashMap; +import java.util.Map; + +import lombok.extern.slf4j.Slf4j; + +/** + * Demo Stats metric derived from the OfferPayload messages we get from the seed nodes + * + * @author Florian Reimair + */ +@Slf4j +public class P2PMarketStats extends P2PSeedNodeSnapshot { + + /** + * Efficient way to count occurrences. + */ + private class Counter { + private long value = 0; + + synchronized long value() { + return value; + } + + synchronized void increment() { + value++; + } + } + + private class MyStatistics implements Statistics { + private final Map buckets = new HashMap<>(); + + @Override + public Statistics create() { + return new MyStatistics(); + } + + @Override + public synchronized void log(ProtectedStoragePayload message) { + + if(message instanceof OfferPayload) { + OfferPayload currentMessage = (OfferPayload) message; + // For logging different data types + String market = currentMessage.getDirection() + "." + currentMessage.getBaseCurrencyCode() + "_" + currentMessage.getCounterCurrencyCode(); + + buckets.putIfAbsent(market, new Counter()); + buckets.get(market).increment(); + } + } + + @Override + public Map values() { + return buckets; + } + + @Override + public void reset() { + buckets.clear(); + } + } + + public P2PMarketStats(Reporter graphiteReporter) { + super(graphiteReporter); + + statistics = new MyStatistics(); + } + + @Override + protected void report() { + Map report = new HashMap<>(); + bucketsPerHost.forEach((host, statistics) -> statistics.values().forEach((market, numberOfOffers) -> report.put(OnionParser.prettyPrint(host) + "." + market.toString(), String.valueOf(((Counter) numberOfOffers).value())))); + + reporter.report(report, getName()); + } +} diff --git a/monitor/src/main/java/bisq/monitor/metric/P2PNetworkLoad.java b/monitor/src/main/java/bisq/monitor/metric/P2PNetworkLoad.java index 4c6a171515b..32475bd0765 100644 --- a/monitor/src/main/java/bisq/monitor/metric/P2PNetworkLoad.java +++ b/monitor/src/main/java/bisq/monitor/metric/P2PNetworkLoad.java @@ -17,46 +17,45 @@ package bisq.monitor.metric; -import static com.google.common.base.Preconditions.checkNotNull; - import java.io.File; -import java.net.MalformedURLException; -import java.util.ArrayList; + +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.Random; +import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.jetbrains.annotations.NotNull; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.SettableFuture; +import org.springframework.core.env.PropertySource; -import bisq.common.app.Version; +import bisq.common.Clock; +import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.proto.network.NetworkEnvelope; +import bisq.common.proto.network.NetworkProtoResolver; +import bisq.core.app.BisqEnvironment; +import bisq.core.btc.BaseCurrencyNetwork; +import bisq.core.btc.BtcOptionKeys; +import bisq.core.network.p2p.seed.DefaultSeedNodeRepository; import bisq.core.proto.network.CoreNetworkProtoResolver; +import bisq.core.proto.persistable.CorePersistenceProtoResolver; import bisq.monitor.AvailableTor; import bisq.monitor.Metric; import bisq.monitor.Monitor; -import bisq.monitor.OnionParser; import bisq.monitor.Reporter; import bisq.monitor.ThreadGate; -import bisq.network.p2p.CloseConnectionMessage; -import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.Connection; import bisq.network.p2p.network.MessageListener; import bisq.network.p2p.network.NetworkNode; import bisq.network.p2p.network.SetupListener; import bisq.network.p2p.network.TorNetworkNode; -import bisq.network.p2p.peers.getdata.messages.GetDataResponse; -import bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest; -import bisq.network.p2p.storage.P2PDataStorage; -import bisq.network.p2p.storage.payload.PersistableNetworkPayload; -import bisq.network.p2p.storage.payload.ProtectedStorageEntry; -import bisq.network.p2p.storage.payload.ProtectedStoragePayload; +import bisq.network.p2p.peers.PeerManager; +import bisq.network.p2p.peers.keepalive.KeepAliveManager; +import bisq.network.p2p.peers.peerexchange.PeerExchangeManager; +import bisq.network.p2p.storage.messages.BroadcastMessage; import lombok.extern.slf4j.Slf4j; /** @@ -71,39 +70,80 @@ @Slf4j public class P2PNetworkLoad extends Metric implements MessageListener, SetupListener { - private static final String HOSTS = "run.hosts"; private static final String TOR_PROXY_PORT = "run.torProxyPort"; + private static final String MAX_CONNECTIONS = "run.maxConnections"; + private static final String HISTORY_SIZE = "run.historySize"; private NetworkNode networkNode; - private final File torHiddenServiceDir = new File("metric_p2pNetworkLoad"); - private int nonce; - private Map> bucketsPerHost = new ConcurrentHashMap<>(); - private Set hashes = new HashSet<>(); + private final File torHiddenServiceDir = new File("metric_" + getName()); private final ThreadGate hsReady = new ThreadGate(); - private final ThreadGate gate = new ThreadGate(); + private final Map buckets = new ConcurrentHashMap<>(); + + /** + * Buffers the last X message we received. New messages will only be logged in case + * the message isn't already in the history. Note that the oldest message hashes are + * dropped to record newer hashes. + */ + private Map history; + private long lastRun = 0; + + /** + * History implementation using a {@link LinkedHashMap} and its + * {@link LinkedHashMap#removeEldestEntry(Map.Entry)} option. + */ + private class FixedSizeHistoryTracker extends LinkedHashMap { + final int historySize; + + FixedSizeHistoryTracker(int historySize) { + super(historySize, 10, true); + this.historySize = historySize; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > historySize; + } + } /** * Efficient way to count message occurrences. */ private class Counter { - private int value = 0; - - int value() { - return value; + private int value = 1; + + /** + * atomic get and reset + * + * @return the current value + */ + synchronized int getAndReset() { + try { + return value; + } finally { + value = 0; + } } - void increment() { + synchronized void increment() { value++; } } public P2PNetworkLoad(Reporter reporter) { super(reporter); + } - Version.setBaseCryptoNetworkId(0); // set to BTC_MAINNET + @Override + public void configure(Properties properties) { + super.configure(properties); + + history = Collections.synchronizedMap(new FixedSizeHistoryTracker(Integer.parseInt(configuration.getProperty(HISTORY_SIZE, "200")))); + + Capabilities.app.addAll(Capability.DAO_FULL_NODE); } @Override protected void execute() { + // in case we do not have a NetworkNode up and running, we create one if (null == networkNode) { // prepare the gate @@ -117,145 +157,84 @@ protected void execute() { // wait for the HS to be published hsReady.await(); - } - // clear our buckets - bucketsPerHost.clear(); - ArrayList threadList = new ArrayList<>(); - - // for each configured host - for (String current : configuration.getProperty(HOSTS, "").split(",")) { - threadList.add(new Thread(() -> { - try { - // parse Url - NodeAddress target = OnionParser.getNodeAddress(current); - - // do the data request - nonce = new Random().nextInt(); - SettableFuture future = networkNode.sendMessage(target, - new PreliminaryGetDataRequest(nonce, hashes)); - - Futures.addCallback(future, new FutureCallback<>() { - @Override - public void onSuccess(Connection connection) { - connection.addMessageListener(P2PNetworkLoad.this); - log.debug("Send PreliminaryDataRequest to " + connection + " succeeded."); - } - - @Override - public void onFailure(@NotNull Throwable throwable) { - gate.proceed(); - log.error( - "Sending PreliminaryDataRequest failed. That is expected if the peer is offline.\n\tException=" - + throwable.getMessage()); - } - }); - - } catch (Exception e) { - gate.proceed(); // release the gate on error - e.printStackTrace(); + // boot up P2P node + File storageDir = torHiddenServiceDir; + try { + BisqEnvironment environment = new BisqEnvironment(new PropertySource("name") { + + @Override + public String getProperty(String name) { + if(BtcOptionKeys.BASE_CURRENCY_NETWORK.equals(name)) + return BaseCurrencyNetwork.BTC_MAINNET.name(); + return ""; } - }, current)); + }); + int maxConnections = Integer.parseInt(configuration.getProperty(MAX_CONNECTIONS, "12")); + NetworkProtoResolver networkProtoResolver = new CoreNetworkProtoResolver(); + CorePersistenceProtoResolver persistenceProtoResolver = new CorePersistenceProtoResolver(null, + networkProtoResolver, storageDir); + DefaultSeedNodeRepository seedNodeRepository = new DefaultSeedNodeRepository(environment, null); + PeerManager peerManager = new PeerManager(networkNode, seedNodeRepository, new Clock(), + persistenceProtoResolver, maxConnections, storageDir); + + // init file storage + peerManager.readPersisted(); + + PeerExchangeManager peerExchangeManager = new PeerExchangeManager(networkNode, seedNodeRepository, + peerManager); + // updates the peer list every now and then as well + peerExchangeManager + .requestReportedPeersFromSeedNodes(seedNodeRepository.getSeedNodeAddresses().iterator().next()); + + KeepAliveManager keepAliveManager = new KeepAliveManager(networkNode, peerManager); + keepAliveManager.start(); + + networkNode.addMessageListener(this); + } catch (Throwable e) { + e.printStackTrace(); + } } - gate.engage(threadList.size()); - - // start all threads and wait until they all finished. We do that so we can - // minimize the time between querying the hosts and therefore the chance of - // inconsistencies. - threadList.forEach(Thread::start); - gate.await(); - // report Map report = new HashMap<>(); - // - assemble histograms - bucketsPerHost.forEach((host, buckets) -> buckets.forEach((type, counter) -> report - .put(OnionParser.prettyPrint(host) + "." + type, String.valueOf(counter.value())))); - - // - assemble diffs - Map messagesPerHost = new HashMap<>(); - bucketsPerHost.forEach((host, buckets) -> messagesPerHost.put(OnionParser.prettyPrint(host), - buckets.values().stream().mapToInt(Counter::value).sum())); - Optional referenceHost = messagesPerHost.keySet().stream().sorted().findFirst(); - Integer referenceValue = messagesPerHost.get(referenceHost.get()); - - messagesPerHost.forEach( - (host, numberOfMessages) -> { - try { - report.put(OnionParser.prettyPrint(host) + ".relativeNumberOfMessages", - String.valueOf(numberOfMessages - referenceValue)); - report.put(OnionParser.prettyPrint(host) + ".referenceHost", referenceHost.get()); - report.put(OnionParser.prettyPrint(host) + ".referenceValue", String.valueOf(referenceValue)); - } catch (MalformedURLException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - }); - - // when our hash cache exceeds a hard limit, we clear the cache and start anew - if (hashes.size() > 150000) - hashes.clear(); - // in case we just started anew, do not report our findings as they contain not - // only the changes since our last run, but a whole lot more data dating back even - // to the beginning of bisq. - if (!hashes.isEmpty()) - reporter.report(report, "bisq." + getName()); - } + if(lastRun != 0 && System.currentTimeMillis() - lastRun != 0) { + // - normalize to data/minute + double perMinuteFactor = 60000.0 / (System.currentTimeMillis() - lastRun); - @Override - public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { - if (networkEnvelope instanceof GetDataResponse) { + // - get snapshot so we do not loose data + Set keys = new HashSet<>(buckets.keySet()); - GetDataResponse dataResponse = (GetDataResponse) networkEnvelope; - Map buckets = new HashMap<>(); - final Set dataSet = dataResponse.getDataSet(); - dataSet.forEach(e -> { - final ProtectedStoragePayload protectedStoragePayload = e.getProtectedStoragePayload(); - if (protectedStoragePayload == null) { - log.warn("StoragePayload was null: {}", networkEnvelope.toString()); - return; - } - - // memorize message hashes - hashes.add(P2PDataStorage.get32ByteHash(protectedStoragePayload)); - - // For logging different data types - String className = protectedStoragePayload.getClass().getSimpleName(); - try { - buckets.get(className).increment(); - } catch (NullPointerException nullPointerException) { - buckets.put(className, new Counter()); + // - transfer values to report + keys.forEach(key -> { + int value = buckets.get(key).getAndReset(); + if (value != 0) { + report.put(key, String.format("%.2f", value * perMinuteFactor)); } }); - Set persistableNetworkPayloadSet = dataResponse - .getPersistableNetworkPayloadSet(); - if (persistableNetworkPayloadSet != null) { - persistableNetworkPayloadSet.forEach(persistableNetworkPayload -> { + // - report + reporter.report(report, getName()); + } - // memorize message hashes - hashes.add(persistableNetworkPayload.getHash()); + // - reset last run + lastRun = System.currentTimeMillis(); + } - // For logging different data types - String className = persistableNetworkPayload.getClass().getSimpleName(); - buckets.putIfAbsent(className, new Counter()); - buckets.get(className).increment(); - }); + @Override + public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { + if (networkEnvelope instanceof BroadcastMessage) { + try { + if(history.get(networkEnvelope.hashCode()) == null) { + history.put(networkEnvelope.hashCode(), null); + buckets.get(networkEnvelope.getClass().getSimpleName()).increment(); + } + } catch (NullPointerException e) { + // use exception handling because we hardly ever need to add a fresh bucket + buckets.put(networkEnvelope.getClass().getSimpleName(), new Counter()); } - - checkNotNull(connection.peersNodeAddressProperty(), - "although the property is nullable, we need it to not be null"); - bucketsPerHost.put(connection.peersNodeAddressProperty().getValue(), buckets); - - connection.removeMessageListener(this); - gate.proceed(); - } else if (networkEnvelope instanceof CloseConnectionMessage) { - gate.unlock(); - } else { - log.warn("Got a message of type <{}>, expected ", - networkEnvelope.getClass().getSimpleName()); } } diff --git a/monitor/src/main/java/bisq/monitor/metric/P2PRoundTripTime.java b/monitor/src/main/java/bisq/monitor/metric/P2PRoundTripTime.java index 50915add5b8..0cd7ff75951 100644 --- a/monitor/src/main/java/bisq/monitor/metric/P2PRoundTripTime.java +++ b/monitor/src/main/java/bisq/monitor/metric/P2PRoundTripTime.java @@ -17,20 +17,6 @@ package bisq.monitor.metric; -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; -import java.util.Random; - -import org.jetbrains.annotations.NotNull; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.SettableFuture; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; -import bisq.core.proto.network.CoreNetworkProtoResolver; import bisq.monitor.AvailableTor; import bisq.monitor.Metric; import bisq.monitor.Monitor; @@ -38,6 +24,9 @@ import bisq.monitor.Reporter; import bisq.monitor.StatisticsHelper; import bisq.monitor.ThreadGate; + +import bisq.core.proto.network.CoreNetworkProtoResolver; + import bisq.network.p2p.CloseConnectionMessage; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.CloseConnectionReason; @@ -48,8 +37,23 @@ import bisq.network.p2p.network.TorNetworkNode; import bisq.network.p2p.peers.keepalive.messages.Ping; import bisq.network.p2p.peers.keepalive.messages.Pong; + +import bisq.common.proto.network.NetworkEnvelope; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.SettableFuture; + +import java.io.File; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + @Slf4j public class P2PRoundTripTime extends Metric implements MessageListener, SetupListener { @@ -57,7 +61,7 @@ public class P2PRoundTripTime extends Metric implements MessageListener, SetupLi private static final String HOSTS = "run.hosts"; private static final String TOR_PROXY_PORT = "run.torProxyPort"; private NetworkNode networkNode; - private final File torHiddenServiceDir = new File("metric_p2pRoundTripTime"); + private final File torHiddenServiceDir = new File("metric_" + getName()); private int nonce; private long start; private List samples; @@ -66,13 +70,6 @@ public class P2PRoundTripTime extends Metric implements MessageListener, SetupLi public P2PRoundTripTime(Reporter reporter) { super(reporter); - - Version.setBaseCryptoNetworkId(0); // set to BTC_MAINNET - } - - @Override - public void configure(Properties properties) { - super.configure(properties); } @Override @@ -91,7 +88,6 @@ protected void execute() { hsReady.await(); } - // for each configured host for (String current : configuration.getProperty(HOSTS, "").split(",")) { try { @@ -117,7 +113,6 @@ protected void execute() { @Override public void onSuccess(Connection connection) { connection.addMessageListener(P2PRoundTripTime.this); - log.debug("Send ping to " + connection + " succeeded."); } @Override @@ -130,11 +125,15 @@ public void onFailure(@NotNull Throwable throwable) { // wait for the gate to open again gate.await(); + + // remove the message listener so we do not get messages we are not interested in anymore + // (especially relevant when gate.await() times out) + future.get().removeMessageListener(this); } // report reporter.report(StatisticsHelper.process(samples), - "bisq." + getName() + "." + OnionParser.prettyPrint(target)); + getName() + "." + OnionParser.prettyPrint(target)); } catch (Exception e) { gate.proceed(); // release the gate on error e.printStackTrace(); @@ -153,7 +152,6 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { "We drop that message. nonce={} / requestNonce={}", nonce, pong.getRequestNonce()); } - connection.removeMessageListener(this); connection.shutDown(CloseConnectionReason.APP_SHUT_DOWN); // open the gate @@ -162,7 +160,6 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { gate.unlock(); } else { log.warn("Got a message of type <{}>, expected ", networkEnvelope.getClass().getSimpleName()); - connection.shutDown(CloseConnectionReason.APP_SHUT_DOWN); } } diff --git a/monitor/src/main/java/bisq/monitor/metric/P2PNetworkMessageSnapshot.java b/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java similarity index 59% rename from monitor/src/main/java/bisq/monitor/metric/P2PNetworkMessageSnapshot.java rename to monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java index f367a5c9f79..c213dd86f90 100644 --- a/monitor/src/main/java/bisq/monitor/metric/P2PNetworkMessageSnapshot.java +++ b/monitor/src/main/java/bisq/monitor/metric/P2PSeedNodeSnapshot.java @@ -17,67 +17,87 @@ package bisq.monitor.metric; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import org.jetbrains.annotations.NotNull; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.SettableFuture; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; -import bisq.core.proto.network.CoreNetworkProtoResolver; import bisq.monitor.AvailableTor; import bisq.monitor.Metric; import bisq.monitor.Monitor; import bisq.monitor.OnionParser; import bisq.monitor.Reporter; import bisq.monitor.ThreadGate; + +import bisq.core.proto.network.CoreNetworkProtoResolver; + import bisq.network.p2p.CloseConnectionMessage; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.CloseConnectionReason; import bisq.network.p2p.network.Connection; import bisq.network.p2p.network.MessageListener; import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.network.SetupListener; import bisq.network.p2p.network.TorNetworkNode; import bisq.network.p2p.peers.getdata.messages.GetDataResponse; import bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest; import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.network.p2p.storage.payload.ProtectedStorageEntry; import bisq.network.p2p.storage.payload.ProtectedStoragePayload; + +import bisq.common.proto.network.NetworkEnvelope; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.SettableFuture; + +import java.net.MalformedURLException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; + import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + +import static com.google.common.base.Preconditions.checkNotNull; + /** * Contacts a list of hosts and asks them for all the data excluding persisted messages. The * answers are then compiled into buckets of message types. Based on these * buckets, the Metric reports (for each host) the message types observed and * their number. - * + * * @author Florian Reimair * */ @Slf4j -public class P2PNetworkMessageSnapshot extends Metric implements MessageListener, SetupListener { +public class P2PSeedNodeSnapshot extends Metric implements MessageListener { private static final String HOSTS = "run.hosts"; private static final String TOR_PROXY_PORT = "run.torProxyPort"; - private NetworkNode networkNode; - private final File torHiddenServiceDir = new File("metric_p2pNetworkMessageStatus"); - private int nonce; - private Map> bucketsPerHost = new ConcurrentHashMap<>(); - private Set hashes = new HashSet<>(); - private final ThreadGate hsReady = new ThreadGate(); + Statistics statistics; + final Map bucketsPerHost = new ConcurrentHashMap<>(); + private final Set hashes = new TreeSet<>(Arrays::compare); private final ThreadGate gate = new ThreadGate(); + /** + * Statistics Interface for use with derived classes. + * + * @param the value type of the statistics implementation + */ + protected interface Statistics { + + Statistics create(); + + void log(ProtectedStoragePayload message); + + Map values(); + + void reset(); + } + /** * Efficient way to count message occurrences. */ @@ -93,28 +113,53 @@ void increment() { } } - public P2PNetworkMessageSnapshot(Reporter reporter) { + /** + * Use a counter to do statistics. + */ + private class MyStatistics implements Statistics { + + private final Map buckets = new HashMap<>(); + + @Override + public Statistics create() { + return new MyStatistics(); + } + + @Override + public synchronized void log(ProtectedStoragePayload message) { + + // For logging different data types + String className = message.getClass().getSimpleName(); + + buckets.putIfAbsent(className, new Counter()); + buckets.get(className).increment(); + } + + @Override + public Map values() { + return buckets; + } + + @Override + public synchronized void reset() { + buckets.clear(); + } + } + + public P2PSeedNodeSnapshot(Reporter reporter) { super(reporter); - Version.setBaseCryptoNetworkId(0); // set to BTC_MAINNET + statistics = new MyStatistics(); } @Override protected void execute() { - // in case we do not have a NetworkNode up and running, we create one - if (null == networkNode) { - // prepare the gate - hsReady.engage(); - - // start the network node - networkNode = new TorNetworkNode(Integer.parseInt(configuration.getProperty(TOR_PROXY_PORT, "9054")), - new CoreNetworkProtoResolver(), false, - new AvailableTor(Monitor.TOR_WORKING_DIR, torHiddenServiceDir.getName())); - networkNode.start(this); - - // wait for the HS to be published - hsReady.await(); - } + // start the network node + final NetworkNode networkNode = new TorNetworkNode(Integer.parseInt(configuration.getProperty(TOR_PROXY_PORT, "9054")), + new CoreNetworkProtoResolver(), false, + new AvailableTor(Monitor.TOR_WORKING_DIR, "unused")); + // we do not need to start the networkNode, as we do not need the HS + //networkNode.start(this); // clear our buckets bucketsPerHost.clear(); @@ -129,15 +174,13 @@ protected void execute() { NodeAddress target = OnionParser.getNodeAddress(current); // do the data request - nonce = new Random().nextInt(); SettableFuture future = networkNode.sendMessage(target, - new PreliminaryGetDataRequest(nonce, hashes)); + new PreliminaryGetDataRequest(new Random().nextInt(), hashes)); Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(Connection connection) { - connection.addMessageListener(P2PNetworkMessageSnapshot.this); - log.debug("Send PreliminaryDataRequest to " + connection + " succeeded."); + connection.addMessageListener(P2PSeedNodeSnapshot.this); } @Override @@ -165,27 +208,64 @@ public void onFailure(@NotNull Throwable throwable) { threadList.forEach(Thread::start); gate.await(); + report(); + } + + /** + * Report all the stuff. Uses the configured reporter directly. + */ + void report() { + // report Map report = new HashMap<>(); // - assemble histograms - bucketsPerHost.forEach((host, buckets) -> buckets.forEach((type, counter) -> report - .put(OnionParser.prettyPrint(host) + "." + type, String.valueOf(counter.value())))); + bucketsPerHost.forEach((host, statistics) -> statistics.values().forEach((type, counter) -> report + .put(OnionParser.prettyPrint(host) + ".numberOfMessages." + type, String.valueOf(((Counter) counter).value())))); + + // - assemble diffs + // - transfer values + Map messagesPerHost = new HashMap<>(); + bucketsPerHost.forEach((host, value) -> messagesPerHost.put(OnionParser.prettyPrint(host), value)); + + // - pick reference seed node and its values + Optional referenceHost = messagesPerHost.keySet().stream().sorted().findFirst(); + Map referenceValues = messagesPerHost.get(referenceHost.get()).values(); + + // - calculate diffs + messagesPerHost.forEach( + (host, statistics) -> { + statistics.values().forEach((messageType, count) -> { + try { + report.put(OnionParser.prettyPrint(host) + ".relativeNumberOfMessages." + messageType, + String.valueOf(((Counter) count).value() - referenceValues.get(messageType).value())); + } catch (MalformedURLException ignore) { + log.error("we should never got here"); + } + }); + try { + report.put(OnionParser.prettyPrint(host) + ".referenceHost", referenceHost.get()); + } catch (MalformedURLException ignore) { + log.error("we should never got here"); + } + }); + + // cleanup for next run + bucketsPerHost.forEach((host, statistics) -> statistics.reset()); // when our hash cache exceeds a hard limit, we clear the cache and start anew if (hashes.size() > 150000) hashes.clear(); - // report our findings iff we have not just started anew - reporter.report(report, "bisq." + getName()); + reporter.report(report, getName()); } @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof GetDataResponse) { + Statistics result = this.statistics.create(); GetDataResponse dataResponse = (GetDataResponse) networkEnvelope; - Map buckets = new HashMap<>(); final Set dataSet = dataResponse.getDataSet(); dataSet.forEach(e -> { final ProtectedStoragePayload protectedStoragePayload = e.getProtectedStoragePayload(); @@ -194,11 +274,7 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { return; } - // For logging different data types - String className = protectedStoragePayload.getClass().getSimpleName(); - - buckets.putIfAbsent(className, new Counter()); - buckets.get(className).increment(); + result.log(protectedStoragePayload); }); Set persistableNetworkPayloadSet = dataResponse @@ -207,15 +283,19 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { persistableNetworkPayloadSet.forEach(persistableNetworkPayload -> { // memorize message hashes + //Byte[] bytes = new Byte[persistableNetworkPayload.getHash().length]; + //Arrays.setAll(bytes, n -> persistableNetworkPayload.getHash()[n]); + + //hashes.add(bytes); + hashes.add(persistableNetworkPayload.getHash()); }); } - checkNotNull(connection.peersNodeAddressProperty(), + checkNotNull(connection.getPeersNodeAddressProperty(), "although the property is nullable, we need it to not be null"); - bucketsPerHost.put(connection.peersNodeAddressProperty().getValue(), buckets); + bucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), result); - connection.removeMessageListener(this); connection.shutDown(CloseConnectionReason.APP_SHUT_DOWN); gate.proceed(); } else if (networkEnvelope instanceof CloseConnectionMessage) { @@ -223,25 +303,6 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { } else { log.warn("Got a message of type <{}>, expected ", networkEnvelope.getClass().getSimpleName()); - connection.shutDown(CloseConnectionReason.APP_SHUT_DOWN); } } - - @Override - public void onTorNodeReady() { - } - - @Override - public void onHiddenServicePublished() { - // open the gate - hsReady.proceed(); - } - - @Override - public void onSetupFailed(Throwable throwable) { - } - - @Override - public void onRequestCustomBridges() { - } } diff --git a/monitor/src/main/java/bisq/monitor/metric/PriceNodeStats.java b/monitor/src/main/java/bisq/monitor/metric/PriceNodeStats.java new file mode 100644 index 00000000000..b9f3817c3ec --- /dev/null +++ b/monitor/src/main/java/bisq/monitor/metric/PriceNodeStats.java @@ -0,0 +1,156 @@ +/* + * 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.monitor.metric; + +import bisq.monitor.Metric; +import bisq.monitor.OnionParser; +import bisq.monitor.Reporter; + +import bisq.asset.Asset; +import bisq.asset.AssetRegistry; + +import bisq.network.p2p.NodeAddress; + +import org.berndpruenster.netlayer.tor.Tor; +import org.berndpruenster.netlayer.tor.TorCtlException; + +import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; +import com.runjva.sourceforge.jsocks.protocol.SocksSocket; + +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.regex.Matcher; +import java.util.regex.Pattern; + +import lombok.extern.slf4j.Slf4j; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Fetches fee and price data from the configured price nodes. + * Based on the work of HarryMcFinned. + * + * @author Florian Reimair + * @author HarryMcFinned + * + */ +@Slf4j +public class PriceNodeStats extends Metric { + + private static final String HOSTS = "run.hosts"; + private static final String IGNORE = "dashTxFee ltcTxFee dogeTxFee"; + // poor mans JSON parser + private final Pattern stringNumberPattern = Pattern.compile("\"(.+)\" ?: ?(\\d+)"); + private final Pattern pricePattern = Pattern.compile("\"price\" ?: ?([\\d.]+)"); + private final Pattern currencyCodePattern = Pattern.compile("\"currencyCode\" ?: ?\"([A-Z]+)\""); + private final List assets = Arrays.asList(new AssetRegistry().stream().map(Asset::getTickerSymbol).toArray()); + + public PriceNodeStats(Reporter reporter) { + super(reporter); + } + + @Override + protected void execute() { + try { + // fetch proxy + Tor tor = Tor.getDefault(); + checkNotNull(tor, "tor must not be null"); + Socks5Proxy proxy = tor.getProxy(); + + // for each configured host + for (String current : configuration.getProperty(HOSTS, "").split(",")) { + Map result = new HashMap<>(); + // parse Url + NodeAddress tmp = OnionParser.getNodeAddress(current); + + // connect + SocksSocket socket = new SocksSocket(proxy, tmp.getHostName(), tmp.getPort()); + + // prepare to receive data + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + // ask for fee data + PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))); + out.println("GET /getFees/"); + out.println(); + out.flush(); + + // sift through the received lines and see if we got something json-like + String line; + while((line = in.readLine()) != null) { + Matcher matcher = stringNumberPattern.matcher(line); + if(matcher.find()) + if(!IGNORE.contains(matcher.group(1))) + result.put("fees." + matcher.group(1), matcher.group(2)); + } + + in.close(); + out.close(); + socket.close(); + + // connect + socket = new SocksSocket(proxy, tmp.getHostName(), tmp.getPort()); + + // prepare to receive data + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + // ask for exchange rate data + out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))); + out.println("GET /getAllMarketPrices/"); + out.println(); + out.flush(); + + String currencyCode = ""; + while((line = in.readLine()) != null) { + Matcher currencyCodeMatcher = currencyCodePattern.matcher(line); + Matcher priceMatcher = pricePattern.matcher(line); + if(currencyCodeMatcher.find()) { + currencyCode = currencyCodeMatcher.group(1); + if(!assets.contains(currencyCode)) + currencyCode = ""; + } else if(!"".equals(currencyCode) && priceMatcher.find()) + result.put("price." + currencyCode, priceMatcher.group(1)); + } + + // close all the things + in.close(); + out.close(); + socket.close(); + + // report + reporter.report(result, getName()); + + // only ask for data as long as we got none + if(!result.isEmpty()) + break; + } + } catch (TorCtlException | IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} diff --git a/monitor/src/main/java/bisq/monitor/metric/TorHiddenServiceStartupTime.java b/monitor/src/main/java/bisq/monitor/metric/TorHiddenServiceStartupTime.java index 5ef02604df3..21be8526eff 100644 --- a/monitor/src/main/java/bisq/monitor/metric/TorHiddenServiceStartupTime.java +++ b/monitor/src/main/java/bisq/monitor/metric/TorHiddenServiceStartupTime.java @@ -18,6 +18,7 @@ package bisq.monitor.metric; import bisq.monitor.Metric; +import bisq.monitor.Monitor; import bisq.monitor.Reporter; import bisq.monitor.ThreadGate; @@ -53,7 +54,7 @@ protected void execute() { int servicePort = Integer.parseInt(configuration.getProperty(SERVICE_PORT, "9999")); // clear directory so we get a new onion address every time - new File(hiddenServiceDirectory).delete(); + new File(Monitor.TOR_WORKING_DIR + "/" + hiddenServiceDirectory).delete(); log.debug("creating the hidden service"); @@ -67,7 +68,7 @@ protected void execute() { servicePort); hiddenServiceSocket.addReadyListener(socket -> { // stop the timer and report - reporter.report(System.currentTimeMillis() - start, "bisq." + getName()); + reporter.report(System.currentTimeMillis() - start, getName()); log.debug("the hidden service is ready"); gate.proceed(); return null; diff --git a/monitor/src/main/java/bisq/monitor/metric/TorRoundTripTime.java b/monitor/src/main/java/bisq/monitor/metric/TorRoundTripTime.java index 5616529ed93..16499689b08 100644 --- a/monitor/src/main/java/bisq/monitor/metric/TorRoundTripTime.java +++ b/monitor/src/main/java/bisq/monitor/metric/TorRoundTripTime.java @@ -81,7 +81,7 @@ protected void execute() { } // report - reporter.report(StatisticsHelper.process(samples), "bisq." + getName()); + reporter.report(StatisticsHelper.process(samples), getName()); } } catch (TorCtlException | IOException e) { // TODO Auto-generated catch block diff --git a/monitor/src/main/java/bisq/monitor/metric/TorStartupTime.java b/monitor/src/main/java/bisq/monitor/metric/TorStartupTime.java index a4336caa42b..6e53e02462b 100644 --- a/monitor/src/main/java/bisq/monitor/metric/TorStartupTime.java +++ b/monitor/src/main/java/bisq/monitor/metric/TorStartupTime.java @@ -40,7 +40,7 @@ public class TorStartupTime extends Metric { private static final String SOCKS_PORT = "run.socksPort"; - private final File torWorkingDirectory = new File("metric_torStartupTime"); + private final File torWorkingDirectory = new File("monitor/work/metric_torStartupTime"); private Torrc torOverrides; public TorStartupTime(Reporter reporter) { @@ -77,7 +77,7 @@ protected void execute() { tor = new NativeTor(torWorkingDirectory, null, torOverrides); // stop the timer and set its timestamp - reporter.report(System.currentTimeMillis() - start, "bisq." + getName()); + reporter.report(System.currentTimeMillis() - start, getName()); } catch (TorCtlException e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/monitor/src/main/java/bisq/monitor/reporter/ConsoleReporter.java b/monitor/src/main/java/bisq/monitor/reporter/ConsoleReporter.java index 0b2050a5cb1..e3d666dbf21 100644 --- a/monitor/src/main/java/bisq/monitor/reporter/ConsoleReporter.java +++ b/monitor/src/main/java/bisq/monitor/reporter/ConsoleReporter.java @@ -19,6 +19,10 @@ import bisq.monitor.Reporter; +import bisq.core.btc.BaseCurrencyNetwork; + +import bisq.common.app.Version; + import java.util.HashMap; import java.util.Map; @@ -33,7 +37,7 @@ public class ConsoleReporter extends Reporter { public void report(long value, String prefix) { HashMap result = new HashMap<>(); result.put("", String.valueOf(value)); - report(result, "bisq"); + report(result, prefix); } @@ -46,16 +50,22 @@ public void report(long value) { @Override public void report(Map values, String prefix) { - long timestamp = System.currentTimeMillis(); + String timestamp = String.valueOf(System.currentTimeMillis()); values.forEach((key, value) -> { - String report = prefix + ("".equals(key) ? "" : (prefix.isEmpty() ? "" : ".") + key) + " " + value + " " - + timestamp; - System.err.println("Report: " + report); + report(key, value, timestamp, prefix); }); } + @Override + public void report(String key, String value, String timestamp, String prefix) { + System.err.println("Report: bisq" + (Version.getBaseCurrencyNetwork() != 0 ? "-" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].getNetwork() : "") + + (prefix.isEmpty() ? "" : "." + prefix) + + (key.isEmpty() ? "" : "." + key) + + " " + value + " " + timestamp); + } + @Override public void report(Map values) { - report(values, "bisq"); + report(values, ""); } } diff --git a/monitor/src/main/java/bisq/monitor/reporter/GraphiteReporter.java b/monitor/src/main/java/bisq/monitor/reporter/GraphiteReporter.java index 322ddebb5aa..8a9a55d9b54 100644 --- a/monitor/src/main/java/bisq/monitor/reporter/GraphiteReporter.java +++ b/monitor/src/main/java/bisq/monitor/reporter/GraphiteReporter.java @@ -19,8 +19,13 @@ import bisq.monitor.OnionParser; import bisq.monitor.Reporter; + +import bisq.core.btc.BaseCurrencyNetwork; + import bisq.network.p2p.NodeAddress; +import bisq.common.app.Version; + import org.berndpruenster.netlayer.tor.TorSocket; import java.io.IOException; @@ -45,46 +50,54 @@ public void report(long value, String prefix) { @Override public void report(long value) { - report(value, "bisq"); + report(value, ""); } @Override public void report(Map values, String prefix) { - long timestamp = System.currentTimeMillis() / 1000; + String timestamp = String.valueOf(System.currentTimeMillis()); values.forEach((key, value) -> { - String report = prefix + ("".equals(key) ? "" : (prefix.isEmpty() ? "" : ".") + key) + " " + value + " " - + timestamp + "\n"; + report(key, value, timestamp, prefix); try { - NodeAddress nodeAddress = OnionParser.getNodeAddress(configuration.getProperty("serviceUrl")); - Socket socket; - if (nodeAddress.getFullAddress().contains(".onion")) - socket = new TorSocket(nodeAddress.getHostName(), nodeAddress.getPort()); - else - socket = new Socket(nodeAddress.getHostName(), nodeAddress.getPort()); - - socket.getOutputStream().write(report.getBytes()); - socket.close(); - - try { - // give Tor some slack - // TODO maybe use the pickle protocol? - // https://graphite.readthedocs.io/en/latest/feeding-carbon.html - Thread.sleep(100); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } catch (IOException e) { + // give Tor some slack + // TODO maybe use the pickle protocol? + // https://graphite.readthedocs.io/en/latest/feeding-carbon.html + Thread.sleep(100); + } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } - }); } + @Override + public void report(String key, String value, String timeInMilliseconds, String prefix) { + // https://graphite.readthedocs.io/en/latest/feeding-carbon.html + String report = "bisq" + (Version.getBaseCurrencyNetwork() != 0 ? "-" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].getNetwork() : "") + + (prefix.isEmpty() ? "" : "." + prefix) + + (key.isEmpty() ? "" : "." + key) + + " " + value + " " + Long.valueOf(timeInMilliseconds)/1000 + "\n"; + + try { + NodeAddress nodeAddress = OnionParser.getNodeAddress(configuration.getProperty("serviceUrl")); + Socket socket; + if (nodeAddress.getFullAddress().contains(".onion")) + socket = new TorSocket(nodeAddress.getHostName(), nodeAddress.getPort()); + else + socket = new Socket(nodeAddress.getHostName(), nodeAddress.getPort()); + + socket.getOutputStream().write(report.getBytes()); + socket.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + @Override public void report(Map values) { - report(values, "bisq"); + report(values, ""); } } diff --git a/monitor/src/main/resources/metrics.properties b/monitor/src/main/resources/metrics.properties index 76314cda8ff..436d04cef4c 100644 --- a/monitor/src/main/resources/metrics.properties +++ b/monitor/src/main/resources/metrics.properties @@ -1,3 +1,11 @@ +## System configuration + +# true overwrites the reporters picked by the developers (for debugging for example) (defaults to false) +System.useConsoleReporter=true + +# 0 -> BTC_MAINNET, 1 -> BTC_TESTNET (default) +System.baseCurrencyNetwork=0 + ## Each Metric is configured via a set of properties. ## ## The minimal set of properties required to run a Metric is: @@ -5,7 +13,7 @@ ## YourMetricName.enabled=true|false ## YourMetricName.run.interval=10 [seconds] -#Edit and uncomment the lines below for your liking +#Edit and uncomment the lines below to your liking #TorStartupTime Metric TorStartupTime.enabled=true @@ -34,15 +42,29 @@ P2PRoundTripTime.run.torProxyPort=9060 #P2PNetworkLoad Metric P2PNetworkLoad.enabled=true P2PNetworkLoad.run.interval=100 -P2PNetworkLoad.run.hosts=723ljisnynbtdohi.onion:8000, fl3mmribyxgrv63c.onion:8000 P2PNetworkLoad.run.torProxyPort=9061 +P2PNetworkLoad.run.historySize=200 + +#P2PSeedNodeSnapshot Metric +P2PSeedNodeSnapshot.enabled=true +P2PSeedNodeSnapshot.run.interval=24 +P2PSeedNodeSnapshot.run.hosts=3f3cu2yw7u457ztq.onion:8000, 723ljisnynbtdohi.onion:8000, fl3mmribyxgrv63c.onion:8000 +P2PSeedNodeSnapshot.run.torProxyPort=9062 + +#P2PMarketStats Metric +P2PMarketStats.enabled=true +P2PMarketStats.run.interval=37 +P2PMarketStats.run.hosts=ef5qnzx6znifo3df.onion:8000 +P2PMarketStats.run.torProxyPort=9063 -#P2PNetworkMessageSnapshot Metric -P2PNetworkMessageSnapshot.enabled=true -P2PNetworkMessageSnapshot.run.interval=24 -P2PNetworkMessageSnapshot.run.hosts=3f3cu2yw7u457ztq.onion:8000, 723ljisnynbtdohi.onion:8000, fl3mmribyxgrv63c.onion:8000 -P2PNetworkMessageSnapshot.run.torProxyPort=9062 +#PriceNodeStats Metric +PriceNodeStats.enabled=true +PriceNodeStats.run.interval=42 +PriceNodeStats.run.hosts=http://5bmpx76qllutpcyp.onion, http://xc3nh4juf2hshy7e.onion, http://44mgyoe2b6oqiytt.onion, http://62nvujg5iou3vu3i.onion, http://ceaanhbvluug4we6.onion +#MarketStats Metric +MarketStats.enabled=true +MarketStats.run.interval=191 #Another Metric Another.run.interval=5 diff --git a/monitor/src/test/java/bisq/monitor/MonitorInfrastructureTests.java b/monitor/src/test/java/bisq/monitor/MonitorInfrastructureTests.java index 9843779566b..17d0837a5b4 100644 --- a/monitor/src/test/java/bisq/monitor/MonitorInfrastructureTests.java +++ b/monitor/src/test/java/bisq/monitor/MonitorInfrastructureTests.java @@ -21,12 +21,15 @@ import java.util.HashMap; import java.util.Properties; +import java.util.concurrent.ExecutionException; import org.junit.Assert; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +@Disabled public class MonitorInfrastructureTests { /** @@ -44,7 +47,13 @@ public boolean active() { @Override protected void execute() { - // do nothing + try { + Thread.sleep(50000); + + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } } @@ -78,12 +87,11 @@ public void basicConfigurationSuccess() throws Exception { Assert.assertTrue(DUT.active()); // graceful shutdown - DUT.shutdown(); - DUT.join(); + Metric.haltAllMetrics(); } @Test - public void reloadConfig() throws InterruptedException { + public void reloadConfig() throws InterruptedException, ExecutionException { // our dummy Dummy DUT = new Dummy(); @@ -119,9 +127,23 @@ public void reloadConfig() throws InterruptedException { Assert.assertTrue(DUT2.active()); // graceful shutdown - DUT.shutdown(); - DUT.join(); - DUT2.shutdown(); - DUT2.join(); + Metric.haltAllMetrics(); + } + + @Test + public void shutdown() { + Dummy DUT = new Dummy(); + DUT.setName("Dummy"); + Properties dummyProperties = new Properties(); + dummyProperties.put("Dummy.enabled", "true"); + dummyProperties.put("Dummy.run.interval", "1"); + DUT.configure(dummyProperties); + try { + Thread.sleep(2000); + Metric.haltAllMetrics(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } } diff --git a/monitor/src/test/java/bisq/monitor/P2PNetworkLoadTests.java b/monitor/src/test/java/bisq/monitor/P2PNetworkLoadTests.java index db24ea3bc9c..2011e69b0e8 100644 --- a/monitor/src/test/java/bisq/monitor/P2PNetworkLoadTests.java +++ b/monitor/src/test/java/bisq/monitor/P2PNetworkLoadTests.java @@ -101,8 +101,7 @@ void run() throws Exception { Thread.sleep(500); Thread.sleep(20000); - DUT.shutdown(); - DUT.join(); + Metric.haltAllMetrics(); // observe results Map results = reporter.hasResults(); diff --git a/monitor/src/test/java/bisq/monitor/P2PRoundTripTimeTests.java b/monitor/src/test/java/bisq/monitor/P2PRoundTripTimeTests.java index 6c95b7f0cd8..bb976593276 100644 --- a/monitor/src/test/java/bisq/monitor/P2PRoundTripTimeTests.java +++ b/monitor/src/test/java/bisq/monitor/P2PRoundTripTimeTests.java @@ -105,8 +105,7 @@ void run(String sampleSize) throws Exception { while (!DUT.enabled()) Thread.sleep(2000); - DUT.shutdown(); - DUT.join(); + Metric.haltAllMetrics(); // observe results Map results = reporter.hasResults(); diff --git a/monitor/src/test/java/bisq/monitor/PriceNodeStatsTests.java b/monitor/src/test/java/bisq/monitor/PriceNodeStatsTests.java new file mode 100644 index 00000000000..1024f5c9a0f --- /dev/null +++ b/monitor/src/test/java/bisq/monitor/PriceNodeStatsTests.java @@ -0,0 +1,116 @@ +/* + * 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.monitor; + +import bisq.monitor.metric.PriceNodeStats; + +import org.berndpruenster.netlayer.tor.NativeTor; +import org.berndpruenster.netlayer.tor.Tor; +import org.berndpruenster.netlayer.tor.TorCtlException; + +import java.io.File; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.junit.Assert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author Florian Reimair + */ +@Disabled +public class PriceNodeStatsTests { + + private final static File torWorkingDirectory = new File("monitor/" + PriceNodeStatsTests.class.getSimpleName()); + + /** + * A dummy Reporter for development purposes. + */ + private class DummyReporter extends Reporter { + + private Map results; + + @Override + public void report(long value) { + Assert.fail(); + } + + public Map results() { + return results; + } + + @Override + public void report(Map values) { + results = values; + } + + @Override + public void report(Map values, String prefix) { + report(values); + } + + @Override + public void report(String key, String value, String timestamp, String prefix) { + + } + + @Override + public void report(long value, String prefix) { + report(value); + } + } + + @BeforeAll + public static void setup() throws TorCtlException { + // simulate the tor instance available to all metrics + Tor.setDefault(new NativeTor(torWorkingDirectory)); + } + + @Test + public void connect() { + DummyReporter reporter = new DummyReporter(); + Metric DUT = new PriceNodeStats(reporter); + + + Properties configuration = new Properties(); + configuration.put("PriceNodeStats.run.hosts", "http://5bmpx76qllutpcyp.onion"); + + DUT.configure(configuration); + + DUT.execute(); + + Assert.assertNotNull(reporter.results()); + Assert.assertTrue(reporter.results.size() > 0); + } + + @AfterAll + public static void cleanup() { + Tor tor = Tor.getDefault(); + checkNotNull(tor, "tor must not be null"); + tor.shutdown(); + torWorkingDirectory.delete(); + } + +} diff --git a/monitor/src/test/java/bisq/monitor/TorHiddenServiceStartupTimeTests.java b/monitor/src/test/java/bisq/monitor/TorHiddenServiceStartupTimeTests.java index 6081830d620..d517bcd5659 100644 --- a/monitor/src/test/java/bisq/monitor/TorHiddenServiceStartupTimeTests.java +++ b/monitor/src/test/java/bisq/monitor/TorHiddenServiceStartupTimeTests.java @@ -67,6 +67,11 @@ public void report(Map values, String prefix) { report(values); } + @Override + public void report(String key, String value, String timestamp, String prefix) { + + } + @Override public void report(long value, String prefix) { report(value); @@ -94,7 +99,7 @@ public void run() throws Exception { // give it some time and then stop Thread.sleep(180 * 1000); - DUT.shutdown(); + Metric.haltAllMetrics(); // observe results Assert.assertTrue(reporter.results() > 0); diff --git a/monitor/src/test/java/bisq/monitor/TorRoundTripTimeTests.java b/monitor/src/test/java/bisq/monitor/TorRoundTripTimeTests.java index bc59ce4b3de..b2091806a48 100644 --- a/monitor/src/test/java/bisq/monitor/TorRoundTripTimeTests.java +++ b/monitor/src/test/java/bisq/monitor/TorRoundTripTimeTests.java @@ -71,6 +71,11 @@ public void report(Map values, String prefix) { report(values); } + @Override + public void report(String key, String value, String timestamp, String prefix) { + + } + @Override public void report(long value, String prefix) { report(value); @@ -106,8 +111,7 @@ public void run(String sampleSize) throws Exception { // give it some time to start and then stop Thread.sleep(100); - DUT.shutdown(); - DUT.join(); + Metric.haltAllMetrics(); // observe results Map results = reporter.hasResults(); diff --git a/monitor/src/test/java/bisq/monitor/TorStartupTimeTests.java b/monitor/src/test/java/bisq/monitor/TorStartupTimeTests.java index 5e3a594cccf..e72325c847a 100644 --- a/monitor/src/test/java/bisq/monitor/TorStartupTimeTests.java +++ b/monitor/src/test/java/bisq/monitor/TorStartupTimeTests.java @@ -55,6 +55,11 @@ public void report(Map values, String prefix) { report(values); } + @Override + public void report(String key, String value, String timestamp, String prefix) { + + } + @Override public void report(long value, String prefix) { report(value); @@ -78,7 +83,7 @@ public void run() throws Exception { // give it some time and then stop Thread.sleep(15 * 1000); - DUT.shutdown(); + Metric.haltAllMetrics(); // TODO Test fails due timing issue // observe results diff --git a/p2p/src/main/java/bisq/network/NetworkOptionKeys.java b/p2p/src/main/java/bisq/network/NetworkOptionKeys.java index f2c96ea73f5..0cca23093ac 100644 --- a/p2p/src/main/java/bisq/network/NetworkOptionKeys.java +++ b/p2p/src/main/java/bisq/network/NetworkOptionKeys.java @@ -24,7 +24,6 @@ public class NetworkOptionKeys { public static final String PORT_KEY = "nodePort"; public static final String NETWORK_ID = "networkId"; public static final String SEED_NODES_KEY = "seedNodes"; - public static final String MY_ADDRESS = "myAddress"; public static final String BAN_LIST = "banList"; //SOCKS_5_PROXY_BTC_ADDRESS used in network module so dont move it to BtcOptionKeys public static final String SOCKS_5_PROXY_BTC_ADDRESS = "socks5ProxyBtcAddress"; @@ -36,4 +35,8 @@ public class NetworkOptionKeys { public static final String EXTERNAL_TOR_COOKIE_FILE = "torControlCookieFile"; public static final String EXTERNAL_TOR_USE_SAFECOOKIE = "torControlUseSafeCookieAuth"; public static final String TOR_STREAM_ISOLATION = "torStreamIsolation"; + public static final String MSG_THROTTLE_PER_SEC = "msgThrottlePerSec"; + public static final String MSG_THROTTLE_PER_10_SEC = "msgThrottlePer10Sec"; + public static final String SEND_MSG_THROTTLE_TRIGGER = "sendMsgThrottleTrigger"; + public static final String SEND_MSG_THROTTLE_SLEEP = "sendMsgThrottleSleep"; } diff --git a/p2p/src/main/java/bisq/network/p2p/AckMessage.java b/p2p/src/main/java/bisq/network/p2p/AckMessage.java index 9e4f5db79c5..62219761b22 100644 --- a/p2p/src/main/java/bisq/network/p2p/AckMessage.java +++ b/p2p/src/main/java/bisq/network/p2p/AckMessage.java @@ -21,6 +21,7 @@ import bisq.network.p2p.storage.payload.ExpirablePayload; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.app.Version; import bisq.common.proto.ProtoUtil; import bisq.common.proto.network.NetworkEnvelope; @@ -28,9 +29,6 @@ import io.bisq.generated.protobuffer.PB; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -153,10 +151,8 @@ public static AckMessage fromProto(PB.AckMessage proto, int messageVersion) { /////////////////////////////////////////////////////////////////////////////////////////// @Override - public List getRequiredCapabilities() { - return new ArrayList<>(Collections.singletonList( - Capabilities.Capability.ACK_MSG.ordinal() - )); + public Capabilities getRequiredCapabilities() { + return new Capabilities(Capability.ACK_MSG); } @Override diff --git a/p2p/src/main/java/bisq/network/p2p/NetworkNodeProvider.java b/p2p/src/main/java/bisq/network/p2p/NetworkNodeProvider.java index 6a5b7c78893..04349561be1 100644 --- a/p2p/src/main/java/bisq/network/p2p/NetworkNodeProvider.java +++ b/p2p/src/main/java/bisq/network/p2p/NetworkNodeProvider.java @@ -42,7 +42,6 @@ public class NetworkNodeProvider implements Provider { public NetworkNodeProvider(NetworkProtoResolver networkProtoResolver, BridgeAddressProvider bridgeAddressProvider, @Named(NetworkOptionKeys.USE_LOCALHOST_FOR_P2P) boolean useLocalhostForP2P, - @Named(NetworkOptionKeys.MY_ADDRESS) String address, @Named(NetworkOptionKeys.PORT_KEY) int port, @Named(NetworkOptionKeys.TOR_DIR) File torDir, @Named(NetworkOptionKeys.TORRC_FILE) String torrcFile, @@ -53,7 +52,7 @@ public NetworkNodeProvider(NetworkProtoResolver networkProtoResolver, @Named(NetworkOptionKeys.TOR_STREAM_ISOLATION) boolean streamIsolation, @Named(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE) boolean useSafeCookieAuthentication ) { networkNode = useLocalhostForP2P ? - new LocalhostNetworkNode(address, port, networkProtoResolver) : + new LocalhostNetworkNode(port, networkProtoResolver) : new TorNetworkNode(port, networkProtoResolver, streamIsolation, !controlPort.isEmpty() ? new RunningTor(torDir, Integer.parseInt(controlPort), password, cookieFile, useSafeCookieAuthentication) : diff --git a/p2p/src/main/java/bisq/network/p2p/P2PModule.java b/p2p/src/main/java/bisq/network/p2p/P2PModule.java index ad25aa28ef1..3618873a4ce 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PModule.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PModule.java @@ -19,6 +19,8 @@ import bisq.network.NetworkOptionKeys; import bisq.network.Socks5ProxyProvider; +import bisq.network.p2p.network.Connection; +import bisq.network.p2p.network.ConnectionConfig; import bisq.network.p2p.network.NetworkNode; import bisq.network.p2p.peers.BanList; import bisq.network.p2p.peers.Broadcaster; @@ -64,10 +66,12 @@ protected void configure() { bind(KeepAliveManager.class).in(Singleton.class); bind(Broadcaster.class).in(Singleton.class); bind(BanList.class).in(Singleton.class); + bind(ConnectionConfig.class).in(Singleton.class); bind(NetworkNode.class).toProvider(NetworkNodeProvider.class).in(Singleton.class); - bind(Socks5ProxyProvider.class).in(Singleton.class); + requestStaticInjection(Connection.class); + Boolean useLocalhostForP2P = environment.getProperty(NetworkOptionKeys.USE_LOCALHOST_FOR_P2P, boolean.class, false); bind(boolean.class).annotatedWith(Names.named(NetworkOptionKeys.USE_LOCALHOST_FOR_P2P)).toInstance(useLocalhostForP2P); @@ -84,7 +88,6 @@ protected void configure() { Integer networkId = environment.getProperty(NetworkOptionKeys.NETWORK_ID, int.class, 1); bind(int.class).annotatedWith(Names.named(NetworkOptionKeys.NETWORK_ID)).toInstance(networkId); bindConstant().annotatedWith(named(NetworkOptionKeys.SEED_NODES_KEY)).to(environment.getRequiredProperty(NetworkOptionKeys.SEED_NODES_KEY)); - bindConstant().annotatedWith(named(NetworkOptionKeys.MY_ADDRESS)).to(environment.getRequiredProperty(NetworkOptionKeys.MY_ADDRESS)); bindConstant().annotatedWith(named(NetworkOptionKeys.BAN_LIST)).to(environment.getRequiredProperty(NetworkOptionKeys.BAN_LIST)); bindConstant().annotatedWith(named(NetworkOptionKeys.SOCKS_5_PROXY_BTC_ADDRESS)).to(environment.getRequiredProperty(NetworkOptionKeys.SOCKS_5_PROXY_BTC_ADDRESS)); bindConstant().annotatedWith(named(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS)).to(environment.getRequiredProperty(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS)); @@ -93,7 +96,11 @@ protected void configure() { bindConstant().annotatedWith(named(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT)).to(environment.getRequiredProperty(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT)); bindConstant().annotatedWith(named(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD)).to(environment.getRequiredProperty(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD)); bindConstant().annotatedWith(named(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE)).to(environment.getRequiredProperty(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE)); - bindConstant().annotatedWith(named(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE)).to(environment.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE) ? true : false); - bindConstant().annotatedWith(named(NetworkOptionKeys.TOR_STREAM_ISOLATION)).to(environment.containsProperty(NetworkOptionKeys.TOR_STREAM_ISOLATION) ? true : false); + bindConstant().annotatedWith(named(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE)).to(environment.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE)); + bindConstant().annotatedWith(named(NetworkOptionKeys.TOR_STREAM_ISOLATION)).to(environment.containsProperty(NetworkOptionKeys.TOR_STREAM_ISOLATION)); + bindConstant().annotatedWith(named(NetworkOptionKeys.MSG_THROTTLE_PER_SEC)).to(environment.getRequiredProperty(NetworkOptionKeys.MSG_THROTTLE_PER_SEC)); + bindConstant().annotatedWith(named(NetworkOptionKeys.MSG_THROTTLE_PER_10_SEC)).to(environment.getRequiredProperty(NetworkOptionKeys.MSG_THROTTLE_PER_10_SEC)); + bindConstant().annotatedWith(named(NetworkOptionKeys.SEND_MSG_THROTTLE_TRIGGER)).to(environment.getRequiredProperty(NetworkOptionKeys.SEND_MSG_THROTTLE_TRIGGER)); + bindConstant().annotatedWith(named(NetworkOptionKeys.SEND_MSG_THROTTLE_SLEEP)).to(environment.getRequiredProperty(NetworkOptionKeys.SEND_MSG_THROTTLE_SLEEP)); } } diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index b881a159966..e3696025238 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -47,7 +47,6 @@ import bisq.network.p2p.storage.payload.ProtectedStoragePayload; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.crypto.CryptoException; import bisq.common.crypto.KeyRing; import bisq.common.crypto.PubKeyRing; @@ -187,7 +186,6 @@ public void readPersisted() { /////////////////////////////////////////////////////////////////////////////////////////// public void start(@Nullable P2PServiceListener listener) { - Log.traceCall(); if (listener != null) addP2PServiceListener(listener); @@ -195,7 +193,6 @@ public void start(@Nullable P2PServiceListener listener) { } public void onAllServicesInitialized() { - Log.traceCall(); if (networkNode.getNodeAddress() != null) { maybeProcessAllMailboxEntries(); } else { @@ -208,7 +205,6 @@ public void onAllServicesInitialized() { } public void shutDown(Runnable shutDownCompleteHandler) { - Log.traceCall(); if (!shutDownInProgress) { shutDownInProgress = true; @@ -273,8 +269,6 @@ public void shutDown(Runnable shutDownCompleteHandler) { /////////////////////////////////////////////////////////////////////////////////////////// @Override public void onTorNodeReady() { - Log.traceCall(); - socks5ProxyProvider.setSocks5ProxyInternal(networkNode); boolean seedNodesAvailable = requestDataManager.requestPreliminaryData(); @@ -291,8 +285,6 @@ public void onTorNodeReady() { @Override public void onHiddenServicePublished() { - Log.traceCall(); - checkArgument(networkNode.getNodeAddress() != null, "Address must be set when we have the hidden service ready"); hiddenServicePublished.set(true); @@ -302,7 +294,6 @@ public void onHiddenServicePublished() { @Override public void onSetupFailed(Throwable throwable) { - Log.traceCall(); p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable)); } @@ -313,7 +304,6 @@ public void onRequestCustomBridges() { // Called from networkReadyBinding private void onNetworkReady() { - Log.traceCall(); networkReadySubscription.unsubscribe(); Optional seedNodeOfPreliminaryDataRequest = requestDataManager.getNodeAddressOfPreliminaryDataRequest(); @@ -383,7 +373,6 @@ public void onConnection(Connection connection) { @Override public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { - Log.traceCall(); numConnectedPeers.set(networkNode.getAllConnections().size()); //TODO check if still needed and why UserThread.runAfter(() -> numConnectedPeers.set(networkNode.getAllConnections().size()), 3); @@ -401,7 +390,6 @@ public void onError(Throwable throwable) { @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof PrefixedSealedAndSignedMessage) { - Log.traceCall("\n\t" + networkEnvelope.toString() + "\n\tconnection=" + connection); // Seed nodes don't have set the encryptionService try { PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = (PrefixedSealedAndSignedMessage) networkEnvelope; @@ -522,7 +510,6 @@ private void processMailboxEntry(ProtectedMailboxStorageEntry protectedMailboxSt NodeAddress nodeAddress = networkNode.getNodeAddress(); // Seed nodes don't receive mailbox network_messages if (nodeAddress != null && !seedNodeRepository.isSeedNode(nodeAddress)) { - Log.traceCall(); MailboxStoragePayload mailboxStoragePayload = protectedMailboxStorageEntry.getMailboxStoragePayload(); PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = mailboxStoragePayload.getPrefixedSealedAndSignedMessage(); if (verifyAddressPrefixHash(prefixedSealedAndSignedMessage)) { @@ -632,15 +619,12 @@ private boolean capabilityRequiredAndCapabilityNotSupported(NodeAddress peersNod // it from old versions, so we filter those. Optional optionalPeer = allPeers.stream() .filter(peer -> peer.getNodeAddress().equals(peersNodeAddress)) - .filter(peer -> peer.getSupportedCapabilities() != null) - .filter(peer -> !peer.getSupportedCapabilities().isEmpty()) + .filter(peer -> !peer.getCapabilities().isEmpty()) .findAny(); if (optionalPeer.isPresent()) { - Peer peer = optionalPeer.get(); - boolean result = Connection.isCapabilityRequired(message) && - !Connection.isCapabilitySupported((CapabilityRequiringPayload) message, peer.getSupportedCapabilities()); + boolean result = optionalPeer.get().getCapabilities().containsAll(((CapabilityRequiringPayload) message).getRequiredCapabilities()); - if (result) + if (!result) log.warn("We don't send the message because the peer does not support the required capability. " + "peersNodeAddress={}", peersNodeAddress); @@ -665,8 +649,6 @@ private void maybeProcessAllMailboxEntries() { private void addMailboxData(MailboxStoragePayload expirableMailboxStoragePayload, PublicKey receiversPublicKey, SendMailboxMessageListener sendMailboxMessageListener) { - Log.traceCall(); - if (isBootstrapped()) { if (!networkNode.getAllConnections().isEmpty()) { try { @@ -755,7 +737,6 @@ public void removeEntryFromMailbox(DecryptedMessageWithPubKey decryptedMessageWi } private void delayedRemoveEntryFromMailbox(DecryptedMessageWithPubKey decryptedMessageWithPubKey) { - Log.traceCall(); if (!isBootstrapped()) { // We don't throw an NetworkNotReadyException here. // This case should not happen anyway as we check for isBootstrapped in the callers. @@ -799,7 +780,6 @@ public boolean addPersistableNetworkPayload(PersistableNetworkPayload payload, b } public boolean addProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload, boolean isDataOwner) { - Log.traceCall(); if (isBootstrapped()) { try { ProtectedStorageEntry protectedStorageEntry = p2PDataStorage.getProtectedStorageEntry(protectedStoragePayload, keyRing.getSignatureKeyPair()); @@ -814,7 +794,6 @@ public boolean addProtectedStorageEntry(ProtectedStoragePayload protectedStorage } public boolean refreshTTL(ProtectedStoragePayload protectedStoragePayload, boolean isDataOwner) { - Log.traceCall(); if (isBootstrapped()) { try { RefreshOfferMessage refreshTTLMessage = p2PDataStorage.getRefreshTTLMessage(protectedStoragePayload, keyRing.getSignatureKeyPair()); @@ -829,7 +808,6 @@ public boolean refreshTTL(ProtectedStoragePayload protectedStoragePayload, boole } public boolean removeData(ProtectedStoragePayload protectedStoragePayload, boolean isDataOwner) { - Log.traceCall(); if (isBootstrapped()) { try { ProtectedStorageEntry protectedStorageEntry = p2PDataStorage.getProtectedStorageEntry(protectedStoragePayload, keyRing.getSignatureKeyPair()); diff --git a/p2p/src/main/java/bisq/network/p2p/SupportedCapabilitiesMessage.java b/p2p/src/main/java/bisq/network/p2p/SupportedCapabilitiesMessage.java index c73b72e824c..87003312e26 100644 --- a/p2p/src/main/java/bisq/network/p2p/SupportedCapabilitiesMessage.java +++ b/p2p/src/main/java/bisq/network/p2p/SupportedCapabilitiesMessage.java @@ -17,11 +17,11 @@ package bisq.network.p2p; -import java.util.List; +import bisq.common.app.Capabilities; import javax.annotation.Nullable; public interface SupportedCapabilitiesMessage { @Nullable - List getSupportedCapabilities(); + Capabilities getSupportedCapabilities(); } 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 ad92ef25ee3..64901d8ebc0 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/Connection.java +++ b/p2p/src/main/java/bisq/network/p2p/network/Connection.java @@ -38,7 +38,7 @@ import bisq.common.Proto; import bisq.common.UserThread; import bisq.common.app.Capabilities; -import bisq.common.app.Log; +import bisq.common.app.HasCapabilities; import bisq.common.app.Version; import bisq.common.proto.ProtobufferException; import bisq.common.proto.network.NetworkEnvelope; @@ -48,11 +48,12 @@ import io.bisq.generated.protobuffer.PB; +import javax.inject.Inject; + import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.Uninterruptibles; import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.SimpleObjectProperty; import java.net.Socket; @@ -80,9 +81,7 @@ import java.lang.ref.WeakReference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.Nullable; @@ -95,7 +94,7 @@ * All handlers are called on User thread. */ @Slf4j -public class Connection implements MessageListener { +public class Connection implements HasCapabilities, Runnable, MessageListener { /////////////////////////////////////////////////////////////////////////////////////////// // Enums @@ -113,18 +112,20 @@ public enum PeerType { // Static /////////////////////////////////////////////////////////////////////////////////////////// + @Inject + private static ConnectionConfig connectionConfig; + // Leaving some constants package-private for tests to know limits. static final int PERMITTED_MESSAGE_SIZE = 200 * 1024; // 200 kb static final int MAX_PERMITTED_MESSAGE_SIZE = 10 * 1024 * 1024; // 10 MB (425 offers resulted in about 660 kb, mailbox msg will add more to it) offer has usually 2 kb, mailbox 3kb. //TODO decrease limits again after testing - static final int MSG_THROTTLE_PER_SEC = 200; // With MAX_MSG_SIZE of 200kb results in bandwidth of 40MB/sec or 5 mbit/sec - static final int MSG_THROTTLE_PER_10_SEC = 1000; // With MAX_MSG_SIZE of 200kb results in bandwidth of 20MB/sec or 2.5 mbit/sec private static final int SOCKET_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(120); public static int getPermittedMessageSize() { return PERMITTED_MESSAGE_SIZE; } + /////////////////////////////////////////////////////////////////////////////////////////// // Class fields /////////////////////////////////////////////////////////////////////////////////////////// @@ -132,52 +133,69 @@ public static int getPermittedMessageSize() { private final Socket socket; // private final MessageListener messageListener; private final ConnectionListener connectionListener; - private final String portInfo; + @Getter private final String uid; private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); // holder of state shared between InputHandler and Connection - private final SharedModel sharedModel; + @Getter private final Statistic statistic; + private final int msgThrottlePer10Sec; + private final int msgThrottlePerSec; + private final int sendMsgThrottleTrigger; + private final int sendMsgThrottleSleep; // set in init - private InputHandler inputHandler; private SynchronizedProtoOutputStream protoOutputStream; // mutable data, set from other threads but not changed internally. + @Getter private Optional peersNodeAddressOptional = Optional.empty(); + @Getter private volatile boolean stopped; - private PeerType peerType; + + // Use Peer as default, in case of other types they will set it as soon as possible. + @Getter + private PeerType peerType = PeerType.PEER; + @Getter private final ObjectProperty peersNodeAddressProperty = new SimpleObjectProperty<>(); - private final List> messageTimeStamps = new ArrayList<>(); + private final List> messageTimeStamps = new ArrayList<>(); private final CopyOnWriteArraySet messageListeners = new CopyOnWriteArraySet<>(); private volatile long lastSendTimeStamp = 0; private final CopyOnWriteArraySet> capabilitiesListeners = new CopyOnWriteArraySet<>(); + @Getter + private RuleViolation ruleViolation; + private final ConcurrentHashMap ruleViolations = new ConcurrentHashMap<>(); + + private Capabilities capabilities = new Capabilities(); + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor /////////////////////////////////////////////////////////////////////////////////////////// - Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener, - @Nullable NodeAddress peersNodeAddress, NetworkProtoResolver networkProtoResolver) { + Connection(Socket socket, + MessageListener messageListener, + ConnectionListener connectionListener, + @Nullable NodeAddress peersNodeAddress, + NetworkProtoResolver networkProtoResolver) { this.socket = socket; this.connectionListener = connectionListener; uid = UUID.randomUUID().toString(); statistic = new Statistic(); - addMessageListener(messageListener); - - sharedModel = new SharedModel(this, socket); + msgThrottlePerSec = connectionConfig.getMsgThrottlePerSec(); + msgThrottlePer10Sec = connectionConfig.getMsgThrottlePer10Sec(); + sendMsgThrottleTrigger = connectionConfig.getSendMsgThrottleTrigger(); + sendMsgThrottleSleep = connectionConfig.getSendMsgThrottleSleep(); - if (socket.getLocalPort() == 0) - portInfo = "port=" + socket.getPort(); - else - portInfo = "localPort=" + socket.getLocalPort() + "/port=" + socket.getPort(); + addMessageListener(messageListener); - init(peersNodeAddress, networkProtoResolver); + this.networkProtoResolver = networkProtoResolver; + init(peersNodeAddress); } - private void init(@Nullable NodeAddress peersNodeAddress, NetworkProtoResolver networkProtoResolver) { + private void init(@Nullable NodeAddress peersNodeAddress) { try { socket.setSoTimeout(SOCKET_TIMEOUT); // Need to access first the ObjectOutputStream otherwise the ObjectInputStream would block @@ -186,36 +204,28 @@ private void init(@Nullable NodeAddress peersNodeAddress, NetworkProtoResolver n // the associated ObjectOutputStream on the other end of the connection has written. // It will not return until that header has been read. protoOutputStream = new SynchronizedProtoOutputStream(socket.getOutputStream(), statistic); - InputStream protoInputStream = socket.getInputStream(); + protoInputStream = socket.getInputStream(); // We create a thread for handling inputStream data - inputHandler = new InputHandler(sharedModel, protoInputStream, portInfo, this, networkProtoResolver); - singleThreadExecutor.submit(inputHandler); - - // Use Peer as default, in case of other types they will set it as soon as possible. - peerType = PeerType.PEER; + singleThreadExecutor.submit(this); if (peersNodeAddress != null) setPeersNodeAddress(peersNodeAddress); - log.trace("New connection created: " + this.toString()); - UserThread.execute(() -> connectionListener.onConnection(this)); - } catch (Throwable e) { handleException(e); } } - private void handleException(Throwable e) { - if (sharedModel != null) - sharedModel.handleConnectionException(e); - } - - /////////////////////////////////////////////////////////////////////////////////////////// // API /////////////////////////////////////////////////////////////////////////////////////////// + @Override + public Capabilities getCapabilities() { + return capabilities; + } + // Called from various threads public void sendMessage(NetworkEnvelope networkEnvelope) { log.debug(">> Send networkEnvelope of type: " + networkEnvelope.getClass().getSimpleName()); @@ -223,20 +233,19 @@ public void sendMessage(NetworkEnvelope networkEnvelope) { if (!stopped) { if (noCapabilityRequiredOrCapabilityIsSupported(networkEnvelope)) { try { - Log.traceCall(); - // Throttle outbound network_messages long now = System.currentTimeMillis(); long elapsed = now - lastSendTimeStamp; - if (elapsed < 20) { - log.debug("We got 2 sendMessage requests in less than 20 ms. We set the thread to sleep " + - "for 50 ms to avoid flooding our peer. lastSendTimeStamp={}, now={}, elapsed={}", - lastSendTimeStamp, now, elapsed); - Thread.sleep(50); + if (elapsed < sendMsgThrottleTrigger) { + log.debug("We got 2 sendMessage requests in less than {} ms. We set the thread to sleep " + + "for {} ms to avoid flooding our peer. lastSendTimeStamp={}, now={}, elapsed={}, networkEnvelope={}", + sendMsgThrottleTrigger, sendMsgThrottleSleep, lastSendTimeStamp, now, elapsed, + networkEnvelope.getClass().getSimpleName()); + Thread.sleep(sendMsgThrottleSleep); } lastSendTimeStamp = now; - String peersNodeAddress = peersNodeAddressOptional.isPresent() ? peersNodeAddressOptional.get().toString() : "null"; + String peersNodeAddress = peersNodeAddressOptional.map(NodeAddress::toString).orElse("null"); PB.NetworkEnvelope proto = networkEnvelope.toProtoNetworkEnvelope(); log.debug("Sending message: {}", Utilities.toTruncatedString(proto.toString(), 10000)); @@ -271,8 +280,6 @@ public void sendMessage(NetworkEnvelope networkEnvelope) { } catch (Throwable t) { handleException(t); } - } else { - log.info("We did not send the message because the peer does not support our required capabilities. message={}, peers supportedCapabilities={}", networkEnvelope, sharedModel.getSupportedCapabilities()); } } else { log.debug("called sendMessage but was already stopped"); @@ -280,46 +287,29 @@ public void sendMessage(NetworkEnvelope networkEnvelope) { } public boolean noCapabilityRequiredOrCapabilityIsSupported(Proto msg) { - return !isCapabilityRequired(msg) || isCapabilitySupported(msg); - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public static boolean isCapabilityRequired(Proto msg) { + boolean result; if (msg instanceof AddDataMessage) { final ProtectedStoragePayload protectedStoragePayload = (((AddDataMessage) msg).getProtectedStorageEntry()).getProtectedStoragePayload(); - return protectedStoragePayload instanceof CapabilityRequiringPayload; + result = !(protectedStoragePayload instanceof CapabilityRequiringPayload); + if(!result) + result = capabilities.containsAll(((CapabilityRequiringPayload) protectedStoragePayload).getRequiredCapabilities()); } else if (msg instanceof AddPersistableNetworkPayloadMessage) { final PersistableNetworkPayload persistableNetworkPayload = ((AddPersistableNetworkPayloadMessage) msg).getPersistableNetworkPayload(); - return persistableNetworkPayload instanceof CapabilityRequiringPayload; + result = !(persistableNetworkPayload instanceof CapabilityRequiringPayload); + if(!result) + result = capabilities.containsAll(((CapabilityRequiringPayload) persistableNetworkPayload).getRequiredCapabilities()); + } else if(msg instanceof CapabilityRequiringPayload) { + result = capabilities.containsAll(((CapabilityRequiringPayload) msg).getRequiredCapabilities()); } else { - return msg instanceof CapabilityRequiringPayload; + result = true; } - } - private boolean isCapabilitySupported(Proto msg) { - if (msg instanceof AddDataMessage) { - final ProtectedStoragePayload protectedStoragePayload = (((AddDataMessage) msg).getProtectedStorageEntry()).getProtectedStoragePayload(); - return protectedStoragePayload instanceof CapabilityRequiringPayload && isCapabilitySupported((CapabilityRequiringPayload) protectedStoragePayload); - } else if (msg instanceof AddPersistableNetworkPayloadMessage) { - final PersistableNetworkPayload persistableNetworkPayload = ((AddPersistableNetworkPayloadMessage) msg).getPersistableNetworkPayload(); - return persistableNetworkPayload instanceof CapabilityRequiringPayload && isCapabilitySupported((CapabilityRequiringPayload) persistableNetworkPayload); - } else { - return msg instanceof CapabilityRequiringPayload && isCapabilitySupported((CapabilityRequiringPayload) msg); - } - } - - private boolean isCapabilitySupported(CapabilityRequiringPayload payload) { - return isCapabilitySupported(payload, sharedModel.getSupportedCapabilities()); - } - - public static boolean isCapabilitySupported(CapabilityRequiringPayload payload, List supportedCapabilities) { - final List requiredCapabilities = payload.getRequiredCapabilities(); - return Capabilities.isCapabilitySupported(requiredCapabilities, supportedCapabilities); - } + if (!result) + log.info("We did not send the message because the peer does not support our required capabilities. " + + "message={}, peer={}, peers supportedCapabilities={}", + msg, peersNodeAddressOptional, capabilities); - @Nullable - public List getSupportedCapabilities() { - return sharedModel.getSupportedCapabilities(); + return result; } public void addMessageListener(MessageListener messageListener) { @@ -339,34 +329,29 @@ public void addWeakCapabilitiesListener(SupportedCapabilitiesListener listener) capabilitiesListeners.add(new WeakReference<>(listener)); } - @SuppressWarnings({"unused", "UnusedReturnValue"}) - public boolean reportIllegalRequest(RuleViolation ruleViolation) { - return sharedModel.reportInvalidRequest(ruleViolation); - } - // TODO either use the argument or delete it private boolean violatesThrottleLimit(NetworkEnvelope networkEnvelope) { long now = System.currentTimeMillis(); boolean violated = false; //TODO remove message storage after network is tested stable - if (messageTimeStamps.size() >= MSG_THROTTLE_PER_SEC) { + if (messageTimeStamps.size() >= msgThrottlePerSec) { // check if we got more than 200 (MSG_THROTTLE_PER_SEC) msg per sec. - long compareValue = messageTimeStamps.get(messageTimeStamps.size() - MSG_THROTTLE_PER_SEC).first; + long compareValue = messageTimeStamps.get(messageTimeStamps.size() - msgThrottlePerSec).first; // if duration < 1 sec we received too much network_messages violated = now - compareValue < TimeUnit.SECONDS.toMillis(1); if (violated) { log.error("violatesThrottleLimit MSG_THROTTLE_PER_SEC "); log.error("elapsed " + (now - compareValue)); log.error("messageTimeStamps: \n\t" + messageTimeStamps.stream() - .map(e -> "\n\tts=" + e.first.toString() + " message=" + e.second.getClass().getName()) + .map(e -> "\n\tts=" + e.first.toString() + " message=" + e.second) .collect(Collectors.toList()).toString()); } } - if (messageTimeStamps.size() >= MSG_THROTTLE_PER_10_SEC) { + if (messageTimeStamps.size() >= msgThrottlePer10Sec) { if (!violated) { // check if we got more than 50 msg per 10 sec. - long compareValue = messageTimeStamps.get(messageTimeStamps.size() - MSG_THROTTLE_PER_10_SEC).first; + long compareValue = messageTimeStamps.get(messageTimeStamps.size() - msgThrottlePer10Sec).first; // if duration < 10 sec we received too much network_messages violated = now - compareValue < TimeUnit.SECONDS.toMillis(10); @@ -374,15 +359,16 @@ private boolean violatesThrottleLimit(NetworkEnvelope networkEnvelope) { log.error("violatesThrottleLimit MSG_THROTTLE_PER_10_SEC "); log.error("elapsed " + (now - compareValue)); log.error("messageTimeStamps: \n\t" + messageTimeStamps.stream() - .map(e -> "\n\tts=" + e.first.toString() + " message=" + e.second.getClass().getName()) + .map(e -> "\n\tts=" + e.first.toString() + " message=" + e.second) .collect(Collectors.toList()).toString()); } } - // we limit to max 1000 (MSG_THROTTLE_PER_10SEC) entries - messageTimeStamps.remove(0); } + // we limit to max 1000 (MSG_THROTTLE_PER_10SEC) entries + while (messageTimeStamps.size() > msgThrottlePer10Sec) + messageTimeStamps.remove(0); - messageTimeStamps.add(new Tuple2<>(now, networkEnvelope)); + messageTimeStamps.add(new Tuple2<>(now, networkEnvelope.getClass().getName())); return violated; } @@ -394,7 +380,7 @@ private boolean violatesThrottleLimit(NetworkEnvelope networkEnvelope) { @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { checkArgument(connection.equals(this)); - UserThread.execute(() -> messageListeners.stream().forEach(e -> e.onMessage(networkEnvelope, connection))); + UserThread.execute(() -> messageListeners.forEach(e -> e.onMessage(networkEnvelope, connection))); } @@ -424,48 +410,18 @@ public void setPeersNodeAddress(NodeAddress peerNodeAddress) { if (BanList.isBanned(peerNodeAddress)) { log.warn("We detected a connection to a banned peer. We will close that connection. (setPeersNodeAddress)"); - sharedModel.reportInvalidRequest(RuleViolation.PEER_BANNED); + reportInvalidRequest(RuleViolation.PEER_BANNED); } } - /////////////////////////////////////////////////////////////////////////////////////////// // Getters /////////////////////////////////////////////////////////////////////////////////////////// - public Optional getPeersNodeAddressOptional() { - return peersNodeAddressOptional; - } - - public String getUid() { - return uid; - } - public boolean hasPeersNodeAddress() { return peersNodeAddressOptional.isPresent(); } - public boolean isStopped() { - return stopped; - } - - public PeerType getPeerType() { - return peerType; - } - - public ReadOnlyObjectProperty peersNodeAddressProperty() { - return peersNodeAddressProperty; - } - - public RuleViolation getRuleViolation() { - return sharedModel.getRuleViolation(); - } - - public Statistic getStatistic() { - return statistic; - } - - /////////////////////////////////////////////////////////////////////////////////////////// // ShutDown /////////////////////////////////////////////////////////////////////////////////////////// @@ -477,7 +433,7 @@ public void shutDown(CloseConnectionReason closeConnectionReason) { public void shutDown(CloseConnectionReason closeConnectionReason, @Nullable Runnable shutDownCompleteHandler) { log.debug("shutDown: nodeAddressOpt={}, closeConnectionReason={}", this.peersNodeAddressOptional, closeConnectionReason); if (!stopped) { - String peersNodeAddress = peersNodeAddressOptional.isPresent() ? peersNodeAddressOptional.get().toString() : "null"; + String peersNodeAddress = peersNodeAddressOptional.map(NodeAddress::toString).orElse("null"); log.debug("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + "ShutDown connection:" + "\npeersNodeAddress=" + peersNodeAddress @@ -488,25 +444,24 @@ public void shutDown(CloseConnectionReason closeConnectionReason, @Nullable Runn if (closeConnectionReason.sendCloseMessage) { new Thread(() -> { Thread.currentThread().setName("Connection:SendCloseConnectionMessage-" + this.uid); - Log.traceCall("sendCloseConnectionMessage"); try { String reason = closeConnectionReason == CloseConnectionReason.RULE_VIOLATION ? - sharedModel.getRuleViolation().name() : closeConnectionReason.name(); + getRuleViolation().name() : closeConnectionReason.name(); sendMessage(new CloseConnectionMessage(reason)); - setStopFlags(); + stopped = true; Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); } catch (Throwable t) { log.error(t.getMessage()); t.printStackTrace(); } finally { - setStopFlags(); + stopped = true; UserThread.execute(() -> doShutDown(closeConnectionReason, shutDownCompleteHandler)); } }).start(); } else { - setStopFlags(); + stopped = true; doShutDown(closeConnectionReason, shutDownCompleteHandler); } } else { @@ -516,18 +471,11 @@ public void shutDown(CloseConnectionReason closeConnectionReason, @Nullable Runn } } - private void setStopFlags() { - stopped = true; - sharedModel.stop(); - if (inputHandler != null) - inputHandler.stop(); - } - private void doShutDown(CloseConnectionReason closeConnectionReason, @Nullable Runnable shutDownCompleteHandler) { // Use UserThread.execute as its not clear if that is called from a non-UserThread UserThread.execute(() -> connectionListener.onDisconnect(closeConnectionReason, this)); try { - sharedModel.getSocket().close(); + socket.close(); } catch (SocketException e) { log.trace("SocketException at shutdown might be expected " + e.getMessage()); } catch (IOException e) { @@ -535,6 +483,14 @@ private void doShutDown(CloseConnectionReason closeConnectionReason, @Nullable R e.printStackTrace(); } finally { protoOutputStream.onConnectionShutdown(); + + try { + protoInputStream.close(); + } catch (IOException e) { + log.error(e.getMessage()); + e.printStackTrace(); + } + MoreExecutors.shutdownAndAwaitTermination(singleThreadExecutor, 500, TimeUnit.MILLISECONDS); log.debug("Connection shutdown complete " + this.toString()); @@ -571,12 +527,20 @@ public String toString() { @SuppressWarnings("unused") public String printDetails() { + String portInfo; + if (socket.getLocalPort() == 0) + portInfo = "port=" + socket.getPort(); + else + portInfo = "localPort=" + socket.getLocalPort() + "/port=" + socket.getPort(); + return "Connection{" + "peerAddress=" + peersNodeAddressOptional + ", peerType=" + peerType + ", portInfo=" + portInfo + ", uid='" + uid + '\'' + - ", sharedSpace=" + sharedModel.toString() + + ", ruleViolation=" + ruleViolation + + ", ruleViolations=" + ruleViolations + + ", supportedCapabilities=" + capabilities + ", stopped=" + stopped + '}'; } @@ -585,143 +549,77 @@ public String printDetails() { /////////////////////////////////////////////////////////////////////////////////////////// // SharedSpace /////////////////////////////////////////////////////////////////////////////////////////// - /** * Holds all shared data between Connection and InputHandler * Runs in same thread as Connection */ - private static class SharedModel { - private static final Logger log = LoggerFactory.getLogger(SharedModel.class); - - private final Connection connection; - private final Socket socket; - private final ConcurrentHashMap ruleViolations = new ConcurrentHashMap<>(); - // mutable - private volatile boolean stopped; - private CloseConnectionReason closeConnectionReason; - private RuleViolation ruleViolation; - @Nullable - private List supportedCapabilities; - - public SharedModel(Connection connection, Socket socket) { - this.connection = connection; - this.socket = socket; - } - - public boolean reportInvalidRequest(RuleViolation ruleViolation) { - log.warn("We got reported the ruleViolation {} at connection {}", ruleViolation, connection); - int numRuleViolations; - numRuleViolations = ruleViolations.getOrDefault(ruleViolation, 0); - - numRuleViolations++; - ruleViolations.put(ruleViolation, numRuleViolations); - - if (numRuleViolations >= ruleViolation.maxTolerance) { - log.warn("We close connection as we received too many corrupt requests.\n" + - "numRuleViolations={}\n\t" + - "corruptRequest={}\n\t" + - "corruptRequests={}\n\t" + - "connection={}", numRuleViolations, ruleViolation, ruleViolations.toString(), connection); - this.ruleViolation = ruleViolation; - if (ruleViolation == RuleViolation.PEER_BANNED) { - log.warn("We close connection due RuleViolation.PEER_BANNED. peersNodeAddress={}", connection.getPeersNodeAddressOptional()); - shutDown(CloseConnectionReason.PEER_BANNED); - } else if (ruleViolation == RuleViolation.INVALID_CLASS) { - log.warn("We close connection due RuleViolation.INVALID_CLASS"); - shutDown(CloseConnectionReason.INVALID_CLASS_RECEIVED); - } else { - log.warn("We close connection due RuleViolation.RULE_VIOLATION"); - shutDown(CloseConnectionReason.RULE_VIOLATION); - } - - return true; + public boolean reportInvalidRequest(RuleViolation ruleViolation) { + log.warn("We got reported the ruleViolation {} at connection {}", ruleViolation, this); + int numRuleViolations; + numRuleViolations = ruleViolations.getOrDefault(ruleViolation, 0); + + numRuleViolations++; + ruleViolations.put(ruleViolation, numRuleViolations); + + if (numRuleViolations >= ruleViolation.maxTolerance) { + log.warn("We close connection as we received too many corrupt requests.\n" + + "numRuleViolations={}\n\t" + + "corruptRequest={}\n\t" + + "corruptRequests={}\n\t" + + "connection={}", numRuleViolations, ruleViolation, ruleViolations.toString(), this); + this.ruleViolation = ruleViolation; + if (ruleViolation == RuleViolation.PEER_BANNED) { + log.warn("We close connection due RuleViolation.PEER_BANNED. peersNodeAddress={}", getPeersNodeAddressOptional()); + shutDown(CloseConnectionReason.PEER_BANNED); + } else if (ruleViolation == RuleViolation.INVALID_CLASS) { + log.warn("We close connection due RuleViolation.INVALID_CLASS"); + shutDown(CloseConnectionReason.INVALID_CLASS_RECEIVED); } else { - return false; + log.warn("We close connection due RuleViolation.RULE_VIOLATION"); + shutDown(CloseConnectionReason.RULE_VIOLATION); } - } - - @Nullable - public List getSupportedCapabilities() { - return supportedCapabilities; - } - - @SuppressWarnings("NullableProblems") - public void setSupportedCapabilities(List supportedCapabilities) { - this.supportedCapabilities = supportedCapabilities; - connection.capabilitiesListeners.forEach(l -> { - SupportedCapabilitiesListener supportedCapabilitiesListener = l.get(); - if (supportedCapabilitiesListener != null) - supportedCapabilitiesListener.onChanged(supportedCapabilities); - }); - } - - public void handleConnectionException(Throwable e) { - if (e instanceof SocketException) { - if (socket.isClosed()) - closeConnectionReason = CloseConnectionReason.SOCKET_CLOSED; - else - closeConnectionReason = CloseConnectionReason.RESET; - - log.info("SocketException (expected if connection lost). closeConnectionReason={}; connection={}", closeConnectionReason, connection); - } else if (e instanceof SocketTimeoutException || e instanceof TimeoutException) { - closeConnectionReason = CloseConnectionReason.SOCKET_TIMEOUT; - log.info("Shut down caused by exception {} on connection={}", e.toString(), connection); - } else if (e instanceof EOFException) { - closeConnectionReason = CloseConnectionReason.TERMINATED; - log.warn("Shut down caused by exception {} on connection={}", e.toString(), connection); - } else if (e instanceof OptionalDataException || e instanceof StreamCorruptedException) { - closeConnectionReason = CloseConnectionReason.CORRUPTED_DATA; - log.warn("Shut down caused by exception {} on connection={}", e.toString(), connection); - } else { - // TODO sometimes we get StreamCorruptedException, OptionalDataException, IllegalStateException - closeConnectionReason = CloseConnectionReason.UNKNOWN_EXCEPTION; - log.warn("Unknown reason for exception at socket: {}\n\t" + - "peer={}\n\t" + - "Exception={}", - socket.toString(), - connection.peersNodeAddressOptional, - e.toString()); - e.printStackTrace(); - } - shutDown(closeConnectionReason); - } - - public void shutDown(CloseConnectionReason closeConnectionReason) { - if (!stopped) { - stopped = true; - connection.shutDown(closeConnectionReason); - } - } - public Socket getSocket() { - return socket; - } - - public void stop() { - stopped = true; - } - - public RuleViolation getRuleViolation() { - return ruleViolation; + return true; + } else { + return false; } + } - @Override - public String toString() { - return "SharedModel{" + - "\n connection=" + connection + - ",\n socket=" + socket + - ",\n ruleViolations=" + ruleViolations + - ",\n stopped=" + stopped + - ",\n closeConnectionReason=" + closeConnectionReason + - ",\n ruleViolation=" + ruleViolation + - ",\n supportedCapabilities=" + supportedCapabilities + - "\n}"; + private void handleException(Throwable e) { + CloseConnectionReason closeConnectionReason; + + if (e instanceof SocketException) { + if (socket.isClosed()) + closeConnectionReason = CloseConnectionReason.SOCKET_CLOSED; + else + closeConnectionReason = CloseConnectionReason.RESET; + + log.info("SocketException (expected if connection lost). closeConnectionReason={}; connection={}", closeConnectionReason, this); + } else if (e instanceof SocketTimeoutException || e instanceof TimeoutException) { + closeConnectionReason = CloseConnectionReason.SOCKET_TIMEOUT; + log.info("Shut down caused by exception {} on connection={}", e.toString(), this); + } else if (e instanceof EOFException) { + closeConnectionReason = CloseConnectionReason.TERMINATED; + log.warn("Shut down caused by exception {} on connection={}", e.toString(), this); + } else if (e instanceof OptionalDataException || e instanceof StreamCorruptedException) { + closeConnectionReason = CloseConnectionReason.CORRUPTED_DATA; + log.warn("Shut down caused by exception {} on connection={}", e.toString(), this); + } else { + // TODO sometimes we get StreamCorruptedException, OptionalDataException, IllegalStateException + closeConnectionReason = CloseConnectionReason.UNKNOWN_EXCEPTION; + log.warn("Unknown reason for exception at socket: {}\n\t" + + "peer={}\n\t" + + "Exception={}", + socket.toString(), + this.peersNodeAddressOptional, + e.toString()); + e.printStackTrace(); } + shutDown(closeConnectionReason); } - /////////////////////////////////////////////////////////////////////////////////////////// // InputHandler /////////////////////////////////////////////////////////////////////////////////////////// @@ -729,65 +627,29 @@ public String toString() { // Runs in same thread as Connection, receives a message, performs several checks on it // (including throttling limits, validity and statistics) // and delivers it to the message listener given in the constructor. - private static class InputHandler implements Runnable { - private static final Logger log = LoggerFactory.getLogger(InputHandler.class); - - private final SharedModel sharedModel; - private final InputStream protoInputStream; - private final String portInfo; - private final MessageListener messageListener; + private InputStream protoInputStream; private final NetworkProtoResolver networkProtoResolver; - private volatile boolean stopped; private long lastReadTimeStamp; private boolean threadNameSet; - public InputHandler(SharedModel sharedModel, - InputStream protoInputStream, - String portInfo, - MessageListener messageListener, - NetworkProtoResolver networkProtoResolver) { - this.sharedModel = sharedModel; - this.protoInputStream = protoInputStream; - this.portInfo = portInfo; - this.messageListener = messageListener; - this.networkProtoResolver = networkProtoResolver; - } - - public void stop() { - if (!stopped) { - try { - protoInputStream.close(); - } catch (IOException e) { - log.error("IOException at InputHandler.stop\n" + e.getMessage()); - e.printStackTrace(); - } finally { - stopped = true; - } - } - } - @Override public void run() { try { Thread.currentThread().setName("InputHandler"); while (!stopped && !Thread.currentThread().isInterrupted()) { - if (!threadNameSet && sharedModel.connection != null && - sharedModel.connection.getPeersNodeAddressOptional().isPresent()) { - Thread.currentThread().setName("InputHandler-" + sharedModel.connection.getPeersNodeAddressOptional().get().getFullAddress()); + if (!threadNameSet && getPeersNodeAddressOptional().isPresent()) { + Thread.currentThread().setName("InputHandler-" + getPeersNodeAddressOptional().get().getFullAddress()); threadNameSet = true; } try { - if (sharedModel.getSocket() != null && - sharedModel.getSocket().isClosed()) { - log.warn("Socket is null or closed socket={}", sharedModel.getSocket()); - stopAndShutDown(CloseConnectionReason.SOCKET_CLOSED); + if (socket != null && + socket.isClosed()) { + log.warn("Socket is null or closed socket={}", socket); + shutDown(CloseConnectionReason.SOCKET_CLOSED); return; } - Connection connection = checkNotNull(sharedModel.connection, "connection must not be null"); - log.trace("InputHandler waiting for incoming network_messages.\n\tConnection=" + connection); - // Throttle inbound network_messages long now = System.currentTimeMillis(); long elapsed = now - lastReadTimeStamp; @@ -806,7 +668,7 @@ public void run() { log.info("proto is null because protoInputStream.read()=-1 (EOF). That is expected if client got stopped without proper shutdown."); else log.warn("proto is null. protoInputStream.read()=" + protoInputStream.read()); - stopAndShutDown(CloseConnectionReason.NO_PROTO_BUFFER_ENV); + shutDown(CloseConnectionReason.NO_PROTO_BUFFER_ENV); return; } @@ -840,10 +702,10 @@ public void run() { }*/ // We want to track the size of each object even if it is invalid data - connection.statistic.addReceivedBytes(size); + statistic.addReceivedBytes(size); // We want to track the network_messages also before the checks, so do it early... - connection.statistic.addReceivedMessage(networkEnvelope); + statistic.addReceivedMessage(networkEnvelope); // First we check the size boolean exceeds; @@ -870,7 +732,7 @@ public void run() { return; } - if (connection.violatesThrottleLimit(networkEnvelope) + if (violatesThrottleLimit(networkEnvelope) && reportInvalidRequest(RuleViolation.THROTTLE_LIMIT_EXCEEDED)) return; @@ -884,27 +746,27 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { return; } - if (sharedModel.getSupportedCapabilities() == null && networkEnvelope instanceof SupportedCapabilitiesMessage) - sharedModel.setSupportedCapabilities(((SupportedCapabilitiesMessage) networkEnvelope).getSupportedCapabilities()); + if (networkEnvelope instanceof SupportedCapabilitiesMessage) + capabilities.set(((SupportedCapabilitiesMessage) networkEnvelope).getSupportedCapabilities()); if (networkEnvelope instanceof CloseConnectionMessage) { // If we get a CloseConnectionMessage we shut down log.info("CloseConnectionMessage received. Reason={}\n\t" + - "connection={}", proto.getCloseConnectionMessage().getReason(), connection); + "connection={}", proto.getCloseConnectionMessage().getReason(), this); if (CloseConnectionReason.PEER_BANNED.name().equals(proto.getCloseConnectionMessage().getReason())) { log.warn("We got shut down because we are banned by the other peer. (InputHandler.run CloseConnectionMessage)"); - stopAndShutDown(CloseConnectionReason.PEER_BANNED); + shutDown(CloseConnectionReason.PEER_BANNED); } else { - stopAndShutDown(CloseConnectionReason.CLOSE_REQUESTED_BY_PEER); + shutDown(CloseConnectionReason.CLOSE_REQUESTED_BY_PEER); } return; } else if (!stopped) { // We don't want to get the activity ts updated by ping/pong msg if (!(networkEnvelope instanceof KeepAliveMessage)) - connection.statistic.updateLastActivityTimestamp(); + statistic.updateLastActivityTimestamp(); if (networkEnvelope instanceof GetDataRequest) - connection.setPeerType(PeerType.INITIAL_DATA_REQUEST); + setPeerType(PeerType.INITIAL_DATA_REQUEST); // First a seed node gets a message from a peer (PreliminaryDataRequest using // AnonymousMessage interface) which does not have its hidden service @@ -921,7 +783,7 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { // 4. DirectMessage (implements SendersNodeAddressMessage) if (networkEnvelope instanceof SendersNodeAddressMessage) { NodeAddress senderNodeAddress = ((SendersNodeAddressMessage) networkEnvelope).getSenderNodeAddress(); - Optional peersNodeAddressOptional = connection.getPeersNodeAddressOptional(); + Optional peersNodeAddressOptional = getPeersNodeAddressOptional(); if (peersNodeAddressOptional.isPresent()) { // If we have already the peers address we check again if it matches our stored one checkArgument(peersNodeAddressOptional.get().equals(senderNodeAddress), @@ -931,14 +793,14 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { // We must not shut down a banned peer at that moment as it would trigger a connection termination // and we could not send the CloseConnectionMessage. // We check for a banned peer inside setPeersNodeAddress() and shut down if banned. - connection.setPeersNodeAddress(senderNodeAddress); + setPeersNodeAddress(senderNodeAddress); } } if (networkEnvelope instanceof PrefixedSealedAndSignedMessage) - connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER); + setPeerType(Connection.PeerType.DIRECT_MSG_PEER); - messageListener.onMessage(networkEnvelope, connection); + onMessage(networkEnvelope, this); } } catch (InvalidClassException e) { log.error(e.getMessage()); @@ -956,33 +818,4 @@ && reportInvalidRequest(RuleViolation.WRONG_NETWORK_ID)) { handleException(t); } } - - private void stopAndShutDown(CloseConnectionReason reason) { - stop(); - sharedModel.shutDown(reason); - } - - private void handleException(Throwable e) { - stop(); - if (sharedModel != null) - sharedModel.handleConnectionException(e); - } - - - private boolean reportInvalidRequest(RuleViolation ruleViolation) { - boolean causedShutDown = sharedModel.reportInvalidRequest(ruleViolation); - if (causedShutDown) - stop(); - return causedShutDown; - } - - @Override - public String toString() { - return "InputHandler{" + - "sharedSpace=" + sharedModel + - ", port=" + portInfo + - ", stopped=" + stopped + - '}'; - } - } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/ConnectionConfig.java b/p2p/src/main/java/bisq/network/p2p/network/ConnectionConfig.java new file mode 100644 index 00000000000..abf77d7a3a4 --- /dev/null +++ b/p2p/src/main/java/bisq/network/p2p/network/ConnectionConfig.java @@ -0,0 +1,68 @@ +/* + * 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.network; + +import bisq.network.NetworkOptionKeys; + +import com.google.inject.name.Named; + +import javax.inject.Inject; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ConnectionConfig { + public static final int MSG_THROTTLE_PER_SEC = 200; // With MAX_MSG_SIZE of 200kb results in bandwidth of 40MB/sec or 5 mbit/sec + public static final int MSG_THROTTLE_PER_10_SEC = 1000; // With MAX_MSG_SIZE of 200kb results in bandwidth of 20MB/sec or 2.5 mbit/sec + public static final int SEND_MSG_THROTTLE_TRIGGER = 20; // Time in ms when we trigger a sleep if 2 messages are sent + public static final int SEND_MSG_THROTTLE_SLEEP = 50; // Pause in ms to sleep if we get too many messages to send + + @Getter + private int msgThrottlePerSec; + @Getter + private int msgThrottlePer10Sec; + @Getter + private int sendMsgThrottleTrigger; + @Getter + private int sendMsgThrottleSleep; + + @Inject + public ConnectionConfig(@Named(NetworkOptionKeys.MSG_THROTTLE_PER_SEC) int msgThrottlePerSec, + @Named(NetworkOptionKeys.MSG_THROTTLE_PER_10_SEC) int msgThrottlePer10Sec, + @Named(NetworkOptionKeys.SEND_MSG_THROTTLE_TRIGGER) int sendMsgThrottleTrigger, + @Named(NetworkOptionKeys.SEND_MSG_THROTTLE_SLEEP) int sendMsgThrottleSleep) { + this.msgThrottlePerSec = msgThrottlePerSec; + this.msgThrottlePer10Sec = msgThrottlePer10Sec; + this.sendMsgThrottleTrigger = sendMsgThrottleTrigger; + this.sendMsgThrottleSleep = sendMsgThrottleSleep; + + log.info(this.toString()); + } + + + @Override + public String toString() { + return "ConnectionConfig{" + + "\n msgThrottlePerSec=" + msgThrottlePerSec + + ",\n msgThrottlePer10Sec=" + msgThrottlePer10Sec + + ",\n sendMsgThrottleTrigger=" + sendMsgThrottleTrigger + + ",\n sendMsgThrottleSleep=" + sendMsgThrottleSleep + + "\n}"; + } +} diff --git a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java index 12f2608a843..b49bff1e737 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java @@ -20,7 +20,6 @@ import bisq.network.p2p.NodeAddress; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkProtoResolver; import java.net.ServerSocket; @@ -42,8 +41,6 @@ public class LocalhostNetworkNode extends NetworkNode { private static int simulateTorDelayTorNode = 500; private static int simulateTorDelayHiddenService = 500; - private String address; - public static void setSimulateTorDelayTorNode(int simulateTorDelayTorNode) { LocalhostNetworkNode.simulateTorDelayTorNode = simulateTorDelayTorNode; } @@ -57,15 +54,8 @@ public static void setSimulateTorDelayHiddenService(int simulateTorDelayHiddenSe // Constructor /////////////////////////////////////////////////////////////////////////////////////////// - public LocalhostNetworkNode(String address, int port, NetworkProtoResolver networkProtoResolver) { - super(port, networkProtoResolver); - if (null != address && !address.trim().isEmpty()) { - this.address = address; - } - } - public LocalhostNetworkNode(int port, NetworkProtoResolver networkProtoResolver) { - this(null, port, networkProtoResolver); + super(port, networkProtoResolver); } @Override @@ -77,22 +67,18 @@ public void start(@Nullable SetupListener setupListener) { // simulate tor connection delay UserThread.runAfter(() -> { - Log.traceCall("torNode created"); + nodeAddressProperty.set(new NodeAddress("localhost", servicePort)); + setupListeners.stream().forEach(SetupListener::onTorNodeReady); // simulate tor HS publishing delay UserThread.runAfter(() -> { - Log.traceCall("hiddenService created"); try { startServer(new ServerSocket(servicePort)); } catch (IOException e) { e.printStackTrace(); log.error("Exception at startServer: " + e.getMessage()); } - final NodeAddress nodeAddress = address == null ? - new NodeAddress("localhost", servicePort) : - new NodeAddress(address); - nodeAddressProperty.set(nodeAddress); setupListeners.stream().forEach(SetupListener::onHiddenServicePublished); }, simulateTorDelayTorNode, TimeUnit.MILLISECONDS); }, simulateTorDelayHiddenService, TimeUnit.MILLISECONDS); 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 76b04b962d5..8ff8e04334d 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java @@ -20,7 +20,6 @@ import bisq.network.p2p.NodeAddress; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; import bisq.common.proto.network.NetworkProtoResolver; import bisq.common.util.Utilities; @@ -114,6 +113,11 @@ public SettableFuture sendMessage(@NotNull NodeAddress peersNodeAddr final SettableFuture resultFuture = SettableFuture.create(); ListenableFuture future = executorService.submit(() -> { Thread.currentThread().setName("NetworkNode:SendMessage-to-" + peersNodeAddress); + + if(peersNodeAddress.equals(getNodeAddress())){ + throw new ConnectException("We do not send a message to ourselves"); + } + OutboundConnection outboundConnection = null; try { // can take a while when using tor @@ -220,7 +224,7 @@ private InboundConnection getInboundConnection(@NotNull NodeAddress peersNodeAdd Optional inboundConnectionOptional = lookupInBoundConnection(peersNodeAddress); if (inboundConnectionOptional.isPresent()) { InboundConnection connection = inboundConnectionOptional.get(); - log.trace("We have found a connection in inBoundConnections. Connection.uid=" + connection.getUid()); + log.trace("We have found a connection in inBoundConnections. Connection.uid={}", connection.getUid()); if (connection.isStopped()) { log.warn("We have a connection which is already stopped in inBoundConnections. Connection.uid=" + connection.getUid()); inBoundConnections.remove(connection); @@ -238,7 +242,7 @@ private OutboundConnection getOutboundConnection(@NotNull NodeAddress peersNodeA Optional outboundConnectionOptional = lookupOutBoundConnection(peersNodeAddress); if (outboundConnectionOptional.isPresent()) { OutboundConnection connection = outboundConnectionOptional.get(); - log.trace("We have found a connection in outBoundConnections. Connection.uid=" + connection.getUid()); + log.trace("We have found a connection in outBoundConnections. Connection.uid={}", connection.getUid()); if (connection.isStopped()) { log.warn("We have a connection which is already stopped in outBoundConnections. Connection.uid=" + connection.getUid()); outBoundConnections.remove(connection); @@ -258,7 +262,6 @@ public Socks5Proxy getSocksProxy() { public SettableFuture sendMessage(Connection connection, NetworkEnvelope networkEnvelope) { - Log.traceCall("\n\tmessage=" + Utilities.toTruncatedString(networkEnvelope) + "\n\tconnection=" + connection); // connection.sendMessage might take a bit (compression, write to stream), so we use a thread to not block ListenableFuture future = executorService.submit(() -> { Thread.currentThread().setName("NetworkNode:SendMessage-to-" + connection.getUid()); @@ -307,7 +310,6 @@ public Set getNodeAddressesOfConfirmedConnections() { public void shutDown(Runnable shutDownCompleteHandler) { - Log.traceCall(); if (!shutDownInProgress) { shutDownInProgress = true; if (server != null) { @@ -397,7 +399,7 @@ public void onConnection(Connection connection) { @Override public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { - log.trace("onDisconnect at server socket connectionListener\n\tconnection={}" + connection); + log.trace("onDisconnect at server socket connectionListener\n\tconnection={}", connection); //noinspection SuspiciousMethodCalls inBoundConnections.remove(connection); printInboundConnections(); @@ -453,4 +455,9 @@ private void printInboundConnections() { public NodeAddress getNodeAddress() { return nodeAddressProperty.get(); } + + @Nullable + public ObjectProperty getNodeAddressProperty() { + return nodeAddressProperty; + } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/Server.java b/p2p/src/main/java/bisq/network/p2p/network/Server.java index a7a41cbda54..ba235c5a09d 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/Server.java +++ b/p2p/src/main/java/bisq/network/p2p/network/Server.java @@ -17,7 +17,6 @@ package bisq.network.p2p.network; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkProtoResolver; import java.net.ServerSocket; @@ -51,7 +50,6 @@ public Server(ServerSocket serverSocket, ConnectionListener connectionListener, NetworkProtoResolver networkProtoResolver) { this.networkProtoResolver = networkProtoResolver; - Log.traceCall(); this.serverSocket = serverSocket; this.messageListener = messageListener; this.connectionListener = connectionListener; @@ -59,7 +57,6 @@ public Server(ServerSocket serverSocket, @Override public void run() { - Log.traceCall(); try { // Thread created by NetworkNode Thread.currentThread().setName("Server-" + serverSocket.getLocalPort()); @@ -77,8 +74,8 @@ public void run() { log.debug("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" + "Server created new inbound connection:" + "\nlocalPort/port=" + serverSocket.getLocalPort() - + "/" + socket.getPort() - + "\nconnection.uid=" + connection.getUid() + + "/{}" + + "\nconnection.uid={}", socket.getPort(), connection.getUid() + "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"); if (!stopped) @@ -98,7 +95,6 @@ public void run() { } public void shutDown() { - Log.traceCall(); if (!stopped) { stopped = true; diff --git a/p2p/src/main/java/bisq/network/p2p/network/SupportedCapabilitiesListener.java b/p2p/src/main/java/bisq/network/p2p/network/SupportedCapabilitiesListener.java index 1209fd3cc6b..848b6479822 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/SupportedCapabilitiesListener.java +++ b/p2p/src/main/java/bisq/network/p2p/network/SupportedCapabilitiesListener.java @@ -17,8 +17,8 @@ package bisq.network.p2p.network; -import java.util.List; +import bisq.common.app.Capabilities; public interface SupportedCapabilitiesListener { - void onChanged(List supportedCapabilities); + void onChanged(Capabilities supportedCapabilities); } diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index d6ba1028f83..6659c603640 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -22,7 +22,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkProtoResolver; import bisq.common.util.Utilities; @@ -93,6 +92,7 @@ public TorNetworkNode(int servicePort, NetworkProtoResolver networkProtoResolver super(servicePort, networkProtoResolver); this.torMode = torMode; this.streamIsolation = useStreamIsolation; + createExecutorService(); } @@ -107,8 +107,6 @@ public void start(@Nullable SetupListener setupListener) { if (setupListener != null) addSetupListener(setupListener); - createExecutorService(); - // Create the tor node (takes about 6 sec.) createTorAndHiddenService(Utils.findFreeSystemPort(), servicePort); } @@ -150,7 +148,6 @@ public Socks5Proxy getSocksProxy() { } public void shutDown(@Nullable Runnable shutDownCompleteHandler) { - Log.traceCall(); BooleanProperty torNetworkNodeShutDown = torNetworkNodeShutDown(); BooleanProperty networkNodeShutDown = networkNodeShutDown(); BooleanProperty shutDownTimerTriggered = shutDownTimerTriggered(); @@ -225,7 +222,6 @@ private BooleanProperty shutDownTimerTriggered() { /////////////////////////////////////////////////////////////////////////////////////////// private void restartTor(String errorMessage) { - Log.traceCall(); log.info("Restarting Tor"); restartCounter++; if (restartCounter <= MAX_RESTART_ATTEMPTS) { @@ -249,17 +245,16 @@ private void restartTor(String errorMessage) { /////////////////////////////////////////////////////////////////////////////////////////// private void createTorAndHiddenService(int localPort, int servicePort) { - Log.traceCall(); - ListenableFuture future = executorService.submit(() -> { try { // get tor Tor.setDefault(torMode.getTor()); - UserThread.execute(() -> setupListeners.forEach(SetupListener::onTorNodeReady)); // start hidden service long ts2 = new Date().getTime(); hiddenServiceSocket = new HiddenServiceSocket(localPort, torMode.getHiddenServiceDirectory(), servicePort); + nodeAddressProperty.set(new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort())); + UserThread.execute(() -> setupListeners.forEach(SetupListener::onTorNodeReady)); hiddenServiceSocket.addReadyListener(socket -> { try { log.info("\n################################################################\n" + @@ -270,7 +265,6 @@ private void createTorAndHiddenService(int localPort, int servicePort) { @Override public void run() { try { - Log.traceCall("hiddenService created"); nodeAddressProperty.set(new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort())); startServer(socket); UserThread.execute(() -> setupListeners.forEach(SetupListener::onHiddenServicePublished)); @@ -288,10 +282,18 @@ public void run() { }); log.info("It will take some time for the HS to be reachable (~40 seconds). You will be notified about this"); } catch (TorCtlException e) { - log.error("Tor node creation failed: " + (e.getCause() != null ? e.getCause().toString() : e.toString())); - restartTor(e.getMessage()); + String msg = e.getCause() != null ? e.getCause().toString() : e.toString(); + log.error("Tor node creation failed: {}", msg); + if (e.getCause() instanceof IOException) { + // Since we cannot connect to Tor, we cannot do nothing. + // Furthermore, we have no hidden services started yet, so there is no graceful + // shutdown needed either + UserThread.execute(() -> setupListeners.forEach(s -> s.onSetupFailed(new RuntimeException(msg)))); + } else { + restartTor(e.getMessage()); + } } catch (IOException e) { - log.error("Could not connect to running Tor: " + e.getMessage()); + log.error("Could not connect to running Tor: {}", e.getMessage()); // Since we cannot connect to Tor, we cannot do nothing. // Furthermore, we have no hidden services started yet, so there is no graceful // shutdown needed either diff --git a/p2p/src/main/java/bisq/network/p2p/peers/BroadcastHandler.java b/p2p/src/main/java/bisq/network/p2p/peers/BroadcastHandler.java index bcd3d676d16..dde4062d20a 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/BroadcastHandler.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/BroadcastHandler.java @@ -24,7 +24,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.util.Utilities; import com.google.common.util.concurrent.FutureCallback; @@ -117,8 +116,6 @@ public void broadcast(BroadcastMessage message, @Nullable NodeAddress sender, Re this.resultHandler = resultHandler; this.listener = listener; - Log.traceCall("Sender=" + sender + "\n\t" + - "Message=" + Utilities.toTruncatedString(message)); Set connectedPeersSet = networkNode.getConfirmedConnections() .stream() .filter(connection -> !connection.getPeersNodeAddressOptional().get().equals(sender)) @@ -170,15 +167,12 @@ private void sendToPeer(Connection connection, BroadcastMessage message) { if (!connection.isStopped()) { if (connection.noCapabilityRequiredOrCapabilityIsSupported(message)) { NodeAddress nodeAddress = connection.getPeersNodeAddressOptional().get(); - log.trace("Broadcast message to " + nodeAddress + "."); SettableFuture future = networkNode.sendMessage(connection, message); Futures.addCallback(future, new FutureCallback() { @Override public void onSuccess(Connection connection) { numOfCompletedBroadcasts++; if (!stopped) { - log.trace("Broadcast to " + nodeAddress + " succeeded."); - if (listener != null) listener.onBroadcasted(message, numOfCompletedBroadcasts); @@ -211,8 +205,6 @@ public void onFailure(@NotNull Throwable throwable) { } } }); - } else { - log.debug("We did not send the message because the peer does not support our required capabilities. message={}, peers supportedCapabilities={}", Utilities.toTruncatedString(message, 200), connection.getSupportedCapabilities()); } } else { onFault("Connection stopped already", false); diff --git a/p2p/src/main/java/bisq/network/p2p/peers/Broadcaster.java b/p2p/src/main/java/bisq/network/p2p/peers/Broadcaster.java index 004fcfae802..a5f85879e75 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/Broadcaster.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/Broadcaster.java @@ -21,9 +21,6 @@ import bisq.network.p2p.network.NetworkNode; import bisq.network.p2p.storage.messages.BroadcastMessage; -import bisq.common.app.Log; -import bisq.common.util.Utilities; - import javax.inject.Inject; import java.util.Set; @@ -60,9 +57,6 @@ public void shutDown() { public void broadcast(BroadcastMessage message, @Nullable NodeAddress sender, @Nullable BroadcastHandler.Listener listener, boolean isDataOwner) { - Log.traceCall("Sender=" + sender + "\n\t" + - "Message=" + Utilities.toTruncatedString(message)); - BroadcastHandler broadcastHandler = new BroadcastHandler(networkNode, peerManager); broadcastHandler.broadcast(message, sender, this, listener, isDataOwner); broadcastHandlers.add(broadcastHandler); 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 7292866449f..21008b18bc5 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -32,7 +32,7 @@ import bisq.common.Clock; import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; +import bisq.common.app.Capabilities; import bisq.common.proto.persistable.PersistedDataHost; import bisq.common.proto.persistable.PersistenceProtoResolver; import bisq.common.storage.Storage; @@ -161,8 +161,6 @@ public void onAwakeFromStandby(long missedMs) { } public void shutDown() { - Log.traceCall(); - networkNode.removeConnectionListener(this); clock.removeListener(listener); stopCheckMaxConnectionsTimer(); @@ -178,7 +176,7 @@ public void readPersisted() { PeerList persistedPeerList = storage.initAndGetPersistedWithFileName("PeerList", 1000); if (persistedPeerList != null) { long peersWithNoCapabilitiesSet = persistedPeerList.getList().stream() - .filter(e -> e.getSupportedCapabilities().isEmpty()) + .filter(e -> e.getCapabilities().isEmpty()) .mapToInt(e -> 1) .count(); if (peersWithNoCapabilitiesSet > 100) { @@ -301,7 +299,6 @@ private void doHouseKeeping() { } private boolean checkMaxConnections() { - Log.traceCall("maxConnections=" + maxConnections); Set allConnections = networkNode.getAllConnections(); int size = allConnections.size(); log.info("We have {} connections open. Our limit is {}", size, maxConnections); @@ -349,7 +346,7 @@ private boolean checkMaxConnections() { candidates.sort((o1, o2) -> ((Long) o1.getStatistic().getLastActivityTimestamp()).compareTo(((Long) o2.getStatistic().getLastActivityTimestamp()))); Connection connection = candidates.remove(0); log.info("checkMaxConnections: Num candidates for shut down={}. We close oldest connection: {}", candidates.size(), connection); - log.debug("We are going to shut down the oldest connection.\n\tconnection=" + connection.toString()); + log.debug("We are going to shut down the oldest connection.\n\tconnection={}", connection.toString()); if (!connection.isStopped()) connection.shutDown(CloseConnectionReason.TOO_MANY_CONNECTIONS_OPEN, () -> UserThread.runAfter(this::checkMaxConnections, 100, TimeUnit.MILLISECONDS)); return true; @@ -366,7 +363,6 @@ private boolean checkMaxConnections() { } private void removeAnonymousPeers() { - Log.traceCall(); networkNode.getAllConnections().stream() .filter(connection -> !connection.hasPeersNodeAddress()) .forEach(connection -> UserThread.runAfter(() -> { @@ -375,14 +371,13 @@ private void removeAnonymousPeers() { // because he needs longer for the HS publishing if (!connection.hasPeersNodeAddress() && !connection.isStopped()) { log.debug("We close the connection as the peer address is still unknown.\n\t" + - "connection=" + connection); + "connection={}", connection); connection.shutDown(CloseConnectionReason.UNKNOWN_PEER_ADDRESS); } }, REMOVE_ANONYMOUS_PEER_SEC)); } private void removeSuperfluousSeedNodes() { - Log.traceCall(); if (networkNode.getConfirmedConnections().size() > disconnectFromSeedNode) { List seedNodes = networkNode.getConfirmedConnections().stream() .filter(this::isSeedNode) @@ -392,7 +387,7 @@ private void removeSuperfluousSeedNodes() { seedNodes.sort((o1, o2) -> ((Long) o1.getStatistic().getLastActivityTimestamp()).compareTo(((Long) o2.getStatistic().getLastActivityTimestamp()))); log.debug("Number of seed node connections to disconnect. Current size=" + seedNodes.size()); Connection connection = seedNodes.get(0); - log.debug("We are going to shut down the oldest connection.\n\tconnection=" + connection.toString()); + log.debug("We are going to shut down the oldest connection.\n\tconnection={}", connection.toString()); connection.shutDown(CloseConnectionReason.TOO_MANY_SEED_NODES_CONNECTED, () -> UserThread.runAfter(this::removeSuperfluousSeedNodes, 200, TimeUnit.MILLISECONDS)); } @@ -426,7 +421,6 @@ private Peer removeReportedPeer(NodeAddress nodeAddress) { } private void removeTooOldReportedPeers() { - Log.traceCall(); List reportedPeersClone = new ArrayList<>(reportedPeers); Set reportedPeersToRemove = reportedPeersClone.stream() .filter(reportedPeer -> new Date().getTime() - reportedPeer.getDate().getTime() > MAX_AGE) @@ -455,12 +449,11 @@ public void addToReportedPeers(Set reportedPeersToAdd, Connection connecti // 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.reportIllegalRequest(RuleViolation.TOO_MANY_REPORTED_PEERS_SENT); + connection.reportInvalidRequest(RuleViolation.TOO_MANY_REPORTED_PEERS_SENT); } } private void purgeReportedPeersIfExceeds() { - Log.traceCall(); int size = reportedPeers.size(); if (size > MAX_REPORTED_PEERS) { log.info("We have already {} reported peers which exceeds our limit of {}." + @@ -532,7 +525,6 @@ private Optional getPersistedPeerOptional(NodeAddress nodeAddress) { } private void removeTooOldPersistedPeers() { - Log.traceCall(); Set persistedPeersToRemove = persistedPeers.stream() .filter(reportedPeer -> new Date().getTime() - reportedPeer.getDate().getTime() > MAX_AGE) .collect(Collectors.toSet()); @@ -540,7 +532,6 @@ private void removeTooOldPersistedPeers() { } private void purgePersistedPeersIfExceeds() { - Log.traceCall(); int size = persistedPeers.size(); int limit = MAX_PERSISTED_PEERS; if (size > limit) { @@ -611,7 +602,6 @@ public void handleConnectionFault(NodeAddress nodeAddress) { } public void handleConnectionFault(NodeAddress nodeAddress, @Nullable Connection connection) { - Log.traceCall("nodeAddress=" + nodeAddress); log.debug("handleConnectionFault called: nodeAddress=" + nodeAddress); boolean doRemovePersistedPeer = false; removeReportedPeer(nodeAddress); @@ -670,19 +660,18 @@ private Set getConnectedReportedPeers() { // filter(connection -> connection.getPeersNodeAddressOptional().isPresent()) return networkNode.getConfirmedConnections().stream() .map((Connection connection) -> { - List supportedCapabilities = connection.getSupportedCapabilities(); + Capabilities supportedCapabilities = new Capabilities(connection.getCapabilities()); // 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. - if (supportedCapabilities == null || supportedCapabilities.isEmpty()) { + if (supportedCapabilities.isEmpty()) { Set allPeers = new HashSet<>(getPersistedPeers()); allPeers.addAll(getReportedPeers()); - supportedCapabilities = allPeers.stream().filter(peer -> peer.getNodeAddress().equals(connection.getPeersNodeAddressOptional().get())) - .filter(peer -> !peer.getSupportedCapabilities().isEmpty()) - .findAny() - .map(Peer::getSupportedCapabilities) - .filter(list -> !list.isEmpty()) - .orElse(new ArrayList<>()); + Optional ourPeer = allPeers.stream().filter(peer -> peer.getNodeAddress().equals(connection.getPeersNodeAddressOptional().get())) + .filter(peer -> !peer.getCapabilities().isEmpty()) + .findAny(); + if(ourPeer.isPresent()) + supportedCapabilities = new Capabilities(ourPeer.get().getCapabilities()); } Peer peer = new Peer(connection.getPeersNodeAddressOptional().get(), supportedCapabilities); connection.addWeakCapabilitiesListener(peer); diff --git a/p2p/src/main/java/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java b/p2p/src/main/java/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java index 2ab9a0beac6..35e6c9ec217 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java @@ -31,7 +31,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.util.Utilities; import com.google.common.util.concurrent.FutureCallback; @@ -39,7 +38,6 @@ import com.google.common.util.concurrent.SettableFuture; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -51,7 +49,7 @@ @Slf4j public class GetDataRequestHandler { - private static final long TIMEOUT = 60; + private static final long TIMEOUT = 90; /////////////////////////////////////////////////////////////////////////////////////////// @@ -92,8 +90,6 @@ public GetDataRequestHandler(NetworkNode networkNode, P2PDataStorage dataStorage /////////////////////////////////////////////////////////////////////////////////////////// public void handle(GetDataRequest getDataRequest, final Connection connection) { - Log.traceCall(getDataRequest + "\n\tconnection=" + connection); - GetDataResponse getDataResponse = new GetDataResponse(getFilteredProtectedStorageEntries(getDataRequest, connection), getFilteredPersistableNetworkPayload(getDataRequest, connection), getDataRequest.getNonce(), @@ -162,27 +158,11 @@ private Set getFilteredProtectedStorageEntries(GetDataReq final ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload(); boolean doAdd = false; if (protectedStoragePayload instanceof CapabilityRequiringPayload) { - final List requiredCapabilities = ((CapabilityRequiringPayload) protectedStoragePayload).getRequiredCapabilities(); - final List supportedCapabilities = connection.getSupportedCapabilities(); - if (supportedCapabilities != null) { - for (int messageCapability : requiredCapabilities) { - for (int connectionCapability : supportedCapabilities) { - if (messageCapability == connectionCapability) { - doAdd = true; - break; - } - } - } - if (!doAdd) - log.debug("We do not send the message to the peer because he does not support the required capability for that message type.\n" + - "Required capabilities is: " + requiredCapabilities.toString() + "\n" + - "Supported capabilities is: " + supportedCapabilities.toString() + "\n" + - "storagePayload is: " + Utilities.toTruncatedString(protectedStoragePayload)); - } else { - log.debug("We do not send the message to the peer because he uses an old version which does not support capabilities.\n" + - "Required capabilities is: " + requiredCapabilities.toString() + "\n" + + if (connection.getCapabilities().containsAll(((CapabilityRequiringPayload) protectedStoragePayload).getRequiredCapabilities())) + doAdd = true; + else + log.debug("We do not send the message to the peer because he does not support the required capability for that message type.\n" + "storagePayload is: " + Utilities.toTruncatedString(protectedStoragePayload)); - } } else { doAdd = true; } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataHandler.java b/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataHandler.java index 716673048d3..654be9ab4ae 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataHandler.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/getdata/RequestDataHandler.java @@ -35,7 +35,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; import bisq.common.proto.network.NetworkPayload; @@ -61,7 +60,7 @@ @Slf4j class RequestDataHandler implements MessageListener { - private static final long TIMEOUT = 60; + private static final long TIMEOUT = 90; private NodeAddress peersNodeAddress; @@ -114,7 +113,6 @@ public void cancel() { /////////////////////////////////////////////////////////////////////////////////////////// public void requestData(NodeAddress nodeAddress, boolean isPreliminaryDataRequest) { - Log.traceCall("nodeAddress=" + nodeAddress); peersNodeAddress = nodeAddress; if (!stopped) { GetDataRequest getDataRequest; @@ -196,7 +194,6 @@ public void onFailure(@NotNull Throwable throwable) { public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof GetDataResponse) { if (connection.getPeersNodeAddressOptional().isPresent() && connection.getPeersNodeAddressOptional().get().equals(peersNodeAddress)) { - Log.traceCall(networkEnvelope.toString() + "\n\tconnection=" + connection); if (!stopped) { GetDataResponse getDataResponse = (GetDataResponse) networkEnvelope; Map> payloadByClassName = new HashMap<>(); @@ -327,7 +324,6 @@ private void handleFault(String errorMessage, NodeAddress nodeAddress, CloseConn } private void cleanup() { - Log.traceCall(); stopped = true; networkNode.removeMessageListener(this); stopTimeoutTimer(); 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 437efd95f43..cddb7f45fd5 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 @@ -17,7 +17,6 @@ package bisq.network.p2p.peers.getdata; -import bisq.network.NetworkOptionKeys; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.CloseConnectionReason; import bisq.network.p2p.network.Connection; @@ -32,13 +31,12 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; -import com.google.inject.name.Named; - import javax.inject.Inject; +import javafx.beans.property.SimpleObjectProperty; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -110,8 +108,7 @@ public interface Listener { public RequestDataManager(NetworkNode networkNode, SeedNodeRepository seedNodeRepository, P2PDataStorage dataStorage, - PeerManager peerManager, - @javax.annotation.Nullable @Named(NetworkOptionKeys.MY_ADDRESS) String myAddress) { + PeerManager peerManager) { this.networkNode = networkNode; this.dataStorage = dataStorage; this.peerManager = peerManager; @@ -122,18 +119,20 @@ public RequestDataManager(NetworkNode networkNode, this.seedNodeAddresses = new HashSet<>(seedNodeRepository.getSeedNodeAddresses()); - // If we are a seed node we use more redundancy at startup to be sure we get all data. - // We cannot use networkNode.getNodeAddress() as nodeAddress as that is null at this point, so we use - // new NodeAddress(myAddress) for checking if we are a seed node. - // seedNodeAddresses do not contain my own address as that gets filtered out - if (myAddress != null && !myAddress.isEmpty() && seedNodeRepository.isSeedNode(new NodeAddress(myAddress))) { - NUM_SEEDS_FOR_PRELIMINARY_REQUEST = 3; - NUM_ADDITIONAL_SEEDS_FOR_UPDATE_REQUEST = 2; - } + this.networkNode.getNodeAddressProperty().addListener(observable -> { + + NodeAddress myAddress = (NodeAddress) ((SimpleObjectProperty) observable).get(); + + seedNodeAddresses.remove(myAddress); + + if (myAddress != null && seedNodeRepository.isSeedNode(myAddress)) { + NUM_SEEDS_FOR_PRELIMINARY_REQUEST = 3; + NUM_ADDITIONAL_SEEDS_FOR_UPDATE_REQUEST = 2; + } + }); } public void shutDown() { - Log.traceCall(); stopped = true; stopRetryTimer(); networkNode.removeMessageListener(this); @@ -152,7 +151,6 @@ public void addListener(Listener listener) { } public boolean requestPreliminaryData() { - Log.traceCall(); ArrayList nodeAddresses = new ArrayList<>(seedNodeAddresses); if (!nodeAddresses.isEmpty()) { Collections.shuffle(nodeAddresses); @@ -174,7 +172,6 @@ public boolean requestPreliminaryData() { } public void requestUpdateData() { - Log.traceCall(); checkArgument(nodeAddressOfPreliminaryDataRequest.isPresent(), "nodeAddressOfPreliminaryDataRequest must be present"); dataUpdateRequested = true; isPreliminaryDataRequest = false; @@ -214,12 +211,10 @@ public Optional getNodeAddressOfPreliminaryDataRequest() { @Override public void onConnection(Connection connection) { - Log.traceCall(); } @Override public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { - Log.traceCall(); closeHandler(connection); if (peerManager.isNodeBanned(closeConnectionReason, connection) && connection.getPeersNodeAddressOptional().isPresent()) { @@ -240,7 +235,6 @@ public void onError(Throwable throwable) { @Override public void onAllConnectionsLost() { - Log.traceCall(); closeAllHandlers(); stopRetryTimer(); stopped = true; @@ -249,7 +243,6 @@ public void onAllConnectionsLost() { @Override public void onNewConnectionAfterAllConnectionsLost() { - Log.traceCall(); closeAllHandlers(); stopped = false; restart(); @@ -257,7 +250,6 @@ public void onNewConnectionAfterAllConnectionsLost() { @Override public void onAwakeFromStandby() { - Log.traceCall(); closeAllHandlers(); stopped = false; if (!networkNode.getAllConnections().isEmpty()) @@ -272,7 +264,6 @@ public void onAwakeFromStandby() { @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof GetDataRequest) { - Log.traceCall(networkEnvelope.toString() + "\n\tconnection=" + connection); if (!stopped) { if (peerManager.isSeedNode(connection)) connection.setPeerType(Connection.PeerType.SEED_NODE); @@ -324,7 +315,6 @@ public void onFault(String errorMessage, @Nullable Connection connection) { /////////////////////////////////////////////////////////////////////////////////////////// private void requestData(NodeAddress nodeAddress, List remainingNodeAddresses) { - Log.traceCall("nodeAddress=" + nodeAddress + " / remainingNodeAddresses=" + remainingNodeAddresses); if (!stopped) { if (!handlerMap.containsKey(nodeAddress)) { RequestDataHandler requestDataHandler = new RequestDataHandler(networkNode, dataStorage, peerManager, @@ -417,10 +407,8 @@ public void onFault(String errorMessage, @Nullable Connection connection) { /////////////////////////////////////////////////////////////////////////////////////////// private void restart() { - Log.traceCall(); if (retryTimer == null) { retryTimer = UserThread.runAfter(() -> { - log.trace("retryTimer called"); stopped = false; stopRetryTimer(); @@ -485,7 +473,7 @@ private void closeHandler(Connection connection) { handlerMap.remove(nodeAddress); } } else { - log.trace("closeRequestDataHandler: nodeAddress not set in connection " + connection); + log.trace("closeRequestDataHandler: nodeAddress not set in connection {}", connection); } } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/getdata/messages/GetDataResponse.java b/p2p/src/main/java/bisq/network/p2p/peers/getdata/messages/GetDataResponse.java index 0e4b48a20a8..6abd7323f23 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/getdata/messages/GetDataResponse.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/getdata/messages/GetDataResponse.java @@ -31,7 +31,6 @@ import io.bisq.generated.protobuffer.PB; import java.util.HashSet; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -57,7 +56,7 @@ public final class GetDataResponse extends NetworkEnvelope implements SupportedC private final int requestNonce; private final boolean isGetUpdatedDataResponse; @Nullable - private final List supportedCapabilities; + private final Capabilities supportedCapabilities; public GetDataResponse(Set dataSet, @Nullable Set persistableNetworkPayloadSet, @@ -67,7 +66,7 @@ public GetDataResponse(Set dataSet, persistableNetworkPayloadSet, requestNonce, isGetUpdatedDataResponse, - Capabilities.getSupportedCapabilities(), + Capabilities.app, Version.getP2PMessageVersion()); } @@ -79,7 +78,7 @@ private GetDataResponse(Set dataSet, @Nullable Set persistableNetworkPayloadSet, int requestNonce, boolean isGetUpdatedDataResponse, - @Nullable List supportedCapabilities, + @Nullable Capabilities supportedCapabilities, int messageVersion) { super(messageVersion); @@ -106,7 +105,7 @@ public PB.NetworkEnvelope toProtoNetworkEnvelope() { .setRequestNonce(requestNonce) .setIsGetUpdatedDataResponse(isGetUpdatedDataResponse); - Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(supportedCapabilities)); + Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities))); Optional.ofNullable(persistableNetworkPayloadSet).ifPresent(set -> builder.addAllPersistableNetworkPayloadItems(set.stream() .map(PersistableNetworkPayload::toProtoMessage) .collect(Collectors.toList()))); @@ -134,7 +133,7 @@ public static GetDataResponse fromProto(PB.GetDataResponse proto, NetworkProtoRe persistableNetworkPayloadSet, proto.getRequestNonce(), proto.getIsGetUpdatedDataResponse(), - proto.getSupportedCapabilitiesList().isEmpty() ? null : proto.getSupportedCapabilitiesList(), + Capabilities.fromIntList(proto.getSupportedCapabilitiesList()), messageVersion); } } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/getdata/messages/PreliminaryGetDataRequest.java b/p2p/src/main/java/bisq/network/p2p/peers/getdata/messages/PreliminaryGetDataRequest.java index 58af39daa2b..b0038d14bda 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/getdata/messages/PreliminaryGetDataRequest.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/getdata/messages/PreliminaryGetDataRequest.java @@ -28,7 +28,6 @@ import com.google.protobuf.ByteString; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -45,11 +44,11 @@ public final class PreliminaryGetDataRequest extends GetDataRequest implements AnonymousMessage, SupportedCapabilitiesMessage { // ordinals of enum @Nullable - private final List supportedCapabilities; + private final Capabilities supportedCapabilities; public PreliminaryGetDataRequest(int nonce, Set excludedKeys) { - this(nonce, excludedKeys, Capabilities.getSupportedCapabilities(), Version.getP2PMessageVersion()); + this(nonce, excludedKeys, Capabilities.app, Version.getP2PMessageVersion()); } @@ -59,7 +58,7 @@ public PreliminaryGetDataRequest(int nonce, private PreliminaryGetDataRequest(int nonce, Set excludedKeys, - @Nullable List supportedCapabilities, + @Nullable Capabilities supportedCapabilities, int messageVersion) { super(messageVersion, nonce, excludedKeys); @@ -74,7 +73,7 @@ public PB.NetworkEnvelope toProtoNetworkEnvelope() { .map(ByteString::copyFrom) .collect(Collectors.toList())); - Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(supportedCapabilities)); + Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities))); return getNetworkEnvelopeBuilder() .setPreliminaryGetDataRequest(builder) @@ -84,7 +83,7 @@ public PB.NetworkEnvelope toProtoNetworkEnvelope() { public static PreliminaryGetDataRequest fromProto(PB.PreliminaryGetDataRequest proto, int messageVersion) { return new PreliminaryGetDataRequest(proto.getNonce(), ProtoUtil.byteSetFromProtoByteStringList(proto.getExcludedKeysList()), - proto.getSupportedCapabilitiesList().isEmpty() ? null : proto.getSupportedCapabilitiesList(), + Capabilities.fromIntList(proto.getSupportedCapabilitiesList()), messageVersion); } } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/keepalive/KeepAliveHandler.java b/p2p/src/main/java/bisq/network/p2p/peers/keepalive/KeepAliveHandler.java index fae54bb41cf..bda50cf3b16 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/keepalive/KeepAliveHandler.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/keepalive/KeepAliveHandler.java @@ -26,7 +26,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; import com.google.common.util.concurrent.FutureCallback; @@ -100,7 +99,6 @@ public void sendPingAfterRandomDelay(Connection connection) { } private void sendPing(Connection connection) { - Log.traceCall("connection=" + connection + " / this=" + this); if (!stopped) { Ping ping = new Ping(nonce, connection.getStatistic().roundTripTimeProperty().get()); sendTs = System.currentTimeMillis(); @@ -109,7 +107,6 @@ private void sendPing(Connection connection) { @Override public void onSuccess(Connection connection) { if (!stopped) { - log.trace("Send " + ping + " to " + connection + " succeeded."); KeepAliveHandler.this.connection = connection; connection.addMessageListener(KeepAliveHandler.this); } else { @@ -146,12 +143,10 @@ public void onFailure(@NotNull Throwable throwable) { @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof Pong) { - Log.traceCall(networkEnvelope.toString() + "\n\tconnection=" + connection); if (!stopped) { Pong pong = (Pong) networkEnvelope; if (pong.getRequestNonce() == nonce) { int roundTripTime = (int) (System.currentTimeMillis() - sendTs); - log.trace("roundTripTime=" + roundTripTime + "\n\tconnection=" + connection); connection.getStatistic().setRoundTripTime(roundTripTime); cleanup(); listener.onComplete(); diff --git a/p2p/src/main/java/bisq/network/p2p/peers/keepalive/KeepAliveManager.java b/p2p/src/main/java/bisq/network/p2p/peers/keepalive/KeepAliveManager.java index b8a4e26bf8b..6115df52e20 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/keepalive/KeepAliveManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/keepalive/KeepAliveManager.java @@ -29,7 +29,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; import javax.inject.Inject; @@ -77,7 +76,6 @@ public KeepAliveManager(NetworkNode networkNode, } public void shutDown() { - Log.traceCall(); stopped = true; networkNode.removeMessageListener(this); networkNode.removeConnectionListener(this); @@ -103,7 +101,6 @@ public void start() { @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof Ping) { - Log.traceCall(networkEnvelope.toString() + "\n\tconnection=" + connection); if (!stopped) { Ping ping = (Ping) networkEnvelope; @@ -115,7 +112,6 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { Futures.addCallback(future, new FutureCallback() { @Override public void onSuccess(Connection connection) { - log.trace("Pong sent successfully"); } @Override @@ -144,12 +140,10 @@ public void onFailure(@NotNull Throwable throwable) { @Override public void onConnection(Connection connection) { - Log.traceCall(); } @Override public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { - Log.traceCall(); closeHandler(connection); } @@ -164,7 +158,6 @@ public void onError(Throwable throwable) { @Override public void onAllConnectionsLost() { - Log.traceCall(); closeAllHandlers(); stopKeepAliveTimer(); stopped = true; @@ -173,7 +166,6 @@ public void onAllConnectionsLost() { @Override public void onNewConnectionAfterAllConnectionsLost() { - Log.traceCall(); closeAllHandlers(); stopped = false; restart(); @@ -181,7 +173,6 @@ public void onNewConnectionAfterAllConnectionsLost() { @Override public void onAwakeFromStandby() { - Log.traceCall(); closeAllHandlers(); stopped = false; if (!networkNode.getAllConnections().isEmpty()) @@ -203,7 +194,6 @@ private void restart() { private void keepAlive() { if (!stopped) { - Log.traceCall(); networkNode.getConfirmedConnections().stream() .filter(connection -> connection instanceof OutboundConnection && connection.getStatistic().getLastActivityAge() > LAST_ACTIVITY_AGE_MS) 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 ee8af0db45b..d9f627f8291 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 @@ -26,7 +26,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; @@ -84,8 +83,6 @@ public GetPeersRequestHandler(NetworkNode networkNode, PeerManager peerManager, /////////////////////////////////////////////////////////////////////////////////////////// public void handle(GetPeersRequest getPeersRequest, final Connection connection) { - Log.traceCall("getPeersRequest=" + getPeersRequest + "\n\tconnection=" + connection + "\n\tthis=" + this); - checkArgument(connection.getPeersNodeAddressOptional().isPresent(), "The peers address must have been already set at the moment"); GetPeersResponse getPeersResponse = new GetPeersResponse(getPeersRequest.getNonce(), 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 5459d10ea0e..a75648f9974 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 @@ -20,14 +20,14 @@ import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.SupportedCapabilitiesListener; +import bisq.common.app.Capabilities; +import bisq.common.app.HasCapabilities; import bisq.common.proto.network.NetworkPayload; import bisq.common.proto.persistable.PersistablePayload; import io.bisq.generated.protobuffer.PB; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -39,31 +39,30 @@ @Getter @EqualsAndHashCode(exclude = {"date"}) // failedConnectionAttempts is transient and therefore excluded anyway @Slf4j -public final class Peer implements NetworkPayload, PersistablePayload, SupportedCapabilitiesListener { +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 - private List supportedCapabilities = new ArrayList<>(); @Setter transient private int failedConnectionAttempts = 0; + private Capabilities capabilities = new Capabilities(); - public Peer(NodeAddress nodeAddress, @Nullable List supportedCapabilities) { - this(nodeAddress, new Date().getTime(), - supportedCapabilities == null ? new ArrayList<>() : supportedCapabilities); + public Peer(NodeAddress nodeAddress, @Nullable Capabilities supportedCapabilities) { + this(nodeAddress, new Date().getTime(), supportedCapabilities); } /////////////////////////////////////////////////////////////////////////////////////////// // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// - private Peer(NodeAddress nodeAddress, long date, List supportedCapabilities) { + private Peer(NodeAddress nodeAddress, long date, Capabilities supportedCapabilities) { + super(); this.nodeAddress = nodeAddress; this.date = date; - this.supportedCapabilities = supportedCapabilities; + this.capabilities.addAll(supportedCapabilities); } @Override @@ -71,15 +70,14 @@ public PB.Peer toProtoMessage() { return PB.Peer.newBuilder() .setNodeAddress(nodeAddress.toProtoMessage()) .setDate(date) - .addAllSupportedCapabilities(supportedCapabilities) + .addAllSupportedCapabilities(Capabilities.toIntList(getCapabilities())) .build(); } public static Peer fromProto(PB.Peer proto) { return new Peer(NodeAddress.fromProto(proto.getNodeAddress()), proto.getDate(), - proto.getSupportedCapabilitiesList().isEmpty() ? - new ArrayList<>() : new ArrayList<>(proto.getSupportedCapabilitiesList())); + Capabilities.fromIntList(proto.getSupportedCapabilitiesList())); } @@ -100,9 +98,9 @@ public Date getDate() { } @Override - public void onChanged(List supportedCapabilities) { - if (supportedCapabilities != null && !supportedCapabilities.isEmpty()) - this.supportedCapabilities = supportedCapabilities; + public void onChanged(Capabilities supportedCapabilities) { + if (!supportedCapabilities.isEmpty()) + capabilities.set(supportedCapabilities); } @@ -110,7 +108,7 @@ public void onChanged(List supportedCapabilities) { public String toString() { return "Peer{" + "\n nodeAddress=" + nodeAddress + - ",\n supportedCapabilities=" + supportedCapabilities + + ",\n supportedCapabilities=" + capabilities + ",\n failedConnectionAttempts=" + failedConnectionAttempts + ",\n date=" + date + "\n}"; 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 779d8e7ec7e..e8d9c08bc0e 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 @@ -28,7 +28,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; import com.google.common.util.concurrent.FutureCallback; @@ -88,7 +87,6 @@ public PeerExchangeHandler(NetworkNode networkNode, PeerManager peerManager, Lis } public void cancel() { - Log.traceCall(); cleanup(); } @@ -131,7 +129,6 @@ public void onSuccess(Connection connection) { PeerExchangeHandler.this.connection = connection; connection.addMessageListener(PeerExchangeHandler.this); - log.trace("Send " + getPeersRequest + " to " + nodeAddress + " succeeded."); } else { log.trace("We have stopped that handler already. We ignore that sendGetPeersRequest.onSuccess call."); } @@ -164,7 +161,6 @@ public void onFailure(@NotNull Throwable throwable) { public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof GetPeersResponse) { if (!stopped) { - Log.traceCall(networkEnvelope.toString() + "\n\tconnection=" + connection); GetPeersResponse getPeersResponse = (GetPeersResponse) networkEnvelope; if (peerManager.isSeedNode(connection)) connection.setPeerType(Connection.PeerType.SEED_NODE); @@ -192,7 +188,6 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { @SuppressWarnings("UnusedParameters") private void handleFault(String errorMessage, CloseConnectionReason closeConnectionReason, NodeAddress nodeAddress) { - Log.traceCall(); cleanup(); /* if (connection == null) peerManager.shutDownConnection(nodeAddress, closeConnectionReason); @@ -204,7 +199,6 @@ private void handleFault(String errorMessage, CloseConnectionReason closeConnect } private void cleanup() { - Log.traceCall(); stopped = true; if (connection != null) connection.removeMessageListener(this); 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 55ad5b5e0c3..dded72d87ec 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 @@ -29,7 +29,6 @@ import bisq.common.Timer; import bisq.common.UserThread; -import bisq.common.app.Log; import bisq.common.proto.network.NetworkEnvelope; import javax.inject.Inject; @@ -88,7 +87,6 @@ public PeerExchangeManager(NetworkNode networkNode, } public void shutDown() { - Log.traceCall(); stopped = true; networkNode.removeMessageListener(this); networkNode.removeConnectionListener(this); @@ -134,7 +132,6 @@ public void initialRequestPeersFromReportedOrPersistedPeers() { @Override public void onConnection(Connection connection) { - Log.traceCall(); } @Override @@ -165,7 +162,6 @@ public void onError(Throwable throwable) { @Override public void onAllConnectionsLost() { - Log.traceCall(); closeAllHandlers(); stopPeriodicTimer(); stopRetryTimer(); @@ -175,7 +171,6 @@ public void onAllConnectionsLost() { @Override public void onNewConnectionAfterAllConnectionsLost() { - Log.traceCall(); closeAllHandlers(); stopped = false; restart(); @@ -183,7 +178,6 @@ public void onNewConnectionAfterAllConnectionsLost() { @Override public void onAwakeFromStandby() { - Log.traceCall(); closeAllHandlers(); stopped = false; if (!networkNode.getAllConnections().isEmpty()) @@ -198,7 +192,6 @@ public void onAwakeFromStandby() { @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof GetPeersRequest) { - Log.traceCall(networkEnvelope.toString() + "\n\tconnection=" + connection); if (!stopped) { if (peerManager.isSeedNode(connection)) connection.setPeerType(Connection.PeerType.SEED_NODE); @@ -291,7 +284,6 @@ public void onFault(String errorMessage, @Nullable Connection connection) { } private void requestWithAvailablePeers() { - Log.traceCall(); if (!stopped) { if (!peerManager.hasSufficientConnections()) { // We create a new list of not connected candidates @@ -310,7 +302,7 @@ private void requestWithAvailablePeers() { list.addAll(filteredSeedNodeAddresses); log.debug("Number of peers in list for connectToMorePeers: {}", list.size()); - log.trace("Filtered connectToMorePeers list: list=" + list); + log.trace("Filtered connectToMorePeers list: list={}", list); if (!list.isEmpty()) { // Don't shuffle as we want the seed nodes at the last entries NodeAddress nextCandidate = list.get(0); @@ -401,7 +393,6 @@ private void stopRetryTimer() { } private void closeHandler(Connection connection) { - Log.traceCall(); Optional peersNodeAddressOptional = connection.getPeersNodeAddressOptional(); if (peersNodeAddressOptional.isPresent()) { NodeAddress nodeAddress = peersNodeAddressOptional.get(); @@ -409,13 +400,10 @@ private void closeHandler(Connection connection) { handlerMap.get(nodeAddress).cancel(); handlerMap.remove(nodeAddress); } - } else { - log.trace("closeHandler: nodeAddress not set in connection " + connection); } } private void closeAllHandlers() { - Log.traceCall(); handlerMap.values().stream().forEach(PeerExchangeHandler::cancel); handlerMap.clear(); } 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 8b8ab1adf4b..e345d9c7669 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 @@ -29,7 +29,6 @@ import io.bisq.generated.protobuffer.PB; import java.util.HashSet; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -48,10 +47,10 @@ public final class GetPeersRequest extends NetworkEnvelope implements PeerExchan private final int nonce; private final Set reportedPeers; @Nullable - private final List supportedCapabilities; + private final Capabilities supportedCapabilities; public GetPeersRequest(NodeAddress senderNodeAddress, int nonce, Set reportedPeers) { - this(senderNodeAddress, nonce, reportedPeers, Capabilities.getSupportedCapabilities(), Version.getP2PMessageVersion()); + this(senderNodeAddress, nonce, reportedPeers, Capabilities.app, Version.getP2PMessageVersion()); } @@ -62,7 +61,7 @@ public GetPeersRequest(NodeAddress senderNodeAddress, int nonce, Set repor private GetPeersRequest(NodeAddress senderNodeAddress, int nonce, Set reportedPeers, - @Nullable List supportedCapabilities, + @Nullable Capabilities supportedCapabilities, int messageVersion) { super(messageVersion); checkNotNull(senderNodeAddress, "senderNodeAddress must not be null at GetPeersRequest"); @@ -81,7 +80,7 @@ public PB.NetworkEnvelope toProtoNetworkEnvelope() { .map(Peer::toProtoMessage) .collect(Collectors.toList())); - Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(supportedCapabilities)); + Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities))); return getNetworkEnvelopeBuilder() .setGetPeersRequest(builder) @@ -94,7 +93,7 @@ public static GetPeersRequest fromProto(PB.GetPeersRequest proto, int messageVer new HashSet<>(proto.getReportedPeersList().stream() .map(Peer::fromProto) .collect(Collectors.toSet())), - proto.getSupportedCapabilitiesList().isEmpty() ? null : proto.getSupportedCapabilitiesList(), + Capabilities.fromIntList(proto.getSupportedCapabilitiesList()), messageVersion); } } 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 1f6788b63c1..1c01b9cf680 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 @@ -28,7 +28,6 @@ import io.bisq.generated.protobuffer.PB; import java.util.HashSet; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -44,10 +43,10 @@ public final class GetPeersResponse extends NetworkEnvelope implements PeerExcha private final int requestNonce; private final Set reportedPeers; @Nullable - private final List supportedCapabilities; + private final Capabilities supportedCapabilities; public GetPeersResponse(int requestNonce, Set reportedPeers) { - this(requestNonce, reportedPeers, Capabilities.getSupportedCapabilities(), Version.getP2PMessageVersion()); + this(requestNonce, reportedPeers, Capabilities.app, Version.getP2PMessageVersion()); } @@ -57,7 +56,7 @@ public GetPeersResponse(int requestNonce, Set reportedPeers) { private GetPeersResponse(int requestNonce, Set reportedPeers, - @Nullable List supportedCapabilities, + @Nullable Capabilities supportedCapabilities, int messageVersion) { super(messageVersion); this.requestNonce = requestNonce; @@ -73,7 +72,7 @@ public PB.NetworkEnvelope toProtoNetworkEnvelope() { .map(Peer::toProtoMessage) .collect(Collectors.toList())); - Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(supportedCapabilities)); + Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities))); return getNetworkEnvelopeBuilder() .setGetPeersResponse(builder) @@ -86,12 +85,12 @@ public static GetPeersResponse fromProto(PB.GetPeersResponse proto, int messageV .map(peer -> { NodeAddress nodeAddress = new NodeAddress(peer.getNodeAddress().getHostName(), peer.getNodeAddress().getPort()); - return new Peer(nodeAddress, peer.getSupportedCapabilitiesList()); + return new Peer(nodeAddress, Capabilities.fromIntList(peer.getSupportedCapabilitiesList())); }) .collect(Collectors.toCollection(HashSet::new)); return new GetPeersResponse(proto.getRequestNonce(), reportedPeers, - proto.getSupportedCapabilitiesList().isEmpty() ? null : proto.getSupportedCapabilitiesList(), + Capabilities.fromIntList(proto.getSupportedCapabilitiesList()), messageVersion); } } diff --git a/p2p/src/main/java/bisq/network/p2p/seed/SeedNodeRepository.java b/p2p/src/main/java/bisq/network/p2p/seed/SeedNodeRepository.java index c5805af277d..a59807ddb04 100644 --- a/p2p/src/main/java/bisq/network/p2p/seed/SeedNodeRepository.java +++ b/p2p/src/main/java/bisq/network/p2p/seed/SeedNodeRepository.java @@ -19,13 +19,11 @@ import bisq.network.p2p.NodeAddress; -import java.util.Set; +import java.util.Collection; public interface SeedNodeRepository { boolean isSeedNode(NodeAddress nodeAddress); - Set getSeedNodeAddresses(); - - String getOperator(NodeAddress nodeAddress); + Collection getSeedNodeAddresses(); } 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 7eb2436e229..c80d5d990e8 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java @@ -220,7 +220,6 @@ public Map getProtectedDataStor @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof BroadcastMessage) { - Log.traceCall(Utilities.toTruncatedString(networkEnvelope) + "\n\tconnection=" + connection); connection.getPeersNodeAddressOptional().ifPresent(peersNodeAddress -> { if (networkEnvelope instanceof AddDataMessage) { addProtectedStorageEntry(((AddDataMessage) networkEnvelope).getProtectedStorageEntry(), peersNodeAddress, null, false); @@ -338,13 +337,11 @@ public boolean addPersistableNetworkPayload(PersistableNetworkPayload payload, public boolean addProtectedStorageEntry(ProtectedStorageEntry protectedStorageEntry, @Nullable NodeAddress sender, @Nullable BroadcastHandler.Listener listener, boolean isDataOwner) { - Log.traceCall("with allowBroadcast=true"); return addProtectedStorageEntry(protectedStorageEntry, sender, listener, isDataOwner, true); } public boolean addProtectedStorageEntry(ProtectedStorageEntry protectedStorageEntry, @Nullable NodeAddress sender, @Nullable BroadcastHandler.Listener listener, boolean isDataOwner, boolean allowBroadcast) { - Log.traceCall("with allowBroadcast=" + allowBroadcast); final ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload(); ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload); boolean sequenceNrValid = isSequenceNrValid(protectedStorageEntry.getSequenceNumber(), hashOfPayload); @@ -400,8 +397,6 @@ public void broadcastProtectedStorageEntry(ProtectedStorageEntry protectedStorag } public boolean refreshTTL(RefreshOfferMessage refreshTTLMessage, @Nullable NodeAddress sender, boolean isDataOwner) { - Log.traceCall(); - byte[] hashOfDataAndSeqNr = refreshTTLMessage.getHashOfDataAndSeqNr(); byte[] signature = refreshTTLMessage.getSignature(); ByteArray hashOfPayload = new ByteArray(refreshTTLMessage.getHashOfPayload()); @@ -444,7 +439,6 @@ public boolean refreshTTL(RefreshOfferMessage refreshTTLMessage, @Nullable NodeA } public boolean remove(ProtectedStorageEntry protectedStorageEntry, @Nullable NodeAddress sender, boolean isDataOwner) { - Log.traceCall(); final ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload(); ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload); boolean containsKey = map.containsKey(hashOfPayload); @@ -482,7 +476,6 @@ && checkSignature(protectedStorageEntry) @SuppressWarnings("UnusedReturnValue") public boolean removeMailboxData(ProtectedMailboxStorageEntry protectedMailboxStorageEntry, @Nullable NodeAddress sender, boolean isDataOwner) { - Log.traceCall(); ByteArray hashOfData = get32ByteHashAsByteArray(protectedMailboxStorageEntry.getProtectedStoragePayload()); boolean containsKey = map.containsKey(hashOfData); if (!containsKey) @@ -601,7 +594,6 @@ private boolean isSequenceNrValid(int newSequenceNumber, ByteArray hashOfData) { return false; } } else { - log.trace("Sequence number is valid (!sequenceNumberMap.containsKey(hashOfData)). sequenceNumber = " + newSequenceNumber); return true; } } @@ -631,7 +623,6 @@ private boolean hasSequenceNrIncreased(int newSequenceNumber, ByteArray hashOfDa return false; } } else { - log.trace("Sequence number has increased (!sequenceNumberMap.containsKey(hashOfData)). sequenceNumber = " + newSequenceNumber + " / hashOfData=" + hashOfData.toString()); return true; } } @@ -775,7 +766,7 @@ private void printData(String info) { }); sb.append("\n------------------------------------------------------------\n"); log.debug(sb.toString()); - log.debug("Data set " + info + " operation: size=" + map.values().size()); + //log.debug("Data set " + info + " operation: size=" + map.values().size()); } } diff --git a/p2p/src/main/java/bisq/network/p2p/storage/payload/CapabilityRequiringPayload.java b/p2p/src/main/java/bisq/network/p2p/storage/payload/CapabilityRequiringPayload.java index 69e4d04303e..b11d1ad80a0 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/payload/CapabilityRequiringPayload.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/payload/CapabilityRequiringPayload.java @@ -17,10 +17,9 @@ package bisq.network.p2p.storage.payload; +import bisq.common.app.Capabilities; import bisq.common.proto.network.NetworkPayload; -import java.util.List; - /** * Used for payloads which requires certain capability. *

@@ -31,5 +30,5 @@ public interface CapabilityRequiringPayload extends NetworkPayload { /** * @return Capabilities the other node need to support to receive that message */ - List getRequiredCapabilities(); + Capabilities getRequiredCapabilities(); } diff --git a/p2p/src/main/resources/AccountAgeWitnessStore_BTC_DAO_TESTNET b/p2p/src/main/resources/AccountAgeWitnessStore_BTC_DAO_TESTNET new file mode 100644 index 00000000000..5e5f61cf995 --- /dev/null +++ b/p2p/src/main/resources/AccountAgeWitnessStore_BTC_DAO_TESTNET @@ -0,0 +1,15 @@ +j + +IabVxBh|#'ŗ- + +RK-iXbLd& o- + ++ҷԱ:m.s(w- + +뫔0_EWu` o݋ɓ- + +N^d썶~Wn~^- + +U!Z"Ǔ- + +sePd1XLL- \ No newline at end of file diff --git a/p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET b/p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET index ef619f07d2e..fea8601eed7 100644 Binary files a/p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET and b/p2p/src/main/resources/AccountAgeWitnessStore_BTC_MAINNET differ diff --git a/p2p/src/main/resources/AccountAgeWitnessStore_BTC_TESTNET b/p2p/src/main/resources/AccountAgeWitnessStore_BTC_TESTNET deleted file mode 100644 index fe2ce538de5..00000000000 Binary files a/p2p/src/main/resources/AccountAgeWitnessStore_BTC_TESTNET and /dev/null differ diff --git a/p2p/src/main/resources/BlindVoteStore_BTC_DAO_TESTNET b/p2p/src/main/resources/BlindVoteStore_BTC_DAO_TESTNET new file mode 100644 index 00000000000..8f45f227078 Binary files /dev/null and b/p2p/src/main/resources/BlindVoteStore_BTC_DAO_TESTNET differ diff --git a/p2p/src/main/resources/BlindVoteStore_BTC_TESTNET b/p2p/src/main/resources/BlindVoteStore_BTC_TESTNET deleted file mode 100644 index 78d279af567..00000000000 Binary files a/p2p/src/main/resources/BlindVoteStore_BTC_TESTNET and /dev/null differ diff --git a/p2p/src/main/resources/DaoStateStore3_BTC_DAO_TESTNET b/p2p/src/main/resources/DaoStateStore3_BTC_DAO_TESTNET new file mode 100644 index 00000000000..bc794d518bf Binary files /dev/null and b/p2p/src/main/resources/DaoStateStore3_BTC_DAO_TESTNET differ diff --git a/p2p/src/main/resources/DaoStateStore_BTC_TESTNET b/p2p/src/main/resources/DaoStateStore_BTC_TESTNET deleted file mode 100644 index 9b55da149ba..00000000000 Binary files a/p2p/src/main/resources/DaoStateStore_BTC_TESTNET and /dev/null differ diff --git a/p2p/src/main/resources/ProposalStore_BTC_DAO_TESTNET b/p2p/src/main/resources/ProposalStore_BTC_DAO_TESTNET new file mode 100644 index 00000000000..3b9d2f21e4f Binary files /dev/null and b/p2p/src/main/resources/ProposalStore_BTC_DAO_TESTNET differ diff --git a/p2p/src/main/resources/ProposalStore_BTC_TESTNET b/p2p/src/main/resources/ProposalStore_BTC_TESTNET deleted file mode 100644 index 9ed4e056598..00000000000 Binary files a/p2p/src/main/resources/ProposalStore_BTC_TESTNET and /dev/null differ diff --git a/p2p/src/main/resources/TempProposalStore_BTC_TESTNET b/p2p/src/main/resources/TempProposalStore_BTC_TESTNET deleted file mode 100644 index 16a07368d8e..00000000000 Binary files a/p2p/src/main/resources/TempProposalStore_BTC_TESTNET and /dev/null differ diff --git a/p2p/src/main/resources/TradeStatistics2Store_BTC_DAO_TESTNET b/p2p/src/main/resources/TradeStatistics2Store_BTC_DAO_TESTNET new file mode 100644 index 00000000000..caf48e9decb Binary files /dev/null and b/p2p/src/main/resources/TradeStatistics2Store_BTC_DAO_TESTNET differ diff --git a/p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET b/p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET index 617b3cc544d..e997eaba731 100644 Binary files a/p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET and b/p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET differ diff --git a/p2p/src/main/resources/TradeStatistics2Store_BTC_REGTEST b/p2p/src/main/resources/TradeStatistics2Store_BTC_REGTEST index 34005d05ecc..906d5bcc70c 100644 Binary files a/p2p/src/main/resources/TradeStatistics2Store_BTC_REGTEST and b/p2p/src/main/resources/TradeStatistics2Store_BTC_REGTEST differ diff --git a/p2p/src/main/resources/TradeStatistics2Store_BTC_TESTNET b/p2p/src/main/resources/TradeStatistics2Store_BTC_TESTNET index 34005d05ecc..906d5bcc70c 100644 Binary files a/p2p/src/main/resources/TradeStatistics2Store_BTC_TESTNET and b/p2p/src/main/resources/TradeStatistics2Store_BTC_TESTNET differ diff --git a/p2p/src/test/java/bisq/network/p2p/DummySeedNode.java b/p2p/src/test/java/bisq/network/p2p/DummySeedNode.java index 9ea01e6f5f1..814aad5787c 100644 --- a/p2p/src/test/java/bisq/network/p2p/DummySeedNode.java +++ b/p2p/src/test/java/bisq/network/p2p/DummySeedNode.java @@ -65,7 +65,6 @@ public class DummySeedNode { private Level logLevel = Level.WARN; public DummySeedNode(String defaultUserDataDir) { - Log.traceCall("defaultUserDataDir=" + defaultUserDataDir); this.defaultUserDataDir = defaultUserDataDir; } @@ -74,18 +73,17 @@ public DummySeedNode(String defaultUserDataDir) { // API /////////////////////////////////////////////////////////////////////////////////////////// - // args: myAddress (incl. port) bitcoinNetworkId maxConnections useLocalhostForP2P seedNodes (separated with |) + // args: bitcoinNetworkId maxConnections useLocalhostForP2P seedNodes (separated with |) // 2. and 3. args are optional // eg. lmvdenjkyvx2ovga.onion:8001 0 20 false eo5ay2lyzrfvx2nr.onion:8002|si3uu56adkyqkldl.onion:8003 // or when using localhost: localhost:8001 2 20 true localhost:8002|localhost:8003 // BitcoinNetworkId: The id for the bitcoin network (Mainnet = 0, TestNet = 1, Regtest = 2) // localhost:3002 2 50 true // localhost:3002 2 50 localhost:4442|localhost:4443 true - // Usage: -myAddress= -networkId= -maxConnections= -useLocalhostForP2P=false -seedNodes=si3uu56adkyqkldl.onion:8002|eo5ay2lyzrfvx2nr.onion:8002 -ignore=4543y2lyzrfvx2nr.onion:8002|876572lyzrfvx2nr.onion:8002 - // Example usage: -myAddress=lmvdenjkyvx2ovga.onion:8001 -networkId=0 -maxConnections=20 -useLocalhostForP2P=false -seedNodes=si3uu56adkyqkldl.onion:8002|eo5ay2lyzrfvx2nr.onion:8002 -ignore=4543y2lyzrfvx2nr.onion:8002|876572lyzrfvx2nr.onion:8002 + // Usage: -networkId= -maxConnections= -useLocalhostForP2P=false -seedNodes=si3uu56adkyqkldl.onion:8002|eo5ay2lyzrfvx2nr.onion:8002 -ignore=4543y2lyzrfvx2nr.onion:8002|876572lyzrfvx2nr.onion:8002 + // Example usage: -networkId=0 -maxConnections=20 -useLocalhostForP2P=false -seedNodes=si3uu56adkyqkldl.onion:8002|eo5ay2lyzrfvx2nr.onion:8002 -ignore=4543y2lyzrfvx2nr.onion:8002|876572lyzrfvx2nr.onion:8002 public static final String USAGE = "Usage:\n" + - "--myAddress=\n" + "--networkId=[0|1|2] (Mainnet = 0, TestNet = 1, Regtest = 2)\n" + "--maxConnections=\n" + "--useLocalhostForP2P=[true|false]\n" + @@ -101,12 +99,7 @@ public void processArgs(String[] args) { String arg = arg1; if (arg.startsWith("--")) arg = arg.substring(2); - if (arg.startsWith(NetworkOptionKeys.MY_ADDRESS)) { - arg = arg.substring(NetworkOptionKeys.MY_ADDRESS.length() + 1); - checkArgument(arg.contains(":") && arg.split(":").length == 2 && arg.split(":")[1].length() > 3, "Wrong program argument: " + arg); - mySeedNodeAddress = new NodeAddress(arg); - log.debug("From processArgs: mySeedNodeAddress=" + mySeedNodeAddress); - } else if (arg.startsWith(NetworkOptionKeys.NETWORK_ID)) { + if (arg.startsWith(NetworkOptionKeys.NETWORK_ID)) { arg = arg.substring(NetworkOptionKeys.NETWORK_ID.length() + 1); networkId = Integer.parseInt(arg); log.debug("From processArgs: networkId=" + networkId); diff --git a/relay/src/main/resources/version.txt b/relay/src/main/resources/version.txt index 65f5c1516b0..5c11a25a755 100644 --- a/relay/src/main/resources/version.txt +++ b/relay/src/main/resources/version.txt @@ -1 +1 @@ -0.9.3-SNAPSHOT +0.9.5-SNAPSHOT diff --git a/scripts/install_java.sh b/scripts/install_java.sh index 79712bf85f0..2ab9e21df98 100755 --- a/scripts/install_java.sh +++ b/scripts/install_java.sh @@ -3,40 +3,80 @@ # It will also configure it as the default system JDK. # If you need to change to another default JDK for another purpose later, you can use the # following commands and select the default JDK: +# Linux: # update-alternatives --config java # update-alternatives --config javac +# MacOS: +# echo 'export JAVA_HOME=/Library/Java/JavaVirtualMachines//Contents/Home' >>~/.bash_profile +# echo 'export PATH=$JAVA_HOME/bin:$PATH' >>~/.bash_profile +# source ~/.bash_profile -JAVA_HOME=/usr/lib/jvm/openjdk-10.0.2 -JDK_FILENAME=openjdk-10.0.2_linux-x64_bin.tar.gz -JDK_URL=https://download.java.net/java/GA/jdk10/10.0.2/19aef61b38124481863b1413dce1855f/13/openjdk-10.0.2_linux-x64_bin.tar.gz - -# Determine which package manager to use depending on the distribution -declare -A osInfo; -osInfo[/etc/redhat-release]=yum -osInfo[/etc/arch-release]=pacman -osInfo[/etc/gentoo-release]=emerge -osInfo[/etc/SuSE-release]=zypp -osInfo[/etc/debian_version]=apt-get -for f in ${!osInfo[@]} -do - if [[ -f $f ]]; then - PACKAGE_MANAGER=${osInfo[$f]} - break - fi -done - -if [ ! -d "$JAVA_HOME" ]; then - # Ensure curl is installed since it may not be - $PACKAGE_MANAGER -y install curl - - curl -L -O $JDK_URL - mkdir -p $JAVA_HOME - tar -zxf $JDK_FILENAME -C $JAVA_HOME --strip 1 - rm $JDK_FILENAME - - update-alternatives --install /usr/bin/java java $JAVA_HOME/bin/java 2000 - update-alternatives --install /usr/bin/javac javac $JAVA_HOME/bin/javac 2000 -fi - -update-alternatives --set java $JAVA_HOME/bin/java -update-alternatives --set javac $JAVA_HOME/bin/javac + +unameOut="$(uname -s)" +case "${unameOut}" in + Linux*) + JAVA_HOME=/usr/lib/jvm/openjdk-10.0.2 + JDK_FILENAME=openjdk-10.0.2_linux-x64_bin.tar.gz + JDK_URL=https://download.java.net/java/GA/jdk10/10.0.2/19aef61b38124481863b1413dce1855f/13/openjdk-10.0.2_linux-x64_bin.tar.gz + + # Determine which package manager to use depending on the distribution + declare -A osInfo; + osInfo[/etc/redhat-release]=yum + osInfo[/etc/arch-release]=pacman + osInfo[/etc/gentoo-release]=emerge + osInfo[/etc/SuSE-release]=zypp + osInfo[/etc/debian_version]=apt-get + + for f in ${!osInfo[@]} + do + if [[ -f $f ]]; then + PACKAGE_MANAGER=${osInfo[$f]} + break + fi + done + + if [ ! -d "$JAVA_HOME" ]; then + # Ensure curl is installed since it may not be + $PACKAGE_MANAGER -y install curl + + curl -L -O $JDK_URL + mkdir -p $JAVA_HOME + tar -zxf $JDK_FILENAME -C $JAVA_HOME --strip 1 + rm $JDK_FILENAME + + update-alternatives --install /usr/bin/java java $JAVA_HOME/bin/java 2000 + update-alternatives --install /usr/bin/javac javac $JAVA_HOME/bin/javac 2000 + fi + + update-alternatives --set java $JAVA_HOME/bin/java + update-alternatives --set javac $JAVA_HOME/bin/javac + ;; + Darwin*) + JAVA_HOME=/Library/Java/JavaVirtualMachines/openjdk-10.0.2.jdk/Contents/Home + JDK_FILENAME=openjdk-10.0.2_osx-x64_bin.tar.gz + JDK_URL=https://download.java.net/java/GA/jdk10/10.0.2/19aef61b38124481863b1413dce1855f/13/openjdk-10.0.2_osx-x64_bin.tar.gz + if [ ! -d "$JAVA_HOME" ]; then + if [[ $(command -v brew) == "" ]]; then + echo "Installing Hombrew" + /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + else + echo "Updating Homebrew" + brew update + fi + + brew install curl + curl -L -O $JDK_URL + sudo mkdir /Library/Java/JavaVirtualMachines/openjdk-10.0.2.jdk | sudo bash + gunzip -c $JDK_FILENAME | tar xopf - + sudo mv jdk-10.0.2.jdk/* /Library/Java/JavaVirtualMachines/openjdk-10.0.2.jdk + sudo rmdir jdk-10.0.2.jdk + rm $JDK_FILENAME + fi + + echo export JAVA_HOME=$JAVA_HOME >>~/.bash_profile + echo export PATH=$JAVA_HOME/bin:$PATH >>~/.bash_profile + source ~/.bash_profile + ;; + *) machine="UNKNOWN:${unameOut}" +esac +java -version diff --git a/seednode/docker/startSeedNode.sh b/seednode/docker/startSeedNode.sh index fc6278c767d..14ae3764961 100755 --- a/seednode/docker/startSeedNode.sh +++ b/seednode/docker/startSeedNode.sh @@ -27,10 +27,5 @@ fi if [ ! -z "$USE_LOCALHOST_FOR_P2P" ]; then ARGS="$ARGS --useLocalhostForP2P=$USE_LOCALHOST_FOR_P2P" fi -if [ ! -z "$MY_ADDRESS" ]; then - ARGS="$ARGS --myAddress=${MY_ADDRESS}" -elif [ ! -z "$ONION_ADDRESS" ]; then - ARGS="$ARGS --myAddress=${ONION_ADDRESS}.onion:$NODE_PORT" -fi JAVA_OPTS='-Xms1800m -Xmx1800m' ./build/app/bin/bisq-seednode $ARGS diff --git a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java index 6cd2f28d603..b2b982a3128 100644 --- a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java @@ -25,15 +25,18 @@ import bisq.common.UserThread; import bisq.common.app.AppModule; import bisq.common.app.Capabilities; +import bisq.common.app.Capability; import bisq.common.setup.CommonSetup; import joptsimple.OptionSet; +import java.util.List; + import lombok.extern.slf4j.Slf4j; @Slf4j public class SeedNodeMain extends ExecutableForAppWithP2p { - private static final String VERSION = "0.9.3"; + private static final String VERSION = "0.9.5"; private SeedNode seedNode; public SeedNodeMain() { @@ -60,7 +63,7 @@ protected void doExecute(OptionSet options) { @Override protected void addCapabilities() { - Capabilities.addCapability(Capabilities.Capability.SEED_NODE.ordinal()); + Capabilities.app.addAll(Capability.SEED_NODE); } @Override