From 7bfddc1c98961d84625ffd1f2244ed4a541186e5 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Mon, 30 Mar 2020 11:43:03 +0200 Subject: [PATCH] inputs and outputs panes #1 --- drongo | 2 +- .../com/craigraw/sparrow/AppController.java | 54 ++++++++++------ .../sparrow/TransactionController.java | 38 ++++++++---- .../craigraw/sparrow/form/FormController.java | 46 ++++++++++++++ .../sparrow/form/HeadersController.java | 29 ++++++--- .../sparrow/form/InputsController.java | 62 ++++++++++++++++++- .../com/craigraw/sparrow/form/InputsForm.java | 13 +++- .../sparrow/form/OutputsController.java | 32 +++++++++- .../craigraw/sparrow/form/OutputsForm.java | 4 ++ .../resources/com/craigraw/sparrow/app.fxml | 2 +- .../com/craigraw/sparrow/form/headers.css | 18 ++++++ .../com/craigraw/sparrow/form/headers.fxml | 6 +- .../com/craigraw/sparrow/form/inputs.css | 31 ++++++++++ .../com/craigraw/sparrow/form/inputs.fxml | 47 +++++++++++--- .../com/craigraw/sparrow/form/outputs.css | 31 ++++++++++ .../com/craigraw/sparrow/form/outputs.fxml | 40 +++++++++--- .../com/craigraw/sparrow/general.css | 2 +- .../com/craigraw/sparrow/transaction.css | 10 --- 18 files changed, 392 insertions(+), 75 deletions(-) create mode 100644 src/main/java/com/craigraw/sparrow/form/FormController.java create mode 100644 src/main/resources/com/craigraw/sparrow/form/headers.css create mode 100644 src/main/resources/com/craigraw/sparrow/form/inputs.css create mode 100644 src/main/resources/com/craigraw/sparrow/form/outputs.css diff --git a/drongo b/drongo index 7fb5601d..a1696ec2 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 7fb5601de38e79d5c0547ebc684ca137aa895134 +Subproject commit a1696ec2e8d5ca17fc98246e374e9d3ae9ab368f diff --git a/src/main/java/com/craigraw/sparrow/AppController.java b/src/main/java/com/craigraw/sparrow/AppController.java index 70050439..c4c8964c 100644 --- a/src/main/java/com/craigraw/sparrow/AppController.java +++ b/src/main/java/com/craigraw/sparrow/AppController.java @@ -42,9 +42,6 @@ public class AppController implements Initializable { File file = fileChooser.showOpenDialog(window); if (file != null) { - System.out.println(file); - //openFile(file); - if(file.exists()) { try { byte[] bytes = new byte[(int)file.length()]; @@ -52,16 +49,21 @@ public class AppController implements Initializable { stream.read(bytes); stream.close(); + Tab tab = null; if(PSBT.isPSBT(Hex.toHexString(bytes))) { PSBT psbt = new PSBT(bytes); + tab = addTransactionTab(file.getName(), null, psbt); + } - Tab tab = new Tab(file.getName()); - FXMLLoader transactionLoader = new FXMLLoader(getClass().getResource("transaction.fxml")); - tab.setContent(transactionLoader.load()); - TransactionController controller = transactionLoader.getController(); - controller.setPSBT(psbt); + try { + Transaction transaction = new Transaction(bytes); + tab = addTransactionTab(file.getName(), transaction, null); + } catch(Exception e) { + //TODO: Handle not a transaction + } - tabs.getTabs().add(tab); + if(tab != null) { + tabs.getSelectionModel().select(tab); } } catch(IOException e) { throw new RuntimeException(e); @@ -71,25 +73,41 @@ public class AppController implements Initializable { } private void addExampleTxTabs() { - addTransactionTab("p2pkh", "01000000019c2e0f24a03e72002a96acedb12a632e72b6b74c05dc3ceab1fe78237f886c48010000006a47304402203da9d487be5302a6d69e02a861acff1da472885e43d7528ed9b1b537a8e2cac9022002d1bca03a1e9715a99971bafe3b1852b7a4f0168281cbd27a220380a01b3307012102c9950c622494c2e9ff5a003e33b690fe4832477d32c2d256c67eab8bf613b34effffffff02b6f50500000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588ac845e0201000000001976a9145fb0e9755a3424efd2ba0587d20b1e98ee29814a88ac06241559"); - addTransactionTab("p2sh-p2wpkh", "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"); - addTransactionTab("p2wpkh", "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"); - addTransactionTab("p2wsh", "01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000"); - addTransactionTab("test1", "02000000000102ba4dc5a4a14bfaa941b7d115b379b5e15f960635cf694c178b9116763cbd63b11600000017160014fc164cbcac023f5eacfcead2d17d8768c41949affeffffff074d44d2856beb68ba52e8832da60a1682768c2421c2d9a8109ef4e66babd1fd1e000000171600148c3098be6b430859115f5ee99c84c368afecd048feffffff02305310000000000017a914ffaf369c2212b178c7a2c21c9ccdd5d126e74c4187327f0300000000001976a914a7cda2e06b102a143ab606937a01d152e300cd3e88ac02473044022006da0ca227f765179219e08a33026b94e7cacff77f87b8cd8eb1b46d6dda11d6022064faa7912924fd23406b6ed3328f1bbbc3760dc51109a49c1b38bf57029d304f012103c6a2fcd030270427d4abe1041c8af929a9e2dbab07b243673453847ab842ee1f024730440220786316a16095105a0af28dccac5cf80f449dea2ea810a9559a89ecb989c2cb3d02205cbd9913d1217ffec144ae4f2bd895f16d778c2ec49ae9c929fdc8bcc2a2b1db0121024d4985241609d072a59be6418d700e87688f6c4d99a51ad68e66078211f076ee38820900"); + addTransactionTab("p2pkh", "01000000019c2e0f24a03e72002a96acedb12a632e72b6b74c05dc3ceab1fe78237f886c48010000006a47304402203da9d487be5302a6d69e02a861acff1da472885e43d7528ed9b1b537a8e2cac9022002d1bca03a1e9715a99971bafe3b1852b7a4f0168281cbd27a220380a01b3307012102c9950c622494c2e9ff5a003e33b690fe4832477d32c2d256c67eab8bf613b34effffffff02b6f50500000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588ac845e0201000000001976a9145fb0e9755a3424efd2ba0587d20b1e98ee29814a88ac06241559", null); + addTransactionTab("p2sh-p2wpkh", "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000", null); + addTransactionTab("p2wpkh", "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000", null); + addTransactionTab("p2wsh", "01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", null); + addTransactionTab("test1", "02000000000102ba4dc5a4a14bfaa941b7d115b379b5e15f960635cf694c178b9116763cbd63b11600000017160014fc164cbcac023f5eacfcead2d17d8768c41949affeffffff074d44d2856beb68ba52e8832da60a1682768c2421c2d9a8109ef4e66babd1fd1e000000171600148c3098be6b430859115f5ee99c84c368afecd048feffffff02305310000000000017a914ffaf369c2212b178c7a2c21c9ccdd5d126e74c4187327f0300000000001976a914a7cda2e06b102a143ab606937a01d152e300cd3e88ac02473044022006da0ca227f765179219e08a33026b94e7cacff77f87b8cd8eb1b46d6dda11d6022064faa7912924fd23406b6ed3328f1bbbc3760dc51109a49c1b38bf57029d304f012103c6a2fcd030270427d4abe1041c8af929a9e2dbab07b243673453847ab842ee1f024730440220786316a16095105a0af28dccac5cf80f449dea2ea810a9559a89ecb989c2cb3d02205cbd9913d1217ffec144ae4f2bd895f16d778c2ec49ae9c929fdc8bcc2a2b1db0121024d4985241609d072a59be6418d700e87688f6c4d99a51ad68e66078211f076ee38820900", null); + addTransactionTab("3of3-1signed.psbt", null, "70736274ff0100550200000001294c4871c059bb76be81e94b78059ee2e0c9b1b47f38edb6b4e75916062394930000000000feffffff01f82a0000000000001976a914e65b294f890792f2c2725d488567018d660f0cf488ac701c09004f0102aa7ed3044b1635bb800000021bf4bfc48934b7966b39bdebb689525d9b8bfed5c8b16e8c58f9afe4641d6d5f03800b5dbec0355c9f0b5e8227bc903e9d0ff1fe6ced0dcfb6d416541c7412c4331406b57041300000800000008000000080020000804f0102aa7ed3042cd31dee80000002d544b2364010378f8c6cec85f6b7ed83a8203dcdbedb97e2625f431f897b837e0363428de8fcfbfe373c0d9e1e0cc8163d886764bafe71c5822eaa232981356589145f63394f300000800000008000000080020000804f0102aa7ed3049ec7d9f580000002793e04aff18b4e40ebc48bcdc6232c54c69cf7265a38fbd85b35705e34d2d42f03368e79aa2b2b7f736d156905a7a45891df07baa2d0b7f127a537908cb82deed514130a48af300000800000008000000080020000800001012b983a000000000000220020f64748dad1cbad107761aaed5c59f25aba006498d260b440e0a091691350c9aa010569532102f26969eb8d1da34d17d33ff99e2f020cc33b3d11d9798ec14f46b82bc455d3262103171d9b824205cd5db6e9353676a292ca954b24d8310a36fc983469ba3fb507a221037f3794f3be4c4acc086ac84d6902c025713eabf8890f20f44acf0b34e3c0f0f753ae220602f26969eb8d1da34d17d33ff99e2f020cc33b3d11d9798ec14f46b82bc455d3261c130a48af300000800000008000000080020000800000000000000000220603171d9b824205cd5db6e9353676a292ca954b24d8310a36fc983469ba3fb507a21c5f63394f300000800000008000000080020000800000000000000000220203171d9b824205cd5db6e9353676a292ca954b24d8310a36fc983469ba3fb507a24830450221008d27cc4b03bc543726e73b69e7980e7364d6f33f979a5cd9b92fb3d050666bd002204fc81fc9c67baf7c3b77041ed316714a9c117a5bdbb020e8c771ea3bdc342434012206037f3794f3be4c4acc086ac84d6902c025713eabf8890f20f44acf0b34e3c0f0f71c06b570413000008000000080000000800200008000000000000000000000"); } - private void addTransactionTab(String name, String hex) { - try { - byte[] txbytes = Utils.hexToBytes(hex); + private void addTransactionTab(String name, String transactionHex, String psbtHex) { + if(transactionHex != null) { + byte[] txbytes = Utils.hexToBytes(transactionHex); Transaction transaction = new Transaction(txbytes); + addTransactionTab(name, transaction, null); + } else if(psbtHex != null) { + PSBT psbt = PSBT.fromString(psbtHex); + addTransactionTab(name, null, psbt); + } + } + private Tab addTransactionTab(String name, Transaction transaction, PSBT psbt) { + try { Tab tab = new Tab(name); + tab.setClosable(true); FXMLLoader transactionLoader = new FXMLLoader(getClass().getResource("transaction.fxml")); tab.setContent(transactionLoader.load()); TransactionController controller = transactionLoader.getController(); - controller.setTransaction(transaction); + + if(transaction != null) { + controller.setTransaction(transaction); + } else if(psbt != null) { + controller.setPSBT(psbt); + } tabs.getTabs().add(tab); + return tab; } catch(IOException e) { throw new RuntimeException(e); } diff --git a/src/main/java/com/craigraw/sparrow/TransactionController.java b/src/main/java/com/craigraw/sparrow/TransactionController.java index fb382424..07fd9642 100644 --- a/src/main/java/com/craigraw/sparrow/TransactionController.java +++ b/src/main/java/com/craigraw/sparrow/TransactionController.java @@ -5,6 +5,8 @@ import com.craigraw.drongo.psbt.PSBT; import com.craigraw.sparrow.form.*; import javafx.fxml.FXML; import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.Parent; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.control.cell.TextFieldTreeCell; @@ -47,7 +49,7 @@ public class TransactionController implements Initializable, TransactionListener TreeItem
rootItem = new TreeItem<>(headersForm); rootItem.setExpanded(true); - InputsForm inputsForm = new InputsForm(transaction); + InputsForm inputsForm = new InputsForm(transaction, psbt); TreeItem inputsItem = new TreeItem<>(inputsForm); inputsItem.setExpanded(true); for(TransactionInput txInput : transaction.getInputs()) { @@ -83,10 +85,16 @@ public class TransactionController implements Initializable, TransactionListener txtree.getSelectionModel().selectedItemProperty().addListener((observable, old_val, new_val) -> { Form form = new_val.getValue(); - try { - txpane.getChildren().removeAll(); - txpane.getChildren().add(form.getContents()); + Node node = form.getContents(); + txpane.getChildren().clear(); + txpane.getChildren().add(node); + + if(node instanceof Parent) { + Parent parent = (Parent)node; + txhex.getStylesheets().clear(); + txhex.getStylesheets().addAll(parent.getStylesheets()); + } } catch (IOException e) { throw new IllegalStateException("Can't find pane", e); } @@ -120,24 +128,30 @@ public class TransactionController implements Initializable, TransactionListener } //Number of inputs - cursor = addText(hex, cursor, 2, "num-inputs"); + VarInt numInputs = new VarInt(transaction.getInputs().size()); + cursor = addText(hex, cursor, numInputs.getSizeInBytes()*2, "num-inputs"); //Inputs - int totalInputLength = 0; for(TransactionInput input : transaction.getInputs()) { - totalInputLength += input.getLength(); + cursor = addText(hex, cursor, 32*2, "input-hash"); + cursor = addText(hex, cursor, 4*2, "input-index"); + VarInt scriptLen = new VarInt(input.getScriptBytes().length); + cursor = addText(hex, cursor, scriptLen.getSizeInBytes()*2, "input-sigscript-length"); + cursor = addText(hex, cursor, (int)scriptLen.value*2, "input-sigscript"); + cursor = addText(hex, cursor, 4*2, "input-sequence"); } - cursor = addText(hex, cursor, totalInputLength*2, "inputs"); //Number of outputs - cursor = addText(hex, cursor, 2, "num-outputs"); + VarInt numOutputs = new VarInt(transaction.getOutputs().size()); + cursor = addText(hex, cursor, numOutputs.getSizeInBytes()*2, "num-outputs"); //Outputs - int totalOutputLength = 0; for(TransactionOutput output : transaction.getOutputs()) { - totalOutputLength += output.getLength(); + cursor = addText(hex, cursor, 8*2, "output-value"); + VarInt scriptLen = new VarInt(output.getScriptBytes().length); + cursor = addText(hex, cursor, scriptLen.getSizeInBytes()*2, "output-pubkeyscript-length"); + cursor = addText(hex, cursor, (int)scriptLen.value*2, "output-pubkeyscript"); } - cursor = addText(hex, cursor, totalOutputLength*2, "outputs"); if(transaction.hasWitnesses()) { int totalWitnessLength = 0; diff --git a/src/main/java/com/craigraw/sparrow/form/FormController.java b/src/main/java/com/craigraw/sparrow/form/FormController.java new file mode 100644 index 00000000..a8d8f043 --- /dev/null +++ b/src/main/java/com/craigraw/sparrow/form/FormController.java @@ -0,0 +1,46 @@ +package com.craigraw.sparrow.form; + +import com.craigraw.drongo.address.Address; +import com.craigraw.drongo.protocol.TransactionOutput; +import com.craigraw.drongo.protocol.NonStandardScriptException; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.chart.PieChart; +import javafx.scene.control.Tooltip; + +import java.util.List; + +public abstract class FormController { + protected void addPieData(PieChart pie, List outputs) { + ObservableList outputsPieData = FXCollections.observableArrayList(); + + long totalAmt = 0; + for(TransactionOutput output : outputs) { + String name = "Unknown"; + try { + Address[] addresses = output.getScript().getToAddresses(); + if(addresses.length == 1) { + name = addresses[0].getAddress(); + } else { + name = "[" + addresses[0].getAddress() + ",...]"; + } + } catch(NonStandardScriptException e) { + //ignore + } + + totalAmt += output.getValue(); + outputsPieData.add(new PieChart.Data(name, output.getValue())); + } + + pie.setData(outputsPieData); + + final double totalSum = totalAmt; + pie.getData().forEach(data -> { + Tooltip tooltip = new Tooltip(); + double percent = 100.0 * (data.getPieValue() / totalSum); + tooltip.setText(String.format("%.1f", percent) + "%"); + Tooltip.install(data.getNode(), tooltip); + data.pieValueProperty().addListener((observable, oldValue, newValue) -> tooltip.setText(newValue + "%")); + }); + } +} diff --git a/src/main/java/com/craigraw/sparrow/form/HeadersController.java b/src/main/java/com/craigraw/sparrow/form/HeadersController.java index f16dd31b..b3b9c967 100644 --- a/src/main/java/com/craigraw/sparrow/form/HeadersController.java +++ b/src/main/java/com/craigraw/sparrow/form/HeadersController.java @@ -6,7 +6,6 @@ import com.craigraw.sparrow.TransactionListener; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; -import javafx.scene.layout.GridPane; import tornadofx.control.DateTimePicker; import tornadofx.control.Field; import tornadofx.control.Fieldset; @@ -20,9 +19,6 @@ public class HeadersController implements Initializable, TransactionListener { private static final long MAX_BLOCK_LOCKTIME = 500000000L; - @FXML - private GridPane layout; - @FXML private TextField id; @@ -65,6 +61,9 @@ public class HeadersController implements Initializable, TransactionListener { @FXML private TextField virtualSize; + @FXML + private TextField feeRateField; + @Override public void initialize(URL location, ResourceBundle resources) { EventManager.get().subscribe(this); @@ -72,6 +71,10 @@ public class HeadersController implements Initializable, TransactionListener { void setModel(HeadersForm form) { this.headersForm = form; + initializeView(); + } + + private void initializeView() { Transaction tx = headersForm.getTransaction(); updateTxId(); @@ -137,15 +140,21 @@ public class HeadersController implements Initializable, TransactionListener { EventManager.get().notify(tx); }); - if(form.getPsbt() != null) { - fee.setText(form.getPsbt().getFee().toString() + " sats"); + size.setText(tx.getSize() + " B"); + virtualSize.setText(tx.getVirtualSize() + " vB"); + + Long feeAmt = null; + if(headersForm.getPsbt() != null) { + feeAmt = headersForm.getPsbt().getFee(); + } + + if(feeAmt != null) { + fee.setText(feeAmt + " sats"); + double feeRate = feeAmt.doubleValue() / tx.getVirtualSize(); + feeRateField.setText(String.format("%.2f", feeRate) + " sats/vByte"); } else { fee.setText("Unknown"); } - - size.setText(tx.getSize() + " B"); - - virtualSize.setText(tx.getVirtualSize() + " vB"); } private void updateTxId() { diff --git a/src/main/java/com/craigraw/sparrow/form/InputsController.java b/src/main/java/com/craigraw/sparrow/form/InputsController.java index a3a7b809..7bebbf4b 100644 --- a/src/main/java/com/craigraw/sparrow/form/InputsController.java +++ b/src/main/java/com/craigraw/sparrow/form/InputsController.java @@ -1,13 +1,32 @@ package com.craigraw.sparrow.form; +import com.craigraw.drongo.protocol.*; +import com.craigraw.drongo.psbt.PSBTInput; +import javafx.fxml.FXML; import javafx.fxml.Initializable; +import javafx.scene.chart.PieChart; +import javafx.scene.control.TextField; import java.net.URL; +import java.util.ArrayList; +import java.util.List; import java.util.ResourceBundle; -public class InputsController implements Initializable { +public class InputsController extends FormController implements Initializable { private InputsForm inputsForm; + @FXML + private TextField count; + + @FXML + private TextField total; + + @FXML + private TextField signatures; + + @FXML + private PieChart inputsPie; + @Override public void initialize(URL location, ResourceBundle resources) { @@ -15,5 +34,46 @@ public class InputsController implements Initializable { public void setModel(InputsForm form) { this.inputsForm = form; + initialiseView(); + } + + private void initialiseView() { + Transaction tx = inputsForm.getTransaction(); + count.setText(Integer.toString(tx.getInputs().size())); + + total.setText("Unknown"); + signatures.setText("Unknown"); + + if(inputsForm.getPsbt() != null) { + int reqSigs = 0; + int foundSigs = 0; + + List outputs = new ArrayList<>(); + for(int i = 0; i < tx.getInputs().size(); i++) { + TransactionInput input = tx.getInputs().get(i); + PSBTInput psbtInput = inputsForm.getPsbt().getPsbtInputs().get(i); + + if(psbtInput.getNonWitnessUtxo() != null) { + outputs.add(psbtInput.getNonWitnessUtxo().getOutputs().get((int)input.getOutpoint().getIndex())); + } else if(psbtInput.getWitnessUtxo() != null) { + outputs.add(psbtInput.getWitnessUtxo()); + } + + try { + reqSigs += psbtInput.getSigningScript().getNumRequiredSignatures(); + foundSigs += psbtInput.getPartialSignatures().size(); + } catch (NonStandardScriptException e) { + //TODO: Handle unusual transaction sig + } + } + + long totalAmt = 0; + for(TransactionOutput output : outputs) { + totalAmt += output.getValue(); + } + total.setText(totalAmt + " sats"); + signatures.setText(foundSigs + "/" + reqSigs); + addPieData(inputsPie, outputs); + } } } diff --git a/src/main/java/com/craigraw/sparrow/form/InputsForm.java b/src/main/java/com/craigraw/sparrow/form/InputsForm.java index 90c660eb..e880902f 100644 --- a/src/main/java/com/craigraw/sparrow/form/InputsForm.java +++ b/src/main/java/com/craigraw/sparrow/form/InputsForm.java @@ -1,6 +1,7 @@ package com.craigraw.sparrow.form; import com.craigraw.drongo.protocol.Transaction; +import com.craigraw.drongo.psbt.PSBT; import javafx.fxml.FXMLLoader; import javafx.scene.Node; @@ -8,9 +9,19 @@ import java.io.IOException; public class InputsForm extends Form { private Transaction transaction; + private PSBT psbt; - public InputsForm(Transaction transaction) { + public InputsForm(Transaction transaction, PSBT psbt) { this.transaction = transaction; + this.psbt = psbt; + } + + public Transaction getTransaction() { + return transaction; + } + + public PSBT getPsbt() { + return psbt; } public Node getContents() throws IOException { diff --git a/src/main/java/com/craigraw/sparrow/form/OutputsController.java b/src/main/java/com/craigraw/sparrow/form/OutputsController.java index a3ff71ee..45a461f9 100644 --- a/src/main/java/com/craigraw/sparrow/form/OutputsController.java +++ b/src/main/java/com/craigraw/sparrow/form/OutputsController.java @@ -1,13 +1,27 @@ package com.craigraw.sparrow.form; +import com.craigraw.drongo.protocol.Transaction; +import com.craigraw.drongo.protocol.TransactionOutput; +import javafx.fxml.FXML; import javafx.fxml.Initializable; +import javafx.scene.chart.PieChart; +import javafx.scene.control.TextField; import java.net.URL; import java.util.ResourceBundle; -public class OutputsController implements Initializable { +public class OutputsController extends FormController implements Initializable { private OutputsForm outputsForm; + @FXML + private TextField count; + + @FXML + private TextField total; + + @FXML + private PieChart outputsPie; + @Override public void initialize(URL location, ResourceBundle resources) { @@ -15,5 +29,21 @@ public class OutputsController implements Initializable { public void setModel(OutputsForm form) { this.outputsForm = form; + initialiseView(); + } + + private void initialiseView() { + Transaction tx = outputsForm.getTransaction(); + count.setText(Integer.toString(tx.getOutputs().size())); + + long totalAmt = 0; + for(TransactionOutput output : tx.getOutputs()) { + totalAmt += output.getValue(); + } + total.setText(totalAmt + " sats"); + + if(totalAmt > 0) { + addPieData(outputsPie, tx.getOutputs()); + } } } diff --git a/src/main/java/com/craigraw/sparrow/form/OutputsForm.java b/src/main/java/com/craigraw/sparrow/form/OutputsForm.java index fe85b0d1..53dc6560 100644 --- a/src/main/java/com/craigraw/sparrow/form/OutputsForm.java +++ b/src/main/java/com/craigraw/sparrow/form/OutputsForm.java @@ -13,6 +13,10 @@ public class OutputsForm extends Form { this.transaction = transaction; } + public Transaction getTransaction() { + return transaction; + } + public Node getContents() throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("outputs.fxml")); Node node = loader.load(); diff --git a/src/main/resources/com/craigraw/sparrow/app.fxml b/src/main/resources/com/craigraw/sparrow/app.fxml index 3ba429ed..506ac15f 100644 --- a/src/main/resources/com/craigraw/sparrow/app.fxml +++ b/src/main/resources/com/craigraw/sparrow/app.fxml @@ -25,7 +25,7 @@ - +