input pane sequence number

This commit is contained in:
Craig Raw 2020-04-05 16:01:49 +02:00
parent d58167552a
commit 4d97832d97
17 changed files with 290 additions and 96 deletions

2
drongo

@ -1 +1 @@
Subproject commit d28186f8c9ca7fd53a6978b84b07e6096d55df1c
Subproject commit 130fea0937629b62305892d00bc8099872259095

View file

@ -106,7 +106,7 @@ public class AppController implements Initializable {
addTransactionTab("p2sh-p2wsh", "01000000000101708256c5896fb3f00ef37601f8e30c5b460dbcd1fca1cd7199f9b56fc4ecd5400000000023220020615ae01ed1bc1ffaad54da31d7805d0bb55b52dfd3941114330368c1bbf69b4cffffffff01603edb0300000000160014bbef244bcad13cffb68b5cef3017c7423675552204004730440220010d2854b86b90b7c33661ca25f9d9f15c24b88c5c4992630f77ff004b998fb802204106fc3ec8481fa98e07b7e78809ac91b6ccaf60bf4d3f729c5a75899bb664a501473044022046d66321c6766abcb1366a793f9bfd0e11e0b080354f18188588961ea76c5ad002207262381a0661d66f5c39825202524c45f29d500c6476176cd910b1691176858701695221026ccfb8061f235cc110697c0bfb3afb99d82c886672f6b9b5393b25a434c0cbf32103befa190c0c22e2f53720b1be9476dcf11917da4665c44c9c71c3a2d28a933c352102be46dc245f58085743b1cc37c82f0d63a960efa43b5336534275fc469b49f4ac53ae00000000", null);
addTransactionTab("p2wpkh", "01000000000101109d2e41430bfdec7e6dfb02bf78b5827eeb717ef25210ff3203b0db8c76c9260000000000ffffffff01a032eb0500000000160014bbef244bcad13cffb68b5cef3017c742367555220247304402202f7cac3494e521018ae0be4ca18517639ef7c00658d42a9f938b2b344c8454e2022039a54218832fad5d14b331329d9042c51ee6be287e95e49ee5b96fda1f5ce13f0121026ccfb8061f235cc110697c0bfb3afb99d82c886672f6b9b5393b25a434c0cbf300000000", null);
addTransactionTab("p2wsh", "0100000000010193a2db37b841b2a46f4e9bb63fe9c1012da3ab7fe30b9f9c974242778b5af8980000000000ffffffff01806fb307000000001976a914bbef244bcad13cffb68b5cef3017c7423675552288ac040047304402203cdcaf02a44e37e409646e8a506724e9e1394b890cb52429ea65bac4cc2403f1022024b934297bcd0c21f22cee0e48751c8b184cc3a0d704cae2684e14858550af7d01483045022100feb4e1530c13e72226dc912dcd257df90d81ae22dbddb5a3c2f6d86f81d47c8e022069889ddb76388fa7948aaa018b2480ac36132009bb9cfade82b651e88b4b137a01695221026ccfb8061f235cc110697c0bfb3afb99d82c886672f6b9b5393b25a434c0cbf32103befa190c0c22e2f53720b1be9476dcf11917da4665c44c9c71c3a2d28a933c352102be46dc245f58085743b1cc37c82f0d63a960efa43b5336534275fc469b49f4ac53ae00000000", null);
addTransactionTab("test1", "02000000000102ba4dc5a4a14bfaa941b7d115b379b5e15f960635cf694c178b9116763cbd63b11600000017160014fc164cbcac023f5eacfcead2d17d8768c41949affeffffff074d44d2856beb68ba52e8832da60a1682768c2421c2d9a8109ef4e66babd1fd1e000000171600148c3098be6b430859115f5ee99c84c368afecd048feffffff02305310000000000017a914ffaf369c2212b178c7a2c21c9ccdd5d126e74c4187327f0300000000001976a914a7cda2e06b102a143ab606937a01d152e300cd3e88ac02473044022006da0ca227f765179219e08a33026b94e7cacff77f87b8cd8eb1b46d6dda11d6022064faa7912924fd23406b6ed3328f1bbbc3760dc51109a49c1b38bf57029d304f012103c6a2fcd030270427d4abe1041c8af929a9e2dbab07b243673453847ab842ee1f024730440220786316a16095105a0af28dccac5cf80f449dea2ea810a9559a89ecb989c2cb3d02205cbd9913d1217ffec144ae4f2bd895f16d778c2ec49ae9c929fdc8bcc2a2b1db0121024d4985241609d072a59be6418d700e87688f6c4d99a51ad68e66078211f076ee38820900", null);
addTransactionTab("test1", "02000000000102ba4dc5a4a14bfaa941b7d115b379b5e15f960635cf694c178b9116763cbd63b11600000017160014fc164cbcac023f5eacfcead2d17d8768c41949affeffffff074d44d2856beb68ba52e8832da60a1682768c2421c2d9a8109ef4e66babd1fd1e000000171600148c3098be6b430859115f5ee99c84c368afecd0481500400002305310000000000017a914ffaf369c2212b178c7a2c21c9ccdd5d126e74c4187327f0300000000001976a914a7cda2e06b102a143ab606937a01d152e300cd3e88ac02473044022006da0ca227f765179219e08a33026b94e7cacff77f87b8cd8eb1b46d6dda11d6022064faa7912924fd23406b6ed3328f1bbbc3760dc51109a49c1b38bf57029d304f012103c6a2fcd030270427d4abe1041c8af929a9e2dbab07b243673453847ab842ee1f024730440220786316a16095105a0af28dccac5cf80f449dea2ea810a9559a89ecb989c2cb3d02205cbd9913d1217ffec144ae4f2bd895f16d778c2ec49ae9c929fdc8bcc2a2b1db0121024d4985241609d072a59be6418d700e87688f6c4d99a51ad68e66078211f076ee38820900", null);
addTransactionTab("3of3-1signed.psbt", null, "70736274ff0100550200000001294c4871c059bb76be81e94b78059ee2e0c9b1b47f38edb6b4e75916062394930000000000feffffff01f82a0000000000001976a914e65b294f890792f2c2725d488567018d660f0cf488ac701c09004f0102aa7ed3044b1635bb800000021bf4bfc48934b7966b39bdebb689525d9b8bfed5c8b16e8c58f9afe4641d6d5f03800b5dbec0355c9f0b5e8227bc903e9d0ff1fe6ced0dcfb6d416541c7412c4331406b57041300000800000008000000080020000804f0102aa7ed3042cd31dee80000002d544b2364010378f8c6cec85f6b7ed83a8203dcdbedb97e2625f431f897b837e0363428de8fcfbfe373c0d9e1e0cc8163d886764bafe71c5822eaa232981356589145f63394f300000800000008000000080020000804f0102aa7ed3049ec7d9f580000002793e04aff18b4e40ebc48bcdc6232c54c69cf7265a38fbd85b35705e34d2d42f03368e79aa2b2b7f736d156905a7a45891df07baa2d0b7f127a537908cb82deed514130a48af300000800000008000000080020000800001012b983a000000000000220020f64748dad1cbad107761aaed5c59f25aba006498d260b440e0a091691350c9aa010569532102f26969eb8d1da34d17d33ff99e2f020cc33b3d11d9798ec14f46b82bc455d3262103171d9b824205cd5db6e9353676a292ca954b24d8310a36fc983469ba3fb507a221037f3794f3be4c4acc086ac84d6902c025713eabf8890f20f44acf0b34e3c0f0f753ae220602f26969eb8d1da34d17d33ff99e2f020cc33b3d11d9798ec14f46b82bc455d3261c130a48af300000800000008000000080020000800000000000000000220603171d9b824205cd5db6e9353676a292ca954b24d8310a36fc983469ba3fb507a21c5f63394f300000800000008000000080020000800000000000000000220203171d9b824205cd5db6e9353676a292ca954b24d8310a36fc983469ba3fb507a24830450221008d27cc4b03bc543726e73b69e7980e7364d6f33f979a5cd9b92fb3d050666bd002204fc81fc9c67baf7c3b77041ed316714a9c117a5bdbb020e8c771ea3bdc342434012206037f3794f3be4c4acc086ac84d6902c025713eabf8890f20f44acf0b34e3c0f0f71c06b570413000008000000080000000800200008000000000000000000000");
}

View file

@ -14,9 +14,9 @@ import java.time.*;
import java.util.ResourceBundle;
public class HeadersController extends TransactionFormController implements Initializable, TransactionListener {
private HeadersForm headersForm;
public static final String LOCKTIME_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final long MAX_BLOCK_LOCKTIME = 500000000L;
private HeadersForm headersForm;
@FXML
private TextField id;
@ -110,7 +110,7 @@ public class HeadersController extends TransactionFormController implements Init
locktimeFieldset.getChildren().remove(locktimeBlockField);
locktimeFieldset.getChildren().remove(locktimeNoneField);
locktimeFieldset.getChildren().add(locktimeNoneField);
tx.setLockTime(0);
tx.setLocktime(0);
EventManager.get().notify(tx);
} else if(selection.equals("block")) {
locktimeFieldset.getChildren().remove(locktimeDateField);
@ -119,7 +119,7 @@ public class HeadersController extends TransactionFormController implements Init
locktimeFieldset.getChildren().add(locktimeBlockField);
Integer block = locktimeBlock.getValue();
if(block != null) {
tx.setLockTime(block);
tx.setLocktime(block);
EventManager.get().notify(tx);
}
} else {
@ -130,17 +130,17 @@ public class HeadersController extends TransactionFormController implements Init
LocalDateTime date = locktimeDate.getDateTimeValue();
if(date != null) {
locktimeDate.setDateTimeValue(date);
tx.setLockTime(date.toEpochSecond(OffsetDateTime.now(ZoneId.systemDefault()).getOffset()));
tx.setLocktime(date.toEpochSecond(OffsetDateTime.now(ZoneId.systemDefault()).getOffset()));
EventManager.get().notify(tx);
}
}
}
});
locktimeNone.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)MAX_BLOCK_LOCKTIME-1, 0));
if(tx.getLockTime() < MAX_BLOCK_LOCKTIME) {
locktimeBlock.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)MAX_BLOCK_LOCKTIME-1, (int)tx.getLockTime()));
if(tx.getLockTime() == 0) {
locktimeNone.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1, 0));
if(tx.getLocktime() < Transaction.MAX_BLOCK_LOCKTIME) {
locktimeBlock.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1, (int)tx.getLocktime()));
if(tx.getLocktime() == 0) {
locktimeToggleGroup.selectToggle(locktimeNoneType);
} else {
locktimeToggleGroup.selectToggle(locktimeBlockType);
@ -148,20 +148,20 @@ public class HeadersController extends TransactionFormController implements Init
LocalDateTime date = Instant.now().atZone(ZoneId.systemDefault()).toLocalDateTime();
locktimeDate.setDateTimeValue(date);
} else {
locktimeBlock.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)MAX_BLOCK_LOCKTIME-1));
LocalDateTime date = Instant.ofEpochSecond(tx.getLockTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
locktimeBlock.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1));
LocalDateTime date = Instant.ofEpochSecond(tx.getLocktime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
locktimeDate.setDateTimeValue(date);
locktimeToggleGroup.selectToggle(locktimeDateType);
}
locktimeBlock.valueProperty().addListener((obs, oldValue, newValue) -> {
tx.setLockTime(newValue);
tx.setLocktime(newValue);
EventManager.get().notify(tx);
});
locktimeDate.setFormat("yyyy-MM-dd HH:mm:ss");
locktimeDate.setFormat(LOCKTIME_DATE_FORMAT);
locktimeDate.dateTimeValueProperty().addListener((obs, oldValue, newValue) -> {
tx.setLockTime(newValue.toEpochSecond(OffsetDateTime.now(ZoneId.systemDefault()).getOffset()));
tx.setLocktime(newValue.toEpochSecond(OffsetDateTime.now(ZoneId.systemDefault()).getOffset()));
EventManager.get().notify(tx);
});
@ -189,6 +189,6 @@ public class HeadersController extends TransactionFormController implements Init
@Override
public void updated(Transaction transaction) {
updateTxId();
locktimeFieldset.setText(headersForm.getTransaction().isLockTimeEnabled() ? "Locktime" : "Locktime (Disabled)");
locktimeFieldset.setText(headersForm.getTransaction().isLocktimeSequenceEnabled() ? "Absolute Locktime" : "Absolute Locktime (sequence disabled)");
}
}

View file

@ -8,20 +8,12 @@ import javafx.scene.Node;
import java.io.IOException;
public class HeadersForm extends TransactionForm {
private Transaction transaction;
private PSBT psbt;
public HeadersForm(Transaction transaction, PSBT psbt) {
this.transaction = transaction;
this.psbt = psbt;
public HeadersForm(PSBT psbt) {
super(psbt);
}
public Transaction getTransaction() {
return transaction;
}
public PSBT getPsbt() {
return psbt;
public HeadersForm(Transaction transaction) {
super(transaction);
}
public Node getContents() throws IOException {
@ -33,6 +25,6 @@ public class HeadersForm extends TransactionForm {
}
public String toString() {
return "Tx [" + transaction.calculateTxId(false).toString().substring(0, 6) + "]";
return "Tx [" + getTransaction().calculateTxId(false).toString().substring(0, 6) + "]";
}
}

View file

@ -2,17 +2,23 @@ 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.psbt.PSBTInput;
import com.sparrowwallet.sparrow.EventManager;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.*;
import org.fxmisc.richtext.CodeArea;
import tornadofx.control.Field;
import tornadofx.control.Fieldset;
import org.fxmisc.flowless.VirtualizedScrollPane;
import java.net.URL;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.ResourceBundle;
@ -49,6 +55,42 @@ public class InputController extends TransactionFormController implements Initia
@FXML
private CodeArea witnessesArea;
@FXML
private ToggleGroup locktimeToggleGroup;
@FXML
private ToggleButton locktimeNoneType;
@FXML
private ToggleButton locktimeAbsoluteType;
@FXML
private ToggleButton locktimeRelativeType;
@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;
@FXML
private ComboBox<String> locktimeRelativeCombo;
@Override
public void initialize(URL location, ResourceBundle resources) {
@ -63,7 +105,12 @@ public class InputController extends TransactionFormController implements Initia
//TODO: Enable select outpoint when wallet present
outpointSelect.setDisable(true);
initializeScriptFields(txInput);
initializeLocktimeFields(txInput);
}
private void initializeScriptFields(TransactionInput txInput) {
//TODO: Is this safe?
Script redeemScript = txInput.getScriptSig().getFirstNestedScript();
@ -95,6 +142,75 @@ public class InputController extends TransactionFormController implements Initia
}
}
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);
txInput.setSequenceNumber(TransactionInput.SEQUENCE_LOCKTIME_DISABLED);
EventManager.get().notify(transaction);
} else if(selection.equals("absolute")) {
locktimeFieldset.getChildren().removeAll(locktimeRelativeField, locktimeAbsoluteField, locktimeNoneField);
locktimeFieldset.getChildren().add(locktimeAbsoluteField);
long locktime = transaction.getLocktime();
if(locktime < Transaction.MAX_BLOCK_LOCKTIME) {
locktimeAbsoluteField.setText("Block:");
locktimeAbsolute.setText(Long.toString(locktime));
} else {
locktimeAbsoluteField.setText("Date:");
LocalDateTime localDateTime = Instant.ofEpochSecond(locktime).atZone(ZoneId.systemDefault()).toLocalDateTime();
locktimeAbsolute.setText(DateTimeFormatter.ofPattern(HeadersController.LOCKTIME_DATE_FORMAT).format(localDateTime));
}
//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().add(locktimeRelativeField);
setRelativeLocktime(txInput, transaction, locktimeRelative.getValue());
}
}
});
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));
if(txInput.isAbsoluteTimeLockDisabled()) {
locktimeToggleGroup.selectToggle(locktimeNoneType);
} else if(txInput.isAbsoluteTimeLocked()) {
locktimeToggleGroup.selectToggle(locktimeAbsoluteType);
} else {
locktimeRelative.valueFactoryProperty().get().setValue((int)txInput.getRelativeLocktime());
if(txInput.isRelativeTimeLockedInBlocks()) {
locktimeRelativeCombo.getSelectionModel().select(0);
} else {
locktimeRelativeCombo.getSelectionModel().select(1);
}
locktimeToggleGroup.selectToggle(locktimeRelativeType);
}
locktimeRelative.valueProperty().addListener((obs, oldValue, newValue) -> {
setRelativeLocktime(txInput, transaction, newValue);
});
locktimeRelativeCombo.getSelectionModel().selectedItemProperty().addListener((ov, old_toggle, new_toggle) -> {
setRelativeLocktime(txInput, transaction, locktimeRelative.getValue());
});
}
private void setRelativeLocktime(TransactionInput txInput, Transaction transaction, Integer value) {
String relativeSelection = locktimeRelativeCombo.getValue();
if (relativeSelection.equals("blocks")) {
txInput.setSequenceNumber(value & 0xFFFF);
} else {
txInput.setSequenceNumber((value & 0xFFFF) | 0x400000);
}
EventManager.get().notify(transaction);
}
public void setModel(InputForm form) {
this.inputForm = form;
initializeView();

View file

@ -1,6 +1,8 @@
package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionInput;
import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.psbt.PSBTInput;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
@ -11,11 +13,17 @@ public class InputForm extends TransactionForm {
private TransactionInput transactionInput;
private PSBTInput psbtInput;
public InputForm(TransactionInput transactionInput, PSBTInput psbtInput) {
this.transactionInput = transactionInput;
public InputForm(PSBT psbt, PSBTInput psbtInput) {
super(psbt);
this.transactionInput = psbt.getTransaction().getInputs().get(psbt.getPsbtInputs().indexOf(psbtInput));
this.psbtInput = psbtInput;
}
public InputForm(Transaction transaction, TransactionInput transactionInput) {
super(transaction);
this.transactionInput = transactionInput;
}
public TransactionInput getTransactionInput() {
return transactionInput;
}

View file

@ -8,20 +8,12 @@ import javafx.scene.Node;
import java.io.IOException;
public class InputsForm extends TransactionForm {
private Transaction transaction;
private PSBT psbt;
public InputsForm(Transaction transaction, PSBT psbt) {
this.transaction = transaction;
this.psbt = psbt;
public InputsForm(PSBT psbt) {
super(psbt);
}
public Transaction getTransaction() {
return transaction;
}
public PSBT getPsbt() {
return psbt;
public InputsForm(Transaction transaction) {
super(transaction);
}
public Node getContents() throws IOException {

View file

@ -1,6 +1,9 @@
package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.psbt.PSBTOutput;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
@ -8,8 +11,16 @@ import java.io.IOException;
public class OutputForm extends TransactionForm {
private TransactionOutput transactionOutput;
private PSBTOutput psbtOutput;
public OutputForm(TransactionOutput transactionOutput) {
public OutputForm(PSBT psbt, PSBTOutput psbtOutput) {
super(psbt);
this.transactionOutput = psbt.getTransaction().getOutputs().get(psbt.getPsbtOutputs().indexOf(psbtOutput));
this.psbtOutput = psbtOutput;
}
public OutputForm(Transaction transaction, TransactionOutput transactionOutput) {
super(transaction);
this.transactionOutput = transactionOutput;
}
@ -17,6 +28,10 @@ public class OutputForm extends TransactionForm {
return transactionOutput;
}
public PSBTOutput getPsbtOutput() {
return psbtOutput;
}
public Node getContents() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("output.fxml"));
Node node = loader.load();

View file

@ -1,20 +1,19 @@
package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.psbt.PSBT;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import java.io.IOException;
public class OutputsForm extends TransactionForm {
private Transaction transaction;
public OutputsForm(Transaction transaction) {
this.transaction = transaction;
public OutputsForm(PSBT psbt) {
super(psbt);
}
public Transaction getTransaction() {
return transaction;
public OutputsForm(Transaction transaction) {
super(transaction);
}
public Node getContents() throws IOException {

View file

@ -1,5 +1,6 @@
package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.psbt.PSBTInput;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
@ -9,7 +10,8 @@ import java.io.IOException;
public class PartialInputForm extends TransactionForm {
private PSBTInput psbtInput;
public PartialInputForm(PSBTInput psbtInput) {
public PartialInputForm(Transaction transaction, PSBTInput psbtInput) {
super(transaction);
this.psbtInput = psbtInput;
}

View file

@ -1,5 +1,6 @@
package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.psbt.PSBTOutput;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
@ -9,7 +10,8 @@ import java.io.IOException;
public class PartialOutputForm extends TransactionForm {
private PSBTOutput psbtOutput;
public PartialOutputForm(PSBTOutput psbtOutput) {
public PartialOutputForm(Transaction transaction, PSBTOutput psbtOutput) {
super(transaction);
this.psbtOutput = psbtOutput;
}

View file

@ -1,23 +1,28 @@
package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Script;
import com.sparrowwallet.drongo.protocol.ScriptChunk;
import javafx.geometry.Point2D;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.IndexRange;
import javafx.scene.control.MenuItem;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.ContextMenuEvent;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.model.TwoDimensional;
import java.util.OptionalInt;
import static org.fxmisc.richtext.model.TwoDimensional.Bias.Backward;
public class ScriptContextMenu extends ContextMenu {
private Script script;
private MenuItem copyvalue;
private CodeArea area;
private IndexRange range;
private ScriptChunk hoverChunk;
public ScriptContextMenu()
public ScriptContextMenu(CodeArea area, Script script)
{
showingProperty().addListener((ob,ov,showing) -> checkMenuItems(showing));
this.
this.script = script;
copyvalue = new MenuItem("Copy Value");
copyvalue.setOnAction(AE -> {
@ -28,20 +33,22 @@ public class ScriptContextMenu extends ContextMenu {
});
getItems().add(copyvalue);
this.setStyle("-fx-background-color: -fx-color; -fx-font-family: sans-serif; -fx-font-size: 1em;");
}
private void checkMenuItems(boolean showing)
{
if(!showing) return;
area = (CodeArea)getOwnerNode();
range = area.getSelection();
copyvalue.setDisable(hoverChunk == null);
}
public void setHoverChunk(ScriptChunk hoverChunk) {
this.hoverChunk = hoverChunk;
area.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> {
hoverChunk = null;
Point2D point = area.screenToLocal(event.getScreenX(), event.getScreenY());
OptionalInt characterIndex = area.hit(point.getX(), point.getY()).getCharacterIndex();
if(characterIndex.isPresent()) {
TwoDimensional.Position position = area.getParagraph(0).getStyleSpans().offsetToPosition(characterIndex.getAsInt(), Backward);
if(position.getMajor() % 2 == 0) {
ScriptChunk chunk = script.getChunks().get(position.getMajor() / 2);
if(!chunk.isOpCode()) {
this.hoverChunk = chunk;
}
}
}
copyvalue.setDisable(hoverChunk == null);
});
}
}

View file

@ -4,6 +4,7 @@ import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.protocol.*;
import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.psbt.PSBTInput;
import com.sparrowwallet.drongo.psbt.PSBTOutput;
import com.sparrowwallet.sparrow.EventManager;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
@ -48,11 +49,11 @@ public class TransactionController implements Initializable, TransactionListener
}
private void initializeTxTree() {
HeadersForm headersForm = new HeadersForm(transaction, psbt);
HeadersForm headersForm = (psbt == null ? new HeadersForm(transaction) : new HeadersForm(psbt));
TreeItem<TransactionForm> rootItem = new TreeItem<>(headersForm);
rootItem.setExpanded(true);
InputsForm inputsForm = new InputsForm(transaction, psbt);
InputsForm inputsForm = (psbt == null ? new InputsForm(transaction) : new InputsForm(psbt));
TreeItem<TransactionForm> inputsItem = new TreeItem<>(inputsForm);
inputsItem.setExpanded(true);
for(TransactionInput txInput : transaction.getInputs()) {
@ -60,16 +61,20 @@ public class TransactionController implements Initializable, TransactionListener
if(psbt != null && psbt.getPsbtInputs().size() > txInput.getIndex()) {
psbtInput = psbt.getPsbtInputs().get(txInput.getIndex());
}
InputForm inputForm = new InputForm(txInput, psbtInput);
InputForm inputForm = (psbt == null ? new InputForm(transaction, txInput) : new InputForm(psbt, psbtInput));
TreeItem<TransactionForm> inputItem = new TreeItem<>(inputForm);
inputsItem.getChildren().add(inputItem);
}
OutputsForm outputsForm = new OutputsForm(transaction);
OutputsForm outputsForm = (psbt == null ? new OutputsForm(transaction) : new OutputsForm(psbt));
TreeItem<TransactionForm> outputsItem = new TreeItem<>(outputsForm);
outputsItem.setExpanded(true);
for(TransactionOutput txOutput : transaction.getOutputs()) {
OutputForm outputForm = new OutputForm(txOutput);
PSBTOutput psbtOutput = null;
if(psbt != null && psbt.getPsbtOutputs().size() > txOutput.getIndex()) {
psbtOutput = psbt.getPsbtOutputs().get(txOutput.getIndex());
}
OutputForm outputForm = (psbt == null ? new OutputForm(transaction, txOutput) : new OutputForm(psbt, psbtOutput));
TreeItem<TransactionForm> outputItem = new TreeItem<>(outputForm);
outputsItem.getChildren().add(outputItem);
}

View file

@ -1,9 +1,31 @@
package com.sparrowwallet.sparrow.transaction;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.psbt.PSBT;
import javafx.scene.Node;
import java.io.IOException;
public abstract class TransactionForm {
private Transaction transaction;
private PSBT psbt;
public TransactionForm(PSBT psbt) {
this.transaction = psbt.getTransaction();
this.psbt = psbt;
}
public TransactionForm(Transaction transaction) {
this.transaction = transaction;
}
public Transaction getTransaction() {
return transaction;
}
public PSBT getPsbt() {
return psbt;
}
public abstract Node getContents() throws IOException;
}

View file

@ -88,7 +88,6 @@ public abstract class TransactionFormController {
} else if(chunk.isPubKey()) {
codeArea.append("<pubkey" + pubKeyCount++ + ">", "script-pubkey");
} else {
System.out.println(chunk.isOpCode() + " " + chunk.opcode);
codeArea.append(chunk.toString(), "script-other");
}
@ -102,7 +101,7 @@ public abstract class TransactionFormController {
}
protected void addScriptPopup(CodeArea area, Script script) {
ScriptContextMenu contextMenu = new ScriptContextMenu();
ScriptContextMenu contextMenu = new ScriptContextMenu(area, script);
area.setContextMenu(contextMenu);
Popup popup = new Popup();
@ -116,15 +115,10 @@ public abstract class TransactionFormController {
if(position.getMajor() % 2 == 0) {
ScriptChunk hoverChunk = script.getChunks().get(position.getMajor()/2);
if(!hoverChunk.isOpCode()) {
contextMenu.setHoverChunk(hoverChunk);
Point2D pos = e.getScreenPosition();
popupMsg.setText(hoverChunk.toString());
popup.show(area, pos.getX(), pos.getY() + 10);
} else {
contextMenu.setHoverChunk(null);
}
} else {
contextMenu.setHoverChunk(null);
}
});
area.addEventHandler(MouseOverTextEvent.MOUSE_OVER_TEXT_END, e -> {

View file

@ -50,14 +50,14 @@
</Form>
<Form GridPane.columnIndex="1" GridPane.rowIndex="2">
<Fieldset fx:id="locktimeFieldset" text="Locktime" inputGrow="SOMETIMES">
<Fieldset fx:id="locktimeFieldset" text="Absolute Locktime" inputGrow="SOMETIMES">
<Field text="Type:">
<SegmentedButton>
<toggleGroup>
<ToggleGroup fx:id="locktimeToggleGroup" />
</toggleGroup>
<buttons>
<ToggleButton fx:id="locktimeNoneType" text="None" userData="none" toggleGroup="$locktimeToggleGroup" />
<ToggleButton fx:id="locktimeNoneType" text="Disabled" userData="none" toggleGroup="$locktimeToggleGroup" />
<ToggleButton fx:id="locktimeBlockType" text="Block" userData="block" toggleGroup="$locktimeToggleGroup" />
<ToggleButton fx:id="locktimeDateType" text="Date" userData="date" toggleGroup="$locktimeToggleGroup" />
</buttons>

View file

@ -7,6 +7,9 @@
<?import tornadofx.control.*?>
<?import org.fxmisc.richtext.CodeArea?>
<?import org.fxmisc.flowless.VirtualizedScrollPane?>
<?import org.controlsfx.control.SegmentedButton?>
<?import javafx.collections.FXCollections?>
<?import java.lang.String?>
<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>
@ -40,8 +43,8 @@
<Form GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="2">
<Fieldset inputGrow="SOMETIMES" text="Script">
<Field text="ScriptSig:">
<HBox>
<VirtualizedScrollPane prefHeight="42">
<HBox prefHeight="42">
<VirtualizedScrollPane>
<content>
<CodeArea fx:id="scriptSigArea" editable="false" minWidth="570" prefWidth="570" wrapText="true" styleClass="uneditable-codearea" />
</content>
@ -49,8 +52,8 @@
</HBox>
</Field>
<Field text="RedeemScript:">
<HBox>
<VirtualizedScrollPane fx:id="redeemScriptScroll" prefHeight="42">
<HBox prefHeight="42">
<VirtualizedScrollPane fx:id="redeemScriptScroll">
<content>
<CodeArea fx:id="redeemScriptArea" editable="false" minWidth="570" prefWidth="570" wrapText="true" styleClass="uneditable-codearea" />
</content>
@ -58,8 +61,8 @@
</HBox>
</Field>
<Field text="Witnesses:">
<HBox>
<VirtualizedScrollPane fx:id="witnessesScroll" prefHeight="42">
<HBox prefHeight="42">
<VirtualizedScrollPane fx:id="witnessesScroll">
<content>
<CodeArea fx:id="witnessesArea" editable="false" minWidth="570" prefWidth="570" wrapText="true" styleClass="uneditable-codearea" />
</content>
@ -67,8 +70,8 @@
</HBox>
</Field>
<Field text="WitnessScript:">
<HBox>
<VirtualizedScrollPane fx:id="witnessScriptScroll" prefHeight="42">
<HBox prefHeight="42">
<VirtualizedScrollPane fx:id="witnessScriptScroll">
<content>
<CodeArea fx:id="witnessScriptArea" editable="false" minWidth="570" prefWidth="570" wrapText="true" styleClass="uneditable-codearea" />
</content>
@ -77,4 +80,41 @@
</Field>
</Fieldset>
</Form>
<Separator styleClass="form-separator" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="3" />
<Form GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="4">
<Fieldset fx:id="locktimeFieldset" text="Locktime" inputGrow="SOMETIMES">
<Field text="Type:">
<SegmentedButton>
<toggleGroup>
<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" />
</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"/>
<ComboBox fx:id="locktimeRelativeCombo">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="blocks" />
<String fx:value="seconds" />
</FXCollections>
</items>
</ComboBox>
</Field>
</Fieldset>
</Form>
</GridPane>