inputs and outputs panes #1

This commit is contained in:
Craig Raw 2020-03-30 11:43:03 +02:00
parent 7a2e503624
commit 7bfddc1c98
18 changed files with 392 additions and 75 deletions

2
drongo

@ -1 +1 @@
Subproject commit 7fb5601de38e79d5c0547ebc684ca137aa895134 Subproject commit a1696ec2e8d5ca17fc98246e374e9d3ae9ab368f

View file

@ -42,9 +42,6 @@ public class AppController implements Initializable {
File file = fileChooser.showOpenDialog(window); File file = fileChooser.showOpenDialog(window);
if (file != null) { if (file != null) {
System.out.println(file);
//openFile(file);
if(file.exists()) { if(file.exists()) {
try { try {
byte[] bytes = new byte[(int)file.length()]; byte[] bytes = new byte[(int)file.length()];
@ -52,16 +49,21 @@ public class AppController implements Initializable {
stream.read(bytes); stream.read(bytes);
stream.close(); stream.close();
Tab tab = null;
if(PSBT.isPSBT(Hex.toHexString(bytes))) { if(PSBT.isPSBT(Hex.toHexString(bytes))) {
PSBT psbt = new PSBT(bytes); PSBT psbt = new PSBT(bytes);
tab = addTransactionTab(file.getName(), null, psbt);
}
Tab tab = new Tab(file.getName()); try {
FXMLLoader transactionLoader = new FXMLLoader(getClass().getResource("transaction.fxml")); Transaction transaction = new Transaction(bytes);
tab.setContent(transactionLoader.load()); tab = addTransactionTab(file.getName(), transaction, null);
TransactionController controller = transactionLoader.getController(); } catch(Exception e) {
controller.setPSBT(psbt); //TODO: Handle not a transaction
}
tabs.getTabs().add(tab); if(tab != null) {
tabs.getSelectionModel().select(tab);
} }
} catch(IOException e) { } catch(IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -71,25 +73,41 @@ public class AppController implements Initializable {
} }
private void addExampleTxTabs() { private void addExampleTxTabs() {
addTransactionTab("p2pkh", "01000000019c2e0f24a03e72002a96acedb12a632e72b6b74c05dc3ceab1fe78237f886c48010000006a47304402203da9d487be5302a6d69e02a861acff1da472885e43d7528ed9b1b537a8e2cac9022002d1bca03a1e9715a99971bafe3b1852b7a4f0168281cbd27a220380a01b3307012102c9950c622494c2e9ff5a003e33b690fe4832477d32c2d256c67eab8bf613b34effffffff02b6f50500000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588ac845e0201000000001976a9145fb0e9755a3424efd2ba0587d20b1e98ee29814a88ac06241559"); addTransactionTab("p2pkh", "01000000019c2e0f24a03e72002a96acedb12a632e72b6b74c05dc3ceab1fe78237f886c48010000006a47304402203da9d487be5302a6d69e02a861acff1da472885e43d7528ed9b1b537a8e2cac9022002d1bca03a1e9715a99971bafe3b1852b7a4f0168281cbd27a220380a01b3307012102c9950c622494c2e9ff5a003e33b690fe4832477d32c2d256c67eab8bf613b34effffffff02b6f50500000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588ac845e0201000000001976a9145fb0e9755a3424efd2ba0587d20b1e98ee29814a88ac06241559", null);
addTransactionTab("p2sh-p2wpkh", "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"); addTransactionTab("p2sh-p2wpkh", "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000", null);
addTransactionTab("p2wpkh", "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"); addTransactionTab("p2wpkh", "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000", null);
addTransactionTab("p2wsh", "01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000"); addTransactionTab("p2wsh", "01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", null);
addTransactionTab("test1", "02000000000102ba4dc5a4a14bfaa941b7d115b379b5e15f960635cf694c178b9116763cbd63b11600000017160014fc164cbcac023f5eacfcead2d17d8768c41949affeffffff074d44d2856beb68ba52e8832da60a1682768c2421c2d9a8109ef4e66babd1fd1e000000171600148c3098be6b430859115f5ee99c84c368afecd048feffffff02305310000000000017a914ffaf369c2212b178c7a2c21c9ccdd5d126e74c4187327f0300000000001976a914a7cda2e06b102a143ab606937a01d152e300cd3e88ac02473044022006da0ca227f765179219e08a33026b94e7cacff77f87b8cd8eb1b46d6dda11d6022064faa7912924fd23406b6ed3328f1bbbc3760dc51109a49c1b38bf57029d304f012103c6a2fcd030270427d4abe1041c8af929a9e2dbab07b243673453847ab842ee1f024730440220786316a16095105a0af28dccac5cf80f449dea2ea810a9559a89ecb989c2cb3d02205cbd9913d1217ffec144ae4f2bd895f16d778c2ec49ae9c929fdc8bcc2a2b1db0121024d4985241609d072a59be6418d700e87688f6c4d99a51ad68e66078211f076ee38820900"); addTransactionTab("test1", "02000000000102ba4dc5a4a14bfaa941b7d115b379b5e15f960635cf694c178b9116763cbd63b11600000017160014fc164cbcac023f5eacfcead2d17d8768c41949affeffffff074d44d2856beb68ba52e8832da60a1682768c2421c2d9a8109ef4e66babd1fd1e000000171600148c3098be6b430859115f5ee99c84c368afecd048feffffff02305310000000000017a914ffaf369c2212b178c7a2c21c9ccdd5d126e74c4187327f0300000000001976a914a7cda2e06b102a143ab606937a01d152e300cd3e88ac02473044022006da0ca227f765179219e08a33026b94e7cacff77f87b8cd8eb1b46d6dda11d6022064faa7912924fd23406b6ed3328f1bbbc3760dc51109a49c1b38bf57029d304f012103c6a2fcd030270427d4abe1041c8af929a9e2dbab07b243673453847ab842ee1f024730440220786316a16095105a0af28dccac5cf80f449dea2ea810a9559a89ecb989c2cb3d02205cbd9913d1217ffec144ae4f2bd895f16d778c2ec49ae9c929fdc8bcc2a2b1db0121024d4985241609d072a59be6418d700e87688f6c4d99a51ad68e66078211f076ee38820900", null);
addTransactionTab("3of3-1signed.psbt", null, "70736274ff0100550200000001294c4871c059bb76be81e94b78059ee2e0c9b1b47f38edb6b4e75916062394930000000000feffffff01f82a0000000000001976a914e65b294f890792f2c2725d488567018d660f0cf488ac701c09004f0102aa7ed3044b1635bb800000021bf4bfc48934b7966b39bdebb689525d9b8bfed5c8b16e8c58f9afe4641d6d5f03800b5dbec0355c9f0b5e8227bc903e9d0ff1fe6ced0dcfb6d416541c7412c4331406b57041300000800000008000000080020000804f0102aa7ed3042cd31dee80000002d544b2364010378f8c6cec85f6b7ed83a8203dcdbedb97e2625f431f897b837e0363428de8fcfbfe373c0d9e1e0cc8163d886764bafe71c5822eaa232981356589145f63394f300000800000008000000080020000804f0102aa7ed3049ec7d9f580000002793e04aff18b4e40ebc48bcdc6232c54c69cf7265a38fbd85b35705e34d2d42f03368e79aa2b2b7f736d156905a7a45891df07baa2d0b7f127a537908cb82deed514130a48af300000800000008000000080020000800001012b983a000000000000220020f64748dad1cbad107761aaed5c59f25aba006498d260b440e0a091691350c9aa010569532102f26969eb8d1da34d17d33ff99e2f020cc33b3d11d9798ec14f46b82bc455d3262103171d9b824205cd5db6e9353676a292ca954b24d8310a36fc983469ba3fb507a221037f3794f3be4c4acc086ac84d6902c025713eabf8890f20f44acf0b34e3c0f0f753ae220602f26969eb8d1da34d17d33ff99e2f020cc33b3d11d9798ec14f46b82bc455d3261c130a48af300000800000008000000080020000800000000000000000220603171d9b824205cd5db6e9353676a292ca954b24d8310a36fc983469ba3fb507a21c5f63394f300000800000008000000080020000800000000000000000220203171d9b824205cd5db6e9353676a292ca954b24d8310a36fc983469ba3fb507a24830450221008d27cc4b03bc543726e73b69e7980e7364d6f33f979a5cd9b92fb3d050666bd002204fc81fc9c67baf7c3b77041ed316714a9c117a5bdbb020e8c771ea3bdc342434012206037f3794f3be4c4acc086ac84d6902c025713eabf8890f20f44acf0b34e3c0f0f71c06b570413000008000000080000000800200008000000000000000000000");
} }
private void addTransactionTab(String name, String hex) { private void addTransactionTab(String name, String transactionHex, String psbtHex) {
try { if(transactionHex != null) {
byte[] txbytes = Utils.hexToBytes(hex); byte[] txbytes = Utils.hexToBytes(transactionHex);
Transaction transaction = new Transaction(txbytes); 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 tab = new Tab(name);
tab.setClosable(true);
FXMLLoader transactionLoader = new FXMLLoader(getClass().getResource("transaction.fxml")); FXMLLoader transactionLoader = new FXMLLoader(getClass().getResource("transaction.fxml"));
tab.setContent(transactionLoader.load()); tab.setContent(transactionLoader.load());
TransactionController controller = transactionLoader.getController(); TransactionController controller = transactionLoader.getController();
controller.setTransaction(transaction);
if(transaction != null) {
controller.setTransaction(transaction);
} else if(psbt != null) {
controller.setPSBT(psbt);
}
tabs.getTabs().add(tab); tabs.getTabs().add(tab);
return tab;
} catch(IOException e) { } catch(IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View file

@ -5,6 +5,8 @@ import com.craigraw.drongo.psbt.PSBT;
import com.craigraw.sparrow.form.*; import com.craigraw.sparrow.form.*;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.TreeItem; import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView; import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell; import javafx.scene.control.cell.TextFieldTreeCell;
@ -47,7 +49,7 @@ public class TransactionController implements Initializable, TransactionListener
TreeItem<Form> rootItem = new TreeItem<>(headersForm); TreeItem<Form> rootItem = new TreeItem<>(headersForm);
rootItem.setExpanded(true); rootItem.setExpanded(true);
InputsForm inputsForm = new InputsForm(transaction); InputsForm inputsForm = new InputsForm(transaction, psbt);
TreeItem<Form> inputsItem = new TreeItem<>(inputsForm); TreeItem<Form> inputsItem = new TreeItem<>(inputsForm);
inputsItem.setExpanded(true); inputsItem.setExpanded(true);
for(TransactionInput txInput : transaction.getInputs()) { for(TransactionInput txInput : transaction.getInputs()) {
@ -83,10 +85,16 @@ public class TransactionController implements Initializable, TransactionListener
txtree.getSelectionModel().selectedItemProperty().addListener((observable, old_val, new_val) -> { txtree.getSelectionModel().selectedItemProperty().addListener((observable, old_val, new_val) -> {
Form form = new_val.getValue(); Form form = new_val.getValue();
try { try {
txpane.getChildren().removeAll(); Node node = form.getContents();
txpane.getChildren().add(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) { } catch (IOException e) {
throw new IllegalStateException("Can't find pane", e); throw new IllegalStateException("Can't find pane", e);
} }
@ -120,24 +128,30 @@ public class TransactionController implements Initializable, TransactionListener
} }
//Number of inputs //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 //Inputs
int totalInputLength = 0;
for(TransactionInput input : transaction.getInputs()) { 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 //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 //Outputs
int totalOutputLength = 0;
for(TransactionOutput output : transaction.getOutputs()) { 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()) { if(transaction.hasWitnesses()) {
int totalWitnessLength = 0; int totalWitnessLength = 0;

View file

@ -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<TransactionOutput> outputs) {
ObservableList<PieChart.Data> 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 + "%"));
});
}
}

View file

@ -6,7 +6,6 @@ import com.craigraw.sparrow.TransactionListener;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import tornadofx.control.DateTimePicker; import tornadofx.control.DateTimePicker;
import tornadofx.control.Field; import tornadofx.control.Field;
import tornadofx.control.Fieldset; import tornadofx.control.Fieldset;
@ -20,9 +19,6 @@ public class HeadersController implements Initializable, TransactionListener {
private static final long MAX_BLOCK_LOCKTIME = 500000000L; private static final long MAX_BLOCK_LOCKTIME = 500000000L;
@FXML
private GridPane layout;
@FXML @FXML
private TextField id; private TextField id;
@ -65,6 +61,9 @@ public class HeadersController implements Initializable, TransactionListener {
@FXML @FXML
private TextField virtualSize; private TextField virtualSize;
@FXML
private TextField feeRateField;
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
EventManager.get().subscribe(this); EventManager.get().subscribe(this);
@ -72,6 +71,10 @@ public class HeadersController implements Initializable, TransactionListener {
void setModel(HeadersForm form) { void setModel(HeadersForm form) {
this.headersForm = form; this.headersForm = form;
initializeView();
}
private void initializeView() {
Transaction tx = headersForm.getTransaction(); Transaction tx = headersForm.getTransaction();
updateTxId(); updateTxId();
@ -137,15 +140,21 @@ public class HeadersController implements Initializable, TransactionListener {
EventManager.get().notify(tx); EventManager.get().notify(tx);
}); });
if(form.getPsbt() != null) { size.setText(tx.getSize() + " B");
fee.setText(form.getPsbt().getFee().toString() + " sats"); 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 { } else {
fee.setText("Unknown"); fee.setText("Unknown");
} }
size.setText(tx.getSize() + " B");
virtualSize.setText(tx.getVirtualSize() + " vB");
} }
private void updateTxId() { private void updateTxId() {

View file

@ -1,13 +1,32 @@
package com.craigraw.sparrow.form; 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.fxml.Initializable;
import javafx.scene.chart.PieChart;
import javafx.scene.control.TextField;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class InputsController implements Initializable { public class InputsController extends FormController implements Initializable {
private InputsForm inputsForm; private InputsForm inputsForm;
@FXML
private TextField count;
@FXML
private TextField total;
@FXML
private TextField signatures;
@FXML
private PieChart inputsPie;
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
@ -15,5 +34,46 @@ public class InputsController implements Initializable {
public void setModel(InputsForm form) { public void setModel(InputsForm form) {
this.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<TransactionOutput> 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);
}
} }
} }

View file

@ -1,6 +1,7 @@
package com.craigraw.sparrow.form; package com.craigraw.sparrow.form;
import com.craigraw.drongo.protocol.Transaction; import com.craigraw.drongo.protocol.Transaction;
import com.craigraw.drongo.psbt.PSBT;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Node; import javafx.scene.Node;
@ -8,9 +9,19 @@ import java.io.IOException;
public class InputsForm extends Form { public class InputsForm extends Form {
private Transaction transaction; private Transaction transaction;
private PSBT psbt;
public InputsForm(Transaction transaction) { public InputsForm(Transaction transaction, PSBT psbt) {
this.transaction = transaction; this.transaction = transaction;
this.psbt = psbt;
}
public Transaction getTransaction() {
return transaction;
}
public PSBT getPsbt() {
return psbt;
} }
public Node getContents() throws IOException { public Node getContents() throws IOException {

View file

@ -1,13 +1,27 @@
package com.craigraw.sparrow.form; 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.fxml.Initializable;
import javafx.scene.chart.PieChart;
import javafx.scene.control.TextField;
import java.net.URL; import java.net.URL;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class OutputsController implements Initializable { public class OutputsController extends FormController implements Initializable {
private OutputsForm outputsForm; private OutputsForm outputsForm;
@FXML
private TextField count;
@FXML
private TextField total;
@FXML
private PieChart outputsPie;
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
@ -15,5 +29,21 @@ public class OutputsController implements Initializable {
public void setModel(OutputsForm form) { public void setModel(OutputsForm form) {
this.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());
}
} }
} }

View file

@ -13,6 +13,10 @@ public class OutputsForm extends Form {
this.transaction = transaction; this.transaction = transaction;
} }
public Transaction getTransaction() {
return transaction;
}
public Node getContents() throws IOException { public Node getContents() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("outputs.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("outputs.fxml"));
Node node = loader.load(); Node node = loader.load();

View file

@ -25,7 +25,7 @@
</Menu> </Menu>
</menus> </menus>
</MenuBar> </MenuBar>
<TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS" fx:id="tabs" /> <TabPane prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS" fx:id="tabs" />
<Label alignment="BOTTOM_LEFT" text="Label" /> <Label alignment="BOTTOM_LEFT" text="Label" />
</children> </children>

View file

@ -0,0 +1,18 @@
.version-color { -fx-fill: #986801 }
.segwit-marker-color { -fx-fill: #000000 }
.segwit-flag-color { -fx-fill: #4078f2 }
.num-inputs-color { -fx-fill: #ca1243 }
.input-hash-color { -fx-fill: #0184bc }
.input-index-color { -fx-fill: #0184bc }
.input-sigscript-length-color { -fx-fill: #0184bc }
.input-sigscript-color { -fx-fill: #0184bc }
.input-sequence-color { -fx-fill: #0184bc }
.num-outputs-color { -fx-fill: #ca1243 }
.output-value-color { -fx-fill: #50a14f }
.output-pubkeyscript-length-color { -fx-fill: #50a14f }
.output-pubkeyscript-color { -fx-fill: #50a14f }
.witnesses-color { -fx-fill: #a626a4 }
.locktime-color { -fx-fill: #986801 }

View file

@ -12,15 +12,15 @@
<?import tornadofx.control.Fieldset?> <?import tornadofx.control.Fieldset?>
<?import tornadofx.control.Field?> <?import tornadofx.control.Field?>
<GridPane fx:id="layout" hgap="10.0" prefHeight="350.0" prefWidth="600.0" vgap="10.0" alignment="CENTER" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.craigraw.sparrow.form.HeadersController" stylesheets="@../general.css"> <GridPane hgap="10.0" prefHeight="350.0" prefWidth="600.0" vgap="10.0" alignment="TOP_CENTER" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.craigraw.sparrow.form.HeadersController" stylesheets="@headers.css, @../general.css">
<padding> <padding>
<Insets bottom="25.0" left="25.0" right="25.0" top="25.0" /> <Insets bottom="25.0" left="25.0" right="25.0" top="25.0" />
</padding> </padding>
<columnConstraints> <columnConstraints>
<ColumnConstraints minWidth="290" prefWidth="290" <ColumnConstraints minWidth="295" prefWidth="295"
maxWidth="Infinity"> maxWidth="Infinity">
</ColumnConstraints> </ColumnConstraints>
<ColumnConstraints minWidth="290" prefWidth="290" <ColumnConstraints minWidth="295" prefWidth="295"
maxWidth="Infinity"> maxWidth="Infinity">
</ColumnConstraints> </ColumnConstraints>
</columnConstraints> </columnConstraints>

View file

@ -0,0 +1,31 @@
.version-color { -fx-fill: #e5e5e6 }
.segwit-marker-color { -fx-fill: #e5e5e6 }
.segwit-flag-color { -fx-fill: #e5e5e6 }
.num-inputs-color { -fx-fill: #ca1243 }
.input-hash-color { -fx-fill: #0184bc }
.input-index-color { -fx-fill: #000000 }
.input-sigscript-length-color { -fx-fill: #a626a4 }
.input-sigscript-color { -fx-fill: #50a14f }
.input-sequence-color { -fx-fill: #986801 }
.num-outputs-color { -fx-fill: #e5e5e6 }
.output-value-color { -fx-fill: #e5e5e6 }
.output-pubkeyscript-length-color { -fx-fill: #e5e5e6 }
.output-pubkeyscript-color { -fx-fill: #e5e5e6 }
.witnesses-color { -fx-fill: #e5e5e6 }
.locktime-color { -fx-fill: #e5e5e6 }
.chart-legend-item{
-fx-font-family: Courier;
}
.default-color0.chart-pie { -fx-pie-color: #ca1243 }
.default-color1.chart-pie { -fx-pie-color: #d75f00 }
.default-color2.chart-pie { -fx-pie-color: #0184bc }
.default-color3.chart-pie { -fx-pie-color: #c18401 }
.default-color4.chart-pie { -fx-pie-color: #50a14f }
.default-color5.chart-pie { -fx-pie-color: #4078f2 }
.default-color6.chart-pie { -fx-pie-color: #a626a4 }
.default-color7.chart-pie { -fx-pie-color: #986801 }

View file

@ -1,14 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?> <?import javafx.geometry.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import tornadofx.control.*?>
<AnchorPane xmlns="http://javafx.com/javafx" <?import javafx.scene.chart.PieChart?>
xmlns:fx="http://javafx.com/fxml" <GridPane alignment="TOP_CENTER" hgap="10.0" prefHeight="500.0" prefWidth="600.0" stylesheets="@inputs.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.craigraw.sparrow.form.InputsController">
fx:controller="com.craigraw.sparrow.form.InputsController" <padding>
prefHeight="400.0" prefWidth="600.0"> <Insets bottom="25.0" left="25.0" right="25.0" top="25.0" />
</padding>
<columnConstraints>
<ColumnConstraints maxWidth="Infinity" minWidth="290" prefWidth="290">
</ColumnConstraints>
<ColumnConstraints maxWidth="Infinity" minWidth="290" prefWidth="290">
</ColumnConstraints>
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<Form GridPane.columnIndex="0" GridPane.rowIndex="0">
<Fieldset inputGrow="SOMETIMES" text="Inputs">
<Field text="Count:">
<TextField fx:id="count" editable="false" minWidth="520" prefWidth="520" styleClass="copyable-label" />
</Field>
<Field text="Total:">
<TextField fx:id="total" editable="false" minWidth="520" prefWidth="520" styleClass="copyable-label" />
</Field>
</Fieldset>
</Form>
</AnchorPane> <Form GridPane.columnIndex="1" GridPane.rowIndex="0">
<Fieldset inputGrow="SOMETIMES" text="Signatures">
<Field text="Status:">
<TextField fx:id="signatures" editable="false" minWidth="520" prefWidth="520" styleClass="copyable-label" />
</Field>
</Fieldset>
</Form>
<Separator GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.columnSpan="2" styleClass="form-separator"/>
<PieChart fx:id="inputsPie" prefHeight="400" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="2" startAngle="180" labelLineLength="50" labelsVisible="false" />
</GridPane>

View file

@ -0,0 +1,31 @@
.version-color { -fx-fill: #e5e5e6 }
.segwit-marker-color { -fx-fill: #e5e5e6 }
.segwit-flag-color { -fx-fill: #e5e5e6 }
.num-inputs-color { -fx-fill: #e5e5e6 }
.input-hash-color { -fx-fill: #e5e5e6 }
.input-index-color { -fx-fill: #e5e5e6 }
.input-sigscript-length-color { -fx-fill: #e5e5e6 }
.input-sigscript-color { -fx-fill: #e5e5e6 }
.input-sequence-color { -fx-fill: #e5e5e6 }
.num-outputs-color { -fx-fill: #ca1243 }
.output-value-color { -fx-fill: #000000 }
.output-pubkeyscript-length-color { -fx-fill: #a626a4 }
.output-pubkeyscript-color { -fx-fill: #50a14f }
.witnesses-color { -fx-fill: #e5e5e6 }
.locktime-color { -fx-fill: #e5e5e6 }
.chart-legend-item{
-fx-font-family: Courier;
}
.default-color7.chart-pie { -fx-pie-color: #0184bc }
.default-color6.chart-pie { -fx-pie-color: #a626a4 }
.default-color5.chart-pie { -fx-pie-color: #d75f00 }
.default-color4.chart-pie { -fx-pie-color: #986801 }
.default-color3.chart-pie { -fx-pie-color: #ca1243 }
.default-color2.chart-pie { -fx-pie-color: #c18401 }
.default-color1.chart-pie { -fx-pie-color: #50a14f }
.default-color0.chart-pie { -fx-pie-color: #4078f2 }

View file

@ -1,14 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?> <?import javafx.geometry.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import tornadofx.control.*?>
<AnchorPane xmlns="http://javafx.com/javafx" <?import javafx.scene.chart.PieChart?>
xmlns:fx="http://javafx.com/fxml" <GridPane alignment="TOP_CENTER" hgap="10.0" prefHeight="500.0" prefWidth="600.0" stylesheets="@outputs.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.craigraw.sparrow.form.OutputsController">
fx:controller="com.craigraw.sparrow.form.OutputsController" <padding>
prefHeight="400.0" prefWidth="600.0"> <Insets bottom="25.0" left="25.0" right="25.0" top="25.0" />
</padding>
<columnConstraints>
<ColumnConstraints maxWidth="Infinity" minWidth="290" prefWidth="290">
</ColumnConstraints>
<ColumnConstraints maxWidth="Infinity" minWidth="290" prefWidth="290">
</ColumnConstraints>
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<Form GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="0">
<Fieldset inputGrow="SOMETIMES" text="Outputs">
<Field text="Count:">
<TextField fx:id="count" editable="false" minWidth="520" prefWidth="520" styleClass="copyable-label" />
</Field>
<Field text="Total:">
<TextField fx:id="total" editable="false" minWidth="520" prefWidth="520" styleClass="copyable-label" />
</Field>
</Fieldset>
</Form>
<Separator GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.columnSpan="2" styleClass="form-separator"/>
<PieChart fx:id="outputsPie" prefHeight="400" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="2" startAngle="180" labelLineLength="50" labelsVisible="false" />
</GridPane>
</AnchorPane>

View file

@ -6,7 +6,7 @@
} }
.form .fieldset:horizontal .label-container { .form .fieldset:horizontal .label-container {
-fx-pref-width: 80px; -fx-pref-width: 90px;
-fx-pref-height: 25px; -fx-pref-height: 25px;
} }

View file

@ -4,16 +4,6 @@
-fx-padding: 2; -fx-padding: 2;
} }
.version-color { -fx-fill: #986801 }
.segwit-marker-color { -fx-fill: #000000 }
.segwit-flag-color { -fx-fill: #4078f2 }
.num-inputs-color { -fx-fill: #ca1243 }
.inputs-color { -fx-fill: #0184bc }
.num-outputs-color { -fx-fill: #ca1243 }
.outputs-color { -fx-fill: #50a14f }
.witnesses-color { -fx-fill: #a626a4 }
.locktime-color { -fx-fill: #986801 }
.color-0 { -fx-fill: #ca1243 } .color-0 { -fx-fill: #ca1243 }
.color-1 { -fx-fill: #d75f00 } .color-1 { -fx-fill: #d75f00 }
.color-2 { -fx-fill: #c18401 } .color-2 { -fx-fill: #c18401 }