diff --git a/assets/src/main/java/bisq/asset/Base58BitcoinAddressValidator.java b/assets/src/main/java/bisq/asset/Base58BitcoinAddressValidator.java index 9a99385f60..181f4011ba 100644 --- a/assets/src/main/java/bisq/asset/Base58BitcoinAddressValidator.java +++ b/assets/src/main/java/bisq/asset/Base58BitcoinAddressValidator.java @@ -17,8 +17,8 @@ package bisq.asset; -import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.params.MainNetParams; @@ -27,7 +27,7 @@ import org.bitcoinj.params.MainNetParams; * * @author Chris Beams * @since 0.7.0 - * @see org.bitcoinj.core.Address#fromBase58(NetworkParameters, String) + * @see org.bitcoinj.core.LegacyAddress#fromBase58(NetworkParameters, String) */ public class Base58BitcoinAddressValidator implements AddressValidator { @@ -44,7 +44,7 @@ public class Base58BitcoinAddressValidator implements AddressValidator { @Override public AddressValidationResult validate(String address) { try { - Address.fromBase58(networkParameters, address); + LegacyAddress.fromBase58(networkParameters, address); } catch (AddressFormatException ex) { return AddressValidationResult.invalidAddress(ex); } diff --git a/assets/src/main/java/bisq/asset/coins/Actinium.java b/assets/src/main/java/bisq/asset/coins/Actinium.java index d0bfc49b5d..45f0941527 100644 --- a/assets/src/main/java/bisq/asset/coins/Actinium.java +++ b/assets/src/main/java/bisq/asset/coins/Actinium.java @@ -33,7 +33,6 @@ public class Actinium extends Coin { public ActiniumParams() { addressHeader = 53; p2shHeader = 55; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Adeptio.java b/assets/src/main/java/bisq/asset/coins/Adeptio.java index ecfd4b3411..ae10025ecc 100644 --- a/assets/src/main/java/bisq/asset/coins/Adeptio.java +++ b/assets/src/main/java/bisq/asset/coins/Adeptio.java @@ -50,7 +50,6 @@ public class Adeptio extends Coin { public AdeptioParams() { addressHeader = 23; p2shHeader = 16; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Animecoin.java b/assets/src/main/java/bisq/asset/coins/Animecoin.java index 7ddbe6db36..db72f9b4b5 100644 --- a/assets/src/main/java/bisq/asset/coins/Animecoin.java +++ b/assets/src/main/java/bisq/asset/coins/Animecoin.java @@ -30,7 +30,6 @@ public class Animecoin extends Coin { public AnimecoinMainNetParams() { this.addressHeader = 23; this.p2shHeader = 9; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Australiacash.java b/assets/src/main/java/bisq/asset/coins/Australiacash.java index d0dea1b7d8..f120ed5e79 100644 --- a/assets/src/main/java/bisq/asset/coins/Australiacash.java +++ b/assets/src/main/java/bisq/asset/coins/Australiacash.java @@ -30,7 +30,6 @@ public class Australiacash extends Coin { public AustraliacashParams() { addressHeader = 23; p2shHeader = 5; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/BitcoinRhodium.java b/assets/src/main/java/bisq/asset/coins/BitcoinRhodium.java index befb60f205..f827636162 100644 --- a/assets/src/main/java/bisq/asset/coins/BitcoinRhodium.java +++ b/assets/src/main/java/bisq/asset/coins/BitcoinRhodium.java @@ -32,7 +32,6 @@ public class BitcoinRhodium extends Coin { public BitcoinRhodiumParams() { addressHeader = 61; p2shHeader = 123; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Bitmark.java b/assets/src/main/java/bisq/asset/coins/Bitmark.java index c4cac96f85..8bcbe20bb5 100644 --- a/assets/src/main/java/bisq/asset/coins/Bitmark.java +++ b/assets/src/main/java/bisq/asset/coins/Bitmark.java @@ -27,7 +27,6 @@ public class Bitmark extends Coin { public BitmarkParams() { addressHeader = 85; p2shHeader = 5; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } diff --git a/assets/src/main/java/bisq/asset/coins/CTSCoin.java b/assets/src/main/java/bisq/asset/coins/CTSCoin.java index 7db46edc26..f21dee31cb 100644 --- a/assets/src/main/java/bisq/asset/coins/CTSCoin.java +++ b/assets/src/main/java/bisq/asset/coins/CTSCoin.java @@ -30,7 +30,6 @@ public class CTSCoin extends Coin { public CtscMainNetParams() { this.addressHeader = 66; this.p2shHeader = 16; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Chaucha.java b/assets/src/main/java/bisq/asset/coins/Chaucha.java index c26b1a2dcf..0f307a719d 100644 --- a/assets/src/main/java/bisq/asset/coins/Chaucha.java +++ b/assets/src/main/java/bisq/asset/coins/Chaucha.java @@ -32,7 +32,6 @@ public class Chaucha extends Coin { public ChauchaParams() { addressHeader = 88; p2shHeader = 50; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Credits.java b/assets/src/main/java/bisq/asset/coins/Credits.java index 1a64f4a391..8e3224f654 100644 --- a/assets/src/main/java/bisq/asset/coins/Credits.java +++ b/assets/src/main/java/bisq/asset/coins/Credits.java @@ -50,7 +50,6 @@ public class Credits extends Coin { public CreditsParams() { addressHeader = 28; p2shHeader = 5; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } -} \ No newline at end of file +} diff --git a/assets/src/main/java/bisq/asset/coins/DSTRA.java b/assets/src/main/java/bisq/asset/coins/DSTRA.java index e7ec0e2ca3..230dd01dc6 100644 --- a/assets/src/main/java/bisq/asset/coins/DSTRA.java +++ b/assets/src/main/java/bisq/asset/coins/DSTRA.java @@ -50,7 +50,6 @@ public class DSTRA extends Coin { public DSTRAParams() { addressHeader = 30; p2shHeader = 33; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/DarkPay.java b/assets/src/main/java/bisq/asset/coins/DarkPay.java index ac10111f94..8ba492e069 100644 --- a/assets/src/main/java/bisq/asset/coins/DarkPay.java +++ b/assets/src/main/java/bisq/asset/coins/DarkPay.java @@ -30,7 +30,6 @@ public class DarkPay extends Coin { public DarkPayMainNetParams() { this.addressHeader = 31; this.p2shHeader = 60; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Dash.java b/assets/src/main/java/bisq/asset/coins/Dash.java index 754c8528b5..397b3d6111 100644 --- a/assets/src/main/java/bisq/asset/coins/Dash.java +++ b/assets/src/main/java/bisq/asset/coins/Dash.java @@ -30,7 +30,6 @@ public class Dash extends Coin { public DashMainNetParams() { this.addressHeader = 76; this.p2shHeader = 16; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/DeepOnion.java b/assets/src/main/java/bisq/asset/coins/DeepOnion.java index 716d1b96da..947388c06b 100644 --- a/assets/src/main/java/bisq/asset/coins/DeepOnion.java +++ b/assets/src/main/java/bisq/asset/coins/DeepOnion.java @@ -48,7 +48,6 @@ public class DeepOnion extends Coin { super(); addressHeader = 31; p2shHeader = 78; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Dextro.java b/assets/src/main/java/bisq/asset/coins/Dextro.java index 8f880c2bd2..350a932105 100644 --- a/assets/src/main/java/bisq/asset/coins/Dextro.java +++ b/assets/src/main/java/bisq/asset/coins/Dextro.java @@ -51,7 +51,6 @@ public class Dextro extends Coin { super(); addressHeader = 30; p2shHeader = 90; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Dogecoin.java b/assets/src/main/java/bisq/asset/coins/Dogecoin.java index 40a500ea52..e9fedd2813 100644 --- a/assets/src/main/java/bisq/asset/coins/Dogecoin.java +++ b/assets/src/main/java/bisq/asset/coins/Dogecoin.java @@ -31,7 +31,6 @@ public class Dogecoin extends Coin { public DogecoinMainNetParams() { this.addressHeader = 30; this.p2shHeader = 22; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Doichain.java b/assets/src/main/java/bisq/asset/coins/Doichain.java index 9bf904cc71..f050cf632c 100644 --- a/assets/src/main/java/bisq/asset/coins/Doichain.java +++ b/assets/src/main/java/bisq/asset/coins/Doichain.java @@ -31,7 +31,6 @@ public class Doichain extends Coin { public DoichainParams() { addressHeader = 52; p2shHeader = 13; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Donu.java b/assets/src/main/java/bisq/asset/coins/Donu.java index 67c87b41ed..782c53f2bd 100644 --- a/assets/src/main/java/bisq/asset/coins/Donu.java +++ b/assets/src/main/java/bisq/asset/coins/Donu.java @@ -50,7 +50,6 @@ public class Donu extends Coin { public DonuParams() { addressHeader = 53; p2shHeader = 5; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Emercoin.java b/assets/src/main/java/bisq/asset/coins/Emercoin.java index 071eb05d00..5728575412 100644 --- a/assets/src/main/java/bisq/asset/coins/Emercoin.java +++ b/assets/src/main/java/bisq/asset/coins/Emercoin.java @@ -31,7 +31,6 @@ public class Emercoin extends Coin { public EmercoinMainNetParams() { this.addressHeader = 33; this.p2shHeader = 92; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Faircoin.java b/assets/src/main/java/bisq/asset/coins/Faircoin.java index d94efa3a9a..4d2b86cad8 100644 --- a/assets/src/main/java/bisq/asset/coins/Faircoin.java +++ b/assets/src/main/java/bisq/asset/coins/Faircoin.java @@ -32,7 +32,6 @@ public class Faircoin extends Coin { public FaircoinParams() { addressHeader = 95; p2shHeader = 36; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } diff --git a/assets/src/main/java/bisq/asset/coins/Fujicoin.java b/assets/src/main/java/bisq/asset/coins/Fujicoin.java index 9e2b181e15..c54f26a55c 100644 --- a/assets/src/main/java/bisq/asset/coins/Fujicoin.java +++ b/assets/src/main/java/bisq/asset/coins/Fujicoin.java @@ -30,7 +30,6 @@ public class Fujicoin extends Coin { public FujicoinParams() { addressHeader = 36; p2shHeader = 16; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Galilel.java b/assets/src/main/java/bisq/asset/coins/Galilel.java index 138e965330..9dbabc4b8c 100644 --- a/assets/src/main/java/bisq/asset/coins/Galilel.java +++ b/assets/src/main/java/bisq/asset/coins/Galilel.java @@ -30,7 +30,6 @@ public class Galilel extends Coin { public GalilelMainNetParams() { this.addressHeader = 68; this.p2shHeader = 16; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/GambleCoin.java b/assets/src/main/java/bisq/asset/coins/GambleCoin.java index a199219d94..27447cf9a0 100644 --- a/assets/src/main/java/bisq/asset/coins/GambleCoin.java +++ b/assets/src/main/java/bisq/asset/coins/GambleCoin.java @@ -51,7 +51,6 @@ public class GambleCoin extends Coin { super(); addressHeader = 28; p2shHeader = 18; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Genesis.java b/assets/src/main/java/bisq/asset/coins/Genesis.java index 65c885fd64..fcb49992a8 100644 --- a/assets/src/main/java/bisq/asset/coins/Genesis.java +++ b/assets/src/main/java/bisq/asset/coins/Genesis.java @@ -51,7 +51,6 @@ public class Genesis extends Coin { public GenesisParams() { addressHeader = 28; p2shHeader = 63; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Hatch.java b/assets/src/main/java/bisq/asset/coins/Hatch.java index bbff61765b..0d2d5b3b97 100644 --- a/assets/src/main/java/bisq/asset/coins/Hatch.java +++ b/assets/src/main/java/bisq/asset/coins/Hatch.java @@ -30,7 +30,6 @@ public class Hatch extends Coin { public HatchMainNetParams() { this.addressHeader = 76; this.p2shHeader = 16; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Helium.java b/assets/src/main/java/bisq/asset/coins/Helium.java index cec17a9f7b..592c6cfaa1 100644 --- a/assets/src/main/java/bisq/asset/coins/Helium.java +++ b/assets/src/main/java/bisq/asset/coins/Helium.java @@ -33,7 +33,6 @@ public class Helium extends Coin { public HeliumParams() { addressHeader = 63; p2shHeader = 5; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/IdaPay.java b/assets/src/main/java/bisq/asset/coins/IdaPay.java index 84000ff023..c205b2d7e5 100644 --- a/assets/src/main/java/bisq/asset/coins/IdaPay.java +++ b/assets/src/main/java/bisq/asset/coins/IdaPay.java @@ -51,7 +51,6 @@ public class IdaPay extends Coin { super(); addressHeader = 29; p2shHeader = 36; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Kekcoin.java b/assets/src/main/java/bisq/asset/coins/Kekcoin.java index 33f5bc2107..b7927609d7 100644 --- a/assets/src/main/java/bisq/asset/coins/Kekcoin.java +++ b/assets/src/main/java/bisq/asset/coins/Kekcoin.java @@ -34,7 +34,6 @@ public class Kekcoin extends Coin { 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/KnowYourDeveloper.java b/assets/src/main/java/bisq/asset/coins/KnowYourDeveloper.java index b0f93f3d77..cc9468455a 100644 --- a/assets/src/main/java/bisq/asset/coins/KnowYourDeveloper.java +++ b/assets/src/main/java/bisq/asset/coins/KnowYourDeveloper.java @@ -30,7 +30,6 @@ public class KnowYourDeveloper extends Coin { public KydMainNetParams() { this.addressHeader = 78; this.p2shHeader = 85; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Kore.java b/assets/src/main/java/bisq/asset/coins/Kore.java index 98f635719a..184d426f09 100644 --- a/assets/src/main/java/bisq/asset/coins/Kore.java +++ b/assets/src/main/java/bisq/asset/coins/Kore.java @@ -30,7 +30,6 @@ public class Kore extends Coin { public KoreMainNetParams() { this.addressHeader = 45; this.p2shHeader = 85; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/LBRYCredits.java b/assets/src/main/java/bisq/asset/coins/LBRYCredits.java index 6c256f2ea1..b6e9836266 100644 --- a/assets/src/main/java/bisq/asset/coins/LBRYCredits.java +++ b/assets/src/main/java/bisq/asset/coins/LBRYCredits.java @@ -31,7 +31,6 @@ public class LBRYCredits extends Coin { public LBRYCreditsMainNetParams() { this.addressHeader = 0x55; this.p2shHeader = 0x7a; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Litecoin.java b/assets/src/main/java/bisq/asset/coins/Litecoin.java index 57b61a3cb5..6580f851dd 100644 --- a/assets/src/main/java/bisq/asset/coins/Litecoin.java +++ b/assets/src/main/java/bisq/asset/coins/Litecoin.java @@ -30,7 +30,6 @@ public class Litecoin extends Coin { public LitecoinMainNetParams() { this.addressHeader = 48; this.p2shHeader = 5; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/LitecoinPlus.java b/assets/src/main/java/bisq/asset/coins/LitecoinPlus.java index aaf8d669b9..c5ce3070c1 100644 --- a/assets/src/main/java/bisq/asset/coins/LitecoinPlus.java +++ b/assets/src/main/java/bisq/asset/coins/LitecoinPlus.java @@ -31,7 +31,6 @@ public class LitecoinPlus extends Coin { public LitecoinPlusMainNetParams() { this.addressHeader = 75; this.p2shHeader = 8; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Lytix.java b/assets/src/main/java/bisq/asset/coins/Lytix.java index d9a0de079b..629a47f3d4 100644 --- a/assets/src/main/java/bisq/asset/coins/Lytix.java +++ b/assets/src/main/java/bisq/asset/coins/Lytix.java @@ -33,7 +33,6 @@ public class Lytix extends Coin { public LytixParams() { addressHeader = 19; p2shHeader = 11; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/MobitGlobal.java b/assets/src/main/java/bisq/asset/coins/MobitGlobal.java index c4e1eb86d2..6b2bfbff12 100644 --- a/assets/src/main/java/bisq/asset/coins/MobitGlobal.java +++ b/assets/src/main/java/bisq/asset/coins/MobitGlobal.java @@ -50,7 +50,6 @@ public class MobitGlobal extends Coin { public MobitGlobalParams() { addressHeader = 50; p2shHeader = 110; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/MonetaryUnit.java b/assets/src/main/java/bisq/asset/coins/MonetaryUnit.java index b1cd79a995..4055768af3 100644 --- a/assets/src/main/java/bisq/asset/coins/MonetaryUnit.java +++ b/assets/src/main/java/bisq/asset/coins/MonetaryUnit.java @@ -50,7 +50,6 @@ public class MonetaryUnit extends Coin { public MonetaryUnitParams() { addressHeader = 16; p2shHeader = 76; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Myce.java b/assets/src/main/java/bisq/asset/coins/Myce.java index ea8a2a781c..0e39bdf549 100644 --- a/assets/src/main/java/bisq/asset/coins/Myce.java +++ b/assets/src/main/java/bisq/asset/coins/Myce.java @@ -26,12 +26,11 @@ public class Myce extends Coin { public Myce() { super("Myce", "YCE", new Base58BitcoinAddressValidator(new MyceParams())); } - + public static class MyceParams extends NetworkParametersAdapter { public MyceParams() { addressHeader = 50; p2shHeader = 85; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Navcoin.java b/assets/src/main/java/bisq/asset/coins/Navcoin.java index 31c0786657..6427555636 100644 --- a/assets/src/main/java/bisq/asset/coins/Navcoin.java +++ b/assets/src/main/java/bisq/asset/coins/Navcoin.java @@ -30,7 +30,6 @@ public class Navcoin extends Coin { public NavcoinParams() { this.addressHeader = 53; this.p2shHeader = 85; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/PIVX.java b/assets/src/main/java/bisq/asset/coins/PIVX.java index 12bd6a9111..cfde4d8594 100644 --- a/assets/src/main/java/bisq/asset/coins/PIVX.java +++ b/assets/src/main/java/bisq/asset/coins/PIVX.java @@ -50,7 +50,6 @@ public class PIVX extends Coin { public PIVXParams() { addressHeader = 30; p2shHeader = 13; - 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 index f91c9a623d..18f40b9b91 100644 --- a/assets/src/main/java/bisq/asset/coins/PZDC.java +++ b/assets/src/main/java/bisq/asset/coins/PZDC.java @@ -50,7 +50,6 @@ public class PZDC extends Coin { public PZDCParams() { addressHeader = 55; p2shHeader = 13; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Particl.java b/assets/src/main/java/bisq/asset/coins/Particl.java index 8f14c47dd3..601af39662 100644 --- a/assets/src/main/java/bisq/asset/coins/Particl.java +++ b/assets/src/main/java/bisq/asset/coins/Particl.java @@ -32,7 +32,6 @@ public class Particl extends Coin { public ParticlMainNetParams() { this.addressHeader = 56; this.p2shHeader = 60; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } public static class ParticlMainNetAddressValidator extends Base58BitcoinAddressValidator { @@ -49,5 +48,5 @@ public class Particl extends Coin { return super.validate(address); } } - + } diff --git a/assets/src/main/java/bisq/asset/coins/Pinkcoin.java b/assets/src/main/java/bisq/asset/coins/Pinkcoin.java index b27bece164..0792bd0857 100644 --- a/assets/src/main/java/bisq/asset/coins/Pinkcoin.java +++ b/assets/src/main/java/bisq/asset/coins/Pinkcoin.java @@ -33,7 +33,6 @@ public class Pinkcoin extends Coin { public PinkcoinParams() { addressHeader = 3; p2shHeader = 28; - 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 index 984ba4cb21..75f8deade5 100644 --- a/assets/src/main/java/bisq/asset/coins/QMCoin.java +++ b/assets/src/main/java/bisq/asset/coins/QMCoin.java @@ -50,7 +50,6 @@ public class QMCoin extends Coin { public QMCoinParams() { addressHeader = 58; p2shHeader = 120; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Qbase.java b/assets/src/main/java/bisq/asset/coins/Qbase.java index 94d70f4304..2d076a74dc 100644 --- a/assets/src/main/java/bisq/asset/coins/Qbase.java +++ b/assets/src/main/java/bisq/asset/coins/Qbase.java @@ -32,7 +32,6 @@ public class Qbase extends Coin { public QbaseParams() { addressHeader = 25; p2shHeader = 5; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Radium.java b/assets/src/main/java/bisq/asset/coins/Radium.java index aed79c6303..8c59a1258c 100644 --- a/assets/src/main/java/bisq/asset/coins/Radium.java +++ b/assets/src/main/java/bisq/asset/coins/Radium.java @@ -34,7 +34,6 @@ public class Radium extends Coin { 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/SUB1X.java b/assets/src/main/java/bisq/asset/coins/SUB1X.java index 14daac5be0..9bcb32386a 100644 --- a/assets/src/main/java/bisq/asset/coins/SUB1X.java +++ b/assets/src/main/java/bisq/asset/coins/SUB1X.java @@ -50,7 +50,6 @@ public class SUB1X extends Coin { public SUB1XParams() { addressHeader = 80; p2shHeader = 13; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/SixEleven.java b/assets/src/main/java/bisq/asset/coins/SixEleven.java index ded86da4ef..706f97e90d 100644 --- a/assets/src/main/java/bisq/asset/coins/SixEleven.java +++ b/assets/src/main/java/bisq/asset/coins/SixEleven.java @@ -17,6 +17,7 @@ package bisq.asset.coins; +import bisq.asset.AddressValidationResult; import bisq.asset.Base58BitcoinAddressValidator; import bisq.asset.Coin; import bisq.asset.NetworkParametersAdapter; @@ -24,13 +25,27 @@ import bisq.asset.NetworkParametersAdapter; public class SixEleven extends Coin { public SixEleven() { - super("SixEleven", "SIL", new Base58BitcoinAddressValidator(new SixElevenChainParams())); + super("SixEleven", "SIL", new SixElevenAddressValidator()); + } + + public static class SixElevenAddressValidator extends Base58BitcoinAddressValidator { + + public SixElevenAddressValidator() { + super(new SixEleven.SixElevenChainParams()); + } + + @Override + public AddressValidationResult validate(String address) { + if (!address.matches("^[MN][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$")) + return AddressValidationResult.invalidStructure(); + + return super.validate(address); + } } public static class SixElevenChainParams extends NetworkParametersAdapter { public SixElevenChainParams() { addressHeader = 52; - acceptableAddressCodes = new int[]{addressHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Spectrecoin.java b/assets/src/main/java/bisq/asset/coins/Spectrecoin.java index ee9f7a763c..404c351208 100644 --- a/assets/src/main/java/bisq/asset/coins/Spectrecoin.java +++ b/assets/src/main/java/bisq/asset/coins/Spectrecoin.java @@ -33,7 +33,6 @@ public class Spectrecoin extends Coin { public SpectrecoinParams() { addressHeader = 63; p2shHeader = 136; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/UnitedCommunityCoin.java b/assets/src/main/java/bisq/asset/coins/UnitedCommunityCoin.java index 9d893980e8..dba97a2c55 100644 --- a/assets/src/main/java/bisq/asset/coins/UnitedCommunityCoin.java +++ b/assets/src/main/java/bisq/asset/coins/UnitedCommunityCoin.java @@ -51,7 +51,6 @@ public class UnitedCommunityCoin extends Coin { super(); addressHeader = 68; p2shHeader = 18; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Veil.java b/assets/src/main/java/bisq/asset/coins/Veil.java index 792cba487a..8e9e5ee2f9 100644 --- a/assets/src/main/java/bisq/asset/coins/Veil.java +++ b/assets/src/main/java/bisq/asset/coins/Veil.java @@ -50,7 +50,6 @@ public class Veil extends Coin { public VeilParams() { addressHeader = 70; p2shHeader = 5; - acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; } } } diff --git a/assets/src/main/java/bisq/asset/coins/Vertcoin.java b/assets/src/main/java/bisq/asset/coins/Vertcoin.java index 1f8dae6471..a24f386ea3 100644 --- a/assets/src/main/java/bisq/asset/coins/Vertcoin.java +++ b/assets/src/main/java/bisq/asset/coins/Vertcoin.java @@ -30,7 +30,6 @@ public class Vertcoin extends Coin { public VertcoinMainNetParams() { this.addressHeader = 71; this.p2shHeader = 5; - this.acceptableAddressCodes = new int[]{this.addressHeader, this.p2shHeader}; } } } diff --git a/build.gradle b/build.gradle index 13e2c3c172..557b773a60 100644 --- a/build.gradle +++ b/build.gradle @@ -27,9 +27,9 @@ configure(subprojects) { sourceCompatibility = 1.10 ext { // in alphabetical order - bcVersion = '1.56' - bitcoinjVersion = 'a88d36d' - btcdCli4jVersion = '975ff5d4' + bcVersion = '1.63' + bitcoinjVersion = '0d98efb' + btcdCli4jVersion = '27b94333' codecVersion = '1.13' easybindVersion = '1.0.3' easyVersion = '4.0.1' @@ -40,7 +40,7 @@ configure(subprojects) { fontawesomefxMaterialdesignfontVersion = '2.0.26-9.1.2' grpcVersion = '1.25.0' gsonVersion = '2.8.5' - guavaVersion = '20.0' + guavaVersion = '28.2-jre' guiceVersion = '4.2.2' hamcrestVersion = '1.3' httpclientVersion = '4.5.12' @@ -69,7 +69,7 @@ configure(subprojects) { pushyVersion = '0.13.2' qrgenVersion = '1.3' sarxosVersion = '0.3.12' - slf4jVersion = '1.7.25' + slf4jVersion = '1.7.30' sparkVersion = '2.5.2' springBootVersion = '1.5.10.RELEASE' @@ -211,11 +211,14 @@ configure(project(':proto')) { configure(project(':assets')) { dependencies { - compile("com.github.bisq-network.bitcoinj:bitcoinj-core:$bitcoinjVersion") { + compile("com.github.bisq-network:bitcoinj:$bitcoinjVersion") { exclude(module: 'jsr305') exclude(module: 'slf4j-api') exclude(module: 'guava') exclude(module: 'protobuf-java') + exclude(module: 'bcprov-jdk15on') + exclude(module: 'okhttp') + exclude(module: 'okio') } compile "com.google.guava:guava:$guavaVersion" compile "org.slf4j:slf4j-api:$slf4jVersion" @@ -241,11 +244,14 @@ configure(project(':common')) { compile("com.google.inject:guice:$guiceVersion") { exclude(module: 'guava') } - compile("com.github.bisq-network.bitcoinj:bitcoinj-core:$bitcoinjVersion") { + compile("com.github.bisq-network:bitcoinj:$bitcoinjVersion") { exclude(module: 'jsr305') exclude(module: 'slf4j-api') exclude(module: 'guava') exclude(module: 'protobuf-java') + exclude(module: 'bcprov-jdk15on') + exclude(module: 'okhttp') + exclude(module: 'okio') } runtimeOnly("io.grpc:grpc-netty-shaded:$grpcVersion") { exclude(module: 'guava') @@ -306,7 +312,9 @@ configure(project(':core')) { implementation("org.apache.httpcomponents:httpclient:$httpclientVersion") { exclude(module: 'commons-codec') } + compile "com.google.guava:guava:$guavaVersion" compile("network.bisq.btcd-cli4j:btcd-cli4j-core:$btcdCli4jVersion") { + exclude(module: 'guava') exclude(module: 'slf4j-api') exclude(module: 'httpclient') exclude(module: 'commons-lang3') @@ -315,6 +323,7 @@ configure(project(':core')) { exclude(module: 'jackson-databind') } compile("network.bisq.btcd-cli4j:btcd-cli4j-daemon:$btcdCli4jVersion") { + exclude(module: 'guava') exclude(module: 'slf4j-api') exclude(module: 'httpclient') exclude(module: 'commons-lang3') diff --git a/common/src/main/java/bisq/common/crypto/Hash.java b/common/src/main/java/bisq/common/crypto/Hash.java index c07768dcad..b859654d2a 100644 --- a/common/src/main/java/bisq/common/crypto/Hash.java +++ b/common/src/main/java/bisq/common/crypto/Hash.java @@ -21,7 +21,7 @@ import org.bitcoinj.core.Utils; import com.google.common.base.Charsets; -import org.spongycastle.crypto.digests.RIPEMD160Digest; +import org.bouncycastle.crypto.digests.RIPEMD160Digest; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; diff --git a/core/src/main/java/bisq/core/api/CoreWalletsService.java b/core/src/main/java/bisq/core/api/CoreWalletsService.java index 10feea5b8e..950b97958a 100644 --- a/core/src/main/java/bisq/core/api/CoreWalletsService.java +++ b/core/src/main/java/bisq/core/api/CoreWalletsService.java @@ -33,7 +33,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import org.spongycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.KeyParameter; import java.util.List; import java.util.Optional; diff --git a/core/src/main/java/bisq/core/app/AsciiLogo.java b/core/src/main/java/bisq/core/app/AsciiLogo.java new file mode 100644 index 0000000000..f8d30333dd --- /dev/null +++ b/core/src/main/java/bisq/core/app/AsciiLogo.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.core.app; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class AsciiLogo { + public static void showAsciiLogo() { + log.info("\n\n" + + " ........ ...... \n" + + " .............. ...... \n" + + " ................. ...... \n" + + " ...... .......... .. ...... \n" + + " ...... ...... ...... ............... ..... ......... .......... \n" + + " ....... ........ .................. ..... ............. ............... \n" + + " ...... ........ .......... ....... ..... ...... ... ........ ....... \n" + + " ...... ..... ....... ..... ..... ..... ..... ...... \n" + + " ...... ... ... ...... ...... ..... ........... ...... ...... \n" + + " ...... ..... .... ...... ...... ..... ............ ..... ...... \n" + + " ...... ..... ...... ..... ........ ...... ...... \n" + + " ...... .... ... ...... ...... ..... .. ...... ...... ........ \n" + + " ........ .. ....... ................. ..... .............. ................... \n" + + " .......... ......... ............. ..... ............ ................. \n" + + " ...................... ..... .... .... ...... \n" + + " ................ ...... \n" + + " .... ...... \n" + + " ...... \n" + + "\n\n"); + } +} diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index bb36e1ffe4..f9961adda2 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -102,6 +102,7 @@ public abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet /////////////////////////////////////////////////////////////////////////////////////////// protected void doExecute() { + AsciiLogo.showAsciiLogo(); configUserThread(); CoreSetup.setup(config); addCapabilities(); diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index 164f74a04d..3577f38658 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -103,7 +103,7 @@ import javafx.beans.value.ChangeListener; import javafx.collections.ListChangeListener; import javafx.collections.SetChangeListener; -import org.spongycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.KeyParameter; import java.io.IOException; diff --git a/core/src/main/java/bisq/core/app/WalletAppSetup.java b/core/src/main/java/bisq/core/app/WalletAppSetup.java index 88776606e3..a5f27e899c 100644 --- a/core/src/main/java/bisq/core/app/WalletAppSetup.java +++ b/core/src/main/java/bisq/core/app/WalletAppSetup.java @@ -101,7 +101,7 @@ public class WalletAppSetup { Runnable downloadCompleteHandler, Runnable walletInitializedHandler) { log.info("Initialize WalletAppSetup with BitcoinJ version {} and hash of BitcoinJ commit {}", - VersionMessage.BITCOINJ_VERSION, "cd30ad5b"); + VersionMessage.BITCOINJ_VERSION, "0d98efb"); ObjectProperty walletServiceException = new SimpleObjectProperty<>(); btcInfoBinding = EasyBind.combine(walletsSetup.downloadPercentageProperty(), diff --git a/core/src/main/java/bisq/core/btc/exceptions/TxBroadcastTimeoutException.java b/core/src/main/java/bisq/core/btc/exceptions/TxBroadcastTimeoutException.java index a9aa4aa0df..1c97f63d82 100644 --- a/core/src/main/java/bisq/core/btc/exceptions/TxBroadcastTimeoutException.java +++ b/core/src/main/java/bisq/core/btc/exceptions/TxBroadcastTimeoutException.java @@ -41,7 +41,7 @@ public class TxBroadcastTimeoutException extends TxBroadcastException { */ public TxBroadcastTimeoutException(Transaction localTx, int delay, Wallet wallet) { super("The transaction was not broadcasted in " + delay + - " seconds. txId=" + localTx.getHashAsString()); + " seconds. txId=" + localTx.getTxId().toString()); this.localTx = localTx; this.delay = delay; this.wallet = wallet; diff --git a/core/src/main/java/bisq/core/btc/model/AddressEntry.java b/core/src/main/java/bisq/core/btc/model/AddressEntry.java index 26c2c50956..3b036b47a4 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntry.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntry.java @@ -26,6 +26,7 @@ import com.google.protobuf.ByteString; import org.bitcoinj.core.Address; import org.bitcoinj.core.Coin; +import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.crypto.DeterministicKey; import java.util.Optional; @@ -174,7 +175,7 @@ public final class AddressEntry implements PersistablePayload { @Nullable public Address getAddress() { if (address == null && keyPair != null) - address = keyPair.toAddress(Config.baseCurrencyNetworkParameters()); + address = LegacyAddress.fromKey(Config.baseCurrencyNetworkParameters(), keyPair); return address; } diff --git a/core/src/main/java/bisq/core/btc/model/AddressEntryList.java b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java index a3b2ae7856..a3fea3bd40 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntryList.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntryList.java @@ -25,8 +25,10 @@ import bisq.common.storage.Storage; import com.google.protobuf.Message; import org.bitcoinj.core.Address; +import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.Transaction; import org.bitcoinj.crypto.DeterministicKey; +import org.bitcoinj.script.Script; import org.bitcoinj.wallet.Wallet; import com.google.inject.Inject; @@ -105,9 +107,12 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo if (!entrySet.isEmpty()) { Set toBeRemoved = new HashSet<>(); entrySet.forEach(addressEntry -> { - DeterministicKey keyFromPubHash = (DeterministicKey) wallet.findKeyFromPubHash(addressEntry.getPubKeyHash()); + DeterministicKey keyFromPubHash = (DeterministicKey) wallet.findKeyFromPubKeyHash( + addressEntry.getPubKeyHash(), + Script.ScriptType.P2PKH); if (keyFromPubHash != null) { - Address addressFromKey = keyFromPubHash.toAddress(Config.baseCurrencyNetworkParameters()); + Address addressFromKey = LegacyAddress.fromKey(Config.baseCurrencyNetworkParameters(), + keyFromPubHash); // We want to ensure key and address matches in case we have address in entry available already if (addressEntry.getAddress() == null || addressFromKey.equals(addressEntry.getAddress())) { addressEntry.setDeterministicKey(keyFromPubHash); @@ -140,7 +145,7 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo .filter(this::isAddressNotInEntries) .forEach(address -> { log.info("Create AddressEntry for IssuedReceiveAddress. address={}", address.toString()); - DeterministicKey key = (DeterministicKey) wallet.findKeyFromPubHash(address.getHash160()); + DeterministicKey key = (DeterministicKey) wallet.findKeyFromAddress(address); if (key != null) { // Address will be derived from key in getAddress method entrySet.add(new AddressEntry(key, AddressEntry.Context.AVAILABLE)); @@ -175,7 +180,8 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo public void swapToAvailable(AddressEntry addressEntry) { boolean setChangedByRemove = entrySet.remove(addressEntry); - boolean setChangedByAdd = entrySet.add(new AddressEntry(addressEntry.getKeyPair(), AddressEntry.Context.AVAILABLE)); + boolean setChangedByAdd = entrySet.add(new AddressEntry(addressEntry.getKeyPair(), + AddressEntry.Context.AVAILABLE)); if (setChangedByRemove || setChangedByAdd) { persist(); } @@ -208,7 +214,8 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo .map(output -> output.getAddressFromP2PKHScript(wallet.getNetworkParameters())) .filter(Objects::nonNull) .filter(this::isAddressNotInEntries) - .map(address -> (DeterministicKey) wallet.findKeyFromPubHash(address.getHash160())) + .map(address -> (DeterministicKey) wallet.findKeyFromPubKeyHash(address.getHash(), + Script.ScriptType.P2PKH)) .filter(Objects::nonNull) .map(deterministicKey -> new AddressEntry(deterministicKey, AddressEntry.Context.AVAILABLE)) .forEach(this::addAddressEntry); diff --git a/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java b/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java index 2cb29b8f49..7740b17e35 100644 --- a/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java +++ b/core/src/main/java/bisq/core/btc/nodes/BtcNodeConverter.java @@ -55,17 +55,8 @@ class BtcNodeConverter { PeerAddress convertOnionHost(BtcNode node) { // no DNS lookup for onion addresses String onionAddress = Objects.requireNonNull(node.getOnionAddress()); - try { - // OnionCatConverter.onionHostToInetAddress converts onion to ipv6 representation - // inetAddress is not used but required for wallet persistence. Throws nullPointer if not set. - InetAddress inetAddress = facade.onionHostToInetAddress(onionAddress); - PeerAddress result = new PeerAddress(onionAddress, node.getPort()); - result.setAddr(inetAddress); - return result; - } catch (UnknownHostException e) { - log.error("Failed to convert node", e); - return null; - } + PeerAddress result = new PeerAddress(onionAddress, node.getPort()); + return result; } @Nullable @@ -107,7 +98,7 @@ class BtcNodeConverter { // Blocking call. takes about 600 ms ;-( InetAddress lookupAddress = facade.torLookup(proxy, host); InetSocketAddress address = new InetSocketAddress(lookupAddress, port); - return new PeerAddress(address.getAddress(), address.getPort()); + return new PeerAddress(address); } catch (Exception e) { log.error("Failed to create peer address", e); return null; @@ -118,7 +109,7 @@ class BtcNodeConverter { private static PeerAddress create(String hostName, int port) { try { InetSocketAddress address = new InetSocketAddress(hostName, port); - return new PeerAddress(address.getAddress(), address.getPort()); + return new PeerAddress(address); } catch (Exception e) { log.error("Failed to create peer address", e); return null; diff --git a/core/src/main/java/bisq/core/btc/setup/BisqDeterministicKeyChain.java b/core/src/main/java/bisq/core/btc/setup/BisqDeterministicKeyChain.java deleted file mode 100644 index e0d53ddd15..0000000000 --- a/core/src/main/java/bisq/core/btc/setup/BisqDeterministicKeyChain.java +++ /dev/null @@ -1,81 +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.setup; - -import org.bitcoinj.crypto.ChildNumber; -import org.bitcoinj.crypto.DeterministicKey; -import org.bitcoinj.crypto.KeyCrypter; -import org.bitcoinj.wallet.DeterministicKeyChain; -import org.bitcoinj.wallet.DeterministicSeed; - -import com.google.common.collect.ImmutableList; - -import org.spongycastle.crypto.params.KeyParameter; - -import java.security.SecureRandom; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class BisqDeterministicKeyChain extends DeterministicKeyChain { - private static final Logger log = LoggerFactory.getLogger(BisqDeterministicKeyChain.class); - - // See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki - // https://github.com/satoshilabs/slips/blob/master/slip-0044.md - // We have registered 142 (0x8000008E) as coin_type for BSQ - public static final ImmutableList BIP44_BSQ_ACCOUNT_PATH = ImmutableList.of( - new ChildNumber(44, true), - new ChildNumber(142, true), - ChildNumber.ZERO_HARDENED); - - public BisqDeterministicKeyChain(SecureRandom random) { - super(random); - } - - public BisqDeterministicKeyChain(DeterministicKey accountKey, boolean isFollowingKey) { - super(accountKey, isFollowingKey); - } - - public BisqDeterministicKeyChain(DeterministicSeed seed, KeyCrypter crypter) { - super(seed, crypter); - } - - public BisqDeterministicKeyChain(DeterministicSeed seed) { - super(seed); - } - - - @Override - public DeterministicKeyChain toEncrypted(KeyCrypter keyCrypter, KeyParameter aesKey) { - return new BisqDeterministicKeyChain(keyCrypter, aesKey, this); - } - - protected DeterministicKeyChain makeKeyChainFromSeed(DeterministicSeed seed) { - return new BisqDeterministicKeyChain(seed); - } - - protected BisqDeterministicKeyChain(KeyCrypter crypter, KeyParameter aesKey, DeterministicKeyChain chain) { - super(crypter, aesKey, chain); - } - - @Override - protected ImmutableList getAccountPath() { - return BIP44_BSQ_ACCOUNT_PATH; - } - -} diff --git a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java index 0cb088f31d..5c31e0d64c 100644 --- a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java +++ b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java @@ -19,35 +19,56 @@ package bisq.core.btc.setup; import org.bitcoinj.crypto.ChildNumber; import org.bitcoinj.crypto.DeterministicKey; -import org.bitcoinj.crypto.HDUtils; import org.bitcoinj.crypto.KeyCrypter; +import org.bitcoinj.script.Script; +import org.bitcoinj.wallet.DefaultKeyChainFactory; import org.bitcoinj.wallet.DeterministicKeyChain; import org.bitcoinj.wallet.DeterministicSeed; -import org.bitcoinj.wallet.KeyChainFactory; +import org.bitcoinj.wallet.KeyChainGroupStructure; import org.bitcoinj.wallet.Protos; import org.bitcoinj.wallet.UnreadableWalletException; import com.google.common.collect.ImmutableList; -class BisqKeyChainFactory implements KeyChainFactory { - private final boolean useBitcoinDeterministicKeyChain; +/** + * Hack to convert bitcoinj 0.14 wallets to bitcoinj 0.15 format. + * + * This code is required to be executed only once per user (actually twice, for btc and bsq wallets). + * Once all users using bitcoinj 0.14 wallets have executed this code, this class will be no longer needed. + * + * Since that is almost impossible to guarantee, this hack will stay until we decide to don't be + * backwards compatible with pre bitcoinj 0.15 wallets. + * In that scenario, users will have to migrate using this procedure: + * 1) Run pre bitcoinj 0.15 bisq and copy their seed words on a piece of paper. + * 2) Run post bitcoinj 0.15 bisq and use recover from seed. + * */ +public class BisqKeyChainFactory extends DefaultKeyChainFactory { - public BisqKeyChainFactory(boolean useBitcoinDeterministicKeyChain) { - this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain; + private boolean isBsqWallet; + + public BisqKeyChainFactory(boolean isBsqWallet) { + this.isBsqWallet = isBsqWallet; } @Override - public DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed, KeyCrypter crypter, boolean isMarried) { - return useBitcoinDeterministicKeyChain ? new BtcDeterministicKeyChain(seed, crypter) : new BisqDeterministicKeyChain(seed, crypter); + public DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed, KeyCrypter crypter, boolean isMarried, Script.ScriptType outputScriptType, ImmutableList accountPath) { + ImmutableList maybeUpdatedAccountPath = accountPath; + if (DeterministicKeyChain.ACCOUNT_ZERO_PATH.equals(accountPath)) { + // This is a bitcoinj 0.14 wallet that has no account path in the serialized mnemonic + KeyChainGroupStructure structure = new BisqKeyChainGroupStructure(isBsqWallet); + maybeUpdatedAccountPath = structure.accountPathFor(outputScriptType); + } + + return super.makeKeyChain(key, firstSubKey, seed, crypter, isMarried, outputScriptType, maybeUpdatedAccountPath); } @Override - public DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey, - boolean isFollowingKey, boolean isMarried) throws UnreadableWalletException { - ImmutableList accountPath = useBitcoinDeterministicKeyChain ? BtcDeterministicKeyChain.BIP44_BTC_ACCOUNT_PATH : BisqDeterministicKeyChain.BIP44_BSQ_ACCOUNT_PATH; - if (!accountKey.getPath().equals(accountPath)) - throw new UnreadableWalletException("Expecting account key but found key with path: " + - HDUtils.formatPath(accountKey.getPath())); - return useBitcoinDeterministicKeyChain ? new BtcDeterministicKeyChain(accountKey, isFollowingKey) : new BisqDeterministicKeyChain(accountKey, isFollowingKey); + public DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey, boolean isFollowingKey, boolean isMarried, Script.ScriptType outputScriptType) throws UnreadableWalletException { + throw new UnsupportedOperationException("Bisq is not supposed to use this"); + } + + @Override + public DeterministicKeyChain makeSpendingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey, boolean isMarried, Script.ScriptType outputScriptType) throws UnreadableWalletException { + throw new UnsupportedOperationException("Bisq is not supposed to use this"); } } diff --git a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroup.java b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroup.java deleted file mode 100644 index 2146bfea7e..0000000000 --- a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroup.java +++ /dev/null @@ -1,49 +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.setup; - -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.wallet.DeterministicKeyChain; -import org.bitcoinj.wallet.KeyChainGroup; - -import java.security.SecureRandom; - -class BisqKeyChainGroup extends KeyChainGroup { - private final boolean useBitcoinDeterministicKeyChain; - - public boolean isUseBitcoinDeterministicKeyChain() { - return useBitcoinDeterministicKeyChain; - } - - public BisqKeyChainGroup(NetworkParameters params, @SuppressWarnings("SameParameterValue") boolean useBitcoinDeterministicKeyChain) { - super(params); - this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain; - } - - public BisqKeyChainGroup(NetworkParameters params, DeterministicKeyChain chain, boolean useBitcoinDeterministicKeyChain) { - super(params, chain); - - this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain; - } - - @Override - public void createAndActivateNewHDChain() { - DeterministicKeyChain chain = useBitcoinDeterministicKeyChain ? new BtcDeterministicKeyChain(new SecureRandom()) : new BisqDeterministicKeyChain(new SecureRandom()); - addAndActivateHDChain(chain); - } -} diff --git a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java new file mode 100644 index 0000000000..7c7f6a4e3a --- /dev/null +++ b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java @@ -0,0 +1,79 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.core.btc.setup; + +import org.bitcoinj.crypto.ChildNumber; +import org.bitcoinj.script.Script; +import org.bitcoinj.wallet.KeyChainGroupStructure; + +import com.google.common.collect.ImmutableList; + +public class BisqKeyChainGroupStructure implements KeyChainGroupStructure { + + // See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki + // https://github.com/satoshilabs/slips/blob/master/slip-0044.md + // We use 0 (0x80000000) as coin_type for BTC + // m / purpose' / coin_type' / account' / change / address_index + public static final ImmutableList BIP44_BTC_NON_SEGWIT_ACCOUNT_PATH = ImmutableList.of( + new ChildNumber(44, true), + new ChildNumber(0, true), + ChildNumber.ZERO_HARDENED); + + public static final ImmutableList BIP44_BTC_SEGWIT_ACCOUNT_PATH = ImmutableList.of( + new ChildNumber(44, true), + new ChildNumber(0, true), + ChildNumber.ONE_HARDENED); + + // See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki + // https://github.com/satoshilabs/slips/blob/master/slip-0044.md + // We have registered 142 (0x8000008E) as coin_type for BSQ + public static final ImmutableList BIP44_BSQ_NON_SEGWIT_ACCOUNT_PATH = ImmutableList.of( + new ChildNumber(44, true), + new ChildNumber(142, true), + ChildNumber.ZERO_HARDENED); + + public static final ImmutableList BIP44_BSQ_SEGWIT_ACCOUNT_PATH = ImmutableList.of( + new ChildNumber(44, true), + new ChildNumber(142, true), + ChildNumber.ONE_HARDENED); + + private boolean isBsqWallet; + + public BisqKeyChainGroupStructure (boolean isBsqWallet) { + this.isBsqWallet = isBsqWallet; + } + + @Override + public ImmutableList accountPathFor(Script.ScriptType outputScriptType) { + if (!isBsqWallet) { + if (outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH) + return BIP44_BTC_NON_SEGWIT_ACCOUNT_PATH; + else if (outputScriptType == Script.ScriptType.P2WPKH) + return BIP44_BTC_SEGWIT_ACCOUNT_PATH; + else + throw new IllegalArgumentException(outputScriptType.toString()); + } else { + if (outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH) + return BIP44_BSQ_NON_SEGWIT_ACCOUNT_PATH; + else if (outputScriptType == Script.ScriptType.P2WPKH) + return BIP44_BSQ_SEGWIT_ACCOUNT_PATH; + else + throw new IllegalArgumentException(outputScriptType.toString()); + } + } +} diff --git a/core/src/main/java/bisq/core/btc/setup/BsqWallet.java b/core/src/main/java/bisq/core/btc/setup/BsqWallet.java deleted file mode 100644 index 73c5ac9634..0000000000 --- a/core/src/main/java/bisq/core/btc/setup/BsqWallet.java +++ /dev/null @@ -1,29 +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.setup; - -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.wallet.KeyChainGroup; -import org.bitcoinj.wallet.Wallet; - -public class BsqWallet extends Wallet { - - public BsqWallet(NetworkParameters params, KeyChainGroup keyChainGroup) { - super(params, keyChainGroup); - } -} diff --git a/core/src/main/java/bisq/core/btc/setup/BtcDeterministicKeyChain.java b/core/src/main/java/bisq/core/btc/setup/BtcDeterministicKeyChain.java deleted file mode 100644 index fd177aa71a..0000000000 --- a/core/src/main/java/bisq/core/btc/setup/BtcDeterministicKeyChain.java +++ /dev/null @@ -1,82 +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.setup; - -import org.bitcoinj.crypto.ChildNumber; -import org.bitcoinj.crypto.DeterministicKey; -import org.bitcoinj.crypto.KeyCrypter; -import org.bitcoinj.wallet.DeterministicKeyChain; -import org.bitcoinj.wallet.DeterministicSeed; - -import com.google.common.collect.ImmutableList; - -import org.spongycastle.crypto.params.KeyParameter; - -import java.security.SecureRandom; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class BtcDeterministicKeyChain extends DeterministicKeyChain { - private static final Logger log = LoggerFactory.getLogger(BtcDeterministicKeyChain.class); - - // See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki - // https://github.com/satoshilabs/slips/blob/master/slip-0044.md - // We use 0 (0x80000000) as coin_type for BTC - // m / purpose' / coin_type' / account' / change / address_index - public static final ImmutableList BIP44_BTC_ACCOUNT_PATH = ImmutableList.of( - new ChildNumber(44, true), - new ChildNumber(0, true), - ChildNumber.ZERO_HARDENED); - - public BtcDeterministicKeyChain(SecureRandom random) { - super(random); - } - - public BtcDeterministicKeyChain(DeterministicKey accountKey, boolean isFollowingKey) { - super(accountKey, isFollowingKey); - } - - public BtcDeterministicKeyChain(DeterministicSeed seed, KeyCrypter crypter) { - super(seed, crypter); - } - - public BtcDeterministicKeyChain(DeterministicSeed seed) { - super(seed); - } - - @Override - public DeterministicKeyChain toEncrypted(KeyCrypter keyCrypter, KeyParameter aesKey) { - return new BtcDeterministicKeyChain(keyCrypter, aesKey, this); - } - - @Override - protected DeterministicKeyChain makeKeyChainFromSeed(DeterministicSeed seed) { - return new BtcDeterministicKeyChain(seed); - } - - protected BtcDeterministicKeyChain(KeyCrypter crypter, KeyParameter aesKey, DeterministicKeyChain chain) { - super(crypter, aesKey, chain); - } - - @Override - protected ImmutableList getAccountPath() { - return BIP44_BTC_ACCOUNT_PATH; - } - -} diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index 369180bfee..13f18dd4f7 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -20,201 +20,530 @@ package bisq.core.btc.setup; import bisq.core.btc.nodes.LocalBitcoinNode; import bisq.core.btc.nodes.ProxySocketFactory; import bisq.core.btc.wallet.BisqRiskAnalysis; - -import bisq.common.app.Version; import bisq.common.config.Config; -import org.bitcoinj.core.BlockChain; -import org.bitcoinj.core.CheckpointManager; -import org.bitcoinj.core.Context; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.PeerAddress; -import org.bitcoinj.core.PeerGroup; -import org.bitcoinj.core.Utils; -import org.bitcoinj.core.listeners.DownloadProgressTracker; -import org.bitcoinj.core.listeners.PeerDataEventListener; +import com.google.common.collect.*; +import com.google.common.io.Closeables; +import com.google.common.util.concurrent.*; +import org.bitcoinj.core.listeners.*; +import org.bitcoinj.core.*; +import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.net.BlockingClientManager; -import org.bitcoinj.net.discovery.DnsDiscovery; -import org.bitcoinj.net.discovery.PeerDiscovery; -import org.bitcoinj.params.MainNetParams; -import org.bitcoinj.params.RegTestParams; -import org.bitcoinj.params.TestNet3Params; -import org.bitcoinj.store.BlockStore; -import org.bitcoinj.store.BlockStoreException; -import org.bitcoinj.store.SPVBlockStore; -import org.bitcoinj.wallet.DeterministicKeyChain; -import org.bitcoinj.wallet.DeterministicSeed; -import org.bitcoinj.wallet.KeyChainGroup; -import org.bitcoinj.wallet.Protos; -import org.bitcoinj.wallet.Wallet; -import org.bitcoinj.wallet.WalletExtension; -import org.bitcoinj.wallet.WalletProtobufSerializer; +import org.bitcoinj.net.discovery.*; +import org.bitcoinj.script.Script; +import org.bitcoinj.store.*; +import org.bitcoinj.wallet.*; import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.AbstractIdleService; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; +import org.slf4j.*; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Proxy; - -import java.nio.channels.FileLock; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; - -import java.util.List; -import java.util.concurrent.TimeUnit; +import javax.annotation.*; +import java.io.*; +import java.net.*; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; import lombok.Getter; import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.NotNull; - -import javax.annotation.Nullable; import static bisq.common.util.Preconditions.checkDir; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Preconditions.*; -// Derived from WalletAppKit -// Does the basic wiring -@Slf4j +/** + *

Utility class that wraps the boilerplate needed to set up a new SPV bitcoinj app. Instantiate it with a directory + * and file prefix, optionally configure a few things, then use startAsync and optionally awaitRunning. The object will + * construct and configure a {@link BlockChain}, {@link SPVBlockStore}, {@link Wallet} and {@link PeerGroup}. Depending + * on the value of the blockingStartup property, startup will be considered complete once the block chain has fully + * synchronized, so it can take a while.

+ * + *

To add listeners and modify the objects that are constructed, you can either do that by overriding the + * {@link #onSetupCompleted()} method (which will run on a background thread) and make your changes there, + * or by waiting for the service to start and then accessing the objects from wherever you want. However, you cannot + * access the objects this class creates until startup is complete.

+ * + *

The asynchronous design of this class may seem puzzling (just use {@link #awaitRunning()} if you don't want that). + * It is to make it easier to fit bitcoinj into GUI apps, which require a high degree of responsiveness on their main + * thread which handles all the animation and user interaction. Even when blockingStart is false, initializing bitcoinj + * means doing potentially blocking file IO, generating keys and other potentially intensive operations. By running it + * on a background thread, there's no risk of accidentally causing UI lag.

+ * + *

Note that {@link #awaitRunning()} can throw an unchecked {@link IllegalStateException} + * if anything goes wrong during startup - you should probably handle it and use {@link Exception#getCause()} to figure + * out what went wrong more precisely. Same thing if you just use the {@link #startAsync()} method.

+ */ public class WalletConfig extends AbstractIdleService { + private static final int TOR_SOCKET_TIMEOUT = 120 * 1000; // 1 sec used in bitcoinj, but since bisq uses Tor we allow more. private static final int TOR_VERSION_EXCHANGE_TIMEOUT = 125 * 1000; // 5 sec used in bitcoinj, but since bisq uses Tor we allow more. + protected static final Logger log = LoggerFactory.getLogger(WalletConfig.class); - /////////////////////////////////////////////////////////////////////////////////////////// - // WalletFactory - /////////////////////////////////////////////////////////////////////////////////////////// + protected final NetworkParameters params; + protected final String filePrefix; + protected volatile BlockChain vChain; + protected volatile SPVBlockStore vStore; + protected volatile Wallet vBtcWallet; + protected volatile Wallet vBsqWallet; + protected volatile PeerGroup vPeerGroup; - public interface BisqWalletFactory extends WalletProtobufSerializer.WalletFactory { - Wallet create(NetworkParameters params, KeyChainGroup keyChainGroup, boolean isBsqWallet); - } + protected final File directory; + protected volatile File vBtcWalletFile; + protected volatile File vBsqWalletFile; + protected boolean useAutoSave = true; + protected PeerAddress[] peerAddresses; + protected DownloadProgressTracker downloadListener; + protected boolean autoStop = true; + protected InputStream checkpoints; + protected boolean blockingStartup = true; + protected String userAgent, version; + protected WalletProtobufSerializer.WalletFactory walletFactory; + @Nullable protected DeterministicSeed restoreFromSeed; + @Nullable protected DeterministicKey restoreFromKey; + @Nullable protected PeerDiscovery discovery; - /////////////////////////////////////////////////////////////////////////////////////////// - // Fields - /////////////////////////////////////////////////////////////////////////////////////////// + protected volatile Context context; - private final Context context; - private final NetworkParameters params; - private final File directory; - private final String btcWalletFileName; - private final String bsqWalletFileName; - private final String spvChainFileName; - private final Socks5Proxy socks5Proxy; - private final BisqWalletFactory walletFactory; - private final Config config; - private final LocalBitcoinNode localBitcoinNode; - private final String userAgent; - private int numConnectionsForBtc; - - private volatile Wallet vBtcWallet; - @Nullable - private volatile Wallet vBsqWallet; - private volatile File vBtcWalletFile; - @Nullable - private volatile File vBsqWalletFile; - @Nullable - private DeterministicSeed seed; - - private volatile BlockChain vChain; - private volatile SPVBlockStore vStore; - private volatile PeerGroup vPeerGroup; - private boolean useAutoSave = true; - private PeerAddress[] peerAddresses; - private PeerDataEventListener downloadListener; - private boolean autoStop = true; - private InputStream checkpoints; - private boolean blockingStartup = true; + protected Config config; + protected LocalBitcoinNode localBitcoinNode; + protected Socks5Proxy socks5Proxy; + protected int numConnectionsForBtc; @Getter @Setter private int minBroadcastConnections; - @Nullable - private PeerDiscovery discovery; + /** + * Creates a new WalletConfig, with a newly created {@link Context}. Files will be stored in the given directory. + */ + public WalletConfig(NetworkParameters params, File directory, String filePrefix) { + this(new Context(params), directory, filePrefix); + } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - public WalletConfig(NetworkParameters params, - Socks5Proxy socks5Proxy, - File directory, - Config config, - LocalBitcoinNode localBitcoinNode, - String userAgent, - int numConnectionsForBtc, - @SuppressWarnings("SameParameterValue") String btcWalletFileName, - @SuppressWarnings("SameParameterValue") String bsqWalletFileName, - @SuppressWarnings("SameParameterValue") String spvChainFileName) { - this.config = config; - this.localBitcoinNode = localBitcoinNode; - this.userAgent = userAgent; - this.numConnectionsForBtc = numConnectionsForBtc; - this.context = new Context(params); + /** + * Creates a new WalletConfig, with the given {@link Context}. Files will be stored in the given directory. + */ + public WalletConfig(Context context, + File directory, String filePrefix) { + this.context = context; this.params = checkNotNull(context.getParams()); this.directory = checkDir(directory); - this.btcWalletFileName = checkNotNull(btcWalletFileName); - this.bsqWalletFileName = bsqWalletFileName; - this.spvChainFileName = spvChainFileName; + this.filePrefix = checkNotNull(filePrefix); + } + + public WalletConfig setSocks5Proxy(Socks5Proxy socks5Proxy) { + checkState(state() == State.NEW, "Cannot call after startup"); this.socks5Proxy = socks5Proxy; + return this; + } - walletFactory = new BisqWalletFactory() { - @Override - public Wallet create(NetworkParameters params, KeyChainGroup keyChainGroup) { - // This is called when we load an existing wallet - // We have already the chain here so we can use this to distinguish. - List deterministicKeyChains = keyChainGroup.getDeterministicKeyChains(); - if (!deterministicKeyChains.isEmpty() && deterministicKeyChains.get(0) instanceof BisqDeterministicKeyChain) { - return new BsqWallet(params, keyChainGroup); - } else { - return new Wallet(params, keyChainGroup); - } - } + public WalletConfig setConfig(Config config) { + checkState(state() == State.NEW, "Cannot call after startup"); + this.config = config; + return this; + } - @Override - public Wallet create(NetworkParameters params, KeyChainGroup keyChainGroup, boolean isBsqWallet) { - // This is called at first startup when we create the wallet - if (isBsqWallet) { - return new BsqWallet(params, keyChainGroup); - } else { - return new Wallet(params, keyChainGroup); - } - } - }; + public WalletConfig setLocalBitcoinNode(LocalBitcoinNode localBitcoinNode) { + checkState(state() == State.NEW, "Cannot call after startup"); + this.localBitcoinNode = localBitcoinNode; + return this; + } - String path = null; - if (params.equals(MainNetParams.get())) { - // Checkpoints are block headers that ship inside our app: for a new user, we pick the last header - // in the checkpoints file and then download the rest from the network. It makes things much faster. - // Checkpoint files are made using the BuildCheckpoints tool and usually we have to download the - // last months worth or more (takes a few seconds). - path = "/wallet/checkpoints.txt"; - } else if (params.equals(TestNet3Params.get())) { - path = "/wallet/checkpoints.testnet.txt"; - } - if (path != null) { - try { - setCheckpoints(getClass().getResourceAsStream(path)); - } catch (Exception e) { - e.printStackTrace(); - log.error(e.toString()); - } + public WalletConfig setNumConnectionsForBtc(int numConnectionsForBtc) { + checkState(state() == State.NEW, "Cannot call after startup"); + this.numConnectionsForBtc = numConnectionsForBtc; + return this; + } + + + /** Will only connect to the given addresses. Cannot be called after startup. */ + public WalletConfig setPeerNodes(PeerAddress... addresses) { + checkState(state() == State.NEW, "Cannot call after startup"); + this.peerAddresses = addresses; + return this; + } + + /** Will only connect to localhost. Cannot be called after startup. */ + public WalletConfig connectToLocalHost() { + final InetAddress localHost = InetAddress.getLoopbackAddress(); + return setPeerNodes(new PeerAddress(params, localHost, params.getPort())); + } + + /** If true, the wallet will save itself to disk automatically whenever it changes. */ + public WalletConfig setAutoSave(boolean value) { + checkState(state() == State.NEW, "Cannot call after startup"); + useAutoSave = value; + return this; + } + + /** + * If you want to learn about the sync process, you can provide a listener here. For instance, a + * {@link DownloadProgressTracker} is a good choice. This has no effect unless setBlockingStartup(false) has been called + * too, due to some missing implementation code. + */ + public WalletConfig setDownloadListener(DownloadProgressTracker listener) { + this.downloadListener = listener; + return this; + } + + /** If true, will register a shutdown hook to stop the library. Defaults to true. */ + public WalletConfig setAutoStop(boolean autoStop) { + this.autoStop = autoStop; + return this; + } + + /** + * If set, the file is expected to contain a checkpoints file calculated with BuildCheckpoints. It makes initial + * block sync faster for new users - please refer to the documentation on the bitcoinj website + * (https://bitcoinj.github.io/speeding-up-chain-sync) for further details. + */ + public WalletConfig setCheckpoints(InputStream checkpoints) { + if (this.checkpoints != null) + Closeables.closeQuietly(checkpoints); + this.checkpoints = checkNotNull(checkpoints); + return this; + } + + /** + * If true (the default) then the startup of this service won't be considered complete until the network has been + * brought up, peer connections established and the block chain synchronised. Therefore {@link #awaitRunning()} can + * potentially take a very long time. If false, then startup is considered complete once the network activity + * begins and peer connections/block chain sync will continue in the background. + */ + public WalletConfig setBlockingStartup(boolean blockingStartup) { + this.blockingStartup = blockingStartup; + return this; + } + + /** + * Sets the string that will appear in the subver field of the version message. + * @param userAgent A short string that should be the name of your app, e.g. "My Wallet" + * @param version A short string that contains the version number, e.g. "1.0-BETA" + */ + public WalletConfig setUserAgent(String userAgent, String version) { + this.userAgent = checkNotNull(userAgent); + this.version = checkNotNull(version); + return this; + } + + /** + * Sets a wallet factory which will be used when the kit creates a new wallet. + */ + public WalletConfig setWalletFactory(WalletProtobufSerializer.WalletFactory walletFactory) { + this.walletFactory = walletFactory; + return this; + } + + /** + * If a seed is set here then any existing wallet that matches the file name will be renamed to a backup name, + * the chain file will be deleted, and the wallet object will be instantiated with the given seed instead of + * a fresh one being created. This is intended for restoring a wallet from the original seed. To implement restore + * you would shut down the existing appkit, if any, then recreate it with the seed given by the user, then start + * up the new kit. The next time your app starts it should work as normal (that is, don't keep calling this each + * time). + */ + public WalletConfig restoreWalletFromSeed(DeterministicSeed seed) { + this.restoreFromSeed = seed; + return this; + } + + /** + * If an account key is set here then any existing wallet that matches the file name will be renamed to a backup name, + * the chain file will be deleted, and the wallet object will be instantiated with the given key instead of + * a fresh seed being created. This is intended for restoring a wallet from an account key. To implement restore + * you would shut down the existing appkit, if any, then recreate it with the key given by the user, then start + * up the new kit. The next time your app starts it should work as normal (that is, don't keep calling this each + * time). + */ + public WalletConfig restoreWalletFromKey(DeterministicKey accountKey) { + this.restoreFromKey = accountKey; + return this; + } + + /** + * Sets the peer discovery class to use. If none is provided then DNS is used, which is a reasonable default. + */ + public WalletConfig setDiscovery(@Nullable PeerDiscovery discovery) { + this.discovery = discovery; + return this; + } + + /** + *

Override this to return wallet extensions if any are necessary.

+ * + *

When this is called, chain(), store(), and peerGroup() will return the created objects, however they are not + * initialized/started.

+ */ + protected List provideWalletExtensions() throws Exception { + return ImmutableList.of(); + } + + /** + * This method is invoked on a background thread after all objects are initialised, but before the peer group + * or block chain download is started. You can tweak the objects configuration here. + */ + protected void onSetupCompleted() { + // Meant to be overridden by subclasses + } + + /** + * Tests to see if the spvchain file has an operating system file lock on it. Useful for checking if your app + * is already running. If another copy of your app is running and you start the appkit anyway, an exception will + * be thrown during the startup process. Returns false if the chain file does not exist or is a directory. + */ + public boolean isChainFileLocked() throws IOException { + RandomAccessFile file2 = null; + try { + File file = new File(directory, filePrefix + ".spvchain"); + if (!file.exists()) + return false; + if (file.isDirectory()) + return false; + file2 = new RandomAccessFile(file, "rw"); + FileLock lock = file2.getChannel().tryLock(); + if (lock == null) + return true; + lock.release(); + return false; + } finally { + if (file2 != null) + file2.close(); } } + @Override + protected void startUp() throws Exception { + // Runs in a separate thread. + Context.propagate(context); + // bitcoinj's WalletAppKit creates the wallet directory if it was not created already, + // but WalletConfig should not do that. + // if (!directory.exists()) { + // if (!directory.mkdirs()) { + // throw new IOException("Could not create directory " + directory.getAbsolutePath()); + // } + // } + log.info("Starting up with directory = {}", directory); + try { + File chainFile = new File(directory, filePrefix + ".spvchain"); + boolean chainFileExists = chainFile.exists(); + String btcPrefix = "_BTC"; + vBtcWalletFile = new File(directory, filePrefix + btcPrefix + ".wallet"); + boolean shouldReplayWallet = (vBtcWalletFile.exists() && !chainFileExists) || restoreFromSeed != null || restoreFromKey != null; + vBtcWallet = createOrLoadWallet(shouldReplayWallet, vBtcWalletFile, false); + vBtcWallet.allowSpendingUnconfirmedTransactions(); + vBtcWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer()); + + String bsqPrefix = "_BSQ"; + vBsqWalletFile = new File(directory, filePrefix + bsqPrefix + ".wallet"); + vBsqWallet = createOrLoadWallet(shouldReplayWallet, vBsqWalletFile, true); + vBsqWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer()); + + // Initiate Bitcoin network objects (block store, blockchain and peer group) + vStore = new SPVBlockStore(params, chainFile); + if (!chainFileExists || restoreFromSeed != null || restoreFromKey != null) { + if (checkpoints == null && !Utils.isAndroidRuntime()) { + checkpoints = CheckpointManager.openStream(params); + } + + if (checkpoints != null) { + // Initialize the chain file with a checkpoint to speed up first-run sync. + long time; + if (restoreFromSeed != null) { + time = restoreFromSeed.getCreationTimeSeconds(); + if (chainFileExists) { + log.info("Clearing the chain file in preparation for restore."); + vStore.clear(); + } + } else if (restoreFromKey != null) { + time = restoreFromKey.getCreationTimeSeconds(); + if (chainFileExists) { + log.info("Clearing the chain file in preparation for restore."); + vStore.clear(); + } + } else { + time = vBtcWallet.getEarliestKeyCreationTime(); + } + if (time > 0) + CheckpointManager.checkpoint(params, checkpoints, vStore, time); + else + log.warn("Creating a new uncheckpointed block store due to a wallet with a creation time of zero: this will result in a very slow chain sync"); + } else if (chainFileExists) { + log.info("Clearing the chain file in preparation for restore."); + vStore.clear(); + } + } + vChain = new BlockChain(params, vStore); + vPeerGroup = createPeerGroup(); + if (minBroadcastConnections > 0 ) + vPeerGroup.setMinBroadcastConnections(minBroadcastConnections); + if (this.userAgent != null) + vPeerGroup.setUserAgent(userAgent, version); + + // Set up peer addresses or discovery first, so if wallet extensions try to broadcast a transaction + // before we're actually connected the broadcast waits for an appropriate number of connections. + if (peerAddresses != null) { + for (PeerAddress addr : peerAddresses) vPeerGroup.addAddress(addr); + int maxConnections = Math.min(numConnectionsForBtc, peerAddresses.length); + log.info("We try to connect to {} btc nodes", maxConnections); + vPeerGroup.setMaxConnections(maxConnections); + vPeerGroup.setAddPeersFromAddressMessage(false); + peerAddresses = null; + } else if (!params.getId().equals(NetworkParameters.ID_REGTEST)) { + vPeerGroup.addPeerDiscovery(discovery != null ? discovery : new DnsDiscovery(params)); + } + vChain.addWallet(vBtcWallet); + vPeerGroup.addWallet(vBtcWallet); + vChain.addWallet(vBsqWallet); + vPeerGroup.addWallet(vBsqWallet); + onSetupCompleted(); + + if (blockingStartup) { + vPeerGroup.start(); + // Make sure we shut down cleanly. + installShutdownHook(); + //completeExtensionInitiations(vPeerGroup); + + // TODO: Be able to use the provided download listener when doing a blocking startup. + final DownloadProgressTracker listener = new DownloadProgressTracker(); + vPeerGroup.startBlockChainDownload(listener); + listener.await(); + } else { + Futures.addCallback((ListenableFuture) vPeerGroup.startAsync(), new FutureCallback() { + @Override + public void onSuccess(@Nullable Object result) { + //completeExtensionInitiations(vPeerGroup); + final DownloadProgressTracker l = downloadListener == null ? new DownloadProgressTracker() : downloadListener; + vPeerGroup.startBlockChainDownload(l); + } + + @Override + public void onFailure(Throwable t) { + throw new RuntimeException(t); + + } + }, MoreExecutors.directExecutor()); + } + } catch (BlockStoreException e) { + throw new IOException(e); + } + } + + private Wallet createOrLoadWallet(boolean shouldReplayWallet, File walletFile, boolean isBsqWallet) throws Exception { + Wallet wallet; + + maybeMoveOldWalletOutOfTheWay(walletFile); + + if (walletFile.exists()) { + wallet = loadWallet(shouldReplayWallet, walletFile, isBsqWallet); + } else { + wallet = createWallet(isBsqWallet); + wallet.freshReceiveKey(); + for (WalletExtension e : provideWalletExtensions()) { + wallet.addExtension(e); + } + + // Currently the only way we can be sure that an extension is aware of its containing wallet is by + // deserializing the extension (see WalletExtension#deserializeWalletExtension(Wallet, byte[])) + // Hence, we first save and then load wallet to ensure any extensions are correctly initialized. + wallet.saveToFile(walletFile); + wallet = loadWallet(false, walletFile, isBsqWallet); + } + + if (useAutoSave) { + this.setupAutoSave(wallet, walletFile); + } + + return wallet; + } + + protected void setupAutoSave(Wallet wallet, File walletFile) { + wallet.autosaveToFile(walletFile, 5, TimeUnit.SECONDS, null); + } + + private Wallet loadWallet(boolean shouldReplayWallet, File walletFile, boolean isBsqWallet) throws Exception { + Wallet wallet; + FileInputStream walletStream = new FileInputStream(walletFile); + try { + List extensions = provideWalletExtensions(); + WalletExtension[] extArray = extensions.toArray(new WalletExtension[extensions.size()]); + Protos.Wallet proto = WalletProtobufSerializer.parseToProto(walletStream); + final WalletProtobufSerializer serializer; + if (walletFactory != null) + serializer = new WalletProtobufSerializer(walletFactory); + else + serializer = new WalletProtobufSerializer(); + // Hack to convert bitcoinj 0.14 wallets to bitcoinj 0.15 format + serializer.setKeyChainFactory(new BisqKeyChainFactory(isBsqWallet)); + wallet = serializer.readWallet(params, extArray, proto); + if (shouldReplayWallet) + wallet.reset(); + } finally { + walletStream.close(); + } + return wallet; + } + + protected Wallet createWallet(boolean isBsqWallet) { + // Change preferredOutputScriptType of btc wallet to P2WPKH to start using segwit + // Script.ScriptType preferredOutputScriptType = isBsqWallet ? Script.ScriptType.P2PKH : Script.ScriptType.P2WPKH; + Script.ScriptType preferredOutputScriptType = Script.ScriptType.P2PKH; + KeyChainGroupStructure structure = new BisqKeyChainGroupStructure(isBsqWallet); + KeyChainGroup.Builder kcg = KeyChainGroup.builder(params, structure); + if (restoreFromSeed != null) { + kcg.fromSeed(restoreFromSeed, preferredOutputScriptType).build(); + } else if (restoreFromKey != null) { + kcg.addChain(DeterministicKeyChain.builder().spend(restoreFromKey).outputScriptType(preferredOutputScriptType).build()); + } else { + // new wallet + if (!isBsqWallet) { + // btc wallet uses a new random seed. + kcg.fromRandom(preferredOutputScriptType); + } else { + // bsq wallet uses btc wallet's seed created a few milliseconds ago. + kcg.fromSeed(vBtcWallet.getKeyChainSeed(), preferredOutputScriptType); + } + } + if (walletFactory != null) { + return walletFactory.create(params, kcg.build()); + } else { + return new Wallet(params, kcg.build()); // default + } + } + + private void maybeMoveOldWalletOutOfTheWay(File walletFile) { + if (restoreFromSeed == null && restoreFromKey == null) return; + if (!walletFile.exists()) return; + int counter = 1; + File newName; + do { + newName = new File(walletFile.getParent(), "Backup " + counter + " for " + walletFile.getName()); + counter++; + } while (newName.exists()); + log.info("Renaming old wallet file {} to {}", walletFile, newName); + if (!walletFile.renameTo(newName)) { + // This should not happen unless something is really messed up. + throw new RuntimeException("Failed to rename wallet for restore"); + } + } + + /* + * As soon as the transaction broadcaster han been created we will pass it to the + * payment channel extensions + */ + // private void completeExtensionInitiations(TransactionBroadcaster transactionBroadcaster) { + // StoredPaymentChannelClientStates clientStoredChannels = (StoredPaymentChannelClientStates) + // vWallet.getExtensions().get(StoredPaymentChannelClientStates.class.getName()); + // if(clientStoredChannels != null) { + // clientStoredChannels.setTransactionBroadcaster(transactionBroadcaster); + // } + // StoredPaymentChannelServerStates serverStoredChannels = (StoredPaymentChannelServerStates) + // vWallet.getExtensions().get(StoredPaymentChannelServerStates.class.getName()); + // if(serverStoredChannels != null) { + // serverStoredChannels.setTransactionBroadcaster(transactionBroadcaster); + // } + // } + private PeerGroup createPeerGroup() { PeerGroup peerGroup; // no proxy case. @@ -244,322 +573,17 @@ public class WalletConfig extends AbstractIdleService { return peerGroup; } - /** - * Will only connect to the given addresses. Cannot be called after startup. - */ - public WalletConfig setPeerNodes(PeerAddress... addresses) { - checkState(state() == State.NEW, "Cannot call after startup"); - this.peerAddresses = addresses; - return this; - } - - - /** - * If true, the wallet will save itself to disk automatically whenever it changes. - */ - public WalletConfig setAutoSave(boolean value) { - checkState(state() == State.NEW, "Cannot call after startup"); - useAutoSave = value; - return this; - } - - public WalletConfig setDownloadListener(PeerDataEventListener listener) { - this.downloadListener = listener; - return this; - } - - /** - * If true, will register a shutdown hook to stop the library. Defaults to true. - */ - public WalletConfig setAutoStop(boolean autoStop) { - this.autoStop = autoStop; - return this; - } - - /** - * If set, the file is expected to contain a checkpoints file calculated with BuildCheckpoints. It makes initial - * block sync faster for new users - please refer to the documentation on the bitcoinj website for further details. - */ - private void setCheckpoints(InputStream checkpoints) { - if (this.checkpoints != null) - Utils.closeUnchecked(this.checkpoints); - this.checkpoints = checkNotNull(checkpoints); - } - - /** - * If true (the default) then the startup of this service won't be considered complete until the network has been - * brought up, peer connections established and the block chain synchronised. Therefore startAndWait() can - * potentially take a very long time. If false, then startup is considered complete once the network activity - * begins and peer connections/block chain sync will continue in the background. - */ - public WalletConfig setBlockingStartup(@SuppressWarnings("SameParameterValue") boolean blockingStartup) { - this.blockingStartup = blockingStartup; - return this; - } - - /** - * If a seed is set here then any existing wallet that matches the file name will be renamed to a backup name, - * the chain file will be deleted, and the wallet object will be instantiated with the given seed instead of - * a fresh one being created. This is intended for restoring a wallet from the original seed. To implement restore - * you would shut down the existing appkit, if any, then recreate it with the seed given by the user, then start - * up the new kit. The next time your app starts it should work as normal (that is, don't keep calling this each - * time). - */ - public WalletConfig setSeed(@Nullable DeterministicSeed seed) { - this.seed = seed; - return this; - } - - /** - * Sets the peer discovery class to use. If none is provided then DNS is used, which is a reasonable default. - */ - public WalletConfig setDiscovery(@Nullable PeerDiscovery discovery) { - this.discovery = discovery; - return this; - } - - /** - *

Override this to return wallet extensions if any are necessary.

- *

- *

When this is called, chain(), store(), and peerGroup() will return the created objects, however they are not - * initialized/started.

- */ - private List provideWalletExtensions() { - return ImmutableList.of(); - } - - /** - * This method is invoked on a background thread after all objects are initialised, but before the peer group - * or block chain download is started. You can tweak the objects configuration here. - */ - void onSetupCompleted() { - } - - /** - * Tests to see if the spvchain file has an operating system file lock on it. Useful for checking if your app - * is already running. If another copy of your app is running and you start the appkit anyway, an exception will - * be thrown during the startup process. Returns false if the chain file does not exist or is a directory. - */ - public boolean isChainFileLocked() throws IOException { - RandomAccessFile file2 = null; - try { - File file = new File(directory, spvChainFileName); - if (!file.exists()) - return false; - if (file.isDirectory()) - return false; - file2 = new RandomAccessFile(file, "rw"); - FileLock lock = file2.getChannel().tryLock(); - if (lock == null) - return true; - lock.release(); - return false; - } finally { - if (file2 != null) - file2.close(); - } - } - - @Override - protected void startUp() throws Exception { - // Runs in a separate thread. - Context.propagate(context); - log.info("Wallet directory: {}", directory); - try { - File chainFile = new File(directory, spvChainFileName); - boolean chainFileExists = chainFile.exists(); - - // BTC wallet - vBtcWalletFile = new File(directory, btcWalletFileName); - boolean shouldReplayWallet = (vBtcWalletFile.exists() && !chainFileExists) || seed != null; - BisqKeyChainGroup keyChainGroup; - if (seed != null) - keyChainGroup = new BisqKeyChainGroup(params, new BtcDeterministicKeyChain(seed), true); - else - keyChainGroup = new BisqKeyChainGroup(params, true); - vBtcWallet = createOrLoadWallet(vBtcWalletFile, shouldReplayWallet, keyChainGroup, false, seed); - - vBtcWallet.allowSpendingUnconfirmedTransactions(); - vBtcWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer()); - - if (seed != null) - keyChainGroup = new BisqKeyChainGroup(params, new BisqDeterministicKeyChain(seed), false); - else - keyChainGroup = new BisqKeyChainGroup(params, new BisqDeterministicKeyChain(vBtcWallet.getKeyChainSeed()), false); - - // BSQ wallet - vBsqWalletFile = new File(directory, bsqWalletFileName); - vBsqWallet = createOrLoadWallet(vBsqWalletFile, shouldReplayWallet, keyChainGroup, true, seed); - vBsqWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer()); - - // Initiate Bitcoin network objects (block store, blockchain and peer group) - vStore = new SPVBlockStore(params, chainFile); - if (!chainFileExists || seed != null) { - if (checkpoints != null) { - // Initialize the chain file with a checkpoint to speed up first-run sync. - long time; - - if (seed != null) { - // we created both wallets at the same time - time = seed.getCreationTimeSeconds(); - if (chainFileExists) { - log.info("Clearing the chain file in preparation from restore."); - vStore.clear(); - } - } else { - time = vBtcWallet.getEarliestKeyCreationTime(); - } - - - if (time > 0) - CheckpointManager.checkpoint(params, checkpoints, vStore, time); - else - log.warn("Creating a new uncheckpointed block store due to a wallet with a creation time of zero: this will result in a very slow chain sync"); - } else if (chainFileExists) { - log.info("Clearing the chain file in preparation from restore."); - vStore.clear(); + private void installShutdownHook() { + if (autoStop) Runtime.getRuntime().addShutdownHook(new Thread("WalletConfig ShutdownHook") { + @Override public void run() { + try { + WalletConfig.this.stopAsync(); + WalletConfig.this.awaitTerminated(); + } catch (Exception e) { + throw new RuntimeException(e); } } - vChain = new BlockChain(params, vStore); - vPeerGroup = createPeerGroup(); - - if (minBroadcastConnections > 0) - vPeerGroup.setMinBroadcastConnections(minBroadcastConnections); - - vPeerGroup.setUserAgent(userAgent, Version.VERSION); - - // Set up peer addresses or discovery first, so if wallet extensions try to broadcast a transaction - // before we're actually connected the broadcast waits for an appropriate number of connections. - if (peerAddresses != null) { - for (PeerAddress addr : peerAddresses) vPeerGroup.addAddress(addr); - int maxConnections = Math.min(numConnectionsForBtc, peerAddresses.length); - log.info("We try to connect to {} btc nodes", maxConnections); - vPeerGroup.setMaxConnections(maxConnections); - vPeerGroup.setAddPeersFromAddressMessage(false); - peerAddresses = null; - } else if (!params.equals(RegTestParams.get())) { - vPeerGroup.addPeerDiscovery(discovery != null ? discovery : new DnsDiscovery(params)); - } - vChain.addWallet(vBtcWallet); - vPeerGroup.addWallet(vBtcWallet); - - if (vBsqWallet != null) { - //noinspection ConstantConditions - vChain.addWallet(vBsqWallet); - //noinspection ConstantConditions - vPeerGroup.addWallet(vBsqWallet); - } - - onSetupCompleted(); - - if (blockingStartup) { - vPeerGroup.start(); - // Make sure we shut down cleanly. - installShutdownHook(); - - final DownloadProgressTracker listener = new DownloadProgressTracker(); - vPeerGroup.startBlockChainDownload(listener); - listener.await(); - } else { - Futures.addCallback((ListenableFuture) vPeerGroup.startAsync(), new FutureCallback() { - @Override - public void onSuccess(@Nullable Object result) { - final PeerDataEventListener listener = downloadListener == null ? - new DownloadProgressTracker() : downloadListener; - vPeerGroup.startBlockChainDownload(listener); - } - - @Override - public void onFailure(@NotNull Throwable t) { - throw new RuntimeException(t); - } - }); - } - } catch (BlockStoreException e) { - throw new IOException(e); - } - } - - void setPeerNodesForLocalHost() { - setPeerNodes(new PeerAddress(InetAddress.getLoopbackAddress(), params.getPort())); - } - - private Wallet createOrLoadWallet(File walletFile, - boolean shouldReplayWallet, - BisqKeyChainGroup keyChainGroup, - boolean isBsqWallet, - DeterministicSeed restoreFromSeed) - throws Exception { - - if (restoreFromSeed != null) - maybeMoveOldWalletOutOfTheWay(walletFile); - - Wallet wallet; - if (walletFile.exists()) { - wallet = loadWallet(walletFile, shouldReplayWallet, keyChainGroup.isUseBitcoinDeterministicKeyChain()); - } else { - wallet = createWallet(keyChainGroup, isBsqWallet); - wallet.freshReceiveKey(); - wallet.saveToFile(walletFile); - } - - if (useAutoSave) wallet.autosaveToFile(walletFile, 5, TimeUnit.SECONDS, null); - - return wallet; - } - - private void maybeMoveOldWalletOutOfTheWay(File vWalletFile) { - if (!vWalletFile.exists()) return; - - int counter = 1; - File newName; - do { - newName = new File(vWalletFile.getParent(), "Backup " + counter + " for " + vWalletFile.getName()); - counter++; - } while (newName.exists()); - - log.info("Renaming old wallet file {} to {}", vWalletFile, newName); - if (!vWalletFile.renameTo(newName)) { - // This should not happen unless something is really messed up. - throw new RuntimeException("Failed to rename wallet for restore"); - } - } - - private Wallet loadWallet(File walletFile, - boolean shouldReplayWallet, - boolean useBitcoinDeterministicKeyChain) throws Exception { - Wallet wallet; - try (FileInputStream walletStream = new FileInputStream(walletFile)) { - List extensions = provideWalletExtensions(); - WalletExtension[] extArray = extensions.toArray(new WalletExtension[extensions.size()]); - Protos.Wallet proto = WalletProtobufSerializer.parseToProto(walletStream); - final WalletProtobufSerializer serializer; - if (walletFactory != null) - serializer = new WalletProtobufSerializer(walletFactory); - else - serializer = new WalletProtobufSerializer(); - - serializer.setKeyChainFactory(new BisqKeyChainFactory(useBitcoinDeterministicKeyChain)); - wallet = serializer.readWallet(params, extArray, proto); - if (shouldReplayWallet) - wallet.reset(); - } - return wallet; - } - - private Wallet createWallet(BisqKeyChainGroup keyChainGroup, boolean isBsqWallet) { - checkNotNull(walletFactory, "walletFactory must not be null"); - return walletFactory.create(params, keyChainGroup, isBsqWallet); - } - - private void installShutdownHook() { - if (autoStop) Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - WalletConfig.this.stopAsync(); - WalletConfig.this.awaitTerminated(); - } catch (Throwable ignore) { - } - }, "WalletConfig ShutdownHook")); + }); } @Override @@ -608,13 +632,12 @@ public class WalletConfig extends AbstractIdleService { return vStore; } - public Wallet getBtcWallet() { + public Wallet btcWallet() { checkState(state() == State.STARTING || state() == State.RUNNING, "Cannot call until startup is complete"); return vBtcWallet; } - @Nullable - public Wallet getBsqWallet() { + public Wallet bsqWallet() { checkState(state() == State.STARTING || state() == State.RUNNING, "Cannot call until startup is complete"); return vBsqWallet; } diff --git a/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java index d569448457..1f3cc0aab3 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java @@ -35,6 +35,7 @@ import bisq.network.Socks5ProxyProvider; import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.config.Config; +import bisq.common.app.Version; import bisq.common.handlers.ExceptionHandler; import bisq.common.handlers.ResultHandler; import bisq.common.storage.FileUtil; @@ -48,7 +49,9 @@ import org.bitcoinj.core.PeerAddress; import org.bitcoinj.core.PeerGroup; import org.bitcoinj.core.RejectMessage; import org.bitcoinj.core.listeners.DownloadProgressTracker; +import org.bitcoinj.params.MainNetParams; import org.bitcoinj.params.RegTestParams; +import org.bitcoinj.params.TestNet3Params; import org.bitcoinj.utils.Threading; import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.Wallet; @@ -196,15 +199,8 @@ public class WalletsSetup { log.info("Socks5Proxy for bitcoinj: socks5Proxy=" + socks5Proxy); walletConfig = new WalletConfig(params, - socks5Proxy, walletDir, - config, - localBitcoinNode, - userAgent, - numConnectionsForBtc, - btcWalletFileName, - BSQ_WALLET_FILE_NAME, - SPV_CHAIN_FILE_NAME) { + "bisq") { @Override protected void onSetupCompleted() { //We are here in the btcj thread Thread[ STARTING,5,main] @@ -251,7 +247,7 @@ public class WalletsSetup { // Map to user thread UserThread.execute(() -> { - addressEntryList.onWalletReady(walletConfig.getBtcWallet()); + addressEntryList.onWalletReady(walletConfig.btcWallet()); timeoutTimer.stop(); setupCompletedHandlers.stream().forEach(Runnable::run); }); @@ -260,11 +256,31 @@ public class WalletsSetup { UserThread.runAfter(resultHandler::handleResult, 100, TimeUnit.MILLISECONDS); } }; + walletConfig.setSocks5Proxy(socks5Proxy); + walletConfig.setConfig(config); + walletConfig.setLocalBitcoinNode(localBitcoinNode); + walletConfig.setUserAgent(userAgent, Version.VERSION); + walletConfig.setNumConnectionsForBtc(numConnectionsForBtc); + + String checkpointsPath = null; + if (params.equals(MainNetParams.get())) { + // Checkpoints are block headers that ship inside our app: for a new user, we pick the last header + // in the checkpoints file and then download the rest from the network. It makes things much faster. + // Checkpoint files are made using the BuildCheckpoints tool and usually we have to download the + // last months worth or more (takes a few seconds). + checkpointsPath = "/wallet/checkpoints.txt"; + } else if (params.equals(TestNet3Params.get())) { + checkpointsPath = "/wallet/checkpoints.testnet.txt"; + } + if (checkpointsPath != null) { + walletConfig.setCheckpoints(getClass().getResourceAsStream(checkpointsPath)); + } + if (params == RegTestParams.get()) { walletConfig.setMinBroadcastConnections(1); if (regTestHost == RegTestHost.LOCALHOST) { - walletConfig.setPeerNodesForLocalHost(); + walletConfig.connectToLocalHost(); } else if (regTestHost == RegTestHost.REMOTE_HOST) { configPeerNodesForRegTestServer(); } else { @@ -279,7 +295,7 @@ public class WalletsSetup { } } else if (localBitcoinNode.shouldBeUsed()) { walletConfig.setMinBroadcastConnections(1); - walletConfig.setPeerNodesForLocalHost(); + walletConfig.connectToLocalHost(); } else { try { configPeerNodes(socks5Proxy); @@ -295,7 +311,7 @@ public class WalletsSetup { .setBlockingStartup(false); // If seed is non-null it means we are restoring from backup. - walletConfig.setSeed(seed); + walletConfig.restoreWalletFromSeed(seed); walletConfig.addListener(new Service.Listener() { @Override @@ -363,7 +379,7 @@ public class WalletsSetup { if (RegTestHost.HOST.endsWith(".onion")) { walletConfig.setPeerNodes(new PeerAddress(RegTestHost.HOST, params.getPort())); } else { - walletConfig.setPeerNodes(new PeerAddress(InetAddress.getByName(RegTestHost.HOST), params.getPort())); + walletConfig.setPeerNodes(new PeerAddress(params, InetAddress.getByName(RegTestHost.HOST), params.getPort())); } } catch (UnknownHostException e) { log.error(e.toString()); @@ -447,12 +463,12 @@ public class WalletsSetup { /////////////////////////////////////////////////////////////////////////////////////////// public Wallet getBtcWallet() { - return walletConfig.getBtcWallet(); + return walletConfig.btcWallet(); } @Nullable public Wallet getBsqWallet() { - return walletConfig.getBsqWallet(); + return walletConfig.bsqWallet(); } public NetworkParameters getParams() { diff --git a/core/src/main/java/bisq/core/btc/wallet/BisqRiskAnalysis.java b/core/src/main/java/bisq/core/btc/wallet/BisqRiskAnalysis.java index e419a6a23d..8b665c67c9 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BisqRiskAnalysis.java +++ b/core/src/main/java/bisq/core/btc/wallet/BisqRiskAnalysis.java @@ -38,12 +38,14 @@ import org.bitcoinj.core.Coin; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.ECKey.ECDSASignature; import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.SignatureDecodeException; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionInput; import org.bitcoinj.core.TransactionOutput; import org.bitcoinj.crypto.TransactionSignature; import org.bitcoinj.script.ScriptChunk; +import org.bitcoinj.script.ScriptPattern; import org.bitcoinj.wallet.RiskAnalysis; import org.bitcoinj.wallet.Wallet; @@ -113,6 +115,13 @@ public class BisqRiskAnalysis implements RiskAnalysis { if (tx.getConfidence().getSource() == TransactionConfidence.Source.SELF) return Result.OK; + // Commented out to accept replace-by-fee txs. + // // We consider transactions that opt into replace-by-fee at risk of double spending. + // if (tx.isOptInFullRBF()) { + // nonFinal = tx; + // return Result.NON_FINAL; + // } + // Relative time-locked transactions are risky too. We can't check the locks because usually we don't know the // spent outputs (to know when they were created). if (tx.hasRelativeLockTime()) { @@ -197,7 +206,7 @@ public class BisqRiskAnalysis implements RiskAnalysis { */ public static RuleViolation isOutputStandard(TransactionOutput output) { // OP_RETURN has usually output value zero, so we exclude that from the MIN_ANALYSIS_NONDUST_OUTPUT check - if (!output.getScriptPubKey().isOpReturn() + if (!ScriptPattern.isOpReturn(output.getScriptPubKey()) && output.getValue().compareTo(MIN_ANALYSIS_NONDUST_OUTPUT) < 0) return RuleViolation.DUST; for (ScriptChunk chunk : output.getScriptPubKey().getChunks()) { @@ -216,7 +225,7 @@ public class BisqRiskAnalysis implements RiskAnalysis { ECDSASignature signature; try { signature = ECKey.ECDSASignature.decodeFromDER(chunk.data); - } catch (RuntimeException x) { + } catch (SignatureDecodeException x) { // Doesn't look like a signature. signature = null; } @@ -269,11 +278,11 @@ public class BisqRiskAnalysis implements RiskAnalysis { @Override public String toString() { if (!analyzed) - return "Pending risk analysis for " + tx.getHashAsString(); + return "Pending risk analysis for " + tx.getTxId().toString(); else if (nonFinal != null) - return "Risky due to non-finality of " + nonFinal.getHashAsString(); + return "Risky due to non-finality of " + nonFinal.getTxId().toString(); else if (nonStandard != null) - return "Risky due to non-standard tx " + nonStandard.getHashAsString(); + return "Risky due to non-standard tx " + nonStandard.getTxId().toString(); else return "Non-risky"; } diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqCoinSelector.java b/core/src/main/java/bisq/core/btc/wallet/BsqCoinSelector.java index 838a388a3f..e6d122a6b8 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BsqCoinSelector.java +++ b/core/src/main/java/bisq/core/btc/wallet/BsqCoinSelector.java @@ -53,12 +53,12 @@ public class BsqCoinSelector extends BisqDefaultCoinSelector { return false; // If it is a normal confirmed BSQ output we use the default lookup at the daoState - if (daoStateService.isTxOutputSpendable(new TxOutputKey(parentTransaction.getHashAsString(), output.getIndex()))) + if (daoStateService.isTxOutputSpendable(new TxOutputKey(parentTransaction.getTxId().toString(), output.getIndex()))) return true; // It might be that it is an unconfirmed change output which we allow to be used for spending without requiring a confirmation. // We check if we have the output in the dao state, if so we have a confirmed but unspendable output (e.g. confiscated). - if (daoStateService.getTxOutput(new TxOutputKey(parentTransaction.getHashAsString(), output.getIndex())).isPresent()) + if (daoStateService.getTxOutput(new TxOutputKey(parentTransaction.getTxId().toString(), output.getIndex())).isPresent()) return false; // Only if it's not existing yet in the dao state (unconfirmed) we use our unconfirmedBsqChangeOutputList to 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 930a19e270..39eda3be0a 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java @@ -41,21 +41,19 @@ import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.BlockChain; import org.bitcoinj.core.Coin; -import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; +import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.ScriptException; +import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.script.ScriptException; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionInput; import org.bitcoinj.core.TransactionOutPoint; import org.bitcoinj.core.TransactionOutput; -import org.bitcoinj.script.Script; import org.bitcoinj.wallet.CoinSelection; import org.bitcoinj.wallet.CoinSelector; import org.bitcoinj.wallet.SendRequest; -import org.bitcoinj.wallet.Wallet; -import org.bitcoinj.wallet.listeners.AbstractWalletEventListener; import javax.inject.Inject; @@ -142,54 +140,41 @@ public class BsqWalletService extends WalletService implements DaoStateListener wallet = walletsSetup.getBsqWallet(); if (wallet != null) { wallet.setCoinSelector(bsqCoinSelector); - wallet.addEventListener(walletEventListener); - //noinspection deprecation - wallet.addEventListener(new AbstractWalletEventListener() { - @Override - public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { + wallet.addCoinsReceivedEventListener(walletEventListener); + wallet.addCoinsSentEventListener(walletEventListener); + wallet.addReorganizeEventListener(walletEventListener); + wallet.addTransactionConfidenceEventListener(walletEventListener); + + wallet.addCoinsReceivedEventListener((wallet, tx, prevBalance, newBalance) -> { + updateBsqWalletTransactions(); + }); + wallet.addCoinsSentEventListener((wallet, tx, prevBalance, newBalance) -> { + updateBsqWalletTransactions(); + }); + wallet.addReorganizeEventListener(wallet -> { + log.warn("onReorganize "); + updateBsqWalletTransactions(); + unconfirmedBsqChangeOutputListService.onReorganize(); + }); + wallet.addTransactionConfidenceEventListener((wallet, tx) -> { + // We are only interested in updates from unconfirmed txs and confirmed txs at the + // time when it gets into a block. Otherwise we would get called + // updateBsqWalletTransactions for each tx as the block depth changes for all. + if (tx != null && tx.getConfidence() != null && tx.getConfidence().getDepthInBlocks() <= 1 && + daoStateService.isParseBlockChainComplete()) { updateBsqWalletTransactions(); } - - @Override - public void onCoinsSent(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - updateBsqWalletTransactions(); - } - - @Override - public void onReorganize(Wallet wallet) { - log.warn("onReorganize "); - updateBsqWalletTransactions(); - unconfirmedBsqChangeOutputListService.onReorganize(); - } - - @Override - public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) { - // We are only interested in updates from unconfirmed txs and confirmed txs at the - // time when it gets into a block. Otherwise we would get called - // updateBsqWalletTransactions for each tx as the block depth changes for all. - if (tx != null && tx.getConfidence() != null && tx.getConfidence().getDepthInBlocks() <= 1 && - daoStateService.isParseBlockChainComplete()) { - updateBsqWalletTransactions(); - } - unconfirmedBsqChangeOutputListService.onTransactionConfidenceChanged(tx); - } - - @Override - public void onKeysAdded(List keys) { - updateBsqWalletTransactions(); - } - - @Override - public void onScriptsChanged(Wallet wallet, List