mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
transaction view perf improvements
This commit is contained in:
parent
7cc330fde9
commit
82ca9552bd
6 changed files with 212 additions and 150 deletions
|
@ -0,0 +1,159 @@
|
|||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.protocol.*;
|
||||
import org.fxmisc.richtext.CodeArea;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class TransactionHexArea extends CodeArea {
|
||||
private static final int TRUNCATE_AT = 30000;
|
||||
|
||||
private List<TransactionSegment> previousSegmentList = new ArrayList<>();
|
||||
|
||||
public void setTransaction(Transaction transaction) {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
transaction.bitcoinSerializeToStream(baos);
|
||||
|
||||
String hex = Utils.bytesToHex(baos.toByteArray());
|
||||
if(hex.length() > TRUNCATE_AT) {
|
||||
hex = hex.substring(0, TRUNCATE_AT);
|
||||
hex += "[truncated]";
|
||||
}
|
||||
|
||||
clear();
|
||||
appendText(hex);
|
||||
previousSegmentList = new ArrayList<>();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Can't happen");
|
||||
}
|
||||
}
|
||||
|
||||
public void applyHighlighting(Transaction transaction, int selectedInputIndex, int selectedOutputIndex) {
|
||||
List<TransactionSegment> segments = getTransactionSegments(transaction, selectedInputIndex, selectedOutputIndex);
|
||||
List<TransactionSegment> changedSegments = new ArrayList<>(segments);
|
||||
changedSegments.removeAll(previousSegmentList);
|
||||
|
||||
for(TransactionSegment segment : changedSegments) {
|
||||
if(segment.start < TRUNCATE_AT) {
|
||||
setStyleClass(segment.start, Math.min(TRUNCATE_AT, segment.start + segment.length), segment.style);
|
||||
}
|
||||
}
|
||||
|
||||
previousSegmentList = segments;
|
||||
}
|
||||
|
||||
public List<TransactionSegment> getTransactionSegments(Transaction transaction, int selectedInputIndex, int selectedOutputIndex) {
|
||||
List<TransactionSegment> segments = new ArrayList<>();
|
||||
|
||||
int cursor = 0;
|
||||
|
||||
//Version
|
||||
cursor = addSegment(segments, cursor, 8, "version");
|
||||
|
||||
if(transaction.hasWitnesses()) {
|
||||
//Segwit marker
|
||||
cursor = addSegment(segments, cursor, 2, "segwit-marker");
|
||||
//Segwit flag
|
||||
cursor = addSegment(segments, cursor, 2, "segwit-flag");
|
||||
}
|
||||
|
||||
//Number of inputs
|
||||
VarInt numInputs = new VarInt(transaction.getInputs().size());
|
||||
cursor = addSegment(segments, cursor, numInputs.getSizeInBytes() * 2, "num-inputs");
|
||||
|
||||
//Inputs
|
||||
for(int i = 0; i < transaction.getInputs().size(); i++) {
|
||||
TransactionInput input = transaction.getInputs().get(i);
|
||||
cursor = addSegment(segments, cursor, 32 * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "hash"));
|
||||
cursor = addSegment(segments, cursor, 4 * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "index"));
|
||||
VarInt scriptLen = new VarInt(input.getScriptBytes().length);
|
||||
cursor = addSegment(segments, cursor, scriptLen.getSizeInBytes() * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "sigscript-length"));
|
||||
cursor = addSegment(segments, cursor, (int) scriptLen.value * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "sigscript"));
|
||||
cursor = addSegment(segments, cursor, 4 * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "sequence"));
|
||||
}
|
||||
|
||||
//Number of outputs
|
||||
VarInt numOutputs = new VarInt(transaction.getOutputs().size());
|
||||
cursor = addSegment(segments, cursor, numOutputs.getSizeInBytes() * 2, "num-outputs");
|
||||
|
||||
//Outputs
|
||||
for(int i = 0; i < transaction.getOutputs().size(); i++) {
|
||||
TransactionOutput output = transaction.getOutputs().get(i);
|
||||
cursor = addSegment(segments, cursor, 8 * 2, "output-" + getIndexedStyleClass(i, selectedOutputIndex, "value"));
|
||||
VarInt scriptLen = new VarInt(output.getScriptBytes().length);
|
||||
cursor = addSegment(segments, cursor, scriptLen.getSizeInBytes() * 2, "output-" + getIndexedStyleClass(i, selectedOutputIndex, "pubkeyscript-length"));
|
||||
cursor = addSegment(segments, cursor, (int) scriptLen.value * 2, "output-" + getIndexedStyleClass(i, selectedOutputIndex, "pubkeyscript"));
|
||||
}
|
||||
|
||||
if(transaction.hasWitnesses()) {
|
||||
for (int i = 0; i < transaction.getInputs().size(); i++) {
|
||||
TransactionInput input = transaction.getInputs().get(i);
|
||||
if (input.hasWitness()) {
|
||||
TransactionWitness witness = input.getWitness();
|
||||
VarInt witnessCount = new VarInt(witness.getPushCount());
|
||||
cursor = addSegment(segments, cursor, witnessCount.getSizeInBytes() * 2, "witness-" + getIndexedStyleClass(i, selectedInputIndex, "count"));
|
||||
for (byte[] push : witness.getPushes()) {
|
||||
VarInt witnessLen = new VarInt(push.length);
|
||||
cursor = addSegment(segments, cursor, witnessLen.getSizeInBytes() * 2, "witness-" + getIndexedStyleClass(i, selectedInputIndex, "length"));
|
||||
cursor = addSegment(segments, cursor, (int) witnessLen.value * 2, "witness-" + getIndexedStyleClass(i, selectedInputIndex, "data"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Locktime
|
||||
cursor = addSegment(segments, cursor, 8, "locktime");
|
||||
|
||||
if(cursor != getLength()) {
|
||||
//throw new IllegalStateException("Cursor position does not match transaction serialisation " + cursor + ": " + getLength());
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
private int addSegment(List<TransactionSegment> segments, int start, int length, String style) {
|
||||
segments.add(new TransactionSegment(start, length, style));
|
||||
return start + length;
|
||||
}
|
||||
|
||||
private String getIndexedStyleClass(int iterableIndex, int selectedIndex, String styleClass) {
|
||||
if (selectedIndex == -1 || selectedIndex == iterableIndex) {
|
||||
return styleClass;
|
||||
}
|
||||
|
||||
return "other";
|
||||
}
|
||||
|
||||
private static class TransactionSegment {
|
||||
public TransactionSegment(int start, int length, String style) {
|
||||
this.start = start;
|
||||
this.length = length;
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public int start;
|
||||
public int length;
|
||||
public String style;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TransactionSegment segment = (TransactionSegment) o;
|
||||
return start == segment.start &&
|
||||
length == segment.length &&
|
||||
style.equals(segment.style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(start, length, style);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -122,7 +122,9 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
version.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 2, (int)tx.getVersion()));
|
||||
version.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||
tx.setVersion(newValue);
|
||||
if(oldValue != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(tx));
|
||||
}
|
||||
});
|
||||
version.setDisable(!headersForm.isEditable());
|
||||
|
||||
|
@ -144,7 +146,9 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
locktimeFieldset.getChildren().remove(locktimeNoneField);
|
||||
locktimeFieldset.getChildren().add(locktimeNoneField);
|
||||
tx.setLocktime(0);
|
||||
if(old_toggle != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(tx));
|
||||
}
|
||||
} else if(selection.equals("block")) {
|
||||
locktimeFieldset.getChildren().remove(locktimeDateField);
|
||||
locktimeFieldset.getChildren().remove(locktimeBlockField);
|
||||
|
@ -153,8 +157,10 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
Integer block = locktimeBlock.getValue();
|
||||
if(block != null) {
|
||||
tx.setLocktime(block);
|
||||
if(old_toggle != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(tx));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
locktimeFieldset.getChildren().remove(locktimeBlockField);
|
||||
locktimeFieldset.getChildren().remove(locktimeDateField);
|
||||
|
@ -164,10 +170,12 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
if(date != null) {
|
||||
locktimeDate.setDateTimeValue(date);
|
||||
tx.setLocktime(date.toEpochSecond(OffsetDateTime.now(ZoneId.systemDefault()).getOffset()));
|
||||
if(old_toggle != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(tx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
locktimeNone.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)Transaction.MAX_BLOCK_LOCKTIME-1, 0));
|
||||
|
@ -189,13 +197,17 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
|
||||
locktimeBlock.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||
tx.setLocktime(newValue);
|
||||
if(oldValue != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(tx));
|
||||
}
|
||||
});
|
||||
|
||||
locktimeDate.setFormat(LOCKTIME_DATE_FORMAT);
|
||||
locktimeDate.dateTimeValueProperty().addListener((obs, oldValue, newValue) -> {
|
||||
tx.setLocktime(newValue.toEpochSecond(OffsetDateTime.now(ZoneId.systemDefault()).getOffset()));
|
||||
if(oldValue != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(tx));
|
||||
}
|
||||
});
|
||||
|
||||
locktimeNoneType.setDisable(!headersForm.isEditable());
|
||||
|
|
|
@ -322,12 +322,16 @@ public class InputController extends TransactionFormController implements Initia
|
|||
locktimeToggleGroup.selectToggle(locktimeAbsoluteType);
|
||||
} else if(txInput.isAbsoluteTimeLocked()) {
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_RBF_ENABLED);
|
||||
if(oldValue != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(transaction));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(txInput.isAbsoluteTimeLocked()) {
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_LOCKTIME_DISABLED - 1);
|
||||
if(oldValue != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(transaction));
|
||||
}
|
||||
} else if(txInput.isRelativeTimeLocked()) {
|
||||
locktimeToggleGroup.selectToggle(locktimeAbsoluteType);
|
||||
}
|
||||
|
@ -348,7 +352,9 @@ public class InputController extends TransactionFormController implements Initia
|
|||
locktimeAbsoluteField.setDisable(true);
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_LOCKTIME_DISABLED);
|
||||
rbf.setSelected(false);
|
||||
if(old_toggle != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(transaction));
|
||||
}
|
||||
} else if(selection.equals("absolute")) {
|
||||
locktimeFieldset.getChildren().removeAll(locktimeRelativeField, locktimeAbsoluteField);
|
||||
locktimeFieldset.getChildren().add(locktimeAbsoluteField);
|
||||
|
@ -359,14 +365,16 @@ public class InputController extends TransactionFormController implements Initia
|
|||
} else {
|
||||
txInput.setSequenceNumber(TransactionInput.SEQUENCE_LOCKTIME_DISABLED - 1);
|
||||
}
|
||||
if(old_toggle != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(transaction));
|
||||
}
|
||||
} else {
|
||||
locktimeFieldset.getChildren().removeAll(locktimeRelativeField, locktimeAbsoluteField);
|
||||
locktimeFieldset.getChildren().add(locktimeRelativeField);
|
||||
if(locktimeRelativeCombo.getValue() == null) {
|
||||
locktimeRelativeCombo.getSelectionModel().select(0);
|
||||
} else {
|
||||
setRelativeLocktime(txInput, transaction);
|
||||
setRelativeLocktime(txInput, transaction, old_toggle != null);
|
||||
}
|
||||
rbf.setSelected(true);
|
||||
}
|
||||
|
@ -380,7 +388,7 @@ public class InputController extends TransactionFormController implements Initia
|
|||
boolean blocks = locktimeRelativeCombo.getValue().equals("blocks");
|
||||
locktimeRelativeSeconds.setVisible(!blocks);
|
||||
locktimeRelativeBlocks.setVisible(blocks);
|
||||
setRelativeLocktime(txInput, transaction);
|
||||
setRelativeLocktime(txInput, transaction, old_toggle != null);
|
||||
});
|
||||
|
||||
locktimeRelativeType.setDisable(!transaction.isRelativeLocktimeAllowed());
|
||||
|
@ -400,10 +408,10 @@ public class InputController extends TransactionFormController implements Initia
|
|||
}
|
||||
|
||||
locktimeRelativeBlocks.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||
setRelativeLocktime(txInput, transaction);
|
||||
setRelativeLocktime(txInput, transaction, oldValue != null);
|
||||
});
|
||||
locktimeRelativeSeconds.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||
setRelativeLocktime(txInput, transaction);
|
||||
setRelativeLocktime(txInput, transaction, oldValue != null);
|
||||
});
|
||||
|
||||
locktimeNoneType.setDisable(!inputForm.isEditable());
|
||||
|
@ -426,7 +434,7 @@ public class InputController extends TransactionFormController implements Initia
|
|||
}
|
||||
}
|
||||
|
||||
private void setRelativeLocktime(TransactionInput txInput, Transaction transaction) {
|
||||
private void setRelativeLocktime(TransactionInput txInput, Transaction transaction, boolean changed) {
|
||||
String relativeSelection = locktimeRelativeCombo.getValue();
|
||||
if(relativeSelection.equals("blocks")) {
|
||||
Integer value = locktimeRelativeBlocks.getValue();
|
||||
|
@ -435,8 +443,10 @@ public class InputController extends TransactionFormController implements Initia
|
|||
long value = locktimeRelativeSeconds.getValue().toSeconds() / TransactionInput.RELATIVE_TIMELOCK_SECONDS_INCREMENT;
|
||||
txInput.setSequenceNumber((value & TransactionInput.RELATIVE_TIMELOCK_VALUE_MASK) | TransactionInput.RELATIVE_TIMELOCK_TYPE_FLAG);
|
||||
}
|
||||
if(changed) {
|
||||
EventManager.get().post(new TransactionChangedEvent(transaction));
|
||||
}
|
||||
}
|
||||
|
||||
public void setModel(InputForm form) {
|
||||
this.inputForm = form;
|
||||
|
|
|
@ -125,7 +125,8 @@ public class InputsController extends TransactionFormController implements Initi
|
|||
} else {
|
||||
BlockTransaction inputTx = inputTransactions.get(input.getOutpoint().getHash());
|
||||
if(inputTx == null) {
|
||||
throw new IllegalStateException("Cannot find transaction for hash " + input.getOutpoint().getHash());
|
||||
System.out.println("Cannot find transaction for hash " + input.getOutpoint().getHash() + ", still paging?");
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionOutput output = inputTx.getTransaction().getOutputs().get((int)input.getOutpoint().getIndex());
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.sparrowwallet.sparrow.transaction;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.protocol.*;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
||||
|
@ -9,6 +8,7 @@ import com.sparrowwallet.drongo.psbt.PSBTOutput;
|
|||
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
||||
import com.sparrowwallet.sparrow.AppController;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.control.TransactionHexArea;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.io.ElectrumServer;
|
||||
import javafx.application.Platform;
|
||||
|
@ -22,9 +22,7 @@ import javafx.scene.control.cell.TextFieldTreeCell;
|
|||
import javafx.scene.layout.Pane;
|
||||
import javafx.util.StringConverter;
|
||||
import org.controlsfx.control.MasterDetailPane;
|
||||
import org.fxmisc.richtext.CodeArea;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
@ -44,7 +42,7 @@ public class TransactionController implements Initializable {
|
|||
private Pane txpane;
|
||||
|
||||
@FXML
|
||||
private CodeArea txhex;
|
||||
private TransactionHexArea txhex;
|
||||
|
||||
private TransactionData txdata;
|
||||
|
||||
|
@ -54,23 +52,18 @@ public class TransactionController implements Initializable {
|
|||
private int selectedInputIndex = -1;
|
||||
private int selectedOutputIndex = -1;
|
||||
|
||||
private int highestInputIndex;
|
||||
private int highestOutputIndex;
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
EventManager.get().register(this);
|
||||
}
|
||||
|
||||
public void initializeView() {
|
||||
highestInputIndex = Math.min(getTransaction().getInputs().size(), PageForm.PAGE_SIZE);
|
||||
highestOutputIndex = Math.min(getTransaction().getOutputs().size(), PageForm.PAGE_SIZE);
|
||||
|
||||
initializeTxTree();
|
||||
transactionMasterDetail.setShowDetailNode(AppController.showTxHexProperty);
|
||||
refreshTxHex();
|
||||
fetchThisAndInputBlockTransactions(0, highestInputIndex);
|
||||
fetchOutputBlockTransactions(0, highestOutputIndex);
|
||||
txhex.setTransaction(getTransaction());
|
||||
highlightTxHex();
|
||||
fetchThisAndInputBlockTransactions(0, Math.min(getTransaction().getInputs().size(), PageForm.PAGE_SIZE));
|
||||
fetchOutputBlockTransactions(0, Math.min(getTransaction().getOutputs().size(), PageForm.PAGE_SIZE));
|
||||
|
||||
if(TransactionView.INPUT.equals(initialView) && initialIndex >= PageForm.PAGE_SIZE) {
|
||||
fetchThisAndInputBlockTransactions(initialIndex, initialIndex + 1);
|
||||
|
@ -186,15 +179,12 @@ public class TransactionController implements Initializable {
|
|||
}
|
||||
|
||||
if(pageForm.getView().equals(TransactionView.INPUT)) {
|
||||
highestInputIndex = Math.min(max, pageForm.getPageEnd());
|
||||
fetchThisAndInputBlockTransactions(pageForm.getPageStart(), Math.min(max, pageForm.getPageEnd()));
|
||||
} else {
|
||||
highestOutputIndex = Math.min(max, pageForm.getPageEnd());
|
||||
fetchOutputBlockTransactions(pageForm.getPageStart(), Math.min(max, pageForm.getPageEnd()));
|
||||
}
|
||||
|
||||
setTreeSelection(pageForm.getView(), pageForm.getPageStart());
|
||||
Platform.runLater(this::refreshTxHex);
|
||||
}
|
||||
} else {
|
||||
Node detailPane = null;
|
||||
|
@ -233,7 +223,7 @@ public class TransactionController implements Initializable {
|
|||
selectedOutputIndex = outputForm.getTransactionOutput().getIndex();
|
||||
}
|
||||
|
||||
Platform.runLater(this::refreshTxHex);
|
||||
Platform.runLater(this::highlightTxHex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -297,96 +287,8 @@ public class TransactionController implements Initializable {
|
|||
return null;
|
||||
}
|
||||
|
||||
void refreshTxHex() {
|
||||
//TODO: Handle large transactions like efd513fffbbc2977c2d3933dfaab590b5cab5841ee791b3116e531ac9f8034ed better by not replacing text
|
||||
txhex.clear();
|
||||
|
||||
String hex = "";
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
getTransaction().bitcoinSerializeToStream(baos);
|
||||
hex = Utils.bytesToHex(baos.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Can't happen");
|
||||
}
|
||||
|
||||
int cursor = 0;
|
||||
|
||||
//Version
|
||||
cursor = addText(hex, cursor, 8, "version");
|
||||
|
||||
if(getTransaction().hasWitnesses()) {
|
||||
//Segwit marker
|
||||
cursor = addText(hex, cursor, 2, "segwit-marker");
|
||||
//Segwit flag
|
||||
cursor = addText(hex, cursor, 2, "segwit-flag");
|
||||
}
|
||||
|
||||
//Number of inputs
|
||||
VarInt numInputs = new VarInt(getTransaction().getInputs().size());
|
||||
cursor = addText(hex, cursor, numInputs.getSizeInBytes() * 2, "num-inputs");
|
||||
|
||||
//Inputs
|
||||
for(int i = 0; i < getTransaction().getInputs().size(); i++) {
|
||||
if(i == highestInputIndex) {
|
||||
txhex.append("...", "");
|
||||
}
|
||||
|
||||
TransactionInput input = getTransaction().getInputs().get(i);
|
||||
boolean skip = (i >= highestInputIndex);
|
||||
cursor = addText(hex, cursor, 32 * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "hash"), skip);
|
||||
cursor = addText(hex, cursor, 4 * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "index"), skip);
|
||||
VarInt scriptLen = new VarInt(input.getScriptBytes().length);
|
||||
cursor = addText(hex, cursor, scriptLen.getSizeInBytes() * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "sigscript-length"), skip);
|
||||
cursor = addText(hex, cursor, (int) scriptLen.value * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "sigscript"), skip);
|
||||
cursor = addText(hex, cursor, 4 * 2, "input-" + getIndexedStyleClass(i, selectedInputIndex, "sequence"), skip);
|
||||
}
|
||||
|
||||
//Number of outputs
|
||||
VarInt numOutputs = new VarInt(getTransaction().getOutputs().size());
|
||||
cursor = addText(hex, cursor, numOutputs.getSizeInBytes() * 2, "num-outputs");
|
||||
|
||||
//Outputs
|
||||
for(int i = 0; i < getTransaction().getOutputs().size(); i++) {
|
||||
if(i == highestOutputIndex) {
|
||||
txhex.append("...", "");
|
||||
}
|
||||
|
||||
TransactionOutput output = getTransaction().getOutputs().get(i);
|
||||
boolean skip = (i >= highestOutputIndex);
|
||||
cursor = addText(hex, cursor, 8 * 2, "output-" + getIndexedStyleClass(i, selectedOutputIndex, "value"), skip);
|
||||
VarInt scriptLen = new VarInt(output.getScriptBytes().length);
|
||||
cursor = addText(hex, cursor, scriptLen.getSizeInBytes() * 2, "output-" + getIndexedStyleClass(i, selectedOutputIndex, "pubkeyscript-length"), skip);
|
||||
cursor = addText(hex, cursor, (int) scriptLen.value * 2, "output-" + getIndexedStyleClass(i, selectedOutputIndex, "pubkeyscript"), skip);
|
||||
}
|
||||
|
||||
if(getTransaction().hasWitnesses()) {
|
||||
for (int i = 0; i < getTransaction().getInputs().size(); i++) {
|
||||
if(i == highestInputIndex) {
|
||||
txhex.append("...", "");
|
||||
}
|
||||
|
||||
TransactionInput input = getTransaction().getInputs().get(i);
|
||||
boolean skip = (i >= highestInputIndex);
|
||||
if (input.hasWitness()) {
|
||||
TransactionWitness witness = input.getWitness();
|
||||
VarInt witnessCount = new VarInt(witness.getPushCount());
|
||||
cursor = addText(hex, cursor, witnessCount.getSizeInBytes() * 2, "witness-" + getIndexedStyleClass(i, selectedInputIndex, "count"), skip);
|
||||
for (byte[] push : witness.getPushes()) {
|
||||
VarInt witnessLen = new VarInt(push.length);
|
||||
cursor = addText(hex, cursor, witnessLen.getSizeInBytes() * 2, "witness-" + getIndexedStyleClass(i, selectedInputIndex, "length"), skip);
|
||||
cursor = addText(hex, cursor, (int) witnessLen.value * 2, "witness-" + getIndexedStyleClass(i, selectedInputIndex, "data"), skip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Locktime
|
||||
cursor = addText(hex, cursor, 8, "locktime");
|
||||
|
||||
if(cursor != hex.length()) {
|
||||
throw new IllegalStateException("Cursor position does not match transaction serialisation " + cursor + ": " + hex.length());
|
||||
}
|
||||
void highlightTxHex() {
|
||||
txhex.applyHighlighting(getTransaction(), selectedInputIndex, selectedOutputIndex);
|
||||
}
|
||||
|
||||
private void fetchThisAndInputBlockTransactions(int indexStart, int indexEnd) {
|
||||
|
@ -458,26 +360,6 @@ public class TransactionController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
private String getIndexedStyleClass(int iterableIndex, int selectedIndex, String styleClass) {
|
||||
if (selectedIndex == -1 || selectedIndex == iterableIndex) {
|
||||
return styleClass;
|
||||
}
|
||||
|
||||
return "other";
|
||||
}
|
||||
|
||||
private int addText(String hex, int cursor, int length, String styleClass) {
|
||||
return addText(hex, cursor, length, styleClass, false);
|
||||
}
|
||||
|
||||
private int addText(String hex, int cursor, int length, String styleClass, boolean skip) {
|
||||
if(!skip) {
|
||||
txhex.append(hex.substring(cursor, cursor + length), styleClass);
|
||||
}
|
||||
|
||||
return cursor + length;
|
||||
}
|
||||
|
||||
public Transaction getTransaction() {
|
||||
return txdata.getTransaction();
|
||||
}
|
||||
|
@ -546,15 +428,12 @@ public class TransactionController implements Initializable {
|
|||
}
|
||||
|
||||
if(event.getInitialView().equals(TransactionView.INPUT)) {
|
||||
highestInputIndex = Math.min(max, event.getInitialIndex());
|
||||
fetchThisAndInputBlockTransactions(event.getInitialIndex(), event.getInitialIndex() + 1);
|
||||
} else {
|
||||
highestOutputIndex = Math.min(max, event.getInitialIndex());
|
||||
fetchOutputBlockTransactions(event.getInitialIndex(), event.getInitialIndex() + 1);
|
||||
}
|
||||
|
||||
setTreeSelection(event.getInitialView(), event.getInitialIndex());
|
||||
Platform.runLater(this::refreshTxHex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -562,7 +441,8 @@ public class TransactionController implements Initializable {
|
|||
@Subscribe
|
||||
public void transactionChanged(TransactionChangedEvent event) {
|
||||
if(event.getTransaction().equals(getTransaction())) {
|
||||
refreshTxHex();
|
||||
txhex.setTransaction(getTransaction());
|
||||
highlightTxHex();
|
||||
txtree.refresh();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import org.fxmisc.richtext.*?>
|
||||
<?import org.fxmisc.flowless.VirtualizedScrollPane?>
|
||||
<?import org.controlsfx.control.MasterDetailPane?>
|
||||
<?import com.sparrowwallet.sparrow.control.TransactionHexArea?>
|
||||
|
||||
<SplitPane fx:id="tabContent" dividerPositions="0.82" orientation="VERTICAL" stylesheets="@transaction.css" VBox.vgrow="ALWAYS" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.transaction.TransactionController">
|
||||
<items>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<detailNode>
|
||||
<VirtualizedScrollPane>
|
||||
<content>
|
||||
<CodeArea fx:id="txhex" editable="false" wrapText="true" />
|
||||
<TransactionHexArea fx:id="txhex" editable="false" wrapText="true" />
|
||||
</content>
|
||||
</VirtualizedScrollPane>
|
||||
</detailNode>
|
||||
|
|
Loading…
Reference in a new issue