diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 79ee123c2b..6e6eec1148 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ \ No newline at end of file diff --git a/README.md b/README.md index 0b39759034..6bf38dc463 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Bisq is a safe, private and decentralized way to exchange bitcoin for national currencies and other cryptocurrencies. Bisq uses peer-to-peer technology and multi-signature escrow to facilitate trading without the need for a centralized third party exchange. Bisq is non-custodial (never holds your funds), and incorporates a human arbitration system to resolve disputes. -For more information, see https://bisq-network/intro and for step-by-step getting started instructions, see https://bisq.network/get-started. +For more information, see https://bisq.network/intro and for step-by-step getting started instructions, see https://bisq.network/get-started. ## Building Bisq diff --git a/assets/src/main/java/bisq/asset/CryptonoteAddressValidator.java b/assets/src/main/java/bisq/asset/CryptonoteAddressValidator.java index 22b28523da..e5ab3c8788 100644 --- a/assets/src/main/java/bisq/asset/CryptonoteAddressValidator.java +++ b/assets/src/main/java/bisq/asset/CryptonoteAddressValidator.java @@ -40,16 +40,39 @@ public class CryptonoteAddressValidator implements AddressValidator { // Invalid characters return AddressValidationResult.invalidStructure(); } - - if (address.startsWith(prefix) && address.length() == 94 + prefix.length()) { - // Standard address - return AddressValidationResult.validAddress(); - } else if (address.startsWith(subAddressPrefix) && address.length() == 94 + subAddressPrefix.length()) { - // Subaddress - return AddressValidationResult.validAddress(); - } else { - // Integrated? Invalid? Doesn't matter + if (address.startsWith(prefix)) { + if (prefix.length() == 1 && address.length() == 94 + prefix.length()) { + // XMR-type Standard address + return AddressValidationResult.validAddress(); + } + else if (prefix.length() == 2 && address.length() == 95 + prefix.length()) { + //Aeon & Blur-type addresses + return AddressValidationResult.validAddress(); + } + else { + //Non-supported prefix + return AddressValidationResult.invalidStructure(); + } + } + if (address.startsWith(subAddressPrefix)) { + if (subAddressPrefix.length() == 1 && address.length() == 94 + subAddressPrefix.length()) { + // XMR-type subaddress + return AddressValidationResult.validAddress(); + } + else if (subAddressPrefix.length() == 2 && address.length() == 95 + subAddressPrefix.length()) { + // Aeon & Blur-type subaddress + return AddressValidationResult.validAddress(); + } + else { + // Non-supported subAddress return AddressValidationResult.invalidStructure(); - } - } + } + } + else { + //Integrated? Invalid? Doesn't matter + return AddressValidationResult.invalidStructure(); + } + } } + + diff --git a/assets/src/main/java/bisq/asset/coins/Actinium.java b/assets/src/main/java/bisq/asset/coins/Actinium.java new file mode 100644 index 0000000000..25934608a5 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Actinium.java @@ -0,0 +1,39 @@ +/* + * 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 Actinium extends Coin { + + public Actinium() { + super("Actinium", "ACM", new Base58BitcoinAddressValidator(new ActiniumParams())); + } + + + public static class ActiniumParams extends NetworkParametersAdapter { + + public ActiniumParams() { + addressHeader = 53; + p2shHeader = 5; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Bitcoin2.java b/assets/src/main/java/bisq/asset/coins/Bitcoin2.java new file mode 100644 index 0000000000..5ddf567617 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Bitcoin2.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.Base58BitcoinAddressValidator; +import bisq.asset.Coin; + +public class Bitcoin2 extends Coin { + + public Bitcoin2() { + super("Bitcoin 2", "BTC2", new Base58BitcoinAddressValidator()); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Blur.java b/assets/src/main/java/bisq/asset/coins/Blur.java new file mode 100644 index 0000000000..5af9341847 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Blur.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.CryptonoteAddressValidator; + +public class Blur extends Coin { + + public Blur() { + super("Blur", "BLUR", new CryptonoteAddressValidator("bL", "Ry")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Chaucha.java b/assets/src/main/java/bisq/asset/coins/Chaucha.java new file mode 100644 index 0000000000..c26b1a2dcf --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Chaucha.java @@ -0,0 +1,39 @@ +/* + * 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 Chaucha extends Coin { + + public Chaucha() { + super("Chaucha", "CHA", new Base58BitcoinAddressValidator(new ChauchaParams())); + } + + public static class ChauchaParams extends NetworkParametersAdapter { + + public ChauchaParams() { + addressHeader = 88; + p2shHeader = 50; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} + diff --git a/assets/src/main/java/bisq/asset/coins/Croat.java b/assets/src/main/java/bisq/asset/coins/Croat.java new file mode 100644 index 0000000000..c4e99877f4 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Croat.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 Croat extends Coin { + + public Croat() { + super("Croat", "CROAT", new RegexAddressValidator("^C[1-9A-HJ-NP-Za-km-z]{94}")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Dragonglass.java b/assets/src/main/java/bisq/asset/coins/Dragonglass.java new file mode 100644 index 0000000000..17458d6401 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Dragonglass.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 Dragonglass extends Coin { + + public Dragonglass() { + super("Dragonglass", "DRGL", new RegexAddressValidator("^(dRGL)[1-9A-HJ-NP-Za-km-z]{94}$")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Kekcoin.java b/assets/src/main/java/bisq/asset/coins/Kekcoin.java new file mode 100644 index 0000000000..33f5bc2107 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Kekcoin.java @@ -0,0 +1,40 @@ +/* + * 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 Kekcoin extends Coin { + + public Kekcoin() { + super("Kekcoin", "KEK", new Base58BitcoinAddressValidator(new KekcoinParams())); + } + + + public static class KekcoinParams extends NetworkParametersAdapter { + + public KekcoinParams() { + super(); + addressHeader = 45; + p2shHeader = 88; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} \ No newline at end of file diff --git a/assets/src/main/java/bisq/asset/coins/Loki.java b/assets/src/main/java/bisq/asset/coins/Loki.java new file mode 100644 index 0000000000..54401f3b52 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Loki.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 Loki extends Coin { + + public Loki() { + super("Loki", "LOKI", new RegexAddressValidator("^(L[0-9A-Za-z]{94})|(L[PK][0-9A-Za-z]{104})$")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/MobitGlobal.java b/assets/src/main/java/bisq/asset/coins/MobitGlobal.java new file mode 100644 index 0000000000..c4e1eb86d2 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/MobitGlobal.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 MobitGlobal extends Coin { + + public MobitGlobal() { + super("MobitGlobal", "MBGL", new MobitGlobalAddressValidator()); + } + + + public static class MobitGlobalAddressValidator extends Base58BitcoinAddressValidator { + + public MobitGlobalAddressValidator() { + super(new MobitGlobalParams()); + } + + @Override + public AddressValidationResult validate(String address) { + if (!address.matches("^[M][a-zA-Z1-9]{33}$")) + return AddressValidationResult.invalidStructure(); + + return super.validate(address); + } + } + + + public static class MobitGlobalParams extends NetworkParametersAdapter { + + public MobitGlobalParams() { + addressHeader = 50; + p2shHeader = 110; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Neos.java b/assets/src/main/java/bisq/asset/coins/Neos.java new file mode 100644 index 0000000000..f8efecb57f --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Neos.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 Neos extends Coin { + + public Neos() { + super("Neos", "NEOS", new NeosAddressValidator()); + } + + + public static class NeosAddressValidator extends Base58BitcoinAddressValidator { + + public NeosAddressValidator() { + super(new NeosParams()); + } + + @Override + public AddressValidationResult validate(String address) { + if (!address.matches("^[N][a-km-zA-HJ-NP-Z1-9]{25,34}$")) + return AddressValidationResult.invalidStructure(); + + return super.validate(address); + } + } + + + public static class NeosParams extends NetworkParametersAdapter { + + public NeosParams() { + addressHeader = 53; + p2shHeader = 5; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/PZDC.java b/assets/src/main/java/bisq/asset/coins/PZDC.java new file mode 100644 index 0000000000..94ad421dc1 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/PZDC.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 PZDC extends Coin { + + public PZDC() { + super("PZDC", "PZDC", new PZDCAddressValidator()); + } + + + public static class PZDCAddressValidator extends Base58BitcoinAddressValidator { + + public PZDCAddressValidator() { + super(new PZDCParams()); + } + + @Override + public AddressValidationResult validate(String address) { + if (!address.matches("^[P][a-km-zA-HJ-NP-Z1-9]{25,34}$")) + return AddressValidationResult.invalidStructure(); + + return super.validate(address); + } + } + + + public static class PZDCParams extends NetworkParametersAdapter { + + public PZDCParams() { + addressHeader = 55; + p2shHeader = 13; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/QMCoin.java b/assets/src/main/java/bisq/asset/coins/QMCoin.java new file mode 100644 index 0000000000..956c79fd32 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/QMCoin.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 QMCoin extends Coin { + + public QMCoin() { + super("QMCoin", "QMCoin", new QMCoinAddressValidator()); + } + + + public static class QMCoinAddressValidator extends Base58BitcoinAddressValidator { + + public QMCoinAddressValidator() { + super(new QMCoinParams()); + } + + @Override + public AddressValidationResult validate(String address) { + if (!address.matches("^[Q][a-km-zA-HJ-NP-Z1-9]{25,34}$")) + return AddressValidationResult.invalidStructure(); + + return super.validate(address); + } + } + + + public static class QMCoinParams extends NetworkParametersAdapter { + + public QMCoinParams() { + addressHeader = 58; + p2shHeader = 120; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/QRL.java b/assets/src/main/java/bisq/asset/coins/QRL.java new file mode 100644 index 0000000000..1579f0e0af --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/QRL.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 QRL extends Coin { + + public QRL() { + super("Quantum Resistant Ledger", "QRL", new RegexAddressValidator("([Q]\\d{6}[0-9a-fA-F]{72})")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Radium.java b/assets/src/main/java/bisq/asset/coins/Radium.java new file mode 100644 index 0000000000..aed79c6303 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Radium.java @@ -0,0 +1,40 @@ +/* + * 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 Radium extends Coin { + + public Radium() { + super("Radium", "RADS", new Base58BitcoinAddressValidator(new RadiumParams())); + } + + + public static class RadiumParams extends NetworkParametersAdapter { + + public RadiumParams() { + super(); + addressHeader = 76; + p2shHeader = 58; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} \ No newline at end of file diff --git a/assets/src/main/java/bisq/asset/coins/Ryo.java b/assets/src/main/java/bisq/asset/coins/Ryo.java new file mode 100644 index 0000000000..2d719cf251 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Ryo.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 Ryo extends Coin { + + public Ryo() { + super("Ryo", "RYO", new RegexAddressValidator("^((RYoL|RYoS)[1-9A-HJ-NP-Za-km-z]{95}|(RYoK)[1-9A-HJ-NP-Za-km-z]{51})$")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/SUB1X.java b/assets/src/main/java/bisq/asset/coins/SUB1X.java new file mode 100644 index 0000000000..59c8e1f98d --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/SUB1X.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 SUB1X extends Coin { + + public SUB1X() { + super("SUB1X", "SUB1X", new SUB1XAddressValidator()); + } + + + public static class SUB1XAddressValidator extends Base58BitcoinAddressValidator { + + public SUB1XAddressValidator() { + super(new SUB1XParams()); + } + + @Override + public AddressValidationResult validate(String address) { + if (!address.matches("^[Z][a-km-zA-HJ-NP-Z1-9]{25,34}$")) + return AddressValidationResult.invalidStructure(); + + return super.validate(address); + } + } + + + public static class SUB1XParams extends NetworkParametersAdapter { + + public SUB1XParams() { + addressHeader = 80; + p2shHeader = 13; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Starwels.java b/assets/src/main/java/bisq/asset/coins/Starwels.java new file mode 100644 index 0000000000..888f08a947 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Starwels.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.Base58BitcoinAddressValidator; +import bisq.asset.Coin; + +public class Starwels extends Coin { + + public Starwels() { + super("Starwels", "MAI", new Base58BitcoinAddressValidator()); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/TurtleCoin.java b/assets/src/main/java/bisq/asset/coins/TurtleCoin.java new file mode 100644 index 0000000000..ac9095c1f5 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/TurtleCoin.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 TurtleCoin extends Coin { + + public TurtleCoin() { + super("TurtleCoin", "TRTL", new RegexAddressValidator("^TRTL[1-9A-Za-z^OIl]{95}")); + } +} diff --git a/assets/src/main/java/bisq/asset/coins/Zero.java b/assets/src/main/java/bisq/asset/coins/Zero.java new file mode 100644 index 0000000000..910d25f911 --- /dev/null +++ b/assets/src/main/java/bisq/asset/coins/Zero.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.AddressValidationResult; +import bisq.asset.AddressValidator; +import bisq.asset.Coin; + +public class Zero extends Coin { + + public Zero() { + super("Zero", "ZER", new ZeroAddressValidator()); + } + + + public static class ZeroAddressValidator implements AddressValidator { + + @Override + public AddressValidationResult validate(String address) { + // We only support t addresses (transparent transactions) + if (!address.startsWith("t1")) + return AddressValidationResult.invalidAddress("", "validation.altcoin.zAddressesNotSupported"); + + return AddressValidationResult.validAddress(); + } + } +} diff --git a/assets/src/main/java/bisq/asset/tokens/EtherStone.java b/assets/src/main/java/bisq/asset/tokens/EtherStone.java new file mode 100644 index 0000000000..cebe9f8171 --- /dev/null +++ b/assets/src/main/java/bisq/asset/tokens/EtherStone.java @@ -0,0 +1,27 @@ +/* + * 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.tokens; + +import bisq.asset.Erc20Token; + +public class EtherStone extends Erc20Token { + + public EtherStone() { + super("EtherStone", "ETHS"); + } +} diff --git a/assets/src/main/java/bisq/asset/tokens/GreenBlockCoin.java b/assets/src/main/java/bisq/asset/tokens/GreenBlockCoin.java new file mode 100755 index 0000000000..869b46923f --- /dev/null +++ b/assets/src/main/java/bisq/asset/tokens/GreenBlockCoin.java @@ -0,0 +1,27 @@ +/* + * 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.tokens; + +import bisq.asset.Erc20Token; + +public class GreenBlockCoin extends Erc20Token { + + public GreenBlockCoin() { + super("GreenBlockCoin", "GBK"); + } +} 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 6474d489c8..96349c48e3 100644 --- a/assets/src/main/resources/META-INF/services/bisq.asset.Asset +++ b/assets/src/main/resources/META-INF/services/bisq.asset.Asset @@ -3,10 +3,12 @@ # See bisq.asset.Asset and bisq.asset.AssetRegistry for further details. # See https://bisq.network/list-asset for complete instructions. bisq.asset.coins.Achievecoin +bisq.asset.coins.Actinium bisq.asset.coins.Angelcoin bisq.asset.coins.Aquachain bisq.asset.coins.Arto bisq.asset.coins.BitCloud +bisq.asset.coins.Bitcoin2 bisq.asset.coins.BitcoinCash bisq.asset.coins.BitcoinClashic bisq.asset.coins.BitcoinCore @@ -18,6 +20,7 @@ bisq.asset.coins.Bitcoin$Testnet bisq.asset.coins.Bitcore bisq.asset.coins.BitDaric bisq.asset.coins.BitZeny +bisq.asset.coins.Blur bisq.asset.coins.BSQ$Mainnet bisq.asset.coins.BSQ$Regtest bisq.asset.coins.BSQ$Testnet @@ -25,10 +28,12 @@ bisq.asset.coins.Burstcoin bisq.asset.coins.Byteball bisq.asset.coins.Cagecoin bisq.asset.coins.CassubianDetk +bisq.asset.coins.Chaucha bisq.asset.coins.Conceal bisq.asset.coins.Counterparty bisq.asset.coins.Creativecoin bisq.asset.coins.Credits +bisq.asset.coins.Croat bisq.asset.coins.Cryptonite bisq.asset.coins.Cryptonodes bisq.asset.coins.DACash @@ -44,6 +49,7 @@ bisq.asset.coins.Diamond bisq.asset.coins.DigiMoney bisq.asset.coins.Dinero bisq.asset.coins.Dogecoin +bisq.asset.coins.Dragonglass bisq.asset.coins.DRIP bisq.asset.coins.DSTRA bisq.asset.coins.DynamicCoin @@ -56,6 +62,7 @@ bisq.asset.coins.Gridcoin bisq.asset.coins.InfinityEconomics bisq.asset.coins.Instacash bisq.asset.coins.InternetOfPeople +bisq.asset.coins.Kekcoin bisq.asset.coins.Koto bisq.asset.coins.Kumacoin bisq.asset.coins.LBRY @@ -65,6 +72,7 @@ bisq.asset.coins.Litecoin$Mainnet bisq.asset.coins.Litecoin$Regtest bisq.asset.coins.Litecoin$Testnet bisq.asset.coins.Lobstex +bisq.asset.coins.Loki bisq.asset.coins.Madbyte bisq.asset.coins.Madcoin bisq.asset.coins.MaidSafeCoin @@ -73,6 +81,7 @@ bisq.asset.coins.Mazacoin bisq.asset.coins.MegaCoin bisq.asset.coins.MFCoin bisq.asset.coins.MicroCoin +bisq.asset.coins.MobitGlobal bisq.asset.coins.Monero bisq.asset.coins.Motion bisq.asset.coins.Myriadcoin @@ -80,6 +89,7 @@ bisq.asset.coins.Namecoin bisq.asset.coins.Nano bisq.asset.coins.NavCoin bisq.asset.coins.NEETCOIN +bisq.asset.coins.Neos bisq.asset.coins.NewPowerCoin bisq.asset.coins.Nilu bisq.asset.coins.Nimiq @@ -94,9 +104,14 @@ bisq.asset.coins.PIVX bisq.asset.coins.PostCoin bisq.asset.coins.Pranacoin bisq.asset.coins.PRiVCY +bisq.asset.coins.PZDC +bisq.asset.coins.QMCoin +bisq.asset.coins.QRL +bisq.asset.coins.Radium bisq.asset.coins.ReddCoin bisq.asset.coins.Ringo bisq.asset.coins.Roicoin +bisq.asset.coins.Ryo bisq.asset.coins.SafeFileSystemCoin bisq.asset.coins.Semux bisq.asset.coins.Siacoin @@ -104,12 +119,15 @@ bisq.asset.coins.Siafund bisq.asset.coins.Sibcoin bisq.asset.coins.Spectrecoin bisq.asset.coins.SpeedCash +bisq.asset.coins.Starwels bisq.asset.coins.STEEM bisq.asset.coins.Stellite bisq.asset.coins.Strayacoin +bisq.asset.coins.SUB1X bisq.asset.coins.Tamadcoin bisq.asset.coins.Terracoin bisq.asset.coins.Triton +bisq.asset.coins.TurtleCoin bisq.asset.coins.Ubiq bisq.asset.coins.Unobtanium bisq.asset.coins.VDinar @@ -122,13 +140,16 @@ bisq.asset.coins.Yenten bisq.asset.coins.Zcash bisq.asset.coins.Zcoin bisq.asset.coins.ZenCash +bisq.asset.coins.Zero bisq.asset.coins.ZeroOneCoin bisq.asset.tokens.BetterBetting bisq.asset.tokens.DaiStablecoin bisq.asset.tokens.Ellaism +bisq.asset.tokens.EtherStone bisq.asset.tokens.Exceed bisq.asset.tokens.GeoCoin bisq.asset.tokens.Grans +bisq.asset.tokens.GreenBlockCoin bisq.asset.tokens.Internext bisq.asset.tokens.LikeCoin bisq.asset.tokens.Movement diff --git a/assets/src/test/java/bisq/asset/coins/ActiniumTest.java b/assets/src/test/java/bisq/asset/coins/ActiniumTest.java new file mode 100644 index 0000000000..094c5bdd5b --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/ActiniumTest.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 ActiniumTest extends AbstractAssetTest { + + public ActiniumTest() { + super(new Actinium()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("NLzB9iUGJ8GaKSn9GfVKfd55QVRdNdz9FK"); + assertValidAddress("NSz7PKmo1sLQYtFuZjTQ1zZXhPQtHLScKT"); + assertValidAddress("NTFtsh4Ff2ijPNsnQAUf5fKTp7DJaGxSZK"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("MgTFtsh4Ff2ijPNsnQAUf5fKTp7DJaGxSZK"); + assertInvalidAddress("F9z7PKmo1sLQYtFuZjTQ1zZXhPQtHLScKT"); + assertInvalidAddress("16Ftsh4Ff2ijPNsnQAUf5fKTp7DJaGxSZK"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/Bitcoin2Test.java b/assets/src/test/java/bisq/asset/coins/Bitcoin2Test.java new file mode 100644 index 0000000000..b3b9c808b6 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/Bitcoin2Test.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 Bitcoin2Test extends AbstractAssetTest { + + public Bitcoin2Test() { + super(new Bitcoin2()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("1Ns5bawVfpHYy6J7qdANasXy2nBTtq23cg"); + assertValidAddress("1P1WG1SV9AyKsHeGZtdmh8HN6QtCmemMCV"); + assertValidAddress("1mFiSH3mHL6gdqvRXYW5BgQh9E9vLCpNE"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("21HQQgsvLTgN9xD9hNmAgAreakzVzQUSLSHa"); + assertInvalidAddress("bc1q2rskr9eey7kvuv53esm8lm2tzmejpr3yzdz8xg"); + assertInvalidAddress("1HQQgsvLTgN9xD9hNmAgAreakzVzQUSLSH#"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/BlurTest.java b/assets/src/test/java/bisq/asset/coins/BlurTest.java new file mode 100644 index 0000000000..4cf93f3569 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/BlurTest.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 BlurTest extends AbstractAssetTest { + + public BlurTest() { + super(new Blur()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("bL3W1g1d12sbxQDTQ6q8bgU2bBp2rkfFFKfNvQuUQTHqgQHRaxKTHqK5Nqdm53BU3ibPnsqbdYAnnJMyqJ6FfN9m3CSZSNqDE"); + assertValidAddress("bL2zBGUBDkQdyYasdoAdvQCxWLa9Mk5Q1PW8Zk7S38vx9xu7T7NMPPWNfieXqUyswo544ZSB3C1n9jLMfsUvR6p91rnrSdx9h"); + assertValidAddress("Ry49oErHtqyHucxADDT2DfEJ9pRv2ciSpKV9XseCuWmx1PK1CZi4gbPKxhWBdtvLJNNc94c4yDutmZrD3WrsHPYV1nvE9X4Cc"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress(""); + assertInvalidAddress("bl4E2BCFY31DPLjeqF6Gu7TEUM5v2JwpmudFX64AubQtFDYEPBvgvQPzidaawDhjAmHeZSw92wEBnUfdfY5144Sad2ZCknZzC"); + assertInvalidAddress("Ry49oErHtqyHucxADDT2DfEJ9pRv2ciSpKV9XseCuWmx1PK1CZi4gbPKxhWBdtvLJNNc94c4yDutmZrD3WrsHPYV1nvE9X40"); + assertInvalidAddress("bLNHRh8pFh5Y14bhBVAoD4cvqHyoPsQJqB3dr49zoF6bNDFrts96tuuj#RoUKWRwpTHmYt4Kf78FES7LCXAXKXFf6bMsx1sdgz"); + assertInvalidAddress("82zBGUBDkQdyYasdoAdvQCxWLa9Mk5Q1PW#8Zk7S38vx9xu7T7NMPPWNfieXqUyswo544ZSB3C1n9jLMfsUvR6p91rnrSdxwd"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/ChauchaTest.java b/assets/src/test/java/bisq/asset/coins/ChauchaTest.java new file mode 100644 index 0000000000..7b77435373 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/ChauchaTest.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 ChauchaTest extends AbstractAssetTest { + + public ChauchaTest() { + super(new Chaucha()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("cTC7AodMWM4fXsG1TDu4JLn2qKQoMg4F9N"); + assertValidAddress("caWnffHrx8wkQqcSVJ7wpRvN1E7Ztz7kPP"); + assertValidAddress("ciWwaG4trw1vQZSL4F4phQqznK4NgZURdQ"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("1cTC7AodMWM4fXsG1TDu4JLn2qKQoMg4F9N"); + assertInvalidAddress("cTC7AodMWM4fXsG1TDu4JLn2qKQoMg4F9XN"); + assertInvalidAddress("cTC7AodMWM4fXsG1TDu4JLn2qKQoMg4F9N#"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/CroatTest.java b/assets/src/test/java/bisq/asset/coins/CroatTest.java new file mode 100644 index 0000000000..8df3488670 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/CroatTest.java @@ -0,0 +1,47 @@ +/* + * 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 CroatTest extends AbstractAssetTest { + + public CroatTest() { + super(new Croat()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("CsZ46x2mzB3GhjrC2Lt7oZ4Efmj8USUjVM7Bdz8B8EF6bQwN84NzSti7RwLZcFoZG5NR1iaiZY8GP2KwumVc1jGzHLvBzAv"); + assertValidAddress("CjxZDcoWCsx1wmYkmJcFpSTgqpjoFGRW9dQT8JqgwvkBaU6Q3X4MJ4QjVkNUM7GHp6NjYaTrKeH4bSRTK3mCYsHf2818vzv"); + assertValidAddress("CoCJje3bcEH2dkvb5suRy2ZiBtPBeBqWaY9sbMLEtqEvDn969eDx1zqV4FP8erJSJFK5Br6GheGnJJG7BDtG9XFbFcMkUJU"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("ZsZ46x2mzB3GhjrC2Lt7oZ4Efmj8USUjVM7Bdz8B8EF6bQwN84NzSti7RwLZcFoZG5NR1iaiZY8GP2KwumVc1jGzHLvBzAv"); + assertInvalidAddress(""); + assertInvalidAddress("CjxZDcoWCsx1wmYkmJcFpSTgqpjoFGRW9dQT8JqgwvkBaU6Q3X4MJ4QjV#NUM7GHp6NjYaTrKeH4bSRTK3mCYsHf2818vzv"); + assertInvalidAddress("CoCJje3bcEH2dkvb5suRy2ZiBtPBeBqWaY9sbMLEtqEvDn969eDx1zqV4FP8erJSJFK5Br6GheGnJJG7BDtG9XFbFcMkUJUuuuuuuuu"); + assertInvalidAddress("CsZ46x2mzB3GhjrC2Lt7oZ4Efmj8USUjVM7Bdz8B8EF6bQwN84NzSti7RwLZcFoZG5NR1iaiZY8GP2KwumVc1jGzHLvBzAv11111111"); + assertInvalidAddress("CjxZDcoWCsx1wmYkmJcFpSTgqpjoFGRW9dQT8JqgwvkBaU6Q3X4MJ4QjVkNUM7GHp6NjYaTrKeH4bSRTK3m"); + assertInvalidAddress("CjxZDcoWCsx1wmYkmJcFpSTgqpjoFGRW9dQT8JqgwvkBaU6Q3X4MJ4QjVkNUM7GHp6NjYaTrKeH4bSRTK3mCYsHf2818vzv$%"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/DragonglassTest.java b/assets/src/test/java/bisq/asset/coins/DragonglassTest.java new file mode 100644 index 0000000000..51e4974f90 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/DragonglassTest.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 DragonglassTest extends AbstractAssetTest { + + public DragonglassTest() { + super(new Dragonglass()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("dRGLhxvCtLk1vfSD3WmFzyTN5ph2gZYvkZfxvLSrcdry95x4PPJrCKBTKDEFZYTw4bCGqoiaUWxNd8B41vqXaTY72Vi2XcvikX"); + assertValidAddress("dRGLjS5v91tDd4GDZeahUj95nkXSNQs5DMY1YStLN2hSNWD67iZh7ED7oDw841Kx6oUYouZaXmBNFcqSptNZ4dL94CbZbF53jt"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("dRGLjS5v91tDd4GDZeahUj95nkXSNQs5DMY1YStLN2hSNWD67iZh7ED7oDw841Kx6oUYouZaXmBNFcqSptNZ4dL94CbZbF53j"); + assertInvalidAddress("dRGLjS5v91tDd4GDZeahUj95nkXSNQs5DMY1YStLN2hSNWD67iZh7ED7oDw841Ko6oUYouZaXmBNFcqSptNZ4dL94oUCifk4048"); + assertInvalidAddress("DRGLhxvCtLk1vfSD3WmFzyTN5ph2gZYvkZfxvLSrcdry95x4PPJrCKBTKDEFZYTw4bCGqoiaUWxNd8B41vqXaTY72Vi2XcvikX"); + assertInvalidAddress("drglhxvCtLk1vfSD3WmFzyTN5ph2gZYvkZfxvLSrcdry95x4PPJrCKBTKDEFZYTw4bCGqoiaUWxNd8B41vqXaTY72Vi2XcvikX"); + assertInvalidAddress("dRgLjS5v91tDd4GDZeahUj95nkXSNQs5DMY1YStLN2hSNWD67iZh7ED7oDw841Kx6oUYouZaXmBNFcqSptNZ4dL94CbZbF53jt"); + assertInvalidAddress("dRGlhxvCtLk1vfSD3WmFzyTN5ph2gZYvkZfxvLSrcdry95x4PPJrCKBTKDEFZYTw4bCGqoiaUWxNd8B41vqXaTY72Vi2XcvikX"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/KekcoinTest.java b/assets/src/test/java/bisq/asset/coins/KekcoinTest.java new file mode 100644 index 0000000000..30e7dcc8b2 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/KekcoinTest.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 KekcoinTest extends AbstractAssetTest { + + public KekcoinTest() { + super(new Kekcoin()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("KHWHFVU5ZMUfkiYEMMuXRDv1LjD2j1HJ2H"); + assertValidAddress("KSXQWsaKC9qL2e2RoeXNXY4FgQC6qUBpjD"); + assertValidAddress("KNVy3X1iuiF7Gz9a4fSYLF3RehN2yGkFvP"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("1LgfapHEPhZbRF9pMd5WPT35hFXcZS1USrW"); + assertInvalidAddress("1K5B7SDcuZvd2oUTaW9d62gwqsZkteXqA4"); + assertInvalidAddress("1GckU1XSCknLBcTGnayBVRjNsDjxqopNav"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/LokiTest.java b/assets/src/test/java/bisq/asset/coins/LokiTest.java new file mode 100644 index 0000000000..ac9c3b47c8 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/LokiTest.java @@ -0,0 +1,52 @@ +/* + * 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 LokiTest extends AbstractAssetTest { + + public LokiTest() { + super(new Loki()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("LVjRU9UVQ9SXDm3shuHVj5hmavGfbQEVMJwgH8Kh9AyPRyXtzZ64Rpjb15L3sWK2TT6caWVYSBATECKb9pc2qf5tCbsfb6Q"); + assertValidAddress("LTmZwKAZcwhhdRMRWLgGvzY4n5fT3n9Mh33H2xnXiCNVcwmtNToP4iVSu59MNc2YNkGGPVwE5B9ra2nRQ5nYmf3kE1kzXKx"); + assertValidAddress("LQmsf1ktNYQGWD2xbY1MStfYWFi1Sm4Cd7cHVryxepwcPcaa5N6KbpHdFXYDvswNYaRS1W5JLGY52dkRDV6hCVrtBhUCAe5"); + assertValidAddress("LX3F8zHNvth1JvU5qdETsfQFF33PPKw1sHdusAaaGLhi1J1dv6rKZbN92PxpV1uW9o8T5WPwYvYwKDteTiZN2gEE3y5wMqZ"); + assertValidAddress("LWv1YaK5jJaGPCFd1wxJbZ4hHa7yGKNJeGCPC9fJCXqz8NqittAtS1xYkr5gYxnjtYKDWPB6hNQBE93bz7ZVJFtXQJrT1SZ"); + assertValidAddress("LK6DQ17G8R3zs3Xf33wCeViD2B95jgdpjAhcRsjuheJ784dumXn7g3RPAzedWpFq364jJKYL9dkQ8mY66sZG9BiCx3dmHUuhwuSMcRwr9u"); + assertValidAddress("LPDCQ17G8R3zs3Xf33wCeViD2B95jgdpjAhcRsjuheJ784dumXn7g3RPAzedWpFq364jJKYL9dkQ8mY66sZG9BiCx3dmHUuhwuSMcRwr9u"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("LWv1YaK5jJaGPCFd1wxJbZ4hHa7yGKNJeGCPC9fJCXqz8NqittAtS1xYkr5gYxnjtYKDWPB6hNQBE93bz7ZVJFtXQJrT1SZz"); + assertInvalidAddress("lWv1YaK5jJaGPCFd1wxJbZ4hHa7yGKNJeGCPC9fJCXqz8NqittAtS1xYkr5gYxnjtYKDWPB6hNQBE93bz7ZVJFtXQJrT1SZ"); + assertInvalidAddress("Wv1YaK5jJaGPCFd1wxJbZ4hHa7yGKNJeGCPC9fJCXqz8NqittAtS1xYkr5gYxnjtYKDWPB6hNQBE93bz7ZVJFtXQJrT1SZz"); + assertInvalidAddress("LWv1YaK5jJaGPCFd1wxJbZ4hHa7yGKNJeGCPC9fJCXqz8NqittAtS1xYkr5gYxnjtYKDWPB6hNQBE93bz7ZVJFtXQJrT1S"); + assertInvalidAddress("LZ6DQ17G8R3zs3Xf33wCeViD2B95jgdpjAhcRsjuheJ784dumXn7g3RPAzedWpFq364jJKYL9dkQ8mY66sZG9BiCx3dmHUuhwuSMcRwr9u"); + assertInvalidAddress("lK6DQ17G8R3zs3Xf33wCeViD2B95jgdpjAhcRsjuheJ784dumXn7g3RPAzedWpFq364jJKYL9dkQ8mY66sZG9BiCx3dmHUuhwuSMcRwr9u"); + assertInvalidAddress("LK6DQ17G8R3zs3Xf33wCeViD2B95jgdpjAhcRsjuheJ784dumXn7g3RPAzedWpFq364jJKYL9dkQ8mY66sZG9BiCx3dmHUuhwuSMcRwr9uu"); + assertInvalidAddress("LK6DQ17G8R3zs3Xf33wCeViD2B95jgdpjAhcRsjuheJ784dumXn7g3RPAzedWpFq364jJKYL9dkQ8mY66sZG9BiCx3dmHUuhwuSMcRwr9"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/MobitGlobalTest.java b/assets/src/test/java/bisq/asset/coins/MobitGlobalTest.java new file mode 100644 index 0000000000..92be634d74 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/MobitGlobalTest.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 MobitGlobalTest extends AbstractAssetTest { + + public MobitGlobalTest() { + super(new MobitGlobal()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("MKDLXTdJs1AtAJhoRddLBSimXCE6SXbyMq"); + assertValidAddress("MGr2WYY9kSLPozEcsCWSEumXNX2AJXggUR"); + assertValidAddress("MUe1HzGqzcunR1wUxHTqX9cuQNMnEjiN7D"); + assertValidAddress("MWRqbYKkQcSvtHq4GFrPvYGf8GFGsLNPcE"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("AWGfbG22DNhgP2rsKfqyFxCwi1u68BbHAA1"); + assertInvalidAddress("AWGfbG22DNhgP2rsKfqyFxCwi1u68BbHAB"); + assertInvalidAddress("AWGfbG22DNhgP2rsKfqyFxCwi1u68BbHA#"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/NeosTest.java b/assets/src/test/java/bisq/asset/coins/NeosTest.java new file mode 100644 index 0000000000..5c11338bd8 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/NeosTest.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 NeosTest extends AbstractAssetTest { + + public NeosTest() { + super(new Neos()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("NS5cGWdERahJ11pn12GoV5Jb7nsLzdr3kP"); + assertValidAddress("NU7nCzyQiAtTxzXLnDsJu4NhwQqrnPyJZj"); + assertValidAddress("NeeAy35aQirpmTARHEXpP8uTmpPCcSD9Qn"); + assertValidAddress("NScgetCW5bqDTVWFH3EYNMtTo5RcvDxD6B"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemqq"); + assertInvalidAddress("NScgetCW5bqDTVWFH3EYNMtTo5Rc#DxD6B"); + assertInvalidAddress("NeeAy35a0irpmTARHEXpP8uTmpPCcSD9Qn"); + assertInvalidAddress("NScgetCWRcvDxD6B"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/PZDCTest.java b/assets/src/test/java/bisq/asset/coins/PZDCTest.java new file mode 100644 index 0000000000..442d61dea3 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/PZDCTest.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 org.junit.Test; + + + +import bisq.asset.AbstractAssetTest; + +public class PZDCTest extends AbstractAssetTest { + + public PZDCTest() { + super(new PZDC()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("PNxERPUbkvCYeuJk44pH8bsdQJenvEWt5J"); + assertValidAddress("PCwCT1PkW2RsxT8jTb21vRnNDQGDRcWNkM"); + assertValidAddress("PPD3mYyS3vsHBkCrbCfrZyrwCGdr6EJHgG"); + assertValidAddress("PTQDhqksrocR7Z516zbpjuXSGVD37iu8gy"); + assertValidAddress("PXtABooQW1ED9NkARTiFcZv6xUnMmrbhpt"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("pGXsg0jSMzh1dSqggRvHjPvE3cnwvuXC7s"); + assertInvalidAddress("PKfRRcjwzKFq3dIqE9gq8Ztxn922W4GZhm"); + assertInvalidAddress("PXP75NnwDryYswQb9RaPFBchqLRSvBmDP"); + assertInvalidAddress("PKr3vQ7S"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/QMCoinTest.java b/assets/src/test/java/bisq/asset/coins/QMCoinTest.java new file mode 100644 index 0000000000..44e29cd992 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/QMCoinTest.java @@ -0,0 +1,46 @@ +/* + * 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 QMCoinTest extends AbstractAssetTest { + + public QMCoinTest() { + super(new QMCoin()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("QSXwS2opau1PYsvj4PrirPkP6LQHeKbQDx"); + assertValidAddress("QbvD8CPJwAmpQoE8CQhzcfWp1EAmT2E298"); + assertValidAddress("QUAzsb7nqp7XVsRy9vjaE4kTUpgP1pFeoL"); + assertValidAddress("QQDvVM2s3WYa8EZQS1s2esRkR4zmrjy94d"); + assertValidAddress("QgdkWtsy1inr9j8RUrqDeVnrJmhE28WnLX"); + assertValidAddress("Qii56aanBMiEPpjHoaE4zgEW4jPuhGjuj5"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemqq"); + assertInvalidAddress("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYheO"); + assertInvalidAddress("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhek#"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/QRLTest.java b/assets/src/test/java/bisq/asset/coins/QRLTest.java new file mode 100644 index 0000000000..b30e6afaac --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/QRLTest.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 org.junit.Test; + + + +import bisq.asset.AbstractAssetTest; + +public class QRLTest extends AbstractAssetTest { + + public QRLTest() { + super(new QRL()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("Q0104008e2b38425dd2bae2b2b3a88d8df4911b0e0e5e880a71abe9e0f68296cc3560fb52dfb637"); + assertValidAddress("Q0204003808ebc69dfb4d9da48ec06bd7682091589aa4f6d7040d1f26ee1bf947e9f19fa50d253f"); + assertValidAddress("Q00050049194cc61c011dc0bccdfdccefc78cf540544520e283457ede9d3349d074883fc88497bb"); + assertValidAddress("Q010400e6f61e0a86b48e49ff34e5f7837d19e11a69aad2e8d49c1bb625bbc8f076823288a2b38b"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("Z0104008e2b38425dd2bae2b2b3a88d8df4911b0e0e5e880a71abe9e0f68296cc3560fb52dfb637"); + assertInvalidAddress("Q01A4008e2b38425dd2bae2b2b3a88d8df4911b0e0e5e880a71abe9e0f68296cc3560fb52dfb637"); + assertInvalidAddress("Q0104008e2b38425dd2bae2b2b3a88d8df4911b0e0e5e880a71abe9e0fR8296cc3560fb52dfb637"); + assertInvalidAddress("Q0104008e2b38425dd2bae2b2b3a88d8df491?b0e0e5e880a71abe9e0fR8296cc3560fb52dfb637"); + assertInvalidAddress("Q010400e6f61e0a86b48e49ff34e5f7837d19e11a69aad2e8d49c1bb625bbc8f076823288a2b"); + assertInvalidAddress(""); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/RadiumTest.java b/assets/src/test/java/bisq/asset/coins/RadiumTest.java new file mode 100644 index 0000000000..9c5966729e --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/RadiumTest.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 org.junit.Test; + + + +import bisq.asset.AbstractAssetTest; + +public class RadiumTest extends AbstractAssetTest { + + public RadiumTest() { + super(new Radium()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("XfrvQw3Uv4oGgc535TYyBCT2uNU7ofHGDU"); + assertValidAddress("Xwgof4wf1t8TnQUJ2UokZRVwHxRt4t6Feb"); + assertValidAddress("Xep8KxEZUsCxQuvCfPdt2VHuHbp43nX7Pm"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("1LgfapHEPhZbRF9pMd5WPT35hFXcZS1USrW"); + assertInvalidAddress("1K5B7SDcuZvd2oUTaW9d62gwqsZkteXqA4"); + assertInvalidAddress("1GckU1XSCknLBcTGnayBVRjNsDjxqopNav"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/RyoTest.java b/assets/src/test/java/bisq/asset/coins/RyoTest.java new file mode 100644 index 0000000000..6387820cc6 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/RyoTest.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.asset.coins; + +import bisq.asset.AbstractAssetTest; +import org.junit.Test; + +public class RyoTest extends AbstractAssetTest { + + public RyoTest() { + super(new Ryo()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("RYoLsinT9duNEtHGqAUicJKD2cmGiB9gB6sqHqWvV6suB4TtPSR8ynyh2vVVvNyDE6g7WEaBxCG8GD1KM2ffWP7FLXgeJbNYrp2"); + assertValidAddress("RYoSrJ7ES1wGsikGHFm69SU6dTTKt8Vi6V7BoC3wsLcc1Y2CXgQkW7vHSe5uArGU9TjUC5RtvzhCycVDnPPbThTmZA8VqDzTPeM"); + assertValidAddress("RYoKst8YBCucSywKDshsywbjc5uCi8ybSUtWgvM3LfzaYe93d4qqpsJ"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress(""); + assertInvalidAddress("RYoLsinT9duNEtHGqAUicJKD2cmGiB9gB6sqHqWvV6suB4TtPSR8ynyh2vVVvNyDE6g7WEaBxCG8GD1KM2ffWP7FLXgeJbNYrp"); + assertInvalidAddress("RYoLsjCoYrxag2pPoDDTB4cRriKCNn8WjhY99kqjYuNTfE4MU2Yo1CPdpyK7PXpxDcAd5YDNerE6WCc4cVQvEbxLaHk4UcvbRp23"); + assertInvalidAddress("RYoLsinT9duNEtHGqAUicJKD2cmGiB9gB6sqHqWvV6suB4TtPSR8ynyh2vVVvNyDE6g7W!!!xCG8GD1KM2ffWP7FLXgeJbNYrp2"); + assertInvalidAddress("RYoSrJ7ES1IIIIIGHFm69SU6dTTKt8Vi6V7BoC3wsLcc1Y2CXgQkW7vHSe5uArGU9TjUC5RtvzhCycVDnPPbThTmZA8VqDzTPeM"); + assertInvalidAddress("RYoSrJ7ES1wGsikGHFm69SU6dTTKt8Vi6V7BoC3wsLcc1Y2CXgQkW7vHSe5uArGU9TjUC5RtvzhCycVDnPPbThTmZA8VqDzTPe"); + assertInvalidAddress("RYoSrJ7ES1wGsikGHFm69SU6dTTKt8Vi6V7BoC3wsLcc1Y2CXgQkW7vHSe5uArGU9TjUC5RtvzhCycVDnPPbThTmZA8VqDzTPeM1"); + assertInvalidAddress("RYoNsBB18NdcSywKDshsywbjc5uCi8ybSUtWgvM3LfzaYe93d6DEu3PcSywKDshsywbjc5uCi8ybSUtWgvM3LfzaYe93d96NjjvBCYU2SZD2of"); + assertInvalidAddress("RYoKst8YBCucSywKDshsywbjc5uCi8ybSUtWgvM3LfzaYe93d4qqpsJC"); + assertInvalidAddress("RYoKst8YBCucSywKDshsywbjc5uCi8ybSUtWgvM3LfzaYe93d4qqps"); + assertInvalidAddress("RYost8YBCucSywKDshsywbjc5uCi8ybSUtWgvM3LfzaYe93d4qqpsJ"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/SUB1XTest.java b/assets/src/test/java/bisq/asset/coins/SUB1XTest.java new file mode 100644 index 0000000000..4a4ea4d333 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/SUB1XTest.java @@ -0,0 +1,47 @@ +/* + * 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 SUB1XTest extends AbstractAssetTest { + + public SUB1XTest() { + super(new SUB1X()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("ZDxdoVuyosZ6vY3LZAP1Z4H4eXMq2ZpLH7"); + assertValidAddress("ZKi6EksPCZoMi6EGXS9vWVed4NeSov2ZS4"); + assertValidAddress("ZT29B3yDJq1jzkCTBs4LnraM3E854MAPRm"); + assertValidAddress("ZZeaSimQwza3CkFWTrRPQDamZcbntf2uMG"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("ZDxdoVuyosZ6vY3LZAP1Z4H4eXMq2ZpAC7"); + assertInvalidAddress("ZKi6EksPCZoMi6EGXS9vWVedqwfov2ZS4"); + assertInvalidAddress("ZT29B3yDJq1jzkqwrwBs4LnraM3E854MAPRm"); + assertInvalidAddress("ZZeaSimQwza3CkFWTqwrfQDamZcbntf2uMG"); + assertInvalidAddress("Z23t23f"); + assertInvalidAddress("ZZeaSimQwza3CkFWTrRPQDavZcbnta2uMGA"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/StarwelsTest.java b/assets/src/test/java/bisq/asset/coins/StarwelsTest.java new file mode 100644 index 0000000000..e30e93b588 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/StarwelsTest.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 StarwelsTest extends AbstractAssetTest { + + public StarwelsTest() { + super(new Starwels()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("1F7EixuiBdvi9bVxEPzAgJ11GRJsdH3ihh"); + assertValidAddress("17DdVnWvz3XZPvMYHmSRSycUgt2EEv29So"); + assertValidAddress("1HuoFLoGJQCLodNDH5oCXWaR1kL8DwksJX"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("21HQQgsvLTgN9xD9hNmAgAreakzVzQUSLSHa"); + assertInvalidAddress("1HQQgsvLTgN9xD9hNmAgAreakzVzQUSLSHs"); + assertInvalidAddress("1HQQgsvLTgN9xD9hNmAgAreakzVzQUSLSH#"); + } +} diff --git a/assets/src/test/java/bisq/asset/coins/TurtleCoinTest.java b/assets/src/test/java/bisq/asset/coins/TurtleCoinTest.java new file mode 100644 index 0000000000..ad064b3755 --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/TurtleCoinTest.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 TurtleCoinTest extends AbstractAssetTest { + + public TurtleCoinTest() { + super(new TurtleCoin()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("TRTLv2X775FNQmN8x2UC3TVzs6trRHwUAcQSL6RUyRXR6JjwFYP8XG8VTCsi7QgPcWBJUWJk2SwaMYvrMk37T4nFVLPigMXcsf8"); + assertValidAddress("TRTLuyTzuoDL9wvoq9VcyGW9Vrp2R3161V3hSa8nZUxAL4iqbTJfFhSXpsrQunXuCGAnA72cZgYGmP7a8zJ6RrwAf5rKjwhUEU8"); + assertValidAddress("TRTLv2YGSbTgmAkZDYvRM8X6bLcJXYr4qMDTXYth9ppc2rHfnNGXPcbBTWxfRxwPTnJvFX1txGh6j9tQ9spJs3US3WwvDzkGsXC"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("TRTLv23ymatPTWgN1jncG33hMdJxZBLrBcCWQBGGGC14CFMUCq1nvxiV8d5cW92mmavzw542bpyjYXd8"); + assertInvalidAddress("TRLuxauCnCH7XZrSZSZw7XEEbkgrnZcaE1MK8wLtTYkF3g1J7nciYiaZDsTNYm2oDLTAM2JPq4rrlhVN5cXWpTPYh8P5wKbXNdoh"); + assertInvalidAddress(""); + assertInvalidAddress("TRTLv3xxpAFfXKwF5ond4sWDX3AVgZngT88KpPCCJKcuRjGktgp5HHTK2yV7NTo8659u5jwMigLmHaoFKho0OhVmF8WP9pVZhBL9kC#RoUKWRwpsx1F"); + assertInvalidAddress("TRTLuwafXHTPzj1d2wc7c9X69r3qG1277ecnLnUaZ61M1YV5d3GYAs1Jbc2q4C4fWN$C4fWNLoDLDvADvpjNYdt3sdRB434UidKXimQQn"); + assertInvalidAddress("1jRo3rcp9fjdfjdSGpx"); + assertInvalidAddress("GDARp92UtmTWDjZatG8sduRockSteadyWasHere3atrHSXr9vJzjHq2TfPrjateDz9Wc8ZJKuDayqJ$%"); + assertInvalidAddress("F3xQ8Gv6xnvDhUrM57z71bfFvu9HeofXtXpZRLnrCN2s2cKvkQowrWjJTGz4676ymKvU4NzPY8Cadgsdhsdfhg4gfJwL2yhhkJ7"); + } +} \ No newline at end of file diff --git a/assets/src/test/java/bisq/asset/coins/ZeroTest.java b/assets/src/test/java/bisq/asset/coins/ZeroTest.java new file mode 100644 index 0000000000..fc110d137d --- /dev/null +++ b/assets/src/test/java/bisq/asset/coins/ZeroTest.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 ZeroTest extends AbstractAssetTest { + + public ZeroTest() { + super(new Zero()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("t1cZTNaKS6juH6tGEhCUZmZhtbYGeYeuTrK"); + assertValidAddress("t1ZBPYJwK2UPbshwcYWRiCq7vw8VPDYumWu"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"); + assertInvalidAddress("38NwrYsD1HxQW5zfLT0QcUUXGMPvQgzTSn"); + assertInvalidAddress("8tP9rh3SH6n9cSLmV22vnSNNw56LKGpLrB"); + assertInvalidAddress("8Zbvjr"); + } +} diff --git a/assets/src/test/java/bisq/asset/tokens/EtherStoneTest.java b/assets/src/test/java/bisq/asset/tokens/EtherStoneTest.java new file mode 100644 index 0000000000..f5306ff22a --- /dev/null +++ b/assets/src/test/java/bisq/asset/tokens/EtherStoneTest.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.tokens; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class EtherStoneTest extends AbstractAssetTest { + + public EtherStoneTest () { + super(new EtherStone()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("0x0d81d9e21bd7c5bb095535624dcb0759e64b3899"); + assertValidAddress("0d81d9e21bd7c5bb095535624dcb0759e64b3899"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("0x65767ec6d4d3d18a200842352485cdc37cbf3a216"); + assertInvalidAddress("0x65767ec6d4d3d18a200842352485cdc37cbf3a2g"); + assertInvalidAddress("65767ec6d4d3d18a200842352485cdc37cbf3a2g"); + } +} diff --git a/assets/src/test/java/bisq/asset/tokens/GreenBlockCoinTest.java b/assets/src/test/java/bisq/asset/tokens/GreenBlockCoinTest.java new file mode 100755 index 0000000000..48cb2d77a9 --- /dev/null +++ b/assets/src/test/java/bisq/asset/tokens/GreenBlockCoinTest.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.tokens; + +import bisq.asset.AbstractAssetTest; + +import org.junit.Test; + +public class GreenBlockCoinTest extends AbstractAssetTest { + + public GreenBlockCoinTest() { + super(new GreenBlockCoin()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("0x2a65Aca4D5fC5B5C859090a6c34d164135398226"); + assertValidAddress("2a65Aca4D5fC5B5C859090a6c34d164135398226"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266"); + assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d16413539822g"); + assertInvalidAddress("2a65Aca4D5fC5B5C859090a6c34d16413539822g"); + } +} diff --git a/common/build.gradle b/common/build.gradle index 9fe1ab739a..2b65000dc6 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -45,7 +45,7 @@ dependencies { compile('com.google.inject:guice:4.1.0') { exclude(module: 'guava') } - compile('network.bisq.libdohj:libdohj-core:9573d077') { + compile('network.bisq.libdohj:libdohj-core:d4ace7bc') { exclude(module: 'jsr305') exclude(module: 'slf4j-api') exclude(module: 'guava') diff --git a/common/src/main/java/bisq/common/app/Log.java b/common/src/main/java/bisq/common/app/Log.java index 0d808629e2..45eae96b49 100644 --- a/common/src/main/java/bisq/common/app/Log.java +++ b/common/src/main/java/bisq/common/app/Log.java @@ -88,6 +88,10 @@ public class Log { logbackLogger.addAppender(errorAppender);*/ } + 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]; diff --git a/common/src/main/java/bisq/common/proto/network/NetworkEnvelope.java b/common/src/main/java/bisq/common/proto/network/NetworkEnvelope.java index f4691d5d79..b09996f1a8 100644 --- a/common/src/main/java/bisq/common/proto/network/NetworkEnvelope.java +++ b/common/src/main/java/bisq/common/proto/network/NetworkEnvelope.java @@ -67,4 +67,10 @@ public abstract class NetworkEnvelope implements Envelope { return messageVersion; } + @Override + public String toString() { + return "NetworkEnvelope{" + + "\n messageVersion=" + messageVersion + + "\n}"; + } } diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index 28eef44de6..5cb84c2db7 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -56,6 +56,7 @@ message NetworkEnvelope { AddPersistableNetworkPayloadMessage add_persistable_network_payload_message = 31; AckMessage ack_message = 32; + RepublishGovernanceDataRequest republish_governance_data_request = 33; } } @@ -319,6 +320,9 @@ message NewBlockBroadcastMessage { BaseBlock raw_block = 1; } +message RepublishGovernanceDataRequest { +} + /////////////////////////////////////////////////////////////////////////////////////////// // Payload /////////////////////////////////////////////////////////////////////////////////////////// @@ -931,6 +935,9 @@ message PersistableEnvelope { MyBlindVoteList my_blind_vote_list = 22; MeritList merit_list = 23; BondedRoleList bonded_role_list = 24; + RemovedAssetList removed_asset_list = 25; + EvaluatedProposalList evaluated_proposal_list = 26; + DecryptedBallotsWithMeritsList decrypted_ballots_with_merits_list = 27; } } @@ -1336,7 +1343,6 @@ message Tx { repeated BaseTxOutput tx_outputs = 1; TxType txType = 2; int64 burnt_fee = 3; - int32 unlock_block_height = 4; } enum TxType { @@ -1351,8 +1357,8 @@ enum TxType { COMPENSATION_REQUEST = 8; BLIND_VOTE = 9; VOTE_REVEAL = 10; - LOCK_UP = 11; - UN_LOCK = 12; + LOCKUP = 11; + UNLOCK = 12; } message TxInput { @@ -1381,25 +1387,26 @@ message RawTxOutput { message TxOutput { TxOutputType tx_output_type = 1; int32 lock_time = 2; + int32 unlock_block_height = 3; } enum TxOutputType { PB_ERROR_TX_OUTPUT_TYPE = 0; - UNDEFINED = 1; + UNDEFINED_OUTPUT = 1; GENESIS_OUTPUT = 2; BSQ_OUTPUT = 3; BTC_OUTPUT = 4; PROPOSAL_OP_RETURN_OUTPUT = 5; COMP_REQ_OP_RETURN_OUTPUT = 6; - ISSUANCE_CANDIDATE_OUTPUT = 7; - BLIND_VOTE_LOCK_STAKE_OUTPUT = 8; - BLIND_VOTE_OP_RETURN_OUTPUT = 9; - VOTE_REVEAL_UNLOCK_STAKE_OUTPUT = 10; - VOTE_REVEAL_OP_RETURN_OUTPUT = 11; - BOND_LOCK = 12; - BOND_LOCK_OP_RETURN_OUTPUT = 13; - BOND_UNLOCK = 14; - BOND_UNLOCK_OP_RETURN_OUTPUT = 15; + CONFISCATE_BOND_OP_RETURN_OUTPUT = 7; + ISSUANCE_CANDIDATE_OUTPUT = 8; + BLIND_VOTE_LOCK_STAKE_OUTPUT = 9; + BLIND_VOTE_OP_RETURN_OUTPUT = 10; + VOTE_REVEAL_UNLOCK_STAKE_OUTPUT = 11; + VOTE_REVEAL_OP_RETURN_OUTPUT = 12; + LOCKUP_OUTPUT = 13; + LOCKUP_OP_RETURN_OUTPUT = 14; + UNLOCK_OUTPUT = 15; INVALID_OUTPUT = 16; } @@ -1470,11 +1477,11 @@ message Proposal { string tx_id = 5; oneof message { CompensationProposal compensation_proposal = 6; - GenericProposal generic_proposal = 7; - ChangeParamProposal change_param_proposal = 8; - RemoveAltcoinProposal remove_altcoin_proposal = 9; - ConfiscateBondProposal confiscate_bond_proposal = 10; - BondedRoleProposal bonded_role_proposal = 11; + ChangeParamProposal change_param_proposal = 7; + BondedRoleProposal bonded_role_proposal = 8; + ConfiscateBondProposal confiscate_bond_proposal = 9; + GenericProposal generic_proposal = 10; + RemoveAssetProposal remove_asset_proposal = 11; } } @@ -1483,24 +1490,32 @@ message CompensationProposal { string bsq_address = 2; } -message GenericProposal { -} - message ChangeParamProposal { string param = 1; // name of enum int64 param_value = 2; } -message RemoveAltcoinProposal { - string currency_code = 1; +message BondedRoleProposal { + BondedRole bonded_role = 1; } message ConfiscateBondProposal { bytes hash = 1; } -message BondedRoleProposal { - BondedRole bonded_role = 1; +message GenericProposal { +} + +message RemoveAssetProposal { + string ticker_symbol = 1; +} + +message RemovedAsset { + string ticker_symbol = 1; + string remove_reason = 2; +} +message RemovedAssetList { + repeated RemovedAsset removed_asset = 1; } message BondedRole { @@ -1616,6 +1631,39 @@ message MeritList { repeated Merit merit = 1; } +message ProposalVoteResult { + Proposal proposal = 1; + int64 stake_of_Accepted_votes = 2; + int64 stake_of_Rejected_votes = 3; + int32 num_accepted_votes = 4; + int32 num_rejected_votes = 5; + int32 num_ignored_votes = 6; +} + +message EvaluatedProposal { + bool is_accepted = 1; + ProposalVoteResult proposal_vote_result = 2; + int64 required_quorum = 3; + int64 required_threshold = 4; +} + +message EvaluatedProposalList { + repeated EvaluatedProposal evaluated_proposal = 1; +} + +message DecryptedBallotsWithMerits { + bytes hash_of_blind_vote_list = 1; + string vote_reveal_tx_id = 2; + string blind_vote_tx_id = 3; + int64 stake = 4; + BallotList ballot_list = 5; + MeritList merit_list = 6; +} + +message DecryptedBallotsWithMeritsList { + repeated DecryptedBallotsWithMerits decrypted_ballots_with_merits = 1; +} + /////////////////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/build.gradle b/core/build.gradle index 3c1c180bf2..e01ec39b55 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -24,7 +24,7 @@ dependencies { compile project(':assets') compile project(':p2p') compile 'net.sf.jopt-simple:jopt-simple:5.0.3' - compile('network.bisq.btcd-cli4j:btcd-cli4j-core:cb5c474b') { + compile('network.bisq.btcd-cli4j:btcd-cli4j-core:3864e1c4') { exclude(module: 'slf4j-api') exclude(module: 'httpclient') exclude(module: 'commons-lang3') @@ -32,7 +32,7 @@ dependencies { exclude(module: 'jackson-annotations') exclude(module: 'jackson-databind') } - compile('network.bisq.btcd-cli4j:btcd-cli4j-daemon:cb5c474b') { + compile('network.bisq.btcd-cli4j:btcd-cli4j-daemon:3864e1c4') { exclude(module: 'slf4j-api') exclude(module: 'httpclient') exclude(module: 'commons-lang3') diff --git a/core/src/main/java/bisq/core/CoreModule.java b/core/src/main/java/bisq/core/CoreModule.java index 0a65ad953e..9e94ab2021 100644 --- a/core/src/main/java/bisq/core/CoreModule.java +++ b/core/src/main/java/bisq/core/CoreModule.java @@ -19,7 +19,7 @@ package bisq.core; import bisq.core.alert.AlertModule; import bisq.core.app.AppOptionKeys; -import bisq.core.app.AvoidStandbyMode; +import bisq.core.app.AvoidStandbyModeService; import bisq.core.app.BisqEnvironment; import bisq.core.app.BisqFacade; import bisq.core.app.BisqSetup; @@ -94,7 +94,7 @@ public class CoreModule extends AppModule { bind(Preferences.class).in(Singleton.class); bind(BridgeAddressProvider.class).to(Preferences.class).in(Singleton.class); bind(CorruptedDatabaseFilesHandler.class).in(Singleton.class); - bind(AvoidStandbyMode.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); diff --git a/core/src/main/java/bisq/core/app/AvoidStandbyMode.java b/core/src/main/java/bisq/core/app/AvoidStandbyModeService.java similarity index 93% rename from core/src/main/java/bisq/core/app/AvoidStandbyMode.java rename to core/src/main/java/bisq/core/app/AvoidStandbyModeService.java index 657ef0bb7e..732d97edb5 100644 --- a/core/src/main/java/bisq/core/app/AvoidStandbyMode.java +++ b/core/src/main/java/bisq/core/app/AvoidStandbyModeService.java @@ -40,18 +40,18 @@ import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; @Slf4j -public class AvoidStandbyMode { +public class AvoidStandbyModeService { private final Preferences preferences; private volatile boolean isStopped; @Inject - public AvoidStandbyMode(Preferences preferences) { + public AvoidStandbyModeService(Preferences preferences) { this.preferences = preferences; preferences.getUseStandbyModeProperty().addListener((observable, oldValue, newValue) -> { if (newValue) { isStopped = true; - log.info("AvoidStandbyMode stopped"); + log.info("AvoidStandbyModeService stopped"); } else { start(); } @@ -67,9 +67,9 @@ public class AvoidStandbyMode { private void start() { isStopped = false; - log.info("AvoidStandbyMode started"); + log.info("AvoidStandbyModeService started"); Thread thread = new Thread(this::play); - thread.setName("AvoidStandbyMode-thread"); + thread.setName("AvoidStandbyModeService-thread"); thread.start(); } diff --git a/core/src/main/java/bisq/core/app/BisqEnvironment.java b/core/src/main/java/bisq/core/app/BisqEnvironment.java index 5e463e7606..b75074c5b5 100644 --- a/core/src/main/java/bisq/core/app/BisqEnvironment.java +++ b/core/src/main/java/bisq/core/app/BisqEnvironment.java @@ -292,7 +292,7 @@ public class BisqEnvironment extends StandardEnvironment { ""; genesisBlockHeight = commandLineProperties.containsProperty(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) ? (String) commandLineProperties.getProperty(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) : - ""; + "-1"; daoActivated = commandLineProperties.containsProperty(DaoOptionKeys.DAO_ACTIVATED) ? (String) commandLineProperties.getProperty(DaoOptionKeys.DAO_ACTIVATED) : ""; diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index cb99f061f5..0950ffc270 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -18,11 +18,12 @@ package bisq.core.app; import bisq.core.arbitration.ArbitratorManager; +import bisq.core.btc.BaseCurrencyNetwork; import bisq.core.btc.BtcOptionKeys; -import bisq.core.btc.RegTestHost; +import bisq.core.btc.setup.RegTestHost; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.dao.DaoOptionKeys; import bisq.core.dao.DaoSetup; import bisq.core.exceptions.BisqException; @@ -208,7 +209,11 @@ public abstract class BisqExecutable implements GracefulShutDownHandler { protected void setupDevEnv() { DevEnv.setDevMode(injector.getInstance(Key.get(Boolean.class, Names.named(CommonOptionKeys.USE_DEV_MODE)))); - DevEnv.setDaoActivated(injector.getInstance(Key.get(Boolean.class, Names.named(DaoOptionKeys.DAO_ACTIVATED)))); + + BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); + boolean isRegTestOrTestNet = (baseCurrencyNetwork.isTestnet() || baseCurrencyNetwork.isRegtest()); + boolean isDaoActivatedOptionSet = injector.getInstance(Key.get(Boolean.class, Names.named(DaoOptionKeys.DAO_ACTIVATED))); + DevEnv.setDaoActivated(isDaoActivatedOptionSet || isRegTestOrTestNet); } private void setCorruptedDataBaseFilesHandler() { @@ -432,7 +437,7 @@ public abstract class BisqExecutable implements GracefulShutDownHandler { description("Genesis transaction ID when not using the hard coded one", "")) .withRequiredArg(); parser.accepts(DaoOptionKeys.GENESIS_BLOCK_HEIGHT, - description("Genesis transaction block height when not using the hard coded one", "")) + description("Genesis transaction block height when not using the hard coded one", -1)) .withRequiredArg(); parser.accepts(DaoOptionKeys.DAO_ACTIVATED, description("Developer flag. If true it enables dao phase 2 features.", false)) diff --git a/core/src/main/java/bisq/core/app/BisqFacade.java b/core/src/main/java/bisq/core/app/BisqFacade.java index a496e0bab9..235c5f0117 100644 --- a/core/src/main/java/bisq/core/app/BisqFacade.java +++ b/core/src/main/java/bisq/core/app/BisqFacade.java @@ -17,7 +17,7 @@ package bisq.core.app; -import bisq.core.btc.BalanceModel; +import bisq.core.btc.model.BalanceModel; import bisq.core.presentation.BalancePresentation; import bisq.common.app.Version; diff --git a/core/src/main/java/bisq/core/app/BisqHeadlessApp.java b/core/src/main/java/bisq/core/app/BisqHeadlessApp.java index bd0fcc8e77..4518da4ec9 100644 --- a/core/src/main/java/bisq/core/app/BisqHeadlessApp.java +++ b/core/src/main/java/bisq/core/app/BisqHeadlessApp.java @@ -92,6 +92,7 @@ public class BisqHeadlessApp implements HeadlessApp { bisqSetup.setDisplaySecurityRecommendationHandler(key -> log.info("onDisplaySecurityRecommendationHandler")); bisqSetup.setDisplayLocalhostHandler(key -> log.info("onDisplayLocalhostHandler")); bisqSetup.setWrongOSArchitectureHandler(msg -> log.info("onWrongOSArchitectureHandler. msg={}", msg)); + bisqSetup.setVoteResultExceptionHandler(voteResultException -> log.info("voteResultException={}", voteResultException)); //TODO move to bisqSetup corruptedDatabaseFilesHandler.getCorruptedDatabaseFiles().ifPresent(files -> log.info("getCorruptedDatabaseFiles. files={}", files)); diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index 1fbb63757d..e7511c6bd7 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -23,13 +23,15 @@ import bisq.core.alert.PrivateNotificationManager; import bisq.core.alert.PrivateNotificationPayload; import bisq.core.arbitration.ArbitratorManager; import bisq.core.arbitration.DisputeManager; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.BalanceModel; import bisq.core.btc.listeners.BalanceListener; +import bisq.core.btc.model.AddressEntry; +import bisq.core.btc.model.BalanceModel; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.WalletsManager; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.dao.DaoSetup; +import bisq.core.dao.governance.voteresult.VoteResultException; +import bisq.core.dao.governance.voteresult.VoteResultService; import bisq.core.filter.FilterManager; import bisq.core.locale.Res; import bisq.core.notifications.MobileNotificationService; @@ -61,6 +63,7 @@ import bisq.common.Clock; import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.app.DevEnv; +import bisq.common.app.Log; import bisq.common.crypto.CryptoException; import bisq.common.crypto.KeyRing; import bisq.common.crypto.SealedAndSigned; @@ -88,8 +91,6 @@ import javafx.collections.SetChangeListener; import org.spongycastle.crypto.params.KeyParameter; -import java.security.Security; - import java.net.InetSocketAddress; import java.net.Socket; @@ -101,6 +102,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; +import ch.qos.logback.classic.Level; + import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -146,6 +149,7 @@ public class BisqSetup { private final DisputeMsgEvents disputeMsgEvents; private final PriceAlert priceAlert; private final MarketAlerts marketAlerts; + private final VoteResultService voteResultService; private final BSFormatter formatter; @Setter @Nullable @@ -172,6 +176,9 @@ public class BisqSetup { private BiConsumer displayUpdateHandler; @Setter @Nullable + private Consumer voteResultExceptionHandler; + @Setter + @Nullable private Consumer displayPrivateNotificationHandler; @Getter @@ -215,6 +222,7 @@ public class BisqSetup { DisputeMsgEvents disputeMsgEvents, PriceAlert priceAlert, MarketAlerts marketAlerts, + VoteResultService voteResultService, BSFormatter formatter) { @@ -250,6 +258,7 @@ public class BisqSetup { this.disputeMsgEvents = disputeMsgEvents; this.priceAlert = priceAlert; this.marketAlerts = marketAlerts; + this.voteResultService = voteResultService; this.formatter = formatter; } @@ -404,7 +413,6 @@ public class BisqSetup { step3(); }); } catch (Throwable e) { - log.info("Localhost Bitcoin node not detected."); UserThread.execute(BisqSetup.this::step3); } finally { if (socket != null) { @@ -471,6 +479,11 @@ public class BisqSetup { walletInitialized.addListener(walletInitializedListener); else if (displayTorNetworkSettingsHandler != null) displayTorNetworkSettingsHandler.accept(true); + + log.info("Set log level for org.berndpruenster.netlayer classes to DEBUG to show more details for " + + "Tor network connection issues"); + Log.setCustomLogLevel("org.berndpruenster.netlayer", Level.DEBUG); + }, STARTUP_TIMEOUT_MINUTES, TimeUnit.MINUTES); p2pNetworkReady = p2PNetworkSetup.init(this::initWallet, displayTorNetworkSettingsHandler); @@ -627,6 +640,15 @@ public class BisqSetup { } }); + voteResultService.getVoteResultExceptions().addListener((ListChangeListener) c -> { + c.next(); + if (c.wasAdded() && voteResultExceptionHandler != null) { + c.getAddedSubList().forEach(e -> { + voteResultExceptionHandler.accept(e); + }); + } + }); + mobileNotificationService.onAllServicesInitialized(); myOfferTakenEvents.onAllServicesInitialized(); tradeEvents.onAllServicesInitialized(); diff --git a/core/src/main/java/bisq/core/app/P2PNetworkSetup.java b/core/src/main/java/bisq/core/app/P2PNetworkSetup.java index 36385120e4..8d70bf8bdc 100644 --- a/core/src/main/java/bisq/core/app/P2PNetworkSetup.java +++ b/core/src/main/java/bisq/core/app/P2PNetworkSetup.java @@ -77,8 +77,6 @@ public class P2PNetworkSetup { } BooleanProperty init(Runnable initWalletServiceHandler, @Nullable Consumer displayTorNetworkSettingsHandler) { - log.info("init"); - StringProperty bootstrapState = new SimpleStringProperty(); StringProperty bootstrapWarning = new SimpleStringProperty(); BooleanProperty hiddenServicePublished = new SimpleBooleanProperty(); diff --git a/core/src/main/java/bisq/core/app/WalletAppSetup.java b/core/src/main/java/bisq/core/app/WalletAppSetup.java index 537f194738..59e5bca1da 100644 --- a/core/src/main/java/bisq/core/app/WalletAppSetup.java +++ b/core/src/main/java/bisq/core/app/WalletAppSetup.java @@ -17,8 +17,8 @@ package bisq.core.app; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.WalletsManager; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.locale.Res; import bisq.core.user.Preferences; import bisq.core.util.BSFormatter; diff --git a/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java b/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java index af059c8dba..871163fdf1 100644 --- a/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java +++ b/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java @@ -18,10 +18,12 @@ package bisq.core.app.misc; import bisq.core.dao.DaoSetup; +import bisq.core.dao.governance.asset.AssetService; import bisq.core.dao.governance.ballot.BallotListService; import bisq.core.dao.governance.blindvote.MyBlindVoteListService; import bisq.core.dao.governance.myvote.MyVoteListService; import bisq.core.dao.governance.role.BondedRolesService; +import bisq.core.dao.governance.voteresult.VoteResultService; import bisq.core.filter.FilterManager; import bisq.core.payment.AccountAgeWitnessService; import bisq.core.trade.statistics.TradeStatisticsManager; @@ -50,7 +52,9 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P { MyVoteListService myVoteListService, BallotListService ballotListService, MyBlindVoteListService myBlindVoteListService, - BondedRolesService bondedRolesService) { + BondedRolesService bondedRolesService, + AssetService assetService, + VoteResultService voteResultService) { super(encryptionService, keyRing, p2PService, @@ -64,6 +68,8 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P { persistedDataHosts.add(ballotListService); persistedDataHosts.add(myBlindVoteListService); persistedDataHosts.add(bondedRolesService); + persistedDataHosts.add(assetService); + persistedDataHosts.add(voteResultService); } @Override 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 0d8c35395c..a1dbf242e2 100644 --- a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java +++ b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java @@ -21,9 +21,9 @@ import bisq.core.app.AppOptionKeys; import bisq.core.app.BisqEnvironment; import bisq.core.app.BisqExecutable; import bisq.core.arbitration.ArbitratorManager; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.offer.OpenOfferManager; import bisq.network.p2p.P2PService; diff --git a/core/src/main/java/bisq/core/arbitration/DisputeManager.java b/core/src/main/java/bisq/core/arbitration/DisputeManager.java index d32b07c163..637357787c 100644 --- a/core/src/main/java/bisq/core/arbitration/DisputeManager.java +++ b/core/src/main/java/bisq/core/arbitration/DisputeManager.java @@ -24,12 +24,12 @@ import bisq.core.arbitration.messages.OpenNewDisputeMessage; import bisq.core.arbitration.messages.PeerOpenedDisputeMessage; import bisq.core.arbitration.messages.PeerPublishedDisputePayoutTxMessage; import bisq.core.btc.exceptions.TransactionVerificationException; +import bisq.core.btc.exceptions.TxBroadcastException; import bisq.core.btc.exceptions.WalletException; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; -import bisq.core.btc.wallet.TxBroadcastException; import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOfferManager; diff --git a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java index 27abc0235b..51c27f66e0 100644 --- a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java +++ b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java @@ -17,9 +17,6 @@ package bisq.core.btc; -import bisq.core.app.BisqEnvironment; -import bisq.core.provider.fee.FeeService; - import org.libdohj.params.DashMainNetParams; import org.libdohj.params.DashRegTestParams; import org.libdohj.params.DashTestNet3Params; @@ -67,6 +64,10 @@ public enum BaseCurrencyNetwork { return "MAINNET".equals(network); } + public boolean isTestnet() { + return "TESTNET".equals(network); + } + public boolean isRegtest() { return "REGTEST".equals(network); } @@ -84,15 +85,6 @@ public enum BaseCurrencyNetwork { } public long getDefaultMinFeePerByte() { - switch (BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()) { - case "BTC": - return 1; - case "LTC": - return FeeService.LTC_REFERENCE_DEFAULT_MIN_TX_FEE.value; - case "DASH": - return FeeService.DASH_REFERENCE_DEFAULT_MIN_TX_FEE.value; - default: - throw new RuntimeException("Unsupported code at getDefaultMinFee: " + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()); - } + return 1; } } diff --git a/core/src/main/java/bisq/core/btc/BitcoinModule.java b/core/src/main/java/bisq/core/btc/BitcoinModule.java index 8faf462e1f..b9e365a3bb 100644 --- a/core/src/main/java/bisq/core/btc/BitcoinModule.java +++ b/core/src/main/java/bisq/core/btc/BitcoinModule.java @@ -18,12 +18,16 @@ package bisq.core.btc; import bisq.core.app.AppOptionKeys; +import bisq.core.btc.model.AddressEntryList; +import bisq.core.btc.model.BalanceModel; +import bisq.core.btc.nodes.BtcNodes; +import bisq.core.btc.setup.RegTestHost; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BsqCoinSelector; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.NonBsqCoinSelector; import bisq.core.btc.wallet.TradeWalletService; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.provider.PriceNodeHttpClient; import bisq.core.provider.ProvidersRepository; import bisq.core.provider.fee.FeeProvider; @@ -73,7 +77,7 @@ public class BitcoinModule extends AppModule { bind(TradeWalletService.class).in(Singleton.class); bind(BsqCoinSelector.class).in(Singleton.class); bind(NonBsqCoinSelector.class).in(Singleton.class); - bind(BitcoinNodes.class).in(Singleton.class); + bind(BtcNodes.class).in(Singleton.class); bind(BalanceModel.class).in(Singleton.class); bind(PriceNodeHttpClient.class).in(Singleton.class); diff --git a/core/src/main/java/bisq/core/btc/blockchain/package-info.java b/core/src/main/java/bisq/core/btc/blockchain/package-info.java new file mode 100644 index 0000000000..18cd53cfe6 --- /dev/null +++ b/core/src/main/java/bisq/core/btc/blockchain/package-info.java @@ -0,0 +1,20 @@ +/* + * 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 . + */ + +// This package is not used atm but might be used in future when we need to lookup non-wallet transactions + +package bisq.core.btc.blockchain; diff --git a/core/src/main/java/bisq/core/btc/AddressEntryException.java b/core/src/main/java/bisq/core/btc/exceptions/AddressEntryException.java similarity index 96% rename from core/src/main/java/bisq/core/btc/AddressEntryException.java rename to core/src/main/java/bisq/core/btc/exceptions/AddressEntryException.java index ac1dd973d0..4dbb43eced 100644 --- a/core/src/main/java/bisq/core/btc/AddressEntryException.java +++ b/core/src/main/java/bisq/core/btc/exceptions/AddressEntryException.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.exceptions; public class AddressEntryException extends Exception { public AddressEntryException(String message) { diff --git a/core/src/main/java/bisq/core/btc/wallet/InsufficientBsqException.java b/core/src/main/java/bisq/core/btc/exceptions/InsufficientBsqException.java similarity index 96% rename from core/src/main/java/bisq/core/btc/wallet/InsufficientBsqException.java rename to core/src/main/java/bisq/core/btc/exceptions/InsufficientBsqException.java index aa9a819377..3b3749baf4 100644 --- a/core/src/main/java/bisq/core/btc/wallet/InsufficientBsqException.java +++ b/core/src/main/java/bisq/core/btc/exceptions/InsufficientBsqException.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.exceptions; import org.bitcoinj.core.Coin; import org.bitcoinj.core.InsufficientMoneyException; diff --git a/core/src/main/java/bisq/core/btc/InsufficientFundsException.java b/core/src/main/java/bisq/core/btc/exceptions/InsufficientFundsException.java similarity index 96% rename from core/src/main/java/bisq/core/btc/InsufficientFundsException.java rename to core/src/main/java/bisq/core/btc/exceptions/InsufficientFundsException.java index 5c26dc0aff..d5a2af9c29 100644 --- a/core/src/main/java/bisq/core/btc/InsufficientFundsException.java +++ b/core/src/main/java/bisq/core/btc/exceptions/InsufficientFundsException.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.exceptions; public class InsufficientFundsException extends Exception { public InsufficientFundsException(String message) { diff --git a/core/src/main/java/bisq/core/btc/wallet/TxBroadcastException.java b/core/src/main/java/bisq/core/btc/exceptions/TxBroadcastException.java similarity index 97% rename from core/src/main/java/bisq/core/btc/wallet/TxBroadcastException.java rename to core/src/main/java/bisq/core/btc/exceptions/TxBroadcastException.java index 7dddcfa90f..e2eace17cc 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TxBroadcastException.java +++ b/core/src/main/java/bisq/core/btc/exceptions/TxBroadcastException.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.exceptions; import lombok.Getter; diff --git a/core/src/main/java/bisq/core/btc/wallet/TxBroadcastTimeoutException.java b/core/src/main/java/bisq/core/btc/exceptions/TxBroadcastTimeoutException.java similarity index 88% rename from core/src/main/java/bisq/core/btc/wallet/TxBroadcastTimeoutException.java rename to core/src/main/java/bisq/core/btc/exceptions/TxBroadcastTimeoutException.java index 0f6c163530..e54f35d5be 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TxBroadcastTimeoutException.java +++ b/core/src/main/java/bisq/core/btc/exceptions/TxBroadcastTimeoutException.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.exceptions; import org.bitcoinj.core.Transaction; import org.bitcoinj.wallet.Wallet; @@ -35,10 +35,9 @@ public class TxBroadcastTimeoutException extends TxBroadcastException { private final Wallet wallet; /** - * - * @param localTx The tx we sent out - * @param delay The timeout delay - * @param wallet Wallet is needed if a client is calling wallet.commitTx(tx) + * @param localTx The tx we sent out + * @param delay The timeout delay + * @param wallet Wallet is needed if a client is calling wallet.commitTx(tx) */ public TxBroadcastTimeoutException(Transaction localTx, int delay, Wallet wallet) { super("The transaction was not broadcasted in " + delay + diff --git a/core/src/main/java/bisq/core/btc/wallet/TxMalleabilityException.java b/core/src/main/java/bisq/core/btc/exceptions/TxMalleabilityException.java similarity index 97% rename from core/src/main/java/bisq/core/btc/wallet/TxMalleabilityException.java rename to core/src/main/java/bisq/core/btc/exceptions/TxMalleabilityException.java index 08831485d6..caa262b4a3 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TxMalleabilityException.java +++ b/core/src/main/java/bisq/core/btc/exceptions/TxMalleabilityException.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.exceptions; import org.bitcoinj.core.Transaction; diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqBalanceListener.java b/core/src/main/java/bisq/core/btc/listeners/BsqBalanceListener.java similarity index 97% rename from core/src/main/java/bisq/core/btc/wallet/BsqBalanceListener.java rename to core/src/main/java/bisq/core/btc/listeners/BsqBalanceListener.java index e65a159e90..5a49bda668 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BsqBalanceListener.java +++ b/core/src/main/java/bisq/core/btc/listeners/BsqBalanceListener.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.listeners; import org.bitcoinj.core.Coin; diff --git a/core/src/main/java/bisq/core/btc/AddressEntry.java b/core/src/main/java/bisq/core/btc/model/AddressEntry.java similarity index 99% rename from core/src/main/java/bisq/core/btc/AddressEntry.java rename to core/src/main/java/bisq/core/btc/model/AddressEntry.java index 7dad15efb6..5a2455dfc8 100644 --- a/core/src/main/java/bisq/core/btc/AddressEntry.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntry.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.model; import bisq.core.app.BisqEnvironment; diff --git a/core/src/main/java/bisq/core/btc/AddressEntryList.java b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java similarity index 99% rename from core/src/main/java/bisq/core/btc/AddressEntryList.java rename to core/src/main/java/bisq/core/btc/model/AddressEntryList.java index f489f90429..42a5bb0ee5 100644 --- a/core/src/main/java/bisq/core/btc/AddressEntryList.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.model; import bisq.common.proto.persistable.PersistableEnvelope; import bisq.common.proto.persistable.PersistedDataHost; diff --git a/core/src/main/java/bisq/core/btc/BalanceModel.java b/core/src/main/java/bisq/core/btc/model/BalanceModel.java similarity index 99% rename from core/src/main/java/bisq/core/btc/BalanceModel.java rename to core/src/main/java/bisq/core/btc/model/BalanceModel.java index 87b1af4a6c..99bf210019 100644 --- a/core/src/main/java/bisq/core/btc/BalanceModel.java +++ b/core/src/main/java/bisq/core/btc/model/BalanceModel.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.model; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.offer.OpenOfferManager; diff --git a/core/src/main/java/bisq/core/btc/data/InputsAndChangeOutput.java b/core/src/main/java/bisq/core/btc/model/InputsAndChangeOutput.java similarity index 98% rename from core/src/main/java/bisq/core/btc/data/InputsAndChangeOutput.java rename to core/src/main/java/bisq/core/btc/model/InputsAndChangeOutput.java index bbc421a14d..79a1bb6d0b 100644 --- a/core/src/main/java/bisq/core/btc/data/InputsAndChangeOutput.java +++ b/core/src/main/java/bisq/core/btc/model/InputsAndChangeOutput.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.data; +package bisq.core.btc.model; import java.util.ArrayList; diff --git a/core/src/main/java/bisq/core/btc/data/PreparedDepositTxAndMakerInputs.java b/core/src/main/java/bisq/core/btc/model/PreparedDepositTxAndMakerInputs.java similarity index 97% rename from core/src/main/java/bisq/core/btc/data/PreparedDepositTxAndMakerInputs.java rename to core/src/main/java/bisq/core/btc/model/PreparedDepositTxAndMakerInputs.java index 6d414a492c..6fcf8bfe7a 100644 --- a/core/src/main/java/bisq/core/btc/data/PreparedDepositTxAndMakerInputs.java +++ b/core/src/main/java/bisq/core/btc/model/PreparedDepositTxAndMakerInputs.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.data; +package bisq.core.btc.model; import java.util.ArrayList; diff --git a/core/src/main/java/bisq/core/btc/data/RawTransactionInput.java b/core/src/main/java/bisq/core/btc/model/RawTransactionInput.java similarity index 98% rename from core/src/main/java/bisq/core/btc/data/RawTransactionInput.java rename to core/src/main/java/bisq/core/btc/model/RawTransactionInput.java index cc1e42d249..895c061c1f 100644 --- a/core/src/main/java/bisq/core/btc/data/RawTransactionInput.java +++ b/core/src/main/java/bisq/core/btc/model/RawTransactionInput.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.data; +package bisq.core.btc.model; import bisq.common.proto.network.NetworkPayload; import bisq.common.proto.persistable.PersistablePayload; diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletNetworkConfig.java b/core/src/main/java/bisq/core/btc/nodes/BtcNetworkConfig.java similarity index 86% rename from core/src/main/java/bisq/core/btc/wallet/WalletNetworkConfig.java rename to core/src/main/java/bisq/core/btc/nodes/BtcNetworkConfig.java index e7105d294b..0c3477c1c1 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletNetworkConfig.java +++ b/core/src/main/java/bisq/core/btc/nodes/BtcNetworkConfig.java @@ -15,7 +15,9 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.nodes; + +import bisq.core.btc.setup.WalletConfig; import bisq.network.Socks5MultiDiscovery; @@ -32,8 +34,8 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nullable; -class WalletNetworkConfig { - private static final Logger log = LoggerFactory.getLogger(WalletNetworkConfig.class); +public class BtcNetworkConfig { + private static final Logger log = LoggerFactory.getLogger(BtcNetworkConfig.class); @Nullable private final Socks5Proxy proxy; @@ -41,15 +43,15 @@ class WalletNetworkConfig { private final NetworkParameters parameters; private final int socks5DiscoverMode; - WalletNetworkConfig(WalletConfig delegate, NetworkParameters parameters, int socks5DiscoverMode, - @Nullable Socks5Proxy proxy) { + public BtcNetworkConfig(WalletConfig delegate, NetworkParameters parameters, int socks5DiscoverMode, + @Nullable Socks5Proxy proxy) { this.delegate = delegate; this.parameters = parameters; this.socks5DiscoverMode = socks5DiscoverMode; this.proxy = proxy; } - void proposePeers(List peers) { + public void proposePeers(List peers) { if (!peers.isEmpty()) { log.info("You connect with peerAddresses: {}", peers); PeerAddress[] peerAddresses = peers.toArray(new PeerAddress[peers.size()]); diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcNodeConverter.java b/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java similarity index 98% rename from core/src/main/java/bisq/core/btc/wallet/BtcNodeConverter.java rename to core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java index 754cde8ebe..61bf64a4e5 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcNodeConverter.java +++ b/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java @@ -15,9 +15,9 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.nodes; -import bisq.core.btc.BitcoinNodes.BtcNode; +import bisq.core.btc.nodes.BtcNodes.BtcNode; import bisq.network.DnsLookupException; import bisq.network.DnsLookupTor; diff --git a/core/src/main/java/bisq/core/btc/BitcoinNodes.java b/core/src/main/java/bisq/core/btc/nodes/BtcNodes.java similarity index 96% rename from core/src/main/java/bisq/core/btc/BitcoinNodes.java rename to core/src/main/java/bisq/core/btc/nodes/BtcNodes.java index 18e2db3c06..c705d011e2 100644 --- a/core/src/main/java/bisq/core/btc/BitcoinNodes.java +++ b/core/src/main/java/bisq/core/btc/nodes/BtcNodes.java @@ -15,7 +15,7 @@ * along with bisq. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.nodes; import bisq.core.app.BisqEnvironment; @@ -35,7 +35,7 @@ import static com.google.common.base.Preconditions.checkArgument; // Managed here: https://github.com/bisq-network/roles/issues/39 @Slf4j -public class BitcoinNodes { +public class BtcNodes { public enum BitcoinNodesOption { PROVIDED, @@ -82,10 +82,10 @@ public class BitcoinNodes { return BisqEnvironment.getBaseCurrencyNetwork().isBitcoin() && BisqEnvironment.getBaseCurrencyNetwork().isMainnet(); } - public static List toBtcNodesList(Collection nodes) { + public static List toBtcNodesList(Collection nodes) { return nodes.stream() .filter(e -> !e.isEmpty()) - .map(BitcoinNodes.BtcNode::fromFullAddress) + .map(BtcNodes.BtcNode::fromFullAddress) .collect(Collectors.toList()); } diff --git a/core/src/main/java/bisq/core/btc/wallet/PeerAddressesRepository.java b/core/src/main/java/bisq/core/btc/nodes/BtcNodesRepository.java similarity index 79% rename from core/src/main/java/bisq/core/btc/wallet/PeerAddressesRepository.java rename to core/src/main/java/bisq/core/btc/nodes/BtcNodesRepository.java index 2ba9f95a45..4e0013b8fb 100644 --- a/core/src/main/java/bisq/core/btc/wallet/PeerAddressesRepository.java +++ b/core/src/main/java/bisq/core/btc/nodes/BtcNodesRepository.java @@ -15,9 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; - -import bisq.core.btc.BitcoinNodes.BtcNode; +package bisq.core.btc.nodes; import org.bitcoinj.core.PeerAddress; @@ -31,20 +29,20 @@ import java.util.stream.Stream; import javax.annotation.Nullable; -class PeerAddressesRepository { +public class BtcNodesRepository { private final BtcNodeConverter converter; - private final List nodes; + private final List nodes; - PeerAddressesRepository(BtcNodeConverter converter, List nodes) { + public BtcNodesRepository(List nodes) { + this(new BtcNodeConverter(), nodes); + } + + public BtcNodesRepository(BtcNodeConverter converter, List nodes) { this.converter = converter; this.nodes = nodes; } - PeerAddressesRepository(List nodes) { - this(new BtcNodeConverter(), nodes); - } - - List getPeerAddresses(@Nullable Socks5Proxy proxy, boolean isUseClearNodesWithProxies) { + public List getPeerAddresses(@Nullable Socks5Proxy proxy, boolean isUseClearNodesWithProxies) { List result; // We connect to onion nodes only in case we use Tor for BitcoinJ (default) to avoid privacy leaks at // exit nodes with bloom filters. @@ -65,28 +63,26 @@ class PeerAddressesRepository { private List getClearNodes() { return nodes.stream() - .filter(BtcNode::hasClearNetAddress) + .filter(BtcNodes.BtcNode::hasClearNetAddress) .flatMap(node -> nullableAsStream(converter.convertClearNode(node))) .collect(Collectors.toList()); } private List getOnionHosts() { return nodes.stream() - .filter(BtcNode::hasOnionAddress) + .filter(BtcNodes.BtcNode::hasOnionAddress) .flatMap(node -> nullableAsStream(converter.convertOnionHost(node))) .collect(Collectors.toList()); } private List getClearNodesBehindProxy(Socks5Proxy proxy) { return nodes.stream() - .filter(BtcNode::hasClearNetAddress) + .filter(BtcNodes.BtcNode::hasClearNetAddress) .flatMap(node -> nullableAsStream(converter.convertWithTor(node, proxy))) .collect(Collectors.toList()); } private static Stream nullableAsStream(@Nullable T item) { - return Optional.ofNullable(item) - .map(Stream::of) - .orElse(Stream.empty()); + return Optional.ofNullable(item).stream(); } } diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletSetupPreferences.java b/core/src/main/java/bisq/core/btc/nodes/BtcNodesSetupPreferences.java similarity index 67% rename from core/src/main/java/bisq/core/btc/wallet/WalletSetupPreferences.java rename to core/src/main/java/bisq/core/btc/nodes/BtcNodesSetupPreferences.java index 0044ad06e9..17dba40c16 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletSetupPreferences.java +++ b/core/src/main/java/bisq/core/btc/nodes/BtcNodesSetupPreferences.java @@ -15,11 +15,9 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.nodes; -import bisq.core.btc.BitcoinNodes; -import bisq.core.btc.BitcoinNodes.BitcoinNodesOption; -import bisq.core.btc.BitcoinNodes.BtcNode; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.user.Preferences; import bisq.common.util.Utilities; @@ -31,32 +29,29 @@ import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static bisq.core.btc.BitcoinNodes.BitcoinNodesOption.CUSTOM; -import static bisq.core.btc.wallet.WalletsSetup.DEFAULT_CONNECTIONS; - -class WalletSetupPreferences { - private static final Logger log = LoggerFactory.getLogger(WalletSetupPreferences.class); +public class BtcNodesSetupPreferences { + private static final Logger log = LoggerFactory.getLogger(BtcNodesSetupPreferences.class); private final Preferences preferences; - WalletSetupPreferences(Preferences preferences) { + public BtcNodesSetupPreferences(Preferences preferences) { this.preferences = preferences; } - List selectPreferredNodes(BitcoinNodes nodes) { - List result; + public List selectPreferredNodes(BtcNodes nodes) { + List result; - BitcoinNodesOption nodesOption = BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]; + BtcNodes.BitcoinNodesOption nodesOption = BtcNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]; switch (nodesOption) { case CUSTOM: String bitcoinNodes = preferences.getBitcoinNodes(); Set distinctNodes = Utilities.commaSeparatedListToSet(bitcoinNodes, false); - result = BitcoinNodes.toBtcNodesList(distinctNodes); + result = BtcNodes.toBtcNodesList(distinctNodes); if (result.isEmpty()) { log.warn("Custom nodes is set but no valid nodes are provided. " + "We fall back to provided nodes option."); - preferences.setBitcoinNodesOptionOrdinal(BitcoinNodesOption.PROVIDED.ordinal()); + preferences.setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.PROVIDED.ordinal()); result = nodes.getProvidedBtcNodes(); } break; @@ -72,12 +67,12 @@ class WalletSetupPreferences { return result; } - boolean isUseCustomNodes() { - return CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); + public boolean isUseCustomNodes() { + return BtcNodes.BitcoinNodesOption.CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal(); } - int calculateMinBroadcastConnections(List nodes) { - BitcoinNodesOption nodesOption = BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]; + public int calculateMinBroadcastConnections(List nodes) { + BtcNodes.BitcoinNodesOption nodesOption = BtcNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]; int result; switch (nodesOption) { case CUSTOM: @@ -88,7 +83,7 @@ class WalletSetupPreferences { break; case PUBLIC: // We keep the empty nodes - result = (int) Math.floor(DEFAULT_CONNECTIONS * 0.8); + result = (int) Math.floor(WalletsSetup.DEFAULT_CONNECTIONS * 0.8); break; case PROVIDED: default: diff --git a/core/src/main/java/bisq/core/btc/ProxySocketFactory.java b/core/src/main/java/bisq/core/btc/nodes/ProxySocketFactory.java similarity index 99% rename from core/src/main/java/bisq/core/btc/ProxySocketFactory.java rename to core/src/main/java/bisq/core/btc/nodes/ProxySocketFactory.java index 4b3eea873f..97d05e9f4d 100644 --- a/core/src/main/java/bisq/core/btc/ProxySocketFactory.java +++ b/core/src/main/java/bisq/core/btc/nodes/ProxySocketFactory.java @@ -33,7 +33,7 @@ * PircBotX. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.nodes; import javax.net.SocketFactory; diff --git a/core/src/main/java/bisq/core/btc/SeedPeersSocks5Dns.java b/core/src/main/java/bisq/core/btc/nodes/SeedPeersSocks5Dns.java similarity index 98% rename from core/src/main/java/bisq/core/btc/SeedPeersSocks5Dns.java rename to core/src/main/java/bisq/core/btc/nodes/SeedPeersSocks5Dns.java index 60554f8ed2..6c1dabbf2b 100644 --- a/core/src/main/java/bisq/core/btc/SeedPeersSocks5Dns.java +++ b/core/src/main/java/bisq/core/btc/nodes/SeedPeersSocks5Dns.java @@ -31,7 +31,7 @@ * limitations under the License. */ -package bisq.core.btc; +package bisq.core.btc.nodes; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.net.discovery.PeerDiscovery; @@ -50,7 +50,7 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nullable; - +// TODO not used anymore. Not sure if it was replaced by something else or removed by accident. /** * SeedPeersSocks5Dns resolves peers via Proxy (Socks5) remote DNS. */ diff --git a/core/src/main/java/bisq/core/btc/wallet/BisqDeterministicKeyChain.java b/core/src/main/java/bisq/core/btc/setup/BisqDeterministicKeyChain.java similarity index 98% rename from core/src/main/java/bisq/core/btc/wallet/BisqDeterministicKeyChain.java rename to core/src/main/java/bisq/core/btc/setup/BisqDeterministicKeyChain.java index 56ab52a9ea..e0d53ddd15 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BisqDeterministicKeyChain.java +++ b/core/src/main/java/bisq/core/btc/setup/BisqDeterministicKeyChain.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.setup; import org.bitcoinj.crypto.ChildNumber; import org.bitcoinj.crypto.DeterministicKey; diff --git a/core/src/main/java/bisq/core/btc/wallet/BisqKeyChainFactory.java b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java similarity index 98% rename from core/src/main/java/bisq/core/btc/wallet/BisqKeyChainFactory.java rename to core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java index b4a71608aa..0cb088f31d 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BisqKeyChainFactory.java +++ b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.setup; import org.bitcoinj.crypto.ChildNumber; import org.bitcoinj.crypto.DeterministicKey; diff --git a/core/src/main/java/bisq/core/btc/wallet/BisqKeyChainGroup.java b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroup.java similarity index 98% rename from core/src/main/java/bisq/core/btc/wallet/BisqKeyChainGroup.java rename to core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroup.java index e78f16f188..2146bfea7e 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BisqKeyChainGroup.java +++ b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroup.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.setup; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.wallet.DeterministicKeyChain; diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqWallet.java b/core/src/main/java/bisq/core/btc/setup/BsqWallet.java similarity index 97% rename from core/src/main/java/bisq/core/btc/wallet/BsqWallet.java rename to core/src/main/java/bisq/core/btc/setup/BsqWallet.java index 09763377fd..73c5ac9634 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BsqWallet.java +++ b/core/src/main/java/bisq/core/btc/setup/BsqWallet.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.setup; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.wallet.KeyChainGroup; diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcDeterministicKeyChain.java b/core/src/main/java/bisq/core/btc/setup/BtcDeterministicKeyChain.java similarity index 98% rename from core/src/main/java/bisq/core/btc/wallet/BtcDeterministicKeyChain.java rename to core/src/main/java/bisq/core/btc/setup/BtcDeterministicKeyChain.java index bbe17cb060..da704161f2 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcDeterministicKeyChain.java +++ b/core/src/main/java/bisq/core/btc/setup/BtcDeterministicKeyChain.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.setup; import org.bitcoinj.crypto.ChildNumber; import org.bitcoinj.crypto.DeterministicKey; diff --git a/core/src/main/java/bisq/core/btc/RegTestHost.java b/core/src/main/java/bisq/core/btc/setup/RegTestHost.java similarity index 96% rename from core/src/main/java/bisq/core/btc/RegTestHost.java rename to core/src/main/java/bisq/core/btc/setup/RegTestHost.java index d665033ca2..eeed58b6bf 100644 --- a/core/src/main/java/bisq/core/btc/RegTestHost.java +++ b/core/src/main/java/bisq/core/btc/setup/RegTestHost.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.setup; public enum RegTestHost { diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java similarity index 99% rename from core/src/main/java/bisq/core/btc/wallet/WalletConfig.java rename to core/src/main/java/bisq/core/btc/setup/WalletConfig.java index 3c05d94e03..6f160e8846 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -15,10 +15,11 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.setup; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.ProxySocketFactory; +import bisq.core.btc.nodes.ProxySocketFactory; +import bisq.core.btc.wallet.BisqRiskAnalysis; import bisq.common.app.Version; diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletsSetup.java b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java similarity index 94% rename from core/src/main/java/bisq/core/btc/wallet/WalletsSetup.java rename to core/src/main/java/bisq/core/btc/setup/WalletsSetup.java index 66bbfb22b5..819ed8994e 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletsSetup.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java @@ -15,15 +15,17 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.setup; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.AddressEntryList; -import bisq.core.btc.BitcoinNodes; -import bisq.core.btc.BitcoinNodes.BtcNode; import bisq.core.btc.BtcOptionKeys; -import bisq.core.btc.RegTestHost; +import bisq.core.btc.model.AddressEntry; +import bisq.core.btc.model.AddressEntryList; +import bisq.core.btc.nodes.BtcNetworkConfig; +import bisq.core.btc.nodes.BtcNodes; +import bisq.core.btc.nodes.BtcNodes.BtcNode; +import bisq.core.btc.nodes.BtcNodesRepository; +import bisq.core.btc.nodes.BtcNodesSetupPreferences; import bisq.core.user.Preferences; import bisq.network.Socks5MultiDiscovery; @@ -102,7 +104,7 @@ import static com.google.common.base.Preconditions.checkNotNull; @Slf4j public class WalletsSetup { // We reduce defaultConnections from 12 (PeerGroup.DEFAULT_CONNECTIONS) to 9 nodes - static final int DEFAULT_CONNECTIONS = 9; + public static final int DEFAULT_CONNECTIONS = 9; private static final long STARTUP_TIMEOUT = 180; private static final String BSQ_WALLET_FILE_NAME = "bisq_BSQ.wallet"; @@ -113,7 +115,7 @@ public class WalletsSetup { private final Preferences preferences; private final Socks5ProxyProvider socks5ProxyProvider; private final BisqEnvironment bisqEnvironment; - private final BitcoinNodes bitcoinNodes; + private final BtcNodes btcNodes; private final String btcWalletFileName; private final int numConnectionForBtc; private final String userAgent; @@ -138,7 +140,7 @@ public class WalletsSetup { Preferences preferences, Socks5ProxyProvider socks5ProxyProvider, BisqEnvironment bisqEnvironment, - BitcoinNodes bitcoinNodes, + BtcNodes btcNodes, @Named(BtcOptionKeys.USER_AGENT) String userAgent, @Named(BtcOptionKeys.WALLET_DIR) File appDir, @Named(BtcOptionKeys.USE_ALL_PROVIDED_NODES) String useAllProvidedNodes, @@ -149,7 +151,7 @@ public class WalletsSetup { this.preferences = preferences; this.socks5ProxyProvider = socks5ProxyProvider; this.bisqEnvironment = bisqEnvironment; - this.bitcoinNodes = bitcoinNodes; + this.btcNodes = btcNodes; this.numConnectionForBtc = numConnectionForBtc != null ? Integer.parseInt(numConnectionForBtc) : DEFAULT_CONNECTIONS; this.useAllProvidedNodes = "true".equals(useAllProvidedNodes); this.userAgent = userAgent; @@ -322,17 +324,17 @@ public class WalletsSetup { } private void configPeerNodes(@Nullable Socks5Proxy proxy) { - WalletSetupPreferences walletSetupPreferences = new WalletSetupPreferences(preferences); + BtcNodesSetupPreferences btcNodesSetupPreferences = new BtcNodesSetupPreferences(preferences); - List nodes = walletSetupPreferences.selectPreferredNodes(bitcoinNodes); - int minBroadcastConnections = walletSetupPreferences.calculateMinBroadcastConnections(nodes); + List nodes = btcNodesSetupPreferences.selectPreferredNodes(btcNodes); + int minBroadcastConnections = btcNodesSetupPreferences.calculateMinBroadcastConnections(nodes); walletConfig.setMinBroadcastConnections(minBroadcastConnections); - PeerAddressesRepository repository = new PeerAddressesRepository(nodes); - boolean isUseClearNodesWithProxies = (useAllProvidedNodes || walletSetupPreferences.isUseCustomNodes()); + BtcNodesRepository repository = new BtcNodesRepository(nodes); + boolean isUseClearNodesWithProxies = (useAllProvidedNodes || btcNodesSetupPreferences.isUseCustomNodes()); List peers = repository.getPeerAddresses(proxy, isUseClearNodesWithProxies); - WalletNetworkConfig networkConfig = new WalletNetworkConfig(walletConfig, params, socks5DiscoverMode, proxy); + BtcNetworkConfig networkConfig = new BtcNetworkConfig(walletConfig, params, socks5DiscoverMode, proxy); networkConfig.proposePeers(peers); } @@ -341,12 +343,12 @@ public class WalletsSetup { // Backup /////////////////////////////////////////////////////////////////////////////////////////// - void backupWallets() { + public void backupWallets() { FileUtil.rollingBackup(walletDir, btcWalletFileName, 20); FileUtil.rollingBackup(walletDir, BSQ_WALLET_FILE_NAME, 20); } - void clearBackups() { + public void clearBackups() { try { FileUtil.deleteDirectory(new File(Paths.get(walletDir.getAbsolutePath(), "backup").toString())); } catch (IOException e) { diff --git a/core/src/main/java/bisq/core/btc/wallet/BisqDefaultCoinSelector.java b/core/src/main/java/bisq/core/btc/wallet/BisqDefaultCoinSelector.java index 624d41d8c1..496c37ae43 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BisqDefaultCoinSelector.java +++ b/core/src/main/java/bisq/core/btc/wallet/BisqDefaultCoinSelector.java @@ -17,8 +17,6 @@ package bisq.core.btc.wallet; -import bisq.core.btc.Restrictions; - import org.bitcoinj.core.Coin; import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.NetworkParameters; diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqUtxoTransactionOutput.java b/core/src/main/java/bisq/core/btc/wallet/BsqUtxoTransactionOutput.java deleted file mode 100644 index d22eedf932..0000000000 --- a/core/src/main/java/bisq/core/btc/wallet/BsqUtxoTransactionOutput.java +++ /dev/null @@ -1,74 +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.wallet; - -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.TransactionOutput; -import org.bitcoinj.core.UTXO; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class BsqUtxoTransactionOutput extends TransactionOutput { - private static final Logger log = LoggerFactory.getLogger(BsqUtxoTransactionOutput.class); - private final UTXO output; - private final int chainHeight; - - /** - * Construct a free standing Transaction Output. - * - * @param params The network parameters. - * @param output The stored output (free standing). - */ - public BsqUtxoTransactionOutput(NetworkParameters params, UTXO output, int chainHeight) { - super(params, null, output.getValue(), output.getScript().getProgram()); - this.output = output; - this.chainHeight = chainHeight; - } - - /** - * Get the {@link UTXO}. - * - * @return The stored output. - */ - public UTXO getUTXO() { - return output; - } - - /** - * Get the depth withing the chain of the parent tx, depth is 1 if it the output height is the height of - * the latest block. - * - * @return The depth. - */ - @Override - public int getParentTransactionDepthInBlocks() { - return chainHeight - output.getHeight() + 1; - } - - @Override - public int getIndex() { - return (int) output.getIndex(); - } - - @Override - public Sha256Hash getParentTransactionHash() { - return output.getHash(); - } -} 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 93812d640f..3c223e2740 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java @@ -18,9 +18,11 @@ package bisq.core.btc.wallet; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.Restrictions; +import bisq.core.btc.exceptions.InsufficientBsqException; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; +import bisq.core.btc.listeners.BsqBalanceListener; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.dao.state.BsqStateListener; import bisq.core.dao.state.BsqStateService; import bisq.core.dao.state.blockchain.Block; 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 838f2c5bf4..b58901d8d7 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -17,13 +17,13 @@ package bisq.core.btc.wallet; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.AddressEntryException; -import bisq.core.btc.AddressEntryList; -import bisq.core.btc.InsufficientFundsException; -import bisq.core.btc.Restrictions; +import bisq.core.btc.exceptions.AddressEntryException; +import bisq.core.btc.exceptions.InsufficientFundsException; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; +import bisq.core.btc.model.AddressEntry; +import bisq.core.btc.model.AddressEntryList; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.provider.fee.FeeService; import bisq.core.user.Preferences; diff --git a/core/src/main/java/bisq/core/btc/Restrictions.java b/core/src/main/java/bisq/core/btc/wallet/Restrictions.java similarity index 99% rename from core/src/main/java/bisq/core/btc/Restrictions.java rename to core/src/main/java/bisq/core/btc/wallet/Restrictions.java index 7943804b27..8d55fadcd3 100644 --- a/core/src/main/java/bisq/core/btc/Restrictions.java +++ b/core/src/main/java/bisq/core/btc/wallet/Restrictions.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.wallet; import bisq.core.app.BisqEnvironment; 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 ee54276643..cc41d02195 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -18,13 +18,15 @@ package bisq.core.btc.wallet; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.data.InputsAndChangeOutput; -import bisq.core.btc.data.PreparedDepositTxAndMakerInputs; -import bisq.core.btc.data.RawTransactionInput; import bisq.core.btc.exceptions.SigningException; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; +import bisq.core.btc.model.AddressEntry; +import bisq.core.btc.model.InputsAndChangeOutput; +import bisq.core.btc.model.PreparedDepositTxAndMakerInputs; +import bisq.core.btc.model.RawTransactionInput; +import bisq.core.btc.setup.WalletConfig; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.locale.Res; import bisq.common.app.Log; 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 22da81121e..e39985d047 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TxBroadcaster.java +++ b/core/src/main/java/bisq/core/btc/wallet/TxBroadcaster.java @@ -17,6 +17,10 @@ package bisq.core.btc.wallet; +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; @@ -44,8 +48,7 @@ public class TxBroadcaster { default void onTimeout(TxBroadcastTimeoutException exception) { Transaction tx = exception.getLocalTx(); if (tx != null) { - String txId = tx.getHashAsString(); - log.warn("TxBroadcaster.onTimeout called: {}\n" + + 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" + @@ -68,7 +71,11 @@ public class TxBroadcaster { // 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. - exception.getWallet().maybeCommitTx(tx); + // 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); onSuccess(tx); } else { @@ -85,7 +92,7 @@ public class TxBroadcaster { void onFailure(TxBroadcastException exception); } - private static final int DEFAULT_BROADCAST_TIMEOUT = 20; + private static final int DEFAULT_BROADCAST_TIMEOUT = 30; private static Map broadcastTimerMap = new HashMap<>(); public static void broadcastTx(Wallet wallet, PeerGroup peerGroup, Transaction localTx, Callback callback) { 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 fb31bdda01..97a6d85bcf 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -23,6 +23,7 @@ import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.listeners.AddressConfidenceListener; import bisq.core.btc.listeners.BalanceListener; import bisq.core.btc.listeners.TxConfidenceListener; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.provider.fee.FeeService; import bisq.core.user.Preferences; diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java b/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java index 87f000d7f2..a6c0511ca5 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java @@ -18,6 +18,7 @@ package bisq.core.btc.wallet; import bisq.core.app.BisqEnvironment; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.crypto.ScryptUtil; import bisq.core.locale.Res; diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java index 3bbbf1255c..4ad6817f79 100644 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ b/core/src/main/java/bisq/core/dao/DaoFacade.java @@ -37,9 +37,12 @@ import bisq.core.dao.governance.proposal.ProposalConsensus; import bisq.core.dao.governance.proposal.ProposalListPresentation; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; +import bisq.core.dao.governance.proposal.compensation.CompensationConsensus; import bisq.core.dao.governance.proposal.compensation.CompensationProposalService; import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposalService; +import bisq.core.dao.governance.proposal.generic.GenericProposalService; import bisq.core.dao.governance.proposal.param.ChangeParamProposalService; +import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposalService; import bisq.core.dao.governance.proposal.role.BondedRoleProposalService; import bisq.core.dao.governance.role.BondedRole; import bisq.core.dao.governance.role.BondedRolesService; @@ -78,11 +81,15 @@ import java.util.Set; import javax.annotation.Nullable; + + +import bisq.asset.Asset; + /** * Provides a facade to interact with the Dao domain. Hides complexity and domain details to clients (e.g. UI or APIs) * by providing a reduced API and/or aggregating subroutines. */ -public class DaoFacade { +public class DaoFacade implements DaoSetupService { private final ProposalListPresentation proposalListPresentation; private final BallotListService ballotListService; private final BallotListPresentation ballotListPresentation; @@ -95,10 +102,11 @@ public class DaoFacade { private final ChangeParamProposalService changeParamProposalService; private final ConfiscateBondProposalService confiscateBondProposalService; private final BondedRoleProposalService bondedRoleProposalService; + private final GenericProposalService genericProposalService; + private final RemoveAssetProposalService removeAssetProposalService; private final BondedRolesService bondedRolesService; private final LockupService lockupService; private final UnlockService unlockService; - private final ProposalConsensus proposalConsensus; private final ObjectProperty phaseProperty = new SimpleObjectProperty<>(DaoPhase.Phase.UNDEFINED); @@ -115,10 +123,11 @@ public class DaoFacade { ChangeParamProposalService changeParamProposalService, ConfiscateBondProposalService confiscateBondProposalService, BondedRoleProposalService bondedRoleProposalService, + GenericProposalService genericProposalService, + RemoveAssetProposalService removeAssetProposalService, BondedRolesService bondedRolesService, LockupService lockupService, - UnlockService unlockService, - ProposalConsensus proposalConsensus) { + UnlockService unlockService) { this.proposalListPresentation = proposalListPresentation; this.ballotListService = ballotListService; this.ballotListPresentation = ballotListPresentation; @@ -131,11 +140,24 @@ public class DaoFacade { this.changeParamProposalService = changeParamProposalService; this.confiscateBondProposalService = confiscateBondProposalService; this.bondedRoleProposalService = bondedRoleProposalService; + this.genericProposalService = genericProposalService; + this.removeAssetProposalService = removeAssetProposalService; this.bondedRolesService = bondedRolesService; this.lockupService = lockupService; this.unlockService = unlockService; - this.proposalConsensus = proposalConsensus; + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // DaoSetupService + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void addListeners() { + } + + @Override + public void start() { bsqStateService.addBsqStateListener(new BsqStateListener() { @Override public void onNewBlockHeight(int blockHeight) { @@ -153,6 +175,7 @@ public class DaoFacade { }); } + public void addBsqStateListener(BsqStateListener listener) { bsqStateService.addBsqStateListener(listener); } @@ -219,13 +242,26 @@ public class DaoFacade { return bondedRoleProposalService.createProposalWithTransaction(bondedRole); } + public ProposalWithTransaction getGenericProposalWithTransaction(String name, + String link) + throws ValidationException, InsufficientMoneyException, TxException { + return genericProposalService.createProposalWithTransaction(name, link); + } + + public ProposalWithTransaction getRemoveAssetProposalWithTransaction(String name, + String link, + Asset asset) + throws ValidationException, InsufficientMoneyException, TxException { + return removeAssetProposalService.createProposalWithTransaction(name, link, asset); + } + public List getBondedRoleList() { return bondedRolesService.getBondedRoleList(); } // Show fee public Coin getProposalFee(int chainHeight) { - return proposalConsensus.getFee(bsqStateService, chainHeight); + return ProposalConsensus.getFee(bsqStateService, chainHeight); } // Publish proposal tx, proposal payload and and persist it to myProposalList @@ -451,16 +487,16 @@ public class DaoFacade { return periodService.isInPhaseButNotLastBlock(phase); } - public boolean isTxInCorrectCycle(int txHeight, int chainHeadHeight) { - return periodService.isTxInCorrectCycle(txHeight, chainHeadHeight); + public boolean isTxInCorrectCycle(int txHeight, int chainHeight) { + return periodService.isTxInCorrectCycle(txHeight, chainHeight); } - public boolean isTxInCorrectCycle(String txId, int chainHeadHeight) { - return periodService.isTxInCorrectCycle(txId, chainHeadHeight); + public boolean isTxInCorrectCycle(String txId, int chainHeight) { + return periodService.isTxInCorrectCycle(txId, chainHeight); } - public boolean isTxInPhaseAndCycle(String txId, DaoPhase.Phase phase, int chainHeadHeight) { - return periodService.isTxInPhaseAndCycle(txId, phase, chainHeadHeight); + public boolean isTxInPhaseAndCycle(String txId, DaoPhase.Phase phase, int chainHeight) { + return periodService.isTxInPhaseAndCycle(txId, phase, chainHeight); } public boolean isUnspent(TxOutputKey key) { @@ -474,4 +510,16 @@ public class DaoFacade { public boolean isUnlocking(BondedRole bondedRole) { return bsqStateService.isUnlocking(bondedRole); } + + public Coin getMinCompensationRequestAmount() { + return CompensationConsensus.getMinCompensationRequestAmount(bsqStateService, periodService.getChainHeight()); + } + + public Coin getMaxCompensationRequestAmount() { + return CompensationConsensus.getMaxCompensationRequestAmount(bsqStateService, periodService.getChainHeight()); + } + + public long getPramValue(Param param) { + return bsqStateService.getParamValue(param, periodService.getChainHeight()); + } } diff --git a/core/src/main/java/bisq/core/dao/DaoModule.java b/core/src/main/java/bisq/core/dao/DaoModule.java index 293a609630..cd7286a46c 100644 --- a/core/src/main/java/bisq/core/dao/DaoModule.java +++ b/core/src/main/java/bisq/core/dao/DaoModule.java @@ -19,26 +19,30 @@ package bisq.core.dao; import bisq.core.dao.bonding.lockup.LockupService; import bisq.core.dao.bonding.unlock.UnlockService; +import bisq.core.dao.governance.asset.AssetService; import bisq.core.dao.governance.ballot.BallotListPresentation; import bisq.core.dao.governance.ballot.BallotListService; import bisq.core.dao.governance.blindvote.BlindVoteListService; import bisq.core.dao.governance.blindvote.BlindVoteValidator; import bisq.core.dao.governance.blindvote.MyBlindVoteListService; +import bisq.core.dao.governance.blindvote.network.RepublishGovernanceDataHandler; import bisq.core.dao.governance.blindvote.storage.BlindVoteStorageService; import bisq.core.dao.governance.blindvote.storage.BlindVoteStore; import bisq.core.dao.governance.myvote.MyVoteListService; 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.ProposalValidator; -import bisq.core.dao.governance.proposal.compensation.CompensationConsensus; import bisq.core.dao.governance.proposal.compensation.CompensationProposalService; import bisq.core.dao.governance.proposal.compensation.CompensationValidator; import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposalService; import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondValidator; +import bisq.core.dao.governance.proposal.generic.GenericProposalService; +import bisq.core.dao.governance.proposal.generic.GenericProposalValidator; import bisq.core.dao.governance.proposal.param.ChangeParamProposalService; import bisq.core.dao.governance.proposal.param.ChangeParamValidator; +import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposalService; +import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetValidator; import bisq.core.dao.governance.proposal.role.BondedRoleProposalService; import bisq.core.dao.governance.proposal.role.BondedRoleValidator; import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStorageService; @@ -46,6 +50,7 @@ import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStore; import bisq.core.dao.governance.proposal.storage.temp.TempProposalStorageService; import bisq.core.dao.governance.proposal.storage.temp.TempProposalStore; import bisq.core.dao.governance.role.BondedRolesService; +import bisq.core.dao.governance.voteresult.MissingDataRequestService; import bisq.core.dao.governance.voteresult.VoteResultService; import bisq.core.dao.governance.voteresult.issuance.IssuanceService; import bisq.core.dao.governance.votereveal.VoteRevealService; @@ -53,7 +58,7 @@ import bisq.core.dao.node.BsqNodeProvider; import bisq.core.dao.node.full.FullNode; import bisq.core.dao.node.full.RpcService; import bisq.core.dao.node.full.network.FullNodeNetworkService; -import bisq.core.dao.node.json.JsonBlockChainExporter; +import bisq.core.dao.node.json.ExportJsonFilesService; import bisq.core.dao.node.lite.LiteNode; import bisq.core.dao.node.lite.network.LiteNodeNetworkService; import bisq.core.dao.node.parser.BlockParser; @@ -99,7 +104,7 @@ public class DaoModule extends AppModule { bind(BsqState.class).in(Singleton.class); bind(BsqStateService.class).in(Singleton.class); bind(SnapshotManager.class).in(Singleton.class); - bind(JsonBlockChainExporter.class).in(Singleton.class); + bind(ExportJsonFilesService.class).in(Singleton.class); // Period bind(CycleService.class).in(Singleton.class); @@ -109,7 +114,6 @@ public class DaoModule extends AppModule { bind(TxParser.class).in(Singleton.class); // Proposal - bind(ProposalConsensus.class).in(Singleton.class); bind(ProposalService.class).in(Singleton.class); bind(MyProposalListService.class).in(Singleton.class); @@ -122,7 +126,6 @@ public class DaoModule extends AppModule { bind(ProposalValidator.class).in(Singleton.class); bind(CompensationValidator.class).in(Singleton.class); - bind(CompensationConsensus.class).in(Singleton.class); bind(CompensationProposalService.class).in(Singleton.class); bind(ChangeParamValidator.class).in(Singleton.class); @@ -134,6 +137,12 @@ public class DaoModule extends AppModule { bind(ConfiscateBondValidator.class).in(Singleton.class); bind(ConfiscateBondProposalService.class).in(Singleton.class); + bind(GenericProposalValidator.class).in(Singleton.class); + bind(GenericProposalService.class).in(Singleton.class); + + bind(RemoveAssetValidator.class).in(Singleton.class); + bind(RemoveAssetProposalService.class).in(Singleton.class); + // Ballot bind(BallotListService.class).in(Singleton.class); @@ -154,13 +163,15 @@ public class DaoModule extends AppModule { // VoteResult bind(VoteResultService.class).in(Singleton.class); + bind(MissingDataRequestService.class).in(Singleton.class); bind(IssuanceService.class).in(Singleton.class); + bind(RepublishGovernanceDataHandler.class).in(Singleton.class); // Genesis - String genesisTxId = environment.getProperty(DaoOptionKeys.GENESIS_TX_ID, String.class, null); + String genesisTxId = environment.getProperty(DaoOptionKeys.GENESIS_TX_ID, String.class, ""); bind(String.class).annotatedWith(Names.named(DaoOptionKeys.GENESIS_TX_ID)).toInstance(genesisTxId); - Integer genesisBlockHeight = environment.getProperty(DaoOptionKeys.GENESIS_BLOCK_HEIGHT, Integer.class, 0); + Integer genesisBlockHeight = environment.getProperty(DaoOptionKeys.GENESIS_BLOCK_HEIGHT, Integer.class, -1); bind(Integer.class).annotatedWith(Names.named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT)).toInstance(genesisBlockHeight); // Bonds @@ -168,6 +179,9 @@ public class DaoModule extends AppModule { bind(UnlockService.class).in(Singleton.class); bind(BondedRolesService.class).in(Singleton.class); + // Asset + bind(AssetService.class).in(Singleton.class); + // Options bindConstant().annotatedWith(named(DaoOptionKeys.RPC_USER)).to(environment.getRequiredProperty(DaoOptionKeys.RPC_USER)); bindConstant().annotatedWith(named(DaoOptionKeys.RPC_PASSWORD)).to(environment.getRequiredProperty(DaoOptionKeys.RPC_PASSWORD)); diff --git a/core/src/main/java/bisq/core/dao/DaoSetup.java b/core/src/main/java/bisq/core/dao/DaoSetup.java index f09246988d..02a1214f7d 100644 --- a/core/src/main/java/bisq/core/dao/DaoSetup.java +++ b/core/src/main/java/bisq/core/dao/DaoSetup.java @@ -21,10 +21,12 @@ import bisq.core.dao.governance.ballot.BallotListService; import bisq.core.dao.governance.blindvote.BlindVoteListService; import bisq.core.dao.governance.blindvote.MyBlindVoteListService; import bisq.core.dao.governance.proposal.ProposalService; +import bisq.core.dao.governance.voteresult.MissingDataRequestService; import bisq.core.dao.governance.voteresult.VoteResultService; import bisq.core.dao.governance.votereveal.VoteRevealService; import bisq.core.dao.node.BsqNode; import bisq.core.dao.node.BsqNodeProvider; +import bisq.core.dao.node.json.ExportJsonFilesService; import bisq.core.dao.state.BsqStateService; import bisq.core.dao.state.period.CycleService; @@ -35,7 +37,6 @@ import com.google.inject.Inject; /** * High level entry point for Dao domain. * We initialize all main service classes here to be sure they are started. - * */ public class DaoSetup { private final BsqStateService bsqStateService; @@ -47,6 +48,9 @@ public class DaoSetup { private final VoteRevealService voteRevealService; private final VoteResultService voteResultService; private final BsqNode bsqNode; + private final MissingDataRequestService missingDataRequestService; + private final DaoFacade daoFacade; + private final ExportJsonFilesService exportJsonFilesService; @Inject public DaoSetup(BsqNodeProvider bsqNodeProvider, @@ -57,7 +61,10 @@ public class DaoSetup { BlindVoteListService blindVoteListService, MyBlindVoteListService myBlindVoteListService, VoteRevealService voteRevealService, - VoteResultService voteResultService) { + VoteResultService voteResultService, + MissingDataRequestService missingDataRequestService, + DaoFacade daoFacade, + ExportJsonFilesService exportJsonFilesService) { this.bsqStateService = bsqStateService; this.cycleService = cycleService; this.proposalService = proposalService; @@ -66,6 +73,9 @@ public class DaoSetup { this.myBlindVoteListService = myBlindVoteListService; this.voteRevealService = voteRevealService; this.voteResultService = voteResultService; + this.missingDataRequestService = missingDataRequestService; + this.daoFacade = daoFacade; + this.exportJsonFilesService = exportJsonFilesService; bsqNode = bsqNodeProvider.getBsqNode(); } @@ -81,6 +91,9 @@ public class DaoSetup { myBlindVoteListService.addListeners(); voteRevealService.addListeners(); voteResultService.addListeners(); + missingDataRequestService.addListeners(); + daoFacade.addListeners(); + exportJsonFilesService.addListeners(); bsqStateService.start(); cycleService.start(); @@ -90,6 +103,9 @@ public class DaoSetup { myBlindVoteListService.start(); voteRevealService.start(); voteResultService.start(); + missingDataRequestService.start(); + daoFacade.start(); + exportJsonFilesService.start(); bsqNode.setErrorMessageHandler(errorMessageHandler); bsqNode.start(); diff --git a/core/src/main/java/bisq/core/dao/bonding/lockup/LockupService.java b/core/src/main/java/bisq/core/dao/bonding/lockup/LockupService.java index 8187b5b91a..fdc93760c7 100644 --- a/core/src/main/java/bisq/core/dao/bonding/lockup/LockupService.java +++ b/core/src/main/java/bisq/core/dao/bonding/lockup/LockupService.java @@ -18,12 +18,12 @@ package bisq.core.dao.bonding.lockup; import bisq.core.btc.exceptions.TransactionVerificationException; +import bisq.core.btc.exceptions.TxBroadcastException; +import bisq.core.btc.exceptions.TxMalleabilityException; import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcastException; import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.TxMalleabilityException; import bisq.core.btc.wallet.WalletsManager; import bisq.core.dao.bonding.BondingConsensus; import bisq.core.dao.governance.role.BondedRole; diff --git a/core/src/main/java/bisq/core/dao/bonding/unlock/UnlockService.java b/core/src/main/java/bisq/core/dao/bonding/unlock/UnlockService.java index 1a8ae7d78c..dc18aa163a 100644 --- a/core/src/main/java/bisq/core/dao/bonding/unlock/UnlockService.java +++ b/core/src/main/java/bisq/core/dao/bonding/unlock/UnlockService.java @@ -18,12 +18,12 @@ package bisq.core.dao.bonding.unlock; import bisq.core.btc.exceptions.TransactionVerificationException; +import bisq.core.btc.exceptions.TxBroadcastException; +import bisq.core.btc.exceptions.TxMalleabilityException; import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcastException; import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.TxMalleabilityException; import bisq.core.btc.wallet.WalletsManager; import bisq.core.dao.state.BsqStateService; import bisq.core.dao.state.blockchain.TxOutput; diff --git a/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java b/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java new file mode 100644 index 0000000000..26eca88e1a --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java @@ -0,0 +1,120 @@ +/* + * 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.asset; + +import bisq.core.app.BisqEnvironment; +import bisq.core.dao.state.BsqStateService; +import bisq.core.locale.CurrencyUtil; + +import bisq.common.proto.persistable.PersistedDataHost; +import bisq.common.storage.Storage; + +import javax.inject.Inject; + +import java.util.List; +import java.util.stream.Collectors; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class AssetService implements PersistedDataHost { + + + private final Storage storage; + private final BsqStateService bsqStateService; + @Getter + private final RemovedAssetsList removedAssetsList = new RemovedAssetsList(); + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + + @Inject + public AssetService(Storage storage, BsqStateService bsqStateService) { + this.storage = storage; + this.bsqStateService = bsqStateService; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PersistedDataHost + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void readPersisted() { + if (BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq()) { + RemovedAssetsList persisted = storage.initAndGetPersisted(removedAssetsList, 100); + if (persisted != null) { + removedAssetsList.clear(); + removedAssetsList.addAll(persisted.getList()); + } + } + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + public void addToRemovedAssetsListByVoting(String tickerSymbol) { + log.info("Asset '{}' was removed by DAO voting", CurrencyUtil.getNameAndCode(tickerSymbol)); + removedAssetsList.add(new RemovedAsset(tickerSymbol, RemoveReason.VOTING)); + persist(); + } + + public boolean hasPaidBSQFee(String tickerSymbol) { + //TODO + return false; + } + + + public boolean isAssetRemoved(String tickerSymbol) { + boolean isRemoved = removedAssetsList.getList().stream() + .anyMatch(removedAsset -> removedAsset.getTickerSymbol().equals(tickerSymbol)); + if (isRemoved) + log.info("Asset '{}' was removed", CurrencyUtil.getNameAndCode(tickerSymbol)); + + return isRemoved; + } + + public boolean isAssetRemovedByVoting1(String tickerSymbol) { + boolean isRemoved = getRemovedAssetsByRemoveReason(RemoveReason.VOTING).stream() + .anyMatch(removedAsset -> removedAsset.getTickerSymbol().equals(tickerSymbol)); + if (isRemoved) + log.info("Asset '{}' was removed by DAO voting", CurrencyUtil.getNameAndCode(tickerSymbol)); + + return isRemoved; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private List getRemovedAssetsByRemoveReason(RemoveReason removeReason) { + return removedAssetsList.getList().stream() + .filter(e -> e.getRemoveReason() == removeReason) + .collect(Collectors.toList()); + } + + private void persist() { + storage.queueUpForSave(20); + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/asset/RemoveReason.java b/core/src/main/java/bisq/core/dao/governance/asset/RemoveReason.java new file mode 100644 index 0000000000..81635722b1 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/asset/RemoveReason.java @@ -0,0 +1,23 @@ +/* + * 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.asset; + +public enum RemoveReason { + VOTING, + INACTIVITY +} diff --git a/core/src/main/java/bisq/core/dao/governance/asset/RemovedAsset.java b/core/src/main/java/bisq/core/dao/governance/asset/RemovedAsset.java new file mode 100644 index 0000000000..908b2adb08 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/asset/RemovedAsset.java @@ -0,0 +1,53 @@ +/* + * 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.asset; + +import bisq.common.proto.ProtoUtil; +import bisq.common.proto.persistable.PersistablePayload; + +import io.bisq.generated.protobuffer.PB; + +import lombok.Value; + +@Value +public class RemovedAsset implements PersistablePayload { + private final String tickerSymbol; + private final RemoveReason removeReason; + + public RemovedAsset(String tickerSymbol, RemoveReason removeReason) { + this.tickerSymbol = tickerSymbol; + this.removeReason = removeReason; + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public PB.RemovedAsset toProtoMessage() { + PB.RemovedAsset.Builder builder = PB.RemovedAsset.newBuilder() + .setTickerSymbol(tickerSymbol) + .setRemoveReason(removeReason.name()); + return builder.build(); + } + + public static RemovedAsset fromProto(PB.RemovedAsset proto) { + return new RemovedAsset(proto.getTickerSymbol(), + ProtoUtil.enumFromProto(RemoveReason.class, proto.getRemoveReason())); + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/asset/RemovedAssetsList.java b/core/src/main/java/bisq/core/dao/governance/asset/RemovedAssetsList.java new file mode 100644 index 0000000000..9c655886ce --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/asset/RemovedAssetsList.java @@ -0,0 +1,75 @@ +/* + * 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.asset; + +import bisq.core.dao.governance.ConsensusCritical; + +import bisq.common.proto.persistable.PersistableList; + +import io.bisq.generated.protobuffer.PB; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import lombok.EqualsAndHashCode; + +/** + * PersistableEnvelope wrapper for list of removedAssets. + */ +@EqualsAndHashCode(callSuper = true) +public class RemovedAssetsList extends PersistableList implements ConsensusCritical { + + public RemovedAssetsList(List list) { + super(list); + } + + public RemovedAssetsList() { + super(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public PB.PersistableEnvelope toProtoMessage() { + return PB.PersistableEnvelope.newBuilder().setRemovedAssetList(getBuilder()).build(); + } + + public PB.RemovedAssetList.Builder getBuilder() { + return PB.RemovedAssetList.newBuilder() + .addAllRemovedAsset(getList().stream() + .map(RemovedAsset::toProtoMessage) + .collect(Collectors.toList())); + } + + public static RemovedAssetsList fromProto(PB.RemovedAssetList proto) { + return new RemovedAssetsList(new ArrayList<>(proto.getRemovedAssetList().stream() + .map(RemovedAsset::fromProto) + .collect(Collectors.toList()))); + } + + @Override + public String toString() { + return "List of tickerSymbols in RemovedAssetList: " + getList().stream() + .map(RemovedAsset::getTickerSymbol) + .collect(Collectors.toList()); + } +} 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 bd0278688d..809af63ec8 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 @@ -75,13 +75,13 @@ public class BallotListPresentation implements BallotListService.BallotListChang @Override public void onParseTxsComplete(Block block) { - onListChanged(ballotListService.getBallotList().getList()); + onListChanged(ballotListService.getValidatedBallotList()); } @Override public void onParseBlockChainComplete() { // As we update the list in ProposalService.onParseBlockChainComplete we need to update here as well. - onListChanged(ballotListService.getBallotList().getList()); + onListChanged(ballotListService.getValidatedBallotList()); } 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 8066c94827..e0081c0f38 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 @@ -21,7 +21,9 @@ import bisq.core.app.BisqEnvironment; import bisq.core.dao.DaoSetupService; import bisq.core.dao.governance.ballot.vote.Vote; import bisq.core.dao.governance.proposal.ProposalService; +import bisq.core.dao.governance.proposal.ProposalValidator; import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; +import bisq.core.dao.state.period.PeriodService; import bisq.common.proto.persistable.PersistedDataHost; import bisq.common.storage.Storage; @@ -32,6 +34,7 @@ import javafx.collections.ListChangeListener; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -49,14 +52,19 @@ public class BallotListService implements PersistedDataHost, DaoSetupService { } private final ProposalService proposalService; + private final PeriodService periodService; + private final ProposalValidator proposalValidator; private final Storage storage; private final BallotList ballotList = new BallotList(); private final List listeners = new CopyOnWriteArrayList<>(); @Inject - public BallotListService(ProposalService proposalService, Storage storage) { + public BallotListService(ProposalService proposalService, PeriodService periodService, + ProposalValidator proposalValidator, Storage storage) { this.proposalService = proposalService; + this.periodService = periodService; + this.proposalValidator = proposalValidator; this.storage = storage; } @@ -121,8 +129,17 @@ public class BallotListService implements PersistedDataHost, DaoSetupService { listeners.add(listener); } - public BallotList getBallotList() { - return ballotList; + public List getValidatedBallotList() { + return ballotList.stream() + .filter(ballot -> proposalValidator.isTxTypeValid(ballot.getProposal())) + .collect(Collectors.toList()); + } + + public List getValidBallotsOfCycle() { + return ballotList.stream() + .filter(ballot -> proposalValidator.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/BlindVoteConsensus.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java index 1f419a7943..812febe430 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java @@ -56,7 +56,7 @@ public class BlindVoteConsensus { } public static BallotList getSortedBallotList(BallotListService ballotListService) { - List ballotList = ballotListService.getBallotList().stream() + List ballotList = ballotListService.getValidBallotsOfCycle().stream() .sorted(Comparator.comparing(Ballot::getTxId)) .collect(Collectors.toList()); log.info("Sorted ballotList: " + ballotList); @@ -115,8 +115,8 @@ public class BlindVoteConsensus { } } - public static Coin getFee(BsqStateService bsqStateService, int chainHeadHeight) { - Coin fee = Coin.valueOf(bsqStateService.getParamValue(Param.BLIND_VOTE_FEE, chainHeadHeight)); + public static Coin getFee(BsqStateService bsqStateService, int chainHeight) { + Coin fee = Coin.valueOf(bsqStateService.getParamValue(Param.BLIND_VOTE_FEE, chainHeight)); log.info("Fee for blind vote: " + fee); return fee; } 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 5596a59756..676f239a46 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 @@ -39,6 +39,7 @@ import javafx.collections.ObservableList; import java.util.List; import java.util.stream.Collectors; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** @@ -49,8 +50,8 @@ public class BlindVoteListService implements AppendOnlyDataStoreListener, BsqSta private final BsqStateService bsqStateService; private final P2PService p2PService; private final BlindVoteValidator blindVoteValidator; - - private final ObservableList appendOnlyStoreList = FXCollections.observableArrayList(); + @Getter + private final ObservableList blindVotePayloads = FXCollections.observableArrayList(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -122,7 +123,7 @@ public class BlindVoteListService implements AppendOnlyDataStoreListener, BsqSta /////////////////////////////////////////////////////////////////////////////////////////// public List getBlindVotesInPhaseAndCycle() { - return appendOnlyStoreList.stream() + return blindVotePayloads.stream() .filter(blindVotePayload -> blindVoteValidator.isTxInPhaseAndCycle(blindVotePayload.getBlindVote())) .map(BlindVotePayload::getBlindVote) .collect(Collectors.toList()); @@ -140,14 +141,14 @@ public class BlindVoteListService implements AppendOnlyDataStoreListener, BsqSta private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload) { if (persistableNetworkPayload instanceof BlindVotePayload) { BlindVotePayload blindVotePayload = (BlindVotePayload) persistableNetworkPayload; - if (!appendOnlyStoreList.contains(blindVotePayload)) { + if (!blindVotePayloads.contains(blindVotePayload)) { BlindVote blindVote = blindVotePayload.getBlindVote(); String txId = blindVote.getTxId(); // We don't check the phase and the cycle as we want to add all object independently when we receive it // (or when we start the app to fill our list from the data we gor from the seed node). if (blindVoteValidator.areDataFieldsValid(blindVote)) { // We don't validate as we might receive blindVotes from other cycles or phases at startup. - appendOnlyStoreList.add(blindVotePayload); + blindVotePayloads.add(blindVotePayload); 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 99956130ac..0773d829df 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 @@ -89,13 +89,18 @@ public class BlindVoteValidator { public boolean isTxInPhaseAndCycle(BlindVote blindVote) { String txId = blindVote.getTxId(); Optional optionalTx = bsqStateService.getTx(txId); + if (!optionalTx.isPresent()) { + log.debug("Tx is not in bsqStateService. blindVoteTxId={}", txId); + return false; + } + int txHeight = optionalTx.get().getBlockHeight(); if (!periodService.isTxInCorrectCycle(txHeight, bsqStateService.getChainHeight())) { log.debug("Tx is not in current cycle. blindVote={}", blindVote); return false; } if (!periodService.isTxInPhase(txId, DaoPhase.Phase.BLIND_VOTE)) { - log.warn("Tx is not in BLIND_VOTE phase. blindVote={}", blindVote); + log.debug("Tx is not in BLIND_VOTE phase. blindVote={}", blindVote); return false; } return true; 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 e62c7f49f5..9c518d1e61 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 @@ -19,12 +19,12 @@ package bisq.core.dao.governance.blindvote; import bisq.core.app.BisqEnvironment; import bisq.core.btc.exceptions.TransactionVerificationException; +import bisq.core.btc.exceptions.TxBroadcastException; +import bisq.core.btc.exceptions.TxMalleabilityException; import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcastException; import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.TxMalleabilityException; import bisq.core.btc.wallet.WalletsManager; import bisq.core.dao.DaoSetupService; import bisq.core.dao.exceptions.PublishToP2PNetworkException; @@ -271,10 +271,11 @@ public class MyBlindVoteListService implements PersistedDataHost, BsqStateListen // blindVoteTxId is null if we use the method from the getCurrentlyAvailableMerit call. public MeritList getMerits(@Nullable String blindVoteTxId) { - // Create a lookup set for txIds of own comp. requests + // Create a lookup set for txIds of own comp. requests from past cycles (we ignore request form that cycle) Set myCompensationProposalTxIs = myProposalListService.getList().stream() .filter(proposal -> proposal instanceof CompensationProposal) .map(Proposal::getTxId) + .filter(txId -> periodService.isTxInPastCycle(txId, periodService.getChainHeight())) .collect(Collectors.toSet()); return new MeritList(bsqStateService.getIssuanceSet().stream() 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 new file mode 100644 index 0000000000..75e34d2bbc --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java @@ -0,0 +1,214 @@ +/* + * 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.blindvote.network; + +import bisq.core.dao.governance.blindvote.network.messages.RepublishGovernanceDataRequest; + +import bisq.network.p2p.NodeAddress; +import bisq.network.p2p.network.Connection; +import bisq.network.p2p.network.NetworkNode; +import bisq.network.p2p.peers.PeerManager; +import bisq.network.p2p.peers.peerexchange.Peer; +import bisq.network.p2p.seed.SeedNodeRepository; + +import bisq.common.Timer; +import bisq.common.UserThread; +import bisq.common.app.Capabilities; + +import javax.inject.Inject; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.SettableFuture; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import lombok.extern.slf4j.Slf4j; + +import org.jetbrains.annotations.NotNull; + +/** + * Responsible for sending a RepublishGovernanceDataRequest to full nodes. + * Processing of RepublishBlindVotesRequests at full nodes is done in the FullNodeNetworkService. + */ +@Slf4j +public final class RepublishGovernanceDataHandler { + private static final long TIMEOUT = 120; + + private final Collection seedNodeAddresses; + private final NetworkNode networkNode; + private final PeerManager peerManager; + + private boolean stopped; + private Timer timeoutTimer; + + @Inject + public RepublishGovernanceDataHandler(NetworkNode networkNode, + PeerManager peerManager, + SeedNodeRepository seedNodesRepository) { + this.networkNode = networkNode; + this.peerManager = peerManager; + this.seedNodeAddresses = new HashSet<>(seedNodesRepository.getSeedNodeAddresses()); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + public void sendRepublishRequest() { + // First try if we have a seed node in our connections. All seed nodes are full nodes. + if (!stopped) + connectToNextNode(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private void sendRepublishRequest(NodeAddress nodeAddress) { + RepublishGovernanceDataRequest republishGovernanceDataRequest = new RepublishGovernanceDataRequest(); + if (timeoutTimer == null) { + timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions + if (!stopped) { + String errorMessage = "A timeout occurred at sending republishGovernanceDataRequest:" + + " to nodeAddress:" + nodeAddress; + log.warn(errorMessage); + connectToNextNode(); + } else { + log.warn("We have stopped already. We ignore that timeoutTimer.run call. " + + "Might be caused by an previous networkNode.sendMessage.onFailure."); + } + }, + TIMEOUT); + } + + log.info("We send to peer {} a republishGovernanceDataRequest.", nodeAddress); + SettableFuture future = networkNode.sendMessage(nodeAddress, republishGovernanceDataRequest); + Futures.addCallback(future, new FutureCallback<>() { + @Override + public void onSuccess(Connection connection) { + if (!stopped) { + log.info("Sending of RepublishGovernanceDataRequest message to peer {} succeeded.", nodeAddress.getFullAddress()); + stop(); + } else { + log.trace("We have stopped already. We ignore that networkNode.sendMessage.onSuccess call." + + "Might be caused by an previous timeout."); + } + } + + @Override + public void onFailure(@NotNull Throwable throwable) { + if (!stopped) { + String errorMessage = "Sending republishGovernanceDataRequest to " + nodeAddress + + " failed. That is expected if the peer is offline.\n\t" + + "\n\tException=" + throwable.getMessage(); + log.info(errorMessage); + handleFault(nodeAddress); + connectToNextNode(); + } else { + log.trace("We have stopped already. We ignore that networkNode.sendMessage.onFailure call. " + + "Might be caused by an previous timeout."); + } + } + }); + } + + private void connectToNextNode() { + // First we try our connected seed nodes + Optional connectionToSeedNodeOptional = networkNode.getConfirmedConnections().stream() + .filter(peerManager::isSeedNode) + .findAny(); + if (connectionToSeedNodeOptional.isPresent() && + connectionToSeedNodeOptional.get().getPeersNodeAddressOptional().isPresent()) { + NodeAddress nodeAddress = connectionToSeedNodeOptional.get().getPeersNodeAddressOptional().get(); + sendRepublishRequest(nodeAddress); + } else { + // If connected seed nodes did not confirm receipt of message we try next seed node from seedNodeAddresses + List list = seedNodeAddresses.stream() + .filter(e -> peerManager.isSeedNode(e) && !peerManager.isSelf(e)) + .collect(Collectors.toList()); + Collections.shuffle(list); + + if (!list.isEmpty()) { + NodeAddress nodeAddress = list.get(0); + seedNodeAddresses.remove(nodeAddress); + sendRepublishRequest(nodeAddress); + } else { + log.warn("No more seed nodes available. We try any of our other peers."); + connectToAnyFullNode(); + } + } + } + + private void connectToAnyFullNode() { + List required = new ArrayList<>(Collections.singletonList( + Capabilities.Capability.DAO_FULL_NODE.ordinal() + )); + + List list = peerManager.getLivePeers(null).stream() + .filter(peer -> Capabilities.isCapabilitySupported(required, peer.getSupportedCapabilities())) + .collect(Collectors.toList()); + + if (list.isEmpty()) + list = peerManager.getReportedPeers().stream() + .filter(peer -> Capabilities.isCapabilitySupported(required, peer.getSupportedCapabilities())) + .collect(Collectors.toList()); + + if (list.isEmpty()) + list = peerManager.getPersistedPeers().stream() + .filter(peer -> Capabilities.isCapabilitySupported(required, peer.getSupportedCapabilities())) + .collect(Collectors.toList()); + + if (!list.isEmpty()) { + // We avoid the complexity to maintain the results of all our peers and to iterate all until we find a good peer, + // but we prefer simplicity with the risk that we don't get the data so we request from max 4 peers in parallel + // assuming that at least one will republish and therefore we should receive all data. + list = new ArrayList<>(list.subList(0, Math.min(list.size(), 4))); + list.stream() + .map(Peer::getNodeAddress) + .forEach(this::sendRepublishRequest); + } else { + log.warn("No other nodes found. We try again in 60 seconds."); + UserThread.runAfter(this::connectToNextNode, 60); + } + } + + private void handleFault(NodeAddress nodeAddress) { + peerManager.handleConnectionFault(nodeAddress); + } + + private void stop() { + stopped = true; + stopTimeoutTimer(); + } + + private void stopTimeoutTimer() { + if (timeoutTimer != null) { + timeoutTimer.stop(); + timeoutTimer = null; + } + } +} 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 new file mode 100644 index 0000000000..c11ac0500c --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/network/messages/RepublishGovernanceDataRequest.java @@ -0,0 +1,76 @@ +/* + * 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.blindvote.network.messages; + +import bisq.network.p2p.DirectMessage; +import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; + +import bisq.common.app.Capabilities; +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; + +@EqualsAndHashCode(callSuper = true) +@Getter +public final class RepublishGovernanceDataRequest extends NetworkEnvelope implements DirectMessage, CapabilityRequiringPayload { + + public RepublishGovernanceDataRequest() { + this(Version.getP2PMessageVersion()); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private RepublishGovernanceDataRequest(int messageVersion) { + super(messageVersion); + } + + @Override + public PB.NetworkEnvelope toProtoNetworkEnvelope() { + return getNetworkEnvelopeBuilder() + .setRepublishGovernanceDataRequest(PB.RepublishGovernanceDataRequest.newBuilder()) + .build(); + } + + public static NetworkEnvelope fromProto(PB.RepublishGovernanceDataRequest proto, int messageVersion) { + return new RepublishGovernanceDataRequest(messageVersion); + } + + @Override + public List getRequiredCapabilities() { + return new ArrayList<>(Collections.singletonList( + Capabilities.Capability.DAO_FULL_NODE.ordinal() + )); + } + + @Override + public String toString() { + return "RepublishGovernanceDataRequest{" + + "\n} " + super.toString(); + } +} 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 7033a28731..ef081a1a87 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 @@ -27,6 +27,7 @@ import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.common.app.Capabilities; import bisq.common.crypto.Hash; import bisq.common.proto.persistable.PersistableEnvelope; +import bisq.common.util.Utilities; import io.bisq.generated.protobuffer.PB; @@ -138,4 +139,13 @@ public final class BlindVotePayload implements PersistableNetworkPayload, Persis Capabilities.Capability.BLIND_VOTE.ordinal() )); } + + @Override + public String toString() { + return "BlindVotePayload{" + + "\n blindVote=" + blindVote + + ",\n date=" + new Date(date) + + ",\n hash=" + Utilities.bytesAsHexString(hash) + + "\n}"; + } } diff --git a/core/src/main/java/bisq/core/dao/governance/merit/Merit.java b/core/src/main/java/bisq/core/dao/governance/merit/Merit.java index 2c3eef2719..d0633ec7db 100644 --- a/core/src/main/java/bisq/core/dao/governance/merit/Merit.java +++ b/core/src/main/java/bisq/core/dao/governance/merit/Merit.java @@ -22,6 +22,7 @@ import bisq.core.dao.state.governance.Issuance; import bisq.common.proto.network.NetworkPayload; import bisq.common.proto.persistable.PersistablePayload; +import bisq.common.util.Utilities; import io.bisq.generated.protobuffer.PB; @@ -63,4 +64,12 @@ public class Merit implements PersistablePayload, NetworkPayload, ConsensusCriti public String getIssuanceTxId() { return issuance.getTxId(); } + + @Override + public String toString() { + return "Merit{" + + "\n issuance=" + issuance + + ",\n signature=" + Utilities.bytesAsHexString(signature) + + "\n}"; + } } diff --git a/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java b/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java index 45bd987458..c26ef3ef6c 100644 --- a/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java +++ b/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java @@ -41,12 +41,13 @@ public class MeritConsensus { // Value with 144 blocks a day and 365 days would be 52560. We take a close round number instead. private static final int BLOCKS_PER_YEAR = 50_000; - public static MeritList decryptMeritList(byte[] encryptedMeritList, SecretKey secretKey) throws VoteResultException { + public static MeritList decryptMeritList(byte[] encryptedMeritList, SecretKey secretKey) + throws VoteResultException.DecryptionException { try { - final byte[] decrypted = Encryption.decrypt(encryptedMeritList, secretKey); + byte[] decrypted = Encryption.decrypt(encryptedMeritList, secretKey); return MeritList.getMeritListFromBytes(decrypted); } catch (Throwable t) { - throw new VoteResultException(t); + throw new VoteResultException.DecryptionException(t); } } @@ -84,7 +85,7 @@ public class MeritConsensus { // We verify if signature of hash of blindVoteTxId is correct. EC key from first input for blind vote tx is // used for signature. if (pubKeyAsHex == null) { - log.error("Error at getMeritStake: pubKeyAsHex is null"); + log.error("Error at isSignatureValid: pubKeyAsHex is null"); return false; } @@ -110,16 +111,15 @@ public class MeritConsensus { public static long getWeightedMeritAmount(long amount, int issuanceHeight, int blockHeight, int blocksPerYear) { if (issuanceHeight > blockHeight) - throw new IllegalArgumentException("issuanceHeight must not be larger than blockHeight"); + throw new IllegalArgumentException("issuanceHeight must not be larger than blockHeight. issuanceHeight=" + issuanceHeight + "; blockHeight=" + blockHeight); if (blockHeight < 0) - throw new IllegalArgumentException("blockHeight must not be negative"); + throw new IllegalArgumentException("blockHeight must not be negative. blockHeight=" + blockHeight); if (amount < 0) - throw new IllegalArgumentException("amount must not be negative"); + throw new IllegalArgumentException("amount must not be negative. amount" + amount); if (blocksPerYear < 0) - throw new IllegalArgumentException("blocksPerYear must not be negative"); + throw new IllegalArgumentException("blocksPerYear must not be negative. blocksPerYear=" + blocksPerYear); if (issuanceHeight < 0) - throw new IllegalArgumentException("issuanceHeight must not be negative"); - + throw new IllegalArgumentException("issuanceHeight must not be negative. issuanceHeight=" + issuanceHeight); // We use a linear function to apply a factor for the issuance amount of 1 if the issuance was recent and 0 // if the issuance was 2 years old or older. diff --git a/core/src/main/java/bisq/core/dao/governance/merit/MeritList.java b/core/src/main/java/bisq/core/dao/governance/merit/MeritList.java index d9ca1d3998..7332257bf2 100644 --- a/core/src/main/java/bisq/core/dao/governance/merit/MeritList.java +++ b/core/src/main/java/bisq/core/dao/governance/merit/MeritList.java @@ -50,7 +50,7 @@ public class MeritList extends PersistableList implements ConsensusCritic return getBuilder().build(); } - private PB.MeritList.Builder getBuilder() { + public PB.MeritList.Builder getBuilder() { return PB.MeritList.newBuilder() .addAllMerit(getList().stream() .map(Merit::toProtoMessage) diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalService.java index ec87e34898..56f45cf269 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalService.java @@ -43,7 +43,6 @@ public abstract class BaseProposalService { protected final BsqWalletService bsqWalletService; protected final BtcWalletService btcWalletService; protected final BsqStateService bsqStateService; - protected final ProposalConsensus proposalConsensus; protected final ProposalValidator proposalValidator; @Nullable protected String name; @@ -58,12 +57,10 @@ public abstract class BaseProposalService { public BaseProposalService(BsqWalletService bsqWalletService, BtcWalletService btcWalletService, BsqStateService bsqStateService, - ProposalConsensus proposalConsensus, ProposalValidator proposalValidator) { this.bsqWalletService = bsqWalletService; this.btcWalletService = btcWalletService; this.bsqStateService = bsqStateService; - this.proposalConsensus = proposalConsensus; this.proposalValidator = proposalValidator; } @@ -87,12 +84,12 @@ public abstract class BaseProposalService { // The hashOfPayload used in the opReturnData is created with the txId set to null. protected Transaction createTransaction(R proposal) throws InsufficientMoneyException, TxException { try { - final Coin fee = proposalConsensus.getFee(bsqStateService, bsqStateService.getChainHeight()); + final Coin fee = ProposalConsensus.getFee(bsqStateService, bsqStateService.getChainHeight()); // We create a prepared Bsq Tx for the proposal fee. final Transaction preparedBurnFeeTx = bsqWalletService.getPreparedProposalTx(fee); // payload does not have txId at that moment - byte[] hashOfPayload = proposalConsensus.getHashOfPayload(proposal); + byte[] hashOfPayload = ProposalConsensus.getHashOfPayload(proposal); byte[] opReturnData = getOpReturnData(hashOfPayload); // We add the BTC inputs for the miner fee. @@ -108,7 +105,7 @@ public abstract class BaseProposalService { } protected byte[] getOpReturnData(byte[] hashOfPayload) { - return proposalConsensus.getOpReturnData(hashOfPayload, OpReturnType.PROPOSAL.getType(), Version.PROPOSAL); + return ProposalConsensus.getOpReturnData(hashOfPayload, OpReturnType.PROPOSAL.getType(), Version.PROPOSAL); } protected Transaction completeTx(Transaction preparedBurnFeeTx, byte[] opReturnData, Proposal proposal) diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java b/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java index 392c9d6b98..ce8c0e7e7b 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java @@ -18,9 +18,9 @@ package bisq.core.dao.governance.proposal; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.wallet.TxBroadcastException; +import bisq.core.btc.exceptions.TxBroadcastException; +import bisq.core.btc.exceptions.TxMalleabilityException; import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.TxMalleabilityException; import bisq.core.btc.wallet.WalletsManager; import bisq.core.dao.governance.proposal.storage.temp.TempProposalPayload; import bisq.core.dao.state.BsqStateListener; diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/Proposal.java b/core/src/main/java/bisq/core/dao/governance/proposal/Proposal.java index 0cf538a8a8..bd73f1b0a2 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/Proposal.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/Proposal.java @@ -20,7 +20,9 @@ package bisq.core.dao.governance.proposal; import bisq.core.dao.governance.ConsensusCritical; import bisq.core.dao.governance.proposal.compensation.CompensationProposal; import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposal; +import bisq.core.dao.governance.proposal.generic.GenericProposal; import bisq.core.dao.governance.proposal.param.ChangeParamProposal; +import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposal; import bisq.core.dao.governance.proposal.role.BondedRoleProposal; import bisq.core.dao.state.blockchain.TxType; import bisq.core.dao.state.governance.Param; @@ -91,16 +93,16 @@ public abstract class Proposal implements PersistablePayload, NetworkPayload, Co switch (proto.getMessageCase()) { case COMPENSATION_PROPOSAL: return CompensationProposal.fromProto(proto); - case GENERIC_PROPOSAL: - throw new ProtobufferRuntimeException("Not implemented yet: " + proto); case CHANGE_PARAM_PROPOSAL: return ChangeParamProposal.fromProto(proto); - case REMOVE_ALTCOIN_PROPOSAL: - throw new ProtobufferRuntimeException("Not implemented yet: " + proto); - case CONFISCATE_BOND_PROPOSAL: - return ConfiscateBondProposal.fromProto(proto); case BONDED_ROLE_PROPOSAL: return BondedRoleProposal.fromProto(proto); + case CONFISCATE_BOND_PROPOSAL: + return ConfiscateBondProposal.fromProto(proto); + case GENERIC_PROPOSAL: + return GenericProposal.fromProto(proto); + case REMOVE_ASSET_PROPOSAL: + return RemoveAssetProposal.fromProto(proto); case MESSAGE_NOT_SET: default: throw new ProtobufferRuntimeException("Unknown message case: " + proto); @@ -131,7 +133,6 @@ public abstract class Proposal implements PersistablePayload, NetworkPayload, Co public abstract Param getThresholdParam(); - @Override public String toString() { return "Proposal{" + diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalConsensus.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalConsensus.java index dd37beda1f..112df7a799 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalConsensus.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalConsensus.java @@ -34,16 +34,16 @@ import lombok.extern.slf4j.Slf4j; */ @Slf4j public class ProposalConsensus { - public Coin getFee(BsqStateService bsqStateService, int chainHeadHeight) { - return Coin.valueOf(bsqStateService.getParamValue(Param.PROPOSAL_FEE, chainHeadHeight)); + public static Coin getFee(BsqStateService bsqStateService, int chainHeight) { + return Coin.valueOf(bsqStateService.getParamValue(Param.PROPOSAL_FEE, chainHeight)); } - public byte[] getHashOfPayload(Proposal payload) { + public static byte[] getHashOfPayload(Proposal payload) { final byte[] bytes = payload.toProtoMessage().toByteArray(); return Hash.getSha256Ripemd160hash(bytes); } - public byte[] getOpReturnData(byte[] hashOfPayload, byte opReturnType, byte version) { + public static byte[] getOpReturnData(byte[] hashOfPayload, byte opReturnType, byte version) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { outputStream.write(opReturnType); outputStream.write(version); 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 a80cc30036..dfa83e45ed 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 @@ -45,6 +45,9 @@ import javax.inject.Named; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import java.util.List; +import java.util.stream.Collectors; + import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -174,6 +177,19 @@ public class ProposalService implements HashMapChangedListener, AppendOnlyDataSt } + /////////////////////////////////////////////////////////////////////////////////////////// + // Getter + /////////////////////////////////////////////////////////////////////////////////////////// + + + public List getValidatedProposals() { + return proposalPayloads.stream() + .map(proposalPayload -> proposalPayload.getProposal()) + .filter(proposal -> proposalValidator.isTxTypeValid(proposal)) + .collect(Collectors.toList()); + } + + /////////////////////////////////////////////////////////////////////////////////////////// // Private /////////////////////////////////////////////////////////////////////////////////////////// @@ -212,7 +228,7 @@ public class ProposalService implements HashMapChangedListener, AppendOnlyDataSt log.info("We received a TempProposalPayload and store it to our protectedStoreList. proposalTxId={}", proposal.getTxId()); } else { - log.warn("We received an invalid proposal from the P2P network. Proposal.txId={}, blockHeight={}", + log.debug("We received an invalid proposal from the P2P network. Proposal.txId={}, blockHeight={}", proposal.getTxId(), bsqStateService.getChainHeight()); } } 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 94422066e2..343ae773fa 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 @@ -21,11 +21,11 @@ import bisq.core.locale.Res; public enum ProposalType { COMPENSATION_REQUEST, - BONDED_ROLE, - REMOVE_ALTCOIN, CHANGE_PARAM, + BONDED_ROLE, + CONFISCATE_BOND, GENERIC, - CONFISCATE_BOND; + REMOVE_ASSET; public String getDisplayName() { return Res.get("dao.proposal.type." + name()); 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 34462a6dd7..be72a1aea0 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 @@ -36,8 +36,8 @@ import static org.apache.commons.lang3.Validate.notEmpty; @Slf4j public class ProposalValidator { - private final BsqStateService bsqStateService; - private final PeriodService periodService; + protected final BsqStateService bsqStateService; + protected final PeriodService periodService; @Inject public ProposalValidator(BsqStateService bsqStateService, PeriodService periodService) { @@ -71,6 +71,19 @@ public class ProposalValidator { return isValid(proposal, false); } + public boolean isTxTypeValid(Proposal proposal) { + String txId = proposal.getTxId(); + if (txId == null || txId.equals("")) { + log.warn("txId must be set. proposal.getTxId()={}", proposal.getTxId()); + return false; + } + Optional optionalTxType = bsqStateService.getOptionalTxType(txId); + boolean present = optionalTxType.filter(txType -> txType == proposal.getTxType()).isPresent(); + if (!present) + log.debug("optionalTxType not present for proposal {}" + proposal); + return present; + } + private boolean isValid(Proposal proposal, boolean allowUnconfirmed) { if (!areDataFieldsValid(proposal)) { log.warn("proposal data fields are invalid. proposal={}", proposal); diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationConsensus.java b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationConsensus.java index dacd4834c0..fc8a0775dd 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationConsensus.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationConsensus.java @@ -17,19 +17,21 @@ package bisq.core.dao.governance.proposal.compensation; -import bisq.core.dao.governance.proposal.ProposalConsensus; +import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.governance.Param; import org.bitcoinj.core.Coin; import lombok.extern.slf4j.Slf4j; @Slf4j -public class CompensationConsensus extends ProposalConsensus { - public static Coin getMinCompensationRequestAmount() { - return Coin.valueOf(1_000); // 10 BSQ +public class CompensationConsensus { + public static Coin getMinCompensationRequestAmount(BsqStateService bsqStateService, int chainHeight) { + return Coin.valueOf(bsqStateService.getParamValue(Param.COMPENSATION_REQUEST_MIN_AMOUNT, chainHeight)); } - static Coin getMaxCompensationRequestAmount() { - return Coin.valueOf(20_000_000); // 200 000 BSQ + public static Coin getMaxCompensationRequestAmount(BsqStateService bsqStateService, int chainHeight) { + return Coin.valueOf(bsqStateService.getParamValue(Param.COMPENSATION_REQUEST_MAX_AMOUNT, chainHeight)); } + } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalService.java index e5cc369579..22e07d1f2f 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalService.java @@ -53,12 +53,10 @@ public class CompensationProposalService extends BaseProposalService= 0, - "Requested BSQ must not be less than MinCompensationRequestAmount"); + Coin maxCompensationRequestAmount = CompensationConsensus.getMaxCompensationRequestAmount(bsqStateService, periodService.getChainHeight()); + checkArgument(requestedBsq.compareTo(maxCompensationRequestAmount) <= 0, + "Requested BSQ must not exceed " + (maxCompensationRequestAmount.value / 100L) + " BSQ"); + Coin minCompensationRequestAmount = CompensationConsensus.getMinCompensationRequestAmount(bsqStateService, periodService.getChainHeight()); + checkArgument(requestedBsq.compareTo(minCompensationRequestAmount) >= 0, + "Requested BSQ must not be less than " + (minCompensationRequestAmount.value / 100L) + " BSQ"); + } catch (Throwable throwable) { throw new ValidationException(throwable); } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalService.java index 987de19aa6..4e351668e8 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalService.java @@ -21,7 +21,6 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.BaseProposalService; -import bisq.core.dao.governance.proposal.ProposalConsensus; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.state.BsqStateService; @@ -48,12 +47,10 @@ public class ConfiscateBondProposalService extends BaseProposalService. + */ + +package bisq.core.dao.governance.proposal.generic; + +import bisq.core.dao.governance.proposal.Proposal; +import bisq.core.dao.governance.proposal.ProposalType; +import bisq.core.dao.state.blockchain.TxType; +import bisq.core.dao.state.governance.Param; + +import bisq.common.app.Version; + +import io.bisq.generated.protobuffer.PB; + +import java.util.Date; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@Slf4j +@EqualsAndHashCode(callSuper = true) +@Value +public final class GenericProposal extends Proposal { + + GenericProposal(String name, + String link) { + this(name, + link, + Version.PROPOSAL, + new Date().getTime(), + ""); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private GenericProposal(String name, + String link, + byte version, + long creationDate, + String txId) { + super(name, + link, + version, + creationDate, + txId); + } + + @Override + public PB.Proposal.Builder getProposalBuilder() { + final PB.GenericProposal.Builder builder = PB.GenericProposal.newBuilder(); + return super.getProposalBuilder().setGenericProposal(builder); + } + + public static GenericProposal fromProto(PB.Proposal proto) { + return new GenericProposal(proto.getName(), + proto.getLink(), + (byte) proto.getVersion(), + proto.getCreationDate(), + proto.getTxId()); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Getters + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public ProposalType getType() { + return ProposalType.GENERIC; + } + + @Override + public Param getQuorumParam() { + return Param.QUORUM_GENERIC; + } + + @Override + public Param getThresholdParam() { + return Param.THRESHOLD_GENERIC; + } + + @Override + public TxType getTxType() { + return TxType.PROPOSAL; + } + + @Override + public Proposal cloneProposalAndAddTxId(String txId) { + return new GenericProposal(getName(), + getLink(), + getVersion(), + getCreationDate().getTime(), + txId); + } + + @Override + public String toString() { + return "GenericProposal{" + + "\n} " + super.toString(); + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalService.java new file mode 100644 index 0000000000..6d88307a2c --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalService.java @@ -0,0 +1,66 @@ +/* + * This file is part of Bisq. + * + * bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with bisq. If not, see . + */ + +package bisq.core.dao.governance.proposal.generic; + +import bisq.core.btc.wallet.BsqWalletService; +import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.proposal.BaseProposalService; +import bisq.core.dao.governance.proposal.ProposalWithTransaction; +import bisq.core.dao.governance.proposal.TxException; +import bisq.core.dao.state.BsqStateService; + +import org.bitcoinj.core.InsufficientMoneyException; + +import javax.inject.Inject; + +import lombok.extern.slf4j.Slf4j; + +/** + * Creates GenericProposal and transaction. + */ +@Slf4j +public class GenericProposalService extends BaseProposalService { + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + + @Inject + public GenericProposalService(BsqWalletService bsqWalletService, + BtcWalletService btcWalletService, + BsqStateService bsqStateService, + GenericProposalValidator proposalValidator) { + super(bsqWalletService, + btcWalletService, + bsqStateService, + proposalValidator); + } + + public ProposalWithTransaction createProposalWithTransaction(String name, String link) + throws ValidationException, InsufficientMoneyException, TxException { + + return super.createProposalWithTransaction(name, link); + } + + @Override + protected GenericProposal createProposalWithoutTxId() { + return new GenericProposal(name, link); + } +} 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 new file mode 100644 index 0000000000..0435d2782b --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalValidator.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.dao.governance.proposal.generic; + +import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.proposal.Proposal; +import bisq.core.dao.governance.proposal.ProposalValidator; +import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.period.PeriodService; + +import javax.inject.Inject; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class GenericProposalValidator extends ProposalValidator { + + @Inject + public GenericProposalValidator(BsqStateService bsqStateService, PeriodService periodService) { + super(bsqStateService, periodService); + } + + @Override + public void validateDataFields(Proposal proposal) throws ValidationException { + try { + super.validateDataFields(proposal); + + GenericProposal genericProposalProposal = (GenericProposal) proposal; + //TODO + } catch (Throwable throwable) { + throw new ValidationException(throwable); + } + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalService.java index 64f179bdb4..4df9922855 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalService.java @@ -21,7 +21,6 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.BaseProposalService; -import bisq.core.dao.governance.proposal.ProposalConsensus; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.state.BsqStateService; @@ -50,12 +49,10 @@ public class ChangeParamProposalService extends BaseProposalService. + */ + +package bisq.core.dao.governance.proposal.param; + +public class ChangeParamValidationException extends Exception { + public ChangeParamValidationException(String message) { + super(message); + } +} 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 e919a4087e..dd123a325f 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,22 +17,33 @@ package bisq.core.dao.governance.proposal.param; +import bisq.core.btc.wallet.Restrictions; import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.Proposal; import bisq.core.dao.governance.proposal.ProposalValidator; import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.governance.Param; import bisq.core.dao.state.period.PeriodService; +import bisq.core.locale.Res; +import bisq.core.util.BsqFormatter; import javax.inject.Inject; +import com.google.common.annotations.VisibleForTesting; + import lombok.extern.slf4j.Slf4j; +import static com.google.common.base.Preconditions.checkArgument; + @Slf4j public class ChangeParamValidator extends ProposalValidator { + private BsqFormatter bsqFormatter; + @Inject - public ChangeParamValidator(BsqStateService bsqStateService, PeriodService periodService) { + public ChangeParamValidator(BsqStateService bsqStateService, PeriodService periodService, BsqFormatter bsqFormatter) { super(bsqStateService, periodService); + this.bsqFormatter = bsqFormatter; } @Override @@ -41,9 +52,113 @@ public class ChangeParamValidator extends ProposalValidator { super.validateDataFields(proposal); ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; - //TODO + + validateParamValue(changeParamProposal.getParam(), changeParamProposal.getParamValue()); } catch (Throwable throwable) { throw new ValidationException(throwable); } } + + // TODO + public void validateParamValue(Param param, long paramValue) throws ChangeParamValidationException { + // max 4 times the current value. min 25% of current value as general boundaries + checkMinMax(param, paramValue, 300, -75); + + switch (param) { + case UNDEFINED: + break; + + case DEFAULT_MAKER_FEE_BSQ: + break; + case DEFAULT_TAKER_FEE_BSQ: + break; + case DEFAULT_MAKER_FEE_BTC: + break; + case DEFAULT_TAKER_FEE_BTC: + break; + + case PROPOSAL_FEE: + break; + case BLIND_VOTE_FEE: + break; + + case COMPENSATION_REQUEST_MIN_AMOUNT: + if (paramValue < Restrictions.getMinNonDustOutput().value) + throw new ChangeParamValidationException(Res.get("validation.amountBelowDust", Restrictions.getMinNonDustOutput().value)); + checkMinMax(param, paramValue, 100, -50); + break; + case COMPENSATION_REQUEST_MAX_AMOUNT: + checkMinMax(param, paramValue, 100, -50); + break; + + case QUORUM_COMP_REQUEST: + break; + case QUORUM_CHANGE_PARAM: + break; + case QUORUM_ROLE: + break; + case QUORUM_CONFISCATION: + break; + case QUORUM_GENERIC: + break; + case QUORUM_REMOVE_ASSET: + break; + + case THRESHOLD_COMP_REQUEST: + break; + case THRESHOLD_CHANGE_PARAM: + break; + case THRESHOLD_ROLE: + break; + case THRESHOLD_CONFISCATION: + break; + case THRESHOLD_GENERIC: + break; + case THRESHOLD_REMOVE_ASSET: + break; + + case PHASE_UNDEFINED: + break; + case PHASE_PROPOSAL: + break; + case PHASE_BREAK1: + break; + case PHASE_BLIND_VOTE: + break; + case PHASE_BREAK2: + break; + case PHASE_VOTE_REVEAL: + break; + case PHASE_BREAK3: + break; + case PHASE_RESULT: + break; + } + } + + private void checkMinMax(Param param, long paramValue, long maxPercentChange, long minPercentChange) throws ChangeParamValidationException { + long max = getNewValueByPercentChange(param, maxPercentChange); + if (paramValue > max) + throw new ChangeParamValidationException(Res.get("validation.inputTooLarge", bsqFormatter.formatParamValue(param, max))); + long min = getNewValueByPercentChange(param, minPercentChange); + if (paramValue < min) + throw new ChangeParamValidationException(Res.get("validation.inputTooSmall", bsqFormatter.formatParamValue(param, min))); + } + + /** + * @param param The param to change + * @param percentChange 100 means 100% more than current value -> 2 times current value. -50 means half of the current value + * @return The new value. + */ + //TODO add test + // TODO use multiplier to make it more intuitive? (4,4) means 4 times current value for max and divided by 4 to get min value) + @VisibleForTesting + long getNewValueByPercentChange(Param param, long percentChange) { + checkArgument(percentChange > -100, "percentChange must be bigger than -100"); + return (getCurrentValue(param) * 100 * (100 + percentChange)) / 10000; + } + + private long getCurrentValue(Param param) { + return bsqStateService.getParamValue(param, periodService.getChainHeight()); + } } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposal.java b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposal.java new file mode 100644 index 0000000000..5f497aa022 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposal.java @@ -0,0 +1,133 @@ +/* + * 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.removeAsset; + +import bisq.core.dao.governance.proposal.Proposal; +import bisq.core.dao.governance.proposal.ProposalType; +import bisq.core.dao.state.blockchain.TxType; +import bisq.core.dao.state.governance.Param; + +import bisq.common.app.Version; + +import io.bisq.generated.protobuffer.PB; + +import java.util.Date; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@Slf4j +@EqualsAndHashCode(callSuper = true) +@Value +public final class RemoveAssetProposal extends Proposal { + private final String tickerSymbol; + + RemoveAssetProposal(String name, + String link, + String tickerSymbol) { + this(name, + link, + tickerSymbol, + Version.PROPOSAL, + new Date().getTime(), + ""); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private RemoveAssetProposal(String name, + String link, + String tickerSymbol, + byte version, + long creationDate, + String txId) { + super(name, + link, + version, + creationDate, + txId); + + this.tickerSymbol = tickerSymbol; + } + + @Override + public PB.Proposal.Builder getProposalBuilder() { + final PB.RemoveAssetProposal.Builder builder = PB.RemoveAssetProposal.newBuilder() + .setTickerSymbol(tickerSymbol); + return super.getProposalBuilder().setRemoveAssetProposal(builder); + } + + public static RemoveAssetProposal fromProto(PB.Proposal proto) { + final PB.RemoveAssetProposal proposalProto = proto.getRemoveAssetProposal(); + return new RemoveAssetProposal(proto.getName(), + proto.getLink(), + proposalProto.getTickerSymbol(), + (byte) proto.getVersion(), + proto.getCreationDate(), + proto.getTxId()); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Getters + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public ProposalType getType() { + return ProposalType.REMOVE_ASSET; + } + + @Override + public Param getQuorumParam() { + return Param.QUORUM_REMOVE_ASSET; + } + + @Override + public Param getThresholdParam() { + return Param.THRESHOLD_REMOVE_ASSET; + } + + @Override + public TxType getTxType() { + return TxType.PROPOSAL; + } + + @Override + public Proposal cloneProposalAndAddTxId(String txId) { + return new RemoveAssetProposal(getName(), + getLink(), + getTickerSymbol(), + getVersion(), + getCreationDate().getTime(), + txId); + } + + @Override + public String toString() { + return "GenericProposal{" + + "\n tickerSymbol=" + tickerSymbol + + "\n} " + super.toString(); + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalService.java new file mode 100644 index 0000000000..ba20114335 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalService.java @@ -0,0 +1,77 @@ +/* + * 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.removeAsset; + +import bisq.core.btc.wallet.BsqWalletService; +import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.proposal.BaseProposalService; +import bisq.core.dao.governance.proposal.ProposalWithTransaction; +import bisq.core.dao.governance.proposal.TxException; +import bisq.core.dao.state.BsqStateService; + +import org.bitcoinj.core.InsufficientMoneyException; + +import javax.inject.Inject; + +import lombok.extern.slf4j.Slf4j; + + + +import bisq.asset.Asset; + +/** + * Creates RemoveAssetProposal and transaction. + */ +@Slf4j +public class RemoveAssetProposalService extends BaseProposalService { + private Asset asset; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + + @Inject + public RemoveAssetProposalService(BsqWalletService bsqWalletService, + BtcWalletService btcWalletService, + BsqStateService bsqStateService, + RemoveAssetValidator proposalValidator) { + super(bsqWalletService, + btcWalletService, + bsqStateService, + proposalValidator); + } + + public ProposalWithTransaction createProposalWithTransaction(String name, + String link, + Asset asset) + throws ValidationException, InsufficientMoneyException, TxException { + this.asset = asset; + + return super.createProposalWithTransaction(name, link); + } + + @Override + protected RemoveAssetProposal createProposalWithoutTxId() { + return new RemoveAssetProposal( + name, + link, + asset.getTickerSymbol()); + } +} 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 new file mode 100644 index 0000000000..d88e4fa2a6 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.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.dao.governance.proposal.removeAsset; + +import bisq.core.dao.exceptions.ValidationException; +import bisq.core.dao.governance.proposal.Proposal; +import bisq.core.dao.governance.proposal.ProposalValidator; +import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.period.PeriodService; + +import javax.inject.Inject; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class RemoveAssetValidator extends ProposalValidator { + + @Inject + public RemoveAssetValidator(BsqStateService bsqStateService, PeriodService periodService) { + super(bsqStateService, periodService); + } + + @Override + public void validateDataFields(Proposal proposal) throws ValidationException { + try { + super.validateDataFields(proposal); + + RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal; + //TODO + } catch (Throwable throwable) { + throw new ValidationException(throwable); + } + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/role/BondedRoleProposal.java b/core/src/main/java/bisq/core/dao/governance/proposal/role/BondedRoleProposal.java index 1fd2dcff61..0a961b5e29 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/role/BondedRoleProposal.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/role/BondedRoleProposal.java @@ -100,12 +100,12 @@ public final class BondedRoleProposal extends Proposal { @Override public Param getQuorumParam() { - return Param.QUORUM_PROPOSAL; + return Param.QUORUM_ROLE; } @Override public Param getThresholdParam() { - return Param.THRESHOLD_PROPOSAL; + return Param.THRESHOLD_ROLE; } @Override diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/role/BondedRoleProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/role/BondedRoleProposalService.java index 0630fbb296..afccb954f4 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/role/BondedRoleProposalService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/role/BondedRoleProposalService.java @@ -21,7 +21,6 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.BaseProposalService; -import bisq.core.dao.governance.proposal.ProposalConsensus; import bisq.core.dao.governance.proposal.ProposalWithTransaction; import bisq.core.dao.governance.proposal.TxException; import bisq.core.dao.governance.role.BondedRole; @@ -49,12 +48,10 @@ public class BondedRoleProposalService extends BaseProposalService getVote(String proposalTxId) { return ballotList.stream() .filter(ballot -> ballot.getTxId().equals(proposalTxId)) diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/DecryptedBallotsWithMeritsList.java b/core/src/main/java/bisq/core/dao/governance/voteresult/DecryptedBallotsWithMeritsList.java new file mode 100644 index 0000000000..ba57ec0cda --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/DecryptedBallotsWithMeritsList.java @@ -0,0 +1,76 @@ +/* + * 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.voteresult; + +import bisq.core.dao.governance.ConsensusCritical; + +import bisq.common.proto.persistable.PersistableList; + +import io.bisq.generated.protobuffer.PB; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import lombok.EqualsAndHashCode; + +/** + * PersistableEnvelope wrapper for list of decryptedBallotsWithMerits. + */ +@EqualsAndHashCode(callSuper = true) +public class DecryptedBallotsWithMeritsList extends PersistableList implements ConsensusCritical { + + public DecryptedBallotsWithMeritsList(List list) { + super(list); + } + + public DecryptedBallotsWithMeritsList() { + super(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public PB.PersistableEnvelope toProtoMessage() { + return PB.PersistableEnvelope.newBuilder().setDecryptedBallotsWithMeritsList(getBuilder()).build(); + } + + private PB.DecryptedBallotsWithMeritsList.Builder getBuilder() { + return PB.DecryptedBallotsWithMeritsList.newBuilder() + .addAllDecryptedBallotsWithMerits(getList().stream() + .map(DecryptedBallotsWithMerits::toProtoMessage) + .collect(Collectors.toList())); + } + + public static DecryptedBallotsWithMeritsList fromProto(PB.DecryptedBallotsWithMeritsList proto) { + return new DecryptedBallotsWithMeritsList(new ArrayList<>(proto.getDecryptedBallotsWithMeritsList().stream() + .map(DecryptedBallotsWithMerits::fromProto) + .collect(Collectors.toList()))); + } + + @Override + public String toString() { + return "List of blindVoteTxId's in DecryptedBallotsWithMeritsList: " + getList().stream() + .map(DecryptedBallotsWithMerits::getBlindVoteTxId) + .collect(Collectors.toList()); + } +} + diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/EvaluatedProposal.java b/core/src/main/java/bisq/core/dao/governance/voteresult/EvaluatedProposal.java index 37cd907e0b..4945f741a9 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/EvaluatedProposal.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/EvaluatedProposal.java @@ -19,22 +19,53 @@ package bisq.core.dao.governance.voteresult; import bisq.core.dao.governance.proposal.Proposal; +import bisq.common.proto.persistable.PersistablePayload; + +import io.bisq.generated.protobuffer.PB; + import lombok.Value; @Value -public class EvaluatedProposal { +public class EvaluatedProposal implements PersistablePayload { private final boolean isAccepted; private final ProposalVoteResult proposalVoteResult; private final long requiredQuorum; private final long requiredThreshold; - public EvaluatedProposal(boolean isAccepted, ProposalVoteResult proposalVoteResult, long requiredQuorum, long requiredThreshold) { + EvaluatedProposal(boolean isAccepted, ProposalVoteResult proposalVoteResult, long requiredQuorum, long requiredThreshold) { this.isAccepted = isAccepted; this.proposalVoteResult = proposalVoteResult; this.requiredQuorum = requiredQuorum; this.requiredThreshold = requiredThreshold; } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public PB.EvaluatedProposal toProtoMessage() { + PB.EvaluatedProposal.Builder builder = PB.EvaluatedProposal.newBuilder() + .setIsAccepted(isAccepted) + .setProposalVoteResult(proposalVoteResult.toProtoMessage()) + .setRequiredQuorum(requiredQuorum) + .setRequiredThreshold(requiredThreshold); + return builder.build(); + } + + public static EvaluatedProposal fromProto(PB.EvaluatedProposal proto) { + return new EvaluatedProposal(proto.getIsAccepted(), + ProposalVoteResult.fromProto(proto.getProposalVoteResult()), + proto.getRequiredQuorum(), + proto.getRequiredThreshold()); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + public Proposal getProposal() { return proposalVoteResult.getProposal(); } diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/EvaluatedProposalList.java b/core/src/main/java/bisq/core/dao/governance/voteresult/EvaluatedProposalList.java new file mode 100644 index 0000000000..0ebe000301 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/EvaluatedProposalList.java @@ -0,0 +1,76 @@ +/* + * 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.voteresult; + +import bisq.core.dao.governance.ConsensusCritical; + +import bisq.common.proto.persistable.PersistableList; + +import io.bisq.generated.protobuffer.PB; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import lombok.EqualsAndHashCode; + +/** + * PersistableEnvelope wrapper for list of evaluatedProposals. + */ +@EqualsAndHashCode(callSuper = true) +public class EvaluatedProposalList extends PersistableList implements ConsensusCritical { + + public EvaluatedProposalList(List list) { + super(list); + } + + public EvaluatedProposalList() { + super(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public PB.PersistableEnvelope toProtoMessage() { + return PB.PersistableEnvelope.newBuilder().setEvaluatedProposalList(getBuilder()).build(); + } + + public PB.EvaluatedProposalList.Builder getBuilder() { + return PB.EvaluatedProposalList.newBuilder() + .addAllEvaluatedProposal(getList().stream() + .map(EvaluatedProposal::toProtoMessage) + .collect(Collectors.toList())); + } + + public static EvaluatedProposalList fromProto(PB.EvaluatedProposalList proto) { + return new EvaluatedProposalList(new ArrayList<>(proto.getEvaluatedProposalList().stream() + .map(EvaluatedProposal::fromProto) + .collect(Collectors.toList()))); + } + + @Override + public String toString() { + return "List of proposalTxId's in EvaluatedProposalList: " + getList().stream() + .map(EvaluatedProposal::getProposalTxId) + .collect(Collectors.toList()); + } +} + diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java new file mode 100644 index 0000000000..d0b47df820 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java @@ -0,0 +1,69 @@ +/* + * 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.voteresult; + +import bisq.core.dao.DaoSetupService; +import bisq.core.dao.governance.blindvote.network.RepublishGovernanceDataHandler; + +import javax.inject.Inject; + +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; + +import lombok.Getter; + +public class MissingDataRequestService implements DaoSetupService { + private final RepublishGovernanceDataHandler republishGovernanceDataHandler; + + @Getter + private final ObservableList voteResultExceptions = FXCollections.observableArrayList(); + + @Inject + public MissingDataRequestService(RepublishGovernanceDataHandler republishGovernanceDataHandler) { + this.republishGovernanceDataHandler = republishGovernanceDataHandler; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // DaoSetupService + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void addListeners() { + voteResultExceptions.addListener((ListChangeListener) c -> { + c.next(); + if (c.wasAdded()) { + republishGovernanceDataHandler.sendRepublishRequest(); + } + }); + } + + @Override + public void start() { + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + public void addVoteResultException(VoteResultException voteResultException) { + this.voteResultExceptions.add(voteResultException); + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/ProposalVoteResult.java b/core/src/main/java/bisq/core/dao/governance/voteresult/ProposalVoteResult.java index 42c66a2b3a..14b0d3172d 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/ProposalVoteResult.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/ProposalVoteResult.java @@ -19,6 +19,10 @@ package bisq.core.dao.governance.voteresult; import bisq.core.dao.governance.proposal.Proposal; +import bisq.common.proto.persistable.PersistablePayload; + +import io.bisq.generated.protobuffer.PB; + import lombok.Value; import lombok.extern.slf4j.Slf4j; @@ -26,7 +30,7 @@ import static com.google.common.base.Preconditions.checkArgument; @Value @Slf4j -public class ProposalVoteResult { +public class ProposalVoteResult implements PersistablePayload { private final Proposal proposal; private final long stakeOfAcceptedVotes; private final long stakeOfRejectedVotes; @@ -44,6 +48,36 @@ public class ProposalVoteResult { this.numIgnoredVotes = numIgnoredVotes; } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public PB.ProposalVoteResult toProtoMessage() { + PB.ProposalVoteResult.Builder builder = PB.ProposalVoteResult.newBuilder() + .setProposal(proposal.toProtoMessage()) + .setStakeOfAcceptedVotes(stakeOfAcceptedVotes) + .setStakeOfRejectedVotes(stakeOfRejectedVotes) + .setNumAcceptedVotes(numAcceptedVotes) + .setNumRejectedVotes(numRejectedVotes) + .setNumIgnoredVotes(numIgnoredVotes); + return builder.build(); + } + + public static ProposalVoteResult fromProto(PB.ProposalVoteResult proto) { + return new ProposalVoteResult(Proposal.fromProto(proto.getProposal()), + proto.getStakeOfAcceptedVotes(), + proto.getStakeOfRejectedVotes(), + proto.getNumAcceptedVotes(), + proto.getNumRejectedVotes(), + proto.getNumIgnoredVotes()); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + public int getNumActiveVotes() { return numAcceptedVotes + numRejectedVotes; } diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java index 2b9bf79209..3b0ad118e7 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java @@ -54,35 +54,41 @@ public class VoteResultConsensus { return Arrays.copyOfRange(opReturnData, 2, 22); } - public static VoteWithProposalTxIdList decryptVotes(byte[] encryptedVotes, SecretKey secretKey) throws VoteResultException { + public static VoteWithProposalTxIdList decryptVotes(byte[] encryptedVotes, SecretKey secretKey) + throws VoteResultException.DecryptionException { try { byte[] decrypted = Encryption.decrypt(encryptedVotes, secretKey); return VoteWithProposalTxIdList.getVoteWithProposalTxIdListFromBytes(decrypted); } catch (Throwable t) { - throw new VoteResultException(t); + throw new VoteResultException.DecryptionException(t); } } // We compare first by stake and in case we have multiple entries with same stake we use the // hex encoded hashOfProposalList for comparision @Nullable - public static byte[] getMajorityHash(List hashWithStakeList) throws VoteResultException { - checkArgument(!hashWithStakeList.isEmpty(), "hashWithStakeList must not be empty"); - hashWithStakeList.sort(Comparator.comparingLong(VoteResultService.HashWithStake::getStake).reversed() - .thenComparing(hashWithStake -> Utilities.encodeToHex(hashWithStake.getHash()))); + public static byte[] getMajorityHash(List hashWithStakeList) + throws VoteResultException.ConsensusException, VoteResultException.ValidationException { + try { + checkArgument(!hashWithStakeList.isEmpty(), "hashWithStakeList must not be empty"); + hashWithStakeList.sort(Comparator.comparingLong(VoteResultService.HashWithStake::getStake).reversed() + .thenComparing(hashWithStake -> Utilities.encodeToHex(hashWithStake.getHash()))); - // If there are conflicting data views (multiple hashes) we only consider the voting round as valid if - // the majority is a super majority with > 80%. - if (hashWithStakeList.size() > 1) { - long stakeOfAll = hashWithStakeList.stream().mapToLong(VoteResultService.HashWithStake::getStake).sum(); - long stakeOfFirst = hashWithStakeList.get(0).getStake(); - if ((double) stakeOfFirst / (double) stakeOfAll < 0.8) { - throw new VoteResultException("The winning data view has less then 80% of the total stake of " + - "all data views. We consider the voting cycle as invalid if the winning data view does not " + - "reach a super majority."); + // If there are conflicting data views (multiple hashes) we only consider the voting round as valid if + // the majority is a super majority with > 80%. + if (hashWithStakeList.size() > 1) { + long stakeOfAll = hashWithStakeList.stream().mapToLong(VoteResultService.HashWithStake::getStake).sum(); + long stakeOfFirst = hashWithStakeList.get(0).getStake(); + if ((double) stakeOfFirst / (double) stakeOfAll < 0.8) { + throw new VoteResultException.ConsensusException("The winning data view has less then 80% of the " + + "total stake of all data views. We consider the voting cycle as invalid if the " + + "winning data view does not reach a super majority."); + } } + return hashWithStakeList.get(0).getHash(); + } catch (Throwable t) { + throw new VoteResultException.ValidationException(t); } - return hashWithStakeList.get(0).getHash(); } // Key is stored after version and type bytes and list of Blind votes. It has 16 bytes @@ -92,24 +98,24 @@ public class VoteResultConsensus { } public static TxOutput getConnectedBlindVoteStakeOutput(Tx voteRevealTx, BsqStateService bsqStateService) - throws VoteResultException { + throws VoteResultException.ValidationException { try { // We use the stake output of the blind vote tx as first input - final TxInput stakeTxInput = voteRevealTx.getTxInputs().get(0); + TxInput stakeTxInput = voteRevealTx.getTxInputs().get(0); Optional optionalBlindVoteStakeOutput = bsqStateService.getConnectedTxOutput(stakeTxInput); checkArgument(optionalBlindVoteStakeOutput.isPresent(), "blindVoteStakeOutput must be present"); - final TxOutput blindVoteStakeOutput = optionalBlindVoteStakeOutput.get(); + TxOutput blindVoteStakeOutput = optionalBlindVoteStakeOutput.get(); checkArgument(blindVoteStakeOutput.getTxOutputType() == TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT, "blindVoteStakeOutput must be of type BLIND_VOTE_LOCK_STAKE_OUTPUT"); return blindVoteStakeOutput; } catch (Throwable t) { - throw new VoteResultException(t); + throw new VoteResultException.ValidationException(t); } } public static Tx getBlindVoteTx(TxOutput blindVoteStakeOutput, BsqStateService bsqStateService, PeriodService periodService, int chainHeight) - throws VoteResultException { + throws VoteResultException.ValidationException { try { String blindVoteTxId = blindVoteStakeOutput.getTxId(); Optional optionalBlindVoteTx = bsqStateService.getTx(blindVoteTxId); @@ -128,7 +134,7 @@ public class VoteResultConsensus { + blindVoteTx.getBlockHeight()); return blindVoteTx; } catch (Throwable t) { - throw new VoteResultException(t); + throw new VoteResultException.ValidationException(t); } } } 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 35a0dbb45b..04ea068b79 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 @@ -17,35 +17,112 @@ package bisq.core.dao.governance.voteresult; -import bisq.core.dao.governance.blindvote.storage.BlindVotePayload; +import bisq.core.dao.governance.ballot.Ballot; -import lombok.Getter; +import java.util.List; -import javax.annotation.Nullable; +import lombok.EqualsAndHashCode; +import lombok.Value; public class VoteResultException extends Exception { - @Getter - @Nullable - private BlindVotePayload blindVotePayload; - public VoteResultException(String message, Exception cause, BlindVotePayload blindVotePayload) { - super(message, cause); - this.blindVotePayload = blindVotePayload; + VoteResultException(Throwable cause) { + super(cause); } - public VoteResultException(String message) { + private VoteResultException(String message) { super(message); } - public VoteResultException(Throwable cause) { - super(cause); + private VoteResultException(String message, Throwable cause) { + super(message, cause); } @Override public String toString() { return "VoteResultException{" + - "\n blindVotePayload=" + blindVotePayload + - "\n cause=" + getCause() + "\n} " + super.toString(); } + + @EqualsAndHashCode(callSuper = true) + public static class ConsensusException extends VoteResultException { + + ConsensusException(String message) { + super(message); + } + + @Override + public String toString() { + return "ConsensusException{" + + "\n} " + super.toString(); + } + } + + @EqualsAndHashCode(callSuper = true) + public static class ValidationException extends VoteResultException { + + ValidationException(Throwable cause) { + super("Validation of vote result failed.", cause); + } + + @Override + public String toString() { + return "VoteResultException{" + + "\n cause=" + getCause() + + "\n} " + super.toString(); + } + } + + @EqualsAndHashCode(callSuper = true) + public static abstract class MissingDataException extends VoteResultException { + private MissingDataException(String message) { + super(message); + } + } + + @EqualsAndHashCode(callSuper = true) + @Value + public static class MissingBlindVoteDataException extends MissingDataException { + private String blindVoteTxId; + + MissingBlindVoteDataException(String blindVoteTxId) { + super("Blind vote tx ID " + blindVoteTxId + " is missing"); + this.blindVoteTxId = blindVoteTxId; + } + + @Override + public String toString() { + return "MissingBlindVoteDataException{" + + "\n blindVoteTxId='" + blindVoteTxId + '\'' + + "\n} " + super.toString(); + } + } + + @EqualsAndHashCode(callSuper = true) + @Value + public static class MissingBallotException extends MissingDataException { + private List existingBallots; + private List proposalTxIdsOfMissingBallots; + + MissingBallotException(List existingBallots, List proposalTxIdsOfMissingBallots) { + super("Missing ballots. proposalTxIdsOfMissingBallots=" + proposalTxIdsOfMissingBallots); + this.existingBallots = existingBallots; + this.proposalTxIdsOfMissingBallots = proposalTxIdsOfMissingBallots; + } + } + + + @EqualsAndHashCode(callSuper = true) + @Value + public static class DecryptionException extends VoteResultException { + public DecryptionException(Throwable cause) { + super(cause); + } + + @Override + public String toString() { + return "DecryptionException{" + + "\n} " + super.toString(); + } + } } 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 fc35c794fe..425789f309 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 @@ -17,7 +17,9 @@ package bisq.core.dao.governance.voteresult; +import bisq.core.app.BisqEnvironment; import bisq.core.dao.DaoSetupService; +import bisq.core.dao.governance.asset.AssetService; import bisq.core.dao.governance.ballot.Ballot; import bisq.core.dao.governance.ballot.BallotList; import bisq.core.dao.governance.ballot.BallotListService; @@ -34,6 +36,7 @@ import bisq.core.dao.governance.proposal.ProposalListPresentation; import bisq.core.dao.governance.proposal.compensation.CompensationProposal; import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposal; import bisq.core.dao.governance.proposal.param.ChangeParamProposal; +import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposal; import bisq.core.dao.governance.proposal.role.BondedRoleProposal; import bisq.core.dao.governance.role.BondedRole; import bisq.core.dao.governance.role.BondedRolesService; @@ -49,9 +52,12 @@ import bisq.core.dao.state.governance.ConfiscateBond; import bisq.core.dao.state.governance.ParamChange; import bisq.core.dao.state.period.DaoPhase; import bisq.core.dao.state.period.PeriodService; +import bisq.core.locale.CurrencyUtil; import bisq.network.p2p.storage.P2PDataStorage; +import bisq.common.proto.persistable.PersistedDataHost; +import bisq.common.storage.Storage; import bisq.common.util.Utilities; import javax.inject.Inject; @@ -89,7 +95,7 @@ import static com.google.common.base.Preconditions.checkArgument; * the missing blindVotes from the network. */ @Slf4j -public class VoteResultService implements BsqStateListener, DaoSetupService { +public class VoteResultService implements BsqStateListener, DaoSetupService, PersistedDataHost { private final VoteRevealService voteRevealService; private final ProposalListPresentation proposalListPresentation; private final BsqStateService bsqStateService; @@ -98,15 +104,17 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { private final BlindVoteListService blindVoteListService; private final BondedRolesService bondedRolesService; private final IssuanceService issuanceService; + private final AssetService assetService; + private final MissingDataRequestService missingDataRequestService; @Getter private final ObservableList voteResultExceptions = FXCollections.observableArrayList(); - // Use a list to have order by cycle - // TODO persist, PB + private final Storage evaluatedProposalStorage; + private Storage decryptedBallotsWithMeritsStorage; @Getter - private final Set allEvaluatedProposals = new HashSet<>(); + private final EvaluatedProposalList evaluatedProposalList = new EvaluatedProposalList(); @Getter - private final Set allDecryptedBallotsWithMerits = new HashSet<>(); + private final DecryptedBallotsWithMeritsList decryptedBallotsWithMeritsList = new DecryptedBallotsWithMeritsList(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -121,7 +129,11 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { BallotListService ballotListService, BlindVoteListService blindVoteListService, BondedRolesService bondedRolesService, - IssuanceService issuanceService) { + IssuanceService issuanceService, + AssetService assetService, + MissingDataRequestService missingDataRequestService, + Storage evaluatedProposalStorage, + Storage decryptedBallotsWithMeritsStorage) { this.voteRevealService = voteRevealService; this.proposalListPresentation = proposalListPresentation; this.bsqStateService = bsqStateService; @@ -130,6 +142,32 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { this.blindVoteListService = blindVoteListService; this.bondedRolesService = bondedRolesService; this.issuanceService = issuanceService; + this.assetService = assetService; + this.missingDataRequestService = missingDataRequestService; + this.evaluatedProposalStorage = evaluatedProposalStorage; + this.decryptedBallotsWithMeritsStorage = decryptedBallotsWithMeritsStorage; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PersistedDataHost + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void readPersisted() { + if (BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq()) { + EvaluatedProposalList persisted1 = evaluatedProposalStorage.initAndGetPersisted(evaluatedProposalList, 100); + if (persisted1 != null) { + evaluatedProposalList.clear(); + evaluatedProposalList.addAll(persisted1.getList()); + } + + DecryptedBallotsWithMeritsList persisted2 = decryptedBallotsWithMeritsStorage.initAndGetPersisted(decryptedBallotsWithMeritsList, 100); + if (persisted2 != null) { + decryptedBallotsWithMeritsList.clear(); + decryptedBallotsWithMeritsList.addAll(persisted2.getList()); + } + } } @@ -148,19 +186,6 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { } - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public Set getAllAcceptedEvaluatedProposals() { - return getAcceptedEvaluatedProposals(allEvaluatedProposals); - } - - public Set getAllRejectedEvaluatedProposals() { - return getRejectedEvaluatedProposals(allEvaluatedProposals); - } - - /////////////////////////////////////////////////////////////////////////////////////////// // BsqStateListener /////////////////////////////////////////////////////////////////////////////////////////// @@ -187,7 +212,10 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { private void maybeCalculateVoteResult(int chainHeight) { if (isInVoteResultPhase(chainHeight)) { Set decryptedBallotsWithMeritsSet = getDecryptedBallotsWithMeritsSet(chainHeight); - allDecryptedBallotsWithMerits.addAll(decryptedBallotsWithMeritsSet); + decryptedBallotsWithMeritsSet.stream() + .filter(e -> !decryptedBallotsWithMeritsList.getList().contains(e)) + .forEach(decryptedBallotsWithMeritsList::add); + persistDecryptedBallotsWithMerits(); if (!decryptedBallotsWithMeritsSet.isEmpty()) { // From the decryptedBallotsWithMerits we create a map with the hash of the blind vote list as key and the @@ -215,20 +243,29 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { Set acceptedEvaluatedProposals = getAcceptedEvaluatedProposals(evaluatedProposals); applyAcceptedProposals(acceptedEvaluatedProposals, chainHeight); - allEvaluatedProposals.addAll(evaluatedProposals); + evaluatedProposals.stream() + .filter(e -> !evaluatedProposalList.getList().contains(e)) + .forEach(evaluatedProposalList::add); + persistEvaluatedProposals(); log.info("processAllVoteResults completed"); } else { log.warn("Our list of received blind votes do not match the list from the majority of voters."); // TODO request missing blind votes } - } catch (VoteResultException e) { + } catch (VoteResultException.ValidationException e) { + log.error(e.toString()); + e.printStackTrace(); + voteResultExceptions.add(e); + } catch (VoteResultException.ConsensusException e) { log.error(e.toString()); e.printStackTrace(); //TODO notify application of that case (e.g. add error handler) // The vote cycle is invalid as conflicting data views of the blind vote data exist and the winner // did not reach super majority of 80%. + + voteResultExceptions.add(e); } } else { log.info("There have not been any votes in that cycle. chainHeight={}", chainHeight); @@ -274,28 +311,44 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { .findAny(); if (optionalBlindVote.isPresent()) { BlindVote blindVote = optionalBlindVote.get(); - VoteWithProposalTxIdList voteWithProposalTxIdList = VoteResultConsensus.decryptVotes(blindVote.getEncryptedVotes(), secretKey); - MeritList meritList = MeritConsensus.decryptMeritList(blindVote.getEncryptedMeritList(), secretKey); - - // We lookup for the proposals we have in our local list which match the txId from the - // voteWithProposalTxIdList and create a ballot list with the proposal and the vote from - // the voteWithProposalTxIdList - BallotList ballotList = createBallotList(voteWithProposalTxIdList); - return new DecryptedBallotsWithMerits(hashOfBlindVoteList, voteRevealTxId, blindVoteTxId, blindVoteStake, ballotList, meritList); + try { + VoteWithProposalTxIdList voteWithProposalTxIdList = VoteResultConsensus.decryptVotes(blindVote.getEncryptedVotes(), secretKey); + MeritList meritList = MeritConsensus.decryptMeritList(blindVote.getEncryptedMeritList(), secretKey); + // We lookup for the proposals we have in our local list which match the txId from the + // voteWithProposalTxIdList and create a ballot list with the proposal and the vote from + // the voteWithProposalTxIdList + BallotList ballotList = createBallotList(voteWithProposalTxIdList); + return new DecryptedBallotsWithMerits(hashOfBlindVoteList, voteRevealTxId, blindVoteTxId, blindVoteStake, ballotList, meritList); + } catch (VoteResultException.MissingBallotException missingBallotException) { + //TODO handle case that we are missing proposals + log.warn("We are missing proposals to create the vote result: " + missingBallotException.toString()); + missingDataRequestService.addVoteResultException(missingBallotException); + voteResultExceptions.add(missingBallotException); + return null; + } catch (VoteResultException.DecryptionException decryptionException) { + log.error("Could not decrypt data: " + decryptionException.toString()); + voteResultExceptions.add(decryptionException); + return null; + } } else { //TODO handle recovering log.warn("We have a blindVoteTx but we do not have the corresponding blindVote in our local list.\n" + "That can happen if the blindVote item was not properly broadcast. We will go on " + "and see if that blindVote was part of the majority data view. If so we should " + "recover the missing blind vote by a request to our peers. blindVoteTxId={}", blindVoteTxId); + + VoteResultException.MissingBlindVoteDataException voteResultException = new VoteResultException.MissingBlindVoteDataException(blindVoteTxId); + missingDataRequestService.addVoteResultException(voteResultException); + voteResultExceptions.add(voteResultException); return null; } - } catch (MissingBallotException e) { - //TODO handle case that we are missing proposals - log.error("We are missing proposals to create the vote result: " + e.toString()); + } catch (VoteResultException.ValidationException e) { + log.error("Could not create DecryptedBallotsWithMerits because of voteResultValidationException: " + e.toString()); + voteResultExceptions.add(e); return null; } catch (Throwable e) { - log.error("Could not create DecryptedBallotsWithMerits: " + e.toString()); + log.error("Could not create DecryptedBallotsWithMerits because of an unknown exception: " + e.toString()); + voteResultExceptions.add(new VoteResultException(e)); return null; } }) @@ -303,14 +356,15 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { .collect(Collectors.toSet()); } - private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxIdList) throws MissingBallotException { + private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxIdList) + throws VoteResultException.MissingBallotException { // We convert the list to a map with proposalTxId as key and the vote as value Map voteByTxIdMap = voteWithProposalTxIdList.stream() .filter(voteWithProposalTxId -> voteWithProposalTxId.getVote() != null) .collect(Collectors.toMap(VoteWithProposalTxId::getProposalTxId, VoteWithProposalTxId::getVote)); // We make a map with proposalTxId as key and the ballot as value out of our stored ballot list - Map ballotByTxIdMap = ballotListService.getBallotList().stream() + Map ballotByTxIdMap = ballotListService.getValidatedBallotList().stream() .collect(Collectors.toMap(Ballot::getTxId, ballot -> ballot)); List missingBallots = new ArrayList<>(); @@ -339,7 +393,7 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { .collect(Collectors.toList()); if (!missingBallots.isEmpty()) - throw new MissingBallotException(ballots, missingBallots); + throw new VoteResultException.MissingBallotException(ballots, missingBallots); // Let's keep the data more deterministic by sorting it by txId. Though we are not using the sorting. ballots.sort(Comparator.comparing(Ballot::getTxId)); @@ -366,7 +420,8 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { return map; } - private byte[] getMajorityBlindVoteListHash(Map map) throws VoteResultException { + private byte[] getMajorityBlindVoteListHash(Map map) + throws VoteResultException.ValidationException, VoteResultException.ConsensusException { List list = map.entrySet().stream() .map(entry -> new HashWithStake(entry.getKey().bytes, entry.getValue())) .collect(Collectors.toList()); @@ -545,6 +600,7 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { applyParamChange(acceptedEvaluatedProposals, chainHeight); applyBondedRole(acceptedEvaluatedProposals, chainHeight); applyConfiscateBond(acceptedEvaluatedProposals, chainHeight); + applyRemoveAsset(acceptedEvaluatedProposals, chainHeight); } private void applyIssuance(Set acceptedEvaluatedProposals, int chainHeight) { @@ -601,7 +657,6 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { StringBuilder sb = new StringBuilder(); sb.append("\n################################################################################\n"); sb.append("We changed a parameter. ProposalTxId=").append(changeParamProposal.getTxId()) - .append("\nfor changeParamProposal with UID ").append(changeParamProposal.getTxId()) .append("\nParam: ").append(changeParamProposal.getParam().name()) .append(" new value: ").append(changeParamProposal.getParamValue()) .append("\n################################################################################\n"); @@ -627,7 +682,6 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { StringBuilder sb = new StringBuilder(); sb.append("\n################################################################################\n"); sb.append("We added a bonded role. ProposalTxId=").append(bondedRoleProposal.getTxId()) - .append("\nfor bondedRoleProposal with UID ").append(bondedRoleProposal.getTxId()) .append("\nBondedRole: ").append(bondedRole.getDisplayString()) .append("\n################################################################################\n"); log.info(sb.toString()); @@ -643,8 +697,7 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { StringBuilder sb = new StringBuilder(); sb.append("\n################################################################################\n"); - sb.append("We confiscated bond. ProposalTxId=").append(confiscateBondProposal.getTxId()) - .append("\nfor confiscateBondProposal with UID ").append(confiscateBondProposal.getTxId()) + sb.append("We confiscated a bond. ProposalTxId=").append(confiscateBondProposal.getTxId()) .append("\nHashOfBondId: ").append(Utilities.encodeToHex(confiscateBondProposal.getHash())) .append("\n################################################################################\n"); log.info(sb.toString()); @@ -652,6 +705,23 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { }); } + private void applyRemoveAsset(Set acceptedEvaluatedProposals, int chainHeight) { + acceptedEvaluatedProposals.forEach(evaluatedProposal -> { + if (evaluatedProposal.getProposal() instanceof RemoveAssetProposal) { + RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) evaluatedProposal.getProposal(); + String tickerSymbol = removeAssetProposal.getTickerSymbol(); + assetService.addToRemovedAssetsListByVoting(tickerSymbol); + + StringBuilder sb = new StringBuilder(); + sb.append("\n################################################################################\n"); + sb.append("We removed an asset. ProposalTxId=").append(removeAssetProposal.getTxId()) + .append("\nAsset: ").append(CurrencyUtil.getNameByCode(tickerSymbol)) + .append("\n################################################################################\n"); + log.info(sb.toString()); + } + }); + } + private Set getAcceptedEvaluatedProposals(Set evaluatedProposals) { return evaluatedProposals.stream() .filter(EvaluatedProposal::isAccepted) @@ -668,6 +738,14 @@ public class VoteResultService implements BsqStateListener, DaoSetupService { return periodService.getFirstBlockOfPhase(chainHeight, DaoPhase.Phase.RESULT) == chainHeight; } + private void persistEvaluatedProposals() { + evaluatedProposalStorage.queueUpForSave(20); + } + + private void persistDecryptedBallotsWithMerits() { + decryptedBallotsWithMeritsStorage.queueUpForSave(20); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Inner classes diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java index ae6bd0d8cf..f25d29dd06 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java @@ -72,7 +72,7 @@ public class IssuanceService { StringBuilder sb = new StringBuilder(); sb.append("\n################################################################################\n"); sb.append("We issued new BSQ to tx with ID ").append(txOutput.getTxId()) - .append("\nfor compensationProposal with UID ").append(compensationProposal.getTxId()) + .append("\nIssued BSQ: ").append(compensationProposal.getRequestedBsq()) .append("\n################################################################################\n"); log.info(sb.toString()); } else { diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java index 0eecba7c4d..b15956e86a 100644 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java +++ b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java @@ -18,12 +18,12 @@ package bisq.core.dao.governance.votereveal; import bisq.core.btc.exceptions.TransactionVerificationException; +import bisq.core.btc.exceptions.TxBroadcastException; +import bisq.core.btc.exceptions.TxMalleabilityException; import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcastException; import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.TxMalleabilityException; import bisq.core.btc.wallet.WalletsManager; import bisq.core.dao.DaoSetupService; import bisq.core.dao.governance.blindvote.BlindVote; diff --git a/core/src/main/java/bisq/core/dao/node/BsqNode.java b/core/src/main/java/bisq/core/dao/node/BsqNode.java index 1ff7b43a08..3cb8d0d7cd 100644 --- a/core/src/main/java/bisq/core/dao/node/BsqNode.java +++ b/core/src/main/java/bisq/core/dao/node/BsqNode.java @@ -155,7 +155,11 @@ public abstract class BsqNode implements DaoSetupService { @SuppressWarnings("WeakerAccess") protected int getStartBlockHeight() { - final int startBlockHeight = Math.max(genesisBlockHeight, bsqStateService.getChainHeight()); + int chainHeight = bsqStateService.getChainHeight(); + int startBlockHeight = chainHeight; + if (chainHeight > genesisBlockHeight) + startBlockHeight = chainHeight + 1; + log.info("Start parse blocks:\n" + " Start block height={}\n" + " Genesis txId={}\n" + @@ -164,7 +168,7 @@ public abstract class BsqNode implements DaoSetupService { startBlockHeight, genesisTxId, genesisBlockHeight, - bsqStateService.getChainHeight()); + chainHeight); return startBlockHeight; } 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 05b9ac7db8..945b693043 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 @@ -19,7 +19,7 @@ package bisq.core.dao.node.full; import bisq.core.dao.node.BsqNode; import bisq.core.dao.node.full.network.FullNodeNetworkService; -import bisq.core.dao.node.json.JsonBlockChainExporter; +import bisq.core.dao.node.json.ExportJsonFilesService; import bisq.core.dao.node.parser.BlockParser; import bisq.core.dao.node.parser.exceptions.BlockNotConnectingException; import bisq.core.dao.state.BsqStateService; @@ -40,7 +40,7 @@ import lombok.extern.slf4j.Slf4j; /** * Main class for a full node which have Bitcoin Core with rpc running and does the blockchain lookup itself. * It also provides the BSQ transactions to lite nodes on request and broadcasts new BSQ blocks. - * + *

* TODO request p2p network data again after parsing is complete to be sure that in case we missed data during parsing * we get it added. */ @@ -49,7 +49,7 @@ public class FullNode extends BsqNode { private final RpcService rpcService; private final FullNodeNetworkService fullNodeNetworkService; - private final JsonBlockChainExporter jsonBlockChainExporter; + private final ExportJsonFilesService exportJsonFilesService; private boolean addBlockHandlerAdded; @@ -64,12 +64,12 @@ public class FullNode extends BsqNode { SnapshotManager snapshotManager, P2PService p2PService, RpcService rpcService, - JsonBlockChainExporter jsonBlockChainExporter, + ExportJsonFilesService exportJsonFilesService, FullNodeNetworkService fullNodeNetworkService) { super(blockParser, bsqStateService, snapshotManager, p2PService); this.rpcService = rpcService; - this.jsonBlockChainExporter = jsonBlockChainExporter; + this.exportJsonFilesService = exportJsonFilesService; this.fullNodeNetworkService = fullNodeNetworkService; } @@ -80,6 +80,8 @@ public class FullNode extends BsqNode { @Override public void start() { + fullNodeNetworkService.start(); + rpcService.setup(() -> { super.onInitialized(); startParseBlocks(); @@ -88,7 +90,7 @@ public class FullNode extends BsqNode { } public void shutDown() { - jsonBlockChainExporter.shutDown(); + exportJsonFilesService.shutDown(); fullNodeNetworkService.shutDown(); } @@ -147,18 +149,18 @@ public class FullNode extends BsqNode { } private void onNewBlock(Block block) { - jsonBlockChainExporter.maybeExport(); + exportJsonFilesService.exportToJson(); if (p2pNetworkReady && parseBlockchainComplete) fullNodeNetworkService.publishNewBlock(block); } - private void parseBlocksIfNewBlockAvailable(int chainHeadHeight) { - rpcService.requestChainHeadHeight(newChainHeadHeight -> { - if (newChainHeadHeight > chainHeadHeight) { + private void parseBlocksIfNewBlockAvailable(int chainHeight) { + rpcService.requestChainHeadHeight(newChainHeight -> { + if (newChainHeight > chainHeight) { log.info("During parsing new blocks have arrived. We parse again with those missing blocks." + - "ChainHeadHeight={}, newChainHeadHeight={}", chainHeadHeight, newChainHeadHeight); - parseBlocksOnHeadHeight(chainHeadHeight, newChainHeadHeight); + "ChainHeadHeight={}, newChainHeadHeight={}", chainHeight, newChainHeight); + parseBlocksOnHeadHeight(chainHeight + 1, newChainHeight); } else { log.info("parseBlocksIfNewBlockAvailable did not result in a new block, so we complete."); onParseBlockChainComplete(); @@ -169,27 +171,26 @@ public class FullNode extends BsqNode { private void requestChainHeadHeightAndParseBlocks(int startBlockHeight) { log.info("requestChainHeadHeightAndParseBlocks with startBlockHeight={}", startBlockHeight); - rpcService.requestChainHeadHeight(chainHeadHeight -> parseBlocksOnHeadHeight(startBlockHeight, chainHeadHeight), + rpcService.requestChainHeadHeight(chainHeight -> parseBlocksOnHeadHeight(startBlockHeight, chainHeight), this::handleError); } - private void parseBlocksOnHeadHeight(int startBlockHeight, int chainHeadHeight) { - if (startBlockHeight <= chainHeadHeight) { - log.info("parseBlocks with startBlockHeight={} and chainHeadHeight={}", startBlockHeight, chainHeadHeight); + private void parseBlocksOnHeadHeight(int startBlockHeight, int chainHeight) { + if (startBlockHeight <= chainHeight) { + log.info("parseBlocks with startBlockHeight={} and chainHeight={}", startBlockHeight, chainHeight); parseBlocks(startBlockHeight, - chainHeadHeight, + chainHeight, this::onNewBlock, () -> { // We are done but it might be that new blocks have arrived in the meantime, - // so we try again with startBlockHeight set to current chainHeadHeight + // so we try again with startBlockHeight set to current chainHeight // We also set up the listener in the else main branch where we check // if we are at chainTip, so do not include here another check as it would // not trigger the listener registration. - parseBlocksIfNewBlockAvailable(chainHeadHeight); + parseBlocksIfNewBlockAvailable(chainHeight); }, throwable -> { if (throwable instanceof BlockNotConnectingException) { - int blockHeightOfLastBlock = bsqStateService.getBlockHeightOfLastBlock(); - requestChainHeadHeightAndParseBlocks(blockHeightOfLastBlock); + startReOrgFromLastSnapshot(); } else { handleError(throwable); } @@ -203,15 +204,15 @@ public class FullNode extends BsqNode { } private void parseBlocks(int startBlockHeight, - int chainHeadHeight, + int chainHeight, Consumer newBlockHandler, ResultHandler resultHandler, Consumer errorHandler) { - parseBlock(startBlockHeight, chainHeadHeight, newBlockHandler, resultHandler, errorHandler); + parseBlock(startBlockHeight, chainHeight, newBlockHandler, resultHandler, errorHandler); } // Recursively request and parse all blocks - private void parseBlock(int blockHeight, int chainHeadHeight, + private void parseBlock(int blockHeight, int chainHeight, Consumer newBlockHandler, ResultHandler resultHandler, Consumer errorHandler) { rpcService.requestBtcBlock(blockHeight, @@ -221,10 +222,10 @@ public class FullNode extends BsqNode { Block block = blockParser.parseBlock(rawBlock); newBlockHandler.accept(block); - // Increment blockHeight and recursively call parseBlockAsync until we reach chainHeadHeight - if (blockHeight < chainHeadHeight) { + // Increment blockHeight and recursively call parseBlockAsync until we reach chainHeight + if (blockHeight < chainHeight) { final int newBlockHeight = blockHeight + 1; - parseBlock(newBlockHeight, chainHeadHeight, newBlockHandler, resultHandler, errorHandler); + parseBlock(newBlockHeight, chainHeight, newBlockHandler, resultHandler, errorHandler); } else { // We are done resultHandler.handleResult(); @@ -232,15 +233,20 @@ public class FullNode extends BsqNode { } catch (BlockNotConnectingException e) { errorHandler.accept(e); } + } else { + log.info("Block was already added height=", rawBlock.getHeight()); } }, errorHandler); } private void handleError(Throwable throwable) { - final String errorMessage = "Initializing FullNode failed: Error=" + throwable.toString(); + String errorMessage = "An error occurred: Error=" + throwable.toString(); log.error(errorMessage); + if (throwable instanceof BlockNotConnectingException) + startReOrgFromLastSnapshot(); + if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage(errorMessage); } 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 67e4361a38..febb7d9956 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 @@ -162,6 +162,11 @@ public class RpcService { daemon.addBlockListener(new BlockListener() { @Override public void blockDetected(com.neemre.btcdcli4j.core.domain.RawBlock rawBtcBlock) { + if (rawBtcBlock.getHeight() == null || rawBtcBlock.getHeight() == 0) { + log.warn("We received a RawBlock with no data. blockHash={}", rawBtcBlock.getHash()); + return; + } + try { log.info("New block received: height={}, id={}", rawBtcBlock.getHeight(), rawBtcBlock.getHash()); List txList = rawBtcBlock.getTx().stream() @@ -183,9 +188,9 @@ public class RpcService { void requestChainHeadHeight(Consumer resultHandler, Consumer errorHandler) { ListenableFuture future = executor.submit(client::getBlockCount); - Futures.addCallback(future, new FutureCallback() { - public void onSuccess(Integer chainHeadHeight) { - UserThread.execute(() -> resultHandler.accept(chainHeadHeight)); + Futures.addCallback(future, new FutureCallback<>() { + public void onSuccess(Integer chainHeight) { + UserThread.execute(() -> resultHandler.accept(chainHeight)); } public void onFailure(@NotNull Throwable throwable) { @@ -204,7 +209,7 @@ public class RpcService { List txList = rawBtcBlock.getTx().stream() .map(e -> getTxFromRawTransaction(e, rawBtcBlock)) .collect(Collectors.toList()); - log.info("requestBtcBlock with all txs took {} ms at blockHeight {}; txList.size={}", + log.debug("requestBtcBlock with all txs took {} ms at blockHeight {}; txList.size={}", System.currentTimeMillis() - startTs, blockHeight, txList.size()); return new RawBlock(rawBtcBlock.getHeight(), rawBtcBlock.getTime() * 1000, // rawBtcBlock.getTime() is in sec but we want ms @@ -213,7 +218,7 @@ public class RpcService { ImmutableList.copyOf(txList)); }); - Futures.addCallback(future, new FutureCallback() { + Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(RawBlock block) { UserThread.execute(() -> resultHandler.accept(block)); @@ -221,6 +226,7 @@ public class RpcService { @Override public void onFailure(@NotNull Throwable throwable) { + log.error("Error at requestBtcBlock: blockHeight={}", blockHeight); UserThread.execute(() -> errorHandler.accept(throwable)); } }); 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 4f19f5962c..573d5145c9 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 @@ -17,12 +17,18 @@ package bisq.core.dao.node.full.network; +import bisq.core.dao.governance.blindvote.BlindVoteListService; +import bisq.core.dao.governance.blindvote.network.messages.RepublishGovernanceDataRequest; +import bisq.core.dao.governance.blindvote.storage.BlindVotePayload; +import bisq.core.dao.governance.proposal.ProposalService; +import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; import bisq.core.dao.node.messages.GetBlocksRequest; import bisq.core.dao.node.messages.NewBlockBroadcastMessage; import bisq.core.dao.state.BsqStateService; import bisq.core.dao.state.blockchain.Block; import bisq.core.dao.state.blockchain.RawBlock; +import bisq.network.p2p.P2PService; import bisq.network.p2p.network.Connection; import bisq.network.p2p.network.MessageListener; import bisq.network.p2p.network.NetworkNode; @@ -35,8 +41,12 @@ import bisq.common.proto.network.NetworkEnvelope; import javax.inject.Inject; +import javafx.collections.ObservableList; + import java.util.HashMap; import java.util.Map; +import java.util.Random; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; @@ -58,6 +68,9 @@ public class FullNodeNetworkService implements MessageListener, PeerManager.List private final NetworkNode networkNode; private final PeerManager peerManager; private final Broadcaster broadcaster; + private final BlindVoteListService blindVoteListService; + private final ProposalService proposalService; + private final P2PService p2PService; private final BsqStateService bsqStateService; // Key is connection UID @@ -73,20 +86,28 @@ public class FullNodeNetworkService implements MessageListener, PeerManager.List public FullNodeNetworkService(NetworkNode networkNode, PeerManager peerManager, Broadcaster broadcaster, + BlindVoteListService blindVoteListService, + ProposalService proposalService, + P2PService p2PService, BsqStateService bsqStateService) { this.networkNode = networkNode; this.peerManager = peerManager; this.broadcaster = broadcaster; + this.blindVoteListService = blindVoteListService; + this.proposalService = proposalService; + this.p2PService = p2PService; this.bsqStateService = bsqStateService; - - networkNode.addMessageListener(this); - peerManager.addListener(this); } /////////////////////////////////////////////////////////////////////////////////////////// // API /////////////////////////////////////////////////////////////////////////////////////////// + public void start() { + networkNode.addMessageListener(this); + peerManager.addListener(this); + } + @SuppressWarnings("Duplicates") public void shutDown() { Log.traceCall(); @@ -173,6 +194,39 @@ public class FullNodeNetworkService implements MessageListener, PeerManager.List } else { log.warn("We have stopped already. We ignore that onMessage call."); } + } else if (networkEnvelope instanceof RepublishGovernanceDataRequest) { + ObservableList blindVotePayloads = blindVoteListService.getBlindVotePayloads(); + blindVotePayloads + .forEach(blindVotePayload -> { + // We want a random delay between 0.1 and 30 sec. depending on the number of items + int delay = Math.max(100, Math.min(30_000, new Random().nextInt(blindVotePayloads.size() * 500))); + UserThread.runAfter(() -> { + boolean success = p2PService.addPersistableNetworkPayload(blindVotePayload, true); + String txId = blindVotePayload.getBlindVote().getTxId(); + if (success) { + log.info("We received a RepublishGovernanceDataRequest and re-published a blindVotePayload to " + + "the P2P network as append only data. blindVoteTxId={}", txId); + } else { + log.error("Adding of blindVotePayload to P2P network failed. blindVoteTxId={}", txId); + } + }, delay, TimeUnit.MILLISECONDS); + }); + + ObservableList proposalPayloads = proposalService.getProposalPayloads(); + proposalPayloads.forEach(proposalPayload -> { + // We want a random delay between 0.1 and 30 sec. depending on the number of items + int delay = Math.max(100, Math.min(30_000, new Random().nextInt(proposalPayloads.size() * 500))); + UserThread.runAfter(() -> { + boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, true); + String txId = proposalPayload.getProposal().getTxId(); + if (success) { + log.info("We received a RepublishGovernanceDataRequest and re-published a proposalPayload to " + + "the P2P network as append only data. proposalTxId={}", txId); + } else { + log.error("Adding of proposalPayload to P2P network failed. proposalTxId={}", txId); + } + }, delay, TimeUnit.MILLISECONDS); + }); } } } 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 5714d87f51..9791030f32 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 @@ -95,10 +95,12 @@ class GetBlocksRequestHandler { List rawBlocks = blocks.stream().map(RawBlock::fromBlock).collect(Collectors.toList()); final GetBlocksResponse getBlocksResponse = new GetBlocksResponse(rawBlocks, getBlocksRequest.getNonce()); log.debug("getBlocksResponse " + getBlocksResponse.getRequestNonce()); - + log.info("Received getBlocksResponse from {} for blocks from height {}", + connection.getPeersNodeAddressOptional(), getBlocksRequest.getFromBlockHeight()); if (timeoutTimer == null) { timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions - String errorMessage = "A timeout occurred for getBlocksResponse:" + getBlocksResponse + + String errorMessage = "A timeout occurred for getBlocksResponse.requestNonce:" + + getBlocksResponse.getRequestNonce() + " on connection:" + connection; handleFault(errorMessage, CloseConnectionReason.SEND_MSG_TIMEOUT, connection); }, @@ -110,8 +112,8 @@ class GetBlocksRequestHandler { @Override public void onSuccess(Connection connection) { if (!stopped) { - log.trace("Send DataResponse to {} succeeded. getBlocksResponse={}", - connection.getPeersNodeAddressOptional(), getBlocksResponse); + log.info("Send DataResponse to {} succeeded. getBlocksResponse.getBlocks().size()={}", + connection.getPeersNodeAddressOptional(), getBlocksResponse.getBlocks().size()); cleanup(); listener.onComplete(); } else { diff --git a/core/src/main/java/bisq/core/dao/node/json/ExportJsonFilesService.java b/core/src/main/java/bisq/core/dao/node/json/ExportJsonFilesService.java new file mode 100644 index 0000000000..019a1cd32e --- /dev/null +++ b/core/src/main/java/bisq/core/dao/node/json/ExportJsonFilesService.java @@ -0,0 +1,268 @@ +/* + * 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.node.json; + +import bisq.core.dao.DaoOptionKeys; +import bisq.core.dao.DaoSetupService; +import bisq.core.dao.state.BsqState; +import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.blockchain.Block; +import bisq.core.dao.state.blockchain.PubKeyScript; +import bisq.core.dao.state.blockchain.Tx; +import bisq.core.dao.state.blockchain.TxOutput; +import bisq.core.dao.state.blockchain.TxType; + +import bisq.common.storage.FileUtil; +import bisq.common.storage.JsonFileManager; +import bisq.common.storage.Storage; +import bisq.common.util.Utilities; + +import org.bitcoinj.core.Utils; + +import com.google.inject.Inject; + +import javax.inject.Named; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; + +import java.nio.file.Paths; + +import java.io.File; +import java.io.IOException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import lombok.extern.slf4j.Slf4j; + +import org.jetbrains.annotations.NotNull; + +@Slf4j +public class ExportJsonFilesService implements DaoSetupService { + private final BsqStateService bsqStateService; + private final File storageDir; + private final boolean dumpBlockchainData; + + private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter", + 1, 1, 1200); + private JsonFileManager txFileManager, txOutputFileManager, bsqStateFileManager; + + @Inject + public ExportJsonFilesService(BsqStateService bsqStateService, + @Named(Storage.STORAGE_DIR) File storageDir, + @Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) { + this.bsqStateService = bsqStateService; + this.storageDir = storageDir; + this.dumpBlockchainData = dumpBlockchainData; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // DaoSetupService + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void addListeners() { + } + + @Override + public void start() { + if (dumpBlockchainData) { + File jsonDir = new File(Paths.get(storageDir.getAbsolutePath(), "json").toString()); + File txDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "tx").toString()); + File txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "txo").toString()); + File bsqStateDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "all").toString()); + try { + if (txDir.exists()) + FileUtil.deleteDirectory(txDir); + if (txOutputDir.exists()) + FileUtil.deleteDirectory(txOutputDir); + if (bsqStateDir.exists()) + FileUtil.deleteDirectory(bsqStateDir); + if (jsonDir.exists()) + FileUtil.deleteDirectory(jsonDir); + } catch (IOException e) { + log.error(e.toString()); + e.printStackTrace(); + } + + if (!jsonDir.mkdir()) + log.warn("make jsonDir failed.\njsonDir=" + jsonDir.getAbsolutePath()); + + if (!txDir.mkdir()) + log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath()); + + if (!txOutputDir.mkdir()) + log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath()); + + if (!bsqStateDir.mkdir()) + log.warn("make bsqStateDir failed.\nbsqStateDir=" + bsqStateDir.getAbsolutePath()); + + txFileManager = new JsonFileManager(txDir); + txOutputFileManager = new JsonFileManager(txOutputDir); + bsqStateFileManager = new JsonFileManager(bsqStateDir); + } + } + + public void shutDown() { + if (dumpBlockchainData) { + txFileManager.shutDown(); + txOutputFileManager.shutDown(); + bsqStateFileManager.shutDown(); + } + } + + public void exportToJson() { + if (dumpBlockchainData) { + // We store the data we need once we write the data to disk (in the thread) locally. + // Access to bsqStateService is single threaded, we must not access bsqStateService from the thread. + List allJsonTxOutputs = new ArrayList<>(); + + List jsonTxs = bsqStateService.getTxStream() + .map(tx -> { + JsonTx jsonTx = getJsonTx(tx); + allJsonTxOutputs.addAll(jsonTx.getOutputs()); + return jsonTx; + }).collect(Collectors.toList()); + + BsqState bsqState = bsqStateService.getClone(); + List jsonBlockList = bsqState.getBlocks().stream() + .map(this::getJsonBlock) + .collect(Collectors.toList()); + JsonBlocks jsonBlocks = new JsonBlocks(bsqState.getChainHeight(), jsonBlockList); + + ListenableFuture future = executor.submit(() -> { + bsqStateFileManager.writeToDisc(Utilities.objectToJson(jsonBlocks), "blocks"); + allJsonTxOutputs.forEach(jsonTxOutput -> txOutputFileManager.writeToDisc(Utilities.objectToJson(jsonTxOutput), jsonTxOutput.getId())); + jsonTxs.forEach(jsonTx -> txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), jsonTx.getId())); + return null; + }); + + Futures.addCallback(future, new FutureCallback<>() { + public void onSuccess(Void ignore) { + log.trace("onSuccess"); + } + + public void onFailure(@NotNull Throwable throwable) { + log.error(throwable.toString()); + throwable.printStackTrace(); + } + }); + } + } + + private JsonBlock getJsonBlock(Block block) { + List jsonTxs = block.getTxs().stream() + .map(this::getJsonTx) + .collect(Collectors.toList()); + return new JsonBlock(block.getHeight(), + block.getTime(), + block.getHash(), + block.getPreviousBlockHash(), + jsonTxs); + } + + private JsonTx getJsonTx(Tx tx) { + JsonTxType jsonTxType = getJsonTxType(tx); + String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType); + return new JsonTx(tx.getId(), + tx.getBlockHeight(), + tx.getBlockHash(), + tx.getTime(), + getJsonTxInputs(tx), + getJsonTxOutputs(tx), + jsonTxType, + jsonTxTypeDisplayString, + bsqStateService.getBurntFee(tx.getId()), + tx.getUnlockBlockHeight()); + } + + private List getJsonTxInputs(Tx tx) { + return tx.getTxInputs().stream() + .map(txInput -> { + Optional optionalTxOutput = bsqStateService.getConnectedTxOutput(txInput); + if (optionalTxOutput.isPresent()) { + TxOutput connectedTxOutput = optionalTxOutput.get(); + boolean isBsqTxOutputType = bsqStateService.isBsqTxOutputType(connectedTxOutput); + return new JsonTxInput(txInput.getConnectedTxOutputIndex(), + txInput.getConnectedTxOutputTxId(), + connectedTxOutput.getValue(), + isBsqTxOutputType, + connectedTxOutput.getAddress(), + tx.getTime()); + } else { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private List getJsonTxOutputs(Tx tx) { + JsonTxType jsonTxType = getJsonTxType(tx); + String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType); + return tx.getTxOutputs().stream() + .map(txOutput -> { + boolean isBsqTxOutputType = bsqStateService.isBsqTxOutputType(txOutput); + long bsqAmount = isBsqTxOutputType ? txOutput.getValue() : 0; + long btcAmount = !isBsqTxOutputType ? txOutput.getValue() : 0; + PubKeyScript pubKeyScript = txOutput.getPubKeyScript(); + JsonScriptPubKey scriptPubKey = pubKeyScript != null ? new JsonScriptPubKey(pubKeyScript) : null; + JsonSpentInfo spentInfo = bsqStateService.getSpentInfo(txOutput).map(JsonSpentInfo::new).orElse(null); + JsonTxOutputType txOutputType = JsonTxOutputType.valueOf(txOutput.getTxOutputType().name()); + int lockTime = txOutput.getLockTime(); + String opReturn = txOutput.getOpReturnData() != null ? Utils.HEX.encode(txOutput.getOpReturnData()) : null; + boolean isUnspent = bsqStateService.isUnspent(txOutput.getKey()); + return new JsonTxOutput(tx.getId(), + txOutput.getIndex(), + bsqAmount, + btcAmount, + tx.getBlockHeight(), + isBsqTxOutputType, + bsqStateService.getBurntFee(tx.getId()), + txOutput.getAddress(), + scriptPubKey, + spentInfo, + tx.getTime(), + jsonTxType, + jsonTxTypeDisplayString, + txOutputType, + txOutputType.getDisplayString(), + opReturn, + lockTime, + isUnspent + ); + }) + .collect(Collectors.toList()); + } + + private String getJsonTxTypeDisplayString(JsonTxType jsonTxType) { + return jsonTxType != null ? jsonTxType.getDisplayString() : ""; + } + + private JsonTxType getJsonTxType(Tx tx) { + TxType txType = tx.getTxType(); + return txType != null ? JsonTxType.valueOf(txType.name()) : null; + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/MissingBallotException.java b/core/src/main/java/bisq/core/dao/node/json/JsonBlock.java similarity index 56% rename from core/src/main/java/bisq/core/dao/governance/voteresult/MissingBallotException.java rename to core/src/main/java/bisq/core/dao/node/json/JsonBlock.java index a86d0c1ea4..99227b404d 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/MissingBallotException.java +++ b/core/src/main/java/bisq/core/dao/node/json/JsonBlock.java @@ -15,23 +15,17 @@ * along with Bisq. If not, see . */ -package bisq.core.dao.governance.voteresult; - -import bisq.core.dao.governance.ballot.Ballot; +package bisq.core.dao.node.json; import java.util.List; -import lombok.EqualsAndHashCode; import lombok.Value; -@EqualsAndHashCode(callSuper = true) @Value -public class MissingBallotException extends Exception { - private List existingBallots; - private List proposalTxIdsOfMissingBallots; - - public MissingBallotException(List existingBallots, List proposalTxIdsOfMissingBallots) { - this.existingBallots = existingBallots; - this.proposalTxIdsOfMissingBallots = proposalTxIdsOfMissingBallots; - } +class JsonBlock { + protected final int height; + protected final long time; // in ms + protected final String hash; + protected final String previousBlockHash; + private final List txs; } diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonBlockChainExporter.java b/core/src/main/java/bisq/core/dao/node/json/JsonBlockChainExporter.java deleted file mode 100644 index 25745e3556..0000000000 --- a/core/src/main/java/bisq/core/dao/node/json/JsonBlockChainExporter.java +++ /dev/null @@ -1,206 +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.dao.node.json; - -import bisq.core.dao.DaoOptionKeys; -import bisq.core.dao.state.BsqState; -import bisq.core.dao.state.BsqStateService; -import bisq.core.dao.state.blockchain.PubKeyScript; -import bisq.core.dao.state.blockchain.SpentInfo; -import bisq.core.dao.state.blockchain.Tx; -import bisq.core.dao.state.blockchain.TxOutput; -import bisq.core.dao.state.blockchain.TxType; - -import bisq.common.storage.FileUtil; -import bisq.common.storage.JsonFileManager; -import bisq.common.storage.Storage; -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Utils; - -import com.google.inject.Inject; - -import javax.inject.Named; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; - -import java.nio.file.Paths; - -import java.io.File; -import java.io.IOException; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.NotNull; - -@Slf4j -public class JsonBlockChainExporter { - private final BsqStateService bsqStateService; - private final boolean dumpBlockchainData; - - private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter", 1, 1, 1200); - private JsonFileManager txFileManager, txOutputFileManager, jsonFileManager; - - @Inject - public JsonBlockChainExporter(BsqStateService bsqStateService, - @Named(Storage.STORAGE_DIR) File storageDir, - @Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) { - this.bsqStateService = bsqStateService; - this.dumpBlockchainData = dumpBlockchainData; - - init(storageDir, dumpBlockchainData); - } - - private void init(@Named(Storage.STORAGE_DIR) File storageDir, @Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) { - if (dumpBlockchainData) { - File txDir = new File(Paths.get(storageDir.getAbsolutePath(), "tx").toString()); - File txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "txo").toString()); - File blockchainDir = new File(Paths.get(storageDir.getAbsolutePath(), "all").toString()); - try { - if (txDir.exists()) - FileUtil.deleteDirectory(txDir); - if (txOutputDir.exists()) - FileUtil.deleteDirectory(txOutputDir); - if (blockchainDir.exists()) - FileUtil.deleteDirectory(blockchainDir); - } catch (IOException e) { - e.printStackTrace(); - } - - if (!txDir.mkdir()) - log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath()); - - if (!txOutputDir.mkdir()) - log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath()); - - if (!blockchainDir.mkdir()) - log.warn("make blockchainDir failed.\nblockchainDir=" + blockchainDir.getAbsolutePath()); - - txFileManager = new JsonFileManager(txDir); - txOutputFileManager = new JsonFileManager(txOutputDir); - jsonFileManager = new JsonFileManager(blockchainDir); - } - } - - public void shutDown() { - if (dumpBlockchainData) { - txFileManager.shutDown(); - txOutputFileManager.shutDown(); - jsonFileManager.shutDown(); - } - } - - public void maybeExport() { - if (dumpBlockchainData) { - ListenableFuture future = executor.submit(() -> { - final BsqState bsqStateClone = bsqStateService.getClone(); - Map txMap = bsqStateService.getBlocksFromState(bsqStateClone).stream() - .filter(Objects::nonNull) - .flatMap(block -> block.getTxs().stream()) - .collect(Collectors.toMap(Tx::getId, tx -> tx)); - for (Tx tx : txMap.values()) { - String txId = tx.getId(); - final Optional optionalTxType = bsqStateService.getOptionalTxType(txId); - optionalTxType.ifPresent(txType1 -> { - JsonTxType txType = txType1 != TxType.UNDEFINED_TX_TYPE ? - JsonTxType.valueOf(txType1.name()) : null; - List outputs = new ArrayList<>(); - tx.getTxOutputs().forEach(txOutput -> { - final Optional optionalSpentInfo = bsqStateService.getSpentInfo(txOutput); - final boolean isBsqOutput = bsqStateService.isBsqTxOutputType(txOutput); - final PubKeyScript pubKeyScript = txOutput.getPubKeyScript(); - final JsonTxOutput outputForJson = new JsonTxOutput(txId, - txOutput.getIndex(), - isBsqOutput ? txOutput.getValue() : 0, - !isBsqOutput ? txOutput.getValue() : 0, - txOutput.getBlockHeight(), - isBsqOutput, - bsqStateService.getBurntFee(tx.getId()), - txOutput.getAddress(), - pubKeyScript != null ? new JsonScriptPubKey(pubKeyScript) : null, - optionalSpentInfo.map(JsonSpentInfo::new).orElse(null), - tx.getTime(), - txType, - txType != null ? txType.getDisplayString() : "", - txOutput.getOpReturnData() != null ? Utils.HEX.encode(txOutput.getOpReturnData()) : null - ); - outputs.add(outputForJson); - txOutputFileManager.writeToDisc(Utilities.objectToJson(outputForJson), outputForJson.getId()); - }); - - - List inputs = tx.getTxInputs().stream() - .map(txInput -> { - Optional optionalTxOutput = bsqStateService.getConnectedTxOutput(txInput); - if (optionalTxOutput.isPresent()) { - final TxOutput connectedTxOutput = optionalTxOutput.get(); - final boolean isBsqOutput = bsqStateService.isBsqTxOutputType(connectedTxOutput); - return new JsonTxInput(txInput.getConnectedTxOutputIndex(), - txInput.getConnectedTxOutputTxId(), - connectedTxOutput.getValue(), - isBsqOutput, - connectedTxOutput.getAddress(), - tx.getTime()); - } else { - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - final JsonTx jsonTx = new JsonTx(txId, - tx.getBlockHeight(), - tx.getBlockHash(), - tx.getTime(), - inputs, - outputs, - txType, - txType != null ? txType.getDisplayString() : "", - bsqStateService.getBurntFee(tx.getId())); - - txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), txId); - }); - } - - jsonFileManager.writeToDisc(Utilities.objectToJson(bsqStateClone), "BsqStateService"); - return null; - }); - - Futures.addCallback(future, new FutureCallback() { - public void onSuccess(Void ignore) { - log.trace("onSuccess"); - } - - public void onFailure(@NotNull Throwable throwable) { - log.error(throwable.toString()); - throwable.printStackTrace(); - } - }); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonBlocks.java b/core/src/main/java/bisq/core/dao/node/json/JsonBlocks.java new file mode 100644 index 0000000000..a87a7e5b04 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/node/json/JsonBlocks.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.core.dao.node.json; + +import java.util.List; + +import lombok.Value; + +@Value +class JsonBlocks { + private int chainHeight; + private final List blocks; +} diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonScriptPubKey.java b/core/src/main/java/bisq/core/dao/node/json/JsonScriptPubKey.java index c585b5dc87..a69ae8c397 100644 --- a/core/src/main/java/bisq/core/dao/node/json/JsonScriptPubKey.java +++ b/core/src/main/java/bisq/core/dao/node/json/JsonScriptPubKey.java @@ -26,14 +26,14 @@ import lombok.extern.slf4j.Slf4j; @Slf4j @Value -public class JsonScriptPubKey { +class JsonScriptPubKey { private final List addresses; private final String asm; private final String hex; private final int reqSigs; private final String type; - public JsonScriptPubKey(PubKeyScript pubKeyScript) { + JsonScriptPubKey(PubKeyScript pubKeyScript) { addresses = pubKeyScript.getAddresses(); asm = pubKeyScript.getAsm(); hex = pubKeyScript.getHex(); diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonSpentInfo.java b/core/src/main/java/bisq/core/dao/node/json/JsonSpentInfo.java index 14f82f5505..119579c761 100644 --- a/core/src/main/java/bisq/core/dao/node/json/JsonSpentInfo.java +++ b/core/src/main/java/bisq/core/dao/node/json/JsonSpentInfo.java @@ -22,12 +22,12 @@ import bisq.core.dao.state.blockchain.SpentInfo; import lombok.Value; @Value -public class JsonSpentInfo { +class JsonSpentInfo { private final long height; private final int inputIndex; private final String txId; - public JsonSpentInfo(SpentInfo spentInfo) { + JsonSpentInfo(SpentInfo spentInfo) { height = spentInfo.getBlockHeight(); inputIndex = spentInfo.getInputIndex(); txId = spentInfo.getTxId(); diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTx.java b/core/src/main/java/bisq/core/dao/node/json/JsonTx.java index 5ea7d38e4b..e68e74859e 100644 --- a/core/src/main/java/bisq/core/dao/node/json/JsonTx.java +++ b/core/src/main/java/bisq/core/dao/node/json/JsonTx.java @@ -23,9 +23,8 @@ import java.util.List; import lombok.Value; -//TODO sync up with data model @Value -public class JsonTx { +class JsonTx { private final String txVersion = Version.BSQ_TX_VERSION; private final String id; private final int blockHeight; @@ -36,4 +35,6 @@ public class JsonTx { private final JsonTxType txType; private final String txTypeDisplayString; private final long burntFee; + // If not set it is -1. LockTime of 0 is a valid value. + private final int unlockBlockHeight; } diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTxInput.java b/core/src/main/java/bisq/core/dao/node/json/JsonTxInput.java index 329e75a1e0..684d6031d8 100644 --- a/core/src/main/java/bisq/core/dao/node/json/JsonTxInput.java +++ b/core/src/main/java/bisq/core/dao/node/json/JsonTxInput.java @@ -21,14 +21,13 @@ import lombok.Value; import javax.annotation.concurrent.Immutable; -//TODO sync up with data model @Value @Immutable -public class JsonTxInput { - private final int spendingTxOutputIndex; - private final String spendingTxId; +class JsonTxInput { + private final int spendingTxOutputIndex; // connectedTxOutputIndex + private final String spendingTxId; // connectedTxOutputTxId private final long bsqAmount; - private final boolean isVerified; + private final boolean isVerified; // isBsqTxOutputType private final String address; private final long time; } diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTxOutput.java b/core/src/main/java/bisq/core/dao/node/json/JsonTxOutput.java index 069066a645..03e56d0d79 100644 --- a/core/src/main/java/bisq/core/dao/node/json/JsonTxOutput.java +++ b/core/src/main/java/bisq/core/dao/node/json/JsonTxOutput.java @@ -21,26 +21,34 @@ import bisq.common.app.Version; import lombok.Value; -//TODO sync up with data model +import javax.annotation.Nullable; + @Value -public class JsonTxOutput { +class JsonTxOutput { private final String txVersion = Version.BSQ_TX_VERSION; private final String txId; - private final int outputIndex; + private final int index; private final long bsqAmount; private final long btcAmount; private final int height; - private final boolean isVerified; + private final boolean isVerified; // isBsqTxOutputType private final long burntFee; private final String address; + @Nullable private final JsonScriptPubKey scriptPubKey; + @Nullable private final JsonSpentInfo spentInfo; private final long time; private final JsonTxType txType; private final String txTypeDisplayString; + private final JsonTxOutputType txOutputType; + private final String txOutputTypeDisplayString; + @Nullable private final String opReturn; + private final int lockTime; + private final boolean isUnspent; - public String getId() { - return txId + ":" + outputIndex; + String getId() { + return txId + ":" + index; } } diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTxOutputType.java b/core/src/main/java/bisq/core/dao/node/json/JsonTxOutputType.java new file mode 100644 index 0000000000..f8187e065a --- /dev/null +++ b/core/src/main/java/bisq/core/dao/node/json/JsonTxOutputType.java @@ -0,0 +1,47 @@ +/* + * 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.node.json; + +import lombok.Getter; + +// Need to be in sync with TxOutputType +enum JsonTxOutputType { + UNDEFINED_OUTPUT("Undefined"), + GENESIS_OUTPUT("Genesis"), + BSQ_OUTPUT("BSQ"), + BTC_OUTPUT("BTC"), + PROPOSAL_OP_RETURN_OUTPUT("Proposal opReturn"), + COMP_REQ_OP_RETURN_OUTPUT("Compensation request opReturn"), + CONFISCATE_BOND_OP_RETURN_OUTPUT("Confiscate bond opReturn"), + ISSUANCE_CANDIDATE_OUTPUT("Issuance candidate"), + BLIND_VOTE_LOCK_STAKE_OUTPUT("Blind vote lock stake"), + BLIND_VOTE_OP_RETURN_OUTPUT("Blind vote opReturn"), + VOTE_REVEAL_UNLOCK_STAKE_OUTPUT("Vote reveal unlock stake"), + VOTE_REVEAL_OP_RETURN_OUTPUT("Vote reveal opReturn"), + LOCKUP_OUTPUT("Lockup"), + LOCKUP_OP_RETURN_OUTPUT("Lockup opReturn"), + UNLOCK_OUTPUT("Unlock"), + INVALID_OUTPUT("Invalid"); + + @Getter + private String displayString; + + JsonTxOutputType(String displayString) { + this.displayString = displayString; + } +} diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTxType.java b/core/src/main/java/bisq/core/dao/node/json/JsonTxType.java index e624247377..5d44c504c6 100644 --- a/core/src/main/java/bisq/core/dao/node/json/JsonTxType.java +++ b/core/src/main/java/bisq/core/dao/node/json/JsonTxType.java @@ -19,21 +19,20 @@ package bisq.core.dao.node.json; import lombok.Getter; -//TODO sync up with data model -public enum JsonTxType { +// Need to be in sync with TxOutputType +enum JsonTxType { UNDEFINED_TX_TYPE("Undefined"), UNVERIFIED("Unverified"), INVALID("Invalid"), GENESIS("Genesis"), TRANSFER_BSQ("Transfer BSQ"), PAY_TRADE_FEE("Pay trade fee"), - PROPOSAL("Ballot"), + PROPOSAL("Proposal"), COMPENSATION_REQUEST("Compensation request"), - VOTE("Vote"), BLIND_VOTE("Blind vote"), VOTE_REVEAL("Vote reveal"), - LOCK_UP("Lockup"), - UN_LOCK("Unlock"); + LOCKUP("Lockup"), + UNLOCK("Unlock"); @Getter private String displayString; diff --git a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java b/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java index 35cb2eb6ff..96e264c4ee 100644 --- a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java +++ b/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java @@ -73,7 +73,7 @@ public class LiteNode extends BsqNode { public void start() { super.onInitialized(); - liteNodeNetworkService.init(); + liteNodeNetworkService.start(); } @Override 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 32a24cc898..d9a20d6634 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 @@ -117,7 +117,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen // API /////////////////////////////////////////////////////////////////////////////////////////// - public void init() { + public void start() { networkNode.addMessageListener(this); networkNode.addConnectionListener(this); peerManager.addListener(this); @@ -139,7 +139,6 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen } public void requestBlocks(int startBlockHeight) { - Log.traceCall(); lastRequestedBlockHeight = startBlockHeight; Optional connectionToSeedNodeOptional = networkNode.getConfirmedConnections().stream() .filter(peerManager::isSeedNode) @@ -219,6 +218,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof NewBlockBroadcastMessage) { + log.info("We received blocks from peer {}", connection.getPeersNodeAddressOptional()); listeners.forEach(listener -> listener.onNewBlockReceived((NewBlockBroadcastMessage) networkEnvelope)); } } @@ -239,7 +239,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen new RequestBlocksHandler.Listener() { @Override public void onComplete(GetBlocksResponse getBlocksResponse) { - log.trace("requestBlocksHandler of outbound connection complete. nodeAddress={}", + log.info("requestBlocksHandler of outbound connection complete. nodeAddress={}", peersNodeAddress); stopRetryTimer(); @@ -270,6 +270,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen } }); requestBlocksHandlerMap.put(key, requestBlocksHandler); + log.info("requestBlocks with startBlockHeight={} from peer {}", startBlockHeight, peersNodeAddress); requestBlocksHandler.requestBlocks(); } else { //TODO check with re-orgs 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 371a43f78f..4b3d6051e6 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 @@ -126,14 +126,14 @@ public class RequestBlocksHandler implements MessageListener { TIMEOUT); } - log.debug("We send a {} to peer {}. ", getBlocksRequest.getClass().getSimpleName(), nodeAddress); + log.info("We send to peer {} a {}.", nodeAddress, getBlocksRequest); networkNode.addMessageListener(this); SettableFuture future = networkNode.sendMessage(nodeAddress, getBlocksRequest); Futures.addCallback(future, new FutureCallback() { @Override public void onSuccess(Connection connection) { if (!stopped) { - log.trace("Send " + getBlocksRequest + " to " + nodeAddress + " succeeded."); + log.info("Sending of GetBlocksRequest message to peer {} succeeded.", nodeAddress.getHostName()); } else { log.trace("We have stopped already. We ignore that networkNode.sendMessage.onSuccess call." + "Might be caused by an previous timeout."); @@ -178,6 +178,8 @@ public class RequestBlocksHandler implements MessageListener { "RequestDataHandler.onMessage: connection.getPeersNodeAddressOptional() must be present " + "at that moment"); cleanup(); + log.info("We received from peer {} a BlocksResponse with {} blocks", + nodeAddress.getFullAddress(), getBlocksResponse.getBlocks().size()); listener.onComplete(getBlocksResponse); } else { log.warn("Nonce not matching. That can happen rarely if we get a response after a canceled " + 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 3750f55ccf..ee66b69d1a 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 @@ -32,11 +32,9 @@ import java.util.List; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.ToString; @EqualsAndHashCode(callSuper = true) @Getter -@ToString public final class GetBlocksRequest extends NetworkEnvelope implements DirectMessage, CapabilityRequiringPayload { private final int fromBlockHeight; private final int nonce; @@ -75,4 +73,13 @@ public final class GetBlocksRequest extends NetworkEnvelope implements DirectMes Capabilities.Capability.DAO_FULL_NODE.ordinal() )); } + + + @Override + public String toString() { + return "GetBlocksRequest{" + + "\n fromBlockHeight=" + fromBlockHeight + + ",\n nonce=" + nonce + + "\n} " + super.toString(); + } } diff --git a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java index ad8c6493e4..c8b244d2ef 100644 --- a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java +++ b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java @@ -75,4 +75,13 @@ public final class GetBlocksResponse extends NetworkEnvelope implements DirectMe proto.getRequestNonce(), messageVersion); } + + + @Override + public String toString() { + return "GetBlocksResponse{" + + "\n blocks=" + blocks + + ",\n requestNonce=" + requestNonce + + "\n} " + super.toString(); + } } diff --git a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java index 2e0f5b433b..ccba248673 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java @@ -122,7 +122,7 @@ public class BlockParser { private void validateIfBlockIsConnecting(RawBlock rawBlock) throws BlockNotConnectingException { LinkedList blocks = bsqStateService.getBlocks(); if (!isBlockConnecting(rawBlock, blocks)) { - final Block last = blocks.getLast(); + Block last = blocks.getLast(); log.warn("addBlock called with a not connecting block. New block:\n" + "height()={}, hash()={}, lastBlock.height()={}, lastBlock.hash()={}", rawBlock.getHeight(), diff --git a/core/src/main/java/bisq/core/dao/node/parser/GenesisTxParser.java b/core/src/main/java/bisq/core/dao/node/parser/GenesisTxParser.java new file mode 100644 index 0000000000..83f22913c3 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/node/parser/GenesisTxParser.java @@ -0,0 +1,88 @@ +/* + * 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.node.parser; + +import bisq.core.dao.node.parser.exceptions.InvalidGenesisTxException; +import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.blockchain.RawTx; +import bisq.core.dao.state.blockchain.TempTx; +import bisq.core.dao.state.blockchain.TempTxOutput; +import bisq.core.dao.state.blockchain.Tx; +import bisq.core.dao.state.blockchain.TxOutput; +import bisq.core.dao.state.blockchain.TxOutputType; +import bisq.core.dao.state.blockchain.TxType; + +import org.bitcoinj.core.Coin; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +public class GenesisTxParser { + public static boolean isGenesis(RawTx rawTx, String genesisTxId, int genesisBlockHeight) { + return rawTx.getBlockHeight() == genesisBlockHeight && rawTx.getId().equals(genesisTxId); + } + + public static Tx getGenesisTx(RawTx rawTx, Coin genesisTotalSupply, BsqStateService bsqStateService) { + TempTx genesisTx = getGenesisTempTx(rawTx, genesisTotalSupply); + commitUTXOs(bsqStateService, genesisTx); + return Tx.fromTempTx(genesisTx); + } + + private static void commitUTXOs(BsqStateService bsqStateService, TempTx genesisTx) { + ImmutableList outputs = genesisTx.getTempTxOutputs(); + for (int i = 0; i < outputs.size(); ++i) { + TempTxOutput tempTxOutput = outputs.get(i); + bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(tempTxOutput)); + } + } + + /** + * Parse and return the genesis transaction for bisq, if applicable. + * + * @param rawTx The candidate transaction. + * @param genesisTotalSupply The total supply of the genesis issuance for bisq. + * @return The genesis transaction. + */ + @VisibleForTesting + static TempTx getGenesisTempTx(RawTx rawTx, Coin genesisTotalSupply) { + TempTx tempTx = TempTx.fromRawTx(rawTx); + tempTx.setTxType(TxType.GENESIS); + long remainingInputValue = genesisTotalSupply.getValue(); + List tempTxOutputs = tempTx.getTempTxOutputs(); + for (int i = 0; i < tempTxOutputs.size(); ++i) { + TempTxOutput txOutput = tempTxOutputs.get(i); + long value = txOutput.getValue(); + boolean isValid = value <= remainingInputValue; + if (!isValid) + throw new InvalidGenesisTxException("Genesis tx is invalid; using more than available inputs. " + + "Remaining input value is " + remainingInputValue + " sat; tx info: " + tempTx.toString()); + + remainingInputValue -= value; + txOutput.setTxOutputType(TxOutputType.GENESIS_OUTPUT); + } + + if (remainingInputValue > 0) { + throw new InvalidGenesisTxException("Genesis tx is invalid; not using all available inputs. " + + "Remaining input value is " + remainingInputValue + " sat, tx info: " + tempTx.toString()); + } + + return tempTx; + } +} diff --git a/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java b/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java index f4a11b1974..832d151a50 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java @@ -58,17 +58,16 @@ class OpReturnParser { * Parse the type of OP_RETURN data and validate it. * * @param tempTxOutput The temporary transaction output to parse. - * @param isLastOutput If true, the output being parsed has a non-zero value. * @return The type of the transaction output, which will be either one of the * {@code *_OP_RETURN_OUTPUT} values, or {@code UNDEFINED} in case of * unexpected state. */ - static TxOutputType getTxOutputType(TempTxOutput tempTxOutput, boolean isLastOutput) { + static TxOutputType getTxOutputType(TempTxOutput tempTxOutput) { boolean nonZeroOutput = tempTxOutput.getValue() != 0; byte[] opReturnData = tempTxOutput.getOpReturnData(); checkNotNull(opReturnData, "opReturnData must not be null"); - if (nonZeroOutput || !isLastOutput || opReturnData.length < 22) { + if (nonZeroOutput || opReturnData.length < 22) { log.warn("OP_RETURN data does not match our rules. opReturnData={}", Utils.HEX.encode(opReturnData)); return TxOutputType.INVALID_OUTPUT; 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 e2546099a7..01eb37c64f 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 @@ -25,9 +25,7 @@ import bisq.core.dao.state.blockchain.TxOutputType; import javax.inject.Inject; -import java.util.HashSet; import java.util.Optional; -import java.util.Set; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -37,10 +35,9 @@ import lombok.extern.slf4j.Slf4j; */ @Slf4j public class TxInputParser { - enum VoteRevealInputState { - UNKNOWN, VALID, INVALID - } + private final BsqStateService bsqStateService; + // Getters @Getter private long accumulatedInputValue = 0; @Getter @@ -49,21 +46,27 @@ public class TxInputParser { private int unlockBlockHeight; @Getter private Optional optionalSpentLockupTxOutput = Optional.empty(); - - private final BsqStateService bsqStateService; @Getter - private TxInputParser.VoteRevealInputState voteRevealInputState = TxInputParser.VoteRevealInputState.UNKNOWN; + private boolean isUnLockInputValid = true; - //TODO never read from... remove? - // We use here TxOutput as we do not alter it but take it from the BsqState - private Set spentUnlockConnectedTxOutputs = new HashSet<>(); + // Private + private int numVoteRevealInputs = 0; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// @Inject public TxInputParser(BsqStateService bsqStateService) { this.bsqStateService = bsqStateService; } - @SuppressWarnings("IfCanBeSwitch") + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + void process(TxOutputKey txOutputKey, int blockHeight, String txId, int inputIndex) { bsqStateService.getUnspentTxOutput(txOutputKey) .ifPresent(connectedTxOutput -> { @@ -74,7 +77,7 @@ public class TxInputParser { // for later verification at the outputs of a reveal tx. TxOutputType connectedTxOutputType = connectedTxOutput.getTxOutputType(); switch (connectedTxOutputType) { - case UNDEFINED: + case UNDEFINED_OUTPUT: case GENESIS_OUTPUT: case BSQ_OUTPUT: case BTC_OUTPUT: @@ -84,43 +87,44 @@ public class TxInputParser { case ISSUANCE_CANDIDATE_OUTPUT: break; case BLIND_VOTE_LOCK_STAKE_OUTPUT: - if (voteRevealInputState == TxInputParser.VoteRevealInputState.UNKNOWN) { - // The connected tx output of the blind vote tx is our input for the reveal tx. - // We allow only one input from any blind vote tx otherwise the vote reveal tx is invalid. - voteRevealInputState = TxInputParser.VoteRevealInputState.VALID; - } else { + numVoteRevealInputs++; + // The connected tx output of the blind vote tx is our input for the reveal tx. + // We allow only one input from any blind vote tx otherwise the vote reveal tx is invalid. + if (!isVoteRevealInputValid()) { log.warn("We have a tx which has >1 connected txOutputs marked as BLIND_VOTE_LOCK_STAKE_OUTPUT. " + "This is not a valid BSQ tx."); - voteRevealInputState = TxInputParser.VoteRevealInputState.INVALID; } break; case BLIND_VOTE_OP_RETURN_OUTPUT: case VOTE_REVEAL_UNLOCK_STAKE_OUTPUT: case VOTE_REVEAL_OP_RETURN_OUTPUT: break; - case LOCKUP: + case LOCKUP_OUTPUT: // A LOCKUP BSQ txOutput is spent to a corresponding UNLOCK - // txOutput. The UNLOCK can only be spent after lockTime blocks has passed. - if (!optionalSpentLockupTxOutput.isPresent()) { + // txInput. The UNLOCK can only be spent after lockTime blocks has passed. + isUnLockInputValid = !optionalSpentLockupTxOutput.isPresent(); + if (isUnLockInputValid) { optionalSpentLockupTxOutput = Optional.of(connectedTxOutput); unlockBlockHeight = blockHeight + connectedTxOutput.getLockTime(); + } else { + log.warn("We have a tx which has >1 connected txOutputs marked as LOCKUP_OUTPUT. " + + "This is not a valid BSQ tx."); } break; case LOCKUP_OP_RETURN_OUTPUT: + // Cannot happen break; - case UNLOCK: + case UNLOCK_OUTPUT: // This txInput is Spending an UNLOCK txOutput - spentUnlockConnectedTxOutputs.add(connectedTxOutput); + int unlockBlockHeight = connectedTxOutput.getUnlockBlockHeight(); + if (blockHeight < unlockBlockHeight) { + accumulatedInputValue -= inputValue; + burntBondValue += inputValue; - //TODO We should add unlockBlockHeight to TempTxOutput and remove unlockBlockHeight from tempTx - // then we can use connectedTxOutput to access the unlockBlockHeight instead of the tx - bsqStateService.getTx(connectedTxOutput.getTxId()).ifPresent(unlockTx -> { - // Only count the input as BSQ input if spent after unlock time - if (blockHeight < unlockTx.getUnlockBlockHeight()) { - accumulatedInputValue -= inputValue; - burntBondValue += inputValue; - } - }); + log.warn("We got a tx which spends the output from an unlock tx but before the " + + "unlockTime has passed. That leads to burned BSQ! " + + "blockHeight={}, unLockHeight={}", blockHeight, unlockBlockHeight); + } break; case INVALID_OUTPUT: default: @@ -131,4 +135,8 @@ public class TxInputParser { bsqStateService.removeUnspentTxOutput(connectedTxOutput); }); } + + boolean isVoteRevealInputValid() { + return numVoteRevealInputs == 1; + } } 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 418b0d1062..8a082f4b29 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 @@ -20,13 +20,14 @@ package bisq.core.dao.node.parser; import bisq.core.dao.bonding.BondingConsensus; import bisq.core.dao.state.BsqStateService; import bisq.core.dao.state.blockchain.OpReturnType; -import bisq.core.dao.state.blockchain.TempTx; import bisq.core.dao.state.blockchain.TempTxOutput; import bisq.core.dao.state.blockchain.TxOutput; import bisq.core.dao.state.blockchain.TxOutputType; import com.google.common.annotations.VisibleForTesting; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import lombok.Getter; @@ -50,17 +51,14 @@ public class TxOutputParser { @Setter private int unlockBlockHeight; @Setter - private TempTx tempTx; //TODO remove - @Setter @Getter private Optional optionalSpentLockupTxOutput = Optional.empty(); + // Getters @Getter private boolean bsqOutputFound; @Getter - private Optional optionalOpReturnTypeCandidate = Optional.empty(); - @Getter - private Optional optionalVerifiedOpReturnType = Optional.empty(); + private Optional optionalOpReturnType = Optional.empty(); @Getter private Optional optionalIssuanceCandidate = Optional.empty(); @Getter @@ -70,51 +68,75 @@ public class TxOutputParser { @Getter private Optional optionalLockupOutput = Optional.empty(); + // Private + private int lockTime; + private final List utxoCandidates = new ArrayList<>(); + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + TxOutputParser(BsqStateService bsqStateService) { this.bsqStateService = bsqStateService; } - public void processGenesisTxOutput(TempTx genesisTx) { - for (int i = 0; i < genesisTx.getTempTxOutputs().size(); ++i) { - TempTxOutput tempTxOutput = genesisTx.getTempTxOutputs().get(i); - bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(tempTxOutput)); + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + void processOpReturnOutput(TempTxOutput tempTxOutput) { + byte[] opReturnData = tempTxOutput.getOpReturnData(); + checkNotNull(opReturnData, "opReturnData must not be null"); + TxOutputType txOutputType = OpReturnParser.getTxOutputType(tempTxOutput); + tempTxOutput.setTxOutputType(txOutputType); + + optionalOpReturnType = getMappedOpReturnType(txOutputType); + + // If we have a LOCKUP opReturn output we save the lockTime to apply it later to the LOCKUP output. + // We keep that data in that other output as it makes parsing of the UNLOCK tx easier. + optionalOpReturnType.filter(opReturnType -> opReturnType == OpReturnType.LOCKUP) + .ifPresent(opReturnType -> lockTime = BondingConsensus.getLockTime(opReturnData)); + } + + void processTxOutput(TempTxOutput tempTxOutput) { + // 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()) { + tempTxOutput.setTxOutputType(TxOutputType.INVALID_OUTPUT); + return; + } + + long txOutputValue = tempTxOutput.getValue(); + int index = tempTxOutput.getIndex(); + if (isUnlockBondTx(tempTxOutput.getValue(), index)) { + // We need to handle UNLOCK transactions separately as they don't follow the pattern on spending BSQ + // The LOCKUP BSQ is burnt unless the output exactly matches the input, that would cause the + // output to not be BSQ output at all + handleUnlockBondTx(tempTxOutput); + } else if (availableInputValue > 0 && availableInputValue >= txOutputValue) { + handleBsqOutput(tempTxOutput, index, txOutputValue); + } else { + handleBtcOutput(tempTxOutput, index); } } - void processOpReturnCandidate(TempTxOutput txOutput) { - optionalOpReturnTypeCandidate = OpReturnParser.getOptionalOpReturnTypeCandidate(txOutput); + void commitUTXOCandidates() { + utxoCandidates.forEach(output -> bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(output))); } /** - * Process a transaction output. - * - * @param isLastOutput If it is the last output - * @param tempTxOutput The TempTxOutput we are parsing - * @param index The index in the outputs + * This sets all outputs to BTC_OUTPUT and doesn't add any txOutputs to the unspentTxOutput map in bsqStateService */ - void processTxOutput(boolean isLastOutput, TempTxOutput tempTxOutput, int index) { - // We do not check for pubKeyScript.scriptType.NULL_DATA because that is only set if dumpBlockchainData is true - byte[] opReturnData = tempTxOutput.getOpReturnData(); - if (opReturnData == null) { - long txOutputValue = tempTxOutput.getValue(); - if (isUnlockBondTx(tempTxOutput.getValue(), index)) { - // We need to handle UNLOCK transactions separately as they don't follow the pattern on spending BSQ - // The LOCKUP BSQ is burnt unless the output exactly matches the input, that would cause the - // output to not be BSQ output at all - handleUnlockBondTx(tempTxOutput); - } else if (availableInputValue > 0 && availableInputValue >= txOutputValue) { - handleBsqOutput(tempTxOutput, index, txOutputValue); - } else { - handleBtcOutput(tempTxOutput, index); - } - } else { - handleOpReturnOutput(tempTxOutput, isLastOutput); - } + void invalidateUTXOCandidates() { + utxoCandidates.forEach(output -> output.setTxOutputType(TxOutputType.BTC_OUTPUT)); } - boolean isOpReturnOutput(TempTxOutput txOutput) { - return txOutput.getOpReturnData() != null; - } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// /** * Whether a transaction is a valid unlock bond transaction or not. @@ -135,12 +157,9 @@ public class TxOutputParser { checkArgument(optionalSpentLockupTxOutput.isPresent(), "optionalSpentLockupTxOutput must be present"); availableInputValue -= optionalSpentLockupTxOutput.get().getValue(); - txOutput.setTxOutputType(TxOutputType.UNLOCK); - bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(txOutput)); - - //TODO move up to TxParser - // We should add unlockBlockHeight to TempTxOutput and remove unlockBlockHeight from tempTx - tempTx.setUnlockBlockHeight(unlockBlockHeight); + txOutput.setTxOutputType(TxOutputType.UNLOCK_OUTPUT); + txOutput.setUnlockBlockHeight(unlockBlockHeight); + utxoCandidates.add(txOutput); bsqOutputFound = true; } @@ -152,8 +171,8 @@ public class TxOutputParser { boolean isFirstOutput = index == 0; OpReturnType opReturnTypeCandidate = null; - if (optionalOpReturnTypeCandidate.isPresent()) - opReturnTypeCandidate = optionalOpReturnTypeCandidate.get(); + if (optionalOpReturnType.isPresent()) + opReturnTypeCandidate = optionalOpReturnType.get(); TxOutputType bsqOutput; if (isFirstOutput && opReturnTypeCandidate == OpReturnType.BLIND_VOTE) { @@ -163,46 +182,40 @@ public class TxOutputParser { bsqOutput = TxOutputType.VOTE_REVEAL_UNLOCK_STAKE_OUTPUT; optionalVoteRevealUnlockStakeOutput = Optional.of(txOutput); } else if (isFirstOutput && opReturnTypeCandidate == OpReturnType.LOCKUP) { - bsqOutput = TxOutputType.LOCKUP; + bsqOutput = TxOutputType.LOCKUP_OUTPUT; + + // We store the lockTime in the output which will be used as input for a unlock tx. + // That makes parsing of that data easier as if we would need to access it from the opReturn output of + // that tx. + txOutput.setLockTime(lockTime); optionalLockupOutput = Optional.of(txOutput); } else { bsqOutput = TxOutputType.BSQ_OUTPUT; } txOutput.setTxOutputType(bsqOutput); - bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(txOutput)); + utxoCandidates.add(txOutput); bsqOutputFound = true; } private void handleBtcOutput(TempTxOutput txOutput, int index) { - // If we have BSQ left for burning and at the second output a compensation request output we set the - // candidate to the parsingModel and we don't apply the TxOutputType as we do that later as the OpReturn check. + // If we have BSQ left as fee and we are at the second output it might be a compensation request output. + // We store the candidate but we don't apply the TxOutputType yet as we need to verify the fee after all + // outputs are parsed and check the phase. The TxParser will do that.... if (availableInputValue > 0 && index == 1 && - optionalOpReturnTypeCandidate.isPresent() && - optionalOpReturnTypeCandidate.get() == OpReturnType.COMPENSATION_REQUEST) { - // We don't set the txOutputType yet as we have not fully validated the tx but put the candidate - // into our local optionalIssuanceCandidate. - + optionalOpReturnType.isPresent() && + optionalOpReturnType.get() == OpReturnType.COMPENSATION_REQUEST) { optionalIssuanceCandidate = Optional.of(txOutput); } else { txOutput.setTxOutputType(TxOutputType.BTC_OUTPUT); } } - private void handleOpReturnOutput(TempTxOutput tempTxOutput, boolean isLastOutput) { - TxOutputType txOutputType = OpReturnParser.getTxOutputType(tempTxOutput, isLastOutput); - tempTxOutput.setTxOutputType(txOutputType); - optionalVerifiedOpReturnType = getMappedOpReturnType(txOutputType); - optionalVerifiedOpReturnType.filter(verifiedOpReturnType -> verifiedOpReturnType == OpReturnType.LOCKUP) - .ifPresent(verifiedOpReturnType -> { - byte[] opReturnData = tempTxOutput.getOpReturnData(); - checkNotNull(opReturnData, "opReturnData must not be null"); - int lockTime = BondingConsensus.getLockTime(opReturnData); - tempTxOutput.setLockTime(lockTime); - }); - } + /////////////////////////////////////////////////////////////////////////////////////////// + // Static + /////////////////////////////////////////////////////////////////////////////////////////// @SuppressWarnings("WeakerAccess") @VisibleForTesting diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java index d1c3fb1bc6..69899218d5 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java @@ -17,7 +17,6 @@ package bisq.core.dao.node.parser; -import bisq.core.dao.node.parser.exceptions.InvalidGenesisTxException; import bisq.core.dao.state.BsqStateService; import bisq.core.dao.state.blockchain.OpReturnType; import bisq.core.dao.state.blockchain.RawTx; @@ -25,6 +24,7 @@ import bisq.core.dao.state.blockchain.TempTx; import bisq.core.dao.state.blockchain.TempTxOutput; import bisq.core.dao.state.blockchain.Tx; import bisq.core.dao.state.blockchain.TxInput; +import bisq.core.dao.state.blockchain.TxOutput; import bisq.core.dao.state.blockchain.TxOutputKey; import bisq.core.dao.state.blockchain.TxOutputType; import bisq.core.dao.state.blockchain.TxType; @@ -32,8 +32,6 @@ import bisq.core.dao.state.governance.Param; import bisq.core.dao.state.period.DaoPhase; import bisq.core.dao.state.period.PeriodService; -import bisq.common.app.DevEnv; - import org.bitcoinj.core.Coin; import javax.inject.Inject; @@ -45,8 +43,6 @@ import java.util.Optional; import lombok.extern.slf4j.Slf4j; -import static com.google.common.base.Preconditions.checkArgument; - /** * Verifies if a given transaction is a BSQ transaction. */ @@ -54,10 +50,14 @@ import static com.google.common.base.Preconditions.checkArgument; public class TxParser { private final PeriodService periodService; private final BsqStateService bsqStateService; - private long remainingInputValue; private TxOutputParser txOutputParser; private TxInputParser txInputParser; + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + @Inject public TxParser(PeriodService periodService, BsqStateService bsqStateService) { @@ -65,176 +65,143 @@ public class TxParser { this.bsqStateService = bsqStateService; } + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + public Optional findTx(RawTx rawTx, String genesisTxId, int genesisBlockHeight, Coin genesisTotalSupply) { + if (GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight)) + return Optional.of(GenesisTxParser.getGenesisTx(rawTx, genesisTotalSupply, bsqStateService)); + else + return findTx(rawTx); + } + // Apply state changes to tx, inputs and outputs - // return true if any input contained BSQ - // Any tx with BSQ input is a BSQ tx (except genesis tx but that is not handled in - // that class). + // return Tx if any input contained BSQ + // Any tx with BSQ input is a BSQ tx. // There might be txs without any valid BSQ txOutput but we still keep track of it, // for instance to calculate the total burned BSQ. - public Optional findTx(RawTx rawTx, String genesisTxId, int genesisBlockHeight, Coin genesisTotalSupply) { - txInputParser = new TxInputParser(bsqStateService); - txOutputParser = new TxOutputParser(bsqStateService); - - // Let's see if we have a genesis tx - Optional optionalGenesisTx = TxParser.findGenesisTx( - genesisTxId, - genesisBlockHeight, - genesisTotalSupply, - rawTx); - if (optionalGenesisTx.isPresent()) { - TempTx genesisTx = optionalGenesisTx.get(); - txOutputParser.processGenesisTxOutput(genesisTx); - return Optional.of(Tx.fromTempTx(genesisTx)); - } - - // If it is not a genesis tx we continue to parse to see if it is a valid BSQ tx. + private Optional findTx(RawTx rawTx) { int blockHeight = rawTx.getBlockHeight(); - // We could pass tx also to the sub validators but as long we have not refactored the validators to pure - // functions lets use the parsingModel. TempTx tempTx = TempTx.fromRawTx(rawTx); + //**************************************************************************************** + // Parse Inputs + //**************************************************************************************** + + txInputParser = new TxInputParser(bsqStateService); for (int inputIndex = 0; inputIndex < tempTx.getTxInputs().size(); inputIndex++) { TxInput input = tempTx.getTxInputs().get(inputIndex); TxOutputKey outputKey = input.getConnectedTxOutputKey(); txInputParser.process(outputKey, blockHeight, rawTx.getId(), inputIndex); } + // Results from txInputParser long accumulatedInputValue = txInputParser.getAccumulatedInputValue(); - txOutputParser.setAvailableInputValue(accumulatedInputValue); - txOutputParser.setUnlockBlockHeight(txInputParser.getUnlockBlockHeight()); - txOutputParser.setOptionalSpentLockupTxOutput(txInputParser.getOptionalSpentLockupTxOutput()); - txOutputParser.setTempTx(tempTx); //TODO remove + long burntBondValue = txInputParser.getBurntBondValue(); + boolean unLockInputValid = txInputParser.isUnLockInputValid(); + int unlockBlockHeight = txInputParser.getUnlockBlockHeight(); + Optional optionalSpentLockupTxOutput = txInputParser.getOptionalSpentLockupTxOutput(); boolean hasBsqInputs = accumulatedInputValue > 0; - if (hasBsqInputs) { - final List outputs = tempTx.getTempTxOutputs(); - // We start with last output as that might be an OP_RETURN output and gives us the specific tx type, so it is - // easier and cleaner at parsing the other outputs to detect which kind of tx we deal with. - // Setting the opReturn type here does not mean it will be a valid BSQ tx as the checks are only partial and - // BSQ inputs are not verified yet. - // We keep the temporary opReturn type in the parsingModel object. - checkArgument(!outputs.isEmpty(), "outputs must not be empty"); - int lastIndex = outputs.size() - 1; - txOutputParser.processOpReturnCandidate(outputs.get(lastIndex)); + boolean hasBurntBond = burntBondValue > 0; - // We use order of output index. An output is a BSQ utxo as long there is enough input value - // We iterate all outputs including the opReturn to do a full validation including the BSQ fee - for (int index = 0; index < outputs.size(); index++) { - boolean isLastOutput = index == lastIndex; - txOutputParser.processTxOutput(isLastOutput, - outputs.get(index), - index - ); - } + // If we don't have any BSQ in our input and we don't have burnt bonds we do not consider the tx as a BSQ tx. + if (!hasBsqInputs && !hasBurntBond) + return Optional.empty(); - remainingInputValue = txOutputParser.getAvailableInputValue(); - processOpReturnType(blockHeight, tempTx); + //**************************************************************************************** + // Parse Outputs + //**************************************************************************************** - // We don't allow multiple opReturn outputs (they are non-standard but to be safe lets check it) - long numOpReturnOutputs = tempTx.getTempTxOutputs().stream().filter(txOutputParser::isOpReturnOutput).count(); - if (numOpReturnOutputs <= 1) { - boolean isAnyTxOutputTypeUndefined = tempTx.getTempTxOutputs().stream() - .anyMatch(txOutput -> TxOutputType.UNDEFINED == txOutput.getTxOutputType()); - if (!isAnyTxOutputTypeUndefined) { - // TODO(chirhonul): we don't modify the tempTx within the call below, so maybe we should - // use RawTx? - TxType txType = TxParser.getBisqTxType( - tempTx, - txOutputParser.getOptionalOpReturnTypeCandidate().isPresent(), - remainingInputValue, - getOptionalOpReturnType() - ); - tempTx.setTxType(txType); - if (remainingInputValue > 0) - tempTx.setBurntFee(remainingInputValue); - } else { - tempTx.setTxType(TxType.INVALID); - String msg = "We have undefined txOutput types which must not happen. tx=" + tempTx; - DevEnv.logErrorAndThrowIfDevMode(msg); - } - } else { - // We don't consider a tx with multiple OpReturn outputs valid. - tempTx.setTxType(TxType.INVALID); - String msg = "Invalid tx. We have multiple opReturn outputs. tx=" + tempTx; - log.warn(msg); - } + txOutputParser = new TxOutputParser(bsqStateService); + txOutputParser.setAvailableInputValue(accumulatedInputValue); + txOutputParser.setUnlockBlockHeight(unlockBlockHeight); + txOutputParser.setOptionalSpentLockupTxOutput(optionalSpentLockupTxOutput); + + List outputs = tempTx.getTempTxOutputs(); + // We start with last output as that might be an OP_RETURN output and gives us the specific tx type, so it is + // easier and cleaner at parsing the other outputs to detect which kind of tx we deal with. + int lastIndex = outputs.size() - 1; + int lastNonOpReturnIndex = lastIndex; + if (outputs.get(lastIndex).isOpReturnOutput()) { + txOutputParser.processOpReturnOutput(outputs.get(lastIndex)); + lastNonOpReturnIndex -= 1; } - // TODO || parsingModel.getBurntBondValue() > 0; should not be necessary - // How should we consider the burnt BSQ from spending a LOCKUP tx with the wrong format. - // Example: LOCKUP txOutput is 1000 satoshi but first txOutput in spending tx is 900 - // satoshi, this burns the 1000 satoshi and is currently not considered in the - // bsqInputBalancePositive, hence the need to check for parsingModel.getBurntBondValue - // Perhaps adding boolean parsingModel.isBSQTx and checking for that would be better? + // We need to consider the order of the outputs. An output is a BSQ utxo as long there is enough input value + // We iterate all outputs (excluding an optional opReturn). + for (int index = 0; index <= lastNonOpReturnIndex; index++) { + txOutputParser.processTxOutput(outputs.get(index)); + } - if (hasBsqInputs || txInputParser.getBurntBondValue() > 0) - return Optional.of(Tx.fromTempTx(tempTx)); - else - return Optional.empty(); + // Results from txOutputParser + long remainingInputValue = txOutputParser.getAvailableInputValue(); + Optional optionalOpReturnType = txOutputParser.getOptionalOpReturnType(); + boolean bsqOutputFound = txOutputParser.isBsqOutputFound(); + + long burntBsq = remainingInputValue + burntBondValue; + boolean hasBurntBSQ = burntBsq > 0; + if (hasBurntBSQ) + tempTx.setBurntFee(burntBsq); + + + //**************************************************************************************** + // Verify and apply txType and txOutputTypes after we have all outputs parsed + //**************************************************************************************** + + applyTxTypeAndTxOutputType(blockHeight, tempTx, remainingInputValue); + + TxType txType = evaluateTxType(tempTx, optionalOpReturnType, hasBurntBSQ, unLockInputValid); + tempTx.setTxType(txType); + + if (isTxInvalid(tempTx, bsqOutputFound, hasBurntBond)) { + tempTx.setTxType(TxType.INVALID); + txOutputParser.invalidateUTXOCandidates(); + + if (hasBurntBSQ) { + log.warn("We have destroyed BSQ because of an invalid tx. Burned BSQ={}. tx={}", + burntBsq / 100D, tempTx); + } + } else { + txOutputParser.commitUTXOCandidates(); + } + + return Optional.of(Tx.fromTempTx(tempTx)); } - private void processOpReturnType(int blockHeight, TempTx tempTx) { - // We might have a opReturn output - OpReturnType verifiedOpReturnType = null; - Optional optionalVerifiedOpReturnType = txOutputParser.getOptionalVerifiedOpReturnType(); - if (optionalVerifiedOpReturnType.isPresent()) { - verifiedOpReturnType = optionalVerifiedOpReturnType.get(); - long bsqFee = remainingInputValue; + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// - boolean isFeeAndPhaseValid; - switch (verifiedOpReturnType) { + /** + * This method verifies after all outputs are parsed if the opReturn type and the optional txOutputs required for + * certain use cases are valid. + * It verifies also if the fee is correct (if required) and if the phase is correct (if relevant). + * We set the txType as well as the txOutputType of the relevant outputs. + */ + // TODO That method is not testable and still too complex. + private void applyTxTypeAndTxOutputType(int blockHeight, TempTx tempTx, long bsqFee) { + OpReturnType opReturnType = null; + Optional optionalOpReturnType = txOutputParser.getOptionalOpReturnType(); + if (optionalOpReturnType.isPresent()) { + opReturnType = optionalOpReturnType.get(); + + switch (opReturnType) { case PROPOSAL: - isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE); - if (!isFeeAndPhaseValid) { - tempTx.setTxType(TxType.INVALID); - } + processProposal(blockHeight, tempTx, bsqFee); break; case COMPENSATION_REQUEST: - isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE); - Optional optionalIssuanceCandidate = txOutputParser.getOptionalIssuanceCandidate(); - if (isFeeAndPhaseValid) { - if (optionalIssuanceCandidate.isPresent()) { - // Now after we have validated the opReturn data we will apply the TxOutputType - optionalIssuanceCandidate.get().setTxOutputType(TxOutputType.ISSUANCE_CANDIDATE_OUTPUT); - } else { - log.warn("It can be that we have a opReturn which is correct from its structure but the whole tx " + - "in not valid as the issuanceCandidate in not there. " + - "As the BSQ fee is set it must be either a buggy tx or an manually crafted invalid tx."); - } - } else { - tempTx.setTxType(TxType.INVALID); - optionalIssuanceCandidate.ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); - // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a - // valid BSQ tx. - } + processCompensationRequest(blockHeight, tempTx, bsqFee); break; case BLIND_VOTE: - isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.BLIND_VOTE, Param.BLIND_VOTE_FEE); - if (!isFeeAndPhaseValid) { - tempTx.setTxType(TxType.INVALID); - Optional optionalBlindVoteLockStakeOutput = txOutputParser.getOptionalBlindVoteLockStakeOutput(); - optionalBlindVoteLockStakeOutput.ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); - // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a - // valid BSQ tx. - } + processBlindVote(blockHeight, tempTx, bsqFee); break; case VOTE_REVEAL: - boolean isPhaseValid = isPhaseValid(blockHeight, DaoPhase.Phase.VOTE_REVEAL); - boolean isVoteRevealInputInValid = txInputParser.getVoteRevealInputState() != TxInputParser.VoteRevealInputState.VALID; - if (!isPhaseValid) { - tempTx.setTxType(TxType.INVALID); - } - if (!isPhaseValid || isVoteRevealInputInValid) { - Optional optionalVoteRevealUnlockStakeOutput = txOutputParser - .getOptionalVoteRevealUnlockStakeOutput(); - optionalVoteRevealUnlockStakeOutput.ifPresent( - tempTxOutput -> tempTxOutput - .setTxOutputType(TxOutputType.BTC_OUTPUT)); - // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a - // valid BSQ tx. - } + processVoteReveal(blockHeight, tempTx); break; case LOCKUP: // do nothing @@ -242,32 +209,86 @@ public class TxParser { } } - // We need to check if any temp txOutput is available and if so and the OpRetrun data is invalid we - // set the output to a BTC output. We must not use if else cases here! - if (verifiedOpReturnType != OpReturnType.COMPENSATION_REQUEST) { + // We need to check if any tempTxOutput is available and if so and the OpReturn data is invalid we + // set the output to a BTC output. We must not use `if else` cases here! + if (opReturnType != OpReturnType.COMPENSATION_REQUEST) { txOutputParser.getOptionalIssuanceCandidate().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); } - if (verifiedOpReturnType != OpReturnType.BLIND_VOTE) { + if (opReturnType != OpReturnType.BLIND_VOTE) { txOutputParser.getOptionalBlindVoteLockStakeOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); } - if (verifiedOpReturnType != OpReturnType.VOTE_REVEAL) { + if (opReturnType != OpReturnType.VOTE_REVEAL) { txOutputParser.getOptionalVoteRevealUnlockStakeOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); } - if (verifiedOpReturnType != OpReturnType.LOCKUP) { + if (opReturnType != OpReturnType.LOCKUP) { txOutputParser.getOptionalLockupOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); } } + private void processProposal(int blockHeight, TempTx tempTx, long bsqFee) { + boolean isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE); + if (!isFeeAndPhaseValid) { + tempTx.setTxType(TxType.INVALID); + } + } + + private void processCompensationRequest(int blockHeight, TempTx tempTx, long bsqFee) { + boolean isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE); + Optional optionalIssuanceCandidate = txOutputParser.getOptionalIssuanceCandidate(); + if (isFeeAndPhaseValid) { + if (optionalIssuanceCandidate.isPresent()) { + // Now after we have validated the fee and phase we will apply the TxOutputType + optionalIssuanceCandidate.get().setTxOutputType(TxOutputType.ISSUANCE_CANDIDATE_OUTPUT); + } else { + log.warn("It can be that we have a opReturn which is correct from its structure but the whole tx " + + "in not valid as the issuanceCandidate in not there. " + + "As the BSQ fee is set it must be either a buggy tx or an manually crafted invalid tx."); + tempTx.setTxType(TxType.INVALID); + } + } else { + tempTx.setTxType(TxType.INVALID); + optionalIssuanceCandidate.ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); + // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a + // valid BSQ tx. + } + } + + private void processBlindVote(int blockHeight, TempTx tempTx, long bsqFee) { + boolean isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.BLIND_VOTE, Param.BLIND_VOTE_FEE); + if (!isFeeAndPhaseValid) { + tempTx.setTxType(TxType.INVALID); + txOutputParser.getOptionalBlindVoteLockStakeOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); + // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a + // valid BSQ tx. + } + } + + private void processVoteReveal(int blockHeight, TempTx tempTx) { + boolean isPhaseValid = isPhaseValid(blockHeight, DaoPhase.Phase.VOTE_REVEAL); + if (!isPhaseValid) { + tempTx.setTxType(TxType.INVALID); + } + + // We must not use an `if else` here! + if (!isPhaseValid || !txInputParser.isVoteRevealInputValid()) { + txOutputParser.getOptionalVoteRevealUnlockStakeOutput().ifPresent( + tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); + // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a + // valid BSQ tx. + } + } + + /** * Whether the BSQ fee and phase is valid for a transaction. * - * @param blockHeight The height of the block that the transaction is in. - * @param bsqFee The fee in BSQ, in satoshi. - * @param phase The current phase of the DAO, e.g {@code DaoPhase.Phase.PROPOSAL}. - * @param param The parameter for the fee, e.g {@code Param.PROPOSAL_FEE}. + * @param blockHeight The height of the block that the transaction is in. + * @param bsqFee The fee in BSQ, in satoshi. + * @param phase The current phase of the DAO, e.g {@code DaoPhase.Phase.PROPOSAL}. + * @param param The parameter for the fee, e.g {@code Param.PROPOSAL_FEE}. * @return True if the fee and phase was valid, false otherwise. */ private boolean isFeeAndPhaseValid(int blockHeight, long bsqFee, DaoPhase.Phase phase, Param param) { @@ -293,53 +314,99 @@ public class TxParser { return isInPhase; } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Static methods + /////////////////////////////////////////////////////////////////////////////////////////// + + @VisibleForTesting + // Performs various checks for an invalid tx + static boolean isTxInvalid(TempTx tempTx, boolean bsqOutputFound, boolean burntBondValue) { + if (tempTx.getTxType() == TxType.INVALID) { + // We got already set the invalid type in earlier checks and return early. + return true; + } + + // We don't allow multiple opReturn outputs (they are non-standard but to be safe lets check it) + long numOpReturnOutputs = tempTx.getTempTxOutputs().stream() + .filter(TempTxOutput::isOpReturnOutput) + .count(); + if (numOpReturnOutputs > 1) { + log.warn("Invalid tx. We have multiple opReturn outputs. tx=" + tempTx); + return true; + } + + if (!bsqOutputFound) { + log.warn("Invalid Tx: No BSQ output found. tx=" + tempTx); + return true; + } + + if (burntBondValue) { + log.warn("Invalid Tx: Bond value was burnt. tx=" + tempTx); + return true; + } + + if (tempTx.getTempTxOutputs().stream() + .anyMatch(txOutput -> TxOutputType.UNDEFINED_OUTPUT == txOutput.getTxOutputType() || + TxOutputType.INVALID_OUTPUT == txOutput.getTxOutputType())) { + log.warn("Invalid Tx: We have undefined or invalid txOutput types. tx=" + tempTx); + return true; + } + + return false; + } + /** * Retrieve the type of the transaction, assuming it is relevant to bisq. * - * @param tx The temporary transaction. - * @param hasOpReturnCandidate True if we have a candidate for an OP_RETURN. - * @param remainingInputValue The remaining value of inputs not yet accounted for, in satoshi. - * @param optionalOpReturnType If present, the OP_RETURN type of the transaction. + * @param tempTx The temporary transaction. + * @param optionalOpReturnType The optional OP_RETURN type of the transaction. + * @param hasBurntBSQ If the have been remaining value from the inputs which got not spent in outputs. + * Might be valid BSQ fees or burned BSQ from an invalid tx. * @return The type of the transaction, if it is relevant to bisq. */ @VisibleForTesting - static TxType getBisqTxType(TempTx tx, boolean hasOpReturnCandidate, long remainingInputValue, Optional optionalOpReturnType) { - TxType txType; - // We need to have at least one BSQ output + static TxType evaluateTxType(TempTx tempTx, Optional optionalOpReturnType, + boolean hasBurntBSQ, boolean isUnLockInputValid) { if (optionalOpReturnType.isPresent()) { - log.debug("Optional OP_RETURN type is present for tx."); - txType = TxParser.getTxTypeForOpReturn(tx, optionalOpReturnType.get()); - } else if (!hasOpReturnCandidate) { - log.debug("No optional OP_RETURN type and no OP_RETURN candidate is present for tx."); - - boolean bsqFeesBurnt = remainingInputValue > 0; - if (bsqFeesBurnt) { - // Burned fee but no opReturn - txType = TxType.PAY_TRADE_FEE; - } else if (tx.getTempTxOutputs().get(0).getTxOutputType() == TxOutputType.UNLOCK) { - txType = TxType.UNLOCK; - } else { - log.debug("No burned fee and no OP_RETURN, so this is a TRANSFER_BSQ tx."); - txType = TxType.TRANSFER_BSQ; - } - } else { - log.debug("No optional OP_RETURN type is present for tx but we do have an OP_RETURN candidate, so it failed validation."); - txType = TxType.INVALID; + // We use the opReturnType to find the txType + return evaluateTxTypeFromOpReturnType(tempTx, optionalOpReturnType.get()); } - return txType; + // No opReturnType, so we check for the remaining possible cases + if (hasBurntBSQ) { + // PAY_TRADE_FEE tx has a fee and no opReturn + return TxType.PAY_TRADE_FEE; + } + + // UNLOCK tx has no fee, no opReturn but an UNLOCK_OUTPUT at first output. + if (tempTx.getTempTxOutputs().get(0).getTxOutputType() == TxOutputType.UNLOCK_OUTPUT) { + // We check if there have been invalid inputs + if (!isUnLockInputValid) + return TxType.INVALID; + + // UNLOCK tx has no fee, no OpReturn + return TxType.UNLOCK; + } + + // TRANSFER_BSQ has no fee, no opReturn and no UNLOCK_OUTPUT at first output + log.debug("No burned fee and no OP_RETURN, so this is a TRANSFER_BSQ tx."); + return TxType.TRANSFER_BSQ; } - private static TxType getTxTypeForOpReturn(TempTx tx, OpReturnType opReturnType) { + @VisibleForTesting + static TxType evaluateTxTypeFromOpReturnType(TempTx tempTx, OpReturnType opReturnType) { switch (opReturnType) { + case PROPOSAL: + return TxType.PROPOSAL; case COMPENSATION_REQUEST: - boolean hasCorrectNumOutputs = tx.getTempTxOutputs().size() >= 3; + boolean hasCorrectNumOutputs = tempTx.getTempTxOutputs().size() >= 3; if (!hasCorrectNumOutputs) { log.warn("Compensation request tx need to have at least 3 outputs"); return TxType.INVALID; } - TempTxOutput issuanceTxOutput = tx.getTempTxOutputs().get(1); + TempTxOutput issuanceTxOutput = tempTx.getTempTxOutputs().get(1); boolean hasIssuanceOutput = issuanceTxOutput.getTxOutputType() == TxOutputType.ISSUANCE_CANDIDATE_OUTPUT; if (!hasIssuanceOutput) { log.warn("Compensation request txOutput type of output at index 1 need to be ISSUANCE_CANDIDATE_OUTPUT. " + @@ -348,8 +415,6 @@ public class TxParser { } return TxType.COMPENSATION_REQUEST; - case PROPOSAL: - return TxType.PROPOSAL; case BLIND_VOTE: return TxType.BLIND_VOTE; case VOTE_REVEAL: @@ -357,79 +422,8 @@ public class TxParser { case LOCKUP: return TxType.LOCKUP; default: - log.warn("We got a BSQ tx with fee and unknown OP_RETURN. tx={}", tx); + log.warn("We got a BSQ tx with an unknown OP_RETURN. tx={}, opReturnType={}", tempTx, opReturnType); return TxType.INVALID; } } - - /** - * The type of the OP_RETURN value of the transaction, if it has such a BSQ output. - * - * @return The OP_RETURN type if applicable, otherwise Optional.empty(). - */ - private Optional getOptionalOpReturnType() { - if (txOutputParser.isBsqOutputFound()) { - // We want to be sure that the initial assumption of the opReturn type was matching the result after full - // validation. - Optional optionalOpReturnTypeCandidate = txOutputParser.getOptionalOpReturnTypeCandidate(); - if (optionalOpReturnTypeCandidate.isPresent()) { - OpReturnType opReturnTypeCandidate = optionalOpReturnTypeCandidate.get(); - Optional optionalVerifiedOpReturnType = txOutputParser.getOptionalVerifiedOpReturnType(); - if (optionalVerifiedOpReturnType.isPresent()) { - OpReturnType verifiedOpReturnType = optionalVerifiedOpReturnType.get(); - if (opReturnTypeCandidate == verifiedOpReturnType) { - return optionalVerifiedOpReturnType; - } - } - } - - String msg = "We got a different opReturn type after validation as we expected initially. " + - "optionalOpReturnTypeCandidate=" + optionalOpReturnTypeCandidate + - ", optionalVerifiedOpReturnType=" + txOutputParser.getOptionalVerifiedOpReturnType(); - log.error(msg); - - } else { - String msg = "We got a tx without any valid BSQ output but with burned BSQ. " + - "Burned fee=" + remainingInputValue / 100D + " BSQ."; - log.warn(msg); - } - return Optional.empty(); - } - - /** - * Parse and return the genesis transaction for bisq, if applicable. - * - * @param genesisTxId The transaction id of the bisq genesis transaction. - * @param genesisBlockHeight The block height of the bisq genesis transaction. - * @param genesisTotalSupply The total supply of the genesis issuance for bisq. - * @param rawTx The candidate transaction. - * @return The genesis transaction if applicable, or Optional.empty() otherwise. - */ - public static Optional findGenesisTx(String genesisTxId, int genesisBlockHeight, Coin genesisTotalSupply, - RawTx rawTx) { - boolean isGenesis = rawTx.getBlockHeight() == genesisBlockHeight && - rawTx.getId().equals(genesisTxId); - if (!isGenesis) - return Optional.empty(); - - TempTx tempTx = TempTx.fromRawTx(rawTx); - tempTx.setTxType(TxType.GENESIS); - long remainingInputValue = genesisTotalSupply.getValue(); - for (int i = 0; i < tempTx.getTempTxOutputs().size(); ++i) { - TempTxOutput txOutput = tempTx.getTempTxOutputs().get(i); - long value = txOutput.getValue(); - boolean isValid = value <= remainingInputValue; - if (!isValid) - throw new InvalidGenesisTxException("Genesis tx is invalid; using more than available inputs. " + - "Remaining input value is " + remainingInputValue + " sat; tx info: " + tempTx.toString()); - - remainingInputValue -= value; - txOutput.setTxOutputType(TxOutputType.GENESIS_OUTPUT); - } - if (remainingInputValue > 0) { - throw new InvalidGenesisTxException("Genesis tx is invalid; not using all available inputs. " + - "Remaining input value is " + remainingInputValue + " sat, tx info: " + tempTx.toString()); - } - return Optional.of(tempTx); - } } diff --git a/core/src/main/java/bisq/core/dao/state/BsqState.java b/core/src/main/java/bisq/core/dao/state/BsqState.java index b46c3b2386..62237ff8da 100644 --- a/core/src/main/java/bisq/core/dao/state/BsqState.java +++ b/core/src/main/java/bisq/core/dao/state/BsqState.java @@ -50,12 +50,6 @@ import lombok.extern.slf4j.Slf4j; */ @Slf4j public class BsqState implements PersistableEnvelope { - //TODO not sure if we will use that - /* private static final int ISSUANCE_MATURITY = 144 * 30; // 30 days - - static int getIssuanceMaturity() { - return ISSUANCE_MATURITY; - }*/ /////////////////////////////////////////////////////////////////////////////////////////// // Fields @@ -132,10 +126,10 @@ public class BsqState implements PersistableEnvelope { @Override public Message toProtoMessage() { - return PB.PersistableEnvelope.newBuilder().setBsqState(getStateBuilder()).build(); + return PB.PersistableEnvelope.newBuilder().setBsqState(getBsqStateBuilder()).build(); } - private PB.BsqState.Builder getStateBuilder() { + private PB.BsqState.Builder getBsqStateBuilder() { final PB.BsqState.Builder builder = PB.BsqState.newBuilder(); builder.setChainHeight(chainHeight) .addAllBlocks(blocks.stream().map(Block::toProtoMessage).collect(Collectors.toList())) @@ -193,10 +187,10 @@ public class BsqState implements PersistableEnvelope { } BsqState getClone() { - return (BsqState) BsqState.fromProto(getStateBuilder().build()); + return (BsqState) BsqState.fromProto(getBsqStateBuilder().build()); } BsqState getClone(BsqState snapshotCandidate) { - return (BsqState) BsqState.fromProto(snapshotCandidate.getStateBuilder().build()); + return (BsqState) BsqState.fromProto(snapshotCandidate.getBsqStateBuilder().build()); } } diff --git a/core/src/main/java/bisq/core/dao/state/BsqStateService.java b/core/src/main/java/bisq/core/dao/state/BsqStateService.java index 97eabda13d..3932930e5d 100644 --- a/core/src/main/java/bisq/core/dao/state/BsqStateService.java +++ b/core/src/main/java/bisq/core/dao/state/BsqStateService.java @@ -120,10 +120,6 @@ public class BsqStateService implements DaoSetupService { return bsqState.getClone(); } - public LinkedList getBlocksFromState(BsqState bsqState) { - return new LinkedList<>(bsqState.getBlocks()); - } - /////////////////////////////////////////////////////////////////////////////////////////// // ChainHeight @@ -387,7 +383,7 @@ public class BsqStateService implements DaoSetupService { TxOutput txOutput = optionalTxOutput.get(); switch (txOutput.getTxOutputType()) { - case UNDEFINED: + case UNDEFINED_OUTPUT: return false; case GENESIS_OUTPUT: case BSQ_OUTPUT: @@ -404,11 +400,11 @@ public class BsqStateService implements DaoSetupService { case VOTE_REVEAL_UNLOCK_STAKE_OUTPUT: case VOTE_REVEAL_OP_RETURN_OUTPUT: return true; - case LOCKUP: + case LOCKUP_OUTPUT: return false; case LOCKUP_OP_RETURN_OUTPUT: return true; - case UNLOCK: + case UNLOCK_OUTPUT: return isLockTimeOverForUnlockTxOutput(txOutput); case INVALID_OUTPUT: return false; @@ -431,7 +427,7 @@ public class BsqStateService implements DaoSetupService { public boolean isBsqTxOutputType(TxOutput txOutput) { final TxOutputType txOutputType = txOutput.getTxOutputType(); switch (txOutputType) { - case UNDEFINED: + case UNDEFINED_OUTPUT: return false; case GENESIS_OUTPUT: case BSQ_OUTPUT: @@ -447,9 +443,9 @@ public class BsqStateService implements DaoSetupService { case BLIND_VOTE_OP_RETURN_OUTPUT: case VOTE_REVEAL_UNLOCK_STAKE_OUTPUT: case VOTE_REVEAL_OP_RETURN_OUTPUT: - case LOCKUP: + case LOCKUP_OUTPUT: case LOCKUP_OP_RETURN_OUTPUT: - case UNLOCK: + case UNLOCK_OUTPUT: return true; case INVALID_OUTPUT: return false; @@ -564,7 +560,7 @@ public class BsqStateService implements DaoSetupService { public Optional getLockupHash(TxOutput txOutput) { Optional lockupTx = Optional.empty(); String txId = txOutput.getTxId(); - if (txOutput.getTxOutputType() == TxOutputType.LOCKUP) { + if (txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT) { lockupTx = getTx(txId); } else if (isUnlockTxOutputAndLockTimeNotOver(txOutput)) { if (getTx(txId).isPresent()) { @@ -591,7 +587,7 @@ public class BsqStateService implements DaoSetupService { }*/ public boolean isUnlockTxOutputAndLockTimeNotOver(TxOutput txOutput) { - return txOutput.getTxOutputType() == TxOutputType.UNLOCK && !isLockTimeOverForUnlockTxOutput(txOutput); + return txOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT && !isLockTimeOverForUnlockTxOutput(txOutput); } // Lockup @@ -601,15 +597,15 @@ public class BsqStateService implements DaoSetupService { } public boolean isLockupOutput(TxOutput txOutput) { - return txOutput.getTxOutputType() == TxOutputType.LOCKUP; + return txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT; } public Set getLockupTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.LOCKUP); + return getTxOutputsByTxOutputType(TxOutputType.LOCKUP_OUTPUT); } public Set getUnlockTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.UNLOCK); + return getTxOutputsByTxOutputType(TxOutputType.UNLOCK_OUTPUT); } public Optional getLockupTxOutput(String txId) { @@ -633,13 +629,13 @@ public class BsqStateService implements DaoSetupService { // Unlock public boolean isUnlockOutput(TxOutput txOutput) { - return txOutput.getTxOutputType() == TxOutputType.UNLOCK; + return txOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT; } // Unlocking // Return UNLOCK TxOutputs that are not yet spendable as lockTime is not over public Stream getUnspentUnlockingTxOutputsStream() { - return getTxOutputsByTxOutputType(TxOutputType.UNLOCK).stream() + return getTxOutputsByTxOutputType(TxOutputType.UNLOCK_OUTPUT).stream() .filter(txOutput -> isUnspent(txOutput.getKey())) .filter(txOutput -> !isLockTimeOverForUnlockTxOutput(txOutput)); } @@ -657,7 +653,7 @@ public class BsqStateService implements DaoSetupService { // TODO SQ i changed the code here. i think it was wrong before public boolean isUnlockingOutput(TxOutput unlockTxOutput) { - return unlockTxOutput.getTxOutputType() == TxOutputType.UNLOCK && + return unlockTxOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT && !isLockTimeOverForUnlockTxOutput(unlockTxOutput); } @@ -675,7 +671,7 @@ public class BsqStateService implements DaoSetupService { // We don't care here about the unspent state public Stream getUnlockedTxOutputsStream() { - return getTxOutputsByTxOutputType(TxOutputType.UNLOCK).stream() + return getTxOutputsByTxOutputType(TxOutputType.UNLOCK_OUTPUT).stream() .filter(this::isLockTimeOverForUnlockTxOutput); } @@ -707,7 +703,7 @@ public class BsqStateService implements DaoSetupService { } getTxOutputStream() .filter(txOutput -> isUnspent(txOutput.getKey())) - .filter(txOutput -> txOutput.getTxOutputType() == TxOutputType.LOCKUP || + .filter(txOutput -> txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT || (isUnlockTxOutputAndLockTimeNotOver(txOutput))) .filter(txOutput -> { Optional hash = getLockupHash(txOutput); 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 30c24a6eb8..db90bc859d 100644 --- a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java +++ b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java @@ -17,6 +17,8 @@ package bisq.core.dao.state; +import bisq.core.app.BisqEnvironment; +import bisq.core.btc.BaseCurrencyNetwork; import bisq.core.dao.DaoOptionKeys; import org.bitcoinj.core.Coin; @@ -27,8 +29,6 @@ import javax.inject.Named; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import javax.annotation.Nullable; - /** * Encapsulate the genesis txId and height. @@ -45,8 +45,11 @@ public class GenesisTxInfo { public static final Coin GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); - private static final String DEFAULT_GENESIS_TX_ID = "81855816eca165f17f0668898faa8724a105196e90ffc4993f4cac980176674e"; - private static final int DEFAULT_GENESIS_BLOCK_HEIGHT = 524717; // 2018-05-27 + private static final String MAINNET_GENESIS_TX_ID = "81855816eca165f17f0668898faa8724a105196e90ffc4993f4cac980176674e"; + private static final int MAINNET_GENESIS_BLOCK_HEIGHT = 524717; // 2018-05-27 + + private static final String TESTNET_GENESIS_TX_ID = "7085539068b4fc27dfc6c39b0feae2adc7fe20f925e79ca0ba064725fe6c9991"; + private static final int TESTNET_GENESIS_BLOCK_HEIGHT = 1414332; // 2018-09-25 /////////////////////////////////////////////////////////////////////////////////////////// @@ -60,14 +63,6 @@ public class GenesisTxInfo { @Getter private final int genesisBlockHeight; - - //TODO not sure if we will use that - /* private static final int ISSUANCE_MATURITY = 144 * 30; // 30 days - - static int getIssuanceMaturity() { - return ISSUANCE_MATURITY; - }*/ - // mainnet // this tx has a lot of outputs // https://blockchain.info/de/tx/ee921650ab3f978881b8fe291e0c025e0da2b7dc684003d7a03d9649dfee2e15 @@ -85,15 +80,34 @@ public class GenesisTxInfo { // private static final String DEFAULT_GENESIS_TX_ID = "--"; // private static final int DEFAULT_GENESIS_BLOCK_HEIGHT = 499000; // recursive test 137298, 499000 dec 2017 - /////////////////////////////////////////////////////////////////////////////////////////// // Constructor /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public GenesisTxInfo(@Nullable @Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, - @Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) int genesisBlockHeight) { - this.genesisTxId = genesisTxId != null ? genesisTxId : DEFAULT_GENESIS_TX_ID; - this.genesisBlockHeight = genesisBlockHeight != 0 ? genesisBlockHeight : DEFAULT_GENESIS_BLOCK_HEIGHT; + public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, + @Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) Integer genesisBlockHeight) { + BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); + boolean isMainnet = baseCurrencyNetwork.isMainnet(); + boolean isTestnet = baseCurrencyNetwork.isTestnet(); + if (!genesisTxId.isEmpty()) { + this.genesisTxId = genesisTxId; + } else if (isMainnet) { + this.genesisTxId = MAINNET_GENESIS_TX_ID; + } else if (isTestnet) { + this.genesisTxId = TESTNET_GENESIS_TX_ID; + } else { + this.genesisTxId = "genesisTxId is undefined"; + } + + if (genesisBlockHeight > -1) { + this.genesisBlockHeight = genesisBlockHeight; + } else if (isMainnet) { + this.genesisBlockHeight = MAINNET_GENESIS_BLOCK_HEIGHT; + } else if (isTestnet) { + this.genesisBlockHeight = TESTNET_GENESIS_BLOCK_HEIGHT; + } else { + this.genesisBlockHeight = 0; + } } } diff --git a/core/src/main/java/bisq/core/dao/state/SnapshotManager.java b/core/src/main/java/bisq/core/dao/state/SnapshotManager.java index d86e98db34..fee52cd4c2 100644 --- a/core/src/main/java/bisq/core/dao/state/SnapshotManager.java +++ b/core/src/main/java/bisq/core/dao/state/SnapshotManager.java @@ -29,20 +29,24 @@ import com.google.common.annotations.VisibleForTesting; import java.io.File; +import java.util.LinkedList; + import lombok.extern.slf4j.Slf4j; import static com.google.common.base.Preconditions.checkNotNull; /** * Manages snapshots of BsqState. - * // FIXME not working correctly anymore + * + * One BSQ block with empty txs adds 152 bytes which results in about 8 MB/year */ @Slf4j public class SnapshotManager implements BsqStateListener { - private static final int SNAPSHOT_GRID = 11000; + private static final int SNAPSHOT_GRID = 100; private final BsqState bsqState; private final BsqStateService bsqStateService; + private final GenesisTxInfo genesisTxInfo; private final Storage storage; private BsqState snapshotCandidate; @@ -51,9 +55,11 @@ public class SnapshotManager implements BsqStateListener { public SnapshotManager(BsqState bsqState, BsqStateService bsqStateService, PersistenceProtoResolver persistenceProtoResolver, + GenesisTxInfo genesisTxInfo, @Named(Storage.STORAGE_DIR) File storageDir) { this.bsqState = bsqState; this.bsqStateService = bsqStateService; + this.genesisTxInfo = genesisTxInfo; storage = new Storage<>(storageDir, persistenceProtoResolver); this.bsqStateService.addBsqStateListener(this); @@ -70,21 +76,22 @@ public class SnapshotManager implements BsqStateListener { @Override public void onParseTxsComplete(Block block) { - final int chainHeadHeight = block.getHeight(); - if (isSnapshotHeight(chainHeadHeight) && + int chainHeight = block.getHeight(); + if (isSnapshotHeight(chainHeight) && (snapshotCandidate == null || - snapshotCandidate.getChainHeight() != chainHeadHeight)) { + snapshotCandidate.getChainHeight() != chainHeight)) { // At trigger event we store the latest snapshotCandidate to disc if (snapshotCandidate != null) { // We clone because storage is in a threaded context final BsqState cloned = bsqState.getClone(snapshotCandidate); - storage.queueUpForSave(cloned); - log.info("Saved snapshotCandidate to Disc at height " + chainHeadHeight); + if (cloned.getBlocks().getLast().getHeight() >= genesisTxInfo.getGenesisBlockHeight()) + storage.queueUpForSave(cloned); + log.info("Saved snapshotCandidate to Disc at height " + chainHeight); } // Now we clone and keep it in memory for the next trigger snapshotCandidate = bsqState.getClone(); // don't access cloned anymore with methods as locks are transient! - log.debug("Cloned new snapshotCandidate at height " + chainHeadHeight); + log.info("Cloned new snapshotCandidate at height " + chainHeight); } } @@ -101,8 +108,9 @@ public class SnapshotManager implements BsqStateListener { checkNotNull(storage, "storage must not be null"); BsqState persisted = storage.initAndGetPersisted(bsqState, 100); if (persisted != null) { - log.info("applySnapshot persisted.chainHeadHeight=" + bsqStateService.getBlocksFromState(persisted).getLast().getHeight()); - bsqStateService.applySnapshot(persisted); + log.info("applySnapshot persisted.chainHeight=" + new LinkedList<>(persisted.getBlocks()).getLast().getHeight()); + if (persisted.getBlocks().getLast().getHeight() >= genesisTxInfo.getGenesisBlockHeight()) + bsqStateService.applySnapshot(persisted); } else { log.info("Try to apply snapshot but no stored snapshot available"); } diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/BaseBlock.java b/core/src/main/java/bisq/core/dao/state/blockchain/BaseBlock.java index 3ea8349c9f..ebc6c4b4f7 100644 --- a/core/src/main/java/bisq/core/dao/state/blockchain/BaseBlock.java +++ b/core/src/main/java/bisq/core/dao/state/blockchain/BaseBlock.java @@ -19,8 +19,11 @@ package bisq.core.dao.state.blockchain; import io.bisq.generated.protobuffer.PB; +import java.util.Optional; + import lombok.Data; +import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** @@ -33,6 +36,7 @@ public abstract class BaseBlock { protected final int height; protected final long time; // in ms protected final String hash; + @Nullable // in case of first block in the blockchain protected final String previousBlockHash; BaseBlock(int height, long time, String hash, String previousBlockHash) { @@ -48,11 +52,13 @@ public abstract class BaseBlock { /////////////////////////////////////////////////////////////////////////////////////////// PB.BaseBlock.Builder getBaseBlockBuilder() { - return PB.BaseBlock.newBuilder() + PB.BaseBlock.Builder builder = PB.BaseBlock.newBuilder() .setHeight(height) .setTime(time) - .setHash(hash) - .setPreviousBlockHash(previousBlockHash); + .setHash(hash); + Optional.ofNullable(previousBlockHash).ifPresent(builder::setPreviousBlockHash); + return builder; + } @Override diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/ScriptType.java b/core/src/main/java/bisq/core/dao/state/blockchain/ScriptType.java index e89ae2bc13..523f9928f5 100644 --- a/core/src/main/java/bisq/core/dao/state/blockchain/ScriptType.java +++ b/core/src/main/java/bisq/core/dao/state/blockchain/ScriptType.java @@ -35,7 +35,7 @@ import lombok.ToString; @JsonIgnoreProperties(ignoreUnknown = true) public enum ScriptType { - // https://github.com/bitcoin/bitcoin/blob/8152d3fe57a991e9088d0b9d261d2b10936f45a9/src/script/standard.cpp + // https://github.com/bitcoin/bitcoin/blob/master/src/script/standard.cpp PUB_KEY("pubkey"), PUB_KEY_HASH("pubkeyhash"), SCRIPT_HASH("scripthash"), @@ -43,6 +43,7 @@ public enum ScriptType { NULL_DATA("nulldata"), WITNESS_V0_KEYHASH("witness_v0_keyhash"), WITNESS_V0_SCRIPTHASH("witness_v0_scripthash"), + WITNESS_UNKNOWN("witness_unknown"), NONSTANDARD("nonstandard"); private final String name; diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/TempTx.java b/core/src/main/java/bisq/core/dao/state/blockchain/TempTx.java index 73da786329..5d2622c998 100644 --- a/core/src/main/java/bisq/core/dao/state/blockchain/TempTx.java +++ b/core/src/main/java/bisq/core/dao/state/blockchain/TempTx.java @@ -43,7 +43,6 @@ public class TempTx extends BaseTx { rawTx.getTxInputs(), ImmutableList.copyOf(rawTx.getRawTxOutputs().stream().map(TempTxOutput::fromRawTxOutput).collect(Collectors.toList())), null, - 0, 0); } @@ -53,8 +52,6 @@ public class TempTx extends BaseTx { @Nullable private TxType txType; private long burntFee; - // If not set it is -1. LockTime of 0 is a valid value. - private int unlockBlockHeight; /////////////////////////////////////////////////////////////////////////////////////////// @@ -69,8 +66,7 @@ public class TempTx extends BaseTx { ImmutableList txInputs, ImmutableList tempTxOutputs, @Nullable TxType txType, - long burntFee, - int unlockBlockHeight) { + long burntFee) { super(txVersion, id, blockHeight, @@ -80,17 +76,14 @@ public class TempTx extends BaseTx { this.tempTxOutputs = tempTxOutputs; this.txType = txType; this.burntFee = burntFee; - this.unlockBlockHeight = unlockBlockHeight; } - @Override public String toString() { return "TempTx{" + "\n txOutputs=" + tempTxOutputs + ",\n txType=" + txType + ",\n burntFee=" + burntFee + - ",\n unlockBlockHeight=" + unlockBlockHeight + "\n} " + super.toString(); } } diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/TempTxOutput.java b/core/src/main/java/bisq/core/dao/state/blockchain/TempTxOutput.java index b68c790540..2f8e8c7bc7 100644 --- a/core/src/main/java/bisq/core/dao/state/blockchain/TempTxOutput.java +++ b/core/src/main/java/bisq/core/dao/state/blockchain/TempTxOutput.java @@ -37,12 +37,18 @@ public class TempTxOutput extends BaseTxOutput { txOutput.getAddress(), txOutput.getOpReturnData(), txOutput.getBlockHeight(), - TxOutputType.UNDEFINED, + TxOutputType.UNDEFINED_OUTPUT, + -1, 0); } private TxOutputType txOutputType; + + // The lockTime is stored in the first output of the LOCKUP tx. + // If not set it is -1, 0 is a valid value. private int lockTime; + // The unlockBlockHeight is stored in the first output of the UNLOCK tx. + private int unlockBlockHeight; private TempTxOutput(int index, long value, @@ -52,7 +58,8 @@ public class TempTxOutput extends BaseTxOutput { @Nullable byte[] opReturnData, int blockHeight, TxOutputType txOutputType, - int lockTime) { + int lockTime, + int unlockBlockHeight) { super(index, value, txId, @@ -63,6 +70,7 @@ public class TempTxOutput extends BaseTxOutput { this.txOutputType = txOutputType; this.lockTime = lockTime; + this.unlockBlockHeight = unlockBlockHeight; } @@ -71,6 +79,12 @@ public class TempTxOutput extends BaseTxOutput { return "TempTxOutput{" + "\n txOutputType=" + txOutputType + "\n lockTime=" + lockTime + + "\n unlockBlockHeight=" + unlockBlockHeight + "\n} " + super.toString(); } + + public boolean isOpReturnOutput() { + // We do not check for pubKeyScript.scriptType.NULL_DATA because that is only set if dumpBlockchainData is true + return getOpReturnData() != null; + } } diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/Tx.java b/core/src/main/java/bisq/core/dao/state/blockchain/Tx.java index 75c369c573..d7917e0427 100644 --- a/core/src/main/java/bisq/core/dao/state/blockchain/Tx.java +++ b/core/src/main/java/bisq/core/dao/state/blockchain/Tx.java @@ -53,16 +53,13 @@ public final class Tx extends BaseTx implements PersistablePayload { tempTx.getTxInputs(), txOutputs, tempTx.getTxType(), - tempTx.getBurntFee(), - tempTx.getUnlockBlockHeight()); + tempTx.getBurntFee()); } private final ImmutableList txOutputs; @Nullable private final TxType txType; private final long burntFee; - // If not set it is -1. LockTime of 0 is a valid value. - private final int unlockBlockHeight; /////////////////////////////////////////////////////////////////////////////////////////// @@ -77,8 +74,7 @@ public final class Tx extends BaseTx implements PersistablePayload { ImmutableList txInputs, ImmutableList txOutputs, @Nullable TxType txType, - long burntFee, - int unlockBlockHeight) { + long burntFee) { super(txVersion, id, blockHeight, @@ -88,7 +84,7 @@ public final class Tx extends BaseTx implements PersistablePayload { this.txOutputs = txOutputs; this.txType = txType; this.burntFee = burntFee; - this.unlockBlockHeight = unlockBlockHeight; + } @Override @@ -97,8 +93,7 @@ public final class Tx extends BaseTx implements PersistablePayload { .addAllTxOutputs(txOutputs.stream() .map(TxOutput::toProtoMessage) .collect(Collectors.toList())) - .setBurntFee(burntFee) - .setUnlockBlockHeight(unlockBlockHeight); + .setBurntFee(burntFee); Optional.ofNullable(txType).ifPresent(txType -> builder.setTxType(txType.toProtoMessage())); return getBaseTxBuilder().setTx(builder).build(); } @@ -123,8 +118,7 @@ public final class Tx extends BaseTx implements PersistablePayload { txInputs, outputs, TxType.fromProto(protoTx.getTxType()), - protoTx.getBurntFee(), - protoTx.getUnlockBlockHeight()); + protoTx.getBurntFee()); } @@ -137,15 +131,14 @@ public final class Tx extends BaseTx implements PersistablePayload { } - /** - * OpReturn output might contain the lockTime in case of a LockTx. It has to be the last output. - * We store technically the lockTime there as is is stored in the OpReturn data but conceptually we want to provide - * it from the transaction. - * - * @return - */ + // The lockTime is stored in the first output of the LOCKUP tx. public int getLockTime() { - return getLastTxOutput().getLockTime(); + return txOutputs.get(0).getLockTime(); + } + + // The unlockBlockHeight is stored in the first output of the UNLOCK tx. + public int getUnlockBlockHeight() { + return txOutputs.get(0).getUnlockBlockHeight(); } @Override @@ -154,7 +147,6 @@ public final class Tx extends BaseTx implements PersistablePayload { "\n txOutputs=" + txOutputs + ",\n txType=" + txType + ",\n burntFee=" + burntFee + - ",\n unlockBlockHeight=" + unlockBlockHeight + "\n} " + super.toString(); } diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/TxOutput.java b/core/src/main/java/bisq/core/dao/state/blockchain/TxOutput.java index e0ff51a618..a38b66cfe1 100644 --- a/core/src/main/java/bisq/core/dao/state/blockchain/TxOutput.java +++ b/core/src/main/java/bisq/core/dao/state/blockchain/TxOutput.java @@ -44,11 +44,17 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload { tempTxOutput.getOpReturnData(), tempTxOutput.getBlockHeight(), tempTxOutput.getTxOutputType(), - tempTxOutput.getLockTime()); + tempTxOutput.getLockTime(), + tempTxOutput.getUnlockBlockHeight()); } private final TxOutputType txOutputType; + + // The lockTime is stored in the first output of the LOCKUP tx. + // If not set it is -1, 0 is a valid value. private final int lockTime; + // The unlockBlockHeight is stored in the first output of the UNLOCK tx. + private final int unlockBlockHeight; public TxOutput(int index, long value, @@ -58,7 +64,8 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload { @Nullable byte[] opReturnData, int blockHeight, TxOutputType txOutputType, - int lockTime) { + int lockTime, + int unlockBlockHeight) { super(index, value, txId, @@ -69,6 +76,7 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload { this.txOutputType = txOutputType; this.lockTime = lockTime; + this.unlockBlockHeight = unlockBlockHeight; } @@ -80,11 +88,13 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload { public PB.BaseTxOutput toProtoMessage() { PB.TxOutput.Builder builder = PB.TxOutput.newBuilder() .setTxOutputType(txOutputType.toProtoMessage()) - .setLockTime(lockTime); + .setLockTime(lockTime) + .setUnlockBlockHeight(unlockBlockHeight); return getRawTxOutputBuilder().setTxOutput(builder).build(); } public static TxOutput fromProto(PB.BaseTxOutput proto) { + PB.TxOutput protoTxOutput = proto.getTxOutput(); return new TxOutput(proto.getIndex(), proto.getValue(), proto.getTxId(), @@ -92,8 +102,9 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload { proto.getAddress().isEmpty() ? null : proto.getAddress(), proto.getOpReturnData().isEmpty() ? null : proto.getOpReturnData().toByteArray(), proto.getBlockHeight(), - TxOutputType.fromProto(proto.getTxOutput().getTxOutputType()), - proto.getTxOutput().getLockTime()); + TxOutputType.fromProto(protoTxOutput.getTxOutputType()), + protoTxOutput.getLockTime(), + protoTxOutput.getUnlockBlockHeight()); } @@ -102,6 +113,7 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload { return "TxOutput{" + "\n txOutputType=" + txOutputType + "\n lockTime=" + lockTime + + ",\n unlockBlockHeight=" + unlockBlockHeight + "\n} " + super.toString(); } } diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/TxOutputType.java b/core/src/main/java/bisq/core/dao/state/blockchain/TxOutputType.java index 17b7c1ad82..a4ed27bbb2 100644 --- a/core/src/main/java/bisq/core/dao/state/blockchain/TxOutputType.java +++ b/core/src/main/java/bisq/core/dao/state/blockchain/TxOutputType.java @@ -22,7 +22,7 @@ import bisq.common.proto.ProtoUtil; import io.bisq.generated.protobuffer.PB; public enum TxOutputType { - UNDEFINED, + UNDEFINED_OUTPUT, GENESIS_OUTPUT, BSQ_OUTPUT, BTC_OUTPUT, @@ -34,9 +34,9 @@ public enum TxOutputType { BLIND_VOTE_OP_RETURN_OUTPUT, VOTE_REVEAL_UNLOCK_STAKE_OUTPUT, VOTE_REVEAL_OP_RETURN_OUTPUT, - LOCKUP, + LOCKUP_OUTPUT, LOCKUP_OP_RETURN_OUTPUT, - UNLOCK, + UNLOCK_OUTPUT, INVALID_OUTPUT; diff --git a/core/src/main/java/bisq/core/dao/state/governance/Issuance.java b/core/src/main/java/bisq/core/dao/state/governance/Issuance.java index 1334b1a976..daea557313 100644 --- a/core/src/main/java/bisq/core/dao/state/governance/Issuance.java +++ b/core/src/main/java/bisq/core/dao/state/governance/Issuance.java @@ -75,4 +75,14 @@ public class Issuance implements PersistablePayload, NetworkPayload { proto.getAmount(), proto.getPubKey().isEmpty() ? null : proto.getPubKey()); } + + @Override + public String toString() { + return "Issuance{" + + "\n txId='" + txId + '\'' + + ",\n chainHeight=" + chainHeight + + ",\n amount=" + amount + + ",\n pubKey='" + pubKey + '\'' + + "\n}"; + } } diff --git a/core/src/main/java/bisq/core/dao/state/governance/Param.java b/core/src/main/java/bisq/core/dao/state/governance/Param.java index a65ffed2d7..10c1e31665 100644 --- a/core/src/main/java/bisq/core/dao/state/governance/Param.java +++ b/core/src/main/java/bisq/core/dao/state/governance/Param.java @@ -42,52 +42,79 @@ import static com.google.common.base.Preconditions.checkNotNull; public enum Param { UNDEFINED(0), - // TODO trade fee is not implemented yet to be actually used. - // FeeService is setting the fee atm.... - // 0.2% 100 = 1%, 1 is 0.01% - BSQ_MAKER_FEE_IN_PERCENT(20), - BSQ_TAKER_FEE_IN_PERCENT(20), - BTC_MAKER_FEE_IN_PERCENT(20), - BTC_TAKER_FEE_IN_PERCENT(20), + // Fee in BSQ satoshi for a 1 BTC trade. 200 Satoshi = 2 BSQ = 0.02%. + // About 2 USD if 1 BSQ = 1 USD for a 1 BTC trade which is about 10% of the BTC fee., + // Might need adjustment if BSQ/BTC rate changes. + DEFAULT_MAKER_FEE_BSQ(200), // 0.02% + DEFAULT_TAKER_FEE_BSQ(200), + // 0.05 BSQ (5 satoshi) for a 1 BTC trade. 0.05 USD if 1 BSQ = 1 USD, 10 % of the BTC fee + MIN_MAKER_FEE_BSQ(5), // 0.0005%. + MIN_TAKER_FEE_BSQ(5), + + + // Fee in BTC satoshi for a 1 BTC trade. 200_000 Satoshi = 0.00200000 BTC = 0.2%. + // 20 USD at BTC price 10_000 USD for a 1 BTC trade; + DEFAULT_MAKER_FEE_BTC(200_000), + DEFAULT_TAKER_FEE_BTC(200_000), // 0.2% + MIN_MAKER_FEE_BTC(5_000), // 0.005%. + MIN_TAKER_FEE_BTC(5_000), // Fees proposal/voting. Atm we don't use diff. fees for diff. proposal types - PROPOSAL_FEE(100), // 5 BSQ TODO change low dev - BLIND_VOTE_FEE(200), // 10 BSQ TODO change low dev + // See: https://github.com/bisq-network/proposals/issues/46 + PROPOSAL_FEE(200), // 2 BSQ + BLIND_VOTE_FEE(200), // 2 BSQ - // Quorum for voting in BSQ stake - QUORUM_PROPOSAL(100), // 10 000 BSQ TODO change low dev value - QUORUM_COMP_REQUEST(100), // 10 000 BSQ TODO change low dev value - QUORUM_CHANGE_PARAM(300), // 100 000 BSQ TODO change low dev value - QUORUM_REMOVE_ASSET(400), // 10 000 BSQ TODO change low dev value - QUORUM_CONFISCATION(500), // 10 000 BSQ TODO change low dev value + // As BSQ based validation values can change over time if BSQ value rise we need to support that in the Params as well + COMPENSATION_REQUEST_MIN_AMOUNT(1_000), // 10 BSQ + COMPENSATION_REQUEST_MAX_AMOUNT(10_000_000), // 100 000 BSQ + + // Quorum required for voting result to be valid. + // Quorum is the min. amount of total BSQ (earned+stake) which was used for voting on a request. + // E.g. If only 2000 BSQ was used on a vote but 10 000 is required the result is invalid even if the voters voted + // 100% for acceptance. This should prevent that changes can be done with low stakeholder participation. + QUORUM_COMP_REQUEST(2_000_000), // 20 000 BSQ + QUORUM_CHANGE_PARAM(10_000_000), // 100 000 BSQ + QUORUM_ROLE(5_000_000), // 50 000 BSQ + QUORUM_CONFISCATION(20_000_000), // 200 000 BSQ + QUORUM_GENERIC(500_000), // 5 000 BSQ + QUORUM_REMOVE_ASSET(1_000_000), // 10 000 BSQ // Threshold for voting in % with precision of 2 (e.g. 5000 -> 50.00%) - THRESHOLD_PROPOSAL(5_000), // 50% + // This is the required amount of weighted vote result needed for acceptance of the result. + // 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%. THRESHOLD_COMP_REQUEST(5_000), // 50% - THRESHOLD_CHANGE_PARAM(7_500), // 75% -> that might change the THRESHOLD_CHANGE_PARAM and QUORUM_CHANGE_PARAM! + THRESHOLD_CHANGE_PARAM(7_500), // 75% That might change the THRESHOLD_CHANGE_PARAM and QUORUM_CHANGE_PARAM as well. So we have to be careful here! + THRESHOLD_ROLE(5_000), // 50% + THRESHOLD_CONFISCATION(8_500), // 85% Confiscation is considered an exceptional case and need very high consensus among the stakeholders. + THRESHOLD_GENERIC(5_000), // 50% THRESHOLD_REMOVE_ASSET(5_000), // 50% - THRESHOLD_CONFISCATION(8_500), // 85% - // Period phase (16 blocks atm) + //TODO add asset listing params (nr. of trades, volume, time, fee which defines listing state) + + // TODO for dev testing we use short periods... + // Period phase (11 blocks atm) PHASE_UNDEFINED(0), - PHASE_PROPOSAL(2), // 24 days - PHASE_BREAK1(1), // 10 blocks - PHASE_BLIND_VOTE(2), // 4 days - PHASE_BREAK2(1), // 10 blocks - PHASE_VOTE_REVEAL(1), // 2 days - PHASE_BREAK3(1), // 10 blocks - PHASE_RESULT(1), // 1 block - PHASE_BREAK4(1); // 10 blocks + PHASE_PROPOSAL(2), + PHASE_BREAK1(1), + PHASE_BLIND_VOTE(2), + PHASE_BREAK2(1), + PHASE_VOTE_REVEAL(2), + PHASE_BREAK3(1), + PHASE_RESULT(2); - /*PHASE_UNDEFINED(0), - PHASE_PROPOSAL(3456), // 24 days - PHASE_BREAK1(10), // 10 blocks - PHASE_BLIND_VOTE(576), // 4 days + // See: https://github.com/bisq-network/proposals/issues/46 + /* + PHASE_UNDEFINED(0), + PHASE_PROPOSAL(3600), // 24 days + PHASE_BREAK1(150), // 1 day + PHASE_BLIND_VOTE(600), // 4 days PHASE_BREAK2(10), // 10 blocks - PHASE_VOTE_REVEAL(432), // 2 days + PHASE_VOTE_REVEAL(300), // 2 days PHASE_BREAK3(10), // 10 blocks - PHASE_RESULT(1), // 1 block - PHASE_BREAK4(10); // 10 blocks*/ + PHASE_RESULT(10); // 10 block + */ @Getter private long defaultValue; diff --git a/core/src/main/java/bisq/core/dao/state/period/CycleService.java b/core/src/main/java/bisq/core/dao/state/period/CycleService.java index c08067b558..de1fe2e554 100644 --- a/core/src/main/java/bisq/core/dao/state/period/CycleService.java +++ b/core/src/main/java/bisq/core/dao/state/period/CycleService.java @@ -17,17 +17,15 @@ package bisq.core.dao.state.period; -import bisq.core.dao.DaoOptionKeys; import bisq.core.dao.DaoSetupService; import bisq.core.dao.state.BsqStateListener; import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.GenesisTxInfo; import bisq.core.dao.state.blockchain.Block; import bisq.core.dao.state.governance.Param; import com.google.inject.Inject; -import javax.inject.Named; - import com.google.common.collect.ImmutableList; import java.util.Arrays; @@ -51,9 +49,9 @@ public class CycleService implements BsqStateListener, DaoSetupService { @Inject public CycleService(BsqStateService bsqStateService, - @Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) int genesisBlockHeight) { + GenesisTxInfo genesisTxInfo) { this.bsqStateService = bsqStateService; - this.genesisBlockHeight = genesisBlockHeight; + this.genesisBlockHeight = genesisTxInfo.getGenesisBlockHeight(); } diff --git a/core/src/main/java/bisq/core/dao/state/period/PeriodService.java b/core/src/main/java/bisq/core/dao/state/period/PeriodService.java index 780241993f..2ac09506e9 100644 --- a/core/src/main/java/bisq/core/dao/state/period/PeriodService.java +++ b/core/src/main/java/bisq/core/dao/state/period/PeriodService.java @@ -129,9 +129,9 @@ public final class PeriodService { .orElse(0); } - public boolean isTxInPastCycle(String txId, int chainHeadHeight) { + public boolean isTxInPastCycle(String txId, int chainHeight) { return getOptionalTx(txId) - .filter(tx -> isTxInPastCycle(tx.getBlockHeight(), chainHeadHeight)) + .filter(tx -> isTxInPastCycle(tx.getBlockHeight(), chainHeight)) .isPresent(); } diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 4969615a1f..5aeaa55661 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -19,7 +19,7 @@ package bisq.core.filter; import bisq.core.app.AppOptionKeys; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.BitcoinNodes; +import bisq.core.btc.nodes.BtcNodes; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.payment.payload.PaymentMethod; import bisq.core.provider.ProvidersRepository; @@ -222,8 +222,8 @@ public class FilterManager { listeners.forEach(e -> e.onFilterAdded(filter)); if (filter.isPreventPublicBtcNetwork() && - preferences.getBitcoinNodesOptionOrdinal() == BitcoinNodes.BitcoinNodesOption.PUBLIC.ordinal()) - preferences.setBitcoinNodesOptionOrdinal(BitcoinNodes.BitcoinNodesOption.PROVIDED.ordinal()); + preferences.getBitcoinNodesOptionOrdinal() == BtcNodes.BitcoinNodesOption.PUBLIC.ordinal()) + preferences.setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.PROVIDED.ordinal()); } } diff --git a/core/src/main/java/bisq/core/locale/CurrencyUtil.java b/core/src/main/java/bisq/core/locale/CurrencyUtil.java index 3cac7dcfb6..d9468d634f 100644 --- a/core/src/main/java/bisq/core/locale/CurrencyUtil.java +++ b/core/src/main/java/bisq/core/locale/CurrencyUtil.java @@ -18,12 +18,8 @@ package bisq.core.locale; import bisq.core.app.BisqEnvironment; - -import bisq.asset.Asset; -import bisq.asset.AssetRegistry; -import bisq.asset.Coin; -import bisq.asset.Token; -import bisq.asset.coins.BSQ; +import bisq.core.btc.BaseCurrencyNetwork; +import bisq.core.dao.governance.asset.AssetService; import bisq.common.app.DevEnv; @@ -38,8 +34,19 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import static com.google.common.base.Preconditions.checkArgument; + + + +import bisq.asset.Asset; +import bisq.asset.AssetRegistry; +import bisq.asset.Coin; +import bisq.asset.Token; +import bisq.asset.coins.BSQ; + @Slf4j public class CurrencyUtil { @@ -47,6 +54,7 @@ public class CurrencyUtil { setBaseCurrencyCode(BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()); } + @Getter private static final AssetRegistry assetRegistry = new AssetRegistry(); private static String baseCurrencyCode = "BTC"; @@ -106,14 +114,13 @@ public class CurrencyUtil { private static List createAllSortedCryptoCurrenciesList() { List result = assetRegistry.stream() .filter(CurrencyUtil::assetIsNotBaseCurrency) - .filter(CurrencyUtil::excludeBsqUnlessDaoTradingIsActive) - .filter(CurrencyUtil::assetMatchesNetwork) + .filter(asset -> isNotBsqOrBsqTradingActivated(asset, BisqEnvironment.getBaseCurrencyNetwork(), DevEnv.isDaoTradingActivated())) + .filter(asset -> assetMatchesNetworkIfMainnet(asset, BisqEnvironment.getBaseCurrencyNetwork())) .map(CurrencyUtil::assetToCryptoCurrency) .sorted(TradeCurrency::compareTo) .collect(Collectors.toList()); // Util for printing all altcoins for adding to FAQ page - /* StringBuilder sb = new StringBuilder(); result.stream().forEach(e -> sb.append("

  • “") .append(e.getCode()) @@ -344,7 +351,6 @@ public class CurrencyUtil { return new FiatCurrency(currency.getCurrencyCode()); } - public static String getNameByCode(String currencyCode) { if (isCryptoCurrency(currencyCode)) return getCryptoCurrency(currencyCode).get().getName(); @@ -357,6 +363,12 @@ public class CurrencyUtil { } } + public static Optional findCryptoCurrencyByName(String currencyName) { + return getAllSortedCryptoCurrencies().stream() + .filter(e -> e.getName().equals(currencyName)) + .findAny(); + } + public static String getNameAndCode(String currencyCode) { return getNameByCode(currencyCode) + " (" + currencyCode + ")"; } @@ -366,20 +378,84 @@ public class CurrencyUtil { } private static boolean assetIsNotBaseCurrency(Asset asset) { - return !asset.getTickerSymbol().equals(baseCurrencyCode); + return !assetMatchesCurrencyCode(asset, baseCurrencyCode); } - private static boolean assetMatchesNetwork(Asset asset) { + // TODO We handle assets of other types (Token, ERC20) as matching the network which is not correct. + // We should add support for network property in those tokens as well. + public static boolean assetMatchesNetwork(Asset asset, BaseCurrencyNetwork baseCurrencyNetwork) { return !(asset instanceof Coin) || - ((Coin) asset).getNetwork().name().equals(BisqEnvironment.getDefaultBaseCurrencyNetwork().getNetwork()); + ((Coin) asset).getNetwork().name().equals(baseCurrencyNetwork.getNetwork()); + } + + // We only check for coins not other types of assets (TODO network check should be supported for all assets) + public static boolean assetMatchesNetworkIfMainnet(Asset asset, BaseCurrencyNetwork baseCurrencyNetwork) { + return !(asset instanceof Coin) || + coinMatchesNetworkIfMainnet((Coin) asset, baseCurrencyNetwork); + } + + // 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; } private static CryptoCurrency assetToCryptoCurrency(Asset asset) { return new CryptoCurrency(asset.getTickerSymbol(), asset.getName(), asset instanceof Token); } - private static boolean excludeBsqUnlessDaoTradingIsActive(Asset asset) { - return (!(asset instanceof BSQ) || (DevEnv.isDaoTradingActivated() - && ((BSQ) asset).getNetwork().name().equals(BisqEnvironment.getBaseCurrencyNetwork().getNetwork()))); + private static boolean isNotBsqOrBsqTradingActivated(Asset asset, BaseCurrencyNetwork baseCurrencyNetwork, boolean daoTradingActivated) { + return !(asset instanceof BSQ) || + daoTradingActivated && assetMatchesNetwork(asset, baseCurrencyNetwork); + } + + public static boolean assetMatchesCurrencyCode(Asset asset, String currencyCode) { + return currencyCode.equals(asset.getTickerSymbol()); + } + + public static Optional findAsset(AssetRegistry assetRegistry, String currencyCode, + BaseCurrencyNetwork baseCurrencyNetwork, boolean daoTradingActivated) { + List assets = assetRegistry.stream() + .filter(asset -> assetMatchesCurrencyCode(asset, currencyCode)).collect(Collectors.toList()); + + // If we don't have the ticker symbol we throw an exception + if (!assets.stream().findFirst().isPresent()) + return Optional.empty(); + + if (currencyCode.equals("BSQ") && baseCurrencyNetwork.isMainnet() && !daoTradingActivated) + return Optional.empty(); + + // We check for exact match with network, e.g. BTC$TESTNET + Optional optionalAssetMatchesNetwork = assets.stream() + .filter(asset -> assetMatchesNetwork(asset, baseCurrencyNetwork)) + .findFirst(); + if (optionalAssetMatchesNetwork.isPresent()) + return optionalAssetMatchesNetwork; + + // In testnet or regtest we want to show all coins as well. Most coins have only Mainnet defined so we deliver + // that if no exact match was found in previous step + if (!baseCurrencyNetwork.isMainnet()) { + Optional optionalAsset = assets.stream().findFirst(); + checkArgument(optionalAsset.isPresent(), "optionalAsset must be present as we checked for " + + "not matching ticker symbols already above"); + return optionalAsset; + } + + // If we are in mainnet we need have a mainet asset defined. + throw new IllegalArgumentException("We are on mainnet and we could not find an asset with network type mainnet"); + } + + public static Optional findAsset(String tickerSymbol, BaseCurrencyNetwork baseCurrencyNetwork) { + return assetRegistry.stream() + .filter(asset -> asset.getTickerSymbol().equals(tickerSymbol)) + .filter(asset -> assetMatchesNetwork(asset, baseCurrencyNetwork)) + .findAny(); + } + + public static List getWhiteListedSortedCryptoCurrencies(AssetService assetService) { + return getAllSortedCryptoCurrencies().stream() + .filter(e -> !assetService.isAssetRemoved(e.getCode())) + .collect(Collectors.toList()); } } 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 index 79c516fb15..34c6d6dd18 100644 --- a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeAddresses.java +++ b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeAddresses.java @@ -79,8 +79,9 @@ class DefaultSeedNodeAddresses { // new NodeAddress("uqxi3zrpobhtoes6.onion:8000"), // BTC testnet - new NodeAddress("nbphlanpgbei4okt.onion:8001"), - // new NodeAddress("vjkh4ykq7x5skdlt.onion:8001"), // dev test + new NodeAddress("nbphlanpgbei4okt.onion:8001"), // 88.99.216.98 + new NodeAddress("o5qw2hy6l7jsf654.onion:8001"), // explorer node + new NodeAddress("vjkh4ykq7x5skdlt.onion:8001"), // local dev test // BTC regtest // For development you need to change that to your local onion addresses diff --git a/core/src/main/java/bisq/core/notifications/MobileModel.java b/core/src/main/java/bisq/core/notifications/MobileModel.java index d4d50b4b57..ffd015afa0 100644 --- a/core/src/main/java/bisq/core/notifications/MobileModel.java +++ b/core/src/main/java/bisq/core/notifications/MobileModel.java @@ -68,7 +68,7 @@ public class MobileModel { } public void applyKeyAndToken(String keyAndToken) { - log.info("phoneId={}", keyAndToken); + log.info("applyKeyAndToken: keyAndToken={}", keyAndToken.substring(0, 20) + "...(truncated in log for privacy reasons)"); String[] tokens = keyAndToken.split(PHONE_SEPARATOR_ESCAPED); String magic = tokens[0]; descriptor = tokens[1]; diff --git a/core/src/main/java/bisq/core/notifications/MobileNotificationService.java b/core/src/main/java/bisq/core/notifications/MobileNotificationService.java index e7481e7612..4148444333 100644 --- a/core/src/main/java/bisq/core/notifications/MobileNotificationService.java +++ b/core/src/main/java/bisq/core/notifications/MobileNotificationService.java @@ -130,8 +130,7 @@ public class MobileNotificationService { } public boolean sendMessage(MobileMessage message, boolean useSound) throws Exception { - log.info("sendMessage\n" + - "Title: " + message.getTitle() + "\nMessage: " + message.getMessage()); + log.info("Send message: '{}'", message.getMessage()); if (mobileModel.getKey() == null) return false; diff --git a/core/src/main/java/bisq/core/offer/OfferUtil.java b/core/src/main/java/bisq/core/offer/OfferUtil.java index 3a1dd3a040..bc87e11f4a 100644 --- a/core/src/main/java/bisq/core/offer/OfferUtil.java +++ b/core/src/main/java/bisq/core/offer/OfferUtil.java @@ -18,8 +18,8 @@ package bisq.core.offer; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.Restrictions; import bisq.core.btc.wallet.BsqWalletService; +import bisq.core.btc.wallet.Restrictions; import bisq.core.monetary.Price; import bisq.core.monetary.Volume; import bisq.core.provider.fee.FeeService; @@ -89,13 +89,14 @@ public class OfferUtil { @Nullable public static Coin getMakerFee(boolean isCurrencyForMakerFeeBtc, @Nullable Coin amount, boolean marketPriceAvailable, double marketPriceMargin) { if (amount != null) { - final Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getMakerFeePerBtc(isCurrencyForMakerFeeBtc), amount); + Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getMakerFeePerBtc(isCurrencyForMakerFeeBtc), amount); double makerFeeAsDouble = (double) feePerBtc.value; if (marketPriceAvailable) { if (marketPriceMargin > 0) makerFeeAsDouble = makerFeeAsDouble * Math.sqrt(marketPriceMargin * 100); else makerFeeAsDouble = 0; + // For BTC we round so min value change is 100 satoshi if (isCurrencyForMakerFeeBtc) makerFeeAsDouble = MathUtils.roundDouble(makerFeeAsDouble / 100, 0) * 100; 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 8f343505ff..80c9f8b3ba 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 @@ -18,11 +18,11 @@ package bisq.core.offer.placeoffer.tasks; import bisq.core.arbitration.Arbitrator; -import bisq.core.btc.AddressEntry; +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.TxBroadcastException; import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.btc.wallet.WalletService; import bisq.core.offer.Offer; diff --git a/core/src/main/java/bisq/core/payment/OKPayAccount.java b/core/src/main/java/bisq/core/payment/OKPayAccount.java index 4009ee2c56..42282e93e7 100644 --- a/core/src/main/java/bisq/core/payment/OKPayAccount.java +++ b/core/src/main/java/bisq/core/payment/OKPayAccount.java @@ -24,7 +24,7 @@ import bisq.core.payment.payload.PaymentMethod; import lombok.EqualsAndHashCode; -//TODO missing support for selected trade currency +@Deprecated @EqualsAndHashCode(callSuper = true) public final class OKPayAccount extends PaymentAccount { public OKPayAccount() { diff --git a/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java b/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java index 111f841ae7..817f7f0cac 100644 --- a/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java +++ b/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java @@ -34,6 +34,7 @@ import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; +@Deprecated @EqualsAndHashCode(callSuper = true) @ToString @Setter 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 209b512df1..e42ddbd8d4 100644 --- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java @@ -51,6 +51,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable { // time in blocks (average 10 min for one block confirmation private static final long DAY = TimeUnit.HOURS.toMillis(24); + @Deprecated public static final String OK_PAY_ID = "OK_PAY"; public static final String UPHOLD_ID = "UPHOLD"; @Deprecated @@ -81,6 +82,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable { public static final String F2F_ID = "F2F"; public static final String BLOCK_CHAINS_ID = "BLOCK_CHAINS"; + @Deprecated public static PaymentMethod OK_PAY; public static PaymentMethod UPHOLD; @Deprecated diff --git a/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java b/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java index 5a8eb4432b..c6028c52e6 100644 --- a/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java +++ b/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java @@ -18,20 +18,23 @@ package bisq.core.payment.validation; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.BaseCurrencyNetwork; +import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.util.validation.InputValidator; +import bisq.common.app.DevEnv; + +import com.google.inject.Inject; + +import java.util.Optional; + +import lombok.extern.slf4j.Slf4j; + + + import bisq.asset.AddressValidationResult; import bisq.asset.Asset; import bisq.asset.AssetRegistry; -import bisq.asset.Coin; - -import com.google.inject.Inject; - -import lombok.extern.slf4j.Slf4j; - -import static java.lang.String.format; @Slf4j public final class AltCoinAddressValidator extends InputValidator { @@ -54,30 +57,19 @@ public final class AltCoinAddressValidator extends InputValidator { if (!validationResult.isValid || currencyCode == null) return validationResult; - Asset asset = assetRegistry.stream() - .filter(this::assetMatchesSelectedCurrencyCode) - .filter(this::assetIsNotBaseCurrencyForDifferentNetwork) - .findFirst() - .orElseThrow(() -> - new IllegalArgumentException(format("'%s' is not a registered asset", currencyCode))); + Optional optionalAsset = CurrencyUtil.findAsset(assetRegistry, currencyCode, + BisqEnvironment.getBaseCurrencyNetwork(), DevEnv.isDaoTradingActivated()); + if (optionalAsset.isPresent()) { + Asset asset = optionalAsset.get(); + AddressValidationResult result = asset.validateAddress(input); + if (!result.isValid()) { + return new ValidationResult(false, Res.get(result.getI18nKey(), asset.getTickerSymbol(), + result.getMessage())); + } - AddressValidationResult result = asset.validateAddress(input); - if (!result.isValid()) - return new ValidationResult(false, - Res.get(result.getI18nKey(), asset.getTickerSymbol(), result.getMessage())); - - return new ValidationResult(true); - } - - private boolean assetMatchesSelectedCurrencyCode(Asset a) { - return currencyCode.equals(a.getTickerSymbol()); - } - - private boolean assetIsNotBaseCurrencyForDifferentNetwork(Asset asset) { - BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); - - return !(asset instanceof Coin) - || !asset.getTickerSymbol().equals(baseCurrencyNetwork.getCurrencyCode()) - || (((Coin) asset).getNetwork().name().equals(baseCurrencyNetwork.getNetwork())); + return new ValidationResult(true); + } else { + return new ValidationResult(false); + } } } diff --git a/core/src/main/java/bisq/core/presentation/BalancePresentation.java b/core/src/main/java/bisq/core/presentation/BalancePresentation.java index 3ddd94cb16..311a8570ea 100644 --- a/core/src/main/java/bisq/core/presentation/BalancePresentation.java +++ b/core/src/main/java/bisq/core/presentation/BalancePresentation.java @@ -17,7 +17,7 @@ package bisq.core.presentation; -import bisq.core.btc.BalanceModel; +import bisq.core.btc.model.BalanceModel; import bisq.core.util.BSFormatter; import javax.inject.Inject; diff --git a/core/src/main/java/bisq/core/proto/ProtoDevUtil.java b/core/src/main/java/bisq/core/proto/ProtoDevUtil.java index 84ce44b125..c62b65e0ea 100644 --- a/core/src/main/java/bisq/core/proto/ProtoDevUtil.java +++ b/core/src/main/java/bisq/core/proto/ProtoDevUtil.java @@ -18,7 +18,7 @@ package bisq.core.proto; import bisq.core.arbitration.DisputeResult; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.offer.AvailabilityResult; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; diff --git a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java index dc56715e2b..9a119c57c1 100644 --- a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java @@ -26,6 +26,7 @@ import bisq.core.arbitration.messages.DisputeResultMessage; import bisq.core.arbitration.messages.OpenNewDisputeMessage; import bisq.core.arbitration.messages.PeerOpenedDisputeMessage; import bisq.core.arbitration.messages.PeerPublishedDisputePayoutTxMessage; +import bisq.core.dao.governance.blindvote.network.messages.RepublishGovernanceDataRequest; import bisq.core.dao.governance.proposal.storage.temp.TempProposalPayload; import bisq.core.dao.node.messages.GetBlocksRequest; import bisq.core.dao.node.messages.GetBlocksResponse; @@ -157,6 +158,9 @@ public class CoreNetworkProtoResolver extends CoreProtoResolver implements Netwo return AddPersistableNetworkPayloadMessage.fromProto(proto.getAddPersistableNetworkPayloadMessage(), this, messageVersion); case ACK_MESSAGE: return AckMessage.fromProto(proto.getAckMessage(), messageVersion); + case REPUBLISH_GOVERNANCE_DATA_REQUEST: + return RepublishGovernanceDataRequest.fromProto(proto.getRepublishGovernanceDataRequest(), messageVersion); + default: throw new ProtobufferException("Unknown proto message case (PB.NetworkEnvelope). messageCase=" + proto.getMessageCase() + "; proto raw data=" + proto.toString()); diff --git a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java index ef78c0caee..ddecb704b4 100644 --- a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java @@ -18,8 +18,9 @@ package bisq.core.proto.persistable; import bisq.core.arbitration.DisputeList; -import bisq.core.btc.AddressEntryList; +import bisq.core.btc.model.AddressEntryList; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.dao.governance.asset.RemovedAssetsList; import bisq.core.dao.governance.ballot.BallotList; import bisq.core.dao.governance.blindvote.MyBlindVoteList; import bisq.core.dao.governance.blindvote.storage.BlindVoteStore; @@ -29,6 +30,8 @@ import bisq.core.dao.governance.proposal.MyProposalList; import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStore; import bisq.core.dao.governance.proposal.storage.temp.TempProposalStore; import bisq.core.dao.governance.role.BondedRoleList; +import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMeritsList; +import bisq.core.dao.governance.voteresult.EvaluatedProposalList; import bisq.core.dao.state.BsqState; import bisq.core.payment.AccountAgeWitnessStore; import bisq.core.payment.PaymentAccountList; @@ -132,6 +135,12 @@ public class CorePersistenceProtoResolver extends CoreProtoResolver implements P return MeritList.fromProto(proto.getMeritList()); case BONDED_ROLE_LIST: return BondedRoleList.fromProto(proto.getBondedRoleList()); + case REMOVED_ASSET_LIST: + return RemovedAssetsList.fromProto(proto.getRemovedAssetList()); + case EVALUATED_PROPOSAL_LIST: + return EvaluatedProposalList.fromProto(proto.getEvaluatedProposalList()); + case DECRYPTED_BALLOTS_WITH_MERITS_LIST: + return DecryptedBallotsWithMeritsList.fromProto(proto.getDecryptedBallotsWithMeritsList()); default: throw new ProtobufferRuntimeException("Unknown proto message case(PB.PersistableEnvelope). " + "messageCase=" + proto.getMessageCase() + "; proto raw data=" + proto.toString()); diff --git a/core/src/main/java/bisq/core/provider/fee/FeeService.java b/core/src/main/java/bisq/core/provider/fee/FeeService.java index 9ffc31fdac..f6a878541a 100644 --- a/core/src/main/java/bisq/core/provider/fee/FeeService.java +++ b/core/src/main/java/bisq/core/provider/fee/FeeService.java @@ -18,13 +18,15 @@ package bisq.core.provider.fee; import bisq.core.app.BisqEnvironment; +import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.governance.Param; +import bisq.core.dao.state.period.PeriodService; import bisq.common.UserThread; import bisq.common.handlers.FaultHandler; import bisq.common.util.Tuple2; import org.bitcoinj.core.Coin; -import org.bitcoinj.core.Transaction; import com.google.inject.Inject; @@ -53,40 +55,53 @@ import static com.google.common.base.Preconditions.checkNotNull; @Slf4j public class FeeService { - // fixed min fee - public static final Coin BTC_REFERENCE_DEFAULT_MIN_TX_FEE_PER_KB = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; // 5000 - // https://litecoin.info/Transaction_fees min fee is 100_000 - public static final Coin LTC_REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(100_000); - //TODO check - // min tx fee per tx is 10000 now, 1000 in sept 2017 - public static final Coin DASH_REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(10_000); + /////////////////////////////////////////////////////////////////////////////////////////// + // Static + /////////////////////////////////////////////////////////////////////////////////////////// - // DEFAULT_TX_FEE used in FeeRequestService for non-BTC currencies and for BTC only if we cannot access fee service - // fees are per byte - public static final long BTC_DEFAULT_TX_FEE = 200; // fees are between 20-600 sat/byte. We try to stay on the safe side. - public static final long LTC_DEFAULT_TX_FEE = LTC_REFERENCE_DEFAULT_MIN_TX_FEE.value / 200; - public static final long DASH_DEFAULT_TX_FEE = DASH_REFERENCE_DEFAULT_MIN_TX_FEE.value / 200; // 200 bytes tx -> 200*50=10000 + // Miner fees are between 1-600 sat/byte. We try to stay on the safe side. BTC_DEFAULT_TX_FEE is only used if our + // fee service would not deliver data. + private static final long BTC_DEFAULT_TX_FEE = 50; + private static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 2; + private static BsqStateService bsqStateService; + private static PeriodService periodService; - private static long MIN_MAKER_FEE_IN_BASE_CUR; - private static long MIN_TAKER_FEE_IN_BASE_CUR; - private static long DEFAULT_MAKER_FEE_IN_BASE_CUR; - private static long DEFAULT_TAKER_FEE_IN_BASE_CUR; + private static long getFeeFromParam(Param parm) { + return bsqStateService != null && periodService != null ? bsqStateService.getParamValue(parm, periodService.getChainHeight()) : 0; + } - private static final long MIN_MAKER_FEE_IN_BSQ = 5; - private static final long MIN_TAKER_FEE_IN_BSQ = 5; - private static final long DEFAULT_MAKER_FEE_IN_BSQ = 200; // about 2 USD at 1 BSQ = 1 USD for a 1 BTC trade - private static final long DEFAULT_TAKER_FEE_IN_BSQ = 200; + public static Coin getMakerFeePerBtc(boolean currencyForFeeIsBtc) { + long fee = currencyForFeeIsBtc ? getFeeFromParam(Param.DEFAULT_MAKER_FEE_BTC) : getFeeFromParam(Param.DEFAULT_MAKER_FEE_BSQ); + return Coin.valueOf(fee); + } - public static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 2; + public static Coin getMinMakerFee(boolean currencyForFeeIsBtc) { + long fee = currencyForFeeIsBtc ? getFeeFromParam(Param.MIN_MAKER_FEE_BTC) : getFeeFromParam(Param.MIN_MAKER_FEE_BSQ); + return Coin.valueOf(fee); + } + + public static Coin getTakerFeePerBtc(boolean currencyForFeeIsBtc) { + long fee = currencyForFeeIsBtc ? getFeeFromParam(Param.DEFAULT_TAKER_FEE_BTC) : getFeeFromParam(Param.DEFAULT_TAKER_FEE_BSQ); + return Coin.valueOf(fee); + } + + public static Coin getMinTakerFee(boolean currencyForFeeIsBtc) { + long fee = currencyForFeeIsBtc ? getFeeFromParam(Param.MIN_TAKER_FEE_BTC) : getFeeFromParam(Param.MIN_TAKER_FEE_BSQ); + return Coin.valueOf(fee); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Class fields + /////////////////////////////////////////////////////////////////////////////////////////// private final FeeProvider feeProvider; - private final String baseCurrencyCode; - private long txFeePerByte; + private final IntegerProperty feeUpdateCounter = new SimpleIntegerProperty(0); + private long txFeePerByte = BTC_DEFAULT_TX_FEE; private Map timeStampMap; - private long epochInSecondAtLastRequest; private long lastRequest; - private IntegerProperty feeUpdateCounter = new SimpleIntegerProperty(0); private long minFeePerByte; + private long epochInSecondAtLastRequest; /////////////////////////////////////////////////////////////////////////////////////////// @@ -94,50 +109,33 @@ public class FeeService { /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public FeeService(FeeProvider feeProvider) { + public FeeService(FeeProvider feeProvider, BsqStateService bsqStateService, PeriodService periodService) { this.feeProvider = feeProvider; - baseCurrencyCode = BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode(); - - /* How to calculate: - MIN_MAKER_FEE_IN_BASE_CUR = target fiat price * 100000000 / price (in btc: 0.5*100000000/2500) - DEFAULT_MAKER_FEE_IN_BASE_CUR = target fiat price * (100000000 / price) / maxTradeAmount - (in btc: 5*100000000/2500 / 1) - (in ltc: 5*100000000/40 / 50) - */ - switch (baseCurrencyCode) { - case "BTC": - MIN_MAKER_FEE_IN_BASE_CUR = 5_000; // 0.5 USD at BTC price 10000 USD - MIN_TAKER_FEE_IN_BASE_CUR = 5_000; - DEFAULT_MAKER_FEE_IN_BASE_CUR = 200_000; // 20 USD at BTC price 10000 USD for a 1 BTC trade - DEFAULT_TAKER_FEE_IN_BASE_CUR = 200_000; - txFeePerByte = BTC_DEFAULT_TX_FEE; - break; - case "LTC": - MIN_MAKER_FEE_IN_BASE_CUR = 1_200_000; // 0.5 USD at LTC price 40 USD - MIN_TAKER_FEE_IN_BASE_CUR = 1_200_000; - DEFAULT_MAKER_FEE_IN_BASE_CUR = 240_000; // 5 USD at LTC price 40 USD for 50 LTC (maxTradeAmount) - DEFAULT_TAKER_FEE_IN_BASE_CUR = 360_000; // 7.5 USD at LTC price 40 USD - txFeePerByte = LTC_DEFAULT_TX_FEE; - break; - case "DASH": - MIN_MAKER_FEE_IN_BASE_CUR = 300_000; // 0.5 USD at DASH price 150 USD - MIN_TAKER_FEE_IN_BASE_CUR = 300_000; - DEFAULT_MAKER_FEE_IN_BASE_CUR = 160_000; // 5 USD at DASH price 150 USD - DEFAULT_TAKER_FEE_IN_BASE_CUR = 240_000; // 7.5 USD at DASH price 150 USD for 20 DASH (maxTradeAmount) - txFeePerByte = DASH_DEFAULT_TX_FEE; - break; - default: - throw new RuntimeException("baseCurrencyCode not defined. baseCurrencyCode=" + baseCurrencyCode); - } + FeeService.bsqStateService = bsqStateService; + FeeService.periodService = periodService; } + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + public void onAllServicesInitialized() { minFeePerByte = BisqEnvironment.getBaseCurrencyNetwork().getDefaultMinFeePerByte(); - requestFees(null, null); + requestFees(); // We update all 5 min. - UserThread.runPeriodically(() -> requestFees(null, null), 5, TimeUnit.MINUTES); + UserThread.runPeriodically(this::requestFees, 5, TimeUnit.MINUTES); + } + + + public void requestFees() { + requestFees(null, null); + } + + public void requestFees(Runnable resultHandler) { + requestFees(resultHandler, null); } public void requestFees(@Nullable Runnable resultHandler, @Nullable FaultHandler faultHandler) { @@ -155,7 +153,7 @@ public class FeeService { timeStampMap = result.first; epochInSecondAtLastRequest = timeStampMap.get("bitcoinFeesTs"); final Map map = result.second; - txFeePerByte = map.get(baseCurrencyCode); + txFeePerByte = map.get("BTC"); if (txFeePerByte < minFeePerByte) { log.warn("The delivered fee per byte is smaller than the min. default fee of 5 sat/byte"); @@ -163,7 +161,7 @@ public class FeeService { } feeUpdateCounter.set(feeUpdateCounter.get() + 1); - log.info("{} tx fee: txFeePerByte={}", baseCurrencyCode, txFeePerByte); + log.info("BTC tx fee: txFeePerByte={}", txFeePerByte); if (resultHandler != null) resultHandler.run(); }); @@ -193,22 +191,6 @@ public class FeeService { return Coin.valueOf(txFeePerByte); } - public static Coin getMakerFeePerBtc(boolean currencyForMakerFeeBtc) { - return currencyForMakerFeeBtc ? Coin.valueOf(DEFAULT_MAKER_FEE_IN_BASE_CUR) : Coin.valueOf(DEFAULT_MAKER_FEE_IN_BSQ); - } - - public static Coin getMinMakerFee(boolean currencyForMakerFeeBtc) { - return currencyForMakerFeeBtc ? Coin.valueOf(MIN_MAKER_FEE_IN_BASE_CUR) : Coin.valueOf(MIN_MAKER_FEE_IN_BSQ); - } - - public static Coin getTakerFeePerBtc(boolean currencyForTakerFeeBtc) { - return currencyForTakerFeeBtc ? Coin.valueOf(DEFAULT_TAKER_FEE_IN_BASE_CUR) : Coin.valueOf(DEFAULT_TAKER_FEE_IN_BSQ); - } - - public static Coin getMinTakerFee(boolean currencyForTakerFeeBtc) { - return currencyForTakerFeeBtc ? Coin.valueOf(MIN_TAKER_FEE_IN_BASE_CUR) : Coin.valueOf(MIN_TAKER_FEE_IN_BSQ); - } - public ReadOnlyIntegerProperty feeUpdateCounterProperty() { return feeUpdateCounter; } 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 c3b0f9d7fc..76e646792f 100644 --- a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java @@ -280,13 +280,13 @@ public class PriceFeedService { } public Date getLastRequestTimeStampBtcAverage() { - return new Date(epochInSecondAtLastRequest * 1000); + return new Date(epochInSecondAtLastRequest); } public Date getLastRequestTimeStampPoloniex() { Long ts = timeStampMap.get("btcAverageTs"); if (ts != null) { - return new Date(ts * 1000); + return new Date(ts); } else return new Date(); } @@ -294,7 +294,7 @@ public class PriceFeedService { public Date getLastRequestTimeStampCoinmarketcap() { Long ts = timeStampMap.get("coinmarketcapTs"); if (ts != null) { - return new Date(ts * 1000); + return new Date(ts); } else return new Date(); } diff --git a/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java b/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java index 46816773ce..2b396f6760 100644 --- a/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java +++ b/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java @@ -18,13 +18,15 @@ package bisq.core.setup; import bisq.core.arbitration.DisputeManager; -import bisq.core.btc.AddressEntryList; +import bisq.core.btc.model.AddressEntryList; import bisq.core.dao.DaoOptionKeys; +import bisq.core.dao.governance.asset.AssetService; import bisq.core.dao.governance.ballot.BallotListService; import bisq.core.dao.governance.blindvote.MyBlindVoteListService; import bisq.core.dao.governance.myvote.MyVoteListService; import bisq.core.dao.governance.proposal.MyProposalListService; import bisq.core.dao.governance.role.BondedRolesService; +import bisq.core.dao.governance.voteresult.VoteResultService; import bisq.core.offer.OpenOfferManager; import bisq.core.trade.TradeManager; import bisq.core.trade.closed.ClosedTradableManager; @@ -67,6 +69,8 @@ public class CorePersistedDataHost { persistedDataHosts.add(injector.getInstance(MyVoteListService.class)); persistedDataHosts.add(injector.getInstance(MyProposalListService.class)); persistedDataHosts.add(injector.getInstance(BondedRolesService.class)); + persistedDataHosts.add(injector.getInstance(AssetService.class)); + persistedDataHosts.add(injector.getInstance(VoteResultService.class)); } return persistedDataHosts; } diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index 14c8627b07..436cd72d9f 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -17,8 +17,8 @@ package bisq.core.trade; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.AddressEntryException; +import bisq.core.btc.exceptions.AddressEntryException; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; diff --git a/core/src/main/java/bisq/core/trade/messages/PayDepositRequest.java b/core/src/main/java/bisq/core/trade/messages/PayDepositRequest.java index 56fe5c0b17..6f4b0e15c9 100644 --- a/core/src/main/java/bisq/core/trade/messages/PayDepositRequest.java +++ b/core/src/main/java/bisq/core/trade/messages/PayDepositRequest.java @@ -17,7 +17,7 @@ package bisq.core.trade.messages; -import bisq.core.btc.data.RawTransactionInput; +import bisq.core.btc.model.RawTransactionInput; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.proto.CoreProtoResolver; diff --git a/core/src/main/java/bisq/core/trade/messages/PublishDepositTxRequest.java b/core/src/main/java/bisq/core/trade/messages/PublishDepositTxRequest.java index 6a8e3e218f..c493621281 100644 --- a/core/src/main/java/bisq/core/trade/messages/PublishDepositTxRequest.java +++ b/core/src/main/java/bisq/core/trade/messages/PublishDepositTxRequest.java @@ -17,7 +17,7 @@ package bisq.core.trade.messages; -import bisq.core.btc.data.RawTransactionInput; +import bisq.core.btc.model.RawTransactionInput; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.proto.CoreProtoResolver; diff --git a/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java b/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java index fdc56fd3cf..78b798086d 100644 --- a/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java +++ b/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java @@ -18,9 +18,9 @@ package bisq.core.trade.protocol; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.data.RawTransactionInput; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.btc.model.RawTransactionInput; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.filter.FilterManager; import bisq.core.network.MessageState; diff --git a/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java b/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java index ed148c7b7c..9b04dc222f 100644 --- a/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java +++ b/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java @@ -17,7 +17,7 @@ package bisq.core.trade.protocol; -import bisq.core.btc.data.RawTransactionInput; +import bisq.core.btc.model.RawTransactionInput; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.proto.CoreProtoResolver; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessPayoutTxPublishedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessPayoutTxPublishedMessage.java index ae3cdc1d2a..3543fbddd1 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessPayoutTxPublishedMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessPayoutTxPublishedMessage.java @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.buyer; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.trade.Trade; import bisq.core.trade.messages.PayoutTxPublishedMessage; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSendCounterCurrencyTransferStartedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSendCounterCurrencyTransferStartedMessage.java index 00d13310bf..f905981553 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSendCounterCurrencyTransferStartedMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSendCounterCurrencyTransferStartedMessage.java @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.buyer; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.trade.Trade; import bisq.core.trade.messages.CounterCurrencyTransferStartedMessage; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSetupPayoutTxListener.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSetupPayoutTxListener.java index 20fe4cef1d..178a08d786 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSetupPayoutTxListener.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSetupPayoutTxListener.java @@ -17,8 +17,8 @@ package bisq.core.trade.protocol.tasks.buyer; -import bisq.core.btc.AddressEntry; import bisq.core.btc.listeners.AddressConfidenceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerCreatesAndSignsDepositTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerCreatesAndSignsDepositTx.java index c5ae6bc416..4edd9bdb66 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerCreatesAndSignsDepositTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerCreatesAndSignsDepositTx.java @@ -17,10 +17,10 @@ package bisq.core.trade.protocol.tasks.buyer_as_maker; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.data.PreparedDepositTxAndMakerInputs; -import bisq.core.btc.data.RawTransactionInput; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.btc.model.PreparedDepositTxAndMakerInputs; +import bisq.core.btc.model.RawTransactionInput; import bisq.core.offer.Offer; import bisq.core.trade.Trade; import bisq.core.trade.protocol.TradingPeer; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerSignPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerSignPayoutTx.java index 1d0733514b..b1f48b471e 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerSignPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerSignPayoutTx.java @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.buyer_as_maker; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; 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 b8eacae4f9..653525fda5 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 @@ -17,9 +17,9 @@ package bisq.core.trade.protocol.tasks.buyer_as_taker; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.data.InputsAndChangeOutput; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.btc.model.InputsAndChangeOutput; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerSignAndPublishDepositTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerSignAndPublishDepositTx.java index 6a93d8fdec..0e4d4edcc1 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerSignAndPublishDepositTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerSignAndPublishDepositTx.java @@ -17,10 +17,10 @@ package bisq.core.trade.protocol.tasks.buyer_as_taker; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.data.RawTransactionInput; +import bisq.core.btc.exceptions.TxBroadcastException; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcastException; +import bisq.core.btc.model.RawTransactionInput; import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.trade.Trade; import bisq.core.trade.protocol.TradingPeer; 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 1d0844af26..2983383576 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 @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.maker; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.trade.BuyerAsMakerTrade; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java index dd1b7e97cf..5325934d98 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.maker; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.trade.Trade; import bisq.core.trade.messages.DepositTxPublishedMessage; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSendPublishDepositTxRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSendPublishDepositTxRequest.java index 33f1f8e5fa..82e2dd1fe1 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSendPublishDepositTxRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSendPublishDepositTxRequest.java @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.maker; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.trade.Trade; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetupDepositTxListener.java b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetupDepositTxListener.java index 5fe05c1c4b..e296f4015a 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetupDepositTxListener.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetupDepositTxListener.java @@ -17,8 +17,8 @@ package bisq.core.trade.protocol.tasks.maker; -import bisq.core.btc.AddressEntry; import bisq.core.btc.listeners.AddressConfidenceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerBroadcastPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerBroadcastPayoutTx.java index dd90bd6756..c056b52dd8 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerBroadcastPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerBroadcastPayoutTx.java @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.seller; -import bisq.core.btc.wallet.TxBroadcastException; +import bisq.core.btc.exceptions.TxBroadcastException; import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignAndFinalizePayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignAndFinalizePayoutTx.java index eafadb8c5c..2648a14f99 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignAndFinalizePayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignAndFinalizePayoutTx.java @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.seller; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.offer.Offer; import bisq.core.trade.Trade; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_maker/SellerAsMakerCreatesAndSignsDepositTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_maker/SellerAsMakerCreatesAndSignsDepositTx.java index 11a861dff8..5cf0c2c3d7 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_maker/SellerAsMakerCreatesAndSignsDepositTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_maker/SellerAsMakerCreatesAndSignsDepositTx.java @@ -17,10 +17,10 @@ package bisq.core.trade.protocol.tasks.seller_as_maker; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.data.PreparedDepositTxAndMakerInputs; -import bisq.core.btc.data.RawTransactionInput; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.btc.model.PreparedDepositTxAndMakerInputs; +import bisq.core.btc.model.RawTransactionInput; import bisq.core.offer.Offer; import bisq.core.trade.Trade; import bisq.core.trade.protocol.TradingPeer; 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 64f13f6711..14befb65d4 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 @@ -17,9 +17,9 @@ package bisq.core.trade.protocol.tasks.seller_as_taker; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.data.InputsAndChangeOutput; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.btc.model.InputsAndChangeOutput; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java index ff9be4ef6f..73709a364e 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java @@ -17,10 +17,10 @@ package bisq.core.trade.protocol.tasks.seller_as_taker; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.data.RawTransactionInput; +import bisq.core.btc.exceptions.TxBroadcastException; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcastException; +import bisq.core.btc.model.RawTransactionInput; import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.trade.Contract; import bisq.core.trade.Trade; 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 06d181f36f..4a67933a29 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,11 +18,11 @@ package bisq.core.trade.protocol.tasks.taker; import bisq.core.arbitration.Arbitrator; -import bisq.core.btc.AddressEntry; +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.TxBroadcastException; import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.btc.wallet.WalletService; import bisq.core.trade.Trade; 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 a39279427c..353690b2e4 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 @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.taker; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.trade.Trade; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java index 05760aa96a..38430830f7 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java @@ -17,7 +17,7 @@ package bisq.core.trade.protocol.tasks.taker; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.trade.Contract; 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 77b3055dd9..bd7ee28df8 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java @@ -23,6 +23,7 @@ import bisq.core.monetary.AltcoinExchangeRate; import bisq.core.monetary.Price; import bisq.core.monetary.Volume; import bisq.core.offer.OfferPayload; +import bisq.core.offer.OfferUtil; import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; import bisq.network.p2p.storage.payload.LazyProcessedPayload; @@ -253,10 +254,12 @@ public final class TradeStatistics2 implements LazyProcessedPayload, Persistable } public Volume getTradeVolume() { - if (getTradePrice().getMonetary() instanceof Altcoin) + if (getTradePrice().getMonetary() instanceof Altcoin) { return new Volume(new AltcoinExchangeRate((Altcoin) getTradePrice().getMonetary()).coinToAltcoin(getTradeAmount())); - else - return new Volume(new ExchangeRate((Fiat) getTradePrice().getMonetary()).coinToFiat(getTradeAmount())); + } else { + Volume volume = new Volume(new ExchangeRate((Fiat) getTradePrice().getMonetary()).coinToFiat(getTradeAmount())); + return OfferUtil.getRoundedFiatVolume(volume); + } } @Override diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java index 7be153f351..b47cb5dad4 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -18,6 +18,8 @@ package bisq.core.trade.statistics; import bisq.core.app.AppOptionKeys; +import bisq.core.dao.governance.asset.AssetService; +import bisq.core.locale.CryptoCurrency; import bisq.core.locale.CurrencyTuple; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; @@ -32,14 +34,19 @@ import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; import bisq.common.UserThread; import bisq.common.storage.JsonFileManager; import bisq.common.storage.Storage; +import bisq.common.util.Tuple2; import bisq.common.util.Utilities; +import org.bitcoinj.core.Coin; + import com.google.inject.Inject; import com.google.inject.name.Named; import javafx.collections.FXCollections; import javafx.collections.ObservableSet; +import java.time.Duration; + import java.io.File; import java.util.ArrayList; @@ -59,29 +66,11 @@ import static com.google.common.base.Preconditions.checkNotNull; @Slf4j public class TradeStatisticsManager { - static TradeStatistics2 ConvertToTradeStatistics2(TradeStatistics tradeStatistics) { - return new TradeStatistics2(tradeStatistics.getDirection(), - tradeStatistics.getBaseCurrency(), - tradeStatistics.getCounterCurrency(), - tradeStatistics.getOfferPaymentMethod(), - tradeStatistics.getOfferDate(), - tradeStatistics.isOfferUseMarketBasedPrice(), - tradeStatistics.getOfferMarketPriceMargin(), - tradeStatistics.getOfferAmount(), - tradeStatistics.getOfferMinAmount(), - tradeStatistics.getOfferId(), - tradeStatistics.getTradePrice().getValue(), - tradeStatistics.getTradeAmount().getValue(), - tradeStatistics.getTradeDate().getTime(), - tradeStatistics.getDepositTxId(), - null, - tradeStatistics.getExtraDataMap()); - } - private final JsonFileManager jsonFileManager; private final P2PService p2PService; private final PriceFeedService priceFeedService; private final ReferralIdService referralIdService; + private final AssetService assetService; private final boolean dumpStatistics; private final ObservableSet observableTradeStatisticsSet = FXCollections.observableSet(); @@ -91,11 +80,13 @@ public class TradeStatisticsManager { TradeStatistics2StorageService tradeStatistics2StorageService, AppendOnlyDataStoreService appendOnlyDataStoreService, ReferralIdService referralIdService, + AssetService assetService, @Named(Storage.STORAGE_DIR) File storageDir, @Named(AppOptionKeys.DUMP_STATISTICS) boolean dumpStatistics) { this.p2PService = p2PService; this.priceFeedService = priceFeedService; this.referralIdService = referralIdService; + this.assetService = assetService; this.dumpStatistics = dumpStatistics; jsonFileManager = new JsonFileManager(storageDir); @@ -130,8 +121,7 @@ public class TradeStatisticsManager { priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet); dump(); - // print all currencies sorted by nr. of trades - // printAllCurrencyStats(); + checkTradeActivity(); } public void publishTradeStatistics(List trades) { @@ -208,151 +198,102 @@ public class TradeStatisticsManager { } } - // To have automatic check and removal we would need new fields in the asset class for the date when it was - // added/released and a property if it was removed due either getting blocked by the DAO stakehodlers in voting or - // removed due lack of activity. - // For now we use the old script below to print the usage of the coins. - private void printAllCurrencyStats() { - Map> map1 = new HashMap<>(); - for (TradeStatistics2 tradeStatistics : observableTradeStatisticsSet) { - if (CurrencyUtil.isFiatCurrency(tradeStatistics.getCounterCurrency())) { - String counterCurrency = CurrencyUtil.getNameAndCode(tradeStatistics.getCounterCurrency()); - if (!map1.containsKey(counterCurrency)) - map1.put(counterCurrency, new HashSet<>()); + private void checkTradeActivity() { + Date compareDate = new Date(new Date().getTime() - Duration.ofDays(120).toMillis()); + long minTradeAmount = Coin.parseCoin("0.01").value; + long minNumOfTrades = 3; - map1.get(counterCurrency).add(tradeStatistics); + Map> tradeStatMap = new HashMap<>(); + observableTradeStatisticsSet.stream() + .filter(e -> CurrencyUtil.isCryptoCurrency(e.getBaseCurrency())) + .filter(e -> e.getTradeDate().getTime() > compareDate.getTime()) + .forEach(e -> { + tradeStatMap.putIfAbsent(e.getBaseCurrency(), new Tuple2<>(0L, 0)); + Tuple2 tuple2 = tradeStatMap.get(e.getBaseCurrency()); + long accumulatedTradeAmount = tuple2.first + e.getTradeAmount().getValue(); + int numTrades = tuple2.second + 1; + tradeStatMap.put(e.getBaseCurrency(), new Tuple2<>(accumulatedTradeAmount, numTrades)); + }); + StringBuilder sufficientlyTraded = new StringBuilder("\nSufficiently traded assets:"); + StringBuilder insufficientlyTraded = new StringBuilder("\nInsufficiently traded assets:"); + StringBuilder notTraded = new StringBuilder("\nNot traded assets:"); + List whiteListedSortedCryptoCurrencies = CurrencyUtil.getWhiteListedSortedCryptoCurrencies(assetService); + Set assetsToRemove = new HashSet<>(whiteListedSortedCryptoCurrencies); + whiteListedSortedCryptoCurrencies.forEach(e -> { + String code = e.getCode(); + if (!isWarmingUp(code) && !hasPaidBSQFee(code)) { + String nameAndCode = CurrencyUtil.getNameAndCode(code); + if (tradeStatMap.containsKey(code)) { + Tuple2 tuple = tradeStatMap.get(code); + Long tradeAmount = tuple.first; + Integer numTrades = tuple.second; + if (tradeAmount >= minTradeAmount || numTrades >= minNumOfTrades) { + assetsToRemove.remove(e); + sufficientlyTraded.append("\n") + .append(nameAndCode) + .append(": Trade amount: ") + .append(Coin.valueOf(tradeAmount).toFriendlyString()) + .append(", number of trades: ") + .append(numTrades); + } else { + insufficientlyTraded.append("\n") + .append(nameAndCode) + .append(": Trade amount: ") + .append(Coin.valueOf(tradeAmount).toFriendlyString()) + .append(", number of trades: ") + .append(numTrades); + } + } else { + assetsToRemove.remove(e); + notTraded.append("\n").append(nameAndCode); + } } - } + }); - StringBuilder sb1 = new StringBuilder("\nAll traded Fiat currencies:\n"); - map1.entrySet().stream() - .sorted((o1, o2) -> Integer.compare(o2.getValue().size(), o1.getValue().size())) - .forEach(e -> sb1.append(e.getKey()).append(": ").append(e.getValue().size()).append("\n")); - log.error(sb1.toString()); + log.debug(sufficientlyTraded.toString()); + log.debug(insufficientlyTraded.toString()); + log.debug(notTraded.toString()); + } - Map> map2 = new HashMap<>(); - for (TradeStatistics2 tradeStatistics : observableTradeStatisticsSet) { - if (CurrencyUtil.isCryptoCurrency(tradeStatistics.getBaseCurrency())) { - final String code = CurrencyUtil.getNameAndCode(tradeStatistics.getBaseCurrency()); - if (!map2.containsKey(code)) - map2.put(code, new HashSet<>()); + private boolean hasPaidBSQFee(String code) { + return assetService.hasPaidBSQFee(code); + } - map2.get(code).add(tradeStatistics); - } - } - - List allCryptoCurrencies = new ArrayList<>(); - Set coinsWithValidator = new HashSet<>(); - - // List of coins with validator before 0.6.0 hard requirements for address validator - coinsWithValidator.add("BTC"); - coinsWithValidator.add("BSQ"); - coinsWithValidator.add("LTC"); - coinsWithValidator.add("DOGE"); - coinsWithValidator.add("DASH"); - coinsWithValidator.add("ETH"); - coinsWithValidator.add("PIVX"); - coinsWithValidator.add("IOP"); - coinsWithValidator.add("888"); - coinsWithValidator.add("ZEC"); - coinsWithValidator.add("GBYTE"); - coinsWithValidator.add("NXT"); - - // All those need to have a address validator + private boolean isWarmingUp(String code) { Set newlyAdded = new HashSet<>(); - // v0.6.0 - newlyAdded.add("DCT"); - newlyAdded.add("PNC"); - newlyAdded.add("WAC"); - newlyAdded.add("ZEN"); - newlyAdded.add("ELLA"); - newlyAdded.add("XCN"); - newlyAdded.add("TRC"); - newlyAdded.add("INXT"); - newlyAdded.add("PART"); - // v0.6.1 - newlyAdded.add("MAD"); - newlyAdded.add("BCH"); - newlyAdded.add("BCHC"); - newlyAdded.add("BTG"); - // v0.6.2 - newlyAdded.add("CAGE"); - newlyAdded.add("CRED"); - newlyAdded.add("XSPEC"); - // v0.6.3 - newlyAdded.add("WILD"); - newlyAdded.add("ONION"); - // v0.6.4 - newlyAdded.add("CREA"); - newlyAdded.add("XIN"); - // v0.6.5 - newlyAdded.add("BETR"); - newlyAdded.add("MVT"); - newlyAdded.add("REF"); - // v0.6.6 - newlyAdded.add("STL"); - newlyAdded.add("DAI"); - newlyAdded.add("YTN"); - newlyAdded.add("DARX"); - newlyAdded.add("ODN"); - newlyAdded.add("CDT"); - newlyAdded.add("DGM"); - newlyAdded.add("SCS"); - newlyAdded.add("SOS"); - newlyAdded.add("ACH"); - newlyAdded.add("VDN"); - // v0.7.0 - newlyAdded.add("ALC"); - newlyAdded.add("DIN"); - newlyAdded.add("NAH"); - newlyAdded.add("ROI"); - newlyAdded.add("WMCC"); - newlyAdded.add("RTO"); - newlyAdded.add("KOTO"); - newlyAdded.add("PHR"); - newlyAdded.add("UBQ"); - newlyAdded.add("QWARK"); - newlyAdded.add("GEO"); - newlyAdded.add("GRANS"); - newlyAdded.add("ICH"); - // TODO add remaining coins since 0.7.0 - //newlyAdded.clear(); - /* new AssetRegistry().stream() - .sorted(Comparator.comparing(o -> o.getName().toLowerCase())) - .filter(e -> !e.getTickerSymbol().equals("BSQ")) // BSQ is not out yet... - .filter(e -> !e.getTickerSymbol().equals("BTC")) - .map(e -> e.getTickerSymbol()) // We want to get rid of duplicated entries for regtest/testnet... - .distinct() - .forEach(e -> newlyAdded.add(e));*/ + // v0.7.1 Jul 4 2018 + newlyAdded.add("ZOC"); + newlyAdded.add("AQUA"); + newlyAdded.add("BTDX"); + newlyAdded.add("BTCC"); + newlyAdded.add("BTI"); + newlyAdded.add("CRDS"); + newlyAdded.add("CNMC"); + newlyAdded.add("TARI"); + newlyAdded.add("DAC"); + newlyAdded.add("DRIP"); + newlyAdded.add("FTO"); + newlyAdded.add("GRFT"); + newlyAdded.add("LIKE"); + newlyAdded.add("LOBS"); + newlyAdded.add("MAX"); + newlyAdded.add("MEC"); + newlyAdded.add("MCC"); + newlyAdded.add("XMN"); + newlyAdded.add("XMY"); + newlyAdded.add("NANO"); + newlyAdded.add("NPW"); + newlyAdded.add("NIM"); + newlyAdded.add("PIX"); + newlyAdded.add("PXL"); + newlyAdded.add("PRIV"); + newlyAdded.add("TRIT"); + newlyAdded.add("WAVI"); - coinsWithValidator.addAll(newlyAdded); + // v0.8.0 Aug 22 2018 + // none added - CurrencyUtil.getAllSortedCryptoCurrencies() - .forEach(e -> allCryptoCurrencies.add(e.getNameAndCode())); - StringBuilder sb2 = new StringBuilder("\nAll traded Crypto currencies:\n"); - StringBuilder sb3 = new StringBuilder("\nNever traded Crypto currencies:\n"); - map2.entrySet().stream() - .sorted((o1, o2) -> Integer.compare(o2.getValue().size(), o1.getValue().size())) - .forEach(e -> { - String key = e.getKey(); - sb2.append(key).append(": ").append(e.getValue().size()).append("\n"); - // key is: USD Tether (USDT) - String code = key.substring(key.indexOf("(") + 1, key.length() - 1); - if (!coinsWithValidator.contains(code) && !newlyAdded.contains(code)) - allCryptoCurrencies.remove(key); - }); - log.error(sb2.toString()); - - // Not considered age of newly added coins, so take care with removal if coin was added recently. - allCryptoCurrencies.sort(String::compareTo); - allCryptoCurrencies - .forEach(e -> { - // key is: USD Tether (USDT) - String code = e.substring(e.indexOf("(") + 1, e.length() - 1); - if (!coinsWithValidator.contains(code) && !newlyAdded.contains(code)) - sb3.append(e).append("\n"); - }); - log.error(sb3.toString()); + return newlyAdded.contains(code); } } diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index 5d252d0758..173b258b3b 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -20,9 +20,9 @@ package bisq.core.user; import bisq.core.app.AppOptionKeys; import bisq.core.app.BisqEnvironment; import bisq.core.btc.BaseCurrencyNetwork; -import bisq.core.btc.BitcoinNodes; import bisq.core.btc.BtcOptionKeys; -import bisq.core.btc.Restrictions; +import bisq.core.btc.nodes.BtcNodes; +import bisq.core.btc.wallet.Restrictions; import bisq.core.locale.Country; import bisq.core.locale.CountryUtil; import bisq.core.locale.CryptoCurrency; @@ -283,7 +283,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid "The Bitcoin node(s) {} from the program argument will be used.", btcNodesFromOptions); } setBitcoinNodes(btcNodesFromOptions); - setBitcoinNodesOptionOrdinal(BitcoinNodes.BitcoinNodesOption.CUSTOM.ordinal()); + setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.CUSTOM.ordinal()); } if (referralIdFromOptions != null && !referralIdFromOptions.isEmpty()) setReferralId(referralIdFromOptions); diff --git a/core/src/main/java/bisq/core/user/PreferencesPayload.java b/core/src/main/java/bisq/core/user/PreferencesPayload.java index e850943b97..53f2ffd428 100644 --- a/core/src/main/java/bisq/core/user/PreferencesPayload.java +++ b/core/src/main/java/bisq/core/user/PreferencesPayload.java @@ -17,7 +17,7 @@ package bisq.core.user; -import bisq.core.btc.Restrictions; +import bisq.core.btc.wallet.Restrictions; import bisq.core.locale.Country; import bisq.core.locale.CryptoCurrency; import bisq.core.locale.FiatCurrency; diff --git a/core/src/main/java/bisq/core/util/BSFormatter.java b/core/src/main/java/bisq/core/util/BSFormatter.java index 0699df4866..1a49166bb1 100644 --- a/core/src/main/java/bisq/core/util/BSFormatter.java +++ b/core/src/main/java/bisq/core/util/BSFormatter.java @@ -328,7 +328,7 @@ public class BSFormatter { // TODO quick hack... String res; if (altcoin.getCurrencyCode().equals("BSQ")) - res = altcoinFormat.noCode().minDecimals(3).repeatOptionalDecimals(0, 0).format(altcoin).toString(); + res = altcoinFormat.noCode().minDecimals(2).repeatOptionalDecimals(0, 0).format(altcoin).toString(); else res = altcoinFormat.noCode().format(altcoin).toString(); if (appendCurrencyCode) diff --git a/core/src/main/java/bisq/core/util/BsqFormatter.java b/core/src/main/java/bisq/core/util/BsqFormatter.java index 6ad5fc4a70..fa33b34610 100644 --- a/core/src/main/java/bisq/core/util/BsqFormatter.java +++ b/core/src/main/java/bisq/core/util/BsqFormatter.java @@ -18,6 +18,8 @@ package bisq.core.util; import bisq.core.app.BisqEnvironment; +import bisq.core.dao.state.governance.Param; +import bisq.core.locale.Res; import bisq.core.provider.price.MarketPrice; import bisq.common.app.DevEnv; @@ -114,4 +116,105 @@ public class BsqFormatter extends BSFormatter { return Coin.ZERO; } } + + + public String formatParamValue(Param param, long value) { + switch (param) { + case UNDEFINED: + return Res.get("shared.na"); + + case DEFAULT_MAKER_FEE_BSQ: + case DEFAULT_TAKER_FEE_BSQ: + case DEFAULT_MAKER_FEE_BTC: + case DEFAULT_TAKER_FEE_BTC: + return formatToPercentWithSymbol(value / 10000d); + + case PROPOSAL_FEE: + case BLIND_VOTE_FEE: + case COMPENSATION_REQUEST_MIN_AMOUNT: + case COMPENSATION_REQUEST_MAX_AMOUNT: + return formatCoinWithCode(Coin.valueOf(value)); + + case QUORUM_COMP_REQUEST: + case QUORUM_CHANGE_PARAM: + case QUORUM_ROLE: + case QUORUM_CONFISCATION: + case QUORUM_GENERIC: + case QUORUM_REMOVE_ASSET: + return formatCoinWithCode(Coin.valueOf(value)); + + case THRESHOLD_COMP_REQUEST: + case THRESHOLD_CHANGE_PARAM: + case THRESHOLD_ROLE: + case THRESHOLD_CONFISCATION: + case THRESHOLD_GENERIC: + case THRESHOLD_REMOVE_ASSET: + return formatToPercentWithSymbol(value / 10000d); + + case PHASE_UNDEFINED: + return Res.get("shared.na"); + case PHASE_PROPOSAL: + case PHASE_BREAK1: + case PHASE_BLIND_VOTE: + case PHASE_BREAK2: + case PHASE_VOTE_REVEAL: + case PHASE_BREAK3: + case PHASE_RESULT: + return Res.get("dao.param.blocks", value); + + default: + return Res.get("shared.na"); + } + } + + public long parseParamValue(Param param, String inputValue) { + switch (param) { + case UNDEFINED: + return 0; + + case DEFAULT_MAKER_FEE_BSQ: + case DEFAULT_TAKER_FEE_BSQ: + case DEFAULT_MAKER_FEE_BTC: + case DEFAULT_TAKER_FEE_BTC: + return (long) (parsePercentStringToDouble(inputValue) * 10000); + + case PROPOSAL_FEE: + case BLIND_VOTE_FEE: + case COMPENSATION_REQUEST_MIN_AMOUNT: + case COMPENSATION_REQUEST_MAX_AMOUNT: + return parseToCoin(inputValue).value; + + + case QUORUM_COMP_REQUEST: + case QUORUM_CHANGE_PARAM: + case QUORUM_ROLE: + case QUORUM_CONFISCATION: + case QUORUM_GENERIC: + case QUORUM_REMOVE_ASSET: + return parseToCoin(inputValue).value; + + + case THRESHOLD_COMP_REQUEST: + case THRESHOLD_CHANGE_PARAM: + case THRESHOLD_ROLE: + case THRESHOLD_CONFISCATION: + case THRESHOLD_GENERIC: + case THRESHOLD_REMOVE_ASSET: + return (long) (parsePercentStringToDouble(inputValue) * 10000); + + case PHASE_UNDEFINED: + return 0; + case PHASE_PROPOSAL: + case PHASE_BREAK1: + case PHASE_BLIND_VOTE: + case PHASE_BREAK2: + case PHASE_VOTE_REVEAL: + case PHASE_BREAK3: + case PHASE_RESULT: + return Long.valueOf(inputValue); + + default: + return 0; + } + } } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 9450e6541d..669ef32427 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -515,6 +515,7 @@ portfolio.pending.step2_buyer.f2f=Please contact the BTC seller by the provided portfolio.pending.step2_buyer.startPaymentUsing=Start payment using {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.paymentStarted=Payment started portfolio.pending.step2_buyer.warn=You still have not done your {0} payment!\nPlease note that the trade has to be completed by {1} otherwise the trade will be investigated by the arbitrator. portfolio.pending.step2_buyer.openForDispute=You have not completed your payment!\nThe max. period for the trade has elapsed.\n\nPlease contact the arbitrator for opening a dispute. @@ -569,7 +570,9 @@ portfolio.pending.step3_buyer.warn.part2=The BTC seller still has not confirmed portfolio.pending.step3_buyer.openForDispute=The BTC seller has not confirmed your payment!\nThe max. period for the trade has elapsed.\nYou can wait longer and give the trading peer more time or contact the arbitrator for opening a dispute. # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.part=Your trading partner has confirmed that he initiated the {0} payment.\n\n -portfolio.pending.step3_seller.altcoin={0}Please check on your favorite {1} blockchain explorer if the transaction to your receiving address\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}\n\ has already sufficient blockchain confirmations.\nThe payment amount has to be {3}\n\n\ You can copy & paste your {4} address from the main screen after closing that popup. @@ -731,7 +734,7 @@ funds.locked.locked=Locked in multisig for trade with ID: {0} funds.tx.direction.sentTo=Sent to: funds.tx.direction.receivedWith=Received with: funds.tx.direction.genesisTx=From Genesis tx: -funds.tx.txFeePaymentForBsqTx=Tx fee payment for BSQ tx +funds.tx.txFeePaymentForBsqTx=Miner fee for BSQ tx funds.tx.createOfferFee=Maker and tx fee: {0} funds.tx.takeOfferFee=Taker and tx fee: {0} funds.tx.multiSigDeposit=Multisig deposit: {0} @@ -855,7 +858,7 @@ setting.preferences.resetAllFlags=Reset all \"Don't show again\" flags: setting.preferences.reset=Reset settings.preferences.languageChange=To apply the language change to all screens requires a restart. settings.preferences.arbitrationLanguageWarning=In case of a dispute, please note that arbitration is handled in {0}. -settings.preferences.selectCurrencyNetwork=Select base currency +settings.preferences.selectCurrencyNetwork=Select network settings.net.btcHeader=Bitcoin network settings.net.p2pHeader=P2P network @@ -1190,42 +1193,66 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Undefined + # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=BSQ maker fee # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=BSQ taker fee # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.MIN_MAKER_FEE_BSQ=Min. BSQ maker fee # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.MIN_TAKER_FEE_BSQ=Min. BSQ taker fee +# suppress inspection "UnusedProperty" +dao.param.DEFAULT_MAKER_FEE_BTC=BTC maker fee +# suppress inspection "UnusedProperty" +dao.param.DEFAULT_TAKER_FEE_BTC=BTC taker fee +# suppress inspection "UnusedProperty" +# suppress inspection "UnusedProperty" +dao.param.MIN_MAKER_FEE_BTC=Min. BTC maker fee +# suppress inspection "UnusedProperty" +dao.param.MIN_TAKER_FEE_BTC=Min. BTC taker fee # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Proposal fee +dao.param.PROPOSAL_FEE=Proposal fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Voting fee +dao.param.BLIND_VOTE_FEE=Voting fee in BSQ # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Compensation request min. BSQ amount # suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation +dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Compensation request max. BSQ amount # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.QUORUM_GENERIC=Required quorum in BSQ for generic proposal # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request +dao.param.QUORUM_COMP_REQUEST=Required quorum in BSQ for compensation request # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter +dao.param.QUORUM_CHANGE_PARAM=Required quorum in BSQ for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum in BSQ for removing an asset # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation +dao.param.QUORUM_CONFISCATION=Required quorum in BSQ for bond confiscation +# suppress inspection "UnusedProperty" +dao.param.QUORUM_ROLE=Required quorum in BSQ for taking a bonded role + + + +# suppress inspection "UnusedProperty" +dao.param.THRESHOLD_GENERIC=Required threshold in % for generic proposal +# suppress inspection "UnusedProperty" +dao.param.THRESHOLD_COMP_REQUEST=Required threshold in % for compensation request +# suppress inspection "UnusedProperty" +dao.param.THRESHOLD_CHANGE_PARAM=Required threshold in % for changing a parameter +# suppress inspection "UnusedProperty" +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold in % for removing an asset +# suppress inspection "UnusedProperty" +dao.param.THRESHOLD_CONFISCATION=Required threshold in % for bond confiscation +# suppress inspection "UnusedProperty" +dao.param.THRESHOLD_ROLE=Required threshold in % for taking a bonded role + +dao.param.currentValue=Current value: {0} +dao.param.blocks={0} blocks # suppress inspection "UnusedProperty" dao.results.cycle.duration.label=Duration of {0} @@ -1252,8 +1279,6 @@ dao.phase.PHASE_VOTE_REVEAL=Vote reveal phase dao.phase.PHASE_BREAK3=Break 3 # suppress inspection "UnusedProperty" dao.phase.PHASE_RESULT=Result phase -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK4=Break 4 dao.results.votes.table.header.stakeAndMerit=Vote weight dao.results.votes.table.header.stake=Stake @@ -1352,7 +1377,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Compensation request # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin +dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter # suppress inspection "UnusedProperty" @@ -1365,7 +1390,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Compensation request # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1405,6 +1430,7 @@ dao.proposal.display.proposalFee=Proposal fee: dao.proposal.display.myVote=My vote: dao.proposal.display.voteResult=Vote result summary: dao.proposal.display.bondedRoleComboBox.label=Choose bonded role type +dao.proposal.display.tickerSymbol.label=Ticker Symbol dao.proposal.table.header.proposalType=Proposal type dao.proposal.table.header.link=Link @@ -1422,6 +1448,7 @@ dao.proposal.display.paramComboBox.label=Choose parameter dao.proposal.display.paramValue=Parameter value: dao.proposal.display.confiscateBondComboBox.label=Choose bond +dao.proposal.display.assetComboBox.label=Asset to remove dao.blindVote=blind vote @@ -2089,6 +2116,7 @@ payment.emailOrMobile=Email or mobile nr: payment.useCustomAccountName=Use custom account name payment.maxPeriod=Max. allowed trade period: payment.maxPeriodAndLimit=Max. trade duration: {0} / Max. trade limit: {1} / Account age: {2} +payment.maxPeriodAndLimitCrypto=Max. trade duration: {0} / Max. trade limit: {1} payment.currencyWithSymbol=Currency: {0} payment.nameOfAcceptedBank=Name of accepted bank payment.addAcceptedBank=Add accepted bank @@ -2341,3 +2369,5 @@ validation.iban.invalidLength=Number must have length 15 to 34 chars. validation.interacETransfer.invalidAreaCode=Non-Canadian area code validation.interacETransfer.invalidPhone=Invalid phone number format and not an email address validation.inputTooLarge=Input must not be larger than {0} +validation.inputTooSmall=Input has to be larger than {0} +validation.amountBelowDust=The amount below the dust limit of {0} is not allowed. diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 1725ac6c62..b4badf1de4 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Wahlergebnisse für gewählten Vorsch # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Undefiniert # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Erstellergebühr in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Erstellergebühr in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Abnehmergebühr in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Abnehmergebühr in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Erstellergebühr in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Erstellergebühr in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Abnehmergebühr in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Abnehmergebühr in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,7 +1065,7 @@ dao.param.PROPOSAL_FEE=Vorschlag-Gebühr dao.param.BLIND_VOTE_FEE=Stimm-Gebühr # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Benötigtes Quorum für Vorschlag +dao.param.QUORUM_GENERIC=Benötigtes Quorum für Vorschlag # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Benötigtes Quorum für Entlohnungsanfrage # suppress inspection "UnusedProperty" @@ -1076,7 +1076,7 @@ dao.param.QUORUM_REMOVE_ASSET=Benötigtes Quorum um einen Altcoin zu entfernen dao.param.QUORUM_CONFISCATION=Benötigtes Quorum für Kopplung Konfiszierung # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Benötigter Schwellwert für Vorschlag +dao.param.THRESHOLD_GENERIC=Benötigter Schwellwert für Vorschlag # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Benötigter Schwellwert für Entlohnungsanfrage # suppress inspection "UnusedProperty" @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Entlohnungsanfrage # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Vorschlag für Gekoppelte Rolle # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Vorschlag einen Altcoin zu entfernen +dao.proposal.type.REMOVE_ASSET=Vorschlag einen Altcoin zu entfernen # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Vorschlag einen Parameter zu ändern # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Entlohnungsanfrage # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Gekoppelte Rolle # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Einen Altcoin entfernen +dao.proposal.type.short.REMOVE_ASSET=Einen Altcoin entfernen # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Einen Parameter ändern # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=E-Mail oder Mobil: payment.useCustomAccountName=Spezifischen Kontonamen nutzen payment.maxPeriod=Max. erlaubte Handelsdauer: payment.maxPeriodAndLimit=Max. Handelsdauer: {0} / Max. Handelsgrenze: {1} / Konto-Alter: {2} +payment.maxPeriodAndLimitCrypto=Max. Handelsdauer: {0} / Max. Handelsgrenze: {1} payment.currencyWithSymbol=Währung: {0} payment.nameOfAcceptedBank=Name der akzeptierten Bank payment.addAcceptedBank=Akzeptierte Bank hinzufügen diff --git a/core/src/main/resources/i18n/displayStrings_el.properties b/core/src/main/resources/i18n/displayStrings_el.properties index ad5d0a687d..9d5626fb29 100644 --- a/core/src/main/resources/i18n/displayStrings_el.properties +++ b/core/src/main/resources/i18n/displayStrings_el.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Ακαθόριστο # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Αίτημα αποζημίωσης # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Πρόταση για απόσυρση εναλλακτικού νομίσματος +dao.proposal.type.REMOVE_ASSET=Πρόταση για απόσυρση εναλλακτικού νομίσματος # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Πρόταση για αλλαγή παραμέτρου # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Αίτημα αποζημίωσης # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=Email ή αριθμός κινητού: payment.useCustomAccountName=Χρήση προεπιλεγμένου ονόματος λογαριασμού payment.maxPeriod=Μέγιστη επιτρεπόμενη χρονική περίοδος συναλλαγής: payment.maxPeriodAndLimit=Μέγιστη διάρκεια συναλλαγής: {0} / Μέγιστο όριο συναλλαγής: {1} / Ηλικία λογαριασμού: {2} +payment.maxPeriodAndLimitCrypto=Μέγιστη διάρκεια συναλλαγής: {0} / Μέγιστο όριο συναλλαγής: {1} payment.currencyWithSymbol=Νόμισμα: {0} payment.nameOfAcceptedBank=Όνομα αποδεκτής τράπεζας payment.addAcceptedBank=Εισαγωγή αποδεκτής τράπεζας diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index 7ad01989b7..bda4746b0c 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Petición de compensación # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Propuesta para eliminar una altcoin +dao.proposal.type.REMOVE_ASSET=Propuesta para eliminar una altcoin # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Propuesta para cambiar un parámetro # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Petición de compensación # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1871,7 +1871,8 @@ payment.accountNr=Número de cuenta: payment.emailOrMobile=Email o número de móvil: payment.useCustomAccountName=Utilizar nombre de cuenta personalizado payment.maxPeriod=Periodo máximo de intercambio: -payment.maxPeriodAndLimit=Duración máxima de intercambio: {0} / Límite máximo de intercambio: {1} +payment.maxPeriodAndLimit=Duración máxima de intercambio: {0} / Límite máximo de intercambio: {1} +payment.maxPeriodAndLimitCrypto=Duración máxima de intercambio: {0} / Límite máximo de intercambio: {1} payment.currencyWithSymbol=Moneda: {0} payment.nameOfAcceptedBank=Nombre de banco aceptado payment.addAcceptedBank=Añadir banco aceptado diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 8002087f0f..37addf4dbf 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=تعریف نشده # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=درخواست خسارت # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=پیشنهاد برای حذف یک آلت کوین +dao.proposal.type.REMOVE_ASSET=پیشنهاد برای حذف یک آلت کوین # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=پیشنهاد برای تغییر یک پارامتر # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=درخواست خسارت # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=ایمیل یا شماره موبایل: payment.useCustomAccountName=استفاده از نام حساب سفارشی payment.maxPeriod=حداکثر دوره ی زمانی مجاز معامله: payment.maxPeriodAndLimit=حداکثر طول مدت معامله: {0} / حداکثر حد معامله: {1} / عمر حساب: {2} +payment.maxPeriodAndLimitCrypto=حداکثر طول مدت معامله: {0} / حداکثر حد معامله: {1} payment.currencyWithSymbol=ارز: {0} payment.nameOfAcceptedBank=نام بانک پذیرفته شده payment.addAcceptedBank=افزودن بانک پذیرفته شده diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index beb2b1a9e8..e9133d1707 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -975,7 +975,7 @@ dao.phase.short.BREAK4=Break dao.proposal.type.COMPENSATION_REQUEST=Requête de compensation dao.proposal.type.GENERIC=Generic proposal dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter -dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin +dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset dao.proposal.details=Proposal details dao.proposal.selectedProposal=Selected proposal dao.proposal.active.header=Active proposals @@ -1596,6 +1596,7 @@ payment.emailOrMobile=Email ou numéro de mobile: payment.useCustomAccountName=Utiliser un nom de compte personnalisé payment.maxPeriod=Max. allowed trade period: payment.maxPeriodAndLimit=Max. trade duration: {0} / Max. trade limit: {1} / Account age: {2} +payment.maxPeriodAndLimitCrypto=Max. trade duration: {0} / Max. trade limit: {1} payment.currencyWithSymbol=Devise:{0} payment.nameOfAcceptedBank=Nom de la banque acceptée payment.addAcceptedBank=Ajouter une banque acceptée diff --git a/core/src/main/resources/i18n/displayStrings_hu.properties b/core/src/main/resources/i18n/displayStrings_hu.properties index 48dc91beca..c1fea77564 100644 --- a/core/src/main/resources/i18n/displayStrings_hu.properties +++ b/core/src/main/resources/i18n/displayStrings_hu.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Határozatlan # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Kártérítési kérelem # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin +dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Kártérítési kérelem # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=E-mail vagy mobil: payment.useCustomAccountName=Használj egyéni fióknevet payment.maxPeriod=Max. megengedett tranzakció időszak: payment.maxPeriodAndLimit=Max. tranzakció időtartama: {0} / Max. tranzakció korlátozás: {1} / Fiók kora: {2} +payment.maxPeriodAndLimitCrypto=Max. tranzakció időtartama: {0} / Max. tranzakció korlátozás: {1} payment.currencyWithSymbol=Valuta: {0} payment.nameOfAcceptedBank=Elfogadott bank neve payment.addAcceptedBank=Hozzáad elfogadott bankot diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 44b92ffeeb..661d5f6eb9 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Indefinido # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Pedido de compensação # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin +dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Pedido de compensação # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=Email ou celular: payment.useCustomAccountName=Usar número de conta personalizado: payment.maxPeriod=Max. allowed trade period: payment.maxPeriodAndLimit=Max. trade duration: {0} / Max. trade limit: {1} / Account age: {2} +payment.maxPeriodAndLimitCrypto=Max. trade duration: {0} / Max. trade limit: {1} payment.currencyWithSymbol=Moeda: {0} payment.nameOfAcceptedBank=Nome do banco aceito payment.addAcceptedBank=Adicionar banco aceito diff --git a/core/src/main/resources/i18n/displayStrings_ro.properties b/core/src/main/resources/i18n/displayStrings_ro.properties index 8743f33cf2..9fb902dbd7 100644 --- a/core/src/main/resources/i18n/displayStrings_ro.properties +++ b/core/src/main/resources/i18n/displayStrings_ro.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Nedefinit # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Solicitare de despăgubire # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin +dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Solicitare de despăgubire # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=E-mail sau nr. mobil: payment.useCustomAccountName=Folosește nume de cont preferințial payment.maxPeriod=Perioada maximă de tranzacționare permisă: payment.maxPeriodAndLimit=Durata maximă de tranzacționare: {0} / Limita maximă de tranzacționare: {1} / Vechimea contului: {2} +payment.maxPeriodAndLimitCrypto=Durata maximă de tranzacționare: {0} / Limita maximă de tranzacționare: {1} payment.currencyWithSymbol=Valuta: {0} payment.nameOfAcceptedBank=Numele băncii acceptate payment.addAcceptedBank=Adaugă bancă acceptată diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 332b89ecd1..39d3b14dfc 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Результаты голосова # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Неопределено # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Взнос BSQ создателя +dao.param.DEFAULT_MAKER_FEE_BSQ=Взнос BSQ создателя # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Взнос BSQ получателя +dao.param.DEFAULT_TAKER_FEE_BSQ=Взнос BSQ получателя # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Взнос BТС создателя +dao.param.DEFAULT_MAKER_FEE_BTC=Взнос BТС создателя # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Взнос BТС получателя +dao.param.DEFAULT_TAKER_FEE_BTC=Взнос BТС получателя # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,7 +1065,7 @@ dao.param.PROPOSAL_FEE=Сбор за предложение dao.param.BLIND_VOTE_FEE=Сбор за голосование # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Необходимый кворум для предложения +dao.param.QUORUM_GENERIC=Необходимый кворум для предложения # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Необходимый кворум для запроса компенсации # suppress inspection "UnusedProperty" @@ -1076,7 +1076,7 @@ dao.param.QUORUM_REMOVE_ASSET=Необходимый кворум для уда dao.param.QUORUM_CONFISCATION=Необходимый кворум для конфискации гарантийного депозита # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Необходимый порог для предложения +dao.param.THRESHOLD_GENERIC=Необходимый порог для предложения # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Необходимый порог для запроса компенсации # suppress inspection "UnusedProperty" @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Запрос компенсации # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Предложение учредить обеспеченную роль # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Предложение удалить алткойн +dao.proposal.type.REMOVE_ASSET=Предложение удалить алткойн # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Предложение по изменению параметра # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Запрос компенсации # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Обеспеченная роль # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Удаление алткойна +dao.proposal.type.short.REMOVE_ASSET=Удаление алткойна # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Изменение параметра # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=Э-почта или мобильный номер: payment.useCustomAccountName=Использовать своё название счёта payment.maxPeriod=Макс. допустимый срок сделки: payment.maxPeriodAndLimit=Макс. продолжительность сделки: {0} / Макс. торговый предел: {1} / Срок существования счёта: {2} +payment.maxPeriodAndLimitCrypto=Макс. продолжительность сделки: {0} / Макс. торговый предел: {1} payment.currencyWithSymbol=Валюта: {0} payment.nameOfAcceptedBank=Название приемлемого банка payment.addAcceptedBank=Добавить приемлемый банк diff --git a/core/src/main/resources/i18n/displayStrings_sr.properties b/core/src/main/resources/i18n/displayStrings_sr.properties index f1fbd51c90..7ad7712fe7 100644 --- a/core/src/main/resources/i18n/displayStrings_sr.properties +++ b/core/src/main/resources/i18n/displayStrings_sr.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Undefined # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Zahtev za nadoknadu # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin +dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Zahtev za nadoknadu # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=Email ili br. mobilnog: payment.useCustomAccountName=Koristi prilagođeno ime računa payment.maxPeriod=Maks. dozvoljeni period trgovine: payment.maxPeriodAndLimit=Maks. trajanje trgovine: {0} / Maks. rok trgovine: {1} +payment.maxPeriodAndLimitCrypto=Maks. trajanje trgovine: {0} / Maks. rok trgovine: {1} payment.currencyWithSymbol=Valuta: {0} payment.nameOfAcceptedBank=Ime prihvaćene banke payment.addAcceptedBank=Dodaj prihvaćenu banke diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 34f3ffbb6f..02dd86bc1b 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=ไม่ได้กำหนด # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=คำขอชดเชย # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=ข้อเสนอสำหรับการลบ altcoin +dao.proposal.type.REMOVE_ASSET=ข้อเสนอสำหรับการลบ altcoin # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=ข้อเสนอสำหรับการเปลี่ยนข้อจำกัด # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=คำขอชดเชย # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=หมายเลขโทรศัพท์มือถ payment.useCustomAccountName=ใช้ชื่อบัญชีที่กำหนดเอง payment.maxPeriod=ระยะเวลาสูงสุดการค้าที่อนุญาต: payment.maxPeriodAndLimit=ระยะเวลาสูงสุดทางการค้า: {0} / ขีดจำกัดสูงสุดทางการค้า: {1} / อายุบัญชี: {2} +payment.maxPeriodAndLimitCrypto=ระยะเวลาสูงสุดทางการค้า: {0} / ขีดจำกัดสูงสุดทางการค้า: {1} payment.currencyWithSymbol=สกุลเงิน: {0} payment.nameOfAcceptedBank=ชื่อธนาคารที่ได้รับการยอมรับ payment.addAcceptedBank=เพิ่มธนาคารที่ยอมรับ diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 06bfcc9a46..8d2b88e2b1 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Không xác định # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Yêu cầu bồi thường # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Đề xuất gỡ bỏ một altcoin +dao.proposal.type.REMOVE_ASSET=Đề xuất gỡ bỏ một altcoin # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=Đề xuất thay đổi một thông số # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Yêu cầu bồi thường # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=Email hoặc số điện thoại: payment.useCustomAccountName=Sử dụng tên tài khoản thông dụng payment.maxPeriod=Thời gian giao dịch cho phép tối đa: payment.maxPeriodAndLimit=Thời gian giao dịch tối đa: {0} / Giới hạn giao dịch tối đa: {1} / Tuổi tài khoản: {2} +payment.maxPeriodAndLimitCrypto=Thời gian giao dịch tối đa: {0} / Giới hạn giao dịch tối đa: {1} payment.currencyWithSymbol=Tiền tệ: {0} payment.nameOfAcceptedBank=Tên NH được chấp nhận payment.addAcceptedBank=Thêm NH được chấp nhận diff --git a/core/src/main/resources/i18n/displayStrings_zh.properties b/core/src/main/resources/i18n/displayStrings_zh.properties index 2d3d7f917b..e005e37bb2 100644 --- a/core/src/main/resources/i18n/displayStrings_zh.properties +++ b/core/src/main/resources/i18n/displayStrings_zh.properties @@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal # suppress inspection "UnusedProperty" dao.param.UNDEFINED=Undefined # suppress inspection "UnusedProperty" -dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ +dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ +dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ # suppress inspection "UnusedProperty" -dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC +dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC # suppress inspection "UnusedProperty" -dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC +dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC # suppress inspection "UnusedProperty" # suppress inspection "UnusedProperty" @@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee dao.param.BLIND_VOTE_FEE=Voting fee # suppress inspection "UnusedProperty" -dao.param.QUORUM_PROPOSAL=Required quorum for proposal +dao.param.QUORUM_GENERIC=Required quorum for proposal # suppress inspection "UnusedProperty" dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request # suppress inspection "UnusedProperty" dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter # suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin +dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset # suppress inspection "UnusedProperty" dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal +dao.param.THRESHOLD_GENERIC=Required threshold for proposal # suppress inspection "UnusedProperty" dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter # suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin +dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset # suppress inspection "UnusedProperty" dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation @@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=赔偿要求 # suppress inspection "UnusedProperty" dao.proposal.type.BONDED_ROLE=Proposal for a bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin +dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset # suppress inspection "UnusedProperty" dao.proposal.type.CHANGE_PARAM=修改参数的提议 # suppress inspection "UnusedProperty" @@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=赔偿要求 # suppress inspection "UnusedProperty" dao.proposal.type.short.BONDED_ROLE=Bonded role # suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin +dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin # suppress inspection "UnusedProperty" dao.proposal.type.short.CHANGE_PARAM=Changing a parameter # suppress inspection "UnusedProperty" @@ -1872,6 +1872,7 @@ payment.emailOrMobile=电子邮箱或手机号码: payment.useCustomAccountName=使用自定义名称 payment.maxPeriod=最大允许时限: payment.maxPeriodAndLimit=最大交易期限:{0} /最大交易限额:{1} +payment.maxPeriodAndLimitCrypto=最大交易期限:{0} /最大交易限额:{1} payment.currencyWithSymbol=货币:{0} payment.nameOfAcceptedBank=接受的银行名称 payment.addAcceptedBank=添加接受的银行 diff --git a/core/src/test/java/bisq/core/btc/wallet/WalletNetworkConfigTest.java b/core/src/test/java/bisq/core/btc/nodes/BtcNetworkConfigTest.java similarity index 84% rename from core/src/test/java/bisq/core/btc/wallet/WalletNetworkConfigTest.java rename to core/src/test/java/bisq/core/btc/nodes/BtcNetworkConfigTest.java index d1fc971e1e..04825ff7d4 100644 --- a/core/src/test/java/bisq/core/btc/wallet/WalletNetworkConfigTest.java +++ b/core/src/test/java/bisq/core/btc/nodes/BtcNetworkConfigTest.java @@ -15,7 +15,9 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.nodes; + +import bisq.core.btc.setup.WalletConfig; import bisq.network.Socks5MultiDiscovery; @@ -34,7 +36,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -public class WalletNetworkConfigTest { +public class BtcNetworkConfigTest { private static final int MODE = 0; private WalletConfig delegate; @@ -46,7 +48,7 @@ public class WalletNetworkConfigTest { @Test public void testProposePeersWhenProxyPresentAndNoPeers() { - WalletNetworkConfig config = new WalletNetworkConfig(delegate, mock(NetworkParameters.class), MODE, + BtcNetworkConfig config = new BtcNetworkConfig(delegate, mock(NetworkParameters.class), MODE, mock(Socks5Proxy.class)); config.proposePeers(Collections.emptyList()); @@ -56,7 +58,7 @@ public class WalletNetworkConfigTest { @Test public void testProposePeersWhenProxyNotPresentAndNoPeers() { - WalletNetworkConfig config = new WalletNetworkConfig(delegate, mock(NetworkParameters.class), MODE, + BtcNetworkConfig config = new BtcNetworkConfig(delegate, mock(NetworkParameters.class), MODE, null); config.proposePeers(Collections.emptyList()); @@ -66,7 +68,7 @@ public class WalletNetworkConfigTest { @Test public void testProposePeersWhenPeersPresent() { - WalletNetworkConfig config = new WalletNetworkConfig(delegate, mock(NetworkParameters.class), MODE, + BtcNetworkConfig config = new BtcNetworkConfig(delegate, mock(NetworkParameters.class), MODE, null); config.proposePeers(Collections.singletonList(mock(PeerAddress.class))); diff --git a/core/src/test/java/bisq/core/btc/wallet/BtcNodeConverterTest.java b/core/src/test/java/bisq/core/btc/nodes/BtcNodeConverterTest.java similarity index 96% rename from core/src/test/java/bisq/core/btc/wallet/BtcNodeConverterTest.java rename to core/src/test/java/bisq/core/btc/nodes/BtcNodeConverterTest.java index 1381ce9b5f..bc584d3e4b 100644 --- a/core/src/test/java/bisq/core/btc/wallet/BtcNodeConverterTest.java +++ b/core/src/test/java/bisq/core/btc/nodes/BtcNodeConverterTest.java @@ -15,10 +15,10 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.nodes; -import bisq.core.btc.BitcoinNodes.BtcNode; -import bisq.core.btc.wallet.BtcNodeConverter.Facade; +import bisq.core.btc.nodes.BtcNodeConverter.Facade; +import bisq.core.btc.nodes.BtcNodes.BtcNode; import bisq.network.DnsLookupException; diff --git a/core/src/test/java/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java b/core/src/test/java/bisq/core/btc/nodes/BtcNodesRepositoryTest.java similarity index 88% rename from core/src/test/java/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java rename to core/src/test/java/bisq/core/btc/nodes/BtcNodesRepositoryTest.java index 353d0a569a..bead4cceb8 100644 --- a/core/src/test/java/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java +++ b/core/src/test/java/bisq/core/btc/nodes/BtcNodesRepositoryTest.java @@ -15,9 +15,9 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.nodes; -import bisq.core.btc.BitcoinNodes.BtcNode; +import bisq.core.btc.nodes.BtcNodes.BtcNode; import org.bitcoinj.core.PeerAddress; @@ -39,14 +39,14 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class PeerAddressesRepositoryTest { +public class BtcNodesRepositoryTest { @Test public void testGetPeerAddressesWhenClearNodes() { BtcNode node = mock(BtcNode.class); when(node.hasClearNetAddress()).thenReturn(true); BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS); - PeerAddressesRepository repository = new PeerAddressesRepository(converter, + BtcNodesRepository repository = new BtcNodesRepository(converter, Collections.singletonList(node)); List peers = repository.getPeerAddresses(null, false); @@ -62,7 +62,7 @@ public class PeerAddressesRepositoryTest { BtcNode node = mock(BtcNode.class); when(node.hasClearNetAddress()).thenReturn(true); - PeerAddressesRepository repository = new PeerAddressesRepository(converter, + BtcNodesRepository repository = new BtcNodesRepository(converter, Collections.singletonList(node)); List peers = repository.getPeerAddresses(null, false); @@ -80,7 +80,7 @@ public class PeerAddressesRepositoryTest { when(node.hasOnionAddress()).thenReturn(true); BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS); - PeerAddressesRepository repository = new PeerAddressesRepository(converter, + BtcNodesRepository repository = new BtcNodesRepository(converter, Lists.newArrayList(node, onionNode)); List peers = repository.getPeerAddresses(mock(Socks5Proxy.class), true); @@ -97,7 +97,7 @@ public class PeerAddressesRepositoryTest { when(node.hasOnionAddress()).thenReturn(true); BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS); - PeerAddressesRepository repository = new PeerAddressesRepository(converter, + BtcNodesRepository repository = new BtcNodesRepository(converter, Lists.newArrayList(node, onionNode)); List peers = repository.getPeerAddresses(mock(Socks5Proxy.class), false); diff --git a/core/src/test/java/bisq/core/btc/wallet/WalletSetupPreferencesTest.java b/core/src/test/java/bisq/core/btc/nodes/BtcNodesSetupPreferencesTest.java similarity index 80% rename from core/src/test/java/bisq/core/btc/wallet/WalletSetupPreferencesTest.java rename to core/src/test/java/bisq/core/btc/nodes/BtcNodesSetupPreferencesTest.java index 32cd0c2db9..20077fc01f 100644 --- a/core/src/test/java/bisq/core/btc/wallet/WalletSetupPreferencesTest.java +++ b/core/src/test/java/bisq/core/btc/nodes/BtcNodesSetupPreferencesTest.java @@ -15,10 +15,9 @@ * along with Bisq. If not, see . */ -package bisq.core.btc.wallet; +package bisq.core.btc.nodes; -import bisq.core.btc.BitcoinNodes; -import bisq.core.btc.BitcoinNodes.BtcNode; +import bisq.core.btc.nodes.BtcNodes.BtcNode; import bisq.core.user.Preferences; import java.util.List; @@ -30,8 +29,8 @@ import org.powermock.modules.junit4.PowerMockRunner; import org.junit.Test; import org.junit.runner.RunWith; -import static bisq.core.btc.BitcoinNodes.BitcoinNodesOption.CUSTOM; -import static bisq.core.btc.BitcoinNodes.BitcoinNodesOption.PUBLIC; +import static bisq.core.btc.nodes.BtcNodes.BitcoinNodesOption.CUSTOM; +import static bisq.core.btc.nodes.BtcNodes.BitcoinNodesOption.PUBLIC; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -40,14 +39,14 @@ import static org.mockito.Mockito.when; @RunWith(PowerMockRunner.class) @PrepareForTest(Preferences.class) @PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*"}) -public class WalletSetupPreferencesTest { +public class BtcNodesSetupPreferencesTest { @Test public void testSelectPreferredNodesWhenPublicOption() { Preferences delegate = mock(Preferences.class); when(delegate.getBitcoinNodesOptionOrdinal()).thenReturn(PUBLIC.ordinal()); - WalletSetupPreferences preferences = new WalletSetupPreferences(delegate); - List nodes = preferences.selectPreferredNodes(mock(BitcoinNodes.class)); + BtcNodesSetupPreferences preferences = new BtcNodesSetupPreferences(delegate); + List nodes = preferences.selectPreferredNodes(mock(BtcNodes.class)); assertTrue(nodes.isEmpty()); } @@ -58,8 +57,8 @@ public class WalletSetupPreferencesTest { when(delegate.getBitcoinNodesOptionOrdinal()).thenReturn(CUSTOM.ordinal()); when(delegate.getBitcoinNodes()).thenReturn("aaa.onion,bbb.onion"); - WalletSetupPreferences preferences = new WalletSetupPreferences(delegate); - List nodes = preferences.selectPreferredNodes(mock(BitcoinNodes.class)); + BtcNodesSetupPreferences preferences = new BtcNodesSetupPreferences(delegate); + List nodes = preferences.selectPreferredNodes(mock(BtcNodes.class)); assertEquals(2, nodes.size()); } diff --git a/core/src/test/java/bisq/core/btc/RestrictionsTest.java b/core/src/test/java/bisq/core/btc/wallet/RestrictionsTest.java similarity index 98% rename from core/src/test/java/bisq/core/btc/RestrictionsTest.java rename to core/src/test/java/bisq/core/btc/wallet/RestrictionsTest.java index 5a173082a3..f66b2d4c38 100644 --- a/core/src/test/java/bisq/core/btc/RestrictionsTest.java +++ b/core/src/test/java/bisq/core/btc/wallet/RestrictionsTest.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.btc; +package bisq.core.btc.wallet; import org.bitcoinj.core.Coin; diff --git a/core/src/test/java/bisq/core/dao/node/parser/TxParserTest.java b/core/src/test/java/bisq/core/dao/node/parser/GenesisTxParserTest.java similarity index 81% rename from core/src/test/java/bisq/core/dao/node/parser/TxParserTest.java rename to core/src/test/java/bisq/core/dao/node/parser/GenesisTxParserTest.java index 105a6ddb4d..1d6a99e52f 100644 --- a/core/src/test/java/bisq/core/dao/node/parser/TxParserTest.java +++ b/core/src/test/java/bisq/core/dao/node/parser/GenesisTxParserTest.java @@ -32,15 +32,14 @@ import com.google.common.collect.ImmutableList; import java.util.Arrays; import java.util.Date; import java.util.List; -import java.util.Optional; import org.junit.Assert; import org.junit.Test; -public class TxParserTest { +public class GenesisTxParserTest { @Test - public void testGetGenesisTx() { + public void testIsGenesis() { // fixme(chirhonul): Assert.assertEquals(2, 3); int blockHeight = 200; @@ -73,8 +72,8 @@ public class TxParserTest { int genesisBlockHeight = 150; // With mismatch in block height and tx id, we should not get genesis tx back. - Optional result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx); - Optional want = Optional.empty(); + boolean result = GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight); + boolean want = false; Assert.assertEquals(want, result); // With correct block height but mismatch in tx id, we should still not get genesis tx back. @@ -87,12 +86,35 @@ public class TxParserTest { ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output)) ); - result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx); - want = Optional.empty(); + result = GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight); + want = false; Assert.assertEquals(want, result); + } + + @Test + public void testGetGenesisTempTx() { + int blockHeight = 200; + String blockHash = "abc123"; + Coin genesisTotalSupply = Coin.parseCoin("2.5"); + long time = new Date().getTime(); + final List inputs = Arrays.asList( + new TxInput("tx0", 0, null), + new TxInput("tx1", 1, null) + ); + RawTxOutput output = new RawTxOutput( + 0, + genesisTotalSupply.value, + null, + null, + null, + null, + blockHeight + ); + + String genesisTxId = "genesisTxId"; // With correct tx id and block height, we should find our genesis tx with correct tx and output type. - rawTx = new RawTx( + RawTx rawTx = new RawTx( genesisTxId, blockHeight, blockHash, @@ -100,16 +122,16 @@ public class TxParserTest { ImmutableList.copyOf(inputs), ImmutableList.copyOf(Arrays.asList(output)) ); - result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx); + TempTx resultTempTx = GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply); TempTx tempTx = TempTx.fromRawTx(rawTx); tempTx.setTxType(TxType.GENESIS); for (int i = 0; i < tempTx.getTempTxOutputs().size(); ++i) { tempTx.getTempTxOutputs().get(i).setTxOutputType(TxOutputType.GENESIS_OUTPUT); } - want = Optional.of(tempTx); + TempTx wantTempTx = tempTx; - Assert.assertEquals(want, result); + Assert.assertEquals(wantTempTx, resultTempTx); // With correct tx id and block height, but too low sum of outputs (lower than genesisTotalSupply), we // should see an exception raised. @@ -131,7 +153,7 @@ public class TxParserTest { ImmutableList.copyOf(Arrays.asList(output)) ); try { - result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx); + GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply); Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too low"); } catch (InvalidGenesisTxException igtxe) { String wantMessage = "Genesis tx is invalid; not using all available inputs. Remaining input value is 1 sat"; @@ -168,7 +190,7 @@ public class TxParserTest { ImmutableList.copyOf(Arrays.asList(output1, output2)) ); try { - result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx); + GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply); Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too high"); } catch (InvalidGenesisTxException igtxe) { String wantMessage = "Genesis tx is invalid; using more than available inputs. Remaining input value is 2 sat"; diff --git a/core/src/test/java/bisq/core/dao/state/SnapshotManagerTest.java b/core/src/test/java/bisq/core/dao/state/SnapshotManagerTest.java index a1ab7f3ccf..babf312fd6 100644 --- a/core/src/test/java/bisq/core/dao/state/SnapshotManagerTest.java +++ b/core/src/test/java/bisq/core/dao/state/SnapshotManagerTest.java @@ -46,6 +46,7 @@ public class SnapshotManagerTest { snapshotManager = new SnapshotManager(mock(BsqState.class), mock(BsqStateService.class), mock(PersistenceProtoResolver.class), + mock(GenesisTxInfo.class), mock(File.class)); } diff --git a/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java b/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java index 8f6838fc5d..1f4e52708d 100644 --- a/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java +++ b/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java @@ -17,15 +17,30 @@ package bisq.core.locale; +import bisq.core.btc.BaseCurrencyNetwork; + +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Optional; +import java.util.ServiceLoader; +import java.util.stream.Stream; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; + + +import bisq.asset.Asset; +import bisq.asset.AssetRegistry; +import bisq.asset.Coin; +import bisq.asset.coins.Ether; + public class CurrencyUtilTest { @Before @@ -39,9 +54,95 @@ public class CurrencyUtilTest { Optional naira = CurrencyUtil.getTradeCurrency("NGN"); Optional fake = CurrencyUtil.getTradeCurrency("FAK"); - assertTrue(euro.isPresent()); assertTrue(naira.isPresent()); assertFalse("Fake currency shouldn't exist", fake.isPresent()); } + + @Test + public void testFindAsset() { + MockAssetRegistry assetRegistry = new MockAssetRegistry(); + + // test if code is matching + boolean daoTradingActivated = false; + // Test if BSQ on mainnet is failing + Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ", + BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).isPresent()); + + // on testnet/regtest it is allowed + assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ", + BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "BSQ"); + + + daoTradingActivated = true; + // With daoTradingActivated we can request BSQ + assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ", + BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).get().getTickerSymbol(), "BSQ"); + + // Test if not matching ticker is failing + Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ1", + BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).isPresent()); + + // Add a mock coin which has no mainnet version, needs to fail if we are on mainnet + MockTestnetCoin.Testnet mockTestnetCoin = new MockTestnetCoin.Testnet(); + try { + assetRegistry.addAsset(mockTestnetCoin); + CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN", + BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated); + Assert.fail("Expected an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + String wantMessage = "We are on mainnet and we could not find an asset with network type mainnet"; + Assert.assertTrue("Unexpected exception, want message starting with " + + "'" + wantMessage + "', got '" + e.getMessage() + "'", e.getMessage().startsWith(wantMessage)); + } + + // For testnet its ok + assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN", + BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN"); + assertEquals(Coin.Network.TESTNET, mockTestnetCoin.getNetwork()); + + // For regtest its still found + assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN", + BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN"); + + + // We test if we are not on mainnet to get the mainnet coin + Coin ether = new Ether(); + assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH", + BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "ETH"); + assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH", + BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get().getTickerSymbol(), "ETH"); + assertEquals(Coin.Network.MAINNET, ether.getNetwork()); + + // We test if network matches exactly if there are distinct network types defined like with BSQ + Coin bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).get(); + assertEquals("BSQ", bsq.getTickerSymbol()); + assertEquals(Coin.Network.MAINNET, bsq.getNetwork()); + + bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get(); + assertEquals("BSQ", bsq.getTickerSymbol()); + assertEquals(Coin.Network.TESTNET, bsq.getNetwork()); + + bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get(); + assertEquals("BSQ", bsq.getTickerSymbol()); + assertEquals(Coin.Network.REGTEST, bsq.getNetwork()); + } + + class MockAssetRegistry extends AssetRegistry { + private List registeredAssets = new ArrayList<>(); + + MockAssetRegistry() { + for (Asset asset : ServiceLoader.load(Asset.class)) { + registeredAssets.add(asset); + } + } + + void addAsset(Asset asset) { + registeredAssets.add(asset); + } + + public Stream stream() { + return registeredAssets.stream(); + } + } } diff --git a/core/src/test/java/bisq/core/locale/MockTestnetCoin.java b/core/src/test/java/bisq/core/locale/MockTestnetCoin.java new file mode 100644 index 0000000000..ddffc0e8fa --- /dev/null +++ b/core/src/test/java/bisq/core/locale/MockTestnetCoin.java @@ -0,0 +1,69 @@ +/* + * 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.locale; + +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.params.MainNetParams; +import org.bitcoinj.params.RegTestParams; +import org.bitcoinj.params.TestNet3Params; + + + +import bisq.asset.AddressValidationResult; +import bisq.asset.Base58BitcoinAddressValidator; +import bisq.asset.Coin; + +public class MockTestnetCoin extends Coin { + + public MockTestnetCoin(Network network, NetworkParameters networkParameters) { + super("MockTestnetCoin", "MOCK_COIN", new BSQAddressValidator(networkParameters), network); + } + + public static class Mainnet extends MockTestnetCoin { + + public Mainnet() { + super(Network.MAINNET, MainNetParams.get()); + } + } + + public static class Testnet extends MockTestnetCoin { + + public Testnet() { + super(Network.TESTNET, TestNet3Params.get()); + } + } + + public static class Regtest extends MockTestnetCoin { + + public Regtest() { + super(Network.REGTEST, RegTestParams.get()); + } + } + + public static class BSQAddressValidator extends Base58BitcoinAddressValidator { + + public BSQAddressValidator(NetworkParameters networkParameters) { + super(networkParameters); + } + + @Override + public AddressValidationResult validate(String address) { + return super.validate(address); + } + } +} diff --git a/core/src/test/java/bisq/core/offer/OfferMaker.java b/core/src/test/java/bisq/core/offer/OfferMaker.java index c32f9f0adb..aa9d294c2e 100644 --- a/core/src/test/java/bisq/core/offer/OfferMaker.java +++ b/core/src/test/java/bisq/core/offer/OfferMaker.java @@ -33,9 +33,10 @@ public class OfferMaker { public static final Property direction = new Property<>(); public static final Property useMarketBasedPrice = new Property<>(); public static final Property marketPriceMargin = new Property<>(); + public static final Property id = new Property<>(); public static final Instantiator Offer = lookup -> new Offer( - new OfferPayload("", + new OfferPayload(lookup.valueOf(id, "1234"), 0L, null, null, diff --git a/core/src/test/java/bisq/core/payment/validation/AltCoinAddressValidatorTest.java b/core/src/test/java/bisq/core/payment/validation/AltCoinAddressValidatorTest.java index 095e9d9dd9..1ef48be4c3 100644 --- a/core/src/test/java/bisq/core/payment/validation/AltCoinAddressValidatorTest.java +++ b/core/src/test/java/bisq/core/payment/validation/AltCoinAddressValidatorTest.java @@ -22,14 +22,14 @@ import bisq.core.btc.BaseCurrencyNetwork; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; -import bisq.asset.AssetRegistry; - import org.junit.Test; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; + + + +import bisq.asset.AssetRegistry; public class AltCoinAddressValidatorTest { @@ -50,11 +50,7 @@ public class AltCoinAddressValidatorTest { assertTrue(validator.validate("Lg3PX8wRWmApFCoCMAsPF5P9dPHYQHEWKW").isValid); validator.setCurrencyCode("BOGUS"); - try { - validator.validate("1BOGUSADDR"); - fail("expected validation to fail for unregistered asset 'BOGUS'"); - } catch (IllegalArgumentException ex) { - assertThat(ex.getMessage(), containsString("'BOGUS' is not a registered asset")); - } + + assertFalse(validator.validate("1BOGUSADDR").isValid); } } diff --git a/desktop/build.gradle b/desktop/build.gradle index 6ce825246d..d189224faa 100644 --- a/desktop/build.gradle +++ b/desktop/build.gradle @@ -86,7 +86,7 @@ installDist.destinationDir = file('build/app') // 1. Remove the block entirely // 2. Replace the block with the following command: // -// ./gradlew -q calculateChecksums | grep -v network.bisq:bisq- >> build.gradle +// ./gradlew -q calculateChecksums | grep -v network.bisq >> desktop/build.gradle // // 3. Run `git diff` to verify that expected hashes have changed // 4. Commit the changes @@ -101,6 +101,13 @@ dependencyVerification { 'de.jensd:fontawesomefx-commons:5539bb3335ecb822dbf928546f57766eeb9f1516cc1417a064b5709629612149', 'com.googlecode.jcsv:jcsv:73ca7d715e90c8d2c2635cc284543b038245a34f70790660ed590e157b8714a2', 'com.github.sarxos:webcam-capture:d960b7ea8ec3ddf2df0725ef214c3fccc9699ea7772df37f544e1f8e4fd665f6', + 'com.github.JesusMcCloud.netlayer:tor.native:de44e782b21838d3426dbff99abbfd1cbb8e5d3f6d5e997441ff4fd8354934fa', + 'org.apache.httpcomponents:httpclient:db3d1b6c2d6a5e5ad47577ad61854e2f0e0936199b8e05eb541ed52349263135', + 'net.sf.jopt-simple:jopt-simple:6f45c00908265947c39221035250024f2caec9a15c1c8cf553ebeecee289f342', + 'org.fxmisc.easybind:easybind:666af296dda6de68751668a62661571b5238ac6f1c07c8a204fc6f902b222aaf', + 'com.fasterxml.jackson.core:jackson-databind:fcf3c2b0c332f5f54604f7e27fa7ee502378a2cc5df6a944bbfae391872c32ff', + 'com.fasterxml.jackson.core:jackson-core:39a74610521d7fb9eb3f437bb8739bbf47f6435be12d17bf954c731a0c6352bb', + 'com.fasterxml.jackson.core:jackson-annotations:2566b3a6662afa3c6af4f5b25006cb46be2efc68f1b5116291d6998a8cdf7ed3', 'com.google.protobuf:protobuf-java:b5e2d91812d183c9f053ffeebcbcda034d4de6679521940a19064714966c2cd4', 'com.google.code.gson:gson:2d43eb5ea9e133d2ee2405cc14f5ee08951b8361302fdd93494a3a997b508d32', 'com.googlecode.json-simple:json-simple:4e69696892b88b41c55d49ab2fdcc21eead92bf54acc588c0050596c3b75199c', @@ -111,8 +118,6 @@ dependencyVerification { 'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd', 'com.google.guava:guava:36a666e3b71ae7f0f0dca23654b67e086e6c93d192f60ba5dfd5519db6c288c8', 'com.google.inject:guice:9b9df27a5b8c7864112b4137fd92b36c3f1395bfe57be42fedf2f520ead1a93e', - 'network.bisq.libdohj:libdohj-core:77949092ec50f4526257b2d8b84fff995714443fa6c0e37555335fd97fc35fa0', - 'com.github.JesusMcCloud.netlayer:tor.native:de44e782b21838d3426dbff99abbfd1cbb8e5d3f6d5e997441ff4fd8354934fa', 'com.github.JesusMcCloud.netlayer:tor:3896950c56a41985f901ff9475524ac162cba18b2d5a0ed39810b20ddaf5128a', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:841b021d62fc007ce2883963ff9440d5393fb1f6a0604ed68cd016afcaf02967', 'com.github.MicroUtils:kotlin-logging:7dbd501cc210d721f730d480c53ee2a6e3c154ae89b07dc7dee224b9c5aca9eb', @@ -123,21 +128,7 @@ dependencyVerification { 'commons-io:commons-io:cc6a41dc3eaacc9e440a6bd0d2890b20d36b4ee408fe2d67122f328bb6e01581', 'org.apache.commons:commons-lang3:734c8356420cc8e30c795d64fd1fcd5d44ea9d90342a2cc3262c5158fbc6d98b', 'org.bouncycastle:bcprov-jdk15on:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', - 'net.sf.jopt-simple:jopt-simple:6f45c00908265947c39221035250024f2caec9a15c1c8cf553ebeecee289f342', - 'network.bisq.btcd-cli4j:btcd-cli4j-daemon:8a29f395d77080f276ec83db97bd786a48ec4cdff03fb4b578afa1c9d41040c0', - 'network.bisq.btcd-cli4j:btcd-cli4j-core:0ee93923450dd600049d348ef2ea6c20c87f948d5a6ece3dba2b97f09b092436', - 'com.fasterxml.jackson.core:jackson-databind:fcf3c2b0c332f5f54604f7e27fa7ee502378a2cc5df6a944bbfae391872c32ff', - 'com.fasterxml.jackson.core:jackson-core:39a74610521d7fb9eb3f437bb8739bbf47f6435be12d17bf954c731a0c6352bb', - 'com.fasterxml.jackson.core:jackson-annotations:2566b3a6662afa3c6af4f5b25006cb46be2efc68f1b5116291d6998a8cdf7ed3', - 'org.apache.httpcomponents:httpclient:db3d1b6c2d6a5e5ad47577ad61854e2f0e0936199b8e05eb541ed52349263135', - 'org.fxmisc.easybind:easybind:666af296dda6de68751668a62661571b5238ac6f1c07c8a204fc6f902b222aaf', 'com.google.zxing:javase:0ec23e2ec12664ddd6347c8920ad647bb3b9da290f897a88516014b56cc77eb9', - 'commons-logging:commons-logging:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636', - 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', - 'aopalliance:aopalliance:0addec670fedcd3f113c5c8091d783280d23f75e3acb841b61a9cdb079376a08', - 'com.github.bisq-network.bitcoinj:bitcoinj-core:e8a30946ee30c09d7e6cff6cb082c6c9117e30b8b940f73458f4ce4c4edb1585', - 'com.lambdaworks:scrypt:9a82d218099fb14c10c0e86e7eefeebd8c104de920acdc47b8b4b7a686fb73b4', - 'commons-codec:commons-codec:ad19d2601c3abf0b946b5c3a4113e226a8c1e3305e395b90013b78dd94a723ce', 'com.nativelibs4java:bridj:101bcd9b6637e6bc16e56deb3daefba62b1f5e8e9e37e1b3e56e3b5860d659cf', 'com.cedricwalter:tor-binary-macos:87790e9eade1e44eeadc81f92670f338cd47ef1b39b46a4b022c75d0cf6465fd', 'com.cedricwalter:tor-binary-linux32:814f6da3b662c96490bcb09781764dd31dfe497ea9c25c73fe61170d2a78086f', @@ -145,16 +136,22 @@ dependencyVerification { 'com.cedricwalter:tor-binary-windows:9487a735dadcadc6ede5ffad36a911c2d4a484f996be93d71094f26591b8c29e', 'com.github.ravn:jsocks:3c71600af027b2b6d4244e4ad14d98ff2352a379410daebefff5d8cd48d742a4', 'org.apache.httpcomponents:httpcore:d7f853dee87680b07293d30855b39b9eb56c1297bd16ff1cd6f19ddb8fa745fb', + 'commons-codec:commons-codec:ad19d2601c3abf0b946b5c3a4113e226a8c1e3305e395b90013b78dd94a723ce', + 'commons-logging:commons-logging:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636', + 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', + 'aopalliance:aopalliance:0addec670fedcd3f113c5c8091d783280d23f75e3acb841b61a9cdb079376a08', + 'com.github.bisq-network.bitcoinj:bitcoinj-core:15e0f4304dd92259c4e9ff0114cbeab7a79abb51a5817b422ce629d3a0a2d551', + 'com.lambdaworks:scrypt:9a82d218099fb14c10c0e86e7eefeebd8c104de920acdc47b8b4b7a686fb73b4', 'com.google.zxing:core:11aae8fd974ab25faa8208be50468eb12349cd239e93e7c797377fa13e381729', - 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', - 'net.jcip:jcip-annotations:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0', - 'org.bitcoinj:orchid:f836325cfa0466a011cb755c9b0fee6368487a2352eb45f4306ad9e4c18de080', - 'com.squareup.okhttp:okhttp:b4c943138fcef2bcc9d2006b2250c4aabbedeafc5947ed7c0af7fd103ceb2707', 'com.cedricwalter:tor-binary-geoip:7fc7b5ebf80d65ec53d97dd8d3878b8d2c85dc04f3943e5e85e7ba641655492b', 'com.github.JesusMcCloud:jtorctl:c6ef92e46074d8d26db718ce0fe4b64b8cf7b934b7377d164c5d613b4cd7b847', 'org.apache.commons:commons-compress:a778bbd659722889245fc52a0ec2873fbbb89ec661bc1ad3dc043c0757c784c4', 'org.tukaani:xz:a594643d73cc01928cf6ca5ce100e094ea9d73af760a5d4fb6b75fa673ecec96', - 'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266', + 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', + 'net.jcip:jcip-annotations:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0', + 'org.bitcoinj:orchid:f836325cfa0466a011cb755c9b0fee6368487a2352eb45f4306ad9e4c18de080', + 'com.squareup.okhttp:okhttp:b4c943138fcef2bcc9d2006b2250c4aabbedeafc5947ed7c0af7fd103ceb2707', 'org.objenesis:objenesis:5e168368fbc250af3c79aa5fef0c3467a2d64e5a7bd74005f25d8399aeb0708d', + 'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266', ] } diff --git a/desktop/src/main/java/bisq/desktop/app/BisqApp.java b/desktop/src/main/java/bisq/desktop/app/BisqApp.java index 97e450c17f..d57353003e 100644 --- a/desktop/src/main/java/bisq/desktop/app/BisqApp.java +++ b/desktop/src/main/java/bisq/desktop/app/BisqApp.java @@ -17,7 +17,6 @@ package bisq.desktop.app; -import bisq.desktop.SystemTray; import bisq.desktop.common.view.CachingViewLoader; import bisq.desktop.common.view.View; import bisq.desktop.common.view.ViewLoader; @@ -34,7 +33,8 @@ import bisq.desktop.util.ImageUtil; import bisq.core.alert.AlertManager; import bisq.core.app.AppOptionKeys; -import bisq.core.app.AvoidStandbyMode; +import bisq.core.app.AvoidStandbyModeService; +import bisq.core.app.BisqEnvironment; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.WalletsManager; import bisq.core.filter.FilterManager; @@ -44,6 +44,7 @@ import bisq.core.user.Preferences; import bisq.common.UserThread; import bisq.common.app.DevEnv; +import bisq.common.app.Log; import bisq.common.setup.GracefulShutDownHandler; import bisq.common.setup.UncaughtExceptionHandler; import bisq.common.util.Profiler; @@ -73,6 +74,11 @@ import javafx.scene.layout.StackPane; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; + import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -125,7 +131,7 @@ public class BisqApp extends Application implements UncaughtExceptionHandler { scene = createAndConfigScene(mainView, injector); setupStage(scene); - injector.getInstance(AvoidStandbyMode.class).init(); + injector.getInstance(AvoidStandbyModeService.class).init(); UserThread.runPeriodically(() -> Profiler.printSystemLoad(log), LOG_MEMORY_PERIOD_MIN, TimeUnit.MINUTES); } catch (Throwable throwable) { @@ -219,6 +225,10 @@ public class BisqApp extends Application implements UncaughtExceptionHandler { // 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]"; stage.setTitle(appName); stage.setScene(scene); stage.setMinWidth(1020); @@ -259,6 +269,17 @@ public class BisqApp extends Application implements UncaughtExceptionHandler { showSendAlertMessagePopup(injector); } else if (Utilities.isAltOrCtrlPressed(KeyCode.F, keyEvent)) { showFilterPopup(injector); + } else if (Utilities.isAltOrCtrlPressed(KeyCode.T, keyEvent)) { + // Toggle between show tor logs and only show warnings. Helpful in case of connection problems + String pattern = "org.berndpruenster.netlayer"; + Level logLevel = ((Logger) LoggerFactory.getLogger(pattern)).getLevel(); + if (logLevel != Level.DEBUG) { + log.info("Set log level for org.berndpruenster.netlayer classes to DEBUG"); + Log.setCustomLogLevel(pattern, Level.DEBUG); + } else { + log.info("Set log level for org.berndpruenster.netlayer classes to WARN"); + Log.setCustomLogLevel(pattern, Level.WARN); + } } else if (Utilities.isAltOrCtrlPressed(KeyCode.J, keyEvent)) { WalletsManager walletsManager = injector.getInstance(WalletsManager.class); if (walletsManager.areWalletsAvailable()) @@ -291,7 +312,7 @@ public class BisqApp extends Application implements UncaughtExceptionHandler { // We show a popup to inform user that open offers will be removed if Bisq is not running. String key = "showOpenOfferWarnPopupAtShutDown"; - if (injector.getInstance(Preferences.class).showAgain(key)) { + if (injector.getInstance(Preferences.class).showAgain(key) && !DevEnv.isDevMode()) { new Popup<>().information(Res.get("popup.info.shutDownWithOpenOffers")) .dontShowAgainId(key) .useShutDownButton() diff --git a/desktop/src/main/java/bisq/desktop/SystemTray.java b/desktop/src/main/java/bisq/desktop/app/SystemTray.java similarity index 99% rename from desktop/src/main/java/bisq/desktop/SystemTray.java rename to desktop/src/main/java/bisq/desktop/app/SystemTray.java index 14baac9942..8cb53767ae 100644 --- a/desktop/src/main/java/bisq/desktop/SystemTray.java +++ b/desktop/src/main/java/bisq/desktop/app/SystemTray.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.desktop; +package bisq.desktop.app; import bisq.desktop.util.GUIUtil; import bisq.desktop.util.ImageUtil; diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java index b8c7ccdfea..4d397b2210 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java @@ -21,6 +21,7 @@ import bisq.desktop.components.InputTextField; import bisq.desktop.util.FormBuilder; import bisq.desktop.util.Layout; +import bisq.core.dao.governance.asset.AssetService; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; @@ -48,18 +49,15 @@ import javafx.util.StringConverter; import java.util.Optional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import static bisq.desktop.util.FormBuilder.addLabelInputTextField; import static bisq.desktop.util.FormBuilder.addLabelTextField; import static bisq.desktop.util.FormBuilder.addLabelTextFieldWithCopyIcon; public class CryptoCurrencyForm extends PaymentMethodForm { - private static final Logger log = LoggerFactory.getLogger(CryptoCurrencyForm.class); - private final CryptoCurrencyAccount cryptoCurrencyAccount; private final AltCoinAddressValidator altCoinAddressValidator; + private final AssetService assetService; + private InputTextField addressInputTextField; private ComboBox currencyComboBox; @@ -80,10 +78,12 @@ public class CryptoCurrencyForm extends PaymentMethodForm { InputValidator inputValidator, GridPane gridPane, int gridRow, - BSFormatter formatter) { + BSFormatter formatter, + AssetService assetService) { super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter); this.cryptoCurrencyAccount = (CryptoCurrencyAccount) paymentAccount; this.altCoinAddressValidator = altCoinAddressValidator; + this.assetService = assetService; } @Override @@ -162,7 +162,7 @@ public class CryptoCurrencyForm extends PaymentMethodForm { currencyComboBox = FormBuilder.addLabelSearchComboBox(gridPane, ++gridRow, Res.get("payment.altcoin"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; currencyComboBox.setPromptText(Res.get("payment.select.altcoin")); - currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getAllSortedCryptoCurrencies())); + currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getWhiteListedSortedCryptoCurrencies(assetService))); currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 15)); currencyComboBox.setConverter(new StringConverter() { @Override diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/OKPayForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/OKPayForm.java index dd816a0554..21330a13c7 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/OKPayForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/OKPayForm.java @@ -52,6 +52,7 @@ import static bisq.desktop.util.FormBuilder.addLabelInputTextField; import static bisq.desktop.util.FormBuilder.addLabelTextField; import static bisq.desktop.util.FormBuilder.addLabelTextFieldWithCopyIcon; +@Deprecated public class OKPayForm extends PaymentMethodForm { private static final Logger log = LoggerFactory.getLogger(OKPayForm.class); 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 b60e5ac85e..6f09e8c4b1 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java @@ -156,12 +156,20 @@ public abstract class PaymentMethodForm { CurrencyUtil.getAllSortedCryptoCurrencies().get(0) : CurrencyUtil.getDefaultTradeCurrency(); + final boolean isAddAccountScreen = paymentAccount.getAccountName() == null; final long accountAge = !isAddAccountScreen ? accountAgeWitnessService.getMyAccountAge(paymentAccount.getPaymentAccountPayload()) : 0L; - addLabelTextField(gridPane, ++gridRow, Res.get("payment.limitations"), Res.get("payment.maxPeriodAndLimit", - getTimeText(hours), - formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrency.getCode()))), - formatter.formatAccountAge(accountAge))); + + final String limitationsText = paymentAccount instanceof CryptoCurrencyAccount ? + Res.get("payment.maxPeriodAndLimitCrypto", + getTimeText(hours), + formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrency.getCode())))) + : + Res.get("payment.maxPeriodAndLimit", + getTimeText(hours), + formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrency.getCode()))), + formatter.formatAccountAge(accountAge)); + addLabelTextField(gridPane, ++gridRow, Res.get("payment.limitations"), limitationsText); if (isAddAccountScreen) { InputTextField inputTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("payment.salt"), 0).second; diff --git a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java index f54415c432..3235fafa8c 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java @@ -17,6 +17,7 @@ package bisq.desktop.main; +import bisq.desktop.app.BisqApp; import bisq.desktop.common.model.ViewModel; import bisq.desktop.components.BalanceWithConfirmationTextField; import bisq.desktop.components.TxIdTextField; @@ -33,8 +34,8 @@ import bisq.core.alert.PrivateNotificationManager; import bisq.core.app.AppOptionKeys; import bisq.core.app.BisqEnvironment; import bisq.core.app.BisqSetup; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.payment.AccountAgeWitnessService; @@ -281,6 +282,9 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupCompleteList .onAction(() -> GUIUtil.reSyncSPVChain(walletsSetup, preferences)) .show(); }); + bisqSetup.setVoteResultExceptionHandler(voteResultException -> { + new Popup<>().error(voteResultException.toString()).show(); + }); bisqSetup.setChainFileLockedExceptionHandler(msg -> { new Popup<>().warning(msg) @@ -291,7 +295,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupCompleteList bisqSetup.setShowFirstPopupIfResyncSPVRequestedHandler(this::showFirstPopupIfResyncSPVRequested); bisqSetup.setRequestWalletPasswordHandler(aesKeyHandler -> walletPasswordWindow .onAesKey(aesKeyHandler::accept) - .hideCloseButton() + .onClose(() -> BisqApp.getShutDownHandler().run()) .show()); bisqSetup.setDisplayUpdateHandler((alert, key) -> new DisplayUpdateDownloadWindow(alert) diff --git a/desktop/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java b/desktop/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java index 035cfde20e..b7bdfa72a3 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java @@ -21,7 +21,7 @@ import bisq.desktop.common.model.ActivatableViewModel; import bisq.core.arbitration.Arbitrator; import bisq.core.arbitration.ArbitratorManager; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.locale.LanguageUtil; import bisq.core.user.User; 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 8914e3fb3f..6229958422 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 @@ -29,6 +29,7 @@ import bisq.desktop.util.FormBuilder; import bisq.desktop.util.ImageUtil; import bisq.desktop.util.Layout; +import bisq.core.dao.governance.asset.AssetService; import bisq.core.locale.CryptoCurrency; import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; @@ -76,6 +77,7 @@ public class AltCoinAccountsView extends ActivatableViewAndModel toRemove = new ArrayList<>(); paymentAccounts.stream() .filter(paymentAccount -> paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.VENMO_ID) || - paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.CASH_APP_ID)) + paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.CASH_APP_ID) || + paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.OK_PAY_ID)) .forEach(toRemove::add); toRemove.forEach(paymentAccount -> { 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 a233028a10..d32aace01b 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 @@ -395,6 +395,7 @@ public class FiatAccountsView extends ActivatableViewAndModel !paymentMethod.getId().equals(PaymentMethod.BLOCK_CHAINS_ID)) .filter(paymentMethod -> !paymentMethod.getId().equals(PaymentMethod.VENMO_ID)) .filter(paymentMethod -> !paymentMethod.getId().equals(PaymentMethod.CASH_APP_ID)) + .filter(paymentMethod -> !paymentMethod.getId().equals(PaymentMethod.OK_PAY_ID)) .collect(Collectors.toList()); paymentMethodComboBox.setItems(FXCollections.observableArrayList(list)); paymentMethodComboBox.setConverter(new StringConverter() { diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/notifications/MobileNotificationsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/notifications/MobileNotificationsView.java index 0a56003e53..d40cce4be9 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/notifications/MobileNotificationsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/notifications/MobileNotificationsView.java @@ -472,8 +472,7 @@ public class MobileNotificationsView extends ActivatableView { paymentAccountsComboBox = FormBuilder.addLabelComboBox(root, gridRow, Res.getWithCol("account.notifications.marketAlert.selectPaymentAccount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - paymentAccountsComboBox.setPromptText(Res.get("shared.select")); - paymentAccountsComboBox.setConverter(new StringConverter() { + paymentAccountsComboBox.setConverter(new StringConverter<>() { @Override public String toString(PaymentAccount paymentAccount) { return paymentAccount.getAccountName(); 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 663a545575..08c824a12c 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 @@ -203,7 +203,7 @@ public class SeedWordsView extends ActivatableView { walletPasswordWindow.headLine(Res.get("account.seed.enterPw")).onAesKey(aesKey -> { initSeedWords(walletsManager.getDecryptedSeed(aesKey, btcWalletService.getKeyChainSeed(), btcWalletService.getKeyCrypter())); showSeedScreen(); - }).show(); + }).hideForgotPasswordButton().show(); } private void initSeedWords(DeterministicSeed seed) { 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 c25e9107bd..12d3feb416 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,7 +24,7 @@ import bisq.desktop.main.funds.deposit.DepositView; import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.util.GUIUtil; -import bisq.core.btc.wallet.WalletsSetup; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.dao.DaoFacade; import bisq.core.dao.bonding.lockup.LockupType; import bisq.core.dao.governance.role.BondedRole; diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java index ced735affe..4681ebeaee 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java @@ -26,9 +26,9 @@ import bisq.desktop.util.FormBuilder; import bisq.desktop.util.Layout; import bisq.desktop.util.validation.BsqValidator; -import bisq.core.btc.Restrictions; -import bisq.core.btc.wallet.BsqBalanceListener; +import bisq.core.btc.listeners.BsqBalanceListener; import bisq.core.btc.wallet.BsqWalletService; +import bisq.core.btc.wallet.Restrictions; import bisq.core.dao.DaoFacade; import bisq.core.dao.bonding.BondingConsensus; import bisq.core.dao.bonding.lockup.LockupType; @@ -119,8 +119,7 @@ public class LockupView extends ActivatableView implements BsqBa timeInputTextField.setValidator(timeInputTextFieldValidator); lockupTypeComboBox = FormBuilder.addLabelComboBox(root, ++gridRow, Res.get("dao.bonding.lock.type")).second; - lockupTypeComboBox.setPromptText(Res.get("shared.select")); - lockupTypeComboBox.setConverter(new StringConverter() { + lockupTypeComboBox.setConverter(new StringConverter<>() { @Override public String toString(LockupType lockupType) { return lockupType.getDisplayString(); @@ -141,8 +140,7 @@ public class LockupView extends ActivatableView implements BsqBa lockupTypeComboBox.getSelectionModel().select(0); bondedRolesComboBox = FormBuilder.addLabelComboBox(root, ++gridRow, Res.get("dao.bonding.lock.bondedRoles")).second; - bondedRolesComboBox.setPromptText(Res.get("shared.select")); - bondedRolesComboBox.setConverter(new StringConverter() { + bondedRolesComboBox.setConverter(new StringConverter<>() { @Override public String toString(BondedRole bondedRole) { return bondedRole.getDisplayString(); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/unlock/UnlockView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/unlock/UnlockView.java index 54874c4a20..fa90e6dbd5 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/unlock/UnlockView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/unlock/UnlockView.java @@ -26,7 +26,7 @@ import bisq.desktop.main.dao.wallet.BsqBalanceUtil; import bisq.desktop.util.GUIUtil; import bisq.desktop.util.validation.BsqValidator; -import bisq.core.btc.wallet.BsqBalanceListener; +import bisq.core.btc.listeners.BsqBalanceListener; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.dao.DaoFacade; 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 50cb4b9095..4bef53b56c 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 @@ -26,16 +26,18 @@ import bisq.desktop.util.Layout; import bisq.desktop.util.validation.BsqAddressValidator; import bisq.desktop.util.validation.BsqValidator; +import bisq.core.btc.BaseCurrencyNetwork; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.dao.DaoFacade; import bisq.core.dao.governance.ballot.Ballot; import bisq.core.dao.governance.ballot.vote.Vote; import bisq.core.dao.governance.proposal.Proposal; import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.governance.proposal.compensation.CompensationConsensus; import bisq.core.dao.governance.proposal.compensation.CompensationProposal; import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposal; +import bisq.core.dao.governance.proposal.generic.GenericProposal; import bisq.core.dao.governance.proposal.param.ChangeParamProposal; +import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposal; import bisq.core.dao.governance.proposal.role.BondedRoleProposal; import bisq.core.dao.governance.role.BondedRole; import bisq.core.dao.governance.role.BondedRoleType; @@ -43,10 +45,10 @@ import bisq.core.dao.governance.voteresult.EvaluatedProposal; import bisq.core.dao.governance.voteresult.ProposalVoteResult; import bisq.core.dao.state.blockchain.Tx; import bisq.core.dao.state.governance.Param; +import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.util.BsqFormatter; import bisq.core.util.validation.InputValidator; -import bisq.core.util.validation.IntegerValidator; import bisq.common.util.Tuple2; @@ -84,6 +86,10 @@ import javax.annotation.Nullable; import static bisq.desktop.util.FormBuilder.*; import static com.google.common.base.Preconditions.checkNotNull; + + +import bisq.asset.Asset; + @SuppressWarnings("ConstantConditions") @Slf4j public class ProposalDisplay { @@ -106,6 +112,8 @@ public class ProposalDisplay { public ComboBox confiscateBondComboBox; @Nullable public ComboBox bondedRoleTypeComboBox; + @Nullable + public ComboBox assetComboBox; @Getter private int gridRow; @@ -120,6 +128,7 @@ public class ProposalDisplay { private List comboBoxes = new ArrayList<>(); private final ChangeListener focusOutListener; private final ChangeListener inputListener; + private ChangeListener paramChangeListener; public ProposalDisplay(GridPane gridPane, BsqFormatter bsqFormatter, BsqWalletService bsqWalletService, DaoFacade daoFacade) { @@ -156,18 +165,20 @@ public class ProposalDisplay { case COMPENSATION_REQUEST: titledGroupBgRowSpan += 1; break; - case BONDED_ROLE: - break; - case REMOVE_ALTCOIN: - break; case CHANGE_PARAM: titledGroupBgRowSpan += 1; break; - case GENERIC: + case BONDED_ROLE: break; case CONFISCATE_BOND: break; + case GENERIC: + titledGroupBgRowSpan -= 1; + break; + case REMOVE_ASSET: + break; } + // at isMakeProposalScreen we show fee but no uid and txID (+1) // otherwise we don't show fee but show uid and txID (+2) if (isMakeProposalScreen) @@ -204,7 +215,8 @@ public class ProposalDisplay { requestedBsqTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.requestedBsq")).second; BsqValidator bsqValidator = new BsqValidator(bsqFormatter); - bsqValidator.setMinValue(CompensationConsensus.getMinCompensationRequestAmount()); + bsqValidator.setMinValue(daoFacade.getMinCompensationRequestAmount()); + bsqValidator.setMaxValue(daoFacade.getMaxCompensationRequestAmount()); checkNotNull(requestedBsqTextField, "requestedBsqTextField must not be null"); requestedBsqTextField.setValidator(bsqValidator); inputControls.add(requestedBsqTextField); @@ -217,38 +229,16 @@ public class ProposalDisplay { bsqAddressTextField.setValidator(new BsqAddressValidator(bsqFormatter)); inputControls.add(bsqAddressTextField); break; - case BONDED_ROLE: - bondedRoleTypeComboBox = FormBuilder.addLabelComboBox(gridPane, ++gridRow, - Res.getWithCol("dao.proposal.display.bondedRoleComboBox.label")).second; - checkNotNull(bondedRoleTypeComboBox, "bondedRoleTypeComboBox must not be null"); - bondedRoleTypeComboBox.setPromptText(Res.get("shared.select")); - bondedRoleTypeComboBox.setItems(FXCollections.observableArrayList(BondedRoleType.values())); - bondedRoleTypeComboBox.setConverter(new StringConverter() { - @Override - public String toString(BondedRoleType bondedRoleType) { - return bondedRoleType != null ? bondedRoleType.getDisplayString() : ""; - } - - @Override - public BondedRoleType fromString(String string) { - return null; - } - }); - comboBoxes.add(bondedRoleTypeComboBox); - break; - case REMOVE_ALTCOIN: - break; case CHANGE_PARAM: checkNotNull(gridPane, "gridPane must not be null"); paramComboBox = FormBuilder.addLabelComboBox(gridPane, ++gridRow, Res.getWithCol("dao.proposal.display.paramComboBox.label")).second; checkNotNull(paramComboBox, "paramComboBox must not be null"); - paramComboBox.setPromptText(Res.get("shared.select")); List list = Arrays.stream(Param.values()) .filter(e -> e != Param.UNDEFINED && e != Param.PHASE_UNDEFINED) .collect(Collectors.toList()); paramComboBox.setItems(FXCollections.observableArrayList(list)); - paramComboBox.setConverter(new StringConverter() { + paramComboBox.setConverter(new StringConverter<>() { @Override public String toString(Param param) { return param != null ? param.getDisplayString() : ""; @@ -263,18 +253,45 @@ public class ProposalDisplay { paramValueTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.paramValue")).second; //noinspection ConstantConditions - paramValueTextField.setValidator(new IntegerValidator()); + + //TODO use custom param validator + paramValueTextField.setValidator(new InputValidator()); + inputControls.add(paramValueTextField); + + paramChangeListener = (observable, oldValue, newValue) -> { + if (newValue != null) { + paramValueTextField.clear(); + String currentValue = bsqFormatter.formatParamValue(newValue, daoFacade.getPramValue(newValue)); + paramValueTextField.setPromptText(Res.get("dao.param.currentValue", currentValue)); + } + }; + paramComboBox.getSelectionModel().selectedItemProperty().addListener(paramChangeListener); break; - case GENERIC: + case BONDED_ROLE: + bondedRoleTypeComboBox = FormBuilder.addLabelComboBox(gridPane, ++gridRow, + Res.getWithCol("dao.proposal.display.bondedRoleComboBox.label")).second; + checkNotNull(bondedRoleTypeComboBox, "bondedRoleTypeComboBox must not be null"); + bondedRoleTypeComboBox.setItems(FXCollections.observableArrayList(BondedRoleType.values())); + bondedRoleTypeComboBox.setConverter(new StringConverter<>() { + @Override + public String toString(BondedRoleType bondedRoleType) { + return bondedRoleType != null ? bondedRoleType.getDisplayString() : ""; + } + + @Override + public BondedRoleType fromString(String string) { + return null; + } + }); + comboBoxes.add(bondedRoleTypeComboBox); break; case CONFISCATE_BOND: confiscateBondComboBox = FormBuilder.addLabelComboBox(gridPane, ++gridRow, Res.getWithCol("dao.proposal.display.confiscateBondComboBox.label")).second; checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null"); - confiscateBondComboBox.setPromptText(Res.get("shared.select")); confiscateBondComboBox.setItems(FXCollections.observableArrayList(daoFacade.getValidBondedRoleList())); - confiscateBondComboBox.setConverter(new StringConverter() { + confiscateBondComboBox.setConverter(new StringConverter<>() { @Override public String toString(BondedRole bondedRole) { return bondedRole != null ? bondedRole.getDisplayString() : ""; @@ -287,6 +304,31 @@ public class ProposalDisplay { }); comboBoxes.add(confiscateBondComboBox); break; + case GENERIC: + break; + case REMOVE_ASSET: + assetComboBox = FormBuilder.addLabelComboBox(gridPane, ++gridRow, + Res.getWithCol("dao.proposal.display.assetComboBox.label")).second; + checkNotNull(assetComboBox, "assetComboBox must not be null"); + List assetList = CurrencyUtil.getAssetRegistry().stream() + .filter(e -> !e.getTickerSymbol().equals("BSQ")) + .filter(e -> !e.getTickerSymbol().equals("BTC")) + .filter(e -> CurrencyUtil.assetMatchesNetwork(e, BaseCurrencyNetwork.BTC_MAINNET)) + .collect(Collectors.toList()); + assetComboBox.setItems(FXCollections.observableArrayList(assetList)); + assetComboBox.setConverter(new StringConverter<>() { + @Override + public String toString(Asset asset) { + return asset != null ? CurrencyUtil.getNameAndCode(asset.getTickerSymbol()) : ""; + } + + @Override + public Asset fromString(String string) { + return null; + } + }); + comboBoxes.add(assetComboBox); + break; } if (!isMakeProposalScreen) { @@ -411,12 +453,18 @@ public class ProposalDisplay { checkNotNull(bondedRoleTypeComboBox, "bondedRoleComboBox must not be null"); BondedRole bondedRole = bondedRoleProposal.getBondedRole(); bondedRoleTypeComboBox.getSelectionModel().select(bondedRole.getBondedRoleType()); - } else if (proposal instanceof ConfiscateBondProposal) { ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null"); daoFacade.getBondedRoleFromHash(confiscateBondProposal.getHash()) .ifPresent(bondedRole -> confiscateBondComboBox.getSelectionModel().select(bondedRole)); + } else if (proposal instanceof GenericProposal) { + // do nothing + } else if (proposal instanceof RemoveAssetProposal) { + RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal; + checkNotNull(assetComboBox, "assetComboBox must not be null"); + CurrencyUtil.findAsset(removeAssetProposal.getTickerSymbol(), BaseCurrencyNetwork.BTC_MAINNET) + .ifPresent(asset -> assetComboBox.getSelectionModel().select(asset)); } int chainHeight; if (txIdTextField != null) { @@ -453,6 +501,9 @@ public class ProposalDisplay { //noinspection unchecked comboBox.getSelectionModel().selectedItemProperty().removeListener(inputListener); }); + + if (paramComboBox != null && paramChangeListener != null) + paramComboBox.getSelectionModel().selectedItemProperty().removeListener(paramChangeListener); } public void clearForm() { 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 8c8823965e..1545dc5d7d 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 @@ -27,22 +27,22 @@ import bisq.desktop.util.FormBuilder; import bisq.desktop.util.GUIUtil; import bisq.desktop.util.Layout; +import bisq.core.btc.exceptions.InsufficientBsqException; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.InsufficientBsqException; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.dao.DaoFacade; import bisq.core.dao.exceptions.ValidationException; import bisq.core.dao.governance.proposal.Proposal; import bisq.core.dao.governance.proposal.ProposalType; 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.role.BondedRole; import bisq.core.dao.state.BsqStateListener; import bisq.core.dao.state.blockchain.Block; import bisq.core.dao.state.governance.Param; import bisq.core.dao.state.period.DaoPhase; import bisq.core.locale.Res; -import bisq.core.provider.fee.FeeService; import bisq.core.util.BSFormatter; import bisq.core.util.BsqFormatter; @@ -70,7 +70,6 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -78,6 +77,10 @@ import static bisq.desktop.util.FormBuilder.addButtonAfterGroup; import static bisq.desktop.util.FormBuilder.addTitledGroupBg; import static com.google.common.base.Preconditions.checkNotNull; + + +import bisq.asset.Asset; + @FxmlView public class MakeProposalView extends ActivatableView implements BsqStateListener { private final DaoFacade daoFacade; @@ -85,6 +88,7 @@ public class MakeProposalView extends ActivatableView implements private final WalletsSetup walletsSetup; private final P2PService p2PService; private final PhasesView phasesView; + private final ChangeParamValidator changeParamValidator; private final BSFormatter btcFormatter; private final BsqFormatter bsqFormatter; @@ -107,8 +111,8 @@ public class MakeProposalView extends ActivatableView implements BsqWalletService bsqWalletService, WalletsSetup walletsSetup, P2PService p2PService, - FeeService feeService, PhasesView phasesView, + ChangeParamValidator changeParamValidator, BSFormatter btcFormatter, BsqFormatter bsqFormatter) { this.daoFacade = daoFacade; @@ -116,6 +120,7 @@ public class MakeProposalView extends ActivatableView implements this.walletsSetup = walletsSetup; this.p2PService = p2PService; this.phasesView = phasesView; + this.changeParamValidator = changeParamValidator; this.btcFormatter = btcFormatter; this.bsqFormatter = bsqFormatter; } @@ -128,7 +133,7 @@ public class MakeProposalView extends ActivatableView implements addTitledGroupBg(root, ++gridRow, 1, Res.get("dao.proposal.create.selectProposalType"), Layout.GROUP_DISTANCE); proposalTypeComboBox = FormBuilder.addLabelComboBox(root, gridRow, Res.getWithCol("dao.proposal.create.proposalType"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - proposalTypeComboBox.setConverter(new StringConverter() { + proposalTypeComboBox.setConverter(new StringConverter<>() { @Override public String toString(ProposalType proposalType) { return proposalType.getDisplayName(); @@ -139,7 +144,6 @@ public class MakeProposalView extends ActivatableView implements return null; } }); - proposalTypeComboBox.setPromptText(Res.get("shared.select")); proposalTypeChangeListener = (observable, oldValue, newValue) -> { selectedProposalType = newValue; removeProposalDisplay(); @@ -147,11 +151,7 @@ public class MakeProposalView extends ActivatableView implements }; alwaysVisibleGridRowIndex = gridRow + 1; - //TODO remove filter once all are implemented - List proposalTypes = Arrays.stream(ProposalType.values()) - .filter(proposalType -> proposalType != ProposalType.GENERIC && - proposalType != ProposalType.REMOVE_ALTCOIN) - .collect(Collectors.toList()); + List proposalTypes = Arrays.asList(ProposalType.values()); proposalTypeComboBox.setItems(FXCollections.observableArrayList(proposalTypes)); } @@ -207,12 +207,15 @@ public class MakeProposalView extends ActivatableView implements private void publishMyProposal(ProposalType type) { try { - final ProposalWithTransaction proposalWithTransaction = getProposalWithTransaction(type); + ProposalWithTransaction proposalWithTransaction = getProposalWithTransaction(type); + if (proposalWithTransaction == null) + return; + Proposal proposal = proposalWithTransaction.getProposal(); Transaction transaction = proposalWithTransaction.getTransaction(); Coin miningFee = transaction.getFee(); int txSize = transaction.bitcoinSerialize().length; - final Coin fee = daoFacade.getProposalFee(daoFacade.getChainHeight()); + Coin fee = daoFacade.getProposalFee(daoFacade.getChainHeight()); GUIUtil.showBsqFeeInfoPopup(fee, miningFee, txSize, bsqFormatter, btcFormatter, Res.get("dao.proposal"), () -> doPublishMyProposal(proposal, transaction)); @@ -248,6 +251,7 @@ public class MakeProposalView extends ActivatableView implements errorMessage -> new Popup<>().warning(errorMessage).show()); } + @Nullable private ProposalWithTransaction getProposalWithTransaction(ProposalType type) throws InsufficientMoneyException, ValidationException, TxException { @@ -262,16 +266,6 @@ public class MakeProposalView extends ActivatableView implements proposalDisplay.linkInputTextField.getText(), bsqFormatter.parseToCoin(proposalDisplay.requestedBsqTextField.getText()), proposalDisplay.bsqAddressTextField.getText()); - case BONDED_ROLE: - checkNotNull(proposalDisplay.bondedRoleTypeComboBox, - "proposalDisplay.bondedRoleTypeComboBox must not be null"); - bondedRole = new BondedRole(proposalDisplay.nameTextField.getText(), - proposalDisplay.linkInputTextField.getText(), - proposalDisplay.bondedRoleTypeComboBox.getSelectionModel().getSelectedItem()); - return daoFacade.getBondedRoleProposalWithTransaction(bondedRole); - case REMOVE_ALTCOIN: - //TODO - throw new RuntimeException("Not implemented yet"); case CHANGE_PARAM: checkNotNull(proposalDisplay.paramComboBox, "proposalDisplay.paramComboBox must no tbe null"); @@ -283,20 +277,27 @@ public class MakeProposalView extends ActivatableView implements String paramValueAsString = proposalDisplay.paramValueTextField.getText(); if (paramValueAsString == null || paramValueAsString.isEmpty()) throw new ValidationException("paramValue is null or empty"); - long paramValue; + try { - paramValue = Long.valueOf(paramValueAsString); - } catch (Throwable t) { - throw new ValidationException("paramValue is not a long value", t); + long paramValue = bsqFormatter.parseParamValue(selectedParam, paramValueAsString); + log.info("Change param: paramValue={}, paramValueAsString={}", paramValue, paramValueAsString); + + changeParamValidator.validateParamValue(selectedParam, paramValue); + return daoFacade.getParamProposalWithTransaction(proposalDisplay.nameTextField.getText(), + proposalDisplay.linkInputTextField.getText(), + selectedParam, + paramValue); + } catch (Throwable e) { + new Popup<>().warning(e.getMessage()).show(); + return null; } - //TODO add more custom param validation - return daoFacade.getParamProposalWithTransaction(proposalDisplay.nameTextField.getText(), + case BONDED_ROLE: + checkNotNull(proposalDisplay.bondedRoleTypeComboBox, + "proposalDisplay.bondedRoleTypeComboBox must not be null"); + bondedRole = new BondedRole(proposalDisplay.nameTextField.getText(), proposalDisplay.linkInputTextField.getText(), - selectedParam, - paramValue); - case GENERIC: - //TODO - throw new RuntimeException("Not implemented yet"); + proposalDisplay.bondedRoleTypeComboBox.getSelectionModel().getSelectedItem()); + return daoFacade.getBondedRoleProposalWithTransaction(bondedRole); case CONFISCATE_BOND: checkNotNull(proposalDisplay.confiscateBondComboBox, "proposalDisplay.confiscateBondComboBox must not be null"); @@ -304,6 +305,16 @@ public class MakeProposalView extends ActivatableView implements return daoFacade.getConfiscateBondProposalWithTransaction(proposalDisplay.nameTextField.getText(), proposalDisplay.linkInputTextField.getText(), bondedRole.getHash()); + case GENERIC: + return daoFacade.getGenericProposalWithTransaction(proposalDisplay.nameTextField.getText(), + proposalDisplay.linkInputTextField.getText()); + case REMOVE_ASSET: + checkNotNull(proposalDisplay.assetComboBox, + "proposalDisplay.assetComboBox must not be null"); + Asset asset = proposalDisplay.assetComboBox.getSelectionModel().getSelectedItem(); + return daoFacade.getRemoveAssetProposalWithTransaction(proposalDisplay.nameTextField.getText(), + proposalDisplay.linkInputTextField.getText(), + asset); default: final String msg = "Undefined ProposalType " + selectedProposalType; log.error(msg); 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 ea5e1b1e40..17174df51e 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 @@ -36,7 +36,7 @@ import bisq.desktop.util.validation.BsqValidator; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqBalanceListener; +import bisq.core.btc.listeners.BsqBalanceListener; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.dao.DaoFacade; import bisq.core.dao.governance.ballot.Ballot; @@ -306,7 +306,7 @@ public class ProposalsView extends ActivatableView implements Bs onSelectProposal(null); } - GUIUtil.setFitToRowsForTableView(tableView, 33, 28, 80); + GUIUtil.setFitToRowsForTableView(tableView, 33, 28, 2, 4); tableView.layout(); root.layout(); } @@ -418,7 +418,7 @@ public class ProposalsView extends ActivatableView implements Bs private void onSelectProposal(ProposalsListItem item) { selectedItem = item; if (selectedItem != null) { - EvaluatedProposal evaluatedProposal = voteResultService.getAllEvaluatedProposals().stream() + EvaluatedProposal evaluatedProposal = voteResultService.getEvaluatedProposalList().stream() .filter(e -> daoFacade.isTxInCorrectCycle(e.getProposal().getTxId(), daoFacade.getChainHeight())) .filter(e -> e.getProposalTxId().equals(selectedItem.getProposal().getTxId())) 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 121d443cc5..68ace4d7ca 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 @@ -47,7 +47,8 @@ public class CycleListItem { public String getCycle() { int displayIndex = resultsOfCycle.getCycleIndex() + 1; - String dateTime = bsqFormatter.formatDateTime(new Date(resultsOfCycle.getCycleStartTime())); + 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); } 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 ddc3795c5c..31fefbe611 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 @@ -23,9 +23,11 @@ import bisq.core.dao.governance.proposal.Proposal; import bisq.core.dao.governance.proposal.compensation.CompensationProposal; import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposal; import bisq.core.dao.governance.proposal.param.ChangeParamProposal; +import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposal; import bisq.core.dao.governance.proposal.role.BondedRoleProposal; import bisq.core.dao.governance.role.BondedRole; import bisq.core.dao.governance.voteresult.EvaluatedProposal; +import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.util.BsqFormatter; @@ -107,23 +109,22 @@ public class ProposalListItem { CompensationProposal compensationProposal = (CompensationProposal) proposal; Coin requestedBsq = evaluatedProposal.isAccepted() ? compensationProposal.getRequestedBsq() : Coin.ZERO; return bsqFormatter.formatCoinWithCode(requestedBsq); + case CHANGE_PARAM: + ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; + return changeParamProposal.getParam().getDisplayString(); case BONDED_ROLE: BondedRoleProposal bondedRoleProposal = (BondedRoleProposal) proposal; BondedRole bondedRole = bondedRoleProposal.getBondedRole(); return Res.get("dao.bond.bondedRoleType." + bondedRole.getBondedRoleType().name()); - case REMOVE_ALTCOIN: - // TODO - return "N/A"; - case CHANGE_PARAM: - ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; - return changeParamProposal.getParam().getDisplayString(); - case GENERIC: - // TODO - return "N/A"; case CONFISCATE_BOND: ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; // TODO add info to bond return confiscateBondProposal.getTxId(); + case GENERIC: + return proposal.getName(); + case REMOVE_ASSET: + RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal; + return CurrencyUtil.getNameAndCode(removeAssetProposal.getTickerSymbol()); } return "-"; } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ResultsOfCycle.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ResultsOfCycle.java index 98af6dc2e4..d15443596d 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ResultsOfCycle.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ResultsOfCycle.java @@ -20,8 +20,9 @@ package bisq.desktop.main.dao.governance.result; import bisq.core.dao.governance.proposal.Proposal; import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMerits; import bisq.core.dao.governance.voteresult.EvaluatedProposal; +import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.blockchain.Block; import bisq.core.dao.state.period.Cycle; -import bisq.core.util.BsqFormatter; import java.util.List; @@ -33,12 +34,14 @@ import lombok.extern.slf4j.Slf4j; class ResultsOfCycle { private final Cycle cycle; private final int cycleIndex; - private final int numVotes, numAcceptedVotes, numRejectedVotes; - private BsqFormatter bsqFormatter; + private final int numVotes; + private final int numAcceptedVotes; + private final int numRejectedVotes; + private final BsqStateService bsqStateService; private long cycleStartTime; // All available proposals of cycle - private List proposals; + private final List proposals; // Proposals which ended up in voting private final List evaluatedProposals; @@ -50,7 +53,8 @@ class ResultsOfCycle { long cycleStartTime, List proposals, List evaluatedProposals, - List decryptedVotesForCycle) { + List decryptedVotesForCycle, + BsqStateService bsqStateService) { this.cycle = cycle; this.cycleIndex = cycleIndex; this.cycleStartTime = cycleStartTime; @@ -67,5 +71,16 @@ class ResultsOfCycle { numRejectedVotes = evaluatedProposals.stream() .mapToInt(e -> e.getProposalVoteResult().getNumRejectedVotes()) .sum(); + this.bsqStateService = bsqStateService; + } + + public long getCycleStartTime() { + // At a new cycle we have cycleStartTime 0 as the block is not processed yet. + // To display a correct value we access again from the bsqStateService + if (cycleStartTime == 0) + cycleStartTime = bsqStateService.getBlockAtHeight(cycle.getHeightOfFirstBlock()) + .map(Block::getTime) + .orElse(0L); + return cycleStartTime; } } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java index 240166b301..96eff76e63 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java @@ -33,7 +33,6 @@ import bisq.core.dao.DaoFacade; import bisq.core.dao.governance.ballot.Ballot; import bisq.core.dao.governance.proposal.Proposal; import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMerits; import bisq.core.dao.governance.voteresult.EvaluatedProposal; import bisq.core.dao.governance.voteresult.VoteResultService; @@ -242,16 +241,15 @@ public class VoteResultView extends ActivatableView implements B private void fillCycleList() { cycleListItemList.clear(); bsqStateService.getCycles().forEach(cycle -> { - List proposalsForCycle = proposalService.getProposalPayloads().stream() - .map(ProposalPayload::getProposal) + List proposalsForCycle = proposalService.getValidatedProposals().stream() .filter(proposal -> cycleService.isTxInCycle(cycle, proposal.getTxId())) .collect(Collectors.toList()); - List evaluatedProposalsForCycle = voteResultService.getAllEvaluatedProposals().stream() + List evaluatedProposalsForCycle = voteResultService.getEvaluatedProposalList().stream() .filter(evaluatedProposal -> cycleService.isTxInCycle(cycle, evaluatedProposal.getProposal().getTxId())) .collect(Collectors.toList()); - List decryptedVotesForCycle = voteResultService.getAllDecryptedBallotsWithMerits().stream() + List decryptedVotesForCycle = voteResultService.getDecryptedBallotsWithMeritsList().stream() .filter(decryptedBallotsWithMerits -> cycleService.isTxInCycle(cycle, decryptedBallotsWithMerits.getBlindVoteTxId())) .filter(decryptedBallotsWithMerits -> cycleService.isTxInCycle(cycle, decryptedBallotsWithMerits.getVoteRevealTxId())) .collect(Collectors.toList()); @@ -265,13 +263,14 @@ public class VoteResultView extends ActivatableView implements B cycleStartTime, proposalsForCycle, evaluatedProposalsForCycle, - decryptedVotesForCycle); + decryptedVotesForCycle, + bsqStateService); CycleListItem cycleListItem = new CycleListItem(resultsOfCycle, bsqStateService, bsqFormatter); cycleListItemList.add(cycleListItem); }); Collections.reverse(cycleListItemList); - GUIUtil.setFitToRowsForTableView(cyclesTableView, 24, 28, 80); + GUIUtil.setFitToRowsForTableView(cyclesTableView, 24, 28, 2, 4); } @@ -338,7 +337,7 @@ public class VoteResultView extends ActivatableView implements B ballotByProposalTxIdMap.get(evaluatedProposal.getProposalTxId()), bsqFormatter)) .collect(Collectors.toList())); - GUIUtil.setFitToRowsForTableView(proposalsTableView, 33, 28, 80); + GUIUtil.setFitToRowsForTableView(proposalsTableView, 33, 28, 2, 4); } @@ -407,7 +406,7 @@ public class VoteResultView extends ActivatableView implements B }); voteListItemList.sort(Comparator.comparing(VoteListItem::getBlindVoteTxId)); - GUIUtil.setFitToRowsForTableView(votesTableView, 33, 28, 80); + GUIUtil.setFitToRowsForTableView(votesTableView, 33, 28, 2, 4); } @@ -421,12 +420,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(160); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -446,12 +444,11 @@ public class VoteResultView extends ActivatableView implements B column.setMaxWidth(90); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -471,12 +468,11 @@ public class VoteResultView extends ActivatableView implements B column.setMaxWidth(70); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -495,12 +491,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(70); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -519,12 +514,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(70); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -553,12 +547,11 @@ public class VoteResultView extends ActivatableView implements B column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final ProposalListItem item, boolean empty) { super.updateItem(item, empty); @@ -580,12 +573,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(80); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final ProposalListItem item, boolean empty) { @@ -609,13 +601,12 @@ public class VoteResultView extends ActivatableView implements B column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { private HyperlinkWithIcon field; @Override @@ -644,12 +635,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(150); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final ProposalListItem item, boolean empty) { super.updateItem(item, empty); @@ -669,12 +659,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(180); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final ProposalListItem item, boolean empty) { super.updateItem(item, empty); @@ -694,13 +683,12 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(70); column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback, - TableCell>() { + column.setCellFactory(new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { Label myVoteIcon; @Override @@ -728,12 +716,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(90); column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback, - TableCell>() { + column.setCellFactory(new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { Label icon; @Override @@ -771,12 +758,11 @@ public class VoteResultView extends ActivatableView implements B column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { private Label icon; @Override @@ -803,12 +789,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(100); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final VoteListItem item, boolean empty) { super.updateItem(item, empty); @@ -826,12 +811,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(100); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final VoteListItem item, boolean empty) { super.updateItem(item, empty); @@ -850,12 +834,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(100); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final VoteListItem item, boolean empty) { super.updateItem(item, empty); @@ -874,12 +857,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(130); column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback, - TableCell>() { + column.setCellFactory(new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { private HyperlinkWithIcon hyperlinkWithIcon; @Override @@ -909,12 +891,11 @@ public class VoteResultView extends ActivatableView implements B column.setMinWidth(140); column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback, - TableCell>() { + column.setCellFactory(new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { private HyperlinkWithIcon hyperlinkWithIcon; @Override 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 6300a4231c..d34bd2cc18 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 @@ -19,7 +19,7 @@ package bisq.desktop.main.dao.wallet; import bisq.desktop.util.Layout; -import bisq.core.btc.wallet.BsqBalanceListener; +import bisq.core.btc.listeners.BsqBalanceListener; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.locale.Res; import bisq.core.util.BsqFormatter; 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 4c968622bd..99ac25dfc8 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 @@ -33,15 +33,15 @@ import bisq.desktop.util.validation.BsqAddressValidator; import bisq.desktop.util.validation.BsqValidator; import bisq.desktop.util.validation.BtcValidator; -import bisq.core.btc.Restrictions; -import bisq.core.btc.wallet.BsqBalanceListener; +import bisq.core.btc.exceptions.TxBroadcastException; +import bisq.core.btc.exceptions.TxMalleabilityException; +import bisq.core.btc.listeners.BsqBalanceListener; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcastException; +import bisq.core.btc.wallet.Restrictions; import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.TxMalleabilityException; import bisq.core.btc.wallet.WalletsManager; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.locale.Res; import bisq.core.util.BSFormatter; import bisq.core.util.BsqFormatter; diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxListItem.java index 3efb7c37f2..9c7b4f9535 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxListItem.java @@ -43,7 +43,6 @@ import static com.google.common.base.Preconditions.checkNotNull; @EqualsAndHashCode(callSuper = true) @Data class BsqTxListItem extends TxConfidenceListItem { - private final BtcWalletService btcWalletService; private final DaoFacade daoFacade; private final BsqFormatter bsqFormatter; private final Date date; @@ -64,7 +63,6 @@ class BsqTxListItem extends TxConfidenceListItem { BsqFormatter bsqFormatter) { super(transaction, bsqWalletService); - this.btcWalletService = btcWalletService; this.daoFacade = daoFacade; this.isBurnedBsqTx = daoFacade.hasTxBurntFee(transaction.getHashAsString()); this.date = date; 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 d355f80dce..da3369646d 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 @@ -28,7 +28,7 @@ import bisq.desktop.util.FormBuilder; import bisq.desktop.util.GUIUtil; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.wallet.BsqBalanceListener; +import bisq.core.btc.listeners.BsqBalanceListener; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.dao.DaoFacade; diff --git a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java index edf6f28278..7a9a4b7ecd 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java @@ -20,9 +20,9 @@ package bisq.desktop.main.funds.deposit; import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.desktop.util.GUIUtil; -import bisq.core.btc.AddressEntry; import bisq.core.btc.listeners.BalanceListener; import bisq.core.btc.listeners.TxConfidenceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.locale.Res; import bisq.core.util.BSFormatter; 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 4b2e54ce9b..6f13010510 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 @@ -30,8 +30,8 @@ import bisq.desktop.main.overlays.windows.QRCodeWindow; import bisq.desktop.util.GUIUtil; import bisq.desktop.util.Layout; -import bisq.core.btc.AddressEntry; import bisq.core.btc.listeners.BalanceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.locale.Res; import bisq.core.provider.fee.FeeService; diff --git a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedListItem.java index 30816ee563..1bbd53b836 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedListItem.java @@ -19,8 +19,8 @@ package bisq.desktop.main.funds.locked; import bisq.desktop.components.AutoTooltipLabel; -import bisq.core.btc.AddressEntry; import bisq.core.btc.listeners.BalanceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.WalletService; import bisq.core.trade.Tradable; 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 9589fb7a2d..d0de25d517 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 @@ -25,8 +25,8 @@ import bisq.desktop.main.overlays.windows.OfferDetailsWindow; import bisq.desktop.main.overlays.windows.TradeDetailsWindow; import bisq.desktop.util.GUIUtil; -import bisq.core.btc.AddressEntry; import bisq.core.btc.listeners.BalanceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; diff --git a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedListItem.java index 9a06862be3..34cb41be4d 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedListItem.java @@ -19,8 +19,8 @@ package bisq.desktop.main.funds.reserved; import bisq.desktop.components.AutoTooltipLabel; -import bisq.core.btc.AddressEntry; import bisq.core.btc.listeners.BalanceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.offer.OpenOffer; import bisq.core.trade.Tradable; 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 eebc3d08c5..64fe0831d4 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 @@ -25,8 +25,8 @@ import bisq.desktop.main.overlays.windows.OfferDetailsWindow; import bisq.desktop.main.overlays.windows.TradeDetailsWindow; import bisq.desktop.util.GUIUtil; -import bisq.core.btc.AddressEntry; import bisq.core.btc.listeners.BalanceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; 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 d30594a8f8..0d3127dca4 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 @@ -28,8 +28,8 @@ import bisq.desktop.main.overlays.windows.OfferDetailsWindow; import bisq.desktop.main.overlays.windows.TradeDetailsWindow; import bisq.desktop.util.GUIUtil; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; import bisq.core.trade.Tradable; diff --git a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java index a9d2d5347e..acba8ef5ba 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java @@ -19,8 +19,8 @@ package bisq.desktop.main.funds.withdrawal; import bisq.desktop.components.AutoTooltipLabel; -import bisq.core.btc.AddressEntry; import bisq.core.btc.listeners.BalanceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.locale.Res; import bisq.core.util.BSFormatter; 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 2293918598..93af2a5e77 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 @@ -27,13 +27,13 @@ import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.windows.WalletPasswordWindow; import bisq.desktop.util.GUIUtil; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.AddressEntryException; -import bisq.core.btc.InsufficientFundsException; -import bisq.core.btc.Restrictions; +import bisq.core.btc.exceptions.AddressEntryException; +import bisq.core.btc.exceptions.InsufficientFundsException; import bisq.core.btc.listeners.BalanceListener; +import bisq.core.btc.setup.WalletsSetup; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.WalletsSetup; +import bisq.core.btc.wallet.Restrictions; import bisq.core.locale.Res; import bisq.core.trade.Tradable; import bisq.core.trade.Trade; diff --git a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java index fd835a17b6..bea7282c0e 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsViewModel.java @@ -74,6 +74,8 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import javax.annotation.Nullable; + class TradesChartsViewModel extends ActivatableViewModel { private static final int TAB_INDEX = 2; @@ -129,7 +131,10 @@ class TradesChartsViewModel extends ActivatableViewModel { fillTradeCurrencies(); }; - Optional tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(preferences.getTradeChartsScreenCurrencyCode()); + String tradeChartsScreenCurrencyCode = preferences.getTradeChartsScreenCurrencyCode(); + showAllTradeCurrenciesProperty.set(isShowAllEntry(tradeChartsScreenCurrencyCode)); + + Optional tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(tradeChartsScreenCurrencyCode); if (tradeCurrencyOptional.isPresent()) selectedTradeCurrencyProperty.set(tradeCurrencyOptional.get()); else @@ -186,8 +191,8 @@ class TradesChartsViewModel extends ActivatableViewModel { showAllTradeCurrenciesProperty.set(showAllEntry); if (!showAllEntry) { selectedTradeCurrencyProperty.set(tradeCurrency); - preferences.setTradeChartsScreenCurrencyCode(code); } + preferences.setTradeChartsScreenCurrencyCode(code); updateChartData(); @@ -376,11 +381,11 @@ class TradesChartsViewModel extends ActivatableViewModel { return getTimeFromTick(index); } - private boolean isShowAllEntry(String id) { - return id.equals(GUIUtil.SHOW_ALL_FLAG); + private boolean isShowAllEntry(@Nullable String id) { + return id != null && id.equals(GUIUtil.SHOW_ALL_FLAG); } - private boolean isEditEntry(String id) { - return id.equals(GUIUtil.EDIT_FLAG); + private boolean isEditEntry(@Nullable String id) { + return id != null && id.equals(GUIUtil.EDIT_FLAG); } } 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 a1962c0fdd..6f02681699 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java @@ -19,12 +19,12 @@ package bisq.desktop.main.offer; import bisq.core.app.BisqEnvironment; import bisq.core.arbitration.Arbitrator; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.Restrictions; import bisq.core.btc.listeners.BalanceListener; -import bisq.core.btc.wallet.BsqBalanceListener; +import bisq.core.btc.listeners.BsqBalanceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.btc.wallet.Restrictions; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.filter.FilterManager; import bisq.core.locale.CurrencyUtil; @@ -588,7 +588,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs feeService.requestFees(() -> { txFeeFromFeeService = feeService.getTxFee(feeTxSize); calculateTotalToPay(); - }, null); + }); } void setPreferredCurrencyForMakerFeeBtc(boolean preferredCurrencyForMakerFeeBtc) { 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 bd12d115f2..09b5b226bb 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java @@ -35,8 +35,8 @@ import bisq.desktop.util.validation.MonetaryValidator; import bisq.desktop.util.validation.SecurityDepositValidator; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.Restrictions; -import bisq.core.btc.wallet.WalletsSetup; +import bisq.core.btc.setup.WalletsSetup; +import bisq.core.btc.wallet.Restrictions; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/OfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/OfferDataModel.java index c9c188a7c2..ef4535be09 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/OfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/OfferDataModel.java @@ -19,7 +19,7 @@ package bisq.desktop.main.offer; import bisq.desktop.common.model.ActivatableDataModel; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import org.bitcoinj.core.Coin; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/createoffer/CreateOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/createoffer/CreateOfferViewModel.java index c0c2bc3d89..65e0d84dcc 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/createoffer/CreateOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/createoffer/CreateOfferViewModel.java @@ -27,7 +27,7 @@ import bisq.desktop.util.validation.FiatPriceValidator; import bisq.desktop.util.validation.FiatVolumeValidator; import bisq.desktop.util.validation.SecurityDepositValidator; -import bisq.core.btc.wallet.WalletsSetup; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.provider.price.PriceFeedService; import bisq.core.user.Preferences; import bisq.core.util.BSFormatter; 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 283f354d50..756af84354 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 @@ -57,6 +57,7 @@ import bisq.core.util.BSFormatter; import bisq.network.p2p.NodeAddress; +import bisq.common.app.DevEnv; import bisq.common.util.Tuple3; import org.bitcoinj.core.Coin; @@ -405,7 +406,7 @@ public class OfferBookView extends ActivatableViewAndModel().headLine(Res.get("offerbook.warning.noTradingAccountForCurrency.headline")) .instruction(Res.get("offerbook.warning.noTradingAccountForCurrency.msg")) .actionButtonText(Res.get("offerbook.yesCreateOffer")) @@ -727,6 +728,7 @@ public class OfferBookView extends ActivatableViewAndModel observable, Number oldValue, Number newValue) { if (offerBookListItem != null && offerBookListItem.getOffer().getVolume() != null) { + setText(""); setGraphic(new ColoredDecimalPlacesWithZerosText(model.getVolume(offerBookListItem), model.getNumberOfDecimalsForVolume(offerBookListItem))); model.priceFeedService.updateCounterProperty().removeListener(listener); 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 247c079cbd..1e5bccee9b 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 @@ -22,11 +22,11 @@ import bisq.desktop.main.overlays.popups.Popup; import bisq.core.app.BisqEnvironment; import bisq.core.arbitration.Arbitrator; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.Restrictions; import bisq.core.btc.listeners.BalanceListener; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.btc.wallet.Restrictions; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.filter.FilterManager; import bisq.core.locale.CurrencyUtil; @@ -218,7 +218,7 @@ class TakeOfferDataModel extends OfferDataModel { log.debug("We received the tx fee response after we have shown the funding screen and ignore that " + "to avoid that the total funds to pay changes due changed tx fees."); } - }, null); + }); calculateVolume(); calculateTotalToPay(); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java index ebd136793a..6dcc3419b7 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferView.java @@ -240,6 +240,12 @@ public class TakeOfferView extends ActivatableViewAndModel { public void hide() { if (gridPane != null) { - animateHide(() -> { - removeEffectFromBackground(); - - if (stage != null) - stage.hide(); - else - log.warn("Stage is null"); - - cleanup(); - onHidden(); - }); + animateHide(); } isDisplayed = false; } + protected void animateHide() { + animateHide(() -> { + removeEffectFromBackground(); + + if (stage != null) + stage.hide(); + else + log.warn("Stage is null"); + + cleanup(); + onHidden(); + }); + } + protected void onHidden() { } diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/editor/PeerInfoWithTagEditor.java b/desktop/src/main/java/bisq/desktop/main/overlays/editor/PeerInfoWithTagEditor.java index b26b47078a..743f837353 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/editor/PeerInfoWithTagEditor.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/editor/PeerInfoWithTagEditor.java @@ -154,17 +154,7 @@ public class PeerInfoWithTagEditor extends Overlay { @Override public void hide() { - animateHide(() -> { - removeEffectFromBackground(); - - if (stage != null) - stage.hide(); - else - log.warn("Stage is null"); - - cleanup(); - onHidden(); - }); + animateHide(); } @Override diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/notifications/Notification.java b/desktop/src/main/java/bisq/desktop/main/overlays/notifications/Notification.java index 2848b4a9e1..6cc4fa52a8 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/notifications/Notification.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/notifications/Notification.java @@ -37,6 +37,7 @@ import javafx.stage.Window; import javafx.scene.Camera; import javafx.scene.PerspectiveCamera; +import javafx.scene.input.MouseEvent; import javafx.scene.transform.Rotate; import javafx.geometry.Insets; @@ -62,23 +63,18 @@ public class Notification extends Overlay { public void onReadyForDisplay() { super.display(); + if (autoClose && autoCloseTimer == null) autoCloseTimer = UserThread.runAfter(this::doClose, 6); + + stage.addEventHandler(MouseEvent.MOUSE_PRESSED, (event) -> { + doClose(); + }); } @Override public void hide() { - animateHide(() -> { - removeEffectFromBackground(); - - if (stage != null) - stage.hide(); - else - log.warn("Stage is null"); - - cleanup(); - onHidden(); - }); + animateHide(); } @Override 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 1ad28bcde0..db66e4fdbd 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 @@ -27,8 +27,8 @@ import bisq.desktop.util.Layout; import bisq.core.arbitration.Dispute; import bisq.core.arbitration.DisputeManager; import bisq.core.arbitration.DisputeResult; -import bisq.core.btc.AddressEntry; import bisq.core.btc.exceptions.TransactionVerificationException; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.locale.Res; 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 a57890702c..477cde4107 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 @@ -24,11 +24,11 @@ import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.util.GUIUtil; import bisq.desktop.util.Transitions; -import bisq.core.btc.Restrictions; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.btc.wallet.Restrictions; import bisq.core.btc.wallet.WalletService; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.locale.Res; import bisq.core.offer.OpenOfferManager; import bisq.core.util.BSFormatter; diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ManualPayoutTxWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ManualPayoutTxWindow.java index ed67285e03..7b04e96327 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ManualPayoutTxWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ManualPayoutTxWindow.java @@ -23,11 +23,11 @@ import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.util.GUIUtil; import bisq.core.btc.exceptions.TransactionVerificationException; +import bisq.core.btc.exceptions.TxBroadcastException; import bisq.core.btc.exceptions.WalletException; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.TradeWalletService; -import bisq.core.btc.wallet.TxBroadcastException; import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.WalletsSetup; import bisq.network.p2p.P2PService; diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectDepositTxWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectDepositTxWindow.java index 5061eeca62..29e12fafe4 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectDepositTxWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SelectDepositTxWindow.java @@ -98,8 +98,7 @@ public class SelectDepositTxWindow extends Overlay { Tuple2> tuple = FormBuilder.addLabelComboBox(gridPane, ++rowIndex, Res.get("selectDepositTxWindow.select")); transactionsComboBox = tuple.second; - transactionsComboBox.setPromptText(Res.get("shared.select")); - transactionsComboBox.setConverter(new StringConverter() { + transactionsComboBox.setConverter(new StringConverter<>() { @Override public String toString(Transaction transaction) { return transaction.getHashAsString(); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/WalletPasswordWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/WalletPasswordWindow.java index 0604a11bc8..15a4e42b83 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/WalletPasswordWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/WalletPasswordWindow.java @@ -104,6 +104,7 @@ public class WalletPasswordWindow extends Overlay { private ChangeListener wordsTextAreaChangeListener; private ChangeListener seedWordsValidChangeListener; private LocalDate walletCreationDate; + private boolean hideForgotPasswordButton = false; /////////////////////////////////////////////////////////////////////////////////////////// @@ -151,6 +152,11 @@ public class WalletPasswordWindow extends Overlay { return this; } + public WalletPasswordWindow hideForgotPasswordButton() { + this.hideForgotPasswordButton = true; + return this; + } + @Override protected void cleanup() { if (passwordTextField != null) @@ -253,10 +259,12 @@ public class WalletPasswordWindow extends Overlay { GridPane.setRowIndex(hBox, ++rowIndex); GridPane.setColumnIndex(hBox, 1); hBox.setAlignment(Pos.CENTER_LEFT); - if (hideCloseButton) - hBox.getChildren().addAll(unlockButton, forgotPasswordButton, busyAnimation, deriveStatusLabel); - else - hBox.getChildren().addAll(unlockButton, cancelButton); + hBox.getChildren().add(unlockButton); + if (!hideForgotPasswordButton) + hBox.getChildren().add(forgotPasswordButton); + if (!hideCloseButton) + hBox.getChildren().add(cancelButton); + hBox.getChildren().addAll(busyAnimation, deriveStatusLabel); gridPane.getChildren().add(hBox); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferViewModel.java index 561e667d59..0d62d5249d 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/editoffer/EditOfferViewModel.java @@ -26,7 +26,7 @@ import bisq.desktop.util.validation.FiatPriceValidator; import bisq.desktop.util.validation.FiatVolumeValidator; import bisq.desktop.util.validation.SecurityDepositValidator; -import bisq.core.btc.wallet.WalletsSetup; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.offer.OpenOffer; import bisq.core.provider.price.PriceFeedService; import bisq.core.user.Preferences; 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 c8e5411ae9..579f28edf6 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 @@ -30,8 +30,8 @@ import bisq.desktop.util.GUIUtil; import bisq.core.arbitration.Dispute; import bisq.core.arbitration.DisputeAlreadyOpenException; import bisq.core.arbitration.DisputeManager; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.locale.Res; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; @@ -322,6 +322,14 @@ public class PendingTradesDataModel extends ActivatableDataModel { return null; } + @Nullable + public PaymentAccountPayload getBuyersPaymentAccountPayload() { + if (getTrade() != null && getTrade().getContract() != null) + return getTrade().getContract().getBuyerPaymentAccountPayload(); + else + return null; + } + public String getReference() { return getOffer() != null ? getOffer().getShortId() : ""; } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.java index dd744612cb..58bcd52f51 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.java @@ -79,7 +79,6 @@ public class PendingTradesView extends ActivatableViewAndModel tableView; @FXML TableColumn priceColumn, volumeColumn, amountColumn, avatarColumn, marketColumn, roleColumn, paymentMethodColumn, tradeIdColumn, dateColumn; - @FXML private SortedList sortedList; private TradeSubView selectedSubView; @@ -215,7 +214,7 @@ public class PendingTradesView extends ActivatableViewAndModel buyerState = new SimpleObjectProperty<>(); private final ObjectProperty sellerState = new SimpleObjectProperty<>(); @@ -112,7 +115,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel possiblePaymentAccounts = PaymentAccountUtil.getPossiblePaymentAccounts(offer, + model.getUser().getPaymentAccounts()); + PaymentAccountPayload buyersPaymentAccountPayload = model.dataModel.getBuyersPaymentAccountPayload(); + if (buyersPaymentAccountPayload != null && possiblePaymentAccounts.size() > 1) { + String id = buyersPaymentAccountPayload.getId(); + possiblePaymentAccounts.stream() + .filter(paymentAccount -> paymentAccount.getId().equals(id)) + .findFirst() + .ifPresent(paymentAccount -> { + String accountName = paymentAccount.getAccountName(); + FormBuilder.addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, + Res.getWithCol("portfolio.pending.step2_buyer.buyerAccount"), accountName); + }); + } + } + GridPane.setRowSpan(accountTitledGroupBg, gridRow - 3); Tuple3 tuple3 = FormBuilder.addButtonBusyAnimationLabelAfterGroup(gridPane, ++gridRow, 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 713a4f9767..0a1bcbf1d2 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 @@ -32,11 +32,11 @@ import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView; import bisq.desktop.util.FormBuilder; import bisq.desktop.util.Layout; -import bisq.core.btc.AddressEntry; -import bisq.core.btc.AddressEntryException; -import bisq.core.btc.InsufficientFundsException; -import bisq.core.btc.Restrictions; +import bisq.core.btc.exceptions.AddressEntryException; +import bisq.core.btc.exceptions.InsufficientFundsException; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BtcWalletService; +import bisq.core.btc.wallet.Restrictions; import bisq.core.locale.Res; import bisq.core.user.DontShowAgainLookup; import bisq.core.util.BSFormatter; 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 49371c6773..632314eb2d 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 @@ -309,8 +309,11 @@ public class SellerStep3View extends TradeStepView { String id = trade.getShortId(); if (paymentAccountPayload instanceof CryptoCurrencyAccountPayload) { String address = ((CryptoCurrencyAccountPayload) 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); //noinspection UnusedAssignment - message = Res.get("portfolio.pending.step3_seller.altcoin", part1, currencyName, address, tradeVolumeWithCode, currencyName); + message = Res.get("portfolio.pending.step3_seller.altcoin", part1, explorerOrWalletString, address, tradeVolumeWithCode, currencyName); } else { if (paymentAccountPayload instanceof USPostalMoneyOrderAccountPayload) { message = Res.get("portfolio.pending.step3_seller.postal", part1, tradeVolumeWithCode, id); diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java index 69e5a5b276..e921d4f7ed 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java @@ -30,8 +30,8 @@ import bisq.desktop.main.overlays.windows.TorNetworkSettingsWindow; import bisq.desktop.util.GUIUtil; import bisq.core.app.BisqEnvironment; -import bisq.core.btc.BitcoinNodes; -import bisq.core.btc.wallet.WalletsSetup; +import bisq.core.btc.nodes.BtcNodes; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.filter.Filter; import bisq.core.filter.FilterManager; import bisq.core.locale.Res; @@ -108,7 +108,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel btcNodesInputTextFieldFocusListener; private ToggleGroup bitcoinPeersToggleGroup; - private BitcoinNodes.BitcoinNodesOption selectedBitcoinNodesOption; + private BtcNodes.BitcoinNodesOption selectedBitcoinNodesOption; private ChangeListener bitcoinPeersToggleGroupListener; private ChangeListener btcNodesInputTextFieldListener; private ChangeListener filterPropertyListener; @@ -134,7 +134,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel { - selectedBitcoinNodesOption = (BitcoinNodes.BitcoinNodesOption) newValue.getUserData(); + selectedBitcoinNodesOption = (BtcNodes.BitcoinNodesOption) newValue.getUserData(); preferences.setBitcoinNodesOptionOrdinal(selectedBitcoinNodesOption.ordinal()); onBitcoinPeersToggleSelected(true); }; @@ -354,7 +354,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel { UserThread.runAfter(() -> { - selectedBitcoinNodesOption = BitcoinNodes.BitcoinNodesOption.PROVIDED; + selectedBitcoinNodesOption = BtcNodes.BitcoinNodesOption.PROVIDED; preferences.setBitcoinNodesOptionOrdinal(selectedBitcoinNodesOption.ordinal()); selectBitcoinPeersToggle(); onBitcoinPeersToggleSelected(false); @@ -396,13 +396,13 @@ public class NetworkSettingsView extends ActivatableViewAndModel userLanguageComboBox; private ComboBox userCountryComboBox; private ComboBox preferredTradeCurrencyComboBox; - // private ComboBox selectBaseCurrencyNetworkComboBox; + private ComboBox selectBaseCurrencyNetworkComboBox; private CheckBox useAnimationsCheckBox, autoSelectArbitratorsCheckBox, avoidStandbyModeCheckBox, showOwnOffersInOfferBook, sortMarketCurrenciesNumericallyCheckBox, useCustomFeeCheckbox; @@ -102,6 +103,8 @@ public class PreferencesView extends ActivatableViewAndModel fiatCurrenciesListView; @@ -129,12 +132,15 @@ public class PreferencesView extends ActivatableViewAndModeladdLabelComboBox(root, gridRow, Res.getWithCol("settings.preferences.selectCurrencyNetwork"), Layout.FIRST_ROW_DISTANCE).second; - selectBaseCurrencyNetworkComboBox.setConverter(new StringConverter() { + selectBaseCurrencyNetworkComboBox.setConverter(new StringConverter<>() { @Override public String toString(BaseCurrencyNetwork baseCurrencyNetwork) { - return DevEnv.isDevMode() ? (baseCurrencyNetwork.getCurrencyName() + "_" + baseCurrencyNetwork.getNetwork()) : - baseCurrencyNetwork.getCurrencyName(); + return baseCurrencyNetwork != null ? + (baseCurrencyNetwork.getCurrencyName() + "_" + baseCurrencyNetwork.getNetwork()) + : ""; } @Override public BaseCurrencyNetwork fromString(String string) { return null; } - });*/ + }); - userLanguageComboBox = FormBuilder.addLabelComboBox(root, gridRow, - Res.getWithCol("shared.language"), Layout.FIRST_ROW_DISTANCE).second; + userLanguageComboBox = FormBuilder.addLabelComboBox(root, ++gridRow, + Res.getWithCol("shared.language")).second; userCountryComboBox = FormBuilder.addLabelComboBox(root, ++gridRow, Res.getWithCol("shared.country")).second; blockChainExplorerComboBox = FormBuilder.addLabelComboBox(root, ++gridRow, @@ -298,7 +306,7 @@ public class PreferencesView extends ActivatableViewAndModel baseCurrencyNetworks = Arrays.asList(BaseCurrencyNetwork.values()); - // We don't support Dash anymore due lack of activity but leave it in the code in case it will get - // re-activated some day - // show ony mainnet in production version - if (!DevEnv.isDevMode()) - baseCurrencyNetworks = baseCurrencyNetworks.stream() - .filter(e -> !e.isDash()) - .filter(BaseCurrencyNetwork::isMainnet) - .collect(Collectors.toList()); - /* selectBaseCurrencyNetworkComboBox.setItems(FXCollections.observableArrayList(baseCurrencyNetworks)); + // We allow switching to testnet to make it easier for users to test the testnet DAO version + baseCurrencyNetworks = baseCurrencyNetworks.stream() + .filter(BaseCurrencyNetwork::isBitcoin) + .filter(e -> !e.isRegtest()) + .collect(Collectors.toList()); + selectBaseCurrencyNetworkComboBox.setItems(FXCollections.observableArrayList(baseCurrencyNetworks)); selectBaseCurrencyNetworkComboBox.setOnAction(e -> onSelectNetwork()); - selectBaseCurrencyNetworkComboBox.getSelectionModel().select(BisqEnvironment.getBaseCurrencyNetwork());*/ + selectBaseCurrencyNetworkComboBox.getSelectionModel().select(BisqEnvironment.getBaseCurrencyNetwork()); boolean useCustomWithdrawalTxFee = preferences.isUseCustomWithdrawalTxFee(); useCustomFeeCheckbox.setSelected(useCustomWithdrawalTxFee); @@ -658,29 +663,29 @@ public class PreferencesView extends ActivatableViewAndModel preferences.setUseStandbyMode(!avoidStandbyModeCheckBox.isSelected())); } - /* private void onSelectNetwork() { + private void onSelectNetwork() { if (selectBaseCurrencyNetworkComboBox.getSelectionModel().getSelectedItem() != BisqEnvironment.getBaseCurrencyNetwork()) selectNetwork(); - }*/ + } - /* private void selectNetwork() { + private void selectNetwork() { new Popup().warning(Res.get("settings.net.needRestart")) .onAction(() -> { bisqEnvironment.saveBaseCryptoNetwork(selectBaseCurrencyNetworkComboBox.getSelectionModel().getSelectedItem()); - UserThread.runAfter(BisqApp.getShutDownHandler()::run, 500, TimeUnit.MILLISECONDS); + UserThread.runAfter(BisqApp.getShutDownHandler(), 500, TimeUnit.MILLISECONDS); }) .actionButtonText(Res.get("shared.shutDown")) .closeButtonText(Res.get("shared.cancel")) .onClose(() -> selectBaseCurrencyNetworkComboBox.getSelectionModel().select(BisqEnvironment.getBaseCurrencyNetwork())) .show(); - }*/ + } /////////////////////////////////////////////////////////////////////////////////////////// // Deactivate /////////////////////////////////////////////////////////////////////////////////////////// private void deactivateGeneralOptions() { - // selectBaseCurrencyNetworkComboBox.setOnAction(null); + selectBaseCurrencyNetworkComboBox.setOnAction(null); userLanguageComboBox.setOnAction(null); userCountryComboBox.setOnAction(null); blockChainExplorerComboBox.setOnAction(null); diff --git a/desktop/src/main/java/bisq/desktop/util/FormBuilder.java b/desktop/src/main/java/bisq/desktop/util/FormBuilder.java index b8029287cc..55c18da2d0 100644 --- a/desktop/src/main/java/bisq/desktop/util/FormBuilder.java +++ b/desktop/src/main/java/bisq/desktop/util/FormBuilder.java @@ -771,6 +771,24 @@ public class FormBuilder { label = addLabel(gridPane, rowIndex, title, top); ComboBox comboBox = new JFXComboBox<>(); + // We want always the promptText + comboBox.setPromptText(Res.get("shared.select")); + + // Default ComboBox does not show promptText after clear selection. + // https://stackoverflow.com/questions/50569330/how-to-reset-combobox-and-display-prompttext?noredirect=1&lq=1 + comboBox.setButtonCell(new ListCell<>() { + @Override + protected void updateItem(T item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setText(Res.get("shared.select")); + } else { + setText(comboBox.getConverter().toString(item)); + } + } + }); + GridPane.setRowIndex(comboBox, rowIndex); GridPane.setColumnIndex(comboBox, 1); GridPane.setMargin(comboBox, new Insets(top, 0, 0, 0)); diff --git a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java index 4389d3583b..39a3760794 100644 --- a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java @@ -23,8 +23,8 @@ import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.desktop.main.overlays.popups.Popup; import bisq.core.app.BisqEnvironment; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.WalletsManager; -import bisq.core.btc.wallet.WalletsSetup; import bisq.core.locale.Country; import bisq.core.locale.CountryUtil; import bisq.core.locale.CurrencyUtil; @@ -120,6 +120,8 @@ import java.util.function.Consumer; import lombok.extern.slf4j.Slf4j; +import static com.google.common.base.Preconditions.checkArgument; + @Slf4j public class GUIUtil { public final static String SHOW_ALL_FLAG = "SHOW_ALL_FLAG"; @@ -653,9 +655,12 @@ public class GUIUtil { .show(); } - public static void setFitToRowsForTableView(TableView tableView, int rowHeight, int headerHeight, int minHeight) { + public static void setFitToRowsForTableView(TableView tableView, int rowHeight, int headerHeight, int minNumRows, int maxNumRows) { int size = tableView.getItems().size(); - int height = Math.max(minHeight, size * rowHeight + headerHeight); + int minHeight = rowHeight * minNumRows + headerHeight; + int maxHeight = rowHeight * maxNumRows + headerHeight; + checkArgument(maxHeight >= minHeight, "maxHeight cannot be smaller as minHeight"); + int height = Math.min(maxHeight, Math.max(minHeight, size * rowHeight + headerHeight)); tableView.setMaxHeight(-1); tableView.setMinHeight(-1); tableView.setMaxHeight(height); diff --git a/desktop/src/main/java/bisq/desktop/util/validation/BsqValidator.java b/desktop/src/main/java/bisq/desktop/util/validation/BsqValidator.java index 0bca05d284..6841fbbf62 100644 --- a/desktop/src/main/java/bisq/desktop/util/validation/BsqValidator.java +++ b/desktop/src/main/java/bisq/desktop/util/validation/BsqValidator.java @@ -17,7 +17,7 @@ package bisq.desktop.util.validation; -import bisq.core.btc.Restrictions; +import bisq.core.btc.wallet.Restrictions; import bisq.core.locale.Res; import bisq.core.util.BsqFormatter; import bisq.core.util.CoinUtil; diff --git a/desktop/src/main/java/bisq/desktop/util/validation/BtcValidator.java b/desktop/src/main/java/bisq/desktop/util/validation/BtcValidator.java index 0e7adc628b..6a09c74e7e 100644 --- a/desktop/src/main/java/bisq/desktop/util/validation/BtcValidator.java +++ b/desktop/src/main/java/bisq/desktop/util/validation/BtcValidator.java @@ -17,7 +17,7 @@ package bisq.desktop.util.validation; -import bisq.core.btc.Restrictions; +import bisq.core.btc.wallet.Restrictions; import bisq.core.locale.Res; import bisq.core.util.BSFormatter; 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 d011e0f43a..2ca19b0673 100644 --- a/desktop/src/main/java/bisq/desktop/util/validation/SecurityDepositValidator.java +++ b/desktop/src/main/java/bisq/desktop/util/validation/SecurityDepositValidator.java @@ -17,7 +17,7 @@ package bisq.desktop.util.validation; -import bisq.core.btc.Restrictions; +import bisq.core.btc.wallet.Restrictions; import bisq.core.locale.Res; import bisq.core.util.BSFormatter; diff --git a/desktop/src/main/resources/logback.xml b/desktop/src/main/resources/logback.xml index 487ecd4b00..8812d310de 100644 --- a/desktop/src/main/resources/logback.xml +++ b/desktop/src/main/resources/logback.xml @@ -2,7 +2,7 @@ - %highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n) + %highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30}: %msg %xEx%n) @@ -11,4 +11,7 @@ + + diff --git a/desktop/src/test/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModelTest.java b/desktop/src/test/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModelTest.java index cec316e70f..a13b1ce946 100644 --- a/desktop/src/test/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModelTest.java @@ -38,10 +38,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static bisq.core.locale.TradeCurrencyMakers.usd; -import static bisq.core.user.PreferenceMakers.empty; import static bisq.desktop.main.offer.offerbook.OfferBookListItemMaker.btcBuyItem; import static bisq.desktop.main.offer.offerbook.OfferBookListItemMaker.btcSellItem; +import static bisq.desktop.maker.PreferenceMakers.empty; +import static bisq.desktop.maker.TradeCurrencyMakers.usd; import static com.natpryce.makeiteasy.MakeItEasy.make; import static com.natpryce.makeiteasy.MakeItEasy.with; import static org.junit.Assert.assertEquals; diff --git a/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferViewModelTest.java b/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferViewModelTest.java index 61d1007423..8ee590cf89 100644 --- a/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferViewModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/offer/createoffer/CreateOfferViewModelTest.java @@ -22,7 +22,7 @@ import bisq.desktop.util.validation.BtcValidator; import bisq.desktop.util.validation.FiatPriceValidator; import bisq.desktop.util.validation.SecurityDepositValidator; -import bisq.core.btc.AddressEntry; +import bisq.core.btc.model.AddressEntry; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.locale.CryptoCurrency; @@ -54,7 +54,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static bisq.core.user.PreferenceMakers.empty; +import static bisq.desktop.maker.PreferenceMakers.empty; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; diff --git a/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookListItemMaker.java b/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookListItemMaker.java index fc3a481e02..e6e7790a64 100644 --- a/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookListItemMaker.java +++ b/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookListItemMaker.java @@ -17,7 +17,8 @@ package bisq.desktop.main.offer.offerbook; -import bisq.core.offer.OfferMaker; +import bisq.desktop.maker.OfferMaker; + import bisq.core.offer.OfferPayload; import com.natpryce.makeiteasy.Instantiator; @@ -25,7 +26,7 @@ import com.natpryce.makeiteasy.MakeItEasy; import com.natpryce.makeiteasy.Maker; import com.natpryce.makeiteasy.Property; -import static bisq.core.offer.OfferMaker.btcUsdOffer; +import static bisq.desktop.maker.OfferMaker.btcUsdOffer; import static com.natpryce.makeiteasy.MakeItEasy.a; import static com.natpryce.makeiteasy.MakeItEasy.make; import static com.natpryce.makeiteasy.MakeItEasy.with; diff --git a/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java b/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java index d92f87c1fe..89c2b748a4 100644 --- a/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/offer/offerbook/OfferBookViewModelTest.java @@ -69,9 +69,9 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import static bisq.core.locale.TradeCurrencyMakers.usd; -import static bisq.core.user.PreferenceMakers.empty; import static bisq.desktop.main.offer.offerbook.OfferBookListItemMaker.*; +import static bisq.desktop.maker.PreferenceMakers.empty; +import static bisq.desktop.maker.TradeCurrencyMakers.usd; import static com.natpryce.makeiteasy.MakeItEasy.make; import static com.natpryce.makeiteasy.MakeItEasy.with; import static org.junit.Assert.assertEquals; diff --git a/desktop/src/test/java/bisq/desktop/main/settings/preferences/PreferencesViewModelTest.java b/desktop/src/test/java/bisq/desktop/main/settings/preferences/PreferencesViewModelTest.java index 42b368b145..515b88190f 100644 --- a/desktop/src/test/java/bisq/desktop/main/settings/preferences/PreferencesViewModelTest.java +++ b/desktop/src/test/java/bisq/desktop/main/settings/preferences/PreferencesViewModelTest.java @@ -17,9 +17,10 @@ package bisq.desktop.main.settings.preferences; +import bisq.desktop.maker.PreferenceMakers; + import bisq.core.arbitration.Arbitrator; import bisq.core.arbitration.ArbitratorManager; -import bisq.core.user.PreferenceMakers; import bisq.core.user.Preferences; import bisq.network.p2p.NodeAddress; diff --git a/desktop/src/test/java/bisq/desktop/util/CurrencyListItemMakers.java b/desktop/src/test/java/bisq/desktop/maker/CurrencyListItemMakers.java similarity index 82% rename from desktop/src/test/java/bisq/desktop/util/CurrencyListItemMakers.java rename to desktop/src/test/java/bisq/desktop/maker/CurrencyListItemMakers.java index afcd83a0cc..f85acc19bc 100644 --- a/desktop/src/test/java/bisq/desktop/util/CurrencyListItemMakers.java +++ b/desktop/src/test/java/bisq/desktop/maker/CurrencyListItemMakers.java @@ -15,7 +15,9 @@ * along with Bisq. If not, see . */ -package bisq.desktop.util; +package bisq.desktop.maker; + +import bisq.desktop.util.CurrencyListItem; import bisq.core.locale.TradeCurrency; @@ -23,14 +25,14 @@ import com.natpryce.makeiteasy.Instantiator; import com.natpryce.makeiteasy.Maker; import com.natpryce.makeiteasy.Property; -import static bisq.core.locale.TradeCurrencyMakers.bitcoin; -import static bisq.core.locale.TradeCurrencyMakers.euro; +import static bisq.desktop.maker.TradeCurrencyMakers.bitcoin; +import static bisq.desktop.maker.TradeCurrencyMakers.euro; import static com.natpryce.makeiteasy.MakeItEasy.a; import static com.natpryce.makeiteasy.MakeItEasy.with; public class CurrencyListItemMakers { - public static final Property tradeCurrency = new Property<>(); + public static final Property tradeCurrency = new Property<>(); public static final Property numberOfTrades = new Property<>(); public static final Instantiator CurrencyListItem = lookup -> diff --git a/desktop/src/test/java/bisq/core/offer/OfferMaker.java b/desktop/src/test/java/bisq/desktop/maker/OfferMaker.java similarity index 96% rename from desktop/src/test/java/bisq/core/offer/OfferMaker.java rename to desktop/src/test/java/bisq/desktop/maker/OfferMaker.java index aa9d294c2e..e620d1ba92 100644 --- a/desktop/src/test/java/bisq/core/offer/OfferMaker.java +++ b/desktop/src/test/java/bisq/desktop/maker/OfferMaker.java @@ -15,7 +15,10 @@ * along with Bisq. If not, see . */ -package bisq.core.offer; +package bisq.desktop.maker; + +import bisq.core.offer.Offer; +import bisq.core.offer.OfferPayload; import com.natpryce.makeiteasy.Instantiator; import com.natpryce.makeiteasy.Maker; diff --git a/desktop/src/test/java/bisq/core/user/PreferenceMakers.java b/desktop/src/test/java/bisq/desktop/maker/PreferenceMakers.java similarity index 92% rename from desktop/src/test/java/bisq/core/user/PreferenceMakers.java rename to desktop/src/test/java/bisq/desktop/maker/PreferenceMakers.java index cbaad42cb0..22b3340645 100644 --- a/desktop/src/test/java/bisq/core/user/PreferenceMakers.java +++ b/desktop/src/test/java/bisq/desktop/maker/PreferenceMakers.java @@ -15,9 +15,10 @@ * along with Bisq. If not, see . */ -package bisq.core.user; +package bisq.desktop.maker; import bisq.core.app.BisqEnvironment; +import bisq.core.user.Preferences; import bisq.common.storage.Storage; @@ -30,7 +31,7 @@ import static com.natpryce.makeiteasy.MakeItEasy.make; public class PreferenceMakers { - public static final Property storage = new Property<>(); + public static final Property storage = new Property<>(); public static final Property bisqEnvironment = new Property<>(); public static final Property btcNodesFromOptions = new Property<>(); public static final Property useTorFlagFromOptions = new Property<>(); diff --git a/desktop/src/test/java/bisq/core/monetary/PriceMaker.java b/desktop/src/test/java/bisq/desktop/maker/PriceMaker.java similarity index 94% rename from desktop/src/test/java/bisq/core/monetary/PriceMaker.java rename to desktop/src/test/java/bisq/desktop/maker/PriceMaker.java index 765282aaeb..e96f7c40d2 100644 --- a/desktop/src/test/java/bisq/core/monetary/PriceMaker.java +++ b/desktop/src/test/java/bisq/desktop/maker/PriceMaker.java @@ -15,7 +15,10 @@ * along with Bisq. If not, see . */ -package bisq.core.monetary; +package bisq.desktop.maker; + +import bisq.core.monetary.Altcoin; +import bisq.core.monetary.Price; import org.bitcoinj.utils.Fiat; diff --git a/desktop/src/test/java/bisq/core/locale/TradeCurrencyMakers.java b/desktop/src/test/java/bisq/desktop/maker/TradeCurrencyMakers.java similarity index 82% rename from desktop/src/test/java/bisq/core/locale/TradeCurrencyMakers.java rename to desktop/src/test/java/bisq/desktop/maker/TradeCurrencyMakers.java index 425f148523..cdd4d3cf4f 100644 --- a/desktop/src/test/java/bisq/core/locale/TradeCurrencyMakers.java +++ b/desktop/src/test/java/bisq/desktop/maker/TradeCurrencyMakers.java @@ -15,7 +15,11 @@ * along with Bisq. If not, see . */ -package bisq.core.locale; +package bisq.desktop.maker; + +import bisq.core.locale.CryptoCurrency; +import bisq.core.locale.FiatCurrency; +import bisq.core.locale.TradeCurrency; import com.natpryce.makeiteasy.Instantiator; import com.natpryce.makeiteasy.Property; @@ -29,10 +33,10 @@ public class TradeCurrencyMakers { public static final Property currencyCode = new Property<>(); public static final Property currencyName = new Property<>(); - public static final Instantiator CryptoCurrency = lookup -> + public static final Instantiator CryptoCurrency = lookup -> new CryptoCurrency(lookup.valueOf(currencyCode, "BTC"), lookup.valueOf(currencyName, "Bitcoin")); - public static final Instantiator FiatCurrency = lookup -> + public static final Instantiator FiatCurrency = lookup -> new FiatCurrency(lookup.valueOf(currencyCode, "EUR")); public static final CryptoCurrency bitcoin = make(a(CryptoCurrency)); diff --git a/desktop/src/test/java/bisq/core/monetary/VolumeMaker.java b/desktop/src/test/java/bisq/desktop/maker/VolumeMaker.java similarity index 94% rename from desktop/src/test/java/bisq/core/monetary/VolumeMaker.java rename to desktop/src/test/java/bisq/desktop/maker/VolumeMaker.java index 9ac73504c9..d52d128b3a 100644 --- a/desktop/src/test/java/bisq/core/monetary/VolumeMaker.java +++ b/desktop/src/test/java/bisq/desktop/maker/VolumeMaker.java @@ -15,7 +15,10 @@ * along with Bisq. If not, see . */ -package bisq.core.monetary; +package bisq.desktop.maker; + +import bisq.core.monetary.Altcoin; +import bisq.core.monetary.Volume; import org.bitcoinj.utils.Fiat; diff --git a/desktop/src/test/java/bisq/desktop/util/BSFormatterTest.java b/desktop/src/test/java/bisq/desktop/util/BSFormatterTest.java index 292435e4eb..d9bbf5b9dd 100644 --- a/desktop/src/test/java/bisq/desktop/util/BSFormatterTest.java +++ b/desktop/src/test/java/bisq/desktop/util/BSFormatterTest.java @@ -37,11 +37,11 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static bisq.core.monetary.PriceMaker.priceString; -import static bisq.core.monetary.PriceMaker.usdPrice; -import static bisq.core.monetary.VolumeMaker.usdVolume; -import static bisq.core.monetary.VolumeMaker.volumeString; -import static bisq.core.offer.OfferMaker.btcUsdOffer; +import static bisq.desktop.maker.OfferMaker.btcUsdOffer; +import static bisq.desktop.maker.PriceMaker.priceString; +import static bisq.desktop.maker.PriceMaker.usdPrice; +import static bisq.desktop.maker.VolumeMaker.usdVolume; +import static bisq.desktop.maker.VolumeMaker.volumeString; import static com.natpryce.makeiteasy.MakeItEasy.a; import static com.natpryce.makeiteasy.MakeItEasy.make; import static com.natpryce.makeiteasy.MakeItEasy.with; diff --git a/desktop/src/test/java/bisq/desktop/util/GUIUtilTest.java b/desktop/src/test/java/bisq/desktop/util/GUIUtilTest.java index 0525023158..b7073cb791 100644 --- a/desktop/src/test/java/bisq/desktop/util/GUIUtilTest.java +++ b/desktop/src/test/java/bisq/desktop/util/GUIUtilTest.java @@ -30,12 +30,12 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; -import static bisq.core.locale.TradeCurrencyMakers.bitcoin; -import static bisq.core.locale.TradeCurrencyMakers.euro; -import static bisq.core.user.PreferenceMakers.empty; -import static bisq.desktop.util.CurrencyListItemMakers.bitcoinItem; -import static bisq.desktop.util.CurrencyListItemMakers.euroItem; -import static bisq.desktop.util.CurrencyListItemMakers.numberOfTrades; +import static bisq.desktop.maker.CurrencyListItemMakers.bitcoinItem; +import static bisq.desktop.maker.CurrencyListItemMakers.euroItem; +import static bisq.desktop.maker.CurrencyListItemMakers.numberOfTrades; +import static bisq.desktop.maker.PreferenceMakers.empty; +import static bisq.desktop.maker.TradeCurrencyMakers.bitcoin; +import static bisq.desktop.maker.TradeCurrencyMakers.euro; import static com.natpryce.makeiteasy.MakeItEasy.make; import static com.natpryce.makeiteasy.MakeItEasy.with; import static org.junit.Assert.assertEquals; diff --git a/monitor/src/main/java/bisq/monitor/MonitorAppSetup.java b/monitor/src/main/java/bisq/monitor/MonitorAppSetup.java index 947bf554c7..6a6bfa66c9 100644 --- a/monitor/src/main/java/bisq/monitor/MonitorAppSetup.java +++ b/monitor/src/main/java/bisq/monitor/MonitorAppSetup.java @@ -21,7 +21,7 @@ import bisq.monitor.metrics.p2p.MonitorP2PService; import bisq.core.app.BisqEnvironment; import bisq.core.app.SetupUtils; -import bisq.core.btc.wallet.WalletsSetup; +import bisq.core.btc.setup.WalletsSetup; import bisq.network.crypto.EncryptionService; import bisq.network.p2p.network.SetupListener; diff --git a/monitor/src/main/java/bisq/monitor/metrics/MetricsModel.java b/monitor/src/main/java/bisq/monitor/metrics/MetricsModel.java index ddac2e4243..0bc882021c 100644 --- a/monitor/src/main/java/bisq/monitor/metrics/MetricsModel.java +++ b/monitor/src/main/java/bisq/monitor/metrics/MetricsModel.java @@ -19,8 +19,8 @@ package bisq.monitor.metrics; import bisq.monitor.MonitorOptionKeys; -import bisq.core.btc.BitcoinNodes; -import bisq.core.btc.wallet.WalletsSetup; +import bisq.core.btc.nodes.BtcNodes; +import bisq.core.btc.setup.WalletsSetup; import bisq.core.locale.Res; import bisq.network.p2p.NodeAddress; @@ -72,27 +72,27 @@ public class MetricsModel { private String resultAsHtml; private SeedNodeRepository seedNodeRepository; private SlackApi slackSeedApi, slackBtcApi, slackProviderApi; - private BitcoinNodes bitcoinNodes; + private BtcNodes btcNodes; @Setter private long lastCheckTs; private long btcNodeUptimeTs; private int totalErrors = 0; private HashMap map = new HashMap<>(); private List connectedPeers; - private Map, Integer> btcNodeDownTimeMap = new HashMap<>(); - private Map, Integer> btcNodeUpTimeMap = new HashMap<>(); + private Map, Integer> btcNodeDownTimeMap = new HashMap<>(); + private Map, Integer> btcNodeUpTimeMap = new HashMap<>(); @Getter private Set nodesInError = new HashSet<>(); @Inject public MetricsModel(SeedNodeRepository seedNodeRepository, - BitcoinNodes bitcoinNodes, + BtcNodes btcNodes, WalletsSetup walletsSetup, @Named(MonitorOptionKeys.SLACK_URL_SEED_CHANNEL) String slackUrlSeedChannel, @Named(MonitorOptionKeys.SLACK_BTC_SEED_CHANNEL) String slackUrlBtcChannel, @Named(MonitorOptionKeys.SLACK_PROVIDER_SEED_CHANNEL) String slackUrlProviderChannel) { this.seedNodeRepository = seedNodeRepository; - this.bitcoinNodes = bitcoinNodes; + this.btcNodes = btcNodes; if (!slackUrlSeedChannel.isEmpty()) slackSeedApi = new SlackApi(slackUrlSeedChannel); if (!slackUrlBtcChannel.isEmpty()) @@ -284,8 +284,8 @@ public class MetricsModel { }) .collect(Collectors.toSet()); - List onionBtcNodes = new ArrayList<>(bitcoinNodes.getProvidedBtcNodes().stream() - .filter(BitcoinNodes.BtcNode::hasOnionAddress) + List onionBtcNodes = new ArrayList<>(btcNodes.getProvidedBtcNodes().stream() + .filter(BtcNodes.BtcNode::hasOnionAddress) .collect(Collectors.toSet())); onionBtcNodes.sort((o1, o2) -> o1.getOperator() != null && o2.getOperator() != null ? o1.getOperator().compareTo(o2.getOperator()) : 0); @@ -294,8 +294,8 @@ public class MetricsModel { printTable(html, sb, onionBtcNodes, connectedBtcPeers, elapsed, true); html.append(""); - List clearNetNodes = new ArrayList<>(bitcoinNodes.getProvidedBtcNodes().stream() - .filter(BitcoinNodes.BtcNode::hasClearNetAddress) + List clearNetNodes = new ArrayList<>(btcNodes.getProvidedBtcNodes().stream() + .filter(BtcNodes.BtcNode::hasClearNetAddress) .collect(Collectors.toSet())); clearNetNodes.sort((o1, o2) -> o1.getOperator() != null && o2.getOperator() != null ? o1.getOperator().compareTo(o2.getOperator()) : 0); @@ -325,11 +325,11 @@ public class MetricsModel { ""); } - private void printTable(StringBuilder html, StringBuilder sb, List allBtcNodes, Set connectedBtcPeers, long elapsed, boolean isOnion) { + private void printTable(StringBuilder html, StringBuilder sb, List allBtcNodes, Set connectedBtcPeers, long elapsed, boolean isOnion) { allBtcNodes.stream().forEach(node -> { int upTime = 0; int downTime = 0; - Tuple2 key = new Tuple2<>(node, isOnion); + Tuple2 key = new Tuple2<>(node, isOnion); if (btcNodeUpTimeMap.containsKey(key)) upTime = btcNodeUpTimeMap.get(key); diff --git a/p2p/src/main/java/bisq/network/Socks5SeedOnionDiscovery.java b/p2p/src/main/java/bisq/network/Socks5SeedOnionDiscovery.java index b9c0f9f2d9..0cbbaaefde 100644 --- a/p2p/src/main/java/bisq/network/Socks5SeedOnionDiscovery.java +++ b/p2p/src/main/java/bisq/network/Socks5SeedOnionDiscovery.java @@ -17,8 +17,6 @@ package bisq.network; -import org.libdohj.params.AbstractLitecoinParams; - import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.net.discovery.PeerDiscovery; import org.bitcoinj.net.discovery.PeerDiscoveryException; @@ -53,12 +51,6 @@ public class Socks5SeedOnionDiscovery implements PeerDiscovery { case NetworkParameters.ID_TESTNET: seedAddresses = testNet3Seeds(); break; - case AbstractLitecoinParams.ID_LITE_MAINNET: - seedAddresses = LitecoinMainNetSeeds(); - break; - case AbstractLitecoinParams.ID_LITE_TESTNET: - seedAddresses = LitecoinTestNet4Seeds(); - break; } this.seedAddrs = convertAddrsString(seedAddresses, params.getPort()); diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index b0d7f26d9f..f1fd28a016 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -197,19 +197,12 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis public void onAllServicesInitialized() { Log.traceCall(); if (networkNode.getNodeAddress() != null) { - p2PDataStorage.getMap().values().stream().forEach(protectedStorageEntry -> { - if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry) - processProtectedMailboxStorageEntry((ProtectedMailboxStorageEntry) protectedStorageEntry); - }); + maybeProcessAllMailboxEntries(); } else { // If our HS is still not published networkNode.nodeAddressProperty().addListener((observable, oldValue, newValue) -> { - if (newValue != null) { - p2PDataStorage.getMap().values().stream().forEach(protectedStorageEntry -> { - if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry) - processProtectedMailboxStorageEntry((ProtectedMailboxStorageEntry) protectedStorageEntry); - }); - } + if (newValue != null) + maybeProcessAllMailboxEntries(); }); } } @@ -291,6 +284,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis if (!seedNodesAvailable) { isBootstrapped = true; + maybeProcessAllMailboxEntries(); p2pServiceListeners.stream().forEach(P2PServiceListener::onNoSeedNodeAvailable); } } @@ -354,6 +348,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis public void onUpdatedDataReceived() { if (!isBootstrapped) { isBootstrapped = true; + maybeProcessAllMailboxEntries(); p2pServiceListeners.stream().forEach(P2PServiceListener::onUpdatedDataReceived); p2PDataStorage.onBootstrapComplete(); } @@ -449,7 +444,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis @Override public void onAdded(ProtectedStorageEntry protectedStorageEntry) { if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry) - processProtectedMailboxStorageEntry((ProtectedMailboxStorageEntry) protectedStorageEntry); + processMailboxEntry((ProtectedMailboxStorageEntry) protectedStorageEntry); } @Override @@ -523,9 +518,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis // MailboxMessages /////////////////////////////////////////////////////////////////////////////////////////// - private void processProtectedMailboxStorageEntry(ProtectedMailboxStorageEntry protectedMailboxStorageEntry) { - Log.traceCall(); - final NodeAddress nodeAddress = networkNode.getNodeAddress(); + private void processMailboxEntry(ProtectedMailboxStorageEntry protectedMailboxStorageEntry) { + NodeAddress nodeAddress = networkNode.getNodeAddress(); // Seed nodes don't receive mailbox network_messages if (nodeAddress != null && !seedNodeRepository.isSeedNode(nodeAddress)) { Log.traceCall(); @@ -541,8 +535,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis checkNotNull(senderNodeAddress, "senderAddress must not be null for mailbox network_messages"); mailboxMap.put(mailboxMessage.getUid(), protectedMailboxStorageEntry); - log.trace("Decryption of SealedAndSignedMessage succeeded. senderAddress=" - + senderNodeAddress + " / my address=" + getAddress()); + log.info("Received a {} mailbox message with messageUid {} and senderAddress {}", mailboxMessage.getClass().getSimpleName(), mailboxMessage.getUid(), senderNodeAddress); decryptedMailboxListeners.forEach( e -> e.onMailboxMessageAdded(decryptedMessageWithPubKey, senderNodeAddress)); } else { @@ -660,6 +653,14 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis } + private void maybeProcessAllMailboxEntries() { + if (isBootstrapped) { + p2PDataStorage.getMap().values().forEach(protectedStorageEntry -> { + if (protectedStorageEntry instanceof ProtectedMailboxStorageEntry) + processMailboxEntry((ProtectedMailboxStorageEntry) protectedStorageEntry); + }); + } + } private void addMailboxData(MailboxStoragePayload expirableMailboxStoragePayload, PublicKey receiversPublicKey, @@ -755,36 +756,37 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis private void delayedRemoveEntryFromMailbox(DecryptedMessageWithPubKey decryptedMessageWithPubKey) { Log.traceCall(); - if (isBootstrapped()) { - MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.getNetworkEnvelope(); - String uid = mailboxMessage.getUid(); - if (mailboxMap.containsKey(uid)) { - ProtectedMailboxStorageEntry mailboxData = mailboxMap.get(uid); - if (mailboxData != null && mailboxData.getProtectedStoragePayload() instanceof MailboxStoragePayload) { - MailboxStoragePayload expirableMailboxStoragePayload = (MailboxStoragePayload) mailboxData.getProtectedStoragePayload(); - PublicKey receiversPubKey = mailboxData.getReceiversPubKey(); - checkArgument(receiversPubKey.equals(keyRing.getSignatureKeyPair().getPublic()), - "receiversPubKey is not matching with our key. That must not happen."); - try { - ProtectedMailboxStorageEntry protectedMailboxStorageEntry = p2PDataStorage.getMailboxDataWithSignedSeqNr( - expirableMailboxStoragePayload, - keyRing.getSignatureKeyPair(), - receiversPubKey); - p2PDataStorage.removeMailboxData(protectedMailboxStorageEntry, networkNode.getNodeAddress(), true); - } catch (CryptoException e) { - log.error("Signing at getDataWithSignedSeqNr failed. That should never happen."); - } - - mailboxMap.remove(uid); - log.info("Removed successfully decryptedMsgWithPubKey. uid={}", uid); - } - } else { - log.warn("uid for mailbox entry not found in mailboxMap." + "uid={}", uid); - } - } else { - throw new NetworkNotReadyException(); + if (!isBootstrapped()) { + // We don't throw an NetworkNotReadyException here. + // This case should not happen anyway as we check for isBootstrapped in the callers. + log.warn("You must have bootstrapped before adding data to the P2P network."); } + MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.getNetworkEnvelope(); + String uid = mailboxMessage.getUid(); + if (mailboxMap.containsKey(uid)) { + ProtectedMailboxStorageEntry mailboxData = mailboxMap.get(uid); + if (mailboxData != null && mailboxData.getProtectedStoragePayload() instanceof MailboxStoragePayload) { + MailboxStoragePayload expirableMailboxStoragePayload = (MailboxStoragePayload) mailboxData.getProtectedStoragePayload(); + PublicKey receiversPubKey = mailboxData.getReceiversPubKey(); + checkArgument(receiversPubKey.equals(keyRing.getSignatureKeyPair().getPublic()), + "receiversPubKey is not matching with our key. That must not happen."); + try { + ProtectedMailboxStorageEntry protectedMailboxStorageEntry = p2PDataStorage.getMailboxDataWithSignedSeqNr( + expirableMailboxStoragePayload, + keyRing.getSignatureKeyPair(), + receiversPubKey); + p2PDataStorage.removeMailboxData(protectedMailboxStorageEntry, networkNode.getNodeAddress(), true); + } catch (CryptoException e) { + log.error("Signing at getDataWithSignedSeqNr failed. That should never happen."); + } + + mailboxMap.remove(uid); + log.info("Removed successfully decryptedMsgWithPubKey. uid={}", uid); + } + } else { + log.warn("uid for mailbox entry not found in mailboxMap." + "uid={}", uid); + } } 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 dbdf7dbce3..b6d211378c 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -178,8 +178,19 @@ public class PeerManager implements ConnectionListener, PersistedDataHost { @Override public void readPersisted() { PeerList persistedPeerList = storage.initAndGetPersistedWithFileName("PeerList", 1000); - if (persistedPeerList != null) + if (persistedPeerList != null) { + long peesWithNoCapabilitiesSet = persistedPeerList.getList().stream() + .filter(e -> e.getSupportedCapabilities().isEmpty()) + .mapToInt(e -> 1) + .count(); + if (peesWithNoCapabilitiesSet > 100) { + log.warn("peesWithNoCapabilitiesSet={}, persistedPeerList.size()={}", peesWithNoCapabilitiesSet, persistedPeerList.size()); + } else { + log.info("peesWithNoCapabilitiesSet={}, persistedPeerList.size()={}", peesWithNoCapabilitiesSet, persistedPeerList.size()); + } + this.persistedPeers.addAll(persistedPeerList.getList()); + } } public int getMaxConnections() { @@ -648,7 +659,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost { .filter(peer -> peer.getDate().getTime() > maxAge) .collect(Collectors.toSet()); if (oldNumLatestLivePeers != latestLivePeers.size()) - log.info("Num of latestLivePeers={}, latestLivePeers={}", latestLivePeers.size(), latestLivePeers); + log.info("Num of latestLivePeers={}", latestLivePeers.size()); return latestLivePeers; } 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 bff3cb72b6..5459d10ea0 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 @@ -64,9 +64,6 @@ public final class Peer implements NetworkPayload, PersistablePayload, Supported this.nodeAddress = nodeAddress; this.date = date; this.supportedCapabilities = supportedCapabilities; - - if (supportedCapabilities.isEmpty()) - log.warn("SupportedCapabilities is empty. nodeAddress={}", nodeAddress); } @Override diff --git a/p2p/src/main/java/bisq/network/p2p/storage/persistence/StoreService.java b/p2p/src/main/java/bisq/network/p2p/storage/persistence/StoreService.java index c39034bccf..73fc72ecec 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/persistence/StoreService.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/persistence/StoreService.java @@ -139,9 +139,9 @@ public abstract class StoreService