show signing keystores in transaction blockchain form for spends from multisig wallets
What changed, and why it matters
This commit adds a small UI label that shows which signing keystores have already signed a multisig transaction when viewing it in the transaction details form. It is a user-interface improvement, not a security fix or vulnerability.
No security action needed. This is a normal feature/UI enhancement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces a new ‘Signed by:’ field in the transaction blockchain form. It is visible only for multisig wallets where the required number of signatures is less than the total number of keystores. The code retrieves signed keystores from the wallet for the transaction and displays their labels, separated by commas. It also adds a CSS tweak for block status text color and wires the field’s visibility in the controller.
Changed components
HeadersController.javaheaders.fxmldarktheme.cssInspect captured patch +29 / −0
diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java
index 0e87444..f2bb407 100644
--- a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java
+++ b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java
@@ -4,6 +4,7 @@ import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.SecureString;
import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.address.Address;
+import com.sparrowwallet.drongo.policy.PolicyType;
import com.sparrowwallet.drongo.protocol.*;
import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.psbt.PSBTInput;
@@ -181,6 +182,12 @@ public class HeadersController extends TransactionFormController implements Init
@FXML
private CopyableLabel blockTimestamp;
+ @FXML
+ private Field signedByField;
+
+ @FXML
+ private CopyableLabel signedBy;
+
@FXML
private Form blockchainSpacerForm;
@@ -811,6 +818,7 @@ public class HeadersController extends TransactionFormController implements Init
blockHeightField.managedProperty().bind(blockHeightField.visibleProperty());
blockTimestampField.managedProperty().bind(blockTimestampField.visibleProperty());
+ signedByField.managedProperty().bind(signedByField.visibleProperty());
if(blockTransaction.getHeight() > 0) {
blockHeightField.setVisible(true);
@@ -828,6 +836,19 @@ public class HeadersController extends TransactionFormController implements Init
} else {
blockTimestampField.setVisible(false);
}
+
+ if(headersForm.getWalletTransaction() != null && headersForm.getWalletTransaction().getWallet() != null
+ && headersForm.getWalletTransaction().getWallet().getPolicyType() == PolicyType.MULTI
+ && headersForm.getWalletTransaction().getWallet().getDefaultPolicy().getNumSignaturesRequired() < headersForm.getWalletTransaction().getWallet().getKeystores().size()) {
+ signedByField.setVisible(true);
+ Wallet wallet = headersForm.getWalletTransaction().getWallet();
+ Map<TransactionInput, Map<TransactionSignature, Keystore>> signedKeystores = wallet.getSignedKeystores(blockTransaction.getTransaction());
+ StringJoiner joiner = new StringJoiner(", ");
+ signedKeystores.values().stream().flatMap(map -> map.values().stream()).distinct().forEach(keystore -> joiner.add(keystore.getLabel()));
+ signedBy.setText(joiner.toString());
+ } else {
+ signedByField.setVisible(false);
+ }
}
private void initializeSignButton(Wallet signingWallet) {
@@ -1478,6 +1499,7 @@ public class HeadersController extends TransactionFormController implements Init
errorGlyph.getStyleClass().add("failure");
blockHeightField.setVisible(false);
blockTimestampField.setVisible(false);
+ signedByField.setVisible(false);
}
}
diff --git a/src/main/resources/com/sparrowwallet/sparrow/darktheme.css b/src/main/resources/com/sparrowwallet/sparrow/darktheme.css
index bc8b8fa..c22f165 100644
--- a/src/main/resources/com/sparrowwallet/sparrow/darktheme.css
+++ b/src/main/resources/com/sparrowwallet/sparrow/darktheme.css
@@ -329,6 +329,10 @@ HorizontalHeaderColumn > TableColumnHeader.column-header.table-column{
-fx-stroke: #696c77;
}
+#blockchainForm #blockStatus {
+ -fx-text-fill: white;
+}
+
.root .progress-indicator.progress-timer.warn > .determinate-indicator > .indicator {
-fx-background-color: -fx-box-border, radial-gradient(center 50% 50%, radius 50%, #e06c75 70%, derive(-fx-control-inner-background, -9%) 100%);
}
diff --git a/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml b/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml
index 7d046a5..5a56f72 100644
--- a/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml
+++ b/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml
@@ -196,6 +196,9 @@
<Field fx:id="blockTimestampField" text="Timestamp:">
<CopyableLabel fx:id="blockTimestamp" />
</Field>
+ <Field fx:id="signedByField" text="Signed by:">
+ <CopyableLabel fx:id="signedBy" />
+ </Field>
</Fieldset>
</DynamicForm>
<Form fx:id="blockchainSpacerForm" GridPane.columnIndex="1" GridPane.rowIndex="0" visible="false">
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.