diff --git a/core/src/main/java/io/bitsquare/btc/SavingsWalletCoinSelector.java b/core/src/main/java/io/bitsquare/btc/SavingsWalletCoinSelector.java
index 179f136ed2..d72d8ff219 100644
--- a/core/src/main/java/io/bitsquare/btc/SavingsWalletCoinSelector.java
+++ b/core/src/main/java/io/bitsquare/btc/SavingsWalletCoinSelector.java
@@ -24,11 +24,10 @@ import org.bitcoinj.wallet.CoinSelector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.annotation.Nullable;
import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -39,13 +38,100 @@ import static com.google.common.base.Preconditions.checkNotNull;
class SavingsWalletCoinSelector implements CoinSelector {
private static final Logger log = LoggerFactory.getLogger(SavingsWalletCoinSelector.class);
protected final NetworkParameters params;
+ @Nullable
+ private AddressEntryList addressEntryList;
+ @Nullable
+ private Set
savingsWalletAddressSet;
+
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
- public SavingsWalletCoinSelector(NetworkParameters params) {
+ public SavingsWalletCoinSelector(NetworkParameters params, AddressEntryList addressEntryList) {
this.params = params;
+ this.addressEntryList = addressEntryList;
+ }
+
+ protected SavingsWalletCoinSelector(NetworkParameters params) {
+ this.params = params;
+ }
+
+ protected boolean matchesRequirement(TransactionOutput transactionOutput) {
+ if (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash()) {
+ Address addressOutput = transactionOutput.getScriptPubKey().getToAddress(params);
+ log.trace("only lookup in savings wallet address entries");
+ log.trace(addressOutput.toString());
+
+ if (savingsWalletAddressSet != null && savingsWalletAddressSet.contains(addressOutput)) {
+ return true;
+ } else {
+ log.trace("No match found at matchesRequiredAddress addressOutput / addressEntry " +
+ addressOutput.toString() + " / " + addressOutput.toString());
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private static boolean isInBlockChainOrPending(Transaction tx) {
+ // Pick chain-included transactions and transactions that are pending.
+ TransactionConfidence confidence = tx.getConfidence();
+ TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
+
+ log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
+ return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
+ type.equals(TransactionConfidence.ConfidenceType.PENDING);
+ }
+
+ /**
+ * Sub-classes can override this to just customize whether transactions are usable, but keep age sorting.
+ */
+ protected boolean shouldSelect(Transaction tx) {
+ return isInBlockChainOrPending(tx);
+ }
+
+ @Override
+ public CoinSelection select(Coin target, List candidates) {
+ log.trace("candidates.size: " + candidates.size());
+ long targetAsLong = target.longValue();
+ log.trace("value needed: " + targetAsLong);
+ HashSet selected = new HashSet<>();
+ // Sort the inputs by age*value so we get the highest "coindays" spent.
+ ArrayList sortedOutputs = new ArrayList<>(candidates);
+ // When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting
+ // them in order to improve performance.
+ if (!target.equals(NetworkParameters.MAX_MONEY)) {
+ sortOutputs(sortedOutputs);
+ }
+ // Now iterate over the sorted outputs until we have got as close to the target as possible or a little
+ // bit over (excessive value will be change).
+ long total = 0;
+ if (addressEntryList != null) {
+ savingsWalletAddressSet = addressEntryList.stream()
+ .filter(addressEntry1 -> addressEntry1.getContext() == AddressEntry.Context.SAVINGS)
+ .map(AddressEntry::getAddress)
+ .collect(Collectors.toSet());
+ }
+ for (TransactionOutput output : sortedOutputs) {
+ if (total >= targetAsLong) {
+ break;
+ }
+ // Only pick chain-included transactions, or transactions that are ours and pending.
+ // Only select outputs from our defined address(es)
+ if (!shouldSelect(output.getParentTransaction()) || !matchesRequirement(output)) {
+ continue;
+ }
+
+ selected.add(output);
+ total += output.getValue().longValue();
+
+ log.debug("adding up outputs: output/total: " + output.getValue().longValue() + "/" + total);
+ }
+ // Total may be lower than target here, if the given candidates were insufficient to create to requested
+ // transaction.
+ return new CoinSelection(Coin.valueOf(total), selected);
}
@VisibleForTesting
@@ -71,61 +157,4 @@ class SavingsWalletCoinSelector implements CoinSelector {
});
}
- private static boolean isInBlockChainOrPending(Transaction tx) {
- // Pick chain-included transactions and transactions that are pending.
- TransactionConfidence confidence = tx.getConfidence();
- TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
-
- log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
- return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
- type.equals(TransactionConfidence.ConfidenceType.PENDING);
- }
-
- /**
- * Sub-classes can override this to just customize whether transactions are usable, but keep age sorting.
- */
- protected boolean shouldSelect(Transaction tx) {
- return isInBlockChainOrPending(tx);
- }
-
- protected boolean matchesRequirement(TransactionOutput transactionOutput) {
- return (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash());
- }
-
- @Override
- public CoinSelection select(Coin target, List candidates) {
- log.trace("candidates.size: " + candidates.size());
- long targetAsLong = target.longValue();
- log.trace("value needed: " + targetAsLong);
- HashSet selected = new HashSet<>();
- // Sort the inputs by age*value so we get the highest "coindays" spent.
- ArrayList sortedOutputs = new ArrayList<>(candidates);
- // When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting
- // them in order to improve performance.
- if (!target.equals(NetworkParameters.MAX_MONEY)) {
- sortOutputs(sortedOutputs);
- }
- // Now iterate over the sorted outputs until we have got as close to the target as possible or a little
- // bit over (excessive value will be change).
- long total = 0;
- for (TransactionOutput output : sortedOutputs) {
- if (total >= targetAsLong) {
- break;
- }
- // Only pick chain-included transactions, or transactions that are ours and pending.
- // Only select outputs from our defined address(es)
- if (!shouldSelect(output.getParentTransaction()) || !matchesRequirement(output)) {
- continue;
- }
-
- selected.add(output);
- total += output.getValue().longValue();
-
- log.debug("adding up outputs: output/total: " + output.getValue().longValue() + "/" + total);
- }
- // Total may be lower than target here, if the given candidates were insufficient to create to requested
- // transaction.
- return new CoinSelection(Coin.valueOf(total), selected);
- }
-
}
diff --git a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java
index 932fb8cf20..8965ad04e0 100644
--- a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java
+++ b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java
@@ -101,6 +101,7 @@ public class TradeWalletService {
private WalletAppKit walletAppKit;
@Nullable
private KeyParameter aesKey;
+ private AddressEntryList addressEntryList;
///////////////////////////////////////////////////////////////////////////////////////////
@@ -158,7 +159,7 @@ public class TradeWalletService {
sendRequest.shuffleOutputs = false;
sendRequest.aesKey = aesKey;
if (useSavingsWallet)
- sendRequest.coinSelector = new SavingsWalletCoinSelector(params);
+ sendRequest.coinSelector = new SavingsWalletCoinSelector(params, addressEntryList);
else
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry);
// We use a fixed fee
@@ -1064,4 +1065,8 @@ public class TradeWalletService {
throw new WalletException(t);
}
}
+
+ public void setAddressEntryList(AddressEntryList addressEntryList) {
+ this.addressEntryList = addressEntryList;
+ }
}
diff --git a/core/src/main/java/io/bitsquare/btc/WalletService.java b/core/src/main/java/io/bitsquare/btc/WalletService.java
index 9f8ad2bb14..be7d4247e7 100644
--- a/core/src/main/java/io/bitsquare/btc/WalletService.java
+++ b/core/src/main/java/io/bitsquare/btc/WalletService.java
@@ -188,6 +188,7 @@ public class WalletService {
// set after wallet is ready
tradeWalletService.setWalletAppKit(walletAppKit);
+ tradeWalletService.setAddressEntryList(addressEntryList);
timeoutTimer.stop();
// onSetupCompleted in walletAppKit is not the called on the last invocations, so we add a bit of delay
diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/deposit/DepositView.java b/gui/src/main/java/io/bitsquare/gui/main/funds/deposit/DepositView.java
index 613ecf05ed..b53089fa39 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/funds/deposit/DepositView.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/funds/deposit/DepositView.java
@@ -205,25 +205,6 @@ public class DepositView extends ActivatableView {
};
}
- private Coin getAmountAsCoin() {
- Coin senderAmount = formatter.parseToCoin(amountTextField.getText());
- if (!Restrictions.isAboveFixedTxFeeAndDust(senderAmount)) {
- senderAmount = Coin.ZERO;
- /* new Popup()
- .warning("The amount is lower than the transaction fee and the min. possible tx value (dust).")
- .show();*/
- }
- return senderAmount;
- }
-
- @NotNull
- private String getBitcoinURI() {
- return BitcoinURI.convertToBitcoinURI(addressTextField.getAddress(),
- getAmountAsCoin(),
- paymentLabel,
- null);
- }
-
@Override
protected void activate() {
tableView.getSelectionModel().selectedItemProperty().addListener(tableViewSelectionListener);
@@ -236,6 +217,9 @@ public class DepositView extends ActivatableView {
addressTextField.setAmountAsCoin(formatter.parseToCoin(t));
updateQRCode();
});
+
+ if (tableView.getSelectionModel().getSelectedItem() == null && !sortedList.isEmpty())
+ tableView.getSelectionModel().select(0);
}
@Override
@@ -309,6 +293,24 @@ public class DepositView extends ActivatableView {
.forEach(e -> observableList.add(new DepositListItem(e, walletService, formatter)));
}
+ private Coin getAmountAsCoin() {
+ Coin senderAmount = formatter.parseToCoin(amountTextField.getText());
+ if (!Restrictions.isAboveFixedTxFeeAndDust(senderAmount)) {
+ senderAmount = Coin.ZERO;
+ /* new Popup()
+ .warning("The amount is lower than the transaction fee and the min. possible tx value (dust).")
+ .show();*/
+ }
+ return senderAmount;
+ }
+
+ @NotNull
+ private String getBitcoinURI() {
+ return BitcoinURI.convertToBitcoinURI(addressTextField.getAddress(),
+ getAmountAsCoin(),
+ paymentLabel,
+ null);
+ }
///////////////////////////////////////////////////////////////////////////////////////////
// ColumnCellFactories
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java
index 934744c47b..f683d0ea13 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java
@@ -228,7 +228,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
calculateVolume();
calculateTotalToPay();
- updateBalance();
}
void onTabSelected(boolean isSelected) {
@@ -394,11 +393,13 @@ class CreateOfferDataModel extends ActivatableDataModel {
}
void calculateTotalToPay() {
- if (securityDepositAsCoin != null) {
+ if (direction != null && amountAsCoin.get() != null) {
Coin feeAndSecDeposit = offerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin);
- Coin feeAndSecDepositAndAmount = feeAndSecDeposit.add(amountAsCoin.get() == null ? Coin.ZERO : amountAsCoin.get());
+ Coin feeAndSecDepositAndAmount = feeAndSecDeposit.add(amountAsCoin.get());
Coin required = direction == Offer.Direction.BUY ? feeAndSecDeposit : feeAndSecDepositAndAmount;
totalToPayAsCoin.set(required);
+ log.debug("totalToPayAsCoin " + totalToPayAsCoin.get().toFriendlyString());
+ updateBalance();
}
}
@@ -407,11 +408,12 @@ class CreateOfferDataModel extends ActivatableDataModel {
if (useSavingsWallet) {
Coin savingWalletBalance = walletService.getSavingWalletBalance();
totalAvailableBalance = savingWalletBalance.add(tradeWalletBalance);
-
- if (totalToPayAsCoin.get() != null && totalAvailableBalance.compareTo(totalToPayAsCoin.get()) > 0)
- balance.set(totalToPayAsCoin.get());
- else
- balance.set(totalAvailableBalance);
+ if (totalToPayAsCoin.get() != null) {
+ if (totalAvailableBalance.compareTo(totalToPayAsCoin.get()) > 0)
+ balance.set(totalToPayAsCoin.get());
+ else
+ balance.set(totalAvailableBalance);
+ }
} else {
balance.set(tradeWalletBalance);
}
@@ -422,6 +424,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
missingCoin.set(Coin.ZERO);
}
+ log.debug("missingCoin " + missingCoin.get().toFriendlyString());
+
isWalletFunded.set(isBalanceSufficient(balance.get()));
if (totalToPayAsCoin.get() != null && isWalletFunded.get() && walletFundedNotification == null) {
walletFundedNotification = new Notification()
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java
index 843c05fe56..be2f81cdb4 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java
@@ -176,7 +176,6 @@ class TakeOfferDataModel extends ActivatableDataModel {
calculateVolume();
calculateTotalToPay();
- updateBalance();
balanceListener = new BalanceListener(addressEntry.getAddress()) {
@Override
@@ -325,11 +324,21 @@ class TakeOfferDataModel extends ActivatableDataModel {
}
}
+ void setAmount(Coin amount) {
+ amountAsCoin.set(amount);
+ calculateTotalToPay();
+ }
+
void calculateTotalToPay() {
- if (getDirection() == Offer.Direction.SELL)
- totalToPayAsCoin.set(takerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin));
- else
- totalToPayAsCoin.set(takerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin).add(amountAsCoin.get()));
+ if (offer != null && amountAsCoin.get() != null) {
+ if (getDirection() == Offer.Direction.SELL)
+ totalToPayAsCoin.set(takerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin));
+ else
+ totalToPayAsCoin.set(takerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin).add(amountAsCoin.get()));
+
+ updateBalance();
+ log.debug("totalToPayAsCoin " + totalToPayAsCoin.get().toFriendlyString());
+ }
}
void updateBalance() {
@@ -337,11 +346,12 @@ class TakeOfferDataModel extends ActivatableDataModel {
if (useSavingsWallet) {
Coin savingWalletBalance = walletService.getSavingWalletBalance();
totalAvailableBalance = savingWalletBalance.add(tradeWalletBalance);
-
- if (totalToPayAsCoin.get() != null && totalAvailableBalance.compareTo(totalToPayAsCoin.get()) > 0)
- balance.set(totalToPayAsCoin.get());
- else
- balance.set(totalAvailableBalance);
+ if (totalToPayAsCoin.get() != null) {
+ if (totalAvailableBalance.compareTo(totalToPayAsCoin.get()) > 0)
+ balance.set(totalToPayAsCoin.get());
+ else
+ balance.set(totalAvailableBalance);
+ }
} else {
balance.set(tradeWalletBalance);
}
@@ -350,17 +360,18 @@ class TakeOfferDataModel extends ActivatableDataModel {
if (missingCoin.get().isNegative())
missingCoin.set(Coin.ZERO);
}
+ log.debug("missingCoin " + missingCoin.get().toFriendlyString());
isWalletFunded.set(isBalanceSufficient(balance.get()));
if (totalToPayAsCoin.get() != null && isWalletFunded.get() && walletFundedNotification == null) {
- walletFundedNotification = new Notification()
- .headLine("Trading wallet update")
- .notification("Your trading wallet is sufficiently funded.\n" +
- "Amount: " + formatter.formatCoinWithCode(totalToPayAsCoin.get()))
- .autoClose();
+ walletFundedNotification = new Notification()
+ .headLine("Trading wallet update")
+ .notification("Your trading wallet is sufficiently funded.\n" +
+ "Amount: " + formatter.formatCoinWithCode(totalToPayAsCoin.get()))
+ .autoClose();
- walletFundedNotification.show();
- }
+ walletFundedNotification.show();
+ }
}
private boolean isBalanceSufficient(Coin balance) {
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java
index 9f83b7e738..d1b9708e24 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java
@@ -520,7 +520,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel im
}
private void setAmountToModel() {
- dataModel.amountAsCoin.set(formatter.parseToCoinWith4Decimals(amount.get()));
+ dataModel.setAmount(formatter.parseToCoinWith4Decimals(amount.get()));
}
diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java
index db97460e4b..339299f870 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java
@@ -228,7 +228,7 @@ public class SellerStep3View extends TradeStepView {
if (model.p2PService.isBootstrapped()) {
Preferences preferences = model.dataModel.preferences;
String key = "confirmPaymentReceived";
- if (preferences.showAgain(key)) {
+ if (!BitsquareApp.DEV_MODE && preferences.showAgain(key)) {
new Popup()
.headLine("Confirm that you have received the payment")
.confirmation("Have you received the " + CurrencyUtil.getNameByCode(model.dataModel.getCurrencyCode()) +