relative time locking and rbf input pane

This commit is contained in:
Craig Raw
2020-04-06 14:53:42 +02:00
parent 4d97832d97
commit 731be1a60c
6 changed files with 154 additions and 53 deletions
@@ -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));