Skip to content

Commit ccdcd0f

Browse files
theStackjosibake
authored andcommitted
tests: add BIP-352 test vectors
The vectors are generated with a Python script that converts the .json file from the BIP to C code: $ ./tools/tests_silentpayments_generate.py test_vectors.json > ./src/modules/silentpayments/vectors.h
1 parent 8b320de commit ccdcd0f

File tree

7 files changed

+4439
-0
lines changed

7 files changed

+4439
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
include_HEADERS += include/secp256k1_silentpayments.h
22
noinst_HEADERS += src/modules/silentpayments/main_impl.h
3+
noinst_HEADERS += src/modules/silentpayments/tests_impl.h
4+
noinst_HEADERS += src/modules/silentpayments/vectors.h
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/***********************************************************************
2+
* Distributed under the MIT software license, see the accompanying *
3+
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
4+
***********************************************************************/
5+
6+
#ifndef SECP256K1_MODULE_SILENTPAYMENTS_TESTS_H
7+
#define SECP256K1_MODULE_SILENTPAYMENTS_TESTS_H
8+
9+
#include "../../../include/secp256k1_silentpayments.h"
10+
#include "../../../src/modules/silentpayments/vectors.h"
11+
#include "assert.h"
12+
13+
struct label_cache_entry {
14+
secp256k1_pubkey label;
15+
unsigned char label_tweak[32];
16+
};
17+
struct labels_cache {
18+
const secp256k1_context *ctx;
19+
size_t entries_used;
20+
struct label_cache_entry entries[10];
21+
};
22+
struct labels_cache labels_cache;
23+
const unsigned char* label_lookup(const secp256k1_pubkey* key, const void* cache_ptr) {
24+
const struct labels_cache* cache = (const struct labels_cache*)cache_ptr;
25+
size_t i;
26+
for (i = 0; i < cache->entries_used; i++) {
27+
if (secp256k1_ec_pubkey_cmp(cache->ctx, &cache->entries[i].label, key) == 0) {
28+
return cache->entries[i].label_tweak;
29+
}
30+
}
31+
return NULL;
32+
}
33+
34+
void run_silentpayments_test_vector_send(const struct bip352_test_vector *test) {
35+
secp256k1_silentpayments_recipient recipients[MAX_OUTPUTS_PER_TEST_CASE];
36+
const secp256k1_silentpayments_recipient *recipient_ptrs[MAX_OUTPUTS_PER_TEST_CASE];
37+
secp256k1_xonly_pubkey generated_outputs[MAX_OUTPUTS_PER_TEST_CASE];
38+
secp256k1_xonly_pubkey *generated_output_ptrs[MAX_OUTPUTS_PER_TEST_CASE];
39+
secp256k1_keypair taproot_keypairs[MAX_INPUTS_PER_TEST_CASE];
40+
secp256k1_keypair const *taproot_keypair_ptrs[MAX_INPUTS_PER_TEST_CASE];
41+
unsigned char const *plain_seckeys[MAX_INPUTS_PER_TEST_CASE];
42+
unsigned char created_output[32];
43+
size_t i, j;
44+
int match;
45+
46+
/* Check that sender creates expected outputs */
47+
for (i = 0; i < test->num_outputs; i++) {
48+
CHECK(secp256k1_ec_pubkey_parse(CTX, &recipients[i].scan_pubkey, test->recipient_pubkeys[i].scan_pubkey, 33));
49+
CHECK(secp256k1_ec_pubkey_parse(CTX, &recipients[i].spend_pubkey, test->recipient_pubkeys[i].spend_pubkey, 33));
50+
recipients[i].index = i;
51+
recipient_ptrs[i] = &recipients[i];
52+
generated_output_ptrs[i] = &generated_outputs[i];
53+
}
54+
for (i = 0; i < test->num_plain_inputs; i++) {
55+
plain_seckeys[i] = test->plain_seckeys[i];
56+
}
57+
for (i = 0; i < test->num_taproot_inputs; i++) {
58+
int ret = secp256k1_keypair_create(CTX, &taproot_keypairs[i], test->taproot_seckeys[i]);
59+
assert(ret);
60+
taproot_keypair_ptrs[i] = &taproot_keypairs[i];
61+
}
62+
CHECK(secp256k1_silentpayments_sender_create_outputs(CTX,
63+
generated_output_ptrs,
64+
recipient_ptrs,
65+
test->num_outputs,
66+
test->outpoint_smallest,
67+
test->num_taproot_inputs > 0 ? taproot_keypair_ptrs : NULL, test->num_taproot_inputs,
68+
test->num_plain_inputs > 0 ? plain_seckeys : NULL, test->num_plain_inputs
69+
));
70+
for (i = 0; i < test->num_outputs; i++) {
71+
CHECK(secp256k1_xonly_pubkey_serialize(CTX, created_output, &generated_outputs[i]));
72+
match = 0;
73+
/* Loop over both lists to ensure tests don't fail due to different orderings of outputs */
74+
for (j = 0; j < test->num_recipient_outputs; j++) {
75+
if (secp256k1_memcmp_var(created_output, test->recipient_outputs[j], 32) == 0) {
76+
match = 1;
77+
break;
78+
}
79+
}
80+
CHECK(match);
81+
}
82+
}
83+
84+
void run_silentpayments_test_vector_receive(const struct bip352_test_vector *test) {
85+
secp256k1_pubkey plain_pubkeys_objs[MAX_INPUTS_PER_TEST_CASE];
86+
secp256k1_xonly_pubkey xonly_pubkeys_objs[MAX_INPUTS_PER_TEST_CASE];
87+
secp256k1_xonly_pubkey tx_output_objs[MAX_OUTPUTS_PER_TEST_CASE];
88+
secp256k1_silentpayments_found_output found_output_objs[MAX_OUTPUTS_PER_TEST_CASE];
89+
secp256k1_pubkey const *plain_pubkeys[MAX_INPUTS_PER_TEST_CASE];
90+
secp256k1_xonly_pubkey const *xonly_pubkeys[MAX_INPUTS_PER_TEST_CASE];
91+
secp256k1_xonly_pubkey const *tx_outputs[MAX_OUTPUTS_PER_TEST_CASE];
92+
secp256k1_silentpayments_found_output *found_outputs[MAX_OUTPUTS_PER_TEST_CASE];
93+
secp256k1_pubkey receiver_scan_pubkey;
94+
secp256k1_pubkey receiver_spend_pubkey;
95+
unsigned char shared_secret[33];
96+
size_t i,j;
97+
int match;
98+
size_t n_found = 0;
99+
unsigned char found_output[32];
100+
unsigned char found_signatures[10][64];
101+
unsigned char input_hash_receiver[32];
102+
secp256k1_pubkey A_sum;
103+
104+
105+
/* prepare the inputs */
106+
{
107+
for (i = 0; i < test->num_plain_inputs; i++) {
108+
CHECK(secp256k1_ec_pubkey_parse(CTX, &plain_pubkeys_objs[i], test->plain_pubkeys[i], 33));
109+
plain_pubkeys[i] = &plain_pubkeys_objs[i];
110+
}
111+
for (i = 0; i < test->num_taproot_inputs; i++) {
112+
CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pubkeys_objs[i], test->xonly_pubkeys[i]));
113+
xonly_pubkeys[i] = &xonly_pubkeys_objs[i];
114+
}
115+
CHECK(secp256k1_silentpayments_recipient_compute_public_data(CTX, &A_sum, input_hash_receiver,
116+
test->outpoint_smallest,
117+
test->num_taproot_inputs > 0 ? xonly_pubkeys : NULL, test->num_taproot_inputs,
118+
test->num_plain_inputs > 0 ? plain_pubkeys : NULL, test->num_plain_inputs
119+
));
120+
}
121+
/* prepare the outputs */
122+
{
123+
for (i = 0; i < test->num_to_scan_outputs; i++) {
124+
CHECK(secp256k1_xonly_pubkey_parse(CTX, &tx_output_objs[i], test->to_scan_outputs[i]));
125+
tx_outputs[i] = &tx_output_objs[i];
126+
}
127+
for (i = 0; i < test->num_found_output_pubkeys; i++) {
128+
found_outputs[i] = &found_output_objs[i];
129+
}
130+
}
131+
132+
/* scan / spend pubkeys are not in the given data of the receiver part, so let's compute them */
133+
CHECK(secp256k1_ec_pubkey_create(CTX, &receiver_scan_pubkey, test->scan_seckey));
134+
CHECK(secp256k1_ec_pubkey_create(CTX, &receiver_spend_pubkey, test->spend_seckey));
135+
136+
/* create shared secret */
137+
{
138+
unsigned char shared_secret_fullnode[33];
139+
unsigned char shared_secret_lightclient[33];
140+
secp256k1_pubkey A_tweaked;
141+
142+
CHECK(secp256k1_silentpayments_create_shared_secret(CTX, shared_secret_fullnode, &A_sum, test->scan_seckey, input_hash_receiver));
143+
/* check that creating shared secret in light client / index mode (with intermediate A_tweaked) leads to the same result */
144+
CHECK(secp256k1_silentpayments_recipient_compute_public_data(CTX, &A_tweaked, NULL,
145+
test->outpoint_smallest,
146+
test->num_taproot_inputs > 0 ? xonly_pubkeys : NULL, test->num_taproot_inputs,
147+
test->num_plain_inputs > 0 ? plain_pubkeys : NULL, test->num_plain_inputs
148+
));
149+
CHECK(secp256k1_silentpayments_recipient_create_shared_secret(CTX, shared_secret_lightclient, test->scan_seckey, &A_tweaked));
150+
CHECK(secp256k1_memcmp_var(shared_secret_fullnode, shared_secret_lightclient, 33) == 0);
151+
152+
memcpy(shared_secret, shared_secret_fullnode, 33);
153+
}
154+
155+
/* create labels cache */
156+
labels_cache.ctx = CTX;
157+
labels_cache.entries_used = 0;
158+
for (i = 0; i < test->num_labels; i++) {
159+
unsigned int m = test->label_integers[i];
160+
struct label_cache_entry *cache_entry = &labels_cache.entries[labels_cache.entries_used];
161+
CHECK(secp256k1_silentpayments_recipient_create_label_tweak(CTX, &cache_entry->label, cache_entry->label_tweak, test->scan_seckey, m));
162+
labels_cache.entries_used++;
163+
}
164+
CHECK(secp256k1_silentpayments_recipient_scan_outputs(CTX,
165+
found_outputs, &n_found,
166+
tx_outputs, test->num_to_scan_outputs,
167+
test->scan_seckey,
168+
&A_sum,
169+
&receiver_spend_pubkey,
170+
input_hash_receiver,
171+
label_lookup, &labels_cache)
172+
);
173+
for (i = 0; i < n_found; i++) {
174+
unsigned char full_seckey[32];
175+
secp256k1_keypair keypair;
176+
unsigned char signature[64];
177+
const unsigned char msg32[32] = /* sha256("message") */
178+
{0xab,0x53,0x0a,0x13,0xe4,0x59,0x14,0x98,0x2b,0x79,0xf9,0xb7,0xe3,0xfb,0xa9,0x94,
179+
0xcf,0xd1,0xf3,0xfb,0x22,0xf7,0x1c,0xea,0x1a,0xfb,0xf0,0x2b,0x46,0x0c,0x6d,0x1d};
180+
const unsigned char aux32[32] = /* sha256("random auxiliary data") */
181+
{0x0b,0x3f,0xdd,0xfd,0x67,0xbf,0x76,0xae,0x76,0x39,0xee,0x73,0x5b,0x70,0xff,0x15,
182+
0x83,0xfd,0x92,0x48,0xc0,0x57,0xd2,0x86,0x07,0xa2,0x15,0xf4,0x0b,0x0a,0x3e,0xcc};
183+
memcpy(&full_seckey, test->spend_seckey, 32);
184+
CHECK(secp256k1_ec_seckey_tweak_add(CTX, full_seckey, found_outputs[i]->tweak));
185+
CHECK(secp256k1_keypair_create(CTX, &keypair, full_seckey));
186+
CHECK(secp256k1_schnorrsig_sign32(CTX, signature, msg32, &keypair, aux32));
187+
memcpy(found_signatures[i], signature, 64);
188+
}
189+
190+
/* compare expected and scanned outputs (including calculated seckey tweaks and signatures) */
191+
for (i = 0; i < n_found; i++) {
192+
CHECK(secp256k1_xonly_pubkey_serialize(CTX, found_output, &found_outputs[i]->output));
193+
match = 0;
194+
for (j = 0; j < test->num_found_output_pubkeys; j++) {
195+
if (secp256k1_memcmp_var(&found_output, test->found_output_pubkeys[j], 32) == 0) {
196+
match = 1;
197+
CHECK(secp256k1_memcmp_var(found_outputs[i]->tweak, test->found_seckey_tweaks[j], 32) == 0);
198+
CHECK(secp256k1_memcmp_var(found_signatures[i], test->found_signatures[j], 64) == 0);
199+
break;
200+
}
201+
}
202+
CHECK(match);
203+
}
204+
CHECK(n_found == test->num_found_output_pubkeys);
205+
}
206+
207+
void run_silentpayments_test_vectors(void) {
208+
size_t i;
209+
210+
211+
for (i = 0; i < sizeof(bip352_test_vectors) / sizeof(bip352_test_vectors[0]); i++) {
212+
const struct bip352_test_vector *test = &bip352_test_vectors[i];
213+
run_silentpayments_test_vector_send(test);
214+
run_silentpayments_test_vector_receive(test);
215+
}
216+
}
217+
218+
void run_silentpayments_tests(void) {
219+
run_silentpayments_test_vectors();
220+
/* TODO: add a few manual tests here, that target the ECC-related parts of silent payments */
221+
}
222+
223+
#endif

0 commit comments

Comments
 (0)