mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
finish perf improvements to tx viewer
This commit is contained in:
parent
82ca9552bd
commit
9cd8a9b9ee
10 changed files with 125 additions and 28 deletions
|
@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.control;
|
|||
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.protocol.*;
|
||||
import javafx.application.Platform;
|
||||
import org.fxmisc.richtext.CodeArea;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
@ -12,6 +13,7 @@ import java.util.Objects;
|
|||
|
||||
public class TransactionHexArea extends CodeArea {
|
||||
private static final int TRUNCATE_AT = 30000;
|
||||
private static final int SEGMENTS_INTERVAL = 250;
|
||||
|
||||
private List<TransactionSegment> previousSegmentList = new ArrayList<>();
|
||||
|
||||
|
@ -38,14 +40,24 @@ public class TransactionHexArea extends CodeArea {
|
|||
List<TransactionSegment> segments = getTransactionSegments(transaction, selectedInputIndex, selectedOutputIndex);
|
||||
List<TransactionSegment> changedSegments = new ArrayList<>(segments);
|
||||
changedSegments.removeAll(previousSegmentList);
|
||||
applyHighlighting(0, changedSegments);
|
||||
previousSegmentList = segments;
|
||||
}
|
||||
|
||||
for(TransactionSegment segment : changedSegments) {
|
||||
private void applyHighlighting(int start, List<TransactionSegment> segments) {
|
||||
int end = Math.min(segments.size(), start + SEGMENTS_INTERVAL);
|
||||
for(int i = start; i < end; i++) {
|
||||
TransactionSegment segment = segments.get(i);
|
||||
if(segment.start < TRUNCATE_AT) {
|
||||
setStyleClass(segment.start, Math.min(TRUNCATE_AT, segment.start + segment.length), segment.style);
|
||||
}
|
||||
}
|
||||
|
||||
previousSegmentList = segments;
|
||||
if(end < segments.size()) {
|
||||
Platform.runLater(() -> {
|
||||
applyHighlighting(end, segments);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public List<TransactionSegment> getTransactionSegments(Transaction transaction, int selectedInputIndex, int selectedOutputIndex) {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
|
||||
public class TransactionLocktimeChangedEvent extends TransactionChangedEvent {
|
||||
public TransactionLocktimeChangedEvent(Transaction transaction) {
|
||||
super(transaction);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ public class ViewTransactionEvent {
|
|||
public final Integer initialIndex;
|
||||
|
||||
public ViewTransactionEvent(BlockTransaction blockTransaction) {
|
||||
this(blockTransaction, null, null);
|
||||
this(blockTransaction, TransactionView.HEADERS, null);
|
||||
}
|
||||
|
||||
public ViewTransactionEvent(BlockTransaction blockTransaction, HashIndexEntry hashIndexEntry) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.sparrowwallet.sparrow.control.IdLabel;
|
|||
import com.sparrowwallet.sparrow.control.CopyableLabel;
|
||||
import com.sparrowwallet.sparrow.event.BlockTransactionFetchedEvent;
|
||||
import com.sparrowwallet.sparrow.event.TransactionChangedEvent;
|
||||
import com.sparrowwallet.sparrow.event.TransactionLocktimeChangedEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.*;
|
||||
|
@ -199,6 +200,7 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
tx.setLocktime(newValue);
|
||||
if(oldValue != null) {
|
||||
EventManager.get().post(new TransactionChangedEvent(tx));
|
||||
EventManager.get().post(new TransactionLocktimeChangedEvent(tx));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -249,9 +251,13 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
|
||||
BlockTransaction inputTx = inputTransactions.get(input.getOutpoint().getHash());
|
||||
if(inputTx == null) {
|
||||
System.out.println("Cannot find transaction for hash " + input.getOutpoint().getHash() + ", still paging?");
|
||||
if(headersForm.allInputsFetched()) {
|
||||
throw new IllegalStateException("Cannot find transaction for hash " + input.getOutpoint().getHash());
|
||||
} else {
|
||||
//Still paging
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
feeAmt += inputTx.getTransaction().getOutputs().get((int)input.getOutpoint().getIndex()).getValue();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.sparrowwallet.sparrow.EventManager;
|
|||
import com.sparrowwallet.sparrow.control.*;
|
||||
import com.sparrowwallet.sparrow.event.BlockTransactionFetchedEvent;
|
||||
import com.sparrowwallet.sparrow.event.TransactionChangedEvent;
|
||||
import com.sparrowwallet.sparrow.event.TransactionLocktimeChangedEvent;
|
||||
import com.sparrowwallet.sparrow.event.ViewTransactionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
|
@ -192,9 +193,13 @@ public class InputController extends TransactionFormController implements Initia
|
|||
if(!txInput.isCoinBase()) {
|
||||
BlockTransaction blockTransaction = inputTransactions.get(txInput.getOutpoint().getHash());
|
||||
if(blockTransaction == null) {
|
||||
System.out.println("Could not retrieve block transaction for input #" + inputForm.getIndex());
|
||||
if(inputForm.getIndex() < inputForm.getMaxInputFetched()) {
|
||||
throw new IllegalStateException("Could not retrieve block transaction for input #" + inputForm.getIndex());
|
||||
} else {
|
||||
//Still paging
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TransactionOutput output = blockTransaction.getTransaction().getOutputs().get((int)txInput.getOutpoint().getIndex());
|
||||
updateSpends(output);
|
||||
|
@ -486,4 +491,11 @@ public class InputController extends TransactionFormController implements Initia
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void transctionLocktimeChanged(TransactionLocktimeChangedEvent event) {
|
||||
if(event.getTransaction().equals(inputForm.getTransaction())) {
|
||||
locktimeAbsolute.setText(Long.toString(event.getTransaction().getLocktime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,9 +125,13 @@ public class InputsController extends TransactionFormController implements Initi
|
|||
} else {
|
||||
BlockTransaction inputTx = inputTransactions.get(input.getOutpoint().getHash());
|
||||
if(inputTx == null) {
|
||||
System.out.println("Cannot find transaction for hash " + input.getOutpoint().getHash() + ", still paging?");
|
||||
if(inputsForm.allInputsFetched()) {
|
||||
throw new IllegalStateException("Cannot find transaction for hash " + input.getOutpoint().getHash());
|
||||
} else {
|
||||
//Still paging
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TransactionOutput output = inputTx.getTransaction().getOutputs().get((int)input.getOutpoint().getIndex());
|
||||
outputs.add(output);
|
||||
|
|
|
@ -90,7 +90,7 @@ public class TransactionController implements Initializable {
|
|||
inputsItem.getChildren().add(inputItem);
|
||||
inputPagingAdded = false;
|
||||
} else if(!inputPagingAdded) {
|
||||
PageForm pageForm = new PageForm(TransactionView.INPUT, i, i + PageForm.PAGE_SIZE);
|
||||
PageForm pageForm = new PageForm(TransactionView.INPUT, i, Math.min(getTransaction().getInputs().size(), i + PageForm.PAGE_SIZE));
|
||||
TreeItem<TransactionForm> pageItem = new TreeItem<>(pageForm);
|
||||
inputsItem.getChildren().add(pageItem);
|
||||
inputPagingAdded = true;
|
||||
|
@ -110,7 +110,7 @@ public class TransactionController implements Initializable {
|
|||
outputsItem.getChildren().add(outputItem);
|
||||
outputPagingAdded = false;
|
||||
} else if(!outputPagingAdded) {
|
||||
PageForm pageForm = new PageForm(TransactionView.OUTPUT, i, i + PageForm.PAGE_SIZE);
|
||||
PageForm pageForm = new PageForm(TransactionView.OUTPUT, i, Math.min(getTransaction().getOutputs().size(), i + PageForm.PAGE_SIZE));
|
||||
TreeItem<TransactionForm> pageItem = new TreeItem<>(pageForm);
|
||||
outputsItem.getChildren().add(pageItem);
|
||||
outputPagingAdded = true;
|
||||
|
@ -169,7 +169,7 @@ public class TransactionController implements Initializable {
|
|||
}
|
||||
|
||||
if(pageForm.getPageEnd() < max) {
|
||||
PageForm nextPageForm = new PageForm(pageForm.getView(), pageForm.getPageStart() + PageForm.PAGE_SIZE, pageForm.getPageEnd() + PageForm.PAGE_SIZE);
|
||||
PageForm nextPageForm = new PageForm(pageForm.getView(), pageForm.getPageEnd(), Math.min(max, pageForm.getPageEnd() + PageForm.PAGE_SIZE));
|
||||
TreeItem<TransactionForm> nextPageItem = new TreeItem<>(nextPageForm);
|
||||
if(pageForm.getPageEnd() >= parentItem.getChildren().size()) {
|
||||
parentItem.getChildren().add(nextPageItem);
|
||||
|
@ -397,16 +397,16 @@ public class TransactionController implements Initializable {
|
|||
setTreeSelection(event.getInitialView(), event.getInitialIndex());
|
||||
} else if(event.getInitialView().equals(TransactionView.INPUT) || event.getInitialView().equals(TransactionView.OUTPUT)) {
|
||||
TreeItem<TransactionForm> parentItem = getTreeItem(event.getInitialView().equals(TransactionView.INPUT) ? TransactionView.INPUTS : TransactionView.OUTPUTS, null);
|
||||
|
||||
TreeItem<TransactionForm> newItem = event.getInitialView().equals(TransactionView.INPUT) ? createInputTreeItem(event.getInitialIndex()) : createOutputTreeItem(event.getInitialIndex());
|
||||
PageForm nextPageForm = new PageForm(event.getInitialView(), event.getInitialIndex() + 1, event.getInitialIndex() + 1 + PageForm.PAGE_SIZE);
|
||||
|
||||
int max = event.getInitialView().equals(TransactionView.INPUT) ? getTransaction().getInputs().size() : getTransaction().getOutputs().size();
|
||||
PageForm nextPageForm = new PageForm(event.getInitialView(), event.getInitialIndex() + 1, Math.min(max, event.getInitialIndex() + 1 + PageForm.PAGE_SIZE));
|
||||
TreeItem<TransactionForm> nextPageItem = new TreeItem<>(nextPageForm);
|
||||
|
||||
if(existingItem != null) {
|
||||
parentItem.getChildren().remove(existingItem);
|
||||
}
|
||||
|
||||
int max = event.getInitialView().equals(TransactionView.INPUT) ? getTransaction().getInputs().size() : getTransaction().getOutputs().size();
|
||||
int highestIndex = ((IndexedTransactionForm)parentItem.getChildren().get(parentItem.getChildren().size() - 1).getValue()).getIndex();
|
||||
if(event.getInitialIndex() < highestIndex) {
|
||||
for(int i = 0; i < parentItem.getChildren().size(); i++) {
|
||||
|
@ -466,6 +466,7 @@ public class TransactionController implements Initializable {
|
|||
} else {
|
||||
txdata.getInputTransactions().putAll(event.getInputTransactions());
|
||||
}
|
||||
txdata.updateInputsFetchedRange(event.getPageStart(), event.getPageEnd());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -482,6 +483,7 @@ public class TransactionController implements Initializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
txdata.updateOutputsFetchedRange(event.getPageStart(), event.getPageEnd());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,11 @@ public class TransactionData {
|
|||
private Map<Sha256Hash, BlockTransaction> inputTransactions;
|
||||
private List<BlockTransaction> outputTransactions;
|
||||
|
||||
private int minInputFetched;
|
||||
private int maxInputFetched;
|
||||
private int minOutputFetched;
|
||||
private int maxOutputFetched;
|
||||
|
||||
public TransactionData(PSBT psbt) {
|
||||
this.transaction = psbt.getTransaction();
|
||||
this.psbt = psbt;
|
||||
|
@ -53,6 +58,28 @@ public class TransactionData {
|
|||
this.inputTransactions = inputTransactions;
|
||||
}
|
||||
|
||||
public void updateInputsFetchedRange(int pageStart, int pageEnd) {
|
||||
if(pageStart < 0 || pageEnd > transaction.getInputs().size()) {
|
||||
throw new IllegalStateException("Paging outside transaction inputs range");
|
||||
}
|
||||
|
||||
if(pageStart != maxInputFetched) {
|
||||
//non contiguous range, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
this.minInputFetched = Math.min(minInputFetched, pageStart);
|
||||
this.maxInputFetched = Math.max(maxInputFetched, pageEnd);
|
||||
}
|
||||
|
||||
public int getMaxInputFetched() {
|
||||
return maxInputFetched;
|
||||
}
|
||||
|
||||
public boolean allInputsFetched() {
|
||||
return minInputFetched == 0 && maxInputFetched == transaction.getOutputs().size();
|
||||
}
|
||||
|
||||
public List<BlockTransaction> getOutputTransactions() {
|
||||
return outputTransactions;
|
||||
}
|
||||
|
@ -60,4 +87,22 @@ public class TransactionData {
|
|||
public void setOutputTransactions(List<BlockTransaction> outputTransactions) {
|
||||
this.outputTransactions = outputTransactions;
|
||||
}
|
||||
|
||||
public void updateOutputsFetchedRange(int pageStart, int pageEnd) {
|
||||
if(pageStart < 0 || pageEnd > transaction.getOutputs().size()) {
|
||||
throw new IllegalStateException("Paging outside transaction outputs range");
|
||||
}
|
||||
|
||||
if(pageStart != maxOutputFetched) {
|
||||
//non contiguous range, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
this.minOutputFetched = Math.min(minOutputFetched, pageStart);
|
||||
this.maxOutputFetched = Math.max(maxOutputFetched, pageEnd);
|
||||
}
|
||||
|
||||
public boolean allOutputsFetched() {
|
||||
return minOutputFetched == 0 && maxOutputFetched == transaction.getOutputs().size();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,24 +29,24 @@ public abstract class TransactionForm {
|
|||
return txdata.getBlockTransaction();
|
||||
}
|
||||
|
||||
public void setBlockTransaction(BlockTransaction blockTransaction) {
|
||||
txdata.setBlockTransaction(blockTransaction);
|
||||
}
|
||||
|
||||
public Map<Sha256Hash, BlockTransaction> getInputTransactions() {
|
||||
return txdata.getInputTransactions();
|
||||
}
|
||||
|
||||
public void setInputTransactions(Map<Sha256Hash, BlockTransaction> inputTransactions) {
|
||||
txdata.setInputTransactions(inputTransactions);
|
||||
public int getMaxInputFetched() {
|
||||
return txdata.getMaxInputFetched();
|
||||
}
|
||||
|
||||
public boolean allInputsFetched() {
|
||||
return txdata.allInputsFetched();
|
||||
}
|
||||
|
||||
public List<BlockTransaction> getOutputTransactions() {
|
||||
return txdata.getOutputTransactions();
|
||||
}
|
||||
|
||||
public void setOutputTransactions(List<BlockTransaction> outputTransactions) {
|
||||
txdata.setOutputTransactions(outputTransactions);
|
||||
public boolean allOutputsFetched() {
|
||||
return txdata.allOutputsFetched();
|
||||
}
|
||||
|
||||
public boolean isEditable() {
|
||||
|
|
|
@ -16,18 +16,21 @@ import javafx.scene.input.ClipboardContent;
|
|||
import java.util.List;
|
||||
|
||||
public abstract class TransactionFormController extends BaseController {
|
||||
private static final int MAX_PIE_SEGMENTS = 200;
|
||||
|
||||
protected void addPieData(PieChart pie, List<TransactionOutput> outputs) {
|
||||
ObservableList<PieChart.Data> outputsPieData = FXCollections.observableArrayList();
|
||||
|
||||
long totalAmt = 0;
|
||||
for(TransactionOutput output : outputs) {
|
||||
String name = "Unknown";
|
||||
for(int i = 0; i < outputs.size(); i++) {
|
||||
TransactionOutput output = outputs.get(i);
|
||||
String name = "#" + i;
|
||||
try {
|
||||
Address[] addresses = output.getScript().getToAddresses();
|
||||
if(addresses.length == 1) {
|
||||
name = addresses[0].getAddress();
|
||||
name = name + " " + addresses[0].getAddress();
|
||||
} else {
|
||||
name = "[" + addresses[0].getAddress() + ",...]";
|
||||
name = name + " [" + addresses[0].getAddress() + ",...]";
|
||||
}
|
||||
} catch(NonStandardScriptException e) {
|
||||
//ignore
|
||||
|
@ -46,12 +49,16 @@ public abstract class TransactionFormController extends BaseController {
|
|||
}
|
||||
|
||||
private void addPieData(PieChart pie, ObservableList<PieChart.Data> outputsPieData) {
|
||||
if(outputsPieData.size() > MAX_PIE_SEGMENTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
pie.setData(outputsPieData);
|
||||
final double totalSum = outputsPieData.stream().map(PieChart.Data::getPieValue).mapToDouble(Double::doubleValue).sum();
|
||||
pie.getData().forEach(data -> {
|
||||
Tooltip tooltip = new Tooltip();
|
||||
double percent = 100.0 * (data.getPieValue() / totalSum);
|
||||
tooltip.setText(String.format("%.1f", percent) + "%");
|
||||
tooltip.setText(data.getName() + " " + String.format("%.1f", percent) + "%");
|
||||
Tooltip.install(data.getNode(), tooltip);
|
||||
data.pieValueProperty().addListener((observable, oldValue, newValue) -> tooltip.setText(newValue + "%"));
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue