Skip to content

Commit d29564f

Browse files
committed
improve the example file
1 parent 0b3cc93 commit d29564f

File tree

2 files changed

+158
-78
lines changed

2 files changed

+158
-78
lines changed

examples/schnorr_adaptor.c

+158-78
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*************************************************************************
2-
* Written in 2023-2024 by Gregory Sanders and Sivaram Dhakshinamoorthy *
2+
* Written in 2024 by Sivaram Dhakshinamoorthy *
33
* To the extent possible under law, the author(s) have dedicated all *
44
* copyright and related and neighboring rights to the software in this *
55
* file to the public domain worldwide. This software is distributed *
@@ -16,89 +16,169 @@
1616
#include <secp256k1_schnorr_adaptor.h>
1717

1818
#include "examples_util.h"
19-
/* TODO: make is clear, like multi_hop_tests of ECDSA adaptor */
20-
int main(void) {
21-
unsigned char msg[12] = "Hello World!";
22-
unsigned char msg_hash[32];
23-
unsigned char tag[17] = "my_fancy_protocol";
24-
unsigned char alice_seckey[32];
25-
unsigned char signature[64];
26-
/* Includes signature parity bit so one larger */
27-
unsigned char pre_signature[65];
28-
int is_signature_valid;
29-
int return_val;
30-
secp256k1_xonly_pubkey alice_pubkey;
31-
secp256k1_keypair alice_keypair;
32-
33-
/* Adaptor point T that Alice commits in her pre-signature */
34-
secp256k1_pubkey adaptor_pk;
35-
/* Adaptor point T' (identical to T), extracted by Bob from
36-
* Alice's pre-signature. */
37-
secp256k1_pubkey extracted_adaptor_pk;
38-
39-
/* Secret adaptor t (discrete log of T) that Bob will learn,
40-
* for instance, the upstream PTLC secret he discovers. */
41-
unsigned char sec_adaptor[32];
42-
/* Secret adaptor t' (identical to t) that Alice extracts from
43-
* the pre-signature and BIP340 signature */
44-
unsigned char extracted_sec_adaptor[32];
45-
46-
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
47-
48-
/* Secret adaptor t is generated by original PTLC sender, and
49-
* Bob will learn it later */
50-
if (!fill_random(sec_adaptor, sizeof(sec_adaptor))) {
51-
printf("Failed to generate randomness\n");
52-
return 1;
19+
20+
/** This example implements the Multi-hop Locks protocol described in
21+
* https://github.com/BlockstreamResearch/scriptless-scripts/blob/master/md/multi-hop-locks.md,
22+
* using the Schnorr adaptor module.
23+
*
24+
* In this example, Alice (sender) sends a payment to Carol (recipient)
25+
* via Bob (intermediate hop). The protocol ensures that Alice exchanges
26+
* her coins for a proof of payment from Carol, and Bob securely forwards
27+
* the payment without being able to access its details.
28+
*
29+
* Carol provides Alice with a point (z*G), which acts as the proof of
30+
* payment. Alice sets up cryptographic locks with Bob, and Bob forwards
31+
* the payment to Carol. When Carol reveals the secret z to claim the
32+
* payment, Alice learns the proof of payment.
33+
*/
34+
35+
static int create_keypair(const secp256k1_context *ctx, secp256k1_keypair *keypair, secp256k1_xonly_pubkey *pubkey) {
36+
unsigned char seckey[32];
37+
while (1) {
38+
if (!fill_random(seckey, sizeof(seckey))) {
39+
printf("Failed to generate randomness\n");
40+
return 0;
41+
}
42+
if (secp256k1_keypair_create(ctx, keypair, seckey)) {
43+
break;
44+
}
45+
}
46+
if(!secp256k1_keypair_xonly_pub(ctx, pubkey, NULL, keypair)){
47+
return 0;
5348
}
54-
/* Adaptor point T (= t*G) is given to Alice by original PTLC sender */
55-
return_val = secp256k1_ec_pubkey_create(ctx, &adaptor_pk, sec_adaptor);
56-
assert(return_val);
49+
return 1;
50+
}
51+
52+
/* Creates the locks required for multi-hop payments */
53+
static int create_hop_locks(const secp256k1_context *ctx, secp256k1_pubkey *left_lock, secp256k1_pubkey *right_lock, secp256k1_pubkey *adaptor_pop, unsigned char *tweak_sum, unsigned char *tweak1, unsigned char *tweak2) {
54+
while (1) {
55+
if (!fill_random(tweak1, 32)) {
56+
printf("Failed to generate randomness\n");
57+
return 0;
58+
}
59+
if (!fill_random(tweak2, 32)) {
60+
printf("Failed to generate randomness\n");
61+
return 0;
62+
}
63+
if (secp256k1_ec_seckey_verify(ctx, tweak1) && secp256k1_ec_seckey_verify(ctx, tweak2)) {
64+
break;
65+
}
66+
}
67+
/* Create left lock = (z + tweak1)*G */
68+
memcpy(left_lock, adaptor_pop, sizeof(secp256k1_pubkey));
69+
if(!secp256k1_ec_pubkey_tweak_add(ctx, left_lock, tweak1)) {
70+
return 0;
71+
}
72+
73+
/* Create right lock = (z + tweak1 + tweak2)*G */
74+
memcpy(tweak_sum, tweak1, 32);
75+
if(!secp256k1_ec_seckey_tweak_add(ctx, tweak_sum, tweak2)) {
76+
return 0;
77+
}
78+
memcpy(right_lock, adaptor_pop, sizeof(secp256k1_pubkey));
79+
if(!secp256k1_ec_pubkey_tweak_add(ctx, right_lock, tweak_sum)) {
80+
return 0;
81+
}
82+
83+
return 1;
84+
}
5785

58-
/* Alice generates her keypair, and the message hash to sign */
59-
if (!fill_random(alice_seckey, sizeof(alice_seckey))) {
86+
int main(void) {
87+
unsigned char tx_ab[32] = "alice sends a payment to bob....";
88+
unsigned char tx_bc[32] = "bob sends a payment to carol....";
89+
unsigned char presig_ab[65];
90+
unsigned char presig_bc[65];
91+
unsigned char sig_ab[64];
92+
unsigned char sig_bc[64];
93+
unsigned char tmp[32];
94+
unsigned char tweak1[32];
95+
unsigned char tweak2[32];
96+
unsigned char tweak_sum[32];
97+
unsigned char secret_pop[32]; /* Carol's secret proof of payment */
98+
secp256k1_pubkey adaptor_pop;
99+
secp256k1_pubkey left_lock;
100+
secp256k1_pubkey right_lock;
101+
secp256k1_pubkey tmp_pubkey;
102+
secp256k1_xonly_pubkey pubkey_a, pubkey_b;
103+
secp256k1_keypair keypair_a, keypair_b;
104+
int ret;
105+
106+
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
107+
108+
/* Generate keypairs for Alice and Bob */
109+
ret = create_keypair(ctx, &keypair_a, &pubkey_a);
110+
assert(ret);
111+
ret = create_keypair(ctx, &keypair_b, &pubkey_b);
112+
assert(ret);
113+
114+
/* Carol setup: creates a proof of payment (z*G) */
115+
if (!fill_random(secret_pop, sizeof(secret_pop))) {
60116
printf("Failed to generate randomness\n");
61117
return 1;
62118
}
63-
if (!secp256k1_keypair_create(ctx, &alice_keypair, alice_seckey)) {
64-
printf("Couldn't generate keypair\n");
119+
ret = secp256k1_ec_pubkey_create(ctx, &adaptor_pop, secret_pop);
120+
assert(ret);
121+
122+
/* Alice's setup: Generates tweak1, tweak2, left lock, and right lock
123+
* for the payment. She shares the following:
124+
*
125+
* 1. With Bob: tweak2, left lock, right lock
126+
* 2. With Carol: tweak1 + tweak2, right lock
127+
*/
128+
if (!create_hop_locks(ctx, &left_lock, &right_lock, &adaptor_pop, tweak_sum, tweak1, tweak2)) {
65129
return 1;
66130
}
67-
return_val = secp256k1_keypair_xonly_pub(ctx, &alice_pubkey, NULL, &alice_keypair);
68-
assert(return_val);
69-
return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
70-
assert(return_val);
71-
72-
/* Alice creates a pre-signature using the adaptor point T(=Z+A+B),
73-
* and sends it to Bob */
74-
return_val = secp256k1_schnorr_adaptor_presign(ctx, pre_signature, msg_hash, &alice_keypair, &adaptor_pk, NULL);
75-
assert(return_val);
76-
77-
/* Bob extracts T from the pre-signature */
78-
return_val = secp256k1_schnorr_adaptor_extract(ctx, &extracted_adaptor_pk, pre_signature, msg_hash, &alice_pubkey);
79-
assert(return_val);
80-
assert(secp256k1_ec_pubkey_cmp(ctx, &adaptor_pk, &extracted_adaptor_pk) == 0);
81-
82-
/* Bob forwards the PTLC. Bob's outgoing PTLC (Z+A+B+C) is claimed
83-
* by Carol. Bob decides to go to chain with the full signature to
84-
* claim PTLC, subtracting local secret `c`.
85-
*/
86-
return_val = secp256k1_schnorr_adaptor_adapt(ctx, signature, pre_signature, sec_adaptor);
87-
assert(return_val);
88-
/* Signature should be valid! */
89-
is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &alice_pubkey);
90-
assert(is_signature_valid);
91-
92-
/* A full signature hits the chain. Alice recovers z+a+b; should match
93-
* originally embedded value. */
94-
return_val = secp256k1_schnorr_adaptor_extract_sec(ctx, extracted_sec_adaptor, pre_signature, signature);
95-
assert(return_val == 1);
96-
assert(memcmp(sec_adaptor, extracted_sec_adaptor, sizeof(sec_adaptor)) == 0);
97-
98-
/* Alice subtracts out local blinding factor `b`, can now claim incoming
99-
* PTLC from Alice with sec_adaptor(=z+a) */
100-
101-
printf("Success!\n\n");
131+
/* Alice sends a pre-signature to Bob */
132+
ret = secp256k1_schnorr_adaptor_presign(ctx, presig_ab, tx_ab, &keypair_a, &left_lock, NULL);
133+
assert(ret);
134+
135+
/* Bob setup: extracts the left lock from Alice's pre-signature and verifies it */
136+
ret = secp256k1_schnorr_adaptor_extract(ctx, &tmp_pubkey, presig_ab, tx_ab, &pubkey_a);
137+
assert(ret);
138+
assert(memcmp(&tmp_pubkey, &left_lock, sizeof(left_lock)) == 0);
139+
/* Bob creates a pre-signature that forwards the payment to Carol */
140+
ret = secp256k1_schnorr_adaptor_presign(ctx, presig_bc, tx_bc, &keypair_b, &right_lock, NULL);
141+
assert(ret);
142+
143+
/* Carol extracts the right lock from Bob's pre-signature and verifies it */
144+
ret = secp256k1_schnorr_adaptor_extract(ctx, &tmp_pubkey, presig_bc, tx_bc, &pubkey_b);
145+
assert(ret);
146+
assert(memcmp(&tmp_pubkey, &right_lock, sizeof(right_lock)) == 0);
147+
/* Carol claims her payment by adapting Bob's pre-signature with the
148+
* secret = z + tweak1 + tweak2, to produce a valid BIP340 Schnorr
149+
* signature. */
150+
memcpy(tmp, secret_pop, sizeof(secret_pop));
151+
ret = secp256k1_ec_seckey_tweak_add(ctx, tmp, tweak_sum);
152+
assert(ret);
153+
ret = secp256k1_schnorr_adaptor_adapt(ctx, sig_bc, presig_bc, tmp);
154+
assert(ret);
155+
assert(secp256k1_schnorrsig_verify(ctx, sig_bc, tx_bc, sizeof(tx_bc), &pubkey_b));
156+
157+
/* Bob extracts the secret = z + tweak1 + tweak2 from his pre-signature
158+
* and the BIP340 signature created by Carol. */
159+
ret = secp256k1_schnorr_adaptor_extract_sec(ctx, tmp, presig_bc, sig_bc);
160+
assert(ret);
161+
/* Bob claims his payment by adapting Alice's pre-signature with the
162+
* secret = z + tweak1, to produce a valid BIP340 Schnorr signature. */
163+
ret = secp256k1_ec_seckey_negate(ctx, tweak2);
164+
assert(ret);
165+
ret = secp256k1_ec_seckey_tweak_add(ctx, tmp, tweak2);
166+
assert(ret);
167+
ret = secp256k1_schnorr_adaptor_adapt(ctx, sig_ab, presig_ab, tmp);
168+
assert(ret);
169+
assert(secp256k1_schnorrsig_verify(ctx, sig_ab, tx_ab, sizeof(tx_ab), &pubkey_a));
170+
171+
/* Alice extracts the proof of payment = z from her pre-signature
172+
* and the BIP340 signature created by Bob. */
173+
ret = secp256k1_schnorr_adaptor_extract_sec(ctx, tmp, presig_ab, sig_ab);
174+
assert(ret);
175+
ret = secp256k1_ec_seckey_negate(ctx, tweak1);
176+
assert(ret);
177+
ret = secp256k1_ec_seckey_tweak_add(ctx, tmp, tweak1);
178+
assert(ret);
179+
assert(memcmp(tmp, secret_pop, sizeof(secret_pop)) == 0);
180+
181+
printf("Multi-hop locks protocol successfully executed!!!\n");
102182
secp256k1_context_destroy(ctx);
103-
return 0;
183+
return 0;
104184
}

schnorr_adaptor_example

4.68 KB
Binary file not shown.

0 commit comments

Comments
 (0)