Adjust code to work with BitcoinJ 0.14.4.1

This commit is contained in:
Manfred Karrer 2017-04-21 15:20:32 -05:00
parent 9ab7cff156
commit b805a795dd
32 changed files with 227 additions and 352 deletions

View file

@ -395,13 +395,6 @@ public class Utilities {
Thread.currentThread().setName(name + "-" + new Random().nextInt(10000)); 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) { public static boolean isDirectory(String path) {
return new File(path).isDirectory(); return new File(path).isDirectory();
} }

View file

@ -27,8 +27,8 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Wallet;
import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.wallet.Wallet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View file

@ -110,7 +110,9 @@ public class SeedPeersSocks5Dns implements PeerDiscovery {
* Returns an array containing all the Bitcoin nodes within the list. * Returns an array containing all the Bitcoin nodes within the list.
*/ */
@Override @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 { try {
return allPeers(); return allPeers();
} catch (UnknownHostException e) { } catch (UnknownHostException e) {

View file

@ -22,11 +22,7 @@ import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDUtils; import org.bitcoinj.crypto.HDUtils;
import org.bitcoinj.crypto.KeyCrypter; import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.store.UnreadableWalletException; import org.bitcoinj.wallet.*;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.bitcoinj.wallet.KeyChainFactory;
import org.bitcoinj.wallet.Protos;
class BisqKeyChainFactory implements KeyChainFactory { class BisqKeyChainFactory implements KeyChainFactory {
private final boolean useBitcoinDeterministicKeyChain; private final boolean useBitcoinDeterministicKeyChain;

View file

@ -25,23 +25,20 @@ import java.security.SecureRandom;
class BisqKeyChainGroup extends KeyChainGroup { class BisqKeyChainGroup extends KeyChainGroup {
private final boolean useBitcoinDeterministicKeyChain; private final boolean useBitcoinDeterministicKeyChain;
private final int lookaheadSize;
public boolean isUseBitcoinDeterministicKeyChain() { public boolean isUseBitcoinDeterministicKeyChain() {
return useBitcoinDeterministicKeyChain; return useBitcoinDeterministicKeyChain;
} }
public BisqKeyChainGroup(NetworkParameters params, boolean useBitcoinDeterministicKeyChain, int lookaheadSize) { public BisqKeyChainGroup(NetworkParameters params, boolean useBitcoinDeterministicKeyChain) {
super(params); super(params);
this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain; 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); super(params, chain);
this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain; this.useBitcoinDeterministicKeyChain = useBitcoinDeterministicKeyChain;
this.lookaheadSize = lookaheadSize;
} }
@Override @Override
@ -52,7 +49,6 @@ class BisqKeyChainGroup extends KeyChainGroup {
@Override @Override
public void createAndActivateNewHDChain() { public void createAndActivateNewHDChain() {
DeterministicKeyChain chain = useBitcoinDeterministicKeyChain ? new BtcDeterministicKeyChain(new SecureRandom()) : new BsqDeterministicKeyChain(new SecureRandom()); DeterministicKeyChain chain = useBitcoinDeterministicKeyChain ? new BtcDeterministicKeyChain(new SecureRandom()) : new BsqDeterministicKeyChain(new SecureRandom());
chain.setLookaheadSize(lookaheadSize);
addAndActivateHDChain(chain); addAndActivateHDChain(chain);
} }
} }

View file

@ -25,6 +25,7 @@ import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.DeterministicSeed;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;
import java.security.SecureRandom; import java.security.SecureRandom;
@ -33,8 +34,7 @@ class BsqDeterministicKeyChain extends DeterministicKeyChain {
// See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki // See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
// https://github.com/satoshilabs/slips/blob/master/slip-0044.md // https://github.com/satoshilabs/slips/blob/master/slip-0044.md
// We use 142 (0x8000008E) as coin_type for BSQ // We have registered 142 (0x8000008E) as coin_type for BSQ
// TODO register
public static final ImmutableList<ChildNumber> BIP44_BSQ_ACCOUNT_PATH = ImmutableList.of( public static final ImmutableList<ChildNumber> BIP44_BSQ_ACCOUNT_PATH = ImmutableList.of(
new ChildNumber(44, true), new ChildNumber(44, true),
new ChildNumber(142, true), new ChildNumber(142, true),
@ -56,6 +56,20 @@ class BsqDeterministicKeyChain extends DeterministicKeyChain {
super(seed); 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 @Override
protected ImmutableList<ChildNumber> getAccountPath() { protected ImmutableList<ChildNumber> getAccountPath() {
return BIP44_BSQ_ACCOUNT_PATH; return BIP44_BSQ_ACCOUNT_PATH;

View file

@ -17,71 +17,13 @@
package io.bisq.core.btc.wallet; package io.bisq.core.btc.wallet;
import org.bitcoinj.core.*; import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.wallet.KeyChainGroup; import org.bitcoinj.wallet.KeyChainGroup;
import org.slf4j.Logger; import org.bitcoinj.wallet.Wallet;
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;
public class BsqWallet extends Wallet { public class BsqWallet extends Wallet {
private static final Logger log = LoggerFactory.getLogger(BsqWallet.class);
public BsqWallet(NetworkParameters params, KeyChainGroup keyChainGroup) { public BsqWallet(NetworkParameters params, KeyChainGroup keyChainGroup) {
super(params, 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<TransactionOutput> 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<TransactionOutput> 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);
}
} }

View file

@ -33,6 +33,9 @@ import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.*; import org.bitcoinj.core.*;
import org.bitcoinj.script.Script; import org.bitcoinj.script.Script;
import org.bitcoinj.wallet.CoinSelection; 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 javax.inject.Inject;
import java.util.HashSet; import java.util.HashSet;
@ -275,9 +278,10 @@ public class BsqWalletService extends WalletService {
"The amount is too low (dust limit)."); "The amount is too low (dust limit).");
tx.addOutput(receiverAmount, new Address(params, receiverAddress)); tx.addOutput(receiverAmount, new Address(params, receiverAddress));
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); SendRequest sendRequest = SendRequest.forTx(tx);
sendRequest.fee = Coin.ZERO; sendRequest.fee = Coin.ZERO;
sendRequest.feePerKb = Coin.ZERO; sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
sendRequest.shuffleOutputs = false; sendRequest.shuffleOutputs = false;
sendRequest.signInputs = false; sendRequest.signInputs = false;

View file

@ -21,6 +21,7 @@ import com.google.common.annotations.VisibleForTesting;
import org.bitcoinj.core.*; import org.bitcoinj.core.*;
import org.bitcoinj.params.RegTestParams; import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.wallet.CoinSelection; import org.bitcoinj.wallet.CoinSelection;
import org.bitcoinj.wallet.Wallet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View file

@ -25,6 +25,7 @@ import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.DeterministicSeed;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;
import java.security.SecureRandom; import java.security.SecureRandom;
@ -56,6 +57,19 @@ class BtcDeterministicKeyChain extends DeterministicKeyChain {
super(seed); 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 @Override
protected ImmutableList<ChildNumber> getAccountPath() { protected ImmutableList<ChildNumber> getAccountPath() {
return BIP44_BTC_ACCOUNT_PATH; return BIP44_BTC_ACCOUNT_PATH;

View file

@ -31,6 +31,8 @@ import org.bitcoinj.core.*;
import org.bitcoinj.crypto.DeterministicKey; import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -201,14 +203,16 @@ public class BtcWalletService extends WalletService {
tx.addOutput(forcedChangeValue, changeAddress); tx.addOutput(forcedChangeValue, changeAddress);
} }
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); SendRequest sendRequest = SendRequest.forTx(tx);
sendRequest.shuffleOutputs = false; sendRequest.shuffleOutputs = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
// signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet) // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
sendRequest.signInputs = false; sendRequest.signInputs = false;
sendRequest.ensureMinRequiredFee = false;
sendRequest.feePerKb = Coin.ZERO;
sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs); sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs);
sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.coinSelector = coinSelector; sendRequest.coinSelector = coinSelector;
sendRequest.changeAddress = changeAddress; sendRequest.changeAddress = changeAddress;
wallet.completeTx(sendRequest); wallet.completeTx(sendRequest);
@ -461,7 +465,7 @@ public class BtcWalletService extends WalletService {
int counter = 0; int counter = 0;
int txSize = 0; int txSize = 0;
Transaction tx; Transaction tx;
Wallet.SendRequest sendRequest; SendRequest sendRequest;
Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte(); Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
do { do {
counter++; counter++;
@ -472,9 +476,10 @@ public class BtcWalletService extends WalletService {
newTransaction.clearOutputs(); newTransaction.clearOutputs();
newTransaction.addOutput(amount.subtract(fee), toAddress); newTransaction.addOutput(amount.subtract(fee), toAddress);
sendRequest = Wallet.SendRequest.forTx(newTransaction); sendRequest = SendRequest.forTx(newTransaction);
sendRequest.fee = fee; sendRequest.fee = fee;
sendRequest.feePerKb = Coin.ZERO; sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
sendRequest.coinSelector = new BtcCoinSelector(toAddress); sendRequest.coinSelector = new BtcCoinSelector(toAddress);
sendRequest.changeAddress = toAddress; sendRequest.changeAddress = toAddress;
@ -491,9 +496,10 @@ public class BtcWalletService extends WalletService {
Wallet.SendResult sendResult = null; Wallet.SendResult sendResult = null;
try { try {
sendRequest = Wallet.SendRequest.forTx(newTransaction); sendRequest = SendRequest.forTx(newTransaction);
sendRequest.fee = fee; sendRequest.fee = fee;
sendRequest.feePerKb = Coin.ZERO; sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
sendRequest.coinSelector = new BtcCoinSelector(toAddress); sendRequest.coinSelector = new BtcCoinSelector(toAddress);
sendRequest.changeAddress = toAddress; sendRequest.changeAddress = toAddress;
@ -506,9 +512,10 @@ public class BtcWalletService extends WalletService {
newTransaction.clearOutputs(); newTransaction.clearOutputs();
newTransaction.addOutput(amount, toAddress); newTransaction.addOutput(amount, toAddress);
sendRequest = Wallet.SendRequest.forTx(newTransaction); sendRequest = SendRequest.forTx(newTransaction);
sendRequest.fee = fee; sendRequest.fee = fee;
sendRequest.feePerKb = Coin.ZERO; sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
sendRequest.coinSelector = new BtcCoinSelector(toAddress, false); sendRequest.coinSelector = new BtcCoinSelector(toAddress, false);
sendRequest.changeAddress = toAddress; sendRequest.changeAddress = toAddress;
@ -584,7 +591,7 @@ public class BtcWalletService extends WalletService {
if (fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) if (fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0)
fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; 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); wallet.completeTx(sendRequest);
tx = sendRequest.tx; tx = sendRequest.tx;
txSize = tx.bitcoinSerialize().length; txSize = tx.bitcoinSerialize().length;
@ -634,7 +641,7 @@ public class BtcWalletService extends WalletService {
fee = txFeeForWithdrawalPerByte.multiply(txSize); fee = txFeeForWithdrawalPerByte.multiply(txSize);
if (fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) if (fee.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0)
fee = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; 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); wallet.completeTx(sendRequest);
tx = sendRequest.tx; tx = sendRequest.tx;
txSize = tx.bitcoinSerialize().length; txSize = tx.bitcoinSerialize().length;
@ -664,7 +671,7 @@ public class BtcWalletService extends WalletService {
AddressEntry.Context context, AddressEntry.Context context,
FutureCallback<Transaction> callback) throws AddressFormatException, FutureCallback<Transaction> callback) throws AddressFormatException,
AddressEntryException, InsufficientMoneyException { 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); Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
Futures.addCallback(sendResult.broadcastComplete, callback); Futures.addCallback(sendResult.broadcastComplete, callback);
@ -681,7 +688,7 @@ public class BtcWalletService extends WalletService {
FutureCallback<Transaction> callback) throws AddressFormatException, FutureCallback<Transaction> callback) throws AddressFormatException,
AddressEntryException, InsufficientMoneyException { 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); Wallet.SendResult sendResult = wallet.sendCoins(request);
Futures.addCallback(sendResult.broadcastComplete, callback); Futures.addCallback(sendResult.broadcastComplete, callback);
@ -689,21 +696,22 @@ public class BtcWalletService extends WalletService {
return sendResult.tx.getHashAsString(); return sendResult.tx.getHashAsString();
} }
private Wallet.SendRequest getSendRequest(String fromAddress, private SendRequest getSendRequest(String fromAddress,
String toAddress, String toAddress,
Coin amount, Coin amount,
Coin fee, Coin fee,
@Nullable KeyParameter aesKey, @Nullable KeyParameter aesKey,
AddressEntry.Context context) throws AddressFormatException, AddressEntry.Context context) throws AddressFormatException,
AddressEntryException { AddressEntryException {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
Preconditions.checkArgument(Restrictions.isAboveDust(amount, fee), Preconditions.checkArgument(Restrictions.isAboveDust(amount, fee),
"The amount is too low (dust limit)."); "The amount is too low (dust limit).");
tx.addOutput(amount.subtract(fee), new Address(params, toAddress)); 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.fee = fee;
sendRequest.feePerKb = Coin.ZERO; sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
sendRequest.shuffleOutputs = false; sendRequest.shuffleOutputs = false;
Optional<AddressEntry> addressEntry = findAddressEntry(fromAddress, context); Optional<AddressEntry> addressEntry = findAddressEntry(fromAddress, context);
@ -717,21 +725,22 @@ public class BtcWalletService extends WalletService {
return sendRequest; return sendRequest;
} }
private Wallet.SendRequest getSendRequestForMultipleAddresses(Set<String> fromAddresses, private SendRequest getSendRequestForMultipleAddresses(Set<String> fromAddresses,
String toAddress, String toAddress,
Coin amount, Coin amount,
Coin fee, Coin fee,
@Nullable String changeAddress, @Nullable String changeAddress,
@Nullable KeyParameter aesKey) throws @Nullable KeyParameter aesKey) throws
AddressFormatException, AddressEntryException { AddressFormatException, AddressEntryException {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
checkArgument(Restrictions.isAboveDust(amount), checkArgument(Restrictions.isAboveDust(amount),
"The amount is too low (dust limit)."); "The amount is too low (dust limit).");
tx.addOutput(amount.subtract(fee), new Address(params, toAddress)); 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.fee = fee;
sendRequest.feePerKb = Coin.ZERO; sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
sendRequest.shuffleOutputs = false; sendRequest.shuffleOutputs = false;
Set<AddressEntry> addressEntries = fromAddresses.stream() Set<AddressEntry> addressEntries = fromAddresses.stream()

View file

@ -22,7 +22,6 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import io.bisq.common.app.Log; import io.bisq.common.app.Log;
import io.bisq.common.util.Utilities;
import io.bisq.core.btc.AddressEntry; import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.InsufficientFundsException; import io.bisq.core.btc.InsufficientFundsException;
import io.bisq.core.btc.data.InputsAndChangeOutput; import io.bisq.core.btc.data.InputsAndChangeOutput;
@ -37,6 +36,8 @@ import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.TransactionSignature; import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.script.Script; import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -125,10 +126,6 @@ public class TradeWalletService {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
void setAesKey(@Nullable KeyParameter newAesKey) { 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; 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 // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to
// wait for 1 confirmation) // 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) // 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.shuffleOutputs = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
if (useSavingsWallet) if (useSavingsWallet)
@ -184,8 +181,10 @@ public class TradeWalletService {
else else
sendRequest.coinSelector = new BtcCoinSelector(fundingAddress); sendRequest.coinSelector = new BtcCoinSelector(fundingAddress);
// We use a fixed fee // We use a fixed fee
sendRequest.feePerKb = Coin.ZERO;
sendRequest.fee = txFee; sendRequest.fee = txFee;
sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
// Change is optional in case of overpay or use of funds from savings wallet // Change is optional in case of overpay or use of funds from savings wallet
sendRequest.changeAddress = changeAddress; 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) // 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); // WalletService.printTx("preparedBsqTx", preparedBsqTx);
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(preparedBsqTx); SendRequest sendRequest = SendRequest.forTx(preparedBsqTx);
sendRequest.shuffleOutputs = false; sendRequest.shuffleOutputs = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
if (useSavingsWallet) if (useSavingsWallet)
@ -251,8 +250,10 @@ public class TradeWalletService {
else else
sendRequest.coinSelector = new BtcCoinSelector(fundingAddress); sendRequest.coinSelector = new BtcCoinSelector(fundingAddress);
// We use a fixed fee // We use a fixed fee
sendRequest.feePerKb = Coin.ZERO;
sendRequest.fee = txFee; sendRequest.fee = txFee;
sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.signInputs = false; sendRequest.signInputs = false;
// Change is optional in case of overpay or use of funds from savings wallet // 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 { private void addAvailableInputsAndChangeOutputs(Transaction transaction, Address address, Address changeAddress, Coin txFee) throws WalletException {
try { try {
// Lets let the framework do the work to find the right inputs // 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.shuffleOutputs = false;
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
// We use a fixed fee // We use a fixed fee
sendRequest.feePerKb = Coin.ZERO;
sendRequest.fee = txFee; 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) // 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); sendRequest.coinSelector = new BtcCoinSelector(address);
// We use always the same address in a trade for all transactions // We use always the same address in a trade for all transactions

View file

@ -24,6 +24,8 @@ import com.google.common.util.concurrent.Futures;
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy; import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
import io.bisq.core.btc.ProxySocketFactory; import io.bisq.core.btc.ProxySocketFactory;
import org.bitcoinj.core.*; 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.BlockingClientManager;
import org.bitcoinj.net.discovery.DnsDiscovery; import org.bitcoinj.net.discovery.DnsDiscovery;
import org.bitcoinj.net.discovery.PeerDiscovery; import org.bitcoinj.net.discovery.PeerDiscovery;
@ -33,11 +35,7 @@ import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.BlockStoreException; import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.store.SPVBlockStore; import org.bitcoinj.store.SPVBlockStore;
import org.bitcoinj.store.WalletProtobufSerializer; import org.bitcoinj.wallet.*;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.bitcoinj.wallet.KeyChainGroup;
import org.bitcoinj.wallet.Protos;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -91,15 +89,13 @@ public class WalletConfig extends AbstractIdleService {
private volatile File vBsqWalletFile; private volatile File vBsqWalletFile;
@Nullable @Nullable
private DeterministicSeed seed; private DeterministicSeed seed;
private int btcWalletLookaheadSize = -1;
private int bsqWalletLookaheadSize = -1;
private volatile BlockChain vChain; private volatile BlockChain vChain;
private volatile BlockStore vStore; private volatile BlockStore vStore;
private volatile PeerGroup vPeerGroup; private volatile PeerGroup vPeerGroup;
private boolean useAutoSave = true; private boolean useAutoSave = true;
private PeerAddress[] peerAddresses; private PeerAddress[] peerAddresses;
private PeerEventListener downloadListener; private PeerDataEventListener downloadListener;
private boolean autoStop = true; private boolean autoStop = true;
private InputStream checkpoints; private InputStream checkpoints;
private boolean blockingStartup = true; private boolean blockingStartup = true;
@ -108,8 +104,6 @@ public class WalletConfig extends AbstractIdleService {
private String version; private String version;
@Nullable @Nullable
private PeerDiscovery discovery; private PeerDiscovery discovery;
private long bloomFilterTweak = 0;
private double bloomFilterFPRate = -1;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -226,12 +220,7 @@ public class WalletConfig extends AbstractIdleService {
return this; return this;
} }
/** public WalletConfig setDownloadListener(PeerDataEventListener listener) {
* 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) {
this.downloadListener = listener; this.downloadListener = listener;
return this; return this;
} }
@ -298,26 +287,6 @@ public class WalletConfig extends AbstractIdleService {
return this; 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;
}
/** /**
* <p>Override this to return wallet extensions if any are necessary.</p> * <p>Override this to return wallet extensions if any are necessary.</p>
* <p> * <p>
@ -388,18 +357,18 @@ public class WalletConfig extends AbstractIdleService {
boolean shouldReplayWallet = (vBtcWalletFile.exists() && !chainFileExists) || seed != null; boolean shouldReplayWallet = (vBtcWalletFile.exists() && !chainFileExists) || seed != null;
BisqKeyChainGroup keyChainGroup; BisqKeyChainGroup keyChainGroup;
if (seed != null) if (seed != null)
keyChainGroup = new BisqKeyChainGroup(params, new BtcDeterministicKeyChain(seed), true, btcWalletLookaheadSize); keyChainGroup = new BisqKeyChainGroup(params, new BtcDeterministicKeyChain(seed), true);
else else
keyChainGroup = new BisqKeyChainGroup(params, true, btcWalletLookaheadSize); keyChainGroup = new BisqKeyChainGroup(params, true);
vBtcWallet = createOrLoadWallet(vBtcWalletFile, shouldReplayWallet, keyChainGroup, false); vBtcWallet = createOrLoadWallet(vBtcWalletFile, shouldReplayWallet, keyChainGroup, false, seed);
// BSQ walelt // BSQ walelt
vBsqWalletFile = new File(directory, bsqWalletFileName); vBsqWalletFile = new File(directory, bsqWalletFileName);
if (seed != null) if (seed != null)
keyChainGroup = new BisqKeyChainGroup(params, new BsqDeterministicKeyChain(seed), false, bsqWalletLookaheadSize); keyChainGroup = new BisqKeyChainGroup(params, new BsqDeterministicKeyChain(seed), false);
else else
keyChainGroup = new BisqKeyChainGroup(params, new BsqDeterministicKeyChain(vBtcWallet.getKeyChainSeed()), false, bsqWalletLookaheadSize); keyChainGroup = new BisqKeyChainGroup(params, new BsqDeterministicKeyChain(vBtcWallet.getKeyChainSeed()), false);
vBsqWallet = createOrLoadWallet(vBsqWalletFile, shouldReplayWallet, keyChainGroup, true); vBsqWallet = createOrLoadWallet(vBsqWalletFile, shouldReplayWallet, keyChainGroup, true, seed);
// Initiate Bitcoin network objects (block store, blockchain and peer group) // Initiate Bitcoin network objects (block store, blockchain and peer group)
vStore = provideBlockStore(chainFile); vStore = provideBlockStore(chainFile);
@ -438,12 +407,6 @@ public class WalletConfig extends AbstractIdleService {
vChain = new BlockChain(params, vStore); vChain = new BlockChain(params, vStore);
vPeerGroup = createPeerGroup(); vPeerGroup = createPeerGroup();
if (bloomFilterFPRate != -1)
vPeerGroup.setBloomFilterFalsePositiveRate(bloomFilterFPRate);
if (bloomFilterTweak != 0)
vPeerGroup.setBloomFilterTweak(bloomFilterTweak);
if (this.userAgent != null) if (this.userAgent != null)
vPeerGroup.setUserAgent(userAgent, version); vPeerGroup.setUserAgent(userAgent, version);
@ -475,7 +438,7 @@ public class WalletConfig extends AbstractIdleService {
Futures.addCallback(vPeerGroup.startAsync(), new FutureCallback() { Futures.addCallback(vPeerGroup.startAsync(), new FutureCallback() {
@Override @Override
public void onSuccess(@Nullable Object result) { public void onSuccess(@Nullable Object result) {
final PeerEventListener listener = downloadListener == null ? final PeerDataEventListener listener = downloadListener == null ?
new DownloadProgressTracker() : downloadListener; new DownloadProgressTracker() : downloadListener;
vPeerGroup.startBlockChainDownload(listener); vPeerGroup.startBlockChainDownload(listener);
} }
@ -493,8 +456,11 @@ public class WalletConfig extends AbstractIdleService {
} }
private Wallet createOrLoadWallet(File walletFile, boolean shouldReplayWallet, private Wallet createOrLoadWallet(File walletFile, boolean shouldReplayWallet,
BisqKeyChainGroup keyChainGroup, boolean isBsqWallet) BisqKeyChainGroup keyChainGroup, boolean isBsqWallet, DeterministicSeed seed)
throws Exception { throws Exception {
maybeMoveOldWalletOutOfTheWay(walletFile, seed);
Wallet wallet; Wallet wallet;
if (walletFile.exists()) { if (walletFile.exists()) {
wallet = loadWallet(walletFile, shouldReplayWallet, keyChainGroup.isUseBitcoinDeterministicKeyChain()); wallet = loadWallet(walletFile, shouldReplayWallet, keyChainGroup.isUseBitcoinDeterministicKeyChain());
@ -509,6 +475,22 @@ public class WalletConfig extends AbstractIdleService {
return wallet; 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 { private Wallet loadWallet(File walletFile, boolean shouldReplayWallet, boolean useBitcoinDeterministicKeyChain) throws Exception {
Wallet wallet; Wallet wallet;
FileInputStream walletStream = new FileInputStream(walletFile); FileInputStream walletStream = new FileInputStream(walletFile);

View file

@ -21,7 +21,6 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import io.bisq.common.handlers.ErrorMessageHandler; import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.handlers.ResultHandler; 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.TransactionVerificationException;
import io.bisq.core.btc.exceptions.WalletException; import io.bisq.core.btc.exceptions.WalletException;
import io.bisq.core.btc.listeners.AddressConfidenceListener; import io.bisq.core.btc.listeners.AddressConfidenceListener;
@ -38,6 +37,8 @@ import org.bitcoinj.script.Script;
import org.bitcoinj.signers.TransactionSigner; import org.bitcoinj.signers.TransactionSigner;
import org.bitcoinj.utils.Threading; import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.*; import org.bitcoinj.wallet.*;
import org.bitcoinj.wallet.listeners.AbstractWalletEventListener;
import org.bitcoinj.wallet.listeners.WalletEventListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -104,8 +105,6 @@ public abstract class WalletService {
void decryptWallet(@NotNull KeyParameter key) { void decryptWallet(@NotNull KeyParameter key) {
wallet.decrypt(key); wallet.decrypt(key);
// Overwrite first with random bytes before setting to null
Utilities.overwriteWithRandomBytes(key.getKey());
aesKey = null; aesKey = null;
} }
@ -313,7 +312,7 @@ public abstract class WalletService {
List<TransactionConfidence> transactionConfidenceList = getOutputsWithConnectedOutputs(tx) List<TransactionConfidence> transactionConfidenceList = getOutputsWithConnectedOutputs(tx)
.stream() .stream()
.filter(WalletUtils::isOutputScriptConvertableToAddress) .filter(WalletUtils::isOutputScriptConvertableToAddress)
.filter(output -> address.equals(WalletUtils.getAddressFromOutput(output))) .filter(output -> address != null && address.equals(WalletUtils.getAddressFromOutput(output)))
.map(o -> tx.getConfidence()) .map(o -> tx.getConfidence())
.collect(Collectors.toList()); .collect(Collectors.toList());
return getMostRecentConfidence(transactionConfidenceList); return getMostRecentConfidence(transactionConfidenceList);
@ -375,6 +374,7 @@ public abstract class WalletService {
Coin balance = Coin.ZERO; Coin balance = Coin.ZERO;
for (TransactionOutput output : transactionOutputs) { for (TransactionOutput output : transactionOutputs) {
if (WalletUtils.isOutputScriptConvertableToAddress(output) && if (WalletUtils.isOutputScriptConvertableToAddress(output) &&
address != null &&
address.equals(WalletUtils.getAddressFromOutput(output))) address.equals(WalletUtils.getAddressFromOutput(output)))
balance = balance.add(output.getValue()); balance = balance.add(output.getValue());
} }
@ -387,6 +387,7 @@ public abstract class WalletService {
int outputs = 0; int outputs = 0;
for (TransactionOutput output : transactionOutputs) { for (TransactionOutput output : transactionOutputs) {
if (WalletUtils.isOutputScriptConvertableToAddress(output) && if (WalletUtils.isOutputScriptConvertableToAddress(output) &&
address != null &&
address.equals(WalletUtils.getAddressFromOutput(output))) address.equals(WalletUtils.getAddressFromOutput(output)))
outputs++; outputs++;
} }
@ -408,7 +409,8 @@ public abstract class WalletService {
public void emptyWallet(String toAddress, KeyParameter aesKey, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) public void emptyWallet(String toAddress, KeyParameter aesKey, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler)
throws InsufficientMoneyException, AddressFormatException { 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.feePerKb = getTxFeeForWithdrawalPerByte().multiply(1000);
sendRequest.aesKey = aesKey; sendRequest.aesKey = aesKey;
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest); Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
@ -550,11 +552,7 @@ public abstract class WalletService {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public static void printTx(String tracePrefix, Transaction tx) { public static void printTx(String tracePrefix, Transaction tx) {
int size = tx.bitcoinSerialize().length; log.info("\n" + tracePrefix + ":\n" + tx.toString());
log.info("\n" + tracePrefix + ":\n" +
tx.toString() +
"Satoshi/byte: " + (tx.getFee() != null ? tx.getFee().value / size : "No fee set yet") +
" (size: " + size + ")");
} }

View file

@ -21,10 +21,10 @@ import com.google.inject.Inject;
import io.bisq.common.handlers.ExceptionHandler; import io.bisq.common.handlers.ExceptionHandler;
import io.bisq.common.handlers.ResultHandler; import io.bisq.common.handlers.ResultHandler;
import io.bisq.core.crypto.ScryptUtil; import io.bisq.core.crypto.ScryptUtil;
import org.bitcoinj.core.Wallet;
import org.bitcoinj.crypto.KeyCrypter; import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.DeterministicSeed;
import org.bitcoinj.wallet.Wallet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.KeyParameter;

View file

@ -40,9 +40,11 @@ import io.bisq.network.Socks5ProxyProvider;
import javafx.beans.property.*; import javafx.beans.property.*;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.bitcoinj.core.*; import org.bitcoinj.core.*;
import org.bitcoinj.core.listeners.DownloadProgressTracker;
import org.bitcoinj.params.RegTestParams; import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.utils.Threading; import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.DeterministicSeed;
import org.bitcoinj.wallet.Wallet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -105,7 +107,6 @@ public class WalletsSetup {
PersistenceProtoResolver persistenceProtoResolver, PersistenceProtoResolver persistenceProtoResolver,
@Named(BtcOptionKeys.WALLET_DIR) File appDir, @Named(BtcOptionKeys.WALLET_DIR) File appDir,
@Named(BtcOptionKeys.SOCKS5_DISCOVER_MODE) String socks5DiscoverModeString) { @Named(BtcOptionKeys.SOCKS5_DISCOVER_MODE) String socks5DiscoverModeString) {
this.regTestHost = regTestHost; this.regTestHost = regTestHost;
this.addressEntryList = addressEntryList; this.addressEntryList = addressEntryList;
this.userAgent = userAgent; this.userAgent = userAgent;
@ -117,6 +118,7 @@ public class WalletsSetup {
WalletUtils.setBitcoinNetwork(bisqEnvironment.getBitcoinNetwork()); WalletUtils.setBitcoinNetwork(bisqEnvironment.getBitcoinNetwork());
params = WalletUtils.getParameters(); params = WalletUtils.getParameters();
walletDir = new File(appDir, "bitcoin"); walletDir = new File(appDir, "bitcoin");
PeerGroup.setIgnoreHttpSeeds(true);
storage = new Storage<>(walletDir, persistenceProtoResolver); storage = new Storage<>(walletDir, persistenceProtoResolver);
LongPersistable nonce = storage.initAndGetPersistedWithFileName("BloomFilterNonce"); LongPersistable nonce = storage.initAndGetPersistedWithFileName("BloomFilterNonce");
@ -152,7 +154,8 @@ public class WalletsSetup {
final Socks5Proxy socks5Proxy = preferences.getUseTorForBitcoinJ() ? socks5ProxyProvider.getSocks5Proxy() : null; final Socks5Proxy socks5Proxy = preferences.getUseTorForBitcoinJ() ? socks5ProxyProvider.getSocks5Proxy() : null;
log.debug("Use socks5Proxy for bitcoinj: " + socks5Proxy); 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 @Override
protected void onSetupCompleted() { protected void onSetupCompleted() {
//We are here in the btcj thread Thread[ STARTING,5,main] //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()) if (preferences.getBitcoinNodes() != null && !preferences.getBitcoinNodes().isEmpty())
peerGroup.setAddPeersFromAddressMessage(false); peerGroup.setAddPeersFromAddressMessage(false);
peerGroup.addEventListener(new PeerEventListener() { peerGroup.addConnectedEventListener((peer, peerCount) -> {
@Override // We get called here on our user thread
public void onPeersDiscovered(Set<PeerAddress> peerAddresses) { numPeers.set(peerCount);
} connectedPeers.set(peerGroup.getConnectedPeers());
});
@Override peerGroup.addDisconnectedEventListener((peer, peerCount) -> {
public void onBlocksDownloaded(Peer peer, Block block, FilteredBlock filteredBlock, int blocksLeft) { // We get called here on our user thread
} numPeers.set(peerCount);
connectedPeers.set(peerGroup.getConnectedPeers());
@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<Message> getData(Peer peer, GetDataMessage m) {
return null;
}
}); });
// Map to user thread // Map to user thread
@ -219,9 +190,7 @@ public class WalletsSetup {
} }
}; };
configBloomfilter();
configPeerNodes(socks5Proxy); configPeerNodes(socks5Proxy);
walletConfig.setDownloadListener(downloadListener) walletConfig.setDownloadListener(downloadListener)
.setBlockingStartup(false) .setBlockingStartup(false)
.setUserAgent(userAgent.getName(), userAgent.getVersion()); .setUserAgent(userAgent.getName(), userAgent.getVersion());
@ -233,7 +202,7 @@ public class WalletsSetup {
@Override @Override
public void failed(@NotNull Service.State from, @NotNull Throwable failure) { public void failed(@NotNull Service.State from, @NotNull Throwable failure) {
walletConfig = null; walletConfig = null;
log.error("walletAppKit failed"); log.error("Service failure from state: {}; failure={}", from, failure);
timeoutTimer.stop(); timeoutTimer.stop();
UserThread.execute(() -> exceptionHandler.handleException(failure)); UserThread.execute(() -> exceptionHandler.handleException(failure));
} }
@ -296,45 +265,6 @@ public class WalletsSetup {
return mode; 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) { private void configPeerNodes(Socks5Proxy socks5Proxy) {
String btcNodes = preferences.getBitcoinNodes(); String btcNodes = preferences.getBitcoinNodes();
log.debug("btcNodes: " + btcNodes); log.debug("btcNodes: " + btcNodes);

View file

@ -42,6 +42,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -79,9 +80,13 @@ public class ProcessModel implements Model, Serializable {
private String accountId; private String accountId;
private PubKeyRing pubKeyRing; private PubKeyRing pubKeyRing;
// Mutable // Transient/Mutable
transient private Transaction takeOfferFeeTx;
@Setter @Setter
transient private TradeMsg tradeMessage; transient private TradeMsg tradeMessage;
// Mutable
private String takeOfferFeeTxId;
@Setter @Setter
private byte[] payoutTxSignature; private byte[] payoutTxSignature;
@Setter @Setter
@ -98,8 +103,6 @@ public class ProcessModel implements Model, Serializable {
@Setter @Setter
private String changeOutputAddress; private String changeOutputAddress;
@Setter @Setter
private Transaction takeOfferFeeTx;
@Setter
private boolean useSavingsWallet; private boolean useSavingsWallet;
@Setter @Setter
private long fundsNeededForTradeAsLong; private long fundsNeededForTradeAsLong;
@ -166,7 +169,6 @@ public class ProcessModel implements Model, Serializable {
return paymentAccount != null ? paymentAccount.getPaymentAccountPayload() : null; return paymentAccount != null ? paymentAccount.getPaymentAccountPayload() : null;
} }
public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentAccountPayload, public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentAccountPayload,
PaymentAccountFilter[] appliedPaymentAccountFilter) { PaymentAccountFilter[] appliedPaymentAccountFilter) {
return filterManager.getFilter() != null && return filterManager.getFilter() != null &&
@ -195,4 +197,15 @@ public class ProcessModel implements Model, Serializable {
public Coin getFundsNeededForTradeAsLong() { public Coin getFundsNeededForTradeAsLong() {
return Coin.valueOf(fundsNeededForTradeAsLong); 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();
}
} }

View file

@ -137,9 +137,6 @@
<urn> <urn>
com.google.inject:guice:3.0:jar:null:compile:9d84f15fe35e2c716a02979fb62f50a29f38aefa com.google.inject:guice:3.0:jar:null:compile:9d84f15fe35e2c716a02979fb62f50a29f38aefa
</urn> </urn>
<urn>
com.google.protobuf:protobuf-java:2.5.0:jar:null:compile:a10732c76bfacdbd633a7eb0f7968b1059a65dfa
</urn>
<urn> <urn>
com.google.zxing:core:2.0:jar:null:compile:001a5b8ccf93ca2fb7c40a94417f8485e3c8b4a6 com.google.zxing:core:2.0:jar:null:compile:001a5b8ccf93ca2fb7c40a94417f8485e3c8b4a6
</urn> </urn>
@ -265,6 +262,9 @@
<urn> <urn>
org.springframework:spring-test:4.1.1.RELEASE:jar:null:test:406ce9c05253f7dd75ac3f31170c71cca7419d8a org.springframework:spring-test:4.1.1.RELEASE:jar:null:test:406ce9c05253f7dd75ac3f31170c71cca7419d8a
</urn> </urn>
<urn>
com.google.protobuf:protobuf-java:3.2.0:jar:null:compile:62ccf171a106ff6791507f2d5364c275f9a3131d
</urn>
<!-- A check for the rules themselves --> <!-- A check for the rules themselves -->
<urn> <urn>

View file

@ -32,7 +32,6 @@ import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.uri.BitcoinURI;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -156,8 +155,7 @@ public class AddressTextField extends AnchorPane {
log.warn("Amount must not be negative"); log.warn("Amount must not be negative");
setAmountAsCoin(Coin.ZERO); setAmountAsCoin(Coin.ZERO);
} }
return GUIUtil.getBitcoinURI(address.get(), amountAsCoin.get(),
return address.get() != null ? BitcoinURI.convertToBitcoinURI(address.get(), amountAsCoin.get(), paymentLabel.get());
paymentLabel.get(), null) : "";
} }
} }

View file

@ -17,7 +17,6 @@
package io.bisq.gui.main.dao.wallet.receive; package io.bisq.gui.main.dao.wallet.receive;
import io.bisq.common.UserThread;
import io.bisq.common.app.DevEnv; import io.bisq.common.app.DevEnv;
import io.bisq.common.locale.Res; import io.bisq.common.locale.Res;
import io.bisq.core.btc.wallet.BsqWalletService; 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.AddressTextField;
import io.bisq.gui.components.InputTextField; import io.bisq.gui.components.InputTextField;
import io.bisq.gui.main.dao.wallet.BsqBalanceUtil; 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.BsqFormatter;
import io.bisq.gui.util.GUIUtil;
import io.bisq.gui.util.Layout; 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 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.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
import javax.inject.Inject; import javax.inject.Inject;
import java.io.ByteArrayInputStream;
import java.util.concurrent.TimeUnit;
import static io.bisq.gui.util.FormBuilder.*; import static io.bisq.gui.util.FormBuilder.*;
@FxmlView @FxmlView
public class BsqReceiveView extends ActivatableView<GridPane, Void> { public class BsqReceiveView extends ActivatableView<GridPane, Void> {
private ImageView qrCodeImageView;
private AddressTextField addressTextField; private AddressTextField addressTextField;
private InputTextField amountTextField; private InputTextField amountTextField;
private TextField balanceTextField;
private final BsqWalletService bsqWalletService; private final BsqWalletService bsqWalletService;
private final BsqFormatter bsqFormatter; private final BsqFormatter bsqFormatter;
private final BsqBalanceUtil bsqBalanceUtil; private final BsqBalanceUtil bsqBalanceUtil;
@ -84,14 +67,6 @@ public class BsqReceiveView extends ActivatableView<GridPane, Void> {
addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.receive.fundYourWallet"), Layout.GROUP_DISTANCE); 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 = addLabelAddressTextField(root, ++gridRow, Res.getWithCol("shared.address")).second;
addressTextField.setPaymentLabel(paymentLabelString); addressTextField.setPaymentLabel(paymentLabelString);
@ -106,47 +81,15 @@ public class BsqReceiveView extends ActivatableView<GridPane, Void> {
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> { amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
addressTextField.setAmountAsCoin(bsqFormatter.parseToCoin(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())); addressTextField.setAddress(bsqFormatter.getBsqAddressStringFromAddress(bsqWalletService.freshReceiveAddress()));
updateQRCode();
} }
@Override @Override
protected void deactivate() { protected void deactivate() {
bsqBalanceUtil.deactivate(); bsqBalanceUtil.deactivate();
qrCodeImageView.setOnMouseClicked(null);
amountTextFieldSubscription.unsubscribe(); 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);
}
} }

View file

@ -56,7 +56,6 @@ import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType; import net.glxn.qrgen.image.ImageType;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.bitcoinj.uri.BitcoinURI;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -294,10 +293,9 @@ public class DepositView extends ActivatableView<VBox, Void> {
@NotNull @NotNull
private String getBitcoinURI() { private String getBitcoinURI() {
return BitcoinURI.convertToBitcoinURI(addressTextField.getAddress(), return GUIUtil.getBitcoinURI(addressTextField.getAddress(),
getAmountAsCoin(), getAmountAsCoin(),
paymentLabelString, paymentLabelString);
null);
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -58,8 +58,13 @@ import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.util.Callback; 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.script.Script;
import org.bitcoinj.wallet.Wallet;
import org.bitcoinj.wallet.listeners.WalletEventListener;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;

View file

@ -54,7 +54,11 @@ import javafx.scene.control.*;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.util.Callback; import javafx.util.Callback;
import org.apache.commons.lang3.StringUtils; 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.jetbrains.annotations.NotNull;
import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.KeyParameter;

View file

@ -66,7 +66,6 @@ import javafx.stage.Window;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import net.glxn.qrgen.QRCode; import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType; import net.glxn.qrgen.image.ImageType;
import org.bitcoinj.uri.BitcoinURI;
import org.controlsfx.control.PopOver; import org.controlsfx.control.PopOver;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
@ -1067,8 +1066,8 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
@NotNull @NotNull
private String getBitcoinURI() { private String getBitcoinURI() {
return model.getAddressAsString() != null ? BitcoinURI.convertToBitcoinURI(model.getAddressAsString(), model.dataModel.getMissingCoin().get(), return GUIUtil.getBitcoinURI(addressTextField.getAddress(), model.dataModel.getMissingCoin().get(),
model.getPaymentLabel(), null) : ""; model.getPaymentLabel());
} }
private void addAmountPriceFields() { private void addAmountPriceFields() {

View file

@ -61,7 +61,6 @@ import javafx.util.StringConverter;
import net.glxn.qrgen.QRCode; import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType; import net.glxn.qrgen.image.ImageType;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.uri.BitcoinURI;
import org.controlsfx.control.PopOver; import org.controlsfx.control.PopOver;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
@ -955,9 +954,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
@NotNull @NotNull
private String getBitcoinURI() { private String getBitcoinURI() {
String addressString = model.dataModel.getAddressEntry().getAddressString(); return GUIUtil.getBitcoinURI(model.dataModel.getAddressEntry().getAddressString(),
return addressString != null ? BitcoinURI.convertToBitcoinURI(addressString, model.dataModel.missingCoin.get(), model.dataModel.missingCoin.get(),
model.getPaymentLabel(), null) : ""; model.getPaymentLabel());
} }
private void addAmountPriceFields() { private void addAmountPriceFields() {

View file

@ -31,6 +31,7 @@ import io.bisq.common.persistence.ListPersistable;
import io.bisq.common.proto.PersistenceProtoResolver; import io.bisq.common.proto.PersistenceProtoResolver;
import io.bisq.common.storage.Storage; import io.bisq.common.storage.Storage;
import io.bisq.common.util.Utilities; import io.bisq.common.util.Utilities;
import io.bisq.core.btc.wallet.WalletUtils;
import io.bisq.core.payment.PaymentAccount; import io.bisq.core.payment.PaymentAccount;
import io.bisq.core.user.DontShowAgainLookup; import io.bisq.core.user.DontShowAgainLookup;
import io.bisq.core.user.Preferences; import io.bisq.core.user.Preferences;
@ -50,8 +51,10 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.uri.BitcoinURI;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.File; import java.io.File;
@ -382,4 +385,11 @@ public class GUIUtil {
component.setPrefHeight(available - initialOccupiedHeight.get()); component.setPrefHeight(available - initialOccupiedHeight.get());
}, 100, TimeUnit.MILLISECONDS); }, 100, TimeUnit.MILLISECONDS);
} }
public static String getBitcoinURI(String address, Coin amount, String label) {
return address != null ?
BitcoinURI.convertToBitcoinURI(Address.fromBase58(WalletUtils.getParameters(),
address), amount, label, null) :
"";
}
} }

View file

@ -75,4 +75,14 @@ public class IOPParams extends NetworkParameters {
public boolean hasMaxMoney() { public boolean hasMaxMoney() {
return false; return false;
} }
@Override
public BitcoinSerializer getSerializer(boolean parseRetain) {
return null;
}
@Override
public int getProtocolVersionNum(ProtocolVersion version) {
return 0;
}
} }

View file

@ -75,4 +75,14 @@ public class PivxParams extends NetworkParameters {
public boolean hasMaxMoney() { public boolean hasMaxMoney() {
return false; return false;
} }
@Override
public BitcoinSerializer getSerializer(boolean parseRetain) {
return null;
}
@Override
public int getProtocolVersionNum(ProtocolVersion version) {
return 0;
}
} }

View file

@ -110,7 +110,9 @@ public class Socks5DnsDiscovery extends MultiplexingDiscovery {
* Returns peer addresses. The actual DNS lookup is performed here. * Returns peer addresses. The actual DNS lookup is performed here.
*/ */
@Override @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 { try {
InetSocketAddress addr = new InetSocketAddress(DnsLookupTor.lookup(proxy, hostname), params.getPort()); InetSocketAddress addr = new InetSocketAddress(DnsLookupTor.lookup(proxy, hostname), params.getPort());
return new InetSocketAddress[]{addr}; return new InetSocketAddress[]{addr};

View file

@ -69,11 +69,10 @@ public class Socks5MultiDiscovery implements PeerDiscovery {
* Returns an array containing all the Bitcoin nodes that have been discovered. * Returns an array containing all the Bitcoin nodes that have been discovered.
*/ */
@Override @Override
public InetSocketAddress[] getPeers(long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException { public InetSocketAddress[] getPeers(long services, long timeoutValue, TimeUnit timeoutUnit) throws PeerDiscoveryException {
ArrayList<InetSocketAddress> list = new ArrayList<>(); ArrayList<InetSocketAddress> list = new ArrayList<>();
for (PeerDiscovery discovery : discoveryList) { 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()]); return list.toArray(new InetSocketAddress[list.size()]);

View file

@ -110,7 +110,9 @@ public class Socks5SeedOnionDiscovery implements PeerDiscovery {
* Returns an array containing all the Bitcoin nodes within the list. * Returns an array containing all the Bitcoin nodes within the list.
*/ */
@Override @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; return seedAddrs;
} }

View file

@ -97,7 +97,7 @@
<dependency> <dependency>
<groupId>org.bitcoinj</groupId> <groupId>org.bitcoinj</groupId>
<artifactId>bitcoinj-core</artifactId> <artifactId>bitcoinj-core</artifactId>
<version>0.13.1.11</version> <version>0.14.4.1</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>com.google.code.findbugs</groupId> <groupId>com.google.code.findbugs</groupId>