From 4d97832d9760069bf2fd864498e180f4e1c1022a Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Sun, 5 Apr 2020 16:01:49 +0200 Subject: [PATCH] input pane sequence number --- drongo | 2 +- .../sparrowwallet/sparrow/AppController.java | 2 +- .../transaction/HeadersController.java | 30 ++--- .../sparrow/transaction/HeadersForm.java | 18 +-- .../sparrow/transaction/InputController.java | 120 +++++++++++++++++- .../sparrow/transaction/InputForm.java | 12 +- .../sparrow/transaction/InputsForm.java | 16 +-- .../sparrow/transaction/OutputForm.java | 17 ++- .../sparrow/transaction/OutputsForm.java | 11 +- .../sparrow/transaction/PartialInputForm.java | 4 +- .../transaction/PartialOutputForm.java | 4 +- .../transaction/ScriptContextMenu.java | 45 ++++--- .../transaction/TransactionController.java | 15 ++- .../sparrow/transaction/TransactionForm.java | 22 ++++ .../TransactionFormController.java | 8 +- .../sparrow/transaction/headers.fxml | 4 +- .../sparrow/transaction/input.fxml | 56 ++++++-- 17 files changed, 290 insertions(+), 96 deletions(-) diff --git a/drongo b/drongo index d28186f8..130fea09 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit d28186f8c9ca7fd53a6978b84b07e6096d55df1c +Subproject commit 130fea0937629b62305892d00bc8099872259095 diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index 19b6de25..0ceb16a1 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -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"); } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java index 982a64eb..a49dd95d 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java @@ -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)"); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersForm.java index efad6294..30d7418a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersForm.java @@ -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) + "]"; } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java index d3df87cc..64e41904 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java @@ -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 locktimeNone; + + @FXML + private TextField locktimeAbsolute; + + @FXML + private Spinner locktimeRelative; + + @FXML + private ComboBox 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(); diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/InputForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/InputForm.java index ef7d379e..f18cab41 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/InputForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/InputForm.java @@ -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; } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/InputsForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/InputsForm.java index 287ecbf3..ef5f374d 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/InputsForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/InputsForm.java @@ -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 { diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java index 34297219..0d98daee 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java @@ -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(); diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputsForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputsForm.java index a060a72c..74710316 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputsForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputsForm.java @@ -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 { diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/PartialInputForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/PartialInputForm.java index ed43ba53..c43878e1 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/PartialInputForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/PartialInputForm.java @@ -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; } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/PartialOutputForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/PartialOutputForm.java index ebfa3974..b2ff376d 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/PartialOutputForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/PartialOutputForm.java @@ -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; } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/ScriptContextMenu.java b/src/main/java/com/sparrowwallet/sparrow/transaction/ScriptContextMenu.java index a69880d5..bd6d97cf 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/ScriptContextMenu.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/ScriptContextMenu.java @@ -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); + }); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java index da43bbfc..d0bdb74c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java @@ -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 rootItem = new TreeItem<>(headersForm); rootItem.setExpanded(true); - InputsForm inputsForm = new InputsForm(transaction, psbt); + InputsForm inputsForm = (psbt == null ? new InputsForm(transaction) : new InputsForm(psbt)); TreeItem 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 inputItem = new TreeItem<>(inputForm); inputsItem.getChildren().add(inputItem); } - OutputsForm outputsForm = new OutputsForm(transaction); + OutputsForm outputsForm = (psbt == null ? new OutputsForm(transaction) : new OutputsForm(psbt)); TreeItem 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 outputItem = new TreeItem<>(outputForm); outputsItem.getChildren().add(outputItem); } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java index 1c8d4f7b..1c67b6fe 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java @@ -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; } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionFormController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionFormController.java index ac6160a2..0ff10924 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionFormController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionFormController.java @@ -88,7 +88,6 @@ public abstract class TransactionFormController { } else if(chunk.isPubKey()) { codeArea.append("", "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 -> { diff --git a/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml b/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml index 63255e0d..e5ea11a8 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml @@ -50,14 +50,14 @@
-
+
- + diff --git a/src/main/resources/com/sparrowwallet/sparrow/transaction/input.fxml b/src/main/resources/com/sparrowwallet/sparrow/transaction/input.fxml index 95dbb388..76ff8d82 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/transaction/input.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/transaction/input.fxml @@ -7,6 +7,9 @@ + + + @@ -40,8 +43,8 @@
- - + + @@ -49,8 +52,8 @@ - - + + @@ -58,8 +61,8 @@ - - + + @@ -67,8 +70,8 @@ - - + + @@ -77,4 +80,41 @@
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+