Skip to content

Commit

Permalink
[Proof editor] add information about signatures status
Browse files Browse the repository at this point in the history
Print good signatures in green, bad signatures in red. Add labels indicating the status of signatures below the proof display.
  • Loading branch information
PiRK committed Aug 23, 2022
1 parent b8b9bfc commit c184354
Showing 1 changed file with 83 additions and 6 deletions.
89 changes: 83 additions & 6 deletions electroncash_gui/qt/avalanche_dialogs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import json
import struct
from dataclasses import dataclass
from typing import TYPE_CHECKING, List, Optional, Union

Expand Down Expand Up @@ -43,6 +44,40 @@ class StakeAndKey:
key: Key


class TextColor:
NEUTRAL = "black"
GOOD_SIG = "darkgreen"
BAD_SIG = "darkred"
GOOD_STAKE_SIG = "blue"
BAD_STAKE_SIG = "darkmagenta"


def colored_text(text: str, color: str) -> str:
return f"<b><font color='{color}'>{text}</font></b>"


def proof_to_rich_text(proof: Proof) -> str:
"""
Return a proof hex as a colored html string. Colors are used to indicate the
validity of stake signatures and of the master signature.
"""
p = struct.pack("<Qq", proof.sequence, proof.expiration_time)
p += proof.master_pub.serialize()
rich_text = colored_text(p.hex(), TextColor.NEUTRAL)

for ss in proof.signed_stakes:
rich_text += colored_text(ss.stake.to_hex(), TextColor.NEUTRAL)
if ss.verify_signature(proof.stake_commitment):
rich_text += colored_text(ss.sig.hex(), TextColor.GOOD_STAKE_SIG)
else:
rich_text += colored_text(ss.sig.hex(), TextColor.BAD_STAKE_SIG)

rich_text += colored_text(proof.payout_script_pubkey.hex(), TextColor.NEUTRAL)
if proof.verify_master_signature():
return rich_text + colored_text(proof.signature.hex(), TextColor.GOOD_SIG)
return rich_text + colored_text(proof.signature.hex(), TextColor.BAD_SIG)


# We generate a few deterministic private keys to pre-fill some widgets, so the user
# does not need to use an external tool or a dummy wallet to generate keys.
# TODO: don't always use the same keys, increment the index as needed (requires saving
Expand Down Expand Up @@ -230,6 +265,18 @@ def __init__(
self.proof_display.setReadOnly(True)
layout.addWidget(self.proof_display)

proof_status_layout = QtWidgets.QHBoxLayout()
layout.addLayout(proof_status_layout)

master_sig_status_header_label = QtWidgets.QLabel("Master signature: ")
proof_status_layout.addWidget(master_sig_status_header_label)
self.master_sig_status_label = QtWidgets.QLabel("")
proof_status_layout.addWidget(self.master_sig_status_label)
stake_sigs_status_header_label = QtWidgets.QLabel("Stake signatures: ")
proof_status_layout.addWidget(stake_sigs_status_header_label)
self.stake_sigs_status_label = QtWidgets.QLabel("")
proof_status_layout.addWidget(self.stake_sigs_status_label)

proof_buttons_layout = QtWidgets.QHBoxLayout()
layout.addLayout(proof_buttons_layout)

Expand Down Expand Up @@ -281,6 +328,8 @@ def init_data(self):

self.utxos_wigdet.clearContents()
self.proof_display.setText("")
self.master_sig_status_label.clear()
self.stake_sigs_status_label.clear()

def add_utxos(self, utxos: List[dict]):
"""Add UTXOs from a list of dict objects, such as stored internally by
Expand Down Expand Up @@ -440,6 +489,36 @@ def on_merge_stakes_clicked(self):
# TODO: catch possible decoding, format, hex ... errors
self.add_stakes(Proof.from_hex(proof_hex).signed_stakes)

self._on_generate_clicked()

def displayProof(self, proof: Proof):
self.proof_display.setText(proof_to_rich_text(proof))
if proof.verify_master_signature():
self.master_sig_status_label.setText(
colored_text("✅ Valid", TextColor.GOOD_SIG)
)
else:
self.master_sig_status_label.setText(
colored_text("❌ Invalid", TextColor.BAD_SIG)
)

good_count, bad_count = 0, 0
for ss in proof.signed_stakes:
if ss.verify_signature(proof.stake_commitment):
good_count += 1
else:
bad_count += 1
text = ""
if good_count:
text = colored_text(f"{good_count} good", TextColor.GOOD_STAKE_SIG)
if bad_count:
if text:
text += "; "
text += colored_text(f"{bad_count} bad<", TextColor.BAD_STAKE_SIG)
self.stake_sigs_status_label.setText(
text or colored_text("No stakes", TextColor.NEUTRAL)
)

def on_load_proof_clicked(self):
reply = QtWidgets.QMessageBox.question(
self,
Expand Down Expand Up @@ -494,9 +573,7 @@ def load_proof(self, proof: Proof):
self.master_pubkey_view.setText(proof.master_pub.to_hex())
self.add_stakes(proof.signed_stakes)

self.proof_display.setText(
f'<p style="color:black;"><b>{proof.to_hex()}</b></p>'
)
self.displayProof(proof)

def on_save_proof_clicked(self):
if not self.proof_display.toPlainText():
Expand Down Expand Up @@ -524,11 +601,11 @@ def update_master_pubkey(self, master_wif: str):
def _on_generate_clicked(self):
proof = self._build()
if proof is not None:
self.proof_display.setText(f'<p style="color:black;"><b>{proof}</b></p>')
self.displayProof(proof)
self.generate_dg_button.setEnabled(proof is not None)
self.save_proof_button.setEnabled(proof is not None)

def _build(self) -> Optional[str]:
def _build(self) -> Optional[Proof]:
master_wif = self.master_key_edit.text()
if not is_private_key(master_wif):
try:
Expand Down Expand Up @@ -583,7 +660,7 @@ def _build(self) -> Optional[str]:
else:
proofbuilder.add_signed_stake(ss)

return proofbuilder.build().to_hex()
return proofbuilder.build()

def open_dg_dialog(self):
if self.dg_dialog is None:
Expand Down

0 comments on commit c184354

Please sign in to comment.