silentpayments: API docs and internal comment followups
What changed, and why it matters
This commit only updates documentation comments and an example error message in the silent payments module. It does not change any executable code, cryptographic calculations, or security behavior. The changes clarify when the output-creation function can fail and add internal explanatory notes for future maintainers.
No security action required; this is a documentation-only change. Reviewers may verify that the revised API comments accurately reflect the implementation's failure modes.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch is a non-functional follow-up to PR #1765. It revises API documentation in include/secp256k1_silentpayments.h to more accurately describe failure conditions for secp256k1_silentpayments_create_outputs, updates an example printf string in examples/silentpayments.c, and adds two explanatory comments plus a VERIFY_CHECK in src/modules/silentpayments/main_impl.h. The VERIFY_CHECK documents a precondition already guaranteed by existing logic and does not alter control flow.
Changed components
include/secp256k1_silentpayments.hexamples/silentpayments.csrc/modules/silentpayments/main_impl.hInspect captured patch +24 / −22
diff --git a/examples/silentpayments.c b/examples/silentpayments.c
index c282a3b..f1a5de3 100644
--- a/examples/silentpayments.c
+++ b/examples/silentpayments.c
@@ -295,7 +295,7 @@ int main(void) {
NULL, 0
);
if (!ret) {
- printf("Something went wrong, a recipient provided an invalid address.\n");
+ printf("Something went wrong, group limit exceeded or input secret keys sum to zero.\n");
return EXIT_FAILURE;
}
printf("Alice created the following outputs for Bob and Carol:\n");
diff --git a/include/secp256k1_silentpayments.h b/include/secp256k1_silentpayments.h
index 256e95a..5458145 100644
--- a/include/secp256k1_silentpayments.h
+++ b/include/secp256k1_silentpayments.h
@@ -75,15 +75,14 @@ typedef struct secp256k1_silentpayments_recipient {
* unfindable by the recipient.
*
* Returns: 1 if creation of outputs was successful.
- * 0 on failure. This is expected only with an adversarially chosen
- * recipient spend key. Specifically, failure occurs when:
- * - Input secret keys sum to 0
- * (negligible probability if at least one of the input secret
- * keys is uniformly random and independent of all other keys)
- * - A hash output is not a valid scalar (negligible probability
- * per hash evaluation)
- * - Any group (i.e. recipients sharing the same scan public key) exceeds
- * the protocol limit SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT
+ * 0 on failure, i.e., when one of the following occurs:
+ * - The size of any group (i.e. recipients sharing the same scan public key)
+ * exceeds the protocol limit SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT.
+ * - The sum of all input secret keys is 0.
+ * (This occurs only with negligible probability if at least one of the
+ * input secret keys is uniformly random and independent of all other keys.)
+ * - An invalid output public key is created. (This can only happen for an
+ * adversarially chosen recipient spend public key.)
*
* Args: ctx: pointer to a context object
* (not secp256k1_context_static).
@@ -97,16 +96,14 @@ typedef struct secp256k1_silentpayments_recipient {
* In: recipients: pointer to an array of pointers to Silent Payments
* recipients, where each recipient is a scan public
* key, a spend public key, and an index indicating
- * its position in the original ordering. The
- * recipient array will be grouped by scan public key
- * in place (as specified in BIP0352), but generated
- * outputs are saved in the `generated_outputs` array
- * to match the original ordering (using the index
- * field). This ensures the caller is able to match
- * the generated outputs to the correct Silent
- * Payments addresses. The same recipient can be
- * passed multiple times to create multiple outputs
- * for the same recipient.
+ * its position in the original ordering. This function
+ * may reorder the pointers to the recipient objects
+ * within the array, i.e., after the call (including on
+ * failure), the index fields of the recipient objects
+ * may no longer correspond to the positions in the
+ * array. Multiple recipient objects with the same scan
+ * public key and/or same spend public key can be passed
+ * if they carry different indices.
* n_recipients: the size of the recipients array.
* outpoint_smallest36: serialized (36-byte) smallest outpoint
* (lexicographically) from the transaction inputs
diff --git a/src/modules/silentpayments/main_impl.h b/src/modules/silentpayments/main_impl.h
index d2a910e..58c760a 100644
--- a/src/modules/silentpayments/main_impl.h
+++ b/src/modules/silentpayments/main_impl.h
@@ -588,8 +588,11 @@ static int secp256k1_silentpayments_check_label_batch(
*label_tweak = NULL;
secp256k1_ge_set_all_gej_var(label_candidates_ge, label_candidates_gej, 2 * n_batch);
for (i = 0; i < 2 * n_batch; i++) {
- /* Serialize only non-infinity points because candidates are collected only when
- * tx_output != unlabeled_output_xonly. */
+ /* Note: serialize will only fail if a label candidate is the point at infinity, but we know
+ * this cannot happen since we only collect candidates if tx_output != unlabeled_output. Thus,
+ * we know that label_candidate = tx_output - unlabeled_output cannot be the point at infinity.
+ */
+ VERIFY_CHECK(!secp256k1_ge_is_infinity(&label_candidates_ge[i]));
secp256k1_eckey_pubkey_serialize33(&label_candidates_ge[i], label33);
*label_tweak = label_lookup(label33, label_context);
if (*label_tweak != NULL) {
@@ -648,6 +651,8 @@ int secp256k1_silentpayments_recipient_scan_outputs(
}
secp256k1_ge_from_bytes(&prevouts_pubkey_sum_ge, &prevouts_summary->data[5]);
combined = (int)prevouts_summary->data[4];
+ /* Note that the "combined" flag can currently only be 0, as we only have support for full nodes, i.e.,
+ * the following branch is always taken. "combined" can also be 1 once we add light client support. */
if (!combined) {
secp256k1_scalar input_hash_scalar;
secp256k1_scalar_set_b32(&input_hash_scalar, &prevouts_summary->data[5 + 64], NULL);
Why this scored 15/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.