Skip to content

Commit

Permalink
Refactor desktop's BsqSendView, share with api
Browse files Browse the repository at this point in the history
Moved just enough code out of BsqSendView to avoid desktop/api
'sendbsq' duplication, at the cost of adding 1 new method to
BsqSendView.

- Created new BsqTransferModel to hold tx details shared by desktop and api.

- Created new BsqTransferService to send bsq using a BsqTransferModel shared
  by desktop and api.

- Uncommented CoreWalletsService#sendBsq implementation.

- Uncommented sendbsq tests.
  • Loading branch information
ghubstan committed Nov 13, 2020
1 parent dc3274f commit 446bd32
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 24 deletions.
4 changes: 2 additions & 2 deletions apitest/src/test/java/bisq/apitest/scenario/WalletTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ public void testBsqWalletFunding(final TestInfo testInfo) {

bsqWalletTest.testGetUnusedBsqAddress();
bsqWalletTest.testInitialBsqBalances(testInfo);
// bsqWalletTest.testSendBsqAndCheckBalancesBeforeGeneratingBtcBlock(testInfo);
// bsqWalletTest.testBalancesAfterSendingBsqAndGeneratingBtcBlock(testInfo);
bsqWalletTest.testSendBsqAndCheckBalancesBeforeGeneratingBtcBlock(testInfo);
bsqWalletTest.testBalancesAfterSendingBsqAndGeneratingBtcBlock(testInfo);
}

@Test
Expand Down
44 changes: 37 additions & 7 deletions core/src/main/java/bisq/core/api/CoreWalletsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,25 @@
import bisq.core.api.model.BsqBalanceInfo;
import bisq.core.api.model.BtcBalanceInfo;
import bisq.core.btc.Balances;
import bisq.core.btc.exceptions.BsqChangeBelowDustException;
import bisq.core.btc.exceptions.TransactionVerificationException;
import bisq.core.btc.exceptions.WalletException;
import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.model.BsqTransferModel;
import bisq.core.btc.wallet.BsqTransferService;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.TxBroadcaster;
import bisq.core.btc.wallet.WalletsManager;
import bisq.core.util.coin.BsqFormatter;

import bisq.common.Timer;
import bisq.common.UserThread;

import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.crypto.KeyCrypterScrypt;

Expand All @@ -43,6 +52,8 @@

import org.bouncycastle.crypto.params.KeyParameter;

import java.math.BigDecimal;

import java.util.List;
import java.util.Optional;
import java.util.function.Function;
Expand All @@ -52,6 +63,7 @@

import javax.annotation.Nullable;

import static bisq.core.util.ParsingUtils.parseToCoin;
import static java.lang.String.format;
import static java.util.concurrent.TimeUnit.SECONDS;

Expand All @@ -61,6 +73,8 @@ class CoreWalletsService {
private final Balances balances;
private final WalletsManager walletsManager;
private final BsqWalletService bsqWalletService;
private final BsqTransferService bsqTransferService;
private final BsqFormatter bsqFormatter;
private final BtcWalletService btcWalletService;

@Nullable
Expand All @@ -73,10 +87,14 @@ class CoreWalletsService {
public CoreWalletsService(Balances balances,
WalletsManager walletsManager,
BsqWalletService bsqWalletService,
BsqTransferService bsqTransferService,
BsqFormatter bsqFormatter,
BtcWalletService btcWalletService) {
this.balances = balances;
this.walletsManager = walletsManager;
this.bsqWalletService = bsqWalletService;
this.bsqTransferService = bsqTransferService;
this.bsqFormatter = bsqFormatter;
this.btcWalletService = btcWalletService;
}

Expand Down Expand Up @@ -197,15 +215,9 @@ String getUnusedBsqAddress() {
return bsqWalletService.getUnusedBsqAddressAsString();
}

@SuppressWarnings("unused")
void sendBsq(String address,
double amount,
TxBroadcaster.Callback callback) {

throw new UnsupportedOperationException("sendbsq not implemented");

// TODO Uncomment after desktop::BsqSendView refactoring.
/*
try {
LegacyAddress legacyAddress = getValidBsqLegacyAddress(address);
Coin receiverAmount = getValidBsqTransferAmount(amount);
Expand All @@ -218,7 +230,6 @@ void sendBsq(String address,
log.error("", ex);
throw new IllegalStateException(ex);
}
*/
}

int getNumConfirmationsForMostRecentTransaction(String addressString) {
Expand Down Expand Up @@ -331,6 +342,25 @@ private void verifyEncryptedWalletIsUnlocked() {
throw new IllegalStateException("wallet is locked");
}

// Returns a LegacyAddress for the string, or a RuntimeException if invalid.
private LegacyAddress getValidBsqLegacyAddress(String address) {
try {
return bsqFormatter.getAddressFromBsqAddress(address);
} catch (Throwable t) {
log.error("", t);
throw new IllegalStateException(format("%s is not a valid bsq address", address));
}
}

// Returns a Coin for the double amount, or a RuntimeException if invalid.
private Coin getValidBsqTransferAmount(double amount) {
Coin amountAsCoin = parseToCoin(new BigDecimal(amount).toString(), bsqFormatter);
if (amountAsCoin.equals(Coin.ZERO))
throw new IllegalStateException(format("%.2f bsq is an invalid send amount", amount));

return amountAsCoin;
}

private KeyCrypterScrypt getKeyCrypterScrypt() {
KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt();
if (keyCrypterScrypt == null)
Expand Down
77 changes: 77 additions & 0 deletions core/src/main/java/bisq/core/btc/model/BsqTransferModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package bisq.core.btc.model;

import bisq.core.dao.state.model.blockchain.TxType;
import bisq.core.util.coin.CoinUtil;

import org.bitcoinj.core.Coin;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.Transaction;

import lombok.Getter;

@Getter
public final class BsqTransferModel {

private final LegacyAddress receiverAddress;
private final Coin receiverAmount;
private final Transaction preparedSendTx;
private final Transaction txWithBtcFee;
private final Transaction signedTx;
private final Coin miningFee;
private final int txSize;
private final TxType txType;

public BsqTransferModel(LegacyAddress receiverAddress,
Coin receiverAmount,
Transaction preparedSendTx,
Transaction txWithBtcFee,
Transaction signedTx) {
this.receiverAddress = receiverAddress;
this.receiverAmount = receiverAmount;
this.preparedSendTx = preparedSendTx;
this.txWithBtcFee = txWithBtcFee;
this.signedTx = signedTx;
this.miningFee = signedTx.getFee();
this.txSize = signedTx.bitcoinSerialize().length;
this.txType = TxType.TRANSFER_BSQ;
}

public String getReceiverAddressAsString() {
return receiverAddress.toString();
}

public double getMiningFeeInSatoshisPerByte() {
return CoinUtil.getFeePerByte(miningFee, txSize);
}

public double getTxSizeInKb() {
return txSize / 1000d;
}

public String toShortString() {
return "{" + "\n" +
" receiverAddress='" + getReceiverAddressAsString() + '\'' + "\n" +
", receiverAmount=" + receiverAmount + "\n" +
", txWithBtcFee.txId=" + txWithBtcFee.getTxId() + "\n" +
", miningFee=" + miningFee + "\n" +
", miningFeeInSatoshisPerByte=" + getMiningFeeInSatoshisPerByte() + "\n" +
", txSizeInKb=" + getTxSizeInKb() + "\n" +
'}';
}

@Override
public String toString() {
return "BsqTransferModel{" + "\n" +
" receiverAddress='" + getReceiverAddressAsString() + '\'' + "\n" +
", receiverAmount=" + receiverAmount + "\n" +
", preparedSendTx=" + preparedSendTx + "\n" +
", txWithBtcFee=" + txWithBtcFee + "\n" +
", signedTx=" + signedTx + "\n" +
", miningFee=" + miningFee + "\n" +
", miningFeeInSatoshisPerByte=" + getMiningFeeInSatoshisPerByte() + "\n" +
", txSize=" + txSize + "\n" +
", txSizeInKb=" + getTxSizeInKb() + "\n" +
", txType=" + txType + "\n" +
'}';
}
}
59 changes: 59 additions & 0 deletions core/src/main/java/bisq/core/btc/wallet/BsqTransferService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package bisq.core.btc.wallet;

import bisq.core.btc.exceptions.BsqChangeBelowDustException;
import bisq.core.btc.exceptions.TransactionVerificationException;
import bisq.core.btc.exceptions.WalletException;
import bisq.core.btc.model.BsqTransferModel;

import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.Transaction;

import javax.inject.Inject;
import javax.inject.Singleton;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Singleton
public class BsqTransferService {

private final WalletsManager walletsManager;
private final BsqWalletService bsqWalletService;
private final BtcWalletService btcWalletService;

@Inject
public BsqTransferService(WalletsManager walletsManager,
BsqWalletService bsqWalletService,
BtcWalletService btcWalletService) {
this.walletsManager = walletsManager;
this.bsqWalletService = bsqWalletService;
this.btcWalletService = btcWalletService;
}

public BsqTransferModel getBsqTransferModel(LegacyAddress address,
Coin receiverAmount)
throws TransactionVerificationException,
WalletException,
BsqChangeBelowDustException,
InsufficientMoneyException {

Transaction preparedSendTx = bsqWalletService.getPreparedSendBsqTx(address.toString(), receiverAmount);
Transaction txWithBtcFee = btcWalletService.completePreparedSendBsqTx(preparedSendTx, true);
Transaction signedTx = bsqWalletService.signTx(txWithBtcFee);

return new BsqTransferModel(address,
receiverAmount,
preparedSendTx,
txWithBtcFee,
signedTx);
}

public void sendFunds(BsqTransferModel bsqTransferModel, TxBroadcaster.Callback callback) {
log.info("Publishing BSQ transfer {}", bsqTransferModel.toShortString());
walletsManager.publishAndCommitBsqTx(bsqTransferModel.getTxWithBtcFee(),
bsqTransferModel.getTxType(),
callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@
import bisq.desktop.util.validation.BtcValidator;

import bisq.core.btc.exceptions.BsqChangeBelowDustException;
import bisq.core.btc.exceptions.TransactionVerificationException;
import bisq.core.btc.exceptions.TxBroadcastException;
import bisq.core.btc.exceptions.WalletException;
import bisq.core.btc.listeners.BsqBalanceListener;
import bisq.core.btc.model.BsqTransferModel;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BsqTransferService;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.Restrictions;
Expand All @@ -46,10 +50,10 @@
import bisq.core.dao.state.model.blockchain.TxType;
import bisq.core.locale.Res;
import bisq.core.util.FormattingUtils;
import bisq.core.util.ParsingUtils;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.coin.CoinUtil;
import bisq.core.util.ParsingUtils;
import bisq.core.util.validation.BtcAddressValidator;

import bisq.network.p2p.P2PService;
Expand All @@ -59,6 +63,7 @@

import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.Transaction;

import javax.inject.Inject;
Expand Down Expand Up @@ -90,6 +95,7 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
private final BtcValidator btcValidator;
private final BsqAddressValidator bsqAddressValidator;
private final BtcAddressValidator btcAddressValidator;
private final BsqTransferService bsqTransferService;
private final WalletPasswordWindow walletPasswordWindow;

private int gridRow = 0;
Expand Down Expand Up @@ -119,6 +125,7 @@ private BsqSendView(BsqWalletService bsqWalletService,
BtcValidator btcValidator,
BsqAddressValidator bsqAddressValidator,
BtcAddressValidator btcAddressValidator,
BsqTransferService bsqTransferService,
WalletPasswordWindow walletPasswordWindow) {
this.bsqWalletService = bsqWalletService;
this.btcWalletService = btcWalletService;
Expand All @@ -133,6 +140,7 @@ private BsqSendView(BsqWalletService bsqWalletService,
this.btcValidator = btcValidator;
this.bsqAddressValidator = bsqAddressValidator;
this.btcAddressValidator = btcAddressValidator;
this.bsqTransferService = bsqTransferService;
this.walletPasswordWindow = walletPasswordWindow;
}

Expand Down Expand Up @@ -241,22 +249,15 @@ private void addSendBsqGroup() {
sendBsqButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.wallet.send.send"));

sendBsqButton.setOnAction((event) -> {
// TODO break up in methods
if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup)) {
String receiversAddressString = bsqFormatter.getAddressFromBsqAddress(receiversAddressInputTextField.getText()).toString();
Coin receiverAmount = ParsingUtils.parseToCoin(amountInputTextField.getText(), bsqFormatter);
try {
Transaction preparedSendTx = bsqWalletService.getPreparedSendBsqTx(receiversAddressString, receiverAmount);
Transaction txWithBtcFee = btcWalletService.completePreparedSendBsqTx(preparedSendTx, true);
Transaction signedTx = bsqWalletService.signTx(txWithBtcFee);
Coin miningFee = signedTx.getFee();
int txSize = signedTx.bitcoinSerialize().length;
showPublishTxPopup(receiverAmount,
txWithBtcFee,
TxType.TRANSFER_BSQ,
miningFee,
txSize,
receiversAddressInputTextField.getText(),
BsqTransferModel model = getBsqTransferModel();
showPublishTxPopup(model.getReceiverAmount(),
model.getTxWithBtcFee(),
model.getTxType(),
model.getMiningFee(),
model.getTxSize(),
model.getReceiverAddressAsString(),
bsqFormatter,
btcFormatter,
() -> {
Expand All @@ -273,6 +274,16 @@ private void addSendBsqGroup() {
});
}

private BsqTransferModel getBsqTransferModel()
throws InsufficientMoneyException,
TransactionVerificationException,
BsqChangeBelowDustException,
WalletException {
Coin receiverAmount = ParsingUtils.parseToCoin(amountInputTextField.getText(), bsqFormatter);
LegacyAddress legacyAddress = bsqFormatter.getAddressFromBsqAddress(receiversAddressInputTextField.getText());
return bsqTransferService.getBsqTransferModel(legacyAddress, receiverAmount);
}

private void setSendBtcGroupVisibleState(boolean visible) {
btcTitledGroupBg.setVisible(visible);
receiversBtcAddressInputTextField.setVisible(visible);
Expand Down

0 comments on commit 446bd32

Please sign in to comment.