From b805a795ddfc67054ae11efb037dfa55b9cd2dc1 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 21 Apr 2017 15:20:32 -0500 Subject: [PATCH] Adjust code to work with BitcoinJ 0.14.4.1 --- .../java/io/bisq/common/util/Utilities.java | 7 -- .../io/bisq/core/btc/AddressEntryList.java | 2 +- .../io/bisq/core/btc/SeedPeersSocks5Dns.java | 4 +- .../core/btc/wallet/BisqKeyChainFactory.java | 6 +- .../core/btc/wallet/BisqKeyChainGroup.java | 8 +- .../btc/wallet/BsqDeterministicKeyChain.java | 18 +++- .../io/bisq/core/btc/wallet/BsqWallet.java | 62 +---------- .../core/btc/wallet/BsqWalletService.java | 6 +- ...BtcCompensationRequestFeeCoinSelector.java | 1 + .../btc/wallet/BtcDeterministicKeyChain.java | 14 +++ .../core/btc/wallet/BtcWalletService.java | 59 ++++++----- .../core/btc/wallet/TradeWalletService.java | 24 +++-- .../io/bisq/core/btc/wallet/WalletConfig.java | 82 ++++++-------- .../bisq/core/btc/wallet/WalletService.java | 18 ++-- .../bisq/core/btc/wallet/WalletsManager.java | 2 +- .../io/bisq/core/btc/wallet/WalletsSetup.java | 100 +++--------------- .../core/trade/protocol/ProcessModel.java | 21 +++- gui/pom.xml | 6 +- .../bisq/gui/components/AddressTextField.java | 6 +- .../dao/wallet/receive/BsqReceiveView.java | 57 ---------- .../gui/main/funds/deposit/DepositView.java | 6 +- .../funds/transactions/TransactionsView.java | 7 +- .../main/funds/withdrawal/WithdrawalView.java | 6 +- .../offer/createoffer/CreateOfferView.java | 5 +- .../main/offer/takeoffer/TakeOfferView.java | 7 +- .../main/java/io/bisq/gui/util/GUIUtil.java | 10 ++ .../gui/util/validation/params/IOPParams.java | 10 ++ .../util/validation/params/PivxParams.java | 10 ++ .../io/bisq/network/Socks5DnsDiscovery.java | 4 +- .../io/bisq/network/Socks5MultiDiscovery.java | 5 +- .../network/Socks5SeedOnionDiscovery.java | 4 +- pom.xml | 2 +- 32 files changed, 227 insertions(+), 352 deletions(-) diff --git a/common/src/main/java/io/bisq/common/util/Utilities.java b/common/src/main/java/io/bisq/common/util/Utilities.java index 4822df4104..89b4e97de7 100644 --- a/common/src/main/java/io/bisq/common/util/Utilities.java +++ b/common/src/main/java/io/bisq/common/util/Utilities.java @@ -395,13 +395,6 @@ public class Utilities { Thread.currentThread().setName(name + "-" + new Random().nextInt(10000)); } - public static void overwriteWithRandomBytes(byte[] bytes) { - Random random = new Random(); - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) random.nextInt(Integer.MAX_VALUE); - } - } - public static boolean isDirectory(String path) { return new File(path).isDirectory(); } diff --git a/core/src/main/java/io/bisq/core/btc/AddressEntryList.java b/core/src/main/java/io/bisq/core/btc/AddressEntryList.java index b564ca2b16..ca1fc77e71 100644 --- a/core/src/main/java/io/bisq/core/btc/AddressEntryList.java +++ b/core/src/main/java/io/bisq/core/btc/AddressEntryList.java @@ -27,8 +27,8 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; -import org.bitcoinj.core.Wallet; import org.bitcoinj.crypto.DeterministicKey; +import org.bitcoinj.wallet.Wallet; import java.util.ArrayList; import java.util.List; diff --git a/core/src/main/java/io/bisq/core/btc/SeedPeersSocks5Dns.java b/core/src/main/java/io/bisq/core/btc/SeedPeersSocks5Dns.java index 86d840bd5c..79a68e94ac 100644 --- a/core/src/main/java/io/bisq/core/btc/SeedPeersSocks5Dns.java +++ b/core/src/main/java/io/bisq/core/btc/SeedPeersSocks5Dns.java @@ -110,7 +110,9 @@ public class SeedPeersSocks5Dns implements PeerDiscovery { * Returns an array containing all the Bitcoin nodes within the list. */ @Override - public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException { + public InetSocketAddress[] getPeers(long services, long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException { + if (services != 0) + throw new PeerDiscoveryException("DNS seeds cannot filter by services: " + services); try { return allPeers(); } catch (UnknownHostException e) { diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BisqKeyChainFactory.java b/core/src/main/java/io/bisq/core/btc/wallet/BisqKeyChainFactory.java index 961c53b7f5..306d297cb8 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BisqKeyChainFactory.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BisqKeyChainFactory.java @@ -22,11 +22,7 @@ import org.bitcoinj.crypto.ChildNumber; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.HDUtils; import org.bitcoinj.crypto.KeyCrypter; -import org.bitcoinj.store.UnreadableWalletException; -import org.bitcoinj.wallet.DeterministicKeyChain; -import org.bitcoinj.wallet.DeterministicSeed; -import org.bitcoinj.wallet.KeyChainFactory; -import org.bitcoinj.wallet.Protos; +import org.bitcoinj.wallet.*; class BisqKeyChainFactory implements KeyChainFactory { private final boolean useBitcoinDeterministicKeyChain; diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BisqKeyChainGroup.java b/core/src/main/java/io/bisq/core/btc/wallet/BisqKeyChainGroup.java index 26ce294b3f..0008f0034c 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BisqKeyChainGroup.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BisqKeyChainGroup.java @@ -25,23 +25,20 @@ import java.security.SecureRandom; class BisqKeyChainGroup extends KeyChainGroup { private final boolean useBitcoinDeterministicKeyChain; - private final int lookaheadSize; public boolean isUseBitcoinDeterministicKeyChain() { return useBitcoinDeterministicKeyChain; } - public BisqKeyChainGroup(NetworkParameters params, boolean useBitcoinDeterministicKeyChain, int lookaheadSize) { + public BisqKeyChainGroup(NetworkParameters params, boolean useBitcoinDeterministicKeyChain) { super(params); this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain; - this.lookaheadSize = lookaheadSize; } - public BisqKeyChainGroup(NetworkParameters params, DeterministicKeyChain chain, boolean useBitcoinDeterministicKeyChain, int lookaheadSize) { + public BisqKeyChainGroup(NetworkParameters params, DeterministicKeyChain chain, boolean useBitcoinDeterministicKeyChain) { super(params, chain); this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain; - this.lookaheadSize = lookaheadSize; } @Override @@ -52,7 +49,6 @@ class BisqKeyChainGroup extends KeyChainGroup { @Override public void createAndActivateNewHDChain() { DeterministicKeyChain chain = useBitcoinDeterministicKeyChain ? new BtcDeterministicKeyChain(new SecureRandom()) : new BsqDeterministicKeyChain(new SecureRandom()); - chain.setLookaheadSize(lookaheadSize); addAndActivateHDChain(chain); } } diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BsqDeterministicKeyChain.java b/core/src/main/java/io/bisq/core/btc/wallet/BsqDeterministicKeyChain.java index 3804a0d8a3..c1087b663b 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BsqDeterministicKeyChain.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BsqDeterministicKeyChain.java @@ -25,6 +25,7 @@ import org.bitcoinj.wallet.DeterministicKeyChain; import org.bitcoinj.wallet.DeterministicSeed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.spongycastle.crypto.params.KeyParameter; import java.security.SecureRandom; @@ -33,8 +34,7 @@ class BsqDeterministicKeyChain extends DeterministicKeyChain { // See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki // https://github.com/satoshilabs/slips/blob/master/slip-0044.md - // We use 142 (0x8000008E) as coin_type for BSQ - // TODO register + // 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), @@ -56,6 +56,20 @@ class BsqDeterministicKeyChain extends DeterministicKeyChain { super(seed); } + + @Override + public DeterministicKeyChain toEncrypted(KeyCrypter keyCrypter, KeyParameter aesKey) { + return new BsqDeterministicKeyChain(keyCrypter, aesKey, this); + } + + protected DeterministicKeyChain makeKeyChainFromSeed(DeterministicSeed seed) { + return new BsqDeterministicKeyChain(seed); + } + + protected BsqDeterministicKeyChain(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/io/bisq/core/btc/wallet/BsqWallet.java b/core/src/main/java/io/bisq/core/btc/wallet/BsqWallet.java index 7ff6251e1b..26eeb5a495 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BsqWallet.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BsqWallet.java @@ -17,71 +17,13 @@ package io.bisq.core.btc.wallet; -import org.bitcoinj.core.*; +import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.wallet.KeyChainGroup; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; +import org.bitcoinj.wallet.Wallet; public class BsqWallet extends Wallet { - private static final Logger log = LoggerFactory.getLogger(BsqWallet.class); public BsqWallet(NetworkParameters params, KeyChainGroup keyChainGroup) { super(params, keyChainGroup); } - - /** - * Returns the spendable candidates from the {@link UTXOProvider} based on keys that the wallet contains. - * - * @return The list of candidates. - */ - @Override - protected List calculateAllSpendCandidatesFromUTXOProvider(boolean excludeImmatureCoinbases) { - checkState(lock.isHeldByCurrentThread()); - UTXOProvider utxoProvider = checkNotNull(vUTXOProvider, "No UTXO provider has been set"); - // We might get duplicate outputs from the provider and from our pending tx outputs - // To avoid duplicate entries we use a set. - Set candidates = new HashSet<>(); - try { - int chainHeight = utxoProvider.getChainHeadHeight(); - for (UTXO output : getStoredOutputsFromUTXOProvider()) { - boolean coinbase = output.isCoinbase(); - int depth = chainHeight - output.getHeight() + 1; // the current depth of the output (1 = same as head). - // Do not try and spend coinbases that were mined too recently, the protocol forbids it. - if (!excludeImmatureCoinbases || !coinbase || depth >= params.getSpendableCoinbaseDepth()) { - candidates.add(new FreeStandingTransactionOutput(params, output, chainHeight)); - } - } - } catch (UTXOProviderException e) { - throw new RuntimeException("UTXO provider error", e); - } - // We need to handle the pending transactions that we know about. - for (Transaction tx : pending.values()) { - // Remove the spent outputs. - for (TransactionInput input : tx.getInputs()) { - TransactionOutput connectedOutput = input.getConnectedOutput(); - if (connectedOutput != null && connectedOutput.isMine(this)) { - candidates.remove(connectedOutput); - } - } - // Add change outputs. Do not try and spend coinbases that were mined too recently, the protocol forbids it. - - // We might get outputs from pending tx which we already got form the UTXP provider. - // As we use a set it will not lead to duplicate entries. - if (!excludeImmatureCoinbases || tx.isMature()) { - candidates.addAll(tx.getOutputs().stream() - .filter(output -> output.isAvailableForSpending() && output.isMine(this)) - .collect(Collectors.toList())); - } - } - return new ArrayList<>(candidates); - } } diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java index 30f213d560..cf963b32f4 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java @@ -33,6 +33,9 @@ import lombok.extern.slf4j.Slf4j; import org.bitcoinj.core.*; import org.bitcoinj.script.Script; import org.bitcoinj.wallet.CoinSelection; +import org.bitcoinj.wallet.SendRequest; +import org.bitcoinj.wallet.Wallet; +import org.bitcoinj.wallet.listeners.AbstractWalletEventListener; import javax.inject.Inject; import java.util.HashSet; @@ -275,9 +278,10 @@ public class BsqWalletService extends WalletService { "The amount is too low (dust limit)."); tx.addOutput(receiverAmount, new Address(params, receiverAddress)); - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); + SendRequest sendRequest = SendRequest.forTx(tx); sendRequest.fee = Coin.ZERO; sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; sendRequest.aesKey = aesKey; sendRequest.shuffleOutputs = false; sendRequest.signInputs = false; diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcCompensationRequestFeeCoinSelector.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcCompensationRequestFeeCoinSelector.java index 7b768847cf..5212b71eaa 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcCompensationRequestFeeCoinSelector.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcCompensationRequestFeeCoinSelector.java @@ -21,6 +21,7 @@ import com.google.common.annotations.VisibleForTesting; import org.bitcoinj.core.*; import org.bitcoinj.params.RegTestParams; import org.bitcoinj.wallet.CoinSelection; +import org.bitcoinj.wallet.Wallet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcDeterministicKeyChain.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcDeterministicKeyChain.java index 3712b98806..209ea18f63 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcDeterministicKeyChain.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcDeterministicKeyChain.java @@ -25,6 +25,7 @@ import org.bitcoinj.wallet.DeterministicKeyChain; import org.bitcoinj.wallet.DeterministicSeed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.spongycastle.crypto.params.KeyParameter; import java.security.SecureRandom; @@ -56,6 +57,19 @@ class BtcDeterministicKeyChain extends DeterministicKeyChain { super(seed); } + @Override + public DeterministicKeyChain toEncrypted(KeyCrypter keyCrypter, KeyParameter aesKey) { + return new BtcDeterministicKeyChain(keyCrypter, aesKey, this); + } + + 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/io/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java index c2af0dc07e..18baf8d127 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java @@ -31,6 +31,8 @@ import org.bitcoinj.core.*; import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.script.ScriptBuilder; +import org.bitcoinj.wallet.SendRequest; +import org.bitcoinj.wallet.Wallet; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -201,14 +203,16 @@ public class BtcWalletService extends WalletService { tx.addOutput(forcedChangeValue, changeAddress); } - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); + SendRequest sendRequest = SendRequest.forTx(tx); sendRequest.shuffleOutputs = false; sendRequest.aesKey = aesKey; // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet) sendRequest.signInputs = false; - sendRequest.ensureMinRequiredFee = false; - sendRequest.feePerKb = Coin.ZERO; + sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs); + sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; + sendRequest.coinSelector = coinSelector; sendRequest.changeAddress = changeAddress; wallet.completeTx(sendRequest); @@ -461,7 +465,7 @@ public class BtcWalletService extends WalletService { int counter = 0; int txSize = 0; Transaction tx; - Wallet.SendRequest sendRequest; + SendRequest sendRequest; Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte(); do { counter++; @@ -472,9 +476,10 @@ public class BtcWalletService extends WalletService { newTransaction.clearOutputs(); newTransaction.addOutput(amount.subtract(fee), toAddress); - sendRequest = Wallet.SendRequest.forTx(newTransaction); + sendRequest = SendRequest.forTx(newTransaction); sendRequest.fee = fee; sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; sendRequest.aesKey = aesKey; sendRequest.coinSelector = new BtcCoinSelector(toAddress); sendRequest.changeAddress = toAddress; @@ -491,9 +496,10 @@ public class BtcWalletService extends WalletService { Wallet.SendResult sendResult = null; try { - sendRequest = Wallet.SendRequest.forTx(newTransaction); + sendRequest = SendRequest.forTx(newTransaction); sendRequest.fee = fee; sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; sendRequest.aesKey = aesKey; sendRequest.coinSelector = new BtcCoinSelector(toAddress); sendRequest.changeAddress = toAddress; @@ -506,9 +512,10 @@ public class BtcWalletService extends WalletService { newTransaction.clearOutputs(); newTransaction.addOutput(amount, toAddress); - sendRequest = Wallet.SendRequest.forTx(newTransaction); + sendRequest = SendRequest.forTx(newTransaction); sendRequest.fee = fee; sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; sendRequest.aesKey = aesKey; sendRequest.coinSelector = new BtcCoinSelector(toAddress, false); sendRequest.changeAddress = toAddress; @@ -584,7 +591,7 @@ public class BtcWalletService extends WalletService { if (fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; - Wallet.SendRequest sendRequest = getSendRequest(fromAddress, toAddress, amount, fee, aesKey, context); + SendRequest sendRequest = getSendRequest(fromAddress, toAddress, amount, fee, aesKey, context); wallet.completeTx(sendRequest); tx = sendRequest.tx; txSize = tx.bitcoinSerialize().length; @@ -634,7 +641,7 @@ public class BtcWalletService extends WalletService { fee = txFeeForWithdrawalPerByte.multiply(txSize); if (fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; - Wallet.SendRequest sendRequest = getSendRequestForMultipleAddresses(fromAddresses, toAddress, amount, fee, null, aesKey); + SendRequest sendRequest = getSendRequestForMultipleAddresses(fromAddresses, toAddress, amount, fee, null, aesKey); wallet.completeTx(sendRequest); tx = sendRequest.tx; txSize = tx.bitcoinSerialize().length; @@ -664,7 +671,7 @@ public class BtcWalletService extends WalletService { AddressEntry.Context context, FutureCallback callback) throws AddressFormatException, AddressEntryException, InsufficientMoneyException { - Wallet.SendRequest sendRequest = getSendRequest(fromAddress, toAddress, receiverAmount, fee, aesKey, context); + SendRequest sendRequest = getSendRequest(fromAddress, toAddress, receiverAmount, fee, aesKey, context); Wallet.SendResult sendResult = wallet.sendCoins(sendRequest); Futures.addCallback(sendResult.broadcastComplete, callback); @@ -681,7 +688,7 @@ public class BtcWalletService extends WalletService { FutureCallback callback) throws AddressFormatException, AddressEntryException, InsufficientMoneyException { - Wallet.SendRequest request = getSendRequestForMultipleAddresses(fromAddresses, toAddress, receiverAmount, fee, changeAddress, aesKey); + SendRequest request = getSendRequestForMultipleAddresses(fromAddresses, toAddress, receiverAmount, fee, changeAddress, aesKey); Wallet.SendResult sendResult = wallet.sendCoins(request); Futures.addCallback(sendResult.broadcastComplete, callback); @@ -689,21 +696,22 @@ public class BtcWalletService extends WalletService { return sendResult.tx.getHashAsString(); } - private Wallet.SendRequest getSendRequest(String fromAddress, - String toAddress, - Coin amount, - Coin fee, - @Nullable KeyParameter aesKey, - AddressEntry.Context context) throws AddressFormatException, + private SendRequest getSendRequest(String fromAddress, + String toAddress, + Coin amount, + Coin fee, + @Nullable KeyParameter aesKey, + AddressEntry.Context context) throws AddressFormatException, AddressEntryException { Transaction tx = new Transaction(params); Preconditions.checkArgument(Restrictions.isAboveDust(amount, fee), "The amount is too low (dust limit)."); tx.addOutput(amount.subtract(fee), new Address(params, toAddress)); - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); + SendRequest sendRequest = SendRequest.forTx(tx); sendRequest.fee = fee; sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; sendRequest.aesKey = aesKey; sendRequest.shuffleOutputs = false; Optional addressEntry = findAddressEntry(fromAddress, context); @@ -717,21 +725,22 @@ public class BtcWalletService extends WalletService { return sendRequest; } - private Wallet.SendRequest getSendRequestForMultipleAddresses(Set fromAddresses, - String toAddress, - Coin amount, - Coin fee, - @Nullable String changeAddress, - @Nullable KeyParameter aesKey) throws + private SendRequest getSendRequestForMultipleAddresses(Set fromAddresses, + String toAddress, + Coin amount, + Coin fee, + @Nullable String changeAddress, + @Nullable KeyParameter aesKey) throws AddressFormatException, AddressEntryException { Transaction tx = new Transaction(params); checkArgument(Restrictions.isAboveDust(amount), "The amount is too low (dust limit)."); tx.addOutput(amount.subtract(fee), new Address(params, toAddress)); - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); + SendRequest sendRequest = SendRequest.forTx(tx); sendRequest.fee = fee; sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; sendRequest.aesKey = aesKey; sendRequest.shuffleOutputs = false; Set addressEntries = fromAddresses.stream() diff --git a/core/src/main/java/io/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/TradeWalletService.java index 4a4db72991..ab72fcd28a 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/TradeWalletService.java @@ -22,7 +22,6 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import io.bisq.common.app.Log; -import io.bisq.common.util.Utilities; import io.bisq.core.btc.AddressEntry; import io.bisq.core.btc.InsufficientFundsException; import io.bisq.core.btc.data.InputsAndChangeOutput; @@ -37,6 +36,8 @@ import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.TransactionSignature; import org.bitcoinj.script.Script; import org.bitcoinj.script.ScriptBuilder; +import org.bitcoinj.wallet.SendRequest; +import org.bitcoinj.wallet.Wallet; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -125,10 +126,6 @@ public class TradeWalletService { /////////////////////////////////////////////////////////////////////////////////////////// void setAesKey(@Nullable KeyParameter newAesKey) { - // Overwrite first with random bytes before setting to null - if (newAesKey == null && this.aesKey != null) - Utilities.overwriteWithRandomBytes(this.aesKey.getKey()); - this.aesKey = newAesKey; } @@ -176,7 +173,7 @@ public class TradeWalletService { // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to // wait for 1 confirmation) // In case of double spend we will detect later in the trade process and use a ban score to penalize bad behaviour (not impl. yet) - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tradingFeeTx); + SendRequest sendRequest = SendRequest.forTx(tradingFeeTx); sendRequest.shuffleOutputs = false; sendRequest.aesKey = aesKey; if (useSavingsWallet) @@ -184,8 +181,10 @@ public class TradeWalletService { else sendRequest.coinSelector = new BtcCoinSelector(fundingAddress); // We use a fixed fee - sendRequest.feePerKb = Coin.ZERO; + sendRequest.fee = txFee; + sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; // Change is optional in case of overpay or use of funds from savings wallet sendRequest.changeAddress = changeAddress; @@ -243,7 +242,7 @@ public class TradeWalletService { // In case of double spend we will detect later in the trade process and use a ban score to penalize bad behaviour (not impl. yet) // WalletService.printTx("preparedBsqTx", preparedBsqTx); - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(preparedBsqTx); + SendRequest sendRequest = SendRequest.forTx(preparedBsqTx); sendRequest.shuffleOutputs = false; sendRequest.aesKey = aesKey; if (useSavingsWallet) @@ -251,8 +250,10 @@ public class TradeWalletService { else sendRequest.coinSelector = new BtcCoinSelector(fundingAddress); // We use a fixed fee - sendRequest.feePerKb = Coin.ZERO; sendRequest.fee = txFee; + sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; + sendRequest.signInputs = false; // Change is optional in case of overpay or use of funds from savings wallet @@ -1166,12 +1167,13 @@ public class TradeWalletService { private void addAvailableInputsAndChangeOutputs(Transaction transaction, Address address, Address changeAddress, Coin txFee) throws WalletException { try { // Lets let the framework do the work to find the right inputs - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(transaction); + SendRequest sendRequest = SendRequest.forTx(transaction); sendRequest.shuffleOutputs = false; sendRequest.aesKey = aesKey; // We use a fixed fee - sendRequest.feePerKb = Coin.ZERO; sendRequest.fee = txFee; + sendRequest.feePerKb = Coin.ZERO; + sendRequest.ensureMinRequiredFee = false; // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to wait for 1 confirmation) sendRequest.coinSelector = new BtcCoinSelector(address); // We use always the same address in a trade for all transactions diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java index a06acbb0c5..ccf4e649c6 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java @@ -24,6 +24,8 @@ import com.google.common.util.concurrent.Futures; import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; import io.bisq.core.btc.ProxySocketFactory; import org.bitcoinj.core.*; +import org.bitcoinj.core.listeners.DownloadProgressTracker; +import org.bitcoinj.core.listeners.PeerDataEventListener; import org.bitcoinj.net.BlockingClientManager; import org.bitcoinj.net.discovery.DnsDiscovery; import org.bitcoinj.net.discovery.PeerDiscovery; @@ -33,11 +35,7 @@ import org.bitcoinj.params.TestNet3Params; import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.BlockStoreException; import org.bitcoinj.store.SPVBlockStore; -import org.bitcoinj.store.WalletProtobufSerializer; -import org.bitcoinj.wallet.DeterministicKeyChain; -import org.bitcoinj.wallet.DeterministicSeed; -import org.bitcoinj.wallet.KeyChainGroup; -import org.bitcoinj.wallet.Protos; +import org.bitcoinj.wallet.*; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -91,15 +89,13 @@ public class WalletConfig extends AbstractIdleService { private volatile File vBsqWalletFile; @Nullable private DeterministicSeed seed; - private int btcWalletLookaheadSize = -1; - private int bsqWalletLookaheadSize = -1; private volatile BlockChain vChain; private volatile BlockStore vStore; private volatile PeerGroup vPeerGroup; private boolean useAutoSave = true; private PeerAddress[] peerAddresses; - private PeerEventListener downloadListener; + private PeerDataEventListener downloadListener; private boolean autoStop = true; private InputStream checkpoints; private boolean blockingStartup = true; @@ -108,8 +104,6 @@ public class WalletConfig extends AbstractIdleService { private String version; @Nullable private PeerDiscovery discovery; - private long bloomFilterTweak = 0; - private double bloomFilterFPRate = -1; /////////////////////////////////////////////////////////////////////////////////////////// @@ -226,12 +220,7 @@ public class WalletConfig extends AbstractIdleService { return this; } - /** - * If you want to learn about the sync process, you can provide a listener here. For instance, a - * {@link org.bitcoinj.core.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(PeerEventListener listener) { + public WalletConfig setDownloadListener(PeerDataEventListener listener) { this.downloadListener = listener; return this; } @@ -298,26 +287,6 @@ public class WalletConfig extends AbstractIdleService { return this; } - public WalletConfig setBloomFilterFalsePositiveRate(double bloomFilterFPRate) { - this.bloomFilterFPRate = bloomFilterFPRate; - return this; - } - - public WalletConfig setBloomFilterTweak(long bloomFilterTweak) { - this.bloomFilterTweak = bloomFilterTweak; - return this; - } - - public WalletConfig setBtcWalletLookaheadSize(int lookaheadSize) { - this.btcWalletLookaheadSize = lookaheadSize; - return this; - } - - public WalletConfig setBsqWalletLookaheadSize(int lookaheadSize) { - this.bsqWalletLookaheadSize = lookaheadSize; - return this; - } - /** *

Override this to return wallet extensions if any are necessary.

*

@@ -388,18 +357,18 @@ public class WalletConfig extends AbstractIdleService { boolean shouldReplayWallet = (vBtcWalletFile.exists() && !chainFileExists) || seed != null; BisqKeyChainGroup keyChainGroup; if (seed != null) - keyChainGroup = new BisqKeyChainGroup(params, new BtcDeterministicKeyChain(seed), true, btcWalletLookaheadSize); + keyChainGroup = new BisqKeyChainGroup(params, new BtcDeterministicKeyChain(seed), true); else - keyChainGroup = new BisqKeyChainGroup(params, true, btcWalletLookaheadSize); - vBtcWallet = createOrLoadWallet(vBtcWalletFile, shouldReplayWallet, keyChainGroup, false); + keyChainGroup = new BisqKeyChainGroup(params, true); + vBtcWallet = createOrLoadWallet(vBtcWalletFile, shouldReplayWallet, keyChainGroup, false, seed); // BSQ walelt vBsqWalletFile = new File(directory, bsqWalletFileName); if (seed != null) - keyChainGroup = new BisqKeyChainGroup(params, new BsqDeterministicKeyChain(seed), false, bsqWalletLookaheadSize); + keyChainGroup = new BisqKeyChainGroup(params, new BsqDeterministicKeyChain(seed), false); else - keyChainGroup = new BisqKeyChainGroup(params, new BsqDeterministicKeyChain(vBtcWallet.getKeyChainSeed()), false, bsqWalletLookaheadSize); - vBsqWallet = createOrLoadWallet(vBsqWalletFile, shouldReplayWallet, keyChainGroup, true); + keyChainGroup = new BisqKeyChainGroup(params, new BsqDeterministicKeyChain(vBtcWallet.getKeyChainSeed()), false); + vBsqWallet = createOrLoadWallet(vBsqWalletFile, shouldReplayWallet, keyChainGroup, true, seed); // Initiate Bitcoin network objects (block store, blockchain and peer group) vStore = provideBlockStore(chainFile); @@ -438,12 +407,6 @@ public class WalletConfig extends AbstractIdleService { vChain = new BlockChain(params, vStore); vPeerGroup = createPeerGroup(); - if (bloomFilterFPRate != -1) - vPeerGroup.setBloomFilterFalsePositiveRate(bloomFilterFPRate); - - if (bloomFilterTweak != 0) - vPeerGroup.setBloomFilterTweak(bloomFilterTweak); - if (this.userAgent != null) vPeerGroup.setUserAgent(userAgent, version); @@ -475,7 +438,7 @@ public class WalletConfig extends AbstractIdleService { Futures.addCallback(vPeerGroup.startAsync(), new FutureCallback() { @Override public void onSuccess(@Nullable Object result) { - final PeerEventListener listener = downloadListener == null ? + final PeerDataEventListener listener = downloadListener == null ? new DownloadProgressTracker() : downloadListener; vPeerGroup.startBlockChainDownload(listener); } @@ -493,8 +456,11 @@ public class WalletConfig extends AbstractIdleService { } private Wallet createOrLoadWallet(File walletFile, boolean shouldReplayWallet, - BisqKeyChainGroup keyChainGroup, boolean isBsqWallet) + BisqKeyChainGroup keyChainGroup, boolean isBsqWallet, DeterministicSeed seed) throws Exception { + + maybeMoveOldWalletOutOfTheWay(walletFile, seed); + Wallet wallet; if (walletFile.exists()) { wallet = loadWallet(walletFile, shouldReplayWallet, keyChainGroup.isUseBitcoinDeterministicKeyChain()); @@ -509,6 +475,22 @@ public class WalletConfig extends AbstractIdleService { return wallet; } + private void maybeMoveOldWalletOutOfTheWay(File vWalletFile, DeterministicSeed restoreFromSeed) { + if (restoreFromSeed == null) return; + 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; FileInputStream walletStream = new FileInputStream(walletFile); diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java index 986dc63f63..eb862c84b9 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java @@ -21,7 +21,6 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import io.bisq.common.handlers.ErrorMessageHandler; import io.bisq.common.handlers.ResultHandler; -import io.bisq.common.util.Utilities; import io.bisq.core.btc.exceptions.TransactionVerificationException; import io.bisq.core.btc.exceptions.WalletException; import io.bisq.core.btc.listeners.AddressConfidenceListener; @@ -38,6 +37,8 @@ import org.bitcoinj.script.Script; import org.bitcoinj.signers.TransactionSigner; import org.bitcoinj.utils.Threading; import org.bitcoinj.wallet.*; +import org.bitcoinj.wallet.listeners.AbstractWalletEventListener; +import org.bitcoinj.wallet.listeners.WalletEventListener; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -104,8 +105,6 @@ public abstract class WalletService { void decryptWallet(@NotNull KeyParameter key) { wallet.decrypt(key); - // Overwrite first with random bytes before setting to null - Utilities.overwriteWithRandomBytes(key.getKey()); aesKey = null; } @@ -313,7 +312,7 @@ public abstract class WalletService { List transactionConfidenceList = getOutputsWithConnectedOutputs(tx) .stream() .filter(WalletUtils::isOutputScriptConvertableToAddress) - .filter(output -> address.equals(WalletUtils.getAddressFromOutput(output))) + .filter(output -> address != null && address.equals(WalletUtils.getAddressFromOutput(output))) .map(o -> tx.getConfidence()) .collect(Collectors.toList()); return getMostRecentConfidence(transactionConfidenceList); @@ -375,6 +374,7 @@ public abstract class WalletService { Coin balance = Coin.ZERO; for (TransactionOutput output : transactionOutputs) { if (WalletUtils.isOutputScriptConvertableToAddress(output) && + address != null && address.equals(WalletUtils.getAddressFromOutput(output))) balance = balance.add(output.getValue()); } @@ -387,6 +387,7 @@ public abstract class WalletService { int outputs = 0; for (TransactionOutput output : transactionOutputs) { if (WalletUtils.isOutputScriptConvertableToAddress(output) && + address != null && address.equals(WalletUtils.getAddressFromOutput(output))) outputs++; } @@ -408,7 +409,8 @@ public abstract class WalletService { public void emptyWallet(String toAddress, KeyParameter aesKey, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) throws InsufficientMoneyException, AddressFormatException { - Wallet.SendRequest sendRequest = Wallet.SendRequest.emptyWallet(new Address(params, toAddress)); + SendRequest sendRequest = SendRequest.emptyWallet(Address.fromBase58(params, toAddress)); + sendRequest.fee = Coin.ZERO; sendRequest.feePerKb = getTxFeeForWithdrawalPerByte().multiply(1000); sendRequest.aesKey = aesKey; Wallet.SendResult sendResult = wallet.sendCoins(sendRequest); @@ -550,11 +552,7 @@ public abstract class WalletService { /////////////////////////////////////////////////////////////////////////////////////////// public static void printTx(String tracePrefix, Transaction tx) { - int size = tx.bitcoinSerialize().length; - log.info("\n" + tracePrefix + ":\n" + - tx.toString() + - "Satoshi/byte: " + (tx.getFee() != null ? tx.getFee().value / size : "No fee set yet") + - " (size: " + size + ")"); + log.info("\n" + tracePrefix + ":\n" + tx.toString()); } diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletsManager.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletsManager.java index 197cb74dab..3e33e1358f 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletsManager.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletsManager.java @@ -21,10 +21,10 @@ import com.google.inject.Inject; import io.bisq.common.handlers.ExceptionHandler; import io.bisq.common.handlers.ResultHandler; import io.bisq.core.crypto.ScryptUtil; -import org.bitcoinj.core.Wallet; import org.bitcoinj.crypto.KeyCrypter; import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.wallet.DeterministicSeed; +import org.bitcoinj.wallet.Wallet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.spongycastle.crypto.params.KeyParameter; diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java index b0a8f2bdda..11ddc5403b 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java @@ -40,9 +40,11 @@ import io.bisq.network.Socks5ProxyProvider; import javafx.beans.property.*; import org.apache.commons.lang3.StringUtils; import org.bitcoinj.core.*; +import org.bitcoinj.core.listeners.DownloadProgressTracker; import org.bitcoinj.params.RegTestParams; import org.bitcoinj.utils.Threading; import org.bitcoinj.wallet.DeterministicSeed; +import org.bitcoinj.wallet.Wallet; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -105,7 +107,6 @@ public class WalletsSetup { PersistenceProtoResolver persistenceProtoResolver, @Named(BtcOptionKeys.WALLET_DIR) File appDir, @Named(BtcOptionKeys.SOCKS5_DISCOVER_MODE) String socks5DiscoverModeString) { - this.regTestHost = regTestHost; this.addressEntryList = addressEntryList; this.userAgent = userAgent; @@ -117,6 +118,7 @@ public class WalletsSetup { WalletUtils.setBitcoinNetwork(bisqEnvironment.getBitcoinNetwork()); params = WalletUtils.getParameters(); walletDir = new File(appDir, "bitcoin"); + PeerGroup.setIgnoreHttpSeeds(true); storage = new Storage<>(walletDir, persistenceProtoResolver); LongPersistable nonce = storage.initAndGetPersistedWithFileName("BloomFilterNonce"); @@ -152,7 +154,8 @@ public class WalletsSetup { final Socks5Proxy socks5Proxy = preferences.getUseTorForBitcoinJ() ? socks5ProxyProvider.getSocks5Proxy() : null; log.debug("Use socks5Proxy for bitcoinj: " + socks5Proxy); - walletConfig = new WalletConfig(params, socks5Proxy, walletDir, BTC_WALLET_FILE_NAME, BSQ_WALLET_FILE_NAME, SPV_CHAIN_FILE_NAME) { + walletConfig = new WalletConfig(params, socks5Proxy, walletDir, BTC_WALLET_FILE_NAME, + BSQ_WALLET_FILE_NAME, SPV_CHAIN_FILE_NAME) { @Override protected void onSetupCompleted() { //We are here in the btcj thread Thread[ STARTING,5,main] @@ -164,47 +167,15 @@ public class WalletsSetup { if (preferences.getBitcoinNodes() != null && !preferences.getBitcoinNodes().isEmpty()) peerGroup.setAddPeersFromAddressMessage(false); - peerGroup.addEventListener(new PeerEventListener() { - @Override - public void onPeersDiscovered(Set peerAddresses) { - } - - @Override - public void onBlocksDownloaded(Peer peer, Block block, FilteredBlock filteredBlock, int blocksLeft) { - } - - @Override - public void onChainDownloadStarted(Peer peer, int blocksLeft) { - } - - @Override - public void onPeerConnected(Peer peer, int peerCount) { - // We get called here on our user thread - numPeers.set(peerCount); - connectedPeers.set(peerGroup.getConnectedPeers()); - } - - @Override - public void onPeerDisconnected(Peer peer, int peerCount) { - // We get called here on our user thread - numPeers.set(peerCount); - connectedPeers.set(peerGroup.getConnectedPeers()); - } - - @Override - public Message onPreMessageReceived(Peer peer, Message m) { - return null; - } - - @Override - public void onTransaction(Peer peer, Transaction t) { - } - - @Nullable - @Override - public List getData(Peer peer, GetDataMessage m) { - return null; - } + peerGroup.addConnectedEventListener((peer, peerCount) -> { + // We get called here on our user thread + numPeers.set(peerCount); + connectedPeers.set(peerGroup.getConnectedPeers()); + }); + peerGroup.addDisconnectedEventListener((peer, peerCount) -> { + // We get called here on our user thread + numPeers.set(peerCount); + connectedPeers.set(peerGroup.getConnectedPeers()); }); // Map to user thread @@ -219,9 +190,7 @@ public class WalletsSetup { } }; - configBloomfilter(); configPeerNodes(socks5Proxy); - walletConfig.setDownloadListener(downloadListener) .setBlockingStartup(false) .setUserAgent(userAgent.getName(), userAgent.getVersion()); @@ -233,7 +202,7 @@ public class WalletsSetup { @Override public void failed(@NotNull Service.State from, @NotNull Throwable failure) { walletConfig = null; - log.error("walletAppKit failed"); + log.error("Service failure from state: {}; failure={}", from, failure); timeoutTimer.stop(); UserThread.execute(() -> exceptionHandler.handleException(failure)); } @@ -296,45 +265,6 @@ public class WalletsSetup { return mode; } - private void configBloomfilter() { - // Bloom filters in BitcoinJ are completely broken - // See: https://jonasnick.github.io/blog/2015/02/12/privacy-in-bitcoinj/ - // Here are a few improvements to fix a few vulnerabilities. - - // bisq's BitcoinJ fork has added a bloomFilterTweak (nonce) setter to reuse the same seed avoiding the trivial vulnerability - // by getting the real pub keys by intersections of several filters sent at each startup. - walletConfig.setBloomFilterTweak(bloomFilterTweak); - - // Avoid the simple attack (see: https://jonasnick.github.io/blog/2015/02/12/privacy-in-bitcoinj/) due to the - // default implementation using both pubkey and hash of pubkey. We have set a insertPubKey flag in BasicKeyChain to default false. - - // Default only 266 keys are generated (2 * 100+33 -> 100 external and 100 internal keys + buffers of 30%). That would trigger new bloom filters when we are reaching - // the threshold. To avoid reaching the threshold we create much more keys which are unlikely to cause update of the - // filter for most users. With lookaheadSize of 500 we get 1333 keys (500*1.3=666 666 external and 666 internal keys) which should be enough for most users to - // never need to update a bloom filter, which would weaken privacy. - // As we use 2 wallets (BTC, BSQ) we generate 1333 + 266 keys in total. - walletConfig.setBtcWalletLookaheadSize(100); - walletConfig.setBsqWalletLookaheadSize(100); - - // Calculation is derived from: https://www.reddit.com/r/Bitcoin/comments/2vrx6n/privacy_in_bitcoinj_android_wallet_multibit_hive/coknjuz - // No. of false positives (56M keys in the blockchain): - // First attempt for FP rate: - // FP rate = 0,0001; No. of false positives: 0,0001 * 56 000 000 = 5600 - // We have 1333keys: 1333 / (5600 + 1333) = 0.19 -> 19 % probability that a pub key is in our wallet - // After tests I found out that the bandwidth consumption varies widely related to the generated filter. - // About 20- 40 MB for upload and 30-130 MB for download at first start up (spv chain). - // Afterwards its about 1 MB for upload and 20-80 MB for download. - // Probably better then a high FP rate would be to include foreign pubKeyHashes which are tested to not be used - // in many transactions. If we had a pool of 100 000 such keys (2 MB data dump) to random select 4000 we could mix it with our - // 1000 own keys and get a similar probability rate as with the current setup but less variation in bandwidth - // consumption. - - // For now to reduce risks with high bandwidth consumption we reduce the FP rate by half. - // FP rate = 0,00005; No. of false positives: 0,00005 * 56 000 000 = 2800 - // 1333 / (2800 + 1333) = 0.32 -> 32 % probability that a pub key is in our wallet - walletConfig.setBloomFilterFalsePositiveRate(0.00005); - } - private void configPeerNodes(Socks5Proxy socks5Proxy) { String btcNodes = preferences.getBitcoinNodes(); log.debug("btcNodes: " + btcNodes); diff --git a/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java b/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java index 2571927965..13db433917 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java @@ -42,6 +42,7 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.bitcoinj.core.Coin; +import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import javax.annotation.Nullable; @@ -79,9 +80,13 @@ public class ProcessModel implements Model, Serializable { private String accountId; private PubKeyRing pubKeyRing; - // Mutable + // Transient/Mutable + transient private Transaction takeOfferFeeTx; @Setter transient private TradeMsg tradeMessage; + + // Mutable + private String takeOfferFeeTxId; @Setter private byte[] payoutTxSignature; @Setter @@ -98,8 +103,6 @@ public class ProcessModel implements Model, Serializable { @Setter private String changeOutputAddress; @Setter - private Transaction takeOfferFeeTx; - @Setter private boolean useSavingsWallet; @Setter private long fundsNeededForTradeAsLong; @@ -166,7 +169,6 @@ public class ProcessModel implements Model, Serializable { return paymentAccount != null ? paymentAccount.getPaymentAccountPayload() : null; } - public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentAccountPayload, PaymentAccountFilter[] appliedPaymentAccountFilter) { return filterManager.getFilter() != null && @@ -195,4 +197,15 @@ public class ProcessModel implements Model, Serializable { public Coin getFundsNeededForTradeAsLong() { return Coin.valueOf(fundsNeededForTradeAsLong); } + + public Transaction getTakeOfferFeeTx() { + if (takeOfferFeeTx == null) + takeOfferFeeTx = bsqWalletService.getTransaction(Sha256Hash.wrap(takeOfferFeeTxId)); + return takeOfferFeeTx; + } + + public void setTakeOfferFeeTx(Transaction takeOfferFeeTx) { + this.takeOfferFeeTx = takeOfferFeeTx; + takeOfferFeeTxId = takeOfferFeeTx.getHashAsString(); + } } diff --git a/gui/pom.xml b/gui/pom.xml index 86723faf3c..72efb854db 100644 --- a/gui/pom.xml +++ b/gui/pom.xml @@ -137,9 +137,6 @@ com.google.inject:guice:3.0:jar:null:compile:9d84f15fe35e2c716a02979fb62f50a29f38aefa - - com.google.protobuf:protobuf-java:2.5.0:jar:null:compile:a10732c76bfacdbd633a7eb0f7968b1059a65dfa - com.google.zxing:core:2.0:jar:null:compile:001a5b8ccf93ca2fb7c40a94417f8485e3c8b4a6 @@ -265,6 +262,9 @@ org.springframework:spring-test:4.1.1.RELEASE:jar:null:test:406ce9c05253f7dd75ac3f31170c71cca7419d8a + + com.google.protobuf:protobuf-java:3.2.0:jar:null:compile:62ccf171a106ff6791507f2d5364c275f9a3131d + diff --git a/gui/src/main/java/io/bisq/gui/components/AddressTextField.java b/gui/src/main/java/io/bisq/gui/components/AddressTextField.java index a648f9a103..139657931e 100644 --- a/gui/src/main/java/io/bisq/gui/components/AddressTextField.java +++ b/gui/src/main/java/io/bisq/gui/components/AddressTextField.java @@ -32,7 +32,6 @@ import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; import javafx.scene.layout.AnchorPane; import org.bitcoinj.core.Coin; -import org.bitcoinj.uri.BitcoinURI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -156,8 +155,7 @@ public class AddressTextField extends AnchorPane { log.warn("Amount must not be negative"); setAmountAsCoin(Coin.ZERO); } - - return address.get() != null ? BitcoinURI.convertToBitcoinURI(address.get(), amountAsCoin.get(), - paymentLabel.get(), null) : ""; + return GUIUtil.getBitcoinURI(address.get(), amountAsCoin.get(), + paymentLabel.get()); } } diff --git a/gui/src/main/java/io/bisq/gui/main/dao/wallet/receive/BsqReceiveView.java b/gui/src/main/java/io/bisq/gui/main/dao/wallet/receive/BsqReceiveView.java index d808b35ee7..69d08b7eec 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/wallet/receive/BsqReceiveView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/wallet/receive/BsqReceiveView.java @@ -17,7 +17,6 @@ package io.bisq.gui.main.dao.wallet.receive; -import io.bisq.common.UserThread; import io.bisq.common.app.DevEnv; import io.bisq.common.locale.Res; import io.bisq.core.btc.wallet.BsqWalletService; @@ -26,37 +25,21 @@ import io.bisq.gui.common.view.FxmlView; import io.bisq.gui.components.AddressTextField; import io.bisq.gui.components.InputTextField; import io.bisq.gui.main.dao.wallet.BsqBalanceUtil; -import io.bisq.gui.main.overlays.windows.QRCodeWindow; import io.bisq.gui.util.BsqFormatter; -import io.bisq.gui.util.GUIUtil; import io.bisq.gui.util.Layout; -import javafx.geometry.Insets; -import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; -import net.glxn.qrgen.QRCode; -import net.glxn.qrgen.image.ImageType; -import org.bitcoinj.core.Address; -import org.bitcoinj.core.Coin; -import org.bitcoinj.uri.BitcoinURI; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; import javax.inject.Inject; -import java.io.ByteArrayInputStream; -import java.util.concurrent.TimeUnit; import static io.bisq.gui.util.FormBuilder.*; @FxmlView public class BsqReceiveView extends ActivatableView { - private ImageView qrCodeImageView; private AddressTextField addressTextField; private InputTextField amountTextField; - private TextField balanceTextField; private final BsqWalletService bsqWalletService; private final BsqFormatter bsqFormatter; private final BsqBalanceUtil bsqBalanceUtil; @@ -84,14 +67,6 @@ public class BsqReceiveView extends ActivatableView { addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.receive.fundYourWallet"), Layout.GROUP_DISTANCE); - qrCodeImageView = new ImageView(); - qrCodeImageView.setStyle("-fx-cursor: hand;"); - Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow"))); - GridPane.setRowIndex(qrCodeImageView, gridRow); - GridPane.setColumnIndex(qrCodeImageView, 1); - GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0)); - root.getChildren().add(qrCodeImageView); - addressTextField = addLabelAddressTextField(root, ++gridRow, Res.getWithCol("shared.address")).second; addressTextField.setPaymentLabel(paymentLabelString); @@ -106,47 +81,15 @@ public class BsqReceiveView extends ActivatableView { amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> { addressTextField.setAmountAsCoin(bsqFormatter.parseToCoin(t)); - updateQRCode(); }); - qrCodeImageView.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute( - () -> UserThread.runAfter( - () -> new QRCodeWindow(getBitcoinURI()).show(), - 200, TimeUnit.MILLISECONDS))); addressTextField.setAddress(bsqFormatter.getBsqAddressStringFromAddress(bsqWalletService.freshReceiveAddress())); - updateQRCode(); } @Override protected void deactivate() { bsqBalanceUtil.deactivate(); - qrCodeImageView.setOnMouseClicked(null); amountTextFieldSubscription.unsubscribe(); } - - private void updateQRCode() { - if (addressTextField.getAddress() != null && !addressTextField.getAddress().isEmpty()) { - final byte[] imageBytes = QRCode - .from(getBitcoinURI()) - .withSize(150, 150) // code has 41 elements 8 px is border with 150 we get 3x scale and min. border - .to(ImageType.PNG) - .stream() - .toByteArray(); - Image qrImage = new Image(new ByteArrayInputStream(imageBytes)); - qrCodeImageView.setImage(qrImage); - } - } - - private Coin getAmountAsCoin() { - return bsqFormatter.parseToCoin(amountTextField.getText()); - } - - private String getBitcoinURI() { - Address addressFromBsqAddress = bsqFormatter.getAddressFromBsqAddress(addressTextField.getAddress()); - return BitcoinURI.convertToBitcoinURI(addressFromBsqAddress, - getAmountAsCoin(), - paymentLabelString, - null); - } } diff --git a/gui/src/main/java/io/bisq/gui/main/funds/deposit/DepositView.java b/gui/src/main/java/io/bisq/gui/main/funds/deposit/DepositView.java index c69a0cdb86..177cc154ef 100644 --- a/gui/src/main/java/io/bisq/gui/main/funds/deposit/DepositView.java +++ b/gui/src/main/java/io/bisq/gui/main/funds/deposit/DepositView.java @@ -56,7 +56,6 @@ import net.glxn.qrgen.QRCode; import net.glxn.qrgen.image.ImageType; import org.bitcoinj.core.Coin; import org.bitcoinj.core.Transaction; -import org.bitcoinj.uri.BitcoinURI; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; import org.jetbrains.annotations.NotNull; @@ -294,10 +293,9 @@ public class DepositView extends ActivatableView { @NotNull private String getBitcoinURI() { - return BitcoinURI.convertToBitcoinURI(addressTextField.getAddress(), + return GUIUtil.getBitcoinURI(addressTextField.getAddress(), getAmountAsCoin(), - paymentLabelString, - null); + paymentLabelString); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gui/src/main/java/io/bisq/gui/main/funds/transactions/TransactionsView.java b/gui/src/main/java/io/bisq/gui/main/funds/transactions/TransactionsView.java index 112c50b562..f6a9d56356 100644 --- a/gui/src/main/java/io/bisq/gui/main/funds/transactions/TransactionsView.java +++ b/gui/src/main/java/io/bisq/gui/main/funds/transactions/TransactionsView.java @@ -58,8 +58,13 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.util.Callback; -import org.bitcoinj.core.*; +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.script.Script; +import org.bitcoinj.wallet.Wallet; +import org.bitcoinj.wallet.listeners.WalletEventListener; import javax.annotation.Nullable; import javax.inject.Inject; diff --git a/gui/src/main/java/io/bisq/gui/main/funds/withdrawal/WithdrawalView.java b/gui/src/main/java/io/bisq/gui/main/funds/withdrawal/WithdrawalView.java index b9d961e1f9..11f51d4213 100644 --- a/gui/src/main/java/io/bisq/gui/main/funds/withdrawal/WithdrawalView.java +++ b/gui/src/main/java/io/bisq/gui/main/funds/withdrawal/WithdrawalView.java @@ -54,7 +54,11 @@ import javafx.scene.control.*; import javafx.scene.layout.VBox; import javafx.util.Callback; import org.apache.commons.lang3.StringUtils; -import org.bitcoinj.core.*; +import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.InsufficientMoneyException; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.wallet.Wallet; import org.jetbrains.annotations.NotNull; import org.spongycastle.crypto.params.KeyParameter; diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java index baec7f2f79..810120541d 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java @@ -66,7 +66,6 @@ import javafx.stage.Window; import javafx.util.StringConverter; import net.glxn.qrgen.QRCode; import net.glxn.qrgen.image.ImageType; -import org.bitcoinj.uri.BitcoinURI; import org.controlsfx.control.PopOver; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -1067,8 +1066,8 @@ public class CreateOfferView extends ActivatableViewAndModel list = new ArrayList<>(); - for (PeerDiscovery discovery : discoveryList) { - list.addAll(Arrays.asList(discovery.getPeers(timeoutValue, timeoutUnit))); + list.addAll(Arrays.asList(discovery.getPeers(services, timeoutValue, timeoutUnit))); } return list.toArray(new InetSocketAddress[list.size()]); diff --git a/network/src/main/java/io/bisq/network/Socks5SeedOnionDiscovery.java b/network/src/main/java/io/bisq/network/Socks5SeedOnionDiscovery.java index 309ca391f2..63d17ac7f8 100644 --- a/network/src/main/java/io/bisq/network/Socks5SeedOnionDiscovery.java +++ b/network/src/main/java/io/bisq/network/Socks5SeedOnionDiscovery.java @@ -110,7 +110,9 @@ public class Socks5SeedOnionDiscovery implements PeerDiscovery { * Returns an array containing all the Bitcoin nodes within the list. */ @Override - public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException { + public InetSocketAddress[] getPeers(long services, long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException { + if (services != 0) + throw new PeerDiscoveryException("DNS seeds cannot filter by services: " + services); return seedAddrs; } diff --git a/pom.xml b/pom.xml index ede2fdec1e..768f11ce11 100644 --- a/pom.xml +++ b/pom.xml @@ -97,7 +97,7 @@ org.bitcoinj bitcoinj-core - 0.13.1.11 + 0.14.4.1 com.google.code.findbugs