- Added code to create coinjoin PSBT

This commit is contained in:
QcMrHyde 2025-06-02 04:12:25 -04:00
parent 055bc65bff
commit 550df1d91a
6 changed files with 85 additions and 88 deletions

View file

@ -556,7 +556,7 @@ public class AppController implements Initializable {
stage.initOwner(tabs.getScene().getWindow()); stage.initOwner(tabs.getScene().getWindow());
JoinstrController controller = loader.getController(); JoinstrController controller = loader.getController();
JoinstrForm joinstrForm = new JoinstrForm(getSelectedWalletForm().getStorage(), getSelectedWalletForm().getWallet()); JoinstrForm joinstrForm = new JoinstrForm(getSelectedWalletForm());
controller.setJoinstrForm(joinstrForm); controller.setJoinstrForm(joinstrForm);
Scene scene = new Scene(root); Scene scene = new Scene(root);

View file

@ -57,26 +57,21 @@ public class JoinstrController extends JoinstrFormController {
} }
try { try {
URL url = AppServices.class.getResource("joinstr/" + display.toString().toLowerCase(Locale.ROOT) + ".fxml"); URL url = AppServices.class.getResource("joinstr/" + display.toString().toLowerCase(Locale.ROOT) + ".fxml");
if(url == null) { if(url == null) {
throw new IllegalStateException("Cannot find joinstr/" + display.toString().toLowerCase(Locale.ROOT) + ".fxml"); throw new IllegalStateException("Cannot find joinstr/" + display.toString().toLowerCase(Locale.ROOT) + ".fxml");
} }
FXMLLoader displayLoader = new FXMLLoader(url);
FXMLLoader displayLoader = new FXMLLoader(url); Node joinstrDisplay = displayLoader.load();
Node joinstrDisplay = displayLoader.load(); if(!existing) {
joinstrDisplay.setUserData(display);
if(!existing) { joinstrDisplay.setViewOrder(1);
joinstrPane.getChildren().add(joinstrDisplay);
joinstrDisplay.setUserData(display); }
joinstrDisplay.setViewOrder(1); JoinstrFormController controller = displayLoader.getController();
JoinstrForm joinstrForm = getJoinstrForm();
joinstrPane.getChildren().add(joinstrDisplay); controller.setJoinstrForm(joinstrForm);
} controller.initializeView();
JoinstrFormController controller = displayLoader.getController();
JoinstrForm joinstrForm = getJoinstrForm();
controller.setJoinstrForm(joinstrForm);
controller.initializeView();
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Can't find pane", e); throw new IllegalStateException("Can't find pane", e);

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.joinstr;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.io.Storage;
import com.sparrowwallet.sparrow.wallet.WalletForm;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
@ -10,20 +11,22 @@ public class JoinstrForm {
private final BooleanProperty lockedProperty = new SimpleBooleanProperty(false); private final BooleanProperty lockedProperty = new SimpleBooleanProperty(false);
private final Storage storage; private final WalletForm walletForm;
protected Wallet wallet;
public JoinstrForm(Storage storage, Wallet currentWallet) { public JoinstrForm(WalletForm walletForm) {
this.storage = storage; this.walletForm = walletForm;
this.wallet = currentWallet; }
public WalletForm getWalletForm() {
return walletForm;
} }
public Wallet getWallet() { public Wallet getWallet() {
return wallet; return walletForm.getWallet();
} }
public Storage getStorage() { public Storage getStorage() {
return storage; return walletForm.getStorage();
} }
public BooleanProperty lockedProperty() { public BooleanProperty lockedProperty() {

View file

@ -1,10 +1,15 @@
package com.sparrowwallet.sparrow.joinstr; package com.sparrowwallet.sparrow.joinstr;
import com.sparrowwallet.sparrow.BaseController; import com.sparrowwallet.sparrow.BaseController;
import com.sparrowwallet.sparrow.wallet.WalletForm;
public abstract class JoinstrFormController extends BaseController { public abstract class JoinstrFormController extends BaseController {
public JoinstrForm joinstrForm; private JoinstrForm joinstrForm;
public WalletForm getWalletForm() {
return joinstrForm.getWalletForm();
}
public JoinstrForm getJoinstrForm() { public JoinstrForm getJoinstrForm() {
return joinstrForm; return joinstrForm;

View file

@ -3,20 +3,32 @@ package com.sparrowwallet.sparrow.joinstr;
import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
import com.sparrowwallet.drongo.wallet.BnBUtxoSelector;
import com.sparrowwallet.drongo.wallet.CoinbaseTxoFilter; import com.sparrowwallet.drongo.wallet.CoinbaseTxoFilter;
import com.sparrowwallet.drongo.wallet.FrozenTxoFilter; import com.sparrowwallet.drongo.wallet.FrozenTxoFilter;
import com.sparrowwallet.drongo.wallet.InsufficientFundsException;
import com.sparrowwallet.drongo.wallet.KnapsackUtxoSelector;
import com.sparrowwallet.drongo.wallet.Payment; import com.sparrowwallet.drongo.wallet.Payment;
import com.sparrowwallet.drongo.wallet.SpentTxoFilter; import com.sparrowwallet.drongo.wallet.SpentTxoFilter;
import com.sparrowwallet.drongo.wallet.StonewallUtxoSelector;
import com.sparrowwallet.drongo.wallet.TxoFilter; import com.sparrowwallet.drongo.wallet.TxoFilter;
import com.sparrowwallet.drongo.wallet.UtxoSelector; import com.sparrowwallet.drongo.wallet.UtxoSelector;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.drongo.wallet.WalletTransaction; import com.sparrowwallet.drongo.wallet.WalletTransaction;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.control.QRScanDialog; import com.sparrowwallet.sparrow.control.QRScanDialog;
import com.sparrowwallet.sparrow.io.WalletTransactions;
import com.sparrowwallet.sparrow.net.Tor; import com.sparrowwallet.sparrow.net.Tor;
import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.NodeEntry; import com.sparrowwallet.sparrow.wallet.NodeEntry;
import com.sparrowwallet.sparrow.wallet.OptimizationStrategy;
import com.sparrowwallet.sparrow.wallet.UtxoEntry;
import com.sparrowwallet.sparrow.wallet.WalletForm; import com.sparrowwallet.sparrow.wallet.WalletForm;
import com.sparrowwallet.sparrow.wallet.WalletUtxosEntry;
import java.util.*; import java.util.*;
@ -66,21 +78,7 @@ public class JoinstrPool {
return freshNodeEntry.getAddress(); return freshNodeEntry.getAddress();
} }
public void createPSBT() { public void createPSBT() throws InsufficientFundsException {
Address sendAddress = getNewSendAddress();
/* String to byte[]
if(Utils.isBase64(string) && !Utils.isHex(string)) {
addTransactionTab(name, file, Base64.getDecoder().decode(string));
} else if(Utils.isHex(string)) {
addTransactionTab(name, file, Utils.hexToBytes(string));
} else {
throw new ParseException("Input is not base64 or hex", 0);
}
*/
/* PSBT from byte[] /* PSBT from byte[]
@ -102,61 +100,51 @@ public class JoinstrPool {
result.psbt; result.psbt;
*/ */
/* PSBT from TransactionData
TransactionData txdata;
/// TODO fill txdata
psbt = txdata.getPsbt();
*/
// PSBT from WalletTransaction // PSBT from WalletTransaction
List<UtxoSelector> utxoSelectors; Address sendAddress = getNewSendAddress();
/// TODO select UTXO for transaction Wallet wallet = walletForm.getWallet();
double feeRate = 1.0;
double longTermFeeRate = 10.0;
long fee = 10L;
long dustThreshold = getRecipientDustThreshold(sendAddress);
long amount = wallet.getWalletTxos().keySet().stream().mapToLong(BlockTransactionHashIndex::getValue).sum();
long satsLeft = amount;
SpentTxoFilter spentTxoFilter = new SpentTxoFilter(null); String paymentLabel = "coinjoin";
List<TxoFilter> txoFilters = List.of(spentTxoFilter, new FrozenTxoFilter(), new CoinbaseTxoFilter(walletForm.getWallet()));
long satsLeft = 0L; ArrayList<Payment> payments = new ArrayList<Payment>();
Payment coinjoinPayment = new Payment(sendAddress, "coinjoin", denomination, false); while(satsLeft > denomination + dustThreshold) {
/// TODO create multiple coinjoin payments for selected UTXO Payment payment = new Payment(sendAddress, paymentLabel, denomination, false);
Payment changePayment = new Payment(sendAddress, "change", satsLeft, false); satsLeft -= denomination;
List<Payment> payments = List.of(coinjoinPayment, changePayment); payment.setType(Payment.Type.COINJOIN);
payments.add(payment);
}
List<byte[]> opReturns = null; List<UtxoSelector> selectors = new ArrayList<>();
long noInputsFee = wallet.getNoInputsFee(payments, feeRate);
long costOfChange = wallet.getCostOfChange(feeRate, longTermFeeRate);
selectors.addAll(List.of(new BnBUtxoSelector(noInputsFee, costOfChange), new KnapsackUtxoSelector(noInputsFee)));
Set<WalletNode> excludedChangeNodes; SpentTxoFilter spentTxoFilter = new SpentTxoFilter(null);
/// TODO fill excludedChangeNodes List<TxoFilter> txoFilters = List.of(spentTxoFilter, new FrozenTxoFilter(), new CoinbaseTxoFilter(walletForm.getWallet()));
double feeRate = 10.0; ArrayList<byte[]> opReturns = new ArrayList<>();
double longTermFeeRate = 10.0; TreeSet<WalletNode> excludedChangeNodes = new TreeSet<>();
Long fee = 10L;
Integer currentBlockHeight = AppServices.getCurrentBlockHeight();
boolean groupByAddress = false;
boolean includeMempoolOutputs = false;
WalletTransaction toSign = walletForm.getWallet().createWalletTransaction(utxoSelectors, txoFilters, payments, opReturns, excludedChangeNodes, feeRate, longTermFeeRate, fee, currentBlockHeight, groupByAddress, includeMempoolOutputs); Integer currentBlockHeight = AppServices.getCurrentBlockHeight();
PSBT psbt = toSign.createPSBT(); boolean groupByAddress = false;
boolean includeMempoolOutputs = false;
// decryptedWallet.sign(psbt); WalletTransaction walletTransaction = walletForm.getWallet().createWalletTransaction(selectors, txoFilters, payments, opReturns, excludedChangeNodes, feeRate, longTermFeeRate, fee, currentBlockHeight, groupByAddress, includeMempoolOutputs);
// decryptedWallet.finalise(psbt); PSBT psbt = walletTransaction.createPSBT();
// Transaction transaction = psbt.extractTransaction();
/* PSBT from Transaction
PSBT psbt = new PSBT(toSign);
PSBTInput psbtInput = psbt.getPsbtInputs().get(0);
Transaction toSpend;
/// TODO fill toSpend
TransactionOutput utxoOutput = toSpend.getOutputs().get(0);
psbtInput.setWitnessUtxo(utxoOutput);
psbtInput.setSigHash(SigHash.ALL);
psbtInput.sign(scriptType.getOutputKey(privKey));
*/
} }
private long getRecipientDustThreshold(Address address) {
TransactionOutput txOutput = new TransactionOutput(new Transaction(), 1L, address.getOutputScript());
return address.getScriptType().getDustThreshold(txOutput, Transaction.DUST_RELAY_TX_FEE);
}
} }

View file

@ -1,5 +1,6 @@
package com.sparrowwallet.sparrow.joinstr; package com.sparrowwallet.sparrow.joinstr;
import com.sparrowwallet.sparrow.io.Config;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
@ -19,6 +20,7 @@ public class NewPoolController extends JoinstrFormController {
@FXML @FXML
private void handleCreateButton() { private void handleCreateButton() {
try { try {
String denomination = denominationField.getText().trim(); String denomination = denominationField.getText().trim();
String peers = peersField.getText().trim(); String peers = peersField.getText().trim();
@ -49,7 +51,11 @@ public class NewPoolController extends JoinstrFormController {
return; return;
} }
long denominationSats = Long.valueOf((long) (Double.parseDouble(denomination)*100000000.0D));
// TODO: Implement pool creation logic here // TODO: Implement pool creation logic here
JoinstrPool pool = new JoinstrPool(getWalletForm(), Config.get().getNostrRelay(),9999, "pubkey", denominationSats);
pool.createPSBT();
/* /*
Alert alert = new Alert(AlertType.INFORMATION); Alert alert = new Alert(AlertType.INFORMATION);