Skip to content

Commit

Permalink
silentpayments: implement change output creation
Browse files Browse the repository at this point in the history
  • Loading branch information
theStack committed Dec 15, 2023
1 parent c41c326 commit 6cc4bd0
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
36 changes: 36 additions & 0 deletions include/secp256k1_silentpayments.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,42 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_sender_c
size_t n_outputs
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9);

/** Create Silent Payment change output public keys for the sender side.
* Given a list of private keys a_0...a_n (one for each input to spend),
* an outpoints_hash, the sender's scan private key a_scan, the sender's
* spend public key A_spend, compute the corresponding change output public keys
* C_0...C_(n-1):
*
* change_shared_secret = outpoints_hash * a * A_scan
* A_change = A_spend + sha256(ser_256(a_scan)) * G
* Let k = 0
* For each change output desired:
* Let c_k = sha256(ser_P(change_shared_secret) || ser_32(k))
* Let C_k = A_change + c_k * G
* Let k = k + 1
*
* Returns: 1 if change outputs creation was successful. 0 if an error occured.
* Args: ctx: pointer to a context object
* Out: change_outputs_pubkeys33: pointer to an array of 33-byte compressed pubkeys,
* storing n_change_outputs output pubkeys (result)
* In: inputs_privkeys32: pointer to an array of 32-byte private keys of inputs
* n_inputs_privkeys: the number of sender's input private keys (must be at least 1)
* outpoints_hash32: hash of the sorted serialized outpoints
* sender_scan_privkey32: pointer to the sender's 32-byte scan private key
* sender_spend_pubkey33: pointer to the sender's 33-byte spend pubkey
* n_change_outputs: the number of change outputs to create (must be at least 1)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_sender_create_change_outputs(
const secp256k1_context *ctx,
unsigned char *change_outputs_pubkeys33,
const unsigned char *inputs_privkeys32,
size_t n_inputs_privkeys,
const unsigned char *outpoints_hash32,
const unsigned char *sender_scan_privkey32,
const unsigned char *sender_spend_pubkey33,
size_t n_change_outputs
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7);

/** Scan for Silent Payment output public keys on the receiver side.
*
* Given a list of n public keys A_0...A_(n-1) (one for each spent input),
Expand Down
68 changes: 68 additions & 0 deletions src/modules/silentpayments/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,74 @@ int secp256k1_silentpayments_sender_create_outputs(const secp256k1_context *ctx,
return 1;
}

/* Create Silent Payment change output public keys for the sender side. */
int secp256k1_silentpayments_sender_create_change_outputs(const secp256k1_context *ctx, unsigned char *change_outputs_pubkeys33, const unsigned char *inputs_privkeys32, size_t n_inputs_privkeys, const unsigned char *outpoints_hash32, const unsigned char *sender_scan_privkey32, const unsigned char *sender_spend_pubkey33, size_t n_change_outputs) {
unsigned char a_tweaked[32];
secp256k1_pubkey A_scan, A_spend, A_change;
unsigned char change_shared_secret_and_k[33+4];
unsigned char hash_a_scan[32];
secp256k1_sha256 sha;
uint32_t i, k;

/* Sanity check inputs */
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(change_outputs_pubkeys33 != NULL);
ARG_CHECK(inputs_privkeys32 != NULL);
ARG_CHECK(n_inputs_privkeys >= 1);
ARG_CHECK(outpoints_hash32 != NULL);
ARG_CHECK(sender_scan_privkey32 != NULL);
ARG_CHECK(sender_spend_pubkey33 != NULL);
ARG_CHECK(n_change_outputs >= 1);

/* Compute a_tweaked = (a_0 + a_1 + ... + a_n) * outpoints_hash */
memcpy(a_tweaked, &inputs_privkeys32[0], 32);
for (i = 1; i < n_inputs_privkeys; i++) {
if (!secp256k1_ec_seckey_tweak_add(ctx, a_tweaked, &inputs_privkeys32[i*32]))
return 0;
}
if (!secp256k1_ec_seckey_tweak_mul(ctx, a_tweaked, outpoints_hash32))
return 0;

/* Compute change_shared_secret = a_tweaked * A_scan */
if (!secp256k1_ec_pubkey_create(ctx, &A_scan, sender_scan_privkey32))
return 0;
if (!secp256k1_ecdh(ctx, change_shared_secret_and_k, &A_scan, a_tweaked, ecdh_return_pubkey, NULL))
return 0;

/* Compute A_change = A_spend + sha256(ser_256(a_scan)) * G */
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, sender_scan_privkey32, 32);
secp256k1_sha256_finalize(&sha, hash_a_scan);
if (!secp256k1_ec_pubkey_parse(ctx, &A_spend, sender_spend_pubkey33, 33))
return 0;
A_change = A_spend;
if (!secp256k1_ec_pubkey_tweak_add(ctx, &A_change, hash_a_scan))
return 0;

for (k = 0; k < n_change_outputs; k++) {
secp256k1_pubkey C_k;
unsigned char c_k[32];
size_t outputlen = 33;

/* Compute c_k = sha256(ser_P(change_shared_secret) || ser_32(k)) */
secp256k1_write_be32(change_shared_secret_and_k+33, k);
secp256k1_sha256_initialize(&sha);
secp256k1_sha256_write(&sha, change_shared_secret_and_k, sizeof(change_shared_secret_and_k));
secp256k1_sha256_finalize(&sha, c_k);

/* Compute C_k = A_change + c_k * G */
C_k = A_change;
if (!secp256k1_ec_pubkey_tweak_add(ctx, &C_k, c_k))
return 0;

/* Store result in change_outputs_pubkeys33 array */
if (!secp256k1_ec_pubkey_serialize(ctx, change_outputs_pubkeys33 + k*33, &outputlen, &C_k, SECP256K1_EC_COMPRESSED))
return 0;
}

return 1;
}

/* Scan for Silent Payment output public keys on the receiver side. */
int secp256k1_silentpayments_receiver_scan_outputs(const secp256k1_context *ctx, uint32_t *found_indexes, size_t *n_found_indexes, const unsigned char *inputs_pubkeys33, size_t n_inputs_pubkeys, const unsigned char *outpoints_hash32, const unsigned char *receiver_scan_privkey32, const unsigned char *receiver_spend_pubkey33, const unsigned char *outputs_to_check32, size_t n_outputs_to_check, const unsigned char *precomp_labels_to_check33, size_t n_precomp_labels_to_check) {
secp256k1_pubkey A_tweaked, B_spend;
Expand Down

0 comments on commit 6cc4bd0

Please sign in to comment.