Craig Raw
5 years ago
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); |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue