1
1
/*************************************************************************
2
- * Written in 2023- 2024 by Gregory Sanders and Sivaram Dhakshinamoorthy *
2
+ * Written in 2024 by Sivaram Dhakshinamoorthy *
3
3
* To the extent possible under law, the author(s) have dedicated all *
4
4
* copyright and related and neighboring rights to the software in this *
5
5
* file to the public domain worldwide. This software is distributed *
16
16
#include <secp256k1_schnorr_adaptor.h>
17
17
18
18
#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 ;
53
48
}
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
+ }
57
85
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 ))) {
60
116
printf ("Failed to generate randomness\n" );
61
117
return 1 ;
62
118
}
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 )) {
65
129
return 1 ;
66
130
}
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" );
102
182
secp256k1_context_destroy (ctx );
103
- return 0 ;
183
+ return 0 ;
104
184
}
0 commit comments