mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2026-09-14 00:45:08 +00:00
scope payjoin endpoints to the payment tab and transaction instead of the destination address
This commit is contained in:
@@ -19,6 +19,7 @@ import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import com.sparrowwallet.sparrow.net.Auth47;
|
||||
import com.sparrowwallet.drongo.protocol.BlockHeader;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
import com.sparrowwallet.drongo.uri.BitcoinURI;
|
||||
@@ -151,7 +152,7 @@ public class AppServices {
|
||||
|
||||
private static final List<URI> argUris = new ArrayList<>();
|
||||
|
||||
private static final Map<Address, BitcoinURI> payjoinURIs = new HashMap<>();
|
||||
private static final Map<Sha256Hash, BitcoinURI> payjoinURIs = new HashMap<>();
|
||||
|
||||
private final ChangeListener<Boolean> onlineServicesListener = new ChangeListener<>() {
|
||||
@Override
|
||||
@@ -870,19 +871,21 @@ public class AppServices {
|
||||
return devices == null ? new ArrayList<>() : devices;
|
||||
}
|
||||
|
||||
public static BitcoinURI getPayjoinURI(Address address) {
|
||||
return payjoinURIs.get(address);
|
||||
public static BitcoinURI getPayjoinURI(PSBT psbt) {
|
||||
return psbt == null ? null : payjoinURIs.get(psbt.getTransaction().calculateTxId(false));
|
||||
}
|
||||
|
||||
public static void addPayjoinURI(BitcoinURI bitcoinURI) {
|
||||
public static void addPayjoinURI(PSBT psbt, BitcoinURI bitcoinURI) {
|
||||
if(bitcoinURI.getPayjoinUrl() == null || bitcoinURI.getAddress() == null) {
|
||||
throw new IllegalArgumentException("Not a valid payjoin URI");
|
||||
}
|
||||
payjoinURIs.put(bitcoinURI.getAddress(), bitcoinURI);
|
||||
payjoinURIs.put(psbt.getTransaction().calculateTxId(false), bitcoinURI);
|
||||
}
|
||||
|
||||
public static void clearPayjoinURI(Address address) {
|
||||
payjoinURIs.remove(address);
|
||||
public static void clearPayjoinURI(PSBT psbt) {
|
||||
if(psbt != null) {
|
||||
payjoinURIs.remove(psbt.getTransaction().calculateTxId(false));
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearTransactionHistoryCache(Wallet wallet) {
|
||||
@@ -1123,7 +1126,7 @@ public class AppServices {
|
||||
if(wallet != null) {
|
||||
final Wallet sendingWallet = wallet;
|
||||
EventManager.get().post(new SendActionEvent(sendingWallet, new ArrayList<>(sendingWallet.getSpendableUtxos().keySet()), true));
|
||||
Platform.runLater(() -> EventManager.get().post(new SendPaymentsEvent(sendingWallet, List.of(bitcoinURI.toPayment()))));
|
||||
Platform.runLater(() -> EventManager.get().post(new SendPaymentsEvent(sendingWallet, List.of(bitcoinURI.toPayment()), bitcoinURI)));
|
||||
}
|
||||
} catch(Exception e) {
|
||||
showErrorDialog("Not a valid bitcoin URI", e.getMessage());
|
||||
|
||||
@@ -77,6 +77,7 @@ public class TransactionDiagram extends GridPane {
|
||||
private final BooleanProperty finalProperty = new SimpleBooleanProperty(false);
|
||||
private final ObjectProperty<TransactionDiagramLabel> labelProperty = new SimpleObjectProperty<>(null);
|
||||
private final ObjectProperty<OptimizationStrategy> optimizationStrategyProperty = new SimpleObjectProperty<>(OptimizationStrategy.EFFICIENCY);
|
||||
private final ObjectProperty<BitcoinURI> payjoinURIProperty = new SimpleObjectProperty<>(null);
|
||||
private boolean expanded;
|
||||
private TransactionDiagram expandedDiagram;
|
||||
private ContextMenu contextMenu;
|
||||
@@ -224,6 +225,7 @@ public class TransactionDiagram extends GridPane {
|
||||
|
||||
private void updateDerivedDiagram(TransactionDiagram diagram) {
|
||||
diagram.setOptimizationStrategy(getOptimizationStrategy());
|
||||
diagram.setPayjoinURI(getPayjoinURI());
|
||||
diagram.walletTx = walletTx;
|
||||
|
||||
if(diagram.isExpanded()) {
|
||||
@@ -342,20 +344,12 @@ public class TransactionDiagram extends GridPane {
|
||||
}
|
||||
}
|
||||
|
||||
private BitcoinURI getPayjoinURI() {
|
||||
for(Payment payment : walletTx.getPayments()) {
|
||||
try {
|
||||
Address address = payment.getAddress();
|
||||
BitcoinURI bitcoinURI = AppServices.getPayjoinURI(address);
|
||||
if(bitcoinURI != null) {
|
||||
return bitcoinURI;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
public BitcoinURI getPayjoinURI() {
|
||||
return payjoinURIProperty.get();
|
||||
}
|
||||
|
||||
return null;
|
||||
public void setPayjoinURI(BitcoinURI payjoinURI) {
|
||||
this.payjoinURIProperty.set(payjoinURI);
|
||||
}
|
||||
|
||||
private Pane getInputsType(List<Map<BlockTransactionHashIndex, WalletNode>> displayedUtxoSets) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.drongo.uri.BitcoinURI;
|
||||
import com.sparrowwallet.drongo.wallet.Payment;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
@@ -8,10 +9,16 @@ import java.util.List;
|
||||
public class SendPaymentsEvent {
|
||||
private final Wallet wallet;
|
||||
private final List<Payment> payments;
|
||||
private final BitcoinURI bitcoinURI;
|
||||
|
||||
public SendPaymentsEvent(Wallet wallet, List<Payment> payments) {
|
||||
this(wallet, payments, null);
|
||||
}
|
||||
|
||||
public SendPaymentsEvent(Wallet wallet, List<Payment> payments, BitcoinURI bitcoinURI) {
|
||||
this.wallet = wallet;
|
||||
this.payments = payments;
|
||||
this.bitcoinURI = bitcoinURI;
|
||||
}
|
||||
|
||||
public Wallet getWallet() {
|
||||
@@ -21,4 +28,8 @@ public class SendPaymentsEvent {
|
||||
public List<Payment> getPayments() {
|
||||
return payments;
|
||||
}
|
||||
|
||||
public BitcoinURI getBitcoinURI() {
|
||||
return bitcoinURI;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +251,8 @@ public class HeadersController extends TransactionFormController implements Init
|
||||
|
||||
private ElectrumServer.TransactionMempoolService transactionMempoolService;
|
||||
|
||||
private BitcoinURI payjoinURI;
|
||||
|
||||
private final Map<Integer, String> outputIndexLabels = new TreeMap<>();
|
||||
|
||||
@Override
|
||||
@@ -459,6 +461,9 @@ public class HeadersController extends TransactionFormController implements Init
|
||||
updateFee(feeAmt);
|
||||
}
|
||||
|
||||
payjoinURI = getPayjoinURI();
|
||||
transactionDiagram.setPayjoinURI(payjoinURI);
|
||||
|
||||
headersForm.walletTransactionProperty().addListener((observable, oldValue, walletTransaction) -> {
|
||||
transactionDiagram.update(walletTransaction);
|
||||
});
|
||||
@@ -487,7 +492,6 @@ public class HeadersController extends TransactionFormController implements Init
|
||||
saveFinalButton.visibleProperty().bind(broadcastButton.visibleProperty().not());
|
||||
broadcastButton.visibleProperty().bind(AppServices.onlineProperty());
|
||||
|
||||
BitcoinURI payjoinURI = getPayjoinURI();
|
||||
boolean isPayjoinOriginalTx = payjoinURI != null && headersForm.getPsbt() != null && headersForm.getPsbt().getPsbtInputs().stream().noneMatch(PSBTInput::isFinalized);
|
||||
payjoinButton.managedProperty().bind(payjoinButton.visibleProperty());
|
||||
payjoinButton.visibleProperty().set(isPayjoinOriginalTx);
|
||||
@@ -906,21 +910,13 @@ public class HeadersController extends TransactionFormController implements Init
|
||||
}
|
||||
|
||||
private BitcoinURI getPayjoinURI() {
|
||||
if(headersForm.getPsbt() != null) {
|
||||
for(TransactionOutput txOutput : headersForm.getPsbt().getTransaction().getOutputs()) {
|
||||
try {
|
||||
Address address = txOutput.getScript().getToAddresses()[0];
|
||||
BitcoinURI bitcoinURI = AppServices.getPayjoinURI(address);
|
||||
if(bitcoinURI != null) {
|
||||
return bitcoinURI;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
return AppServices.getPayjoinURI(headersForm.getPsbt());
|
||||
}
|
||||
|
||||
return null;
|
||||
private void registerPayjoinURI() {
|
||||
if(payjoinURI != null) {
|
||||
AppServices.addPayjoinURI(headersForm.getPsbt(), payjoinURI);
|
||||
}
|
||||
}
|
||||
|
||||
private static class BlockHeightContextMenu extends ContextMenu {
|
||||
@@ -1283,6 +1279,9 @@ public class HeadersController extends TransactionFormController implements Init
|
||||
|
||||
ElectrumServer.BroadcastTransactionService broadcastTransactionService = new ElectrumServer.BroadcastTransactionService(headersForm.getTransaction(), fee.getValue());
|
||||
broadcastTransactionService.setOnSucceeded(workerStateEvent -> {
|
||||
AppServices.clearPayjoinURI(headersForm.getPsbt());
|
||||
payjoinButton.setVisible(false);
|
||||
|
||||
//Although we wait for WalletNodeHistoryChangedEvent to indicate tx is in mempool, start a scheduled service to check the script hashes should notifications fail
|
||||
if(headersForm.getSigningWallet() != null) {
|
||||
if(transactionMempoolService != null) {
|
||||
@@ -1438,12 +1437,12 @@ public class HeadersController extends TransactionFormController implements Init
|
||||
}
|
||||
|
||||
public void getPayjoinTransaction(ActionEvent event) {
|
||||
BitcoinURI payjoinURI = getPayjoinURI();
|
||||
if(payjoinURI == null) {
|
||||
BitcoinURI currentPayjoinURI = getPayjoinURI();
|
||||
if(currentPayjoinURI == null) {
|
||||
throw new IllegalStateException("No valid Payjoin URI");
|
||||
}
|
||||
|
||||
Payjoin payjoin = new Payjoin(payjoinURI, headersForm.getSigningWallet(), headersForm.getPsbt());
|
||||
Payjoin payjoin = new Payjoin(currentPayjoinURI, headersForm.getSigningWallet(), headersForm.getPsbt());
|
||||
Payjoin.RequestPayjoinPSBTService requestPayjoinPSBTService = new Payjoin.RequestPayjoinPSBTService(payjoin, true);
|
||||
requestPayjoinPSBTService.setOnSucceeded(successEvent -> {
|
||||
PSBT proposalPsbt = requestPayjoinPSBTService.getValue();
|
||||
@@ -1502,6 +1501,7 @@ public class HeadersController extends TransactionFormController implements Init
|
||||
if(headersForm.getTransaction().equals(event.getTransaction())) {
|
||||
updateTxId();
|
||||
updateEditable(headersForm.isEditable());
|
||||
registerPayjoinURI();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1847,6 +1847,7 @@ public class HeadersController extends TransactionFormController implements Init
|
||||
if(event.getPsbt().equals(headersForm.getPsbt())) {
|
||||
updateTxId();
|
||||
headersForm.setWalletTransaction(getWalletTransaction(headersForm.getInputTransactions()));
|
||||
registerPayjoinURI();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,8 @@ public class PaymentController extends WalletFormController implements Initializ
|
||||
|
||||
private final ObjectProperty<DnsPayment> dnsPaymentProperty = new SimpleObjectProperty<>();
|
||||
|
||||
private final ObjectProperty<BitcoinURI> payjoinURIProperty = new SimpleObjectProperty<>();
|
||||
|
||||
private static final Wallet payNymWallet = new Wallet() {
|
||||
@Override
|
||||
public String getFullDisplayName() {
|
||||
@@ -186,6 +188,10 @@ public class PaymentController extends WalletFormController implements Initializ
|
||||
silentPaymentAddressProperty.set(null);
|
||||
}
|
||||
|
||||
if(payjoinURIProperty.get() != null && !newValue.equals(payjoinURIProperty.get().getAddress().toString())) {
|
||||
payjoinURIProperty.set(null);
|
||||
}
|
||||
|
||||
try {
|
||||
BitcoinURI bitcoinURI = new BitcoinURI(newValue);
|
||||
Platform.runLater(() -> updateFromURI(bitcoinURI));
|
||||
@@ -685,6 +691,10 @@ public class PaymentController extends WalletFormController implements Initializ
|
||||
field.textProperty().addListener(listener);
|
||||
}
|
||||
|
||||
public BitcoinURI getPayjoinURI() {
|
||||
return payjoinURIProperty.get();
|
||||
}
|
||||
|
||||
public boolean isValidPayment() {
|
||||
try {
|
||||
getPayment();
|
||||
@@ -752,12 +762,6 @@ public class PaymentController extends WalletFormController implements Initializ
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
try {
|
||||
AppServices.clearPayjoinURI(getRecipientAddress());
|
||||
} catch(InvalidAddressException e) {
|
||||
//ignore
|
||||
}
|
||||
|
||||
address.setText("");
|
||||
label.setText("");
|
||||
|
||||
@@ -773,6 +777,7 @@ public class PaymentController extends WalletFormController implements Initializ
|
||||
payNymProperty.set(null);
|
||||
dnsPaymentProperty.set(null);
|
||||
silentPaymentAddressProperty.set(null);
|
||||
payjoinURIProperty.set(null);
|
||||
}
|
||||
|
||||
public void setMaxInput(ActionEvent event) {
|
||||
@@ -832,12 +837,16 @@ public class PaymentController extends WalletFormController implements Initializ
|
||||
setRecipientValueSats(bitcoinURI.getAmount());
|
||||
setFiatAmount(AppServices.getFiatCurrencyExchangeRate(), bitcoinURI.getAmount());
|
||||
}
|
||||
if(bitcoinURI.getAddress() != null && bitcoinURI.getPayjoinUrl() != null) {
|
||||
AppServices.addPayjoinURI(bitcoinURI);
|
||||
}
|
||||
setPayjoinURI(bitcoinURI);
|
||||
sendController.updateTransaction();
|
||||
}
|
||||
|
||||
public void setPayjoinURI(BitcoinURI bitcoinURI) {
|
||||
if(bitcoinURI.getAddress() != null && bitcoinURI.getPayjoinUrl() != null) {
|
||||
payjoinURIProperty.set(bitcoinURI);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Address> getOtherAddresses() {
|
||||
List<Address> otherAddresses = new ArrayList<>();
|
||||
for(Tab tab : sendController.getPaymentTabs().getTabs()) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
import com.sparrowwallet.drongo.protocol.*;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
import com.sparrowwallet.drongo.silentpayments.SilentPayment;
|
||||
import com.sparrowwallet.drongo.uri.BitcoinURI;
|
||||
import com.sparrowwallet.drongo.wallet.*;
|
||||
import com.sparrowwallet.sparrow.*;
|
||||
import com.sparrowwallet.sparrow.control.*;
|
||||
@@ -414,6 +415,7 @@ public class SendController extends WalletFormController implements Initializabl
|
||||
setFeeRate(feeRate);
|
||||
}
|
||||
|
||||
transactionDiagram.setPayjoinURI(walletTransaction == null ? null : getPayjoinURI(walletTransaction.getPayments()));
|
||||
transactionDiagram.update(walletTransaction);
|
||||
updatePrivacyAnalysis(walletTransaction);
|
||||
createButton.setDisable(walletTransaction == null || isInsufficientFeeRate());
|
||||
@@ -978,12 +980,35 @@ public class SendController extends WalletFormController implements Initializabl
|
||||
|
||||
private boolean isPayjoinTx() {
|
||||
if(walletTransactionProperty.get() != null) {
|
||||
return walletTransactionProperty.get().getPayments().stream().anyMatch(payment -> AppServices.getPayjoinURI(payment.getAddress()) != null);
|
||||
return getPayjoinURI(walletTransactionProperty.get().getPayments()) != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private BitcoinURI getPayjoinURI(List<Payment> payments) {
|
||||
for(Payment payment : payments) {
|
||||
BitcoinURI payjoinURI = getPayjoinURI(payment.getAddress());
|
||||
if(payjoinURI != null) {
|
||||
return payjoinURI;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private BitcoinURI getPayjoinURI(Address address) {
|
||||
for(Tab tab : paymentTabs.getTabs()) {
|
||||
PaymentController controller = (PaymentController)tab.getUserData();
|
||||
BitcoinURI payjoinURI = controller.getPayjoinURI();
|
||||
if(payjoinURI != null && payjoinURI.getAddress().equals(address)) {
|
||||
return payjoinURI;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Node getSliderThumb() {
|
||||
return targetBlocks.lookup(".thumb");
|
||||
}
|
||||
@@ -1011,7 +1036,7 @@ public class SendController extends WalletFormController implements Initializabl
|
||||
private boolean isFakeMixPossible(List<Payment> payments) {
|
||||
return utxoSelectorProperty.get() == null && payments.size() == 1
|
||||
&& (payments.get(0).getAddress().getScriptType() == getWalletForm().getWallet().getNode(KeyPurpose.RECEIVE).getAddress().getScriptType())
|
||||
&& AppServices.getPayjoinURI(payments.get(0).getAddress()) == null;
|
||||
&& getPayjoinURI(payments.get(0).getAddress()) == null;
|
||||
}
|
||||
|
||||
private void updateOptimizationButtons(List<Payment> payments) {
|
||||
@@ -1170,6 +1195,10 @@ public class SendController extends WalletFormController implements Initializabl
|
||||
addWalletTransactionNodes();
|
||||
walletForm.setCreatedWalletTransaction(walletTransaction);
|
||||
PSBT psbt = walletTransaction.createPSBT();
|
||||
BitcoinURI payjoinURI = getPayjoinURI(walletTransaction.getPayments());
|
||||
if(payjoinURI != null) {
|
||||
AppServices.addPayjoinURI(psbt, payjoinURI);
|
||||
}
|
||||
EventManager.get().post(new ViewPSBTEvent(createButton.getScene().getWindow(), walletTransaction.getPayments().get(0).getLabel(), null, psbt));
|
||||
}
|
||||
|
||||
@@ -1511,6 +1540,10 @@ public class SendController extends WalletFormController implements Initializabl
|
||||
clear(null);
|
||||
Platform.runLater(() -> {
|
||||
setPayments(event.getPayments());
|
||||
if(event.getBitcoinURI() != null) {
|
||||
PaymentController controller = (PaymentController)paymentTabs.getTabs().get(0).getUserData();
|
||||
controller.setPayjoinURI(event.getBitcoinURI());
|
||||
}
|
||||
updateTransaction(event.getPayments() == null || event.getPayments().stream().anyMatch(Payment::isSendMax));
|
||||
});
|
||||
}
|
||||
@@ -1661,7 +1694,7 @@ public class SendController extends WalletFormController implements Initializabl
|
||||
boolean roundPaymentAmounts = userPayments.stream().anyMatch(payment -> payment.getAmount() % 100 == 0);
|
||||
boolean mixedAddressTypes = userPayments.stream().anyMatch(payment -> payment.getAddress().getScriptType() != getWalletForm().getWallet().getNode(KeyPurpose.RECEIVE).getAddress().getScriptType());
|
||||
boolean addressReuse = walletNodePayments.stream().anyMatch(walletNodePayment -> !walletNodePayment.getWalletNode().getTransactionOutputs().isEmpty());
|
||||
boolean payjoinPresent = userPayments.stream().anyMatch(payment -> AppServices.getPayjoinURI(payment.getAddress()) != null);
|
||||
boolean payjoinPresent = getPayjoinURI(userPayments) != null;
|
||||
|
||||
if(optimizationStrategy == OptimizationStrategy.PRIVACY) {
|
||||
if(fakeMixPresent) {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.sparrowwallet.sparrow.payjoin;
|
||||
|
||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
import com.sparrowwallet.drongo.protocol.Script;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
import com.sparrowwallet.drongo.protocol.TransactionOutput;
|
||||
import com.sparrowwallet.drongo.protocol.TransactionWitness;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
||||
import com.sparrowwallet.drongo.uri.BitcoinURI;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
public class PayjoinURITest {
|
||||
private static final ECKey SENDER_KEY = ECKey.fromPrivate(BigInteger.valueOf(1001));
|
||||
private static final ECKey CHANGE_KEY = ECKey.fromPrivate(BigInteger.valueOf(1002));
|
||||
private static final ECKey PAYMENT_KEY = ECKey.fromPrivate(BigInteger.valueOf(1003));
|
||||
|
||||
private static final Sha256Hash SENDER_UTXO_HASH = Sha256Hash.wrap("1111111111111111111111111111111111111111111111111111111111111111");
|
||||
private static final Sha256Hash OTHER_UTXO_HASH = Sha256Hash.wrap("3333333333333333333333333333333333333333333333333333333333333333");
|
||||
|
||||
private static final long SENDER_UTXO_VALUE = 200000L;
|
||||
private static final long PAYMENT_VALUE = 100000L;
|
||||
private static final long CHANGE_VALUE = 90000L;
|
||||
|
||||
@AfterEach
|
||||
public void clearPayjoinURIs() {
|
||||
AppServices.clearPayjoinURI(getOriginalPSBT(SENDER_UTXO_HASH));
|
||||
AppServices.clearPayjoinURI(getOriginalPSBT(OTHER_UTXO_HASH));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signedTransactionReturnedForBroadcastRetrievesPayjoinURI() throws Exception {
|
||||
PSBT original = getOriginalPSBT(SENDER_UTXO_HASH);
|
||||
AppServices.addPayjoinURI(original, getPayjoinURI());
|
||||
|
||||
//The signing device is sent the exported PSBT, and returns it signed
|
||||
PSBT exported = PSBT.fromString(original.getForExport().toBase64String());
|
||||
finalise(exported.getPsbtInputs().get(0), exported.getTransaction());
|
||||
PSBT signed = PSBT.fromString(exported.toBase64String());
|
||||
|
||||
Assertions.assertNotNull(AppServices.getPayjoinURI(signed));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void laterTransactionToTheSameAddressDoesNotRetrievePayjoinURI() throws Exception {
|
||||
PSBT original = getOriginalPSBT(SENDER_UTXO_HASH);
|
||||
AppServices.addPayjoinURI(original, getPayjoinURI());
|
||||
|
||||
//A later payment to the same address spending a different utxo
|
||||
PSBT later = getOriginalPSBT(OTHER_UTXO_HASH);
|
||||
|
||||
Assertions.assertNull(AppServices.getPayjoinURI(later));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearedPayjoinURIIsNotRetrieved() throws Exception {
|
||||
PSBT original = getOriginalPSBT(SENDER_UTXO_HASH);
|
||||
AppServices.addPayjoinURI(original, getPayjoinURI());
|
||||
Assertions.assertNotNull(AppServices.getPayjoinURI(original));
|
||||
|
||||
AppServices.clearPayjoinURI(original);
|
||||
Assertions.assertNull(AppServices.getPayjoinURI(original));
|
||||
}
|
||||
|
||||
private BitcoinURI getPayjoinURI() throws Exception {
|
||||
return new BitcoinURI("bitcoin:" + ScriptType.P2WPKH.getAddress(PAYMENT_KEY.getPubKeyHash()) + "?pj=https://payjoin.example.com/pj");
|
||||
}
|
||||
|
||||
private PSBT getOriginalPSBT(Sha256Hash utxoHash) {
|
||||
Transaction transaction = new Transaction();
|
||||
transaction.setVersion(2);
|
||||
transaction.addInput(utxoHash, 0, new Script(new byte[0]));
|
||||
transaction.addOutput(PAYMENT_VALUE, ScriptType.P2WPKH.getOutputScript(PAYMENT_KEY.getPubKeyHash()));
|
||||
transaction.addOutput(CHANGE_VALUE, ScriptType.P2WPKH.getOutputScript(CHANGE_KEY.getPubKeyHash()));
|
||||
|
||||
//Sparrow creates PSBTv2, which is exported as PSBTv0 where no silent payments are present
|
||||
PSBT psbt = new PSBT(transaction);
|
||||
psbt.getPsbtInputs().get(0).setWitnessUtxo(new TransactionOutput(null, SENDER_UTXO_VALUE, ScriptType.P2WPKH.getOutputScript(SENDER_KEY.getPubKeyHash())));
|
||||
|
||||
return psbt;
|
||||
}
|
||||
|
||||
private void finalise(PSBTInput psbtInput, Transaction transaction) {
|
||||
psbtInput.setFinalScriptSig(new Script(new byte[0]));
|
||||
psbtInput.setFinalScriptWitness(new TransactionWitness(transaction, List.of(new byte[71], SENDER_KEY.getPubKey())));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user