Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add silentpayments (BIP352) module #1471

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 49 additions & 0 deletions include/secp256k1_silentpayments.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,55 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_sender_c
unsigned int k
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

typedef struct {
secp256k1_pubkey label;
secp256k1_pubkey label_negated;
} secp256k1_silentpayments_label_data;

/** Scan for Silent Payment transaction output (for receiver).
*
* Given a shared_secret, a recipient's spend public key B_spend,
* an output counter k, and a scanned tx's output x-only public key tx_output,
* calculate the corresponding scanning data:
*
* t_k = hash(shared_secret || ser_32(k))
* P_output = B_spend + t_k * G [not returned]
Comment on lines +248 to +255
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the course of writing benchmarks, I just noticed that this scanning API is flawed from a performance perspective, as we currently recalculate $t_k$ and $P_{output}$ on every scanned output instead of only after the output counter $k$ is increased (that is, if a match was found). It seems that this routine should be split up into one calculating $P_{output}$ (only to be called once per $(B_{spend}, k)$ pair) and another one for calculating label candidates, given $P_{output}$ and $tx_{output}$ (to be called on each scanned output, if there's no match). So the direct_match boolean out-parameter would go away (it was a bit weird anyway), as the caller would do the comparison between $P_{output}$ and $tx_{output}$.

* if P_output == tx_output
* direct_match = 1
* else
* label1 = tx_output - P_output
* label2 = -tx_output - P_output
* direct_match = 0
*
* The resulting data is needed for the receiver to efficiently scan for labels
* in silent payments eligible outputs.
*
* Returns: 1 if output scanning was successful. 0 if an error occured.
* Args: ctx: pointer to a context object
* Out: direct_match: pointer to the resulting boolean indicating whether
* the calculated output pubkey matches the scanned one
* t_k: pointer to the resulting tweak t_k
* label_data: pointer to the resulting label structure, containing the
* two label candidates, only set if direct_match == 0
* (can be NULL if the data is not needed)
* In: shared_secret33: shared secret, derived from either sender's
* or receiver's perspective with routines from above
* receiver_spend_pubkey: pointer to the receiver's spend pubkey
* k: output counter (usually set to 0, should be increased for
* every additional output to the same recipient)
* tx_output: pointer to the scanned tx's output x-only public key
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_receiver_scan_output(
const secp256k1_context *ctx,
int *direct_match,
unsigned char *t_k,
secp256k1_silentpayments_label_data *label_data,
const unsigned char *shared_secret33,
const secp256k1_pubkey *receiver_spend_pubkey,
unsigned int k,
const secp256k1_xonly_pubkey *tx_output
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(8);

#ifdef __cplusplus
}
#endif
Expand Down
59 changes: 59 additions & 0 deletions src/modules/silentpayments/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,4 +325,63 @@ int secp256k1_silentpayments_sender_create_output_pubkey(const secp256k1_context
return 1;
}

int secp256k1_silentpayments_receiver_scan_output(const secp256k1_context *ctx, int *direct_match, unsigned char *t_k, secp256k1_silentpayments_label_data *label_data, const unsigned char *shared_secret33, const secp256k1_pubkey *receiver_spend_pubkey, unsigned int k, const secp256k1_xonly_pubkey *tx_output) {
secp256k1_scalar t_k_scalar;
secp256k1_ge P_output_ge;
secp256k1_xonly_pubkey P_output_xonly;

/* Sanity check inputs */
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(direct_match != NULL);
ARG_CHECK(t_k != NULL);
ARG_CHECK(shared_secret33 != NULL);
ARG_CHECK(receiver_spend_pubkey != NULL);
ARG_CHECK(tx_output != NULL);

/* Calculate t_k = hash(shared_secret || ser_32(k)) */
secp256k1_silentpayments_create_t_k(&t_k_scalar, shared_secret33, k);
secp256k1_scalar_get_b32(t_k, &t_k_scalar);

/* Calculate P_output = B_spend + t_k * G */
secp256k1_pubkey_load(ctx, &P_output_ge, receiver_spend_pubkey);
if (!secp256k1_eckey_pubkey_tweak_add(&P_output_ge, &t_k_scalar)) {
return 0;
}

/* If the calculated output matches the one from the tx, we have a direct match and can
* return without labels calculation (one of the two would result in point of infinity) */
secp256k1_xonly_pubkey_save(&P_output_xonly, &P_output_ge);
if (secp256k1_xonly_pubkey_cmp(ctx, &P_output_xonly, tx_output) == 0) {
*direct_match = 1;
return 1;
}
*direct_match = 0;

/* If desired, also calculate label candidates */
if (label_data != NULL) {
secp256k1_ge P_output_negated_ge, tx_output_ge;
secp256k1_ge label_ge;
secp256k1_gej label_gej;

/* Calculate negated P_output (common addend) first */
secp256k1_ge_neg(&P_output_negated_ge, &P_output_ge);

/* Calculate first scan label candidate: label1 = tx_output - P_output */
secp256k1_xonly_pubkey_load(ctx, &tx_output_ge, tx_output);
secp256k1_gej_set_ge(&label_gej, &tx_output_ge);
secp256k1_gej_add_ge_var(&label_gej, &label_gej, &P_output_negated_ge, NULL);
secp256k1_ge_set_gej(&label_ge, &label_gej);
secp256k1_pubkey_save(&label_data->label, &label_ge);

/* Calculate second scan label candidate: label2 = -tx_output - P_output */
secp256k1_gej_set_ge(&label_gej, &tx_output_ge);
secp256k1_gej_neg(&label_gej, &label_gej);
secp256k1_gej_add_ge_var(&label_gej, &label_gej, &P_output_negated_ge, NULL);
secp256k1_ge_set_gej(&label_ge, &label_gej);
secp256k1_pubkey_save(&label_data->label_negated, &label_ge);
}

return 1;
}

#endif