mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Add option to add opReturn data at BSQ wallet send screen for non-BSQ balance.
This can be used for providing proof of ownership of a genesis output when attaching a custom BM receiver address. See: https://github.com/bisq-network/proposals/issues/390#issuecomment-1306075295 Signed-off-by: HenrikJannsen <boilingfrog@gmx.com>
This commit is contained in:
parent
78d3fd8d44
commit
402c4075d0
@ -591,7 +591,7 @@ public class BsqWalletService extends WalletService implements DaoStateListener
|
|||||||
coinSelector.setUtxoCandidates(null); // We reuse the selectors. Reset the transactionOutputCandidates field
|
coinSelector.setUtxoCandidates(null); // We reuse the selectors. Reset the transactionOutputCandidates field
|
||||||
return tx;
|
return tx;
|
||||||
} catch (InsufficientMoneyException e) {
|
} catch (InsufficientMoneyException e) {
|
||||||
log.error("getPreparedSendTx: tx={}", tx.toString());
|
log.error("getPreparedSendTx: tx={}", tx);
|
||||||
log.error(e.toString());
|
log.error(e.toString());
|
||||||
throw new InsufficientBsqException(e.missing);
|
throw new InsufficientBsqException(e.missing);
|
||||||
}
|
}
|
||||||
|
@ -2494,6 +2494,10 @@ dao.wallet.send.receiverBtcAddress=Receiver's BTC address
|
|||||||
dao.wallet.send.setDestinationAddress=Fill in your destination address
|
dao.wallet.send.setDestinationAddress=Fill in your destination address
|
||||||
dao.wallet.send.send=Send BSQ funds
|
dao.wallet.send.send=Send BSQ funds
|
||||||
dao.wallet.send.inputControl=Select inputs
|
dao.wallet.send.inputControl=Select inputs
|
||||||
|
dao.wallet.send.addOpReturn=Add data
|
||||||
|
dao.wallet.send.preImage=Pre-image
|
||||||
|
dao.wallet.send.opReturnAsHex=Op-Return data Hex encoded
|
||||||
|
dao.wallet.send.opReturnAsHash=Hash of Op-Return data
|
||||||
dao.wallet.send.sendBtc=Send BTC funds
|
dao.wallet.send.sendBtc=Send BTC funds
|
||||||
dao.wallet.send.sendFunds.headline=Confirm withdrawal request
|
dao.wallet.send.sendFunds.headline=Confirm withdrawal request
|
||||||
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired mining fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
|
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired mining fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
|
||||||
|
@ -61,8 +61,11 @@ import bisq.core.util.validation.BtcAddressValidator;
|
|||||||
import bisq.network.p2p.P2PService;
|
import bisq.network.p2p.P2PService;
|
||||||
|
|
||||||
import bisq.common.UserThread;
|
import bisq.common.UserThread;
|
||||||
|
import bisq.common.crypto.Hash;
|
||||||
import bisq.common.handlers.ResultHandler;
|
import bisq.common.handlers.ResultHandler;
|
||||||
|
import bisq.common.util.Hex;
|
||||||
import bisq.common.util.Tuple2;
|
import bisq.common.util.Tuple2;
|
||||||
|
import bisq.common.util.Tuple3;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.InsufficientMoneyException;
|
import org.bitcoinj.core.InsufficientMoneyException;
|
||||||
@ -72,8 +75,13 @@ import org.bitcoinj.core.TransactionOutput;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
|
|
||||||
@ -87,6 +95,7 @@ import javax.annotation.Nullable;
|
|||||||
|
|
||||||
import static bisq.desktop.util.FormBuilder.addInputTextField;
|
import static bisq.desktop.util.FormBuilder.addInputTextField;
|
||||||
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
||||||
|
import static bisq.desktop.util.FormBuilder.addTopLabelTextField;
|
||||||
|
|
||||||
@FxmlView
|
@FxmlView
|
||||||
public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqBalanceListener {
|
public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqBalanceListener {
|
||||||
@ -106,12 +115,15 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
private final WalletPasswordWindow walletPasswordWindow;
|
private final WalletPasswordWindow walletPasswordWindow;
|
||||||
|
|
||||||
private int gridRow = 0;
|
private int gridRow = 0;
|
||||||
private InputTextField amountInputTextField, btcAmountInputTextField;
|
private InputTextField amountInputTextField, btcAmountInputTextField, preImageTextField;
|
||||||
private Button sendBsqButton, sendBtcButton, bsqInputControlButton, btcInputControlButton;
|
private TextField opReturnDataAsHexTextField;
|
||||||
|
private VBox opReturnDataAsHexBox;
|
||||||
|
private Label opReturnDataAsHexLabel;
|
||||||
|
private Button sendBsqButton, sendBtcButton, bsqInputControlButton, btcInputControlButton, btcOpReturnButton;
|
||||||
private InputTextField receiversAddressInputTextField, receiversBtcAddressInputTextField;
|
private InputTextField receiversAddressInputTextField, receiversBtcAddressInputTextField;
|
||||||
private ChangeListener<Boolean> focusOutListener;
|
private ChangeListener<Boolean> focusOutListener;
|
||||||
private TitledGroupBg btcTitledGroupBg;
|
private TitledGroupBg btcTitledGroupBg;
|
||||||
private ChangeListener<String> inputTextFieldListener;
|
private ChangeListener<String> inputTextFieldListener, preImageInputTextFieldListener;
|
||||||
@Nullable
|
@Nullable
|
||||||
private Set<TransactionOutput> bsqUtxoCandidates;
|
private Set<TransactionOutput> bsqUtxoCandidates;
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -166,6 +178,8 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
};
|
};
|
||||||
inputTextFieldListener = (observable, oldValue, newValue) -> onUpdateBalances();
|
inputTextFieldListener = (observable, oldValue, newValue) -> onUpdateBalances();
|
||||||
|
|
||||||
|
preImageInputTextFieldListener = (observable, oldValue, newValue) -> opReturnDataAsHexTextField.setText(getOpReturnDataAsHexFromPreImage(newValue));
|
||||||
|
|
||||||
setSendBtcGroupVisibleState(false);
|
setSendBtcGroupVisibleState(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,6 +197,7 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
bsqInputControlButton.setOnAction((event) -> onBsqInputControl());
|
bsqInputControlButton.setOnAction((event) -> onBsqInputControl());
|
||||||
sendBtcButton.setOnAction((event) -> onSendBtc());
|
sendBtcButton.setOnAction((event) -> onSendBtc());
|
||||||
btcInputControlButton.setOnAction((event) -> onBtcInputControl());
|
btcInputControlButton.setOnAction((event) -> onBtcInputControl());
|
||||||
|
btcOpReturnButton.setOnAction((event) -> onShowPreImageField());
|
||||||
|
|
||||||
receiversAddressInputTextField.focusedProperty().addListener(focusOutListener);
|
receiversAddressInputTextField.focusedProperty().addListener(focusOutListener);
|
||||||
amountInputTextField.focusedProperty().addListener(focusOutListener);
|
amountInputTextField.focusedProperty().addListener(focusOutListener);
|
||||||
@ -193,6 +208,7 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
amountInputTextField.textProperty().addListener(inputTextFieldListener);
|
amountInputTextField.textProperty().addListener(inputTextFieldListener);
|
||||||
receiversBtcAddressInputTextField.textProperty().addListener(inputTextFieldListener);
|
receiversBtcAddressInputTextField.textProperty().addListener(inputTextFieldListener);
|
||||||
btcAmountInputTextField.textProperty().addListener(inputTextFieldListener);
|
btcAmountInputTextField.textProperty().addListener(inputTextFieldListener);
|
||||||
|
preImageTextField.textProperty().addListener(preImageInputTextFieldListener);
|
||||||
|
|
||||||
bsqWalletService.addBsqBalanceListener(this);
|
bsqWalletService.addBsqBalanceListener(this);
|
||||||
|
|
||||||
@ -228,11 +244,13 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
amountInputTextField.textProperty().removeListener(inputTextFieldListener);
|
amountInputTextField.textProperty().removeListener(inputTextFieldListener);
|
||||||
receiversBtcAddressInputTextField.textProperty().removeListener(inputTextFieldListener);
|
receiversBtcAddressInputTextField.textProperty().removeListener(inputTextFieldListener);
|
||||||
btcAmountInputTextField.textProperty().removeListener(inputTextFieldListener);
|
btcAmountInputTextField.textProperty().removeListener(inputTextFieldListener);
|
||||||
|
preImageTextField.textProperty().removeListener(preImageInputTextFieldListener);
|
||||||
|
|
||||||
bsqWalletService.removeBsqBalanceListener(this);
|
bsqWalletService.removeBsqBalanceListener(this);
|
||||||
|
|
||||||
sendBsqButton.setOnAction(null);
|
sendBsqButton.setOnAction(null);
|
||||||
btcInputControlButton.setOnAction(null);
|
btcInputControlButton.setOnAction(null);
|
||||||
|
btcOpReturnButton.setOnAction(null);
|
||||||
sendBtcButton.setOnAction(null);
|
sendBtcButton.setOnAction(null);
|
||||||
bsqInputControlButton.setOnAction(null);
|
bsqInputControlButton.setOnAction(null);
|
||||||
}
|
}
|
||||||
@ -367,12 +385,14 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
btcAmountInputTextField.setVisible(visible);
|
btcAmountInputTextField.setVisible(visible);
|
||||||
sendBtcButton.setVisible(visible);
|
sendBtcButton.setVisible(visible);
|
||||||
btcInputControlButton.setVisible(visible);
|
btcInputControlButton.setVisible(visible);
|
||||||
|
btcOpReturnButton.setVisible(visible);
|
||||||
|
|
||||||
btcTitledGroupBg.setManaged(visible);
|
btcTitledGroupBg.setManaged(visible);
|
||||||
receiversBtcAddressInputTextField.setManaged(visible);
|
receiversBtcAddressInputTextField.setManaged(visible);
|
||||||
btcAmountInputTextField.setManaged(visible);
|
btcAmountInputTextField.setManaged(visible);
|
||||||
sendBtcButton.setManaged(visible);
|
sendBtcButton.setManaged(visible);
|
||||||
btcInputControlButton.setManaged(visible);
|
btcInputControlButton.setManaged(visible);
|
||||||
|
btcOpReturnButton.setManaged(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSendBtcGroup() {
|
private void addSendBtcGroup() {
|
||||||
@ -387,10 +407,24 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
btcAmountInputTextField.setValidator(btcValidator);
|
btcAmountInputTextField.setValidator(btcValidator);
|
||||||
GridPane.setColumnSpan(btcAmountInputTextField, 3);
|
GridPane.setColumnSpan(btcAmountInputTextField, 3);
|
||||||
|
|
||||||
Tuple2<Button, Button> tuple = FormBuilder.add2ButtonsAfterGroup(root, ++gridRow,
|
preImageTextField = addInputTextField(root, ++gridRow, Res.get("dao.wallet.send.preImage"));
|
||||||
Res.get("dao.wallet.send.sendBtc"), Res.get("dao.wallet.send.inputControl"));
|
GridPane.setColumnSpan(preImageTextField, 3);
|
||||||
|
preImageTextField.setVisible(false);
|
||||||
|
preImageTextField.setManaged(false);
|
||||||
|
|
||||||
|
Tuple3<Label, TextField, VBox> opReturnDataAsHexTuple = addTopLabelTextField(root, ++gridRow, Res.get("dao.wallet.send.opReturnAsHex"), -10);
|
||||||
|
opReturnDataAsHexLabel = opReturnDataAsHexTuple.first;
|
||||||
|
opReturnDataAsHexTextField = opReturnDataAsHexTuple.second;
|
||||||
|
opReturnDataAsHexBox = opReturnDataAsHexTuple.third;
|
||||||
|
GridPane.setColumnSpan(opReturnDataAsHexBox, 3);
|
||||||
|
opReturnDataAsHexBox.setVisible(false);
|
||||||
|
opReturnDataAsHexBox.setManaged(false);
|
||||||
|
|
||||||
|
Tuple3<Button, Button, Button> tuple = FormBuilder.add3ButtonsAfterGroup(root, ++gridRow,
|
||||||
|
Res.get("dao.wallet.send.sendBtc"), Res.get("dao.wallet.send.inputControl"), Res.get("dao.wallet.send.addOpReturn"));
|
||||||
sendBtcButton = tuple.first;
|
sendBtcButton = tuple.first;
|
||||||
btcInputControlButton = tuple.second;
|
btcInputControlButton = tuple.second;
|
||||||
|
btcOpReturnButton = tuple.third;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBtcInputControl() {
|
private void onBtcInputControl() {
|
||||||
@ -410,6 +444,15 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onShowPreImageField() {
|
||||||
|
btcOpReturnButton.setDisable(true);
|
||||||
|
preImageTextField.setManaged(true);
|
||||||
|
preImageTextField.setVisible(true);
|
||||||
|
opReturnDataAsHexBox.setManaged(true);
|
||||||
|
opReturnDataAsHexBox.setVisible(true);
|
||||||
|
GridPane.setRowSpan(btcTitledGroupBg, 4);
|
||||||
|
}
|
||||||
|
|
||||||
private void setBtcUtxoCandidates(Set<TransactionOutput> candidates) {
|
private void setBtcUtxoCandidates(Set<TransactionOutput> candidates) {
|
||||||
this.btcUtxoCandidates = candidates;
|
this.btcUtxoCandidates = candidates;
|
||||||
updateBtcValidator(getSpendableBtcBalance());
|
updateBtcValidator(getSpendableBtcBalance());
|
||||||
@ -430,8 +473,12 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
String receiversAddressString = receiversBtcAddressInputTextField.getText();
|
String receiversAddressString = receiversBtcAddressInputTextField.getText();
|
||||||
Coin receiverAmount = bsqFormatter.parseToBTC(btcAmountInputTextField.getText());
|
Coin receiverAmount = bsqFormatter.parseToBTC(btcAmountInputTextField.getText());
|
||||||
try {
|
try {
|
||||||
|
byte[] opReturnData = null;
|
||||||
|
if (preImageTextField.isVisible() && !preImageTextField.getText().trim().isEmpty()) {
|
||||||
|
opReturnData = getOpReturnData(preImageTextField.getText());
|
||||||
|
}
|
||||||
Transaction preparedSendTx = bsqWalletService.getPreparedSendBtcTx(receiversAddressString, receiverAmount, btcUtxoCandidates);
|
Transaction preparedSendTx = bsqWalletService.getPreparedSendBtcTx(receiversAddressString, receiverAmount, btcUtxoCandidates);
|
||||||
Transaction txWithBtcFee = btcWalletService.completePreparedSendBsqTx(preparedSendTx);
|
Transaction txWithBtcFee = btcWalletService.completePreparedBsqTx(preparedSendTx, opReturnData);
|
||||||
Transaction signedTx = bsqWalletService.signTxAndVerifyNoDustOutputs(txWithBtcFee);
|
Transaction signedTx = bsqWalletService.signTxAndVerifyNoDustOutputs(txWithBtcFee);
|
||||||
Coin miningFee = signedTx.getFee();
|
Coin miningFee = signedTx.getFee();
|
||||||
|
|
||||||
@ -449,6 +496,7 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
() -> {
|
() -> {
|
||||||
receiversBtcAddressInputTextField.setText("");
|
receiversBtcAddressInputTextField.setText("");
|
||||||
btcAmountInputTextField.setText("");
|
btcAmountInputTextField.setText("");
|
||||||
|
preImageTextField.clear();
|
||||||
|
|
||||||
receiversBtcAddressInputTextField.resetValidation();
|
receiversBtcAddressInputTextField.resetValidation();
|
||||||
btcAmountInputTextField.resetValidation();
|
btcAmountInputTextField.resetValidation();
|
||||||
@ -463,6 +511,30 @@ public class BsqSendView extends ActivatableView<GridPane, Void> implements BsqB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] getOpReturnData(String preImageAsString) {
|
||||||
|
byte[] opReturnData;
|
||||||
|
try {
|
||||||
|
// If preImage is hex encoded we use it directly
|
||||||
|
opReturnData = Hex.decode(preImageAsString);
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
opReturnData = preImageAsString.getBytes(Charsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If too long for OpReturn we hash it
|
||||||
|
if (opReturnData.length > 80) {
|
||||||
|
opReturnData = Hash.getSha256Ripemd160hash(opReturnData);
|
||||||
|
opReturnDataAsHexLabel.setText(Res.get("dao.wallet.send.opReturnAsHash"));
|
||||||
|
} else {
|
||||||
|
opReturnDataAsHexLabel.setText(Res.get("dao.wallet.send.opReturnAsHex"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return opReturnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getOpReturnDataAsHexFromPreImage(String preImage) {
|
||||||
|
return Hex.encode(getOpReturnData(preImage));
|
||||||
|
}
|
||||||
|
|
||||||
private void handleError(Throwable t) {
|
private void handleError(Throwable t) {
|
||||||
if (t instanceof InsufficientMoneyException) {
|
if (t instanceof InsufficientMoneyException) {
|
||||||
final Coin missingCoin = ((InsufficientMoneyException) t).missing;
|
final Coin missingCoin = ((InsufficientMoneyException) t).missing;
|
||||||
|
Loading…
Reference in New Issue
Block a user