mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-02 20:36:44 +00:00
relative time locking and rbf input pane
This commit is contained in:
parent
4d97832d97
commit
731be1a60c
6 changed files with 154 additions and 53 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit 130fea0937629b62305892d00bc8099872259095
|
||||
Subproject commit 9d15c27bfd8bd6c51f03fe081d6e70718ecf2eb0
|
|
@ -189,6 +189,12 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
@Override
|
||||
public void updated(Transaction transaction) {
|
||||
updateTxId();
|
||||
locktimeFieldset.setText(headersForm.getTransaction().isLocktimeSequenceEnabled() ? "Absolute Locktime" : "Absolute Locktime (sequence disabled)");
|
||||
|
||||
boolean locktimeEnabled = headersForm.getTransaction().isLocktimeSequenceEnabled();
|
||||
locktimeNoneType.setDisable(!locktimeEnabled);
|
||||
locktimeBlockType.setDisable(!locktimeEnabled);
|
||||
locktimeBlock.setDisable(!locktimeEnabled);
|
||||
locktimeDateType.setDisable(!locktimeEnabled);
|
||||
locktimeDate.setDisable(!locktimeEnabled);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
package com.sparrowwallet.sparrow.transaction;
|
||||
|
||||
import com.sparrowwallet.drongo.protocol.Script;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptChunk;
|
||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
import com.sparrowwallet.drongo.protocol.TransactionInput;
|
||||
import com.sparrowwallet.drongo.protocol.*;
|
||||
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.*;
|
||||
import org.controlsfx.control.ToggleSwitch;
|
||||
import org.fxmisc.richtext.CodeArea;
|
||||
import tornadofx.control.Field;
|
||||
import tornadofx.control.Fieldset;
|
||||
|
@ -55,6 +53,12 @@ public class InputController extends TransactionFormController implements Initia
|
|||
@FXML
|
||||
private CodeArea witnessesArea;
|
||||
|
||||
@FXML
|
||||
private TextField signatures;
|
||||
|
||||
@FXML
|
||||
private ToggleSwitch rbf;
|
||||
|
||||
@FXML
|
||||
private ToggleGroup locktimeToggleGroup;
|
||||
|
||||
|
@ -70,23 +74,20 @@ public class InputController extends TransactionFormController implements Initia
|
|||
@FXML
|
||||
private Fieldset locktimeFieldset;
|
||||
|
||||
@FXML
|
||||
private Field locktimeNoneField;
|
||||
|
||||
@FXML
|
||||
private Field locktimeAbsoluteField;
|
||||
|
||||
@FXML
|
||||
private Field locktimeRelativeField;
|
||||
|
||||
@FXML
|
||||
private Spinner<Integer> locktimeNone;
|
||||
|
||||
@FXML
|
||||
private TextField locktimeAbsolute;
|
||||
|
||||
@FXML
|
||||
private Spinner<Integer> locktimeRelative;
|
||||
private Spinner<Integer> locktimeRelativeBlocks;
|
||||
|
||||
@FXML
|
||||
private Spinner<Integer> locktimeRelativeSeconds;
|
||||
|
||||
@FXML
|
||||
private ComboBox<String> locktimeRelativeCombo;
|
||||
|
@ -106,7 +107,7 @@ public class InputController extends TransactionFormController implements Initia
|
|||
//TODO: Enable select outpoint when wallet present
|
||||
outpointSelect.setDisable(true);
|
||||
initializeScriptFields(txInput);
|
||||
|
||||
initializeStatusFields(txInput);
|
||||
initializeLocktimeFields(txInput);
|
||||
}
|
||||
|
||||
|
@ -142,71 +143,137 @@ public class InputController extends TransactionFormController implements Initia
|
|||
}
|
||||
}
|
||||
|
||||
private void initializeStatusFields(TransactionInput txInput) {
|
||||
Transaction transaction = inputForm.getTransaction();
|
||||
|
||||
signatures.setText("Unknown");
|
||||
if(inputForm.getPsbtInput() != null) {
|
||||
PSBTInput psbtInput = inputForm.getPsbtInput();
|
||||
|
||||
try {
|
||||
int reqSigs = psbtInput.getSigningScript().getNumRequiredSignatures();
|
||||
int foundSigs = psbtInput.getPartialSignatures().size();
|
||||
signatures.setText(foundSigs + "/" + reqSigs);
|
||||
} catch (NonStandardScriptException e) {
|
||||
//TODO: Handle unusual transaction sig
|
||||
}
|
||||
}
|
||||
|
||||
rbf.setSelected(txInput.isReplaceByFeeEnabled());
|
||||
rbf.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if(newValue) {
|
||||
if(txInput.isAbsoluteTimeLockDisabled()) {
|
||||
locktimeToggleGroup.selectToggle(locktimeAbsoluteType);
|
||||
} else if(txInput.isAbsoluteTimeLocked()) {
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_RBF_ENABLED);
|
||||
EventManager.get().notify(transaction);
|
||||
}
|
||||
} else {
|
||||
if(txInput.isAbsoluteTimeLocked()) {
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_LOCKTIME_DISABLED - 1);
|
||||
EventManager.get().notify(transaction);
|
||||
} else if(txInput.isRelativeTimeLocked()) {
|
||||
locktimeToggleGroup.selectToggle(locktimeAbsoluteType);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeLocktimeFields(TransactionInput txInput) {
|
||||
Transaction transaction = inputForm.getTransaction();
|
||||
locktimeToggleGroup.selectedToggleProperty().addListener((ov, old_toggle, new_toggle) -> {
|
||||
if(locktimeToggleGroup.getSelectedToggle() != null) {
|
||||
String selection = locktimeToggleGroup.getSelectedToggle().getUserData().toString();
|
||||
if(selection.equals("none")) {
|
||||
locktimeFieldset.getChildren().removeAll(locktimeRelativeField, locktimeAbsoluteField, locktimeNoneField);
|
||||
locktimeFieldset.getChildren().add(locktimeNoneField);
|
||||
locktimeFieldset.getChildren().removeAll(locktimeRelativeField, locktimeAbsoluteField);
|
||||
locktimeFieldset.getChildren().add(locktimeAbsoluteField);
|
||||
updateAbsoluteLocktimeField(transaction);
|
||||
locktimeAbsoluteField.setDisable(true);
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_LOCKTIME_DISABLED);
|
||||
rbf.setSelected(false);
|
||||
EventManager.get().notify(transaction);
|
||||
} else if(selection.equals("absolute")) {
|
||||
locktimeFieldset.getChildren().removeAll(locktimeRelativeField, locktimeAbsoluteField, locktimeNoneField);
|
||||
locktimeFieldset.getChildren().removeAll(locktimeRelativeField, locktimeAbsoluteField);
|
||||
locktimeFieldset.getChildren().add(locktimeAbsoluteField);
|
||||
long locktime = transaction.getLocktime();
|
||||
if(locktime < Transaction.MAX_BLOCK_LOCKTIME) {
|
||||
locktimeAbsoluteField.setText("Block:");
|
||||
locktimeAbsolute.setText(Long.toString(locktime));
|
||||
updateAbsoluteLocktimeField(transaction);
|
||||
locktimeAbsoluteField.setDisable(false);
|
||||
if(rbf.selectedProperty().getValue()) {
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_RBF_ENABLED);
|
||||
} else {
|
||||
locktimeAbsoluteField.setText("Date:");
|
||||
LocalDateTime localDateTime = Instant.ofEpochSecond(locktime).atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
locktimeAbsolute.setText(DateTimeFormatter.ofPattern(HeadersController.LOCKTIME_DATE_FORMAT).format(localDateTime));
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_LOCKTIME_DISABLED - 1);
|
||||
}
|
||||
//TODO: Check RBF field and set appropriately
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_LOCKTIME_DISABLED - 1);
|
||||
EventManager.get().notify(transaction);
|
||||
} else {
|
||||
locktimeFieldset.getChildren().removeAll(locktimeRelativeField, locktimeAbsoluteField, locktimeNoneField);
|
||||
locktimeFieldset.getChildren().removeAll(locktimeRelativeField, locktimeAbsoluteField);
|
||||
locktimeFieldset.getChildren().add(locktimeRelativeField);
|
||||
setRelativeLocktime(txInput, transaction, locktimeRelative.getValue());
|
||||
if(locktimeRelativeCombo.getValue() == null) {
|
||||
locktimeRelativeCombo.getSelectionModel().select(0);
|
||||
} else {
|
||||
setRelativeLocktime(txInput, transaction);
|
||||
}
|
||||
rbf.setSelected(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
locktimeNone.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1, (int)transaction.getLocktime()));
|
||||
locktimeRelativeCombo.getSelectionModel().select(0);
|
||||
locktimeRelative.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)TransactionInput.MAX_RELATIVE_TIMELOCK_IN_BLOCKS, 0));
|
||||
locktimeRelativeBlocks.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)TransactionInput.RELATIVE_TIMELOCK_VALUE_MASK, 0));
|
||||
locktimeRelativeSeconds.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0,
|
||||
(int)TransactionInput.RELATIVE_TIMELOCK_VALUE_MASK*TransactionInput.RELATIVE_TIMELOCK_SECONDS_INCREMENT, 0,
|
||||
TransactionInput.RELATIVE_TIMELOCK_SECONDS_INCREMENT));
|
||||
|
||||
locktimeRelativeBlocks.managedProperty().bind(locktimeRelativeBlocks.visibleProperty());
|
||||
locktimeRelativeSeconds.managedProperty().bind(locktimeRelativeSeconds.visibleProperty());
|
||||
locktimeRelativeCombo.getSelectionModel().selectedItemProperty().addListener((ov, old_toggle, new_toggle) -> {
|
||||
boolean blocks = locktimeRelativeCombo.getValue().equals("blocks");
|
||||
locktimeRelativeSeconds.setVisible(!blocks);
|
||||
locktimeRelativeBlocks.setVisible(blocks);
|
||||
setRelativeLocktime(txInput, transaction);
|
||||
});
|
||||
|
||||
locktimeRelativeType.setDisable(!transaction.isRelativeLocktimeAllowed());
|
||||
if(txInput.isAbsoluteTimeLockDisabled()) {
|
||||
locktimeToggleGroup.selectToggle(locktimeNoneType);
|
||||
} else if(txInput.isAbsoluteTimeLocked()) {
|
||||
locktimeToggleGroup.selectToggle(locktimeAbsoluteType);
|
||||
} else {
|
||||
locktimeRelative.valueFactoryProperty().get().setValue((int)txInput.getRelativeLocktime());
|
||||
if(txInput.isRelativeTimeLockedInBlocks()) {
|
||||
locktimeRelativeBlocks.valueFactoryProperty().get().setValue((int)txInput.getRelativeLocktime());
|
||||
locktimeRelativeCombo.getSelectionModel().select(0);
|
||||
} else {
|
||||
locktimeRelativeSeconds.valueFactoryProperty().get().setValue((int)txInput.getRelativeLocktime() * TransactionInput.RELATIVE_TIMELOCK_SECONDS_INCREMENT);
|
||||
locktimeRelativeCombo.getSelectionModel().select(1);
|
||||
}
|
||||
locktimeToggleGroup.selectToggle(locktimeRelativeType);
|
||||
}
|
||||
|
||||
locktimeRelative.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||
setRelativeLocktime(txInput, transaction, newValue);
|
||||
locktimeRelativeBlocks.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||
setRelativeLocktime(txInput, transaction);
|
||||
});
|
||||
|
||||
locktimeRelativeCombo.getSelectionModel().selectedItemProperty().addListener((ov, old_toggle, new_toggle) -> {
|
||||
setRelativeLocktime(txInput, transaction, locktimeRelative.getValue());
|
||||
locktimeRelativeSeconds.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||
setRelativeLocktime(txInput, transaction);
|
||||
});
|
||||
}
|
||||
|
||||
private void setRelativeLocktime(TransactionInput txInput, Transaction transaction, Integer value) {
|
||||
String relativeSelection = locktimeRelativeCombo.getValue();
|
||||
if (relativeSelection.equals("blocks")) {
|
||||
txInput.setSequenceNumber(value & 0xFFFF);
|
||||
private void updateAbsoluteLocktimeField(Transaction transaction) {
|
||||
long locktime = transaction.getLocktime();
|
||||
if(locktime < Transaction.MAX_BLOCK_LOCKTIME) {
|
||||
locktimeAbsoluteField.setText("Block:");
|
||||
locktimeAbsolute.setText(Long.toString(locktime));
|
||||
} else {
|
||||
txInput.setSequenceNumber((value & 0xFFFF) | 0x400000);
|
||||
locktimeAbsoluteField.setText("Date:");
|
||||
LocalDateTime localDateTime = Instant.ofEpochSecond(locktime).atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
locktimeAbsolute.setText(DateTimeFormatter.ofPattern(HeadersController.LOCKTIME_DATE_FORMAT).format(localDateTime));
|
||||
}
|
||||
}
|
||||
|
||||
private void setRelativeLocktime(TransactionInput txInput, Transaction transaction) {
|
||||
String relativeSelection = locktimeRelativeCombo.getValue();
|
||||
if(relativeSelection.equals("blocks")) {
|
||||
Integer value = locktimeRelativeBlocks.getValue();
|
||||
txInput.setSequenceNumber(value & TransactionInput.RELATIVE_TIMELOCK_VALUE_MASK);
|
||||
} else {
|
||||
Integer value = locktimeRelativeSeconds.getValue() / TransactionInput.RELATIVE_TIMELOCK_SECONDS_INCREMENT;
|
||||
txInput.setSequenceNumber((value & TransactionInput.RELATIVE_TIMELOCK_VALUE_MASK) | TransactionInput.RELATIVE_TIMELOCK_TYPE_FLAG);
|
||||
}
|
||||
EventManager.get().notify(transaction);
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ public abstract class TransactionFormController {
|
|||
|
||||
Popup popup = new Popup();
|
||||
Label popupMsg = new Label();
|
||||
popupMsg.setStyle("-fx-background-color: #696c77; -fx-text-fill: white; -fx-padding: 5;");
|
||||
popupMsg.getStyleClass().add("tooltip");
|
||||
popup.getContent().add(popupMsg);
|
||||
|
||||
area.setMouseOverTextDelay(Duration.ofMillis(150));
|
||||
|
|
|
@ -20,4 +20,10 @@
|
|||
|
||||
.form-separator {
|
||||
-fx-padding: -15 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
-fx-translate-x: -20px;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<?import org.controlsfx.control.SegmentedButton?>
|
||||
<?import javafx.collections.FXCollections?>
|
||||
<?import java.lang.String?>
|
||||
<?import org.controlsfx.control.ToggleSwitch?>
|
||||
|
||||
<GridPane alignment="TOP_CENTER" hgap="10.0" prefHeight="500.0" prefWidth="620.0" stylesheets="@input.css, @../general.css" vgap="10.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.transaction.InputController">
|
||||
<padding>
|
||||
|
@ -83,7 +84,18 @@
|
|||
|
||||
<Separator styleClass="form-separator" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="3" />
|
||||
|
||||
<Form GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="4">
|
||||
<Form GridPane.columnIndex="0" GridPane.rowIndex="4">
|
||||
<Fieldset text="Status" inputGrow="SOMETIMES">
|
||||
<Field text="Signatures:">
|
||||
<TextField fx:id="signatures" editable="false" prefWidth="120" styleClass="copyable-label"/>
|
||||
</Field>
|
||||
<Field text="RBF:">
|
||||
<ToggleSwitch fx:id="rbf"/>
|
||||
</Field>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
|
||||
<Form GridPane.columnIndex="1" GridPane.rowIndex="4">
|
||||
<Fieldset fx:id="locktimeFieldset" text="Locktime" inputGrow="SOMETIMES">
|
||||
<Field text="Type:">
|
||||
<SegmentedButton>
|
||||
|
@ -91,20 +103,30 @@
|
|||
<ToggleGroup fx:id="locktimeToggleGroup" />
|
||||
</toggleGroup>
|
||||
<buttons>
|
||||
<ToggleButton fx:id="locktimeNoneType" text="Disabled" userData="none" toggleGroup="$locktimeToggleGroup" />
|
||||
<ToggleButton fx:id="locktimeAbsoluteType" text="Absolute" userData="absolute" toggleGroup="$locktimeToggleGroup" />
|
||||
<ToggleButton fx:id="locktimeRelativeType" text="Relative" userData="relative" toggleGroup="$locktimeToggleGroup" />
|
||||
<ToggleButton fx:id="locktimeNoneType" text="Disabled" userData="none" toggleGroup="$locktimeToggleGroup">
|
||||
<tooltip>
|
||||
<Tooltip text="Disable time locks"/>
|
||||
</tooltip>
|
||||
</ToggleButton>
|
||||
<ToggleButton fx:id="locktimeAbsoluteType" text="Absolute" userData="absolute" toggleGroup="$locktimeToggleGroup">
|
||||
<tooltip>
|
||||
<Tooltip text="Enable absolute (transaction level) time lock only"/>
|
||||
</tooltip>
|
||||
</ToggleButton>
|
||||
<ToggleButton fx:id="locktimeRelativeType" text="Relative" userData="relative" toggleGroup="$locktimeToggleGroup">
|
||||
<tooltip>
|
||||
<Tooltip text="Enable both absolute and relative time locks"/>
|
||||
</tooltip>
|
||||
</ToggleButton>
|
||||
</buttons>
|
||||
</SegmentedButton>
|
||||
</Field>
|
||||
<Field fx:id="locktimeNoneField" text="Block:">
|
||||
<Spinner fx:id="locktimeNone" disable="true" prefWidth="90"/>
|
||||
</Field>
|
||||
<Field fx:id="locktimeAbsoluteField" text="Block:">
|
||||
<TextField fx:id="locktimeAbsolute" editable="false" prefWidth="120" styleClass="copyable-label"/>
|
||||
</Field>
|
||||
<Field fx:id="locktimeRelativeField" text="Block:">
|
||||
<Spinner fx:id="locktimeRelative" editable="true" prefWidth="90" prefHeight="25"/>
|
||||
<Field fx:id="locktimeRelativeField" text="Value:">
|
||||
<Spinner fx:id="locktimeRelativeBlocks" editable="true" prefWidth="110"/>
|
||||
<Spinner fx:id="locktimeRelativeSeconds" editable="true" prefWidth="110"/>
|
||||
<ComboBox fx:id="locktimeRelativeCombo">
|
||||
<items>
|
||||
<FXCollections fx:factory="observableArrayList">
|
||||
|
|
Loading…
Reference in a new issue