mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2025-11-05 11:56:37 +00:00
Code cleanup & refactoring
This commit is contained in:
parent
550df1d91a
commit
435f126933
4 changed files with 255 additions and 118 deletions
|
|
@ -1,6 +1,5 @@
|
|||
package com.sparrowwallet.sparrow.joinstr;
|
||||
|
||||
import com.sparrowwallet.drongo.KeyPurpose;
|
||||
import com.sparrowwallet.drongo.address.Address;
|
||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
import com.sparrowwallet.drongo.protocol.TransactionOutput;
|
||||
|
|
@ -13,45 +12,33 @@ import com.sparrowwallet.drongo.wallet.InsufficientFundsException;
|
|||
import com.sparrowwallet.drongo.wallet.KnapsackUtxoSelector;
|
||||
import com.sparrowwallet.drongo.wallet.Payment;
|
||||
import com.sparrowwallet.drongo.wallet.SpentTxoFilter;
|
||||
import com.sparrowwallet.drongo.wallet.StonewallUtxoSelector;
|
||||
import com.sparrowwallet.drongo.wallet.TxoFilter;
|
||||
import com.sparrowwallet.drongo.wallet.UtxoSelector;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||
import com.sparrowwallet.drongo.wallet.WalletTransaction;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.control.QRScanDialog;
|
||||
import com.sparrowwallet.sparrow.io.WalletTransactions;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import com.sparrowwallet.sparrow.net.Tor;
|
||||
import com.sparrowwallet.sparrow.wallet.Entry;
|
||||
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.WalletUtxosEntry;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class JoinstrPool {
|
||||
|
||||
private final String relay;
|
||||
private final Integer port;
|
||||
private final Integer port; // Nostr Port ?
|
||||
private final String pubkey;
|
||||
private final long denomination; // Should be in sats, like transaction amounts
|
||||
private final WalletForm walletForm; // [FormController].getWalletForm()
|
||||
|
||||
public JoinstrPool(WalletForm walletForm,String relay, Integer port, String pubkey, long denomination) {
|
||||
public JoinstrPool(Integer port, String pubkey, long denomination) {
|
||||
|
||||
this.walletForm = walletForm;
|
||||
this.relay = relay;
|
||||
this.port = port;
|
||||
this.pubkey = pubkey;
|
||||
this.denomination = denomination;
|
||||
|
||||
}
|
||||
|
||||
public String getRelay() {
|
||||
return relay;
|
||||
private String getNostrRelay() {
|
||||
return Config.get().getNostrRelay();
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
|
|
@ -73,78 +60,13 @@ public class JoinstrPool {
|
|||
|
||||
}
|
||||
|
||||
private Address getNewSendAddress() {
|
||||
NodeEntry freshNodeEntry = walletForm.getFreshNodeEntry(KeyPurpose.SEND, null);
|
||||
return freshNodeEntry.getAddress();
|
||||
public void publicNostrEvent() {
|
||||
// TODO: Publish a nostr event with pool info
|
||||
}
|
||||
|
||||
public void createPSBT() throws InsufficientFundsException {
|
||||
|
||||
/* PSBT from byte[]
|
||||
|
||||
if(PSBT.isPSBT(bytes)) {
|
||||
//Don't verify signatures here - provided PSBT may omit UTXO data that can be found when combining with an existing PSBT
|
||||
PSBT psbt = new PSBT(bytes, false);
|
||||
} else {
|
||||
throw new ParseException("Not a valid PSBT or transaction", 0);
|
||||
public void waitForPeers() {
|
||||
// TODO: Wait for others to join
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/* PSBT from QR Code
|
||||
|
||||
QRScanDialog qrScanDialog = new QRScanDialog();
|
||||
qrScanDialog.initOwner(rootStack.getScene().getWindow());
|
||||
Optional<QRScanDialog.Result> optionalResult = qrScanDialog.showAndWait();
|
||||
QRScanDialog.Result result = optionalResult.get();
|
||||
result.psbt;
|
||||
*/
|
||||
|
||||
// PSBT from WalletTransaction
|
||||
|
||||
Address sendAddress = getNewSendAddress();
|
||||
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;
|
||||
|
||||
String paymentLabel = "coinjoin";
|
||||
|
||||
ArrayList<Payment> payments = new ArrayList<Payment>();
|
||||
while(satsLeft > denomination + dustThreshold) {
|
||||
Payment payment = new Payment(sendAddress, paymentLabel, denomination, false);
|
||||
satsLeft -= denomination;
|
||||
payment.setType(Payment.Type.COINJOIN);
|
||||
payments.add(payment);
|
||||
}
|
||||
|
||||
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)));
|
||||
|
||||
SpentTxoFilter spentTxoFilter = new SpentTxoFilter(null);
|
||||
List<TxoFilter> txoFilters = List.of(spentTxoFilter, new FrozenTxoFilter(), new CoinbaseTxoFilter(walletForm.getWallet()));
|
||||
|
||||
ArrayList<byte[]> opReturns = new ArrayList<>();
|
||||
TreeSet<WalletNode> excludedChangeNodes = new TreeSet<>();
|
||||
|
||||
Integer currentBlockHeight = AppServices.getCurrentBlockHeight();
|
||||
boolean groupByAddress = false;
|
||||
boolean includeMempoolOutputs = false;
|
||||
|
||||
WalletTransaction walletTransaction = walletForm.getWallet().createWalletTransaction(selectors, txoFilters, payments, opReturns, excludedChangeNodes, feeRate, longTermFeeRate, fee, currentBlockHeight, groupByAddress, includeMempoolOutputs);
|
||||
PSBT psbt = walletTransaction.createPSBT();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private long getRecipientDustThreshold(Address address) {
|
||||
TransactionOutput txOutput = new TransactionOutput(new Transaction(), 1L, address.getOutputScript());
|
||||
return address.getScriptType().getDustThreshold(txOutput, Transaction.DUST_RELAY_TX_FEE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,118 @@
|
|||
package com.sparrowwallet.sparrow.joinstr;
|
||||
|
||||
import com.sparrowwallet.drongo.BitcoinUnit;
|
||||
import com.sparrowwallet.drongo.KeyPurpose;
|
||||
import com.sparrowwallet.drongo.address.Address;
|
||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
import com.sparrowwallet.drongo.protocol.TransactionOutput;
|
||||
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.FrozenTxoFilter;
|
||||
import com.sparrowwallet.drongo.wallet.InsufficientFundsException;
|
||||
import com.sparrowwallet.drongo.wallet.KnapsackUtxoSelector;
|
||||
import com.sparrowwallet.drongo.wallet.Payment;
|
||||
import com.sparrowwallet.drongo.wallet.SpentTxoFilter;
|
||||
import com.sparrowwallet.drongo.wallet.TxoFilter;
|
||||
import com.sparrowwallet.drongo.wallet.UtxoSelector;
|
||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||
import com.sparrowwallet.drongo.wallet.WalletTransaction;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import com.sparrowwallet.sparrow.wallet.NodeEntry;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
|
||||
public class NewPoolController extends JoinstrFormController {
|
||||
|
||||
@FXML
|
||||
private Label addressLabel;
|
||||
|
||||
@FXML
|
||||
private TextField labelField;
|
||||
|
||||
@FXML
|
||||
private TextField amountField;
|
||||
|
||||
@FXML
|
||||
private TextField denominationField;
|
||||
|
||||
@FXML
|
||||
private TextField peersField;
|
||||
|
||||
@FXML
|
||||
private ComboBox<BlockTransactionHashIndex> utxosComboBox;
|
||||
|
||||
@FXML
|
||||
private ComboBox<BitcoinUnit> denominationUnit;
|
||||
|
||||
private Address coinjoinAddress;
|
||||
|
||||
@Override
|
||||
public void initializeView() {
|
||||
|
||||
coinjoinAddress = getNewReceiveAddress();
|
||||
addressLabel.setText(coinjoinAddress.getAddress());
|
||||
|
||||
ObservableList<BlockTransactionHashIndex> utxos = FXCollections.observableArrayList(getWalletForm().getWallet().getWalletTxos().keySet().stream().filter(ref -> !ref.isSpent()).collect(Collectors.toList()));
|
||||
if(!utxos.isEmpty()) {
|
||||
utxosComboBox.setItems(utxos);
|
||||
}
|
||||
utxosComboBox.setPromptText("Select an UTXO");
|
||||
denominationUnit.setValue(BitcoinUnit.SATOSHIS);
|
||||
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleCreateButton() {
|
||||
private void setUtxoAmount(ActionEvent event) {
|
||||
switch(denominationUnit.getValue()) {
|
||||
case BTC -> {
|
||||
amountField.setText((utxosComboBox.getValue().getValue()) + " BTC");
|
||||
}
|
||||
case SATOSHIS -> {
|
||||
amountField.setText((utxosComboBox.getValue().getValue()) + " sats");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleDenominationUnitChange(ActionEvent event) {
|
||||
switch(denominationUnit.getValue()) {
|
||||
case BTC -> {
|
||||
if(!amountField.getText().isEmpty()) {
|
||||
amountField.setText((Double.parseDouble(amountField.getText().split(" ")[0]) / 100000000) + " BTC");
|
||||
}
|
||||
}
|
||||
case SATOSHIS -> {
|
||||
double amount = Double.parseDouble(amountField.getText().split(" ")[0]) * 100000000;
|
||||
amountField.setText(String.valueOf(amount).split("\\.")[0] + " sats");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleCreateButton(ActionEvent event) {
|
||||
try {
|
||||
|
||||
String denomination = denominationField.getText().trim();
|
||||
|
|
@ -31,6 +125,9 @@ public class NewPoolController extends JoinstrFormController {
|
|||
|
||||
try {
|
||||
double denominationValue = Double.parseDouble(denomination);
|
||||
if(denominationUnit.getValue() == BitcoinUnit.SATOSHIS) {
|
||||
Long.parseLong(denomination);
|
||||
}
|
||||
if (denominationValue <= 0) {
|
||||
showError("Denomination must be greater than 0");
|
||||
return;
|
||||
|
|
@ -51,11 +148,50 @@ public class NewPoolController extends JoinstrFormController {
|
|||
return;
|
||||
}
|
||||
|
||||
long denominationSats = Long.valueOf((long) (Double.parseDouble(denomination)*100000000.0D));
|
||||
String amount = amountField.getText().trim();
|
||||
if (amount.isEmpty()) {
|
||||
showError("Please select an UTXO to create a pool.");
|
||||
return;
|
||||
}
|
||||
amount = amount.split(" ")[0]; // Removes BTC/sats
|
||||
|
||||
try {
|
||||
double amountValue = Double.parseDouble(amount);
|
||||
if(denominationUnit.getValue() == BitcoinUnit.SATOSHIS) {
|
||||
Long.parseLong(amount);
|
||||
}
|
||||
if (amountValue != Double.parseDouble(denomination) && amountValue < Double.parseDouble(denomination) + getRecipientDustThreshold(coinjoinAddress)) {
|
||||
showError("Amount is smaller than denomination with dust threshold");
|
||||
return;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
showError("Invalid amount format");
|
||||
return;
|
||||
}
|
||||
|
||||
long amountInSats;
|
||||
long denominationInSats;
|
||||
if(denominationUnit.getValue() == BitcoinUnit.SATOSHIS) {
|
||||
denominationInSats = Long.parseLong(denomination);
|
||||
amountInSats = Long.parseLong(amount);
|
||||
} else {
|
||||
denominationInSats = (long)Double.parseDouble(denomination) * 100000000;
|
||||
amountInSats = (long)Double.parseDouble(amount) * 100000000;
|
||||
}
|
||||
|
||||
JoinstrPool pool = new JoinstrPool(9999, "pubkey", denominationInSats);
|
||||
|
||||
// TODO: Publish a nostr event with pool info and wait for peers
|
||||
pool.publicNostrEvent();
|
||||
pool.waitForPeers();
|
||||
|
||||
// TODO: Implement pool creation logic here
|
||||
JoinstrPool pool = new JoinstrPool(getWalletForm(), Config.get().getNostrRelay(),9999, "pubkey", denominationSats);
|
||||
pool.createPSBT();
|
||||
PSBT myPoolPSBT = createPSBT(labelField.getText(), amountInSats, denominationInSats);
|
||||
|
||||
if(myPoolPSBT != null) {
|
||||
/// TODO: Sign using sighash flag ALL | ACP
|
||||
/// TODO: Combine all PSBTs
|
||||
}
|
||||
|
||||
/*
|
||||
Alert alert = new Alert(AlertType.INFORMATION);
|
||||
|
|
@ -67,12 +203,54 @@ public class NewPoolController extends JoinstrFormController {
|
|||
|
||||
denominationField.clear();
|
||||
peersField.clear();
|
||||
amountField.clear();
|
||||
labelField.clear();
|
||||
|
||||
} catch (Exception e) {
|
||||
showError("An error occurred: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private PSBT createPSBT(String paymentLabel, long amount, long denomination) throws InsufficientFundsException {
|
||||
|
||||
// PSBT from WalletTransaction
|
||||
double feeRate = 1.0;
|
||||
double longTermFeeRate = 10.0;
|
||||
long fee = 10L;
|
||||
long dustThreshold = getRecipientDustThreshold(coinjoinAddress);
|
||||
long satsLeft = amount;
|
||||
|
||||
ArrayList<Payment> payments = new ArrayList<Payment>();
|
||||
while(satsLeft == denomination || satsLeft > denomination + dustThreshold) {
|
||||
Payment payment = new Payment(coinjoinAddress, paymentLabel, denomination, false);
|
||||
satsLeft -= denomination;
|
||||
payment.setType(Payment.Type.COINJOIN);
|
||||
payments.add(payment);
|
||||
}
|
||||
|
||||
long noInputsFee = getWalletForm().getWallet().getNoInputsFee(payments, feeRate);
|
||||
long costOfChange = getWalletForm().getWallet().getCostOfChange(feeRate, longTermFeeRate);
|
||||
List<UtxoSelector> selectors = new ArrayList<>(List.of(new BnBUtxoSelector(noInputsFee, costOfChange), new KnapsackUtxoSelector(noInputsFee)));
|
||||
|
||||
SpentTxoFilter spentTxoFilter = new SpentTxoFilter(null);
|
||||
List<TxoFilter> txoFilters = List.of(spentTxoFilter, new FrozenTxoFilter(), new CoinbaseTxoFilter(getWalletForm().getWallet()));
|
||||
|
||||
ArrayList<byte[]> opReturns = new ArrayList<>();
|
||||
TreeSet<WalletNode> excludedChangeNodes = new TreeSet<>();
|
||||
|
||||
Integer currentBlockHeight = AppServices.getCurrentBlockHeight();
|
||||
boolean groupByAddress = false;
|
||||
boolean includeMempoolOutputs = false;
|
||||
|
||||
WalletTransaction walletTransaction = getWalletForm().getWallet().createWalletTransaction(selectors, txoFilters, payments, opReturns, excludedChangeNodes, feeRate, longTermFeeRate, fee, currentBlockHeight, groupByAddress, includeMempoolOutputs);
|
||||
return walletTransaction.createPSBT();
|
||||
}
|
||||
|
||||
private long getRecipientDustThreshold(Address address) {
|
||||
TransactionOutput txOutput = new TransactionOutput(new Transaction(), 1L, address.getOutputScript());
|
||||
return address.getScriptType().getDustThreshold(txOutput, Transaction.DUST_RELAY_TX_FEE);
|
||||
}
|
||||
|
||||
private void showError(String message) {
|
||||
Alert alert = new Alert(AlertType.ERROR);
|
||||
alert.setTitle("Error");
|
||||
|
|
@ -80,4 +258,10 @@ public class NewPoolController extends JoinstrFormController {
|
|||
alert.setContentText(message);
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
private Address getNewReceiveAddress() {
|
||||
NodeEntry freshNodeEntry = getWalletForm().getFreshNodeEntry(KeyPurpose.RECEIVE, null);
|
||||
return freshNodeEntry.getAddress();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,16 +24,21 @@ public class SettingsController extends JoinstrFormController {
|
|||
@Override
|
||||
public void changed(ObservableValue<? extends String> observable,
|
||||
String oldValue, String newValue) {
|
||||
if(nostrRelayTextField.getText().isEmpty()) {
|
||||
nostrRelayTextField.setText("wss://nostr.fmt.wiz.biz");
|
||||
}
|
||||
setDefaultNostrRelayIfEmpty();
|
||||
Config.get().setNostrRelay(nostrRelayTextField.getText());
|
||||
}
|
||||
});
|
||||
setDefaultNostrRelayIfEmpty();
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultNostrRelayIfEmpty() {
|
||||
if(nostrRelayTextField.getText().isEmpty()) {
|
||||
nostrRelayTextField.setText("wss://nostr.fmt.wiz.biz");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,36 +3,62 @@
|
|||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.collections.FXCollections?>
|
||||
|
||||
<BorderPane stylesheets="@joinstr.css, @../wallet/wallet.css, @../general.css" styleClass="wallet-pane" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.sparrowwallet.sparrow.joinstr.NewPoolController">
|
||||
<?import com.sparrowwallet.sparrow.control.FiatLabel?>
|
||||
<?import com.sparrowwallet.drongo.BitcoinUnit?>
|
||||
<?import org.controlsfx.glyphfont.Glyph?>
|
||||
|
||||
<?import tornadofx.control.Form?>
|
||||
<?import tornadofx.control.Fieldset?>
|
||||
<?import tornadofx.control.Field?>
|
||||
|
||||
<BorderPane stylesheets="@joinstr.css, @../wallet/wallet.css, @../general.css" styleClass="send-form" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.sparrowwallet.sparrow.joinstr.NewPoolController">
|
||||
<padding>
|
||||
<Insets top="30" right="30" bottom="30" left="30"/>
|
||||
</padding>
|
||||
<center>
|
||||
<VBox maxWidth="Infinity" fx:id="contentVBox" spacing="20">
|
||||
<VBox maxWidth="Infinity" HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="sub-title">Create a new pool</Label>
|
||||
</VBox>
|
||||
|
||||
<GridPane hgap="10" vgap="15" maxWidth="600">
|
||||
<padding>
|
||||
<Insets top="20"/>
|
||||
</padding>
|
||||
|
||||
<Label text="Denomination (BTC):" GridPane.rowIndex="0" GridPane.columnIndex="0" style="-fx-text-fill: #aaaaaa;"/>
|
||||
<TextField fx:id="denominationField" GridPane.rowIndex="0" GridPane.columnIndex="1"
|
||||
style="-fx-background-color: #444444; -fx-text-fill: white; -fx-prompt-text-fill: #888888;"
|
||||
promptText="Enter denomination"/>
|
||||
|
||||
<Label text="Number of Peers:" GridPane.rowIndex="1" GridPane.columnIndex="0" style="-fx-text-fill: #aaaaaa;"/>
|
||||
<TextField fx:id="peersField" GridPane.rowIndex="1" GridPane.columnIndex="1"
|
||||
style="-fx-background-color: #444444; -fx-text-fill: white; -fx-prompt-text-fill: #888888;"
|
||||
promptText="Enter number of peers"/>
|
||||
|
||||
<Button fx:id="createButton" text="Create" GridPane.rowIndex="2" GridPane.columnIndex="1"
|
||||
onAction="#handleCreateButton"
|
||||
style="-fx-background-color: #2196F3; -fx-text-fill: white; -fx-cursor: hand;"/>
|
||||
</GridPane>
|
||||
<Form maxWidth="Infinity" style="-fx-max-width: 600px;">
|
||||
<Fieldset inputGrow="ALWAYS">
|
||||
<Field text="Pay to:">
|
||||
<Label fx:id="addressLabel" />
|
||||
</Field>
|
||||
<Field text="Label:">
|
||||
<TextField fx:id="labelField" promptText="Required" style="-fx-max-width: 250px;">
|
||||
<tooltip>
|
||||
<Tooltip text="Required to label the transaction (privately in this wallet)"/>
|
||||
</tooltip>
|
||||
</TextField>
|
||||
</Field>
|
||||
<Field text="UTXO:">
|
||||
<ComboBox fx:id="utxosComboBox" styleClass="amount-unit" style="-fx-max-width: 150px;" onAction="#setUtxoAmount" />
|
||||
<TextField fx:id="amountField" disable="true" style="-fx-max-width: 100px;-fx-text-fill: white;" />
|
||||
</Field>
|
||||
<Field text="Denomination:" style="-fx-text-fill: #aaaaaa;">
|
||||
<TextField fx:id="denominationField" promptText="Required" style="-fx-max-width: 100px;-fx-background-color: #444444; -fx-text-fill: white; -fx-prompt-text-fill: #888888;">
|
||||
<tooltip>
|
||||
<Tooltip text="Required to create coinjoin pool"/>
|
||||
</tooltip>
|
||||
</TextField>
|
||||
<ComboBox fx:id="denominationUnit" styleClass="amount-unit" style="-fx-min-width: 60px;" onAction="#handleDenominationUnitChange">
|
||||
<items>
|
||||
<FXCollections fx:factory="observableArrayList">
|
||||
<BitcoinUnit fx:constant="BTC" />
|
||||
<BitcoinUnit fx:constant="SATOSHIS" />
|
||||
</FXCollections>
|
||||
</items>
|
||||
</ComboBox>
|
||||
</Field>
|
||||
<Field text="Number of Peers:" style="-fx-text-fill: #aaaaaa;">
|
||||
<TextField fx:id="peersField" promptText="Req." style="-fx-max-width: 50px;-fx-background-color: #444444; -fx-text-fill: white; -fx-prompt-text-fill: #888888;" />
|
||||
</Field>
|
||||
<Field>
|
||||
<ToggleButton fx:id="createButton" text="Create" onAction="#handleCreateButton" style="-fx-background-color: #2196F3; -fx-text-fill: white; -fx-cursor: hand;" />
|
||||
</Field>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</VBox>
|
||||
</center>
|
||||
</BorderPane>
|
||||
Loading…
Reference in a new issue