Skip to content

Commit c2f2709

Browse files
committed
schnorr_adaptor: add example
This commit adds an example that implements the Multi-hop Locks protocol using the Schnorr adaptor signature APIs
1 parent 060dad2 commit c2f2709

File tree

5 files changed

+201
-0
lines changed

5 files changed

+201
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ libsecp256k1.pc
6666
contrib/gh-pr-create.sh
6767

6868
musig_example
69+
schnorr_adaptor_example
6970

7071
### CMake
7172
/CMakeUserPresets.json

Makefile.am

+11
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,17 @@ musig_example_LDFLAGS += -lbcrypt
195195
endif
196196
TESTS += musig_example
197197
endif
198+
if ENABLE_MODULE_SCHNORR_ADAPTOR
199+
noinst_PROGRAMS += schnorr_adaptor_example
200+
schnorr_adaptor_example_SOURCES = examples/schnorr_adaptor.c
201+
schnorr_adaptor_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC
202+
schnorr_adaptor_example_LDADD = libsecp256k1.la
203+
schnorr_adaptor_example_LDFLAGS = -static
204+
if BUILD_WINDOWS
205+
schnorr_adaptor_example_LDFLAGS += -lbcrypt
206+
endif
207+
TESTS += schnorr_adaptor_example
208+
endif
198209
endif
199210

200211
### Precomputed tables

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Usage examples
7373
Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`.
7474
* [ECDSA example](examples/ecdsa.c)
7575
* [Schnorr signatures example](examples/schnorr.c)
76+
* [Schnorr adaptor signatures example](examples/schnorr_adaptor.c)
7677
* [Deriving a shared secret (ECDH) example](examples/ecdh.c)
7778
* [MuSig example](examples/musig.c)
7879

examples/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,7 @@ endif()
2828
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
2929
add_example(schnorr)
3030
endif()
31+
32+
if(SECP256K1_ENABLE_MODULE_SCHNORR_ADAPTOR)
33+
add_example(schnorr_adaptor)
34+
endif()

examples/schnorr_adaptor.c

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*************************************************************************
2+
* Written in 2024 by Sivaram Dhakshinamoorthy *
3+
* To the extent possible under law, the author(s) have dedicated all *
4+
* copyright and related and neighboring rights to the software in this *
5+
* file to the public domain worldwide. This software is distributed *
6+
* without any warranty. For the CC0 Public Domain Dedication, see *
7+
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
8+
*************************************************************************/
9+
10+
#include <stdio.h>
11+
#include <assert.h>
12+
#include <string.h>
13+
14+
#include <secp256k1.h>
15+
#include <secp256k1_schnorrsig.h>
16+
#include <secp256k1_schnorr_adaptor.h>
17+
18+
#include "examples_util.h"
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;
48+
}
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+
}
85+
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))) {
116+
printf("Failed to generate randomness\n");
117+
return 1;
118+
}
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)) {
129+
return 1;
130+
}
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");
182+
secp256k1_context_destroy(ctx);
183+
return 0;
184+
}

0 commit comments

Comments
 (0)