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));
}
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();
}

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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<ChildNumber> 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<ChildNumber> getAccountPath() {
return BIP44_BSQ_ACCOUNT_PATH;

View file

@ -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<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.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;

View file

@ -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;

View file

@ -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<ChildNumber> getAccountPath() {
return BIP44_BTC_ACCOUNT_PATH;

View file

@ -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<Transaction> 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<Transaction> 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> addressEntry = findAddressEntry(fromAddress, context);
@ -717,21 +725,22 @@ public class BtcWalletService extends WalletService {
return sendRequest;
}
private Wallet.SendRequest getSendRequestForMultipleAddresses(Set<String> fromAddresses,
String toAddress,
Coin amount,
Coin fee,
@Nullable String changeAddress,
@Nullable KeyParameter aesKey) throws
private SendRequest getSendRequestForMultipleAddresses(Set<String> 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<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.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

View file

@ -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;
}
/**
* <p>Override this to return wallet extensions if any are necessary.</p>
* <p>
@ -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);

View file

@ -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<TransactionConfidence> 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());
}

View file

@ -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;

View file

@ -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<PeerAddress> 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<Message> 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);

View file

@ -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();
}
}

View file

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

View file

@ -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());
}
}

View file

@ -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<GridPane, Void> {
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<GridPane, Void> {
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<GridPane, Void> {
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);
}
}

View file

@ -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<VBox, Void> {
@NotNull
private String getBitcoinURI() {
return BitcoinURI.convertToBitcoinURI(addressTextField.getAddress(),
return GUIUtil.getBitcoinURI(addressTextField.getAddress(),
getAmountAsCoin(),
paymentLabelString,
null);
paymentLabelString);
}
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -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;

View file

@ -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;

View file

@ -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<AnchorPane, CreateO
@NotNull
private String getBitcoinURI() {
return model.getAddressAsString() != null ? BitcoinURI.convertToBitcoinURI(model.getAddressAsString(), model.dataModel.getMissingCoin().get(),
model.getPaymentLabel(), null) : "";
return GUIUtil.getBitcoinURI(addressTextField.getAddress(), model.dataModel.getMissingCoin().get(),
model.getPaymentLabel());
}
private void addAmountPriceFields() {

View file

@ -61,7 +61,6 @@ import javafx.util.StringConverter;
import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType;
import org.bitcoinj.core.Coin;
import org.bitcoinj.uri.BitcoinURI;
import org.controlsfx.control.PopOver;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
@ -955,9 +954,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
@NotNull
private String getBitcoinURI() {
String addressString = model.dataModel.getAddressEntry().getAddressString();
return addressString != null ? BitcoinURI.convertToBitcoinURI(addressString, model.dataModel.missingCoin.get(),
model.getPaymentLabel(), null) : "";
return GUIUtil.getBitcoinURI(model.dataModel.getAddressEntry().getAddressString(),
model.dataModel.missingCoin.get(),
model.getPaymentLabel());
}
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.storage.Storage;
import io.bisq.common.util.Utilities;
import io.bisq.core.btc.wallet.WalletUtils;
import io.bisq.core.payment.PaymentAccount;
import io.bisq.core.user.DontShowAgainLookup;
import io.bisq.core.user.Preferences;
@ -50,8 +51,10 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.uri.BitcoinURI;
import javax.annotation.Nullable;
import java.io.File;
@ -382,4 +385,11 @@ public class GUIUtil {
component.setPrefHeight(available - initialOccupiedHeight.get());
}, 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() {
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() {
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.
*/
@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 {
InetSocketAddress addr = new InetSocketAddress(DnsLookupTor.lookup(proxy, hostname), params.getPort());
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.
*/
@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<>();
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()]);

View file

@ -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;
}

View file

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