|
|
@ -1,16 +1,23 @@ |
|
|
|
package com.sparrowwallet.sparrow.transaction; |
|
|
|
|
|
|
|
import com.sparrowwallet.drongo.address.Address; |
|
|
|
import com.sparrowwallet.drongo.protocol.TransactionOutput; |
|
|
|
import com.sparrowwallet.drongo.protocol.NonStandardScriptException; |
|
|
|
import com.sparrowwallet.drongo.protocol.*; |
|
|
|
import javafx.collections.FXCollections; |
|
|
|
import javafx.collections.ObservableList; |
|
|
|
import javafx.geometry.Point2D; |
|
|
|
import javafx.scene.chart.PieChart; |
|
|
|
import javafx.scene.control.Label; |
|
|
|
import javafx.scene.control.Tooltip; |
|
|
|
import javafx.stage.Popup; |
|
|
|
import org.fxmisc.richtext.CodeArea; |
|
|
|
import org.fxmisc.richtext.event.MouseOverTextEvent; |
|
|
|
import org.fxmisc.richtext.model.TwoDimensional; |
|
|
|
|
|
|
|
import java.time.Duration; |
|
|
|
import java.util.List; |
|
|
|
|
|
|
|
import static org.fxmisc.richtext.model.TwoDimensional.Bias.Backward; |
|
|
|
|
|
|
|
public abstract class TransactionFormController { |
|
|
|
protected void addPieData(PieChart pie, List<TransactionOutput> outputs) { |
|
|
|
ObservableList<PieChart.Data> outputsPieData = FXCollections.observableArrayList(); |
|
|
@ -45,41 +52,83 @@ public abstract class TransactionFormController { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
protected void appendScript(CodeArea codeArea, String script) { |
|
|
|
String[] parts = script.split(" "); |
|
|
|
for (int i = 0; i < parts.length; i++) { |
|
|
|
String part = parts[i]; |
|
|
|
protected void appendScript(CodeArea codeArea, Script script) { |
|
|
|
appendScript(codeArea, script, null, null); |
|
|
|
} |
|
|
|
|
|
|
|
if(part.startsWith("(")) { |
|
|
|
codeArea.append("(", "script-nest"); |
|
|
|
part = part.substring(1); |
|
|
|
} |
|
|
|
protected void appendScript(CodeArea codeArea, Script script, Script redeemScript, Script witnessScript) { |
|
|
|
if(ScriptPattern.isP2WPKH(script)) { |
|
|
|
codeArea.append(script.getChunks().get(0).toString(), "script-opcode"); |
|
|
|
codeArea.append(" ", ""); |
|
|
|
codeArea.append("<wpkh>", "script-hash"); |
|
|
|
} else if(ScriptPattern.isP2WSH(script)) { |
|
|
|
codeArea.append(script.getChunks().get(0).toString(), "script-opcode"); |
|
|
|
codeArea.append(" ", ""); |
|
|
|
codeArea.append("<wsh>", "script-hash"); |
|
|
|
} else { |
|
|
|
int signatureCount = 1; |
|
|
|
int pubKeyCount = 1; |
|
|
|
for (int i = 0; i < script.getChunks().size(); i++) { |
|
|
|
ScriptChunk chunk = script.getChunks().get(i); |
|
|
|
if(chunk.isOpCode()) { |
|
|
|
codeArea.append(chunk.toString(), "script-opcode"); |
|
|
|
} else if(chunk.isSignature()) { |
|
|
|
codeArea.append("<signature" + signatureCount++ + ">", "script-signature"); |
|
|
|
} else if(chunk.isScript()) { |
|
|
|
Script nestedScript = new Script(chunk.getData()); |
|
|
|
if (nestedScript.equals(redeemScript)) { |
|
|
|
codeArea.append("<RedeemScript>", "script-redeem"); |
|
|
|
} else if (nestedScript.equals(witnessScript)) { |
|
|
|
codeArea.append("<WitnessScript>", "script-redeem"); |
|
|
|
} else { |
|
|
|
codeArea.append("(", "script-nest"); |
|
|
|
appendScript(codeArea, nestedScript); |
|
|
|
codeArea.append(")", "script-nest"); |
|
|
|
} |
|
|
|
} else if(chunk.isPubKey()) { |
|
|
|
codeArea.append("<pubkey" + pubKeyCount++ + ">", "script-pubkey"); |
|
|
|
} else { |
|
|
|
System.out.println(chunk.isOpCode() + " " + chunk.opcode); |
|
|
|
codeArea.append(chunk.toString(), "script-other"); |
|
|
|
} |
|
|
|
|
|
|
|
boolean appendCloseBracket = false; |
|
|
|
if(part.endsWith(")")) { |
|
|
|
appendCloseBracket = true; |
|
|
|
part = part.substring(0, part.length() - 1); |
|
|
|
if(i < script.getChunks().size() - 1) { |
|
|
|
codeArea.append(" ", ""); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if(part.startsWith("OP")) { |
|
|
|
codeArea.append(part, "script-opcode"); |
|
|
|
} else if(part.startsWith("<signature")) { |
|
|
|
codeArea.append(part, "script-signature"); |
|
|
|
} else if(part.startsWith("<pubkey")) { |
|
|
|
codeArea.append(part, "script-pubkey"); |
|
|
|
} else if(part.startsWith("<wpkh") || part.startsWith("<wsh")) { |
|
|
|
codeArea.append(part, "script-type"); |
|
|
|
} else { |
|
|
|
codeArea.append(part, "script-other"); |
|
|
|
} |
|
|
|
addScriptPopup(codeArea, script); |
|
|
|
} |
|
|
|
|
|
|
|
if(appendCloseBracket) { |
|
|
|
codeArea.append(")", "script-nest"); |
|
|
|
} |
|
|
|
protected void addScriptPopup(CodeArea area, Script script) { |
|
|
|
ScriptContextMenu contextMenu = new ScriptContextMenu(); |
|
|
|
area.setContextMenu(contextMenu); |
|
|
|
|
|
|
|
Popup popup = new Popup(); |
|
|
|
Label popupMsg = new Label(); |
|
|
|
popupMsg.setStyle("-fx-background-color: #696c77; -fx-text-fill: white; -fx-padding: 5;"); |
|
|
|
popup.getContent().add(popupMsg); |
|
|
|
|
|
|
|
if(i < parts.length - 1) { |
|
|
|
codeArea.append(" ", ""); |
|
|
|
area.setMouseOverTextDelay(Duration.ofMillis(150)); |
|
|
|
area.addEventHandler(MouseOverTextEvent.MOUSE_OVER_TEXT_BEGIN, e -> { |
|
|
|
TwoDimensional.Position position = area.getParagraph(0).getStyleSpans().offsetToPosition(e.getCharacterIndex(), Backward); |
|
|
|
if(position.getMajor() % 2 == 0) { |
|
|
|
ScriptChunk hoverChunk = script.getChunks().get(position.getMajor()/2); |
|
|
|
if(!hoverChunk.isOpCode()) { |
|
|
|
contextMenu.setHoverChunk(hoverChunk); |
|
|
|
Point2D pos = e.getScreenPosition(); |
|
|
|
popupMsg.setText(hoverChunk.toString()); |
|
|
|
popup.show(area, pos.getX(), pos.getY() + 10); |
|
|
|
} else { |
|
|
|
contextMenu.setHoverChunk(null); |
|
|
|
} |
|
|
|
} else { |
|
|
|
contextMenu.setHoverChunk(null); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
area.addEventHandler(MouseOverTextEvent.MOUSE_OVER_TEXT_END, e -> { |
|
|
|
popup.hide(); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|