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