Skip to content

Commit a3da3e6

Browse files
committed
tests: add BIP-352 test vectors
Add the 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 f6106ff commit a3da3e6

File tree

8 files changed

+7492
-0
lines changed

8 files changed

+7492
-0
lines changed

Makefile.am

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ maintainer-clean-local: clean-precomp
260260
### Pregenerated test vectors
261261
### (see the comments in the previous section for detailed rationale)
262262
TESTVECTORS = src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h
263+
TESTVECTORS += src/modules/silentpayments/vectors.h
263264

264265
if ENABLE_MODULE_ECDH
265266
TESTVECTORS += src/wycheproof/ecdh_secp256k1_test.h
@@ -273,6 +274,10 @@ src/wycheproof/ecdh_secp256k1_test.h:
273274
mkdir -p $(@D)
274275
python3 $(top_srcdir)/tools/tests_wycheproof_generate_ecdh.py $(top_srcdir)/src/wycheproof/ecdh_secp256k1_test.json > $@
275276

277+
src/modules/silentpayments/vectors.h:
278+
mkdir -p $(@D)
279+
python3 $(top_srcdir)/tools/tests_silentpayments_generate.py $(top_srcdir)/src/modules/silentpayments/bip352_send_and_receive_test_vectors.json > $@
280+
276281
testvectors: $(TESTVECTORS)
277282

278283
BUILT_SOURCES += $(TESTVECTORS)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
include_HEADERS += include/secp256k1_silentpayments.h
22
noinst_HEADERS += src/modules/silentpayments/main_impl.h
33
noinst_HEADERS += src/modules/silentpayments/bench_impl.h
4+
noinst_HEADERS += src/modules/silentpayments/tests_impl.h
5+
noinst_HEADERS += src/modules/silentpayments/vectors.h

src/modules/silentpayments/bip352_send_and_receive_test_vectors.json

Lines changed: 2760 additions & 0 deletions
Large diffs are not rendered by default.

src/modules/silentpayments/tests_impl.h

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define SECP256K1_MODULE_SILENTPAYMENTS_TESTS_H
88

99
#include "../../../include/secp256k1_silentpayments.h"
10+
#include "../../../src/modules/silentpayments/vectors.h"
1011

1112
/** Constants
1213
*
@@ -89,6 +90,20 @@ static unsigned char ALICE_SECKEY[32] = {
8990
0x8a, 0x4c, 0x53, 0xf6, 0xe0, 0x50, 0x7b, 0x42,
9091
0x15, 0x42, 0x01, 0xb8, 0xe5, 0xdf, 0xf3, 0xb1
9192
};
93+
/* sha256("message") */
94+
static unsigned char MSG32[32] = {
95+
0xab,0x53,0x0a,0x13,0xe4,0x59,0x14,0x98,
96+
0x2b,0x79,0xf9,0xb7,0xe3,0xfb,0xa9,0x94,
97+
0xcf,0xd1,0xf3,0xfb,0x22,0xf7,0x1c,0xea,
98+
0x1a,0xfb,0xf0,0x2b,0x46,0x0c,0x6d,0x1d
99+
};
100+
/* sha256("random auxiliary data") */
101+
static unsigned char AUX32[32] = {
102+
0x0b,0x3f,0xdd,0xfd,0x67,0xbf,0x76,0xae,
103+
0x76,0x39,0xee,0x73,0x5b,0x70,0xff,0x15,
104+
0x83,0xfd,0x92,0x48,0xc0,0x57,0xd2,0x86,
105+
0x07,0xa2,0x15,0xf4,0x0b,0x0a,0x3e,0xcc
106+
};
92107

93108
struct label_cache_entry {
94109
unsigned char label[33];
@@ -430,11 +445,246 @@ static void test_recipient_api(void) {
430445
CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_scan_outputs(CTX, fp, &n_f, tp, 1, ALICE_SECKEY, &pd, &p, NULL, NULL));
431446
}
432447

448+
void run_silentpayments_test_vector_send(const struct bip352_test_vector *test) {
449+
secp256k1_silentpayments_recipient recipients[MAX_OUTPUTS_PER_TEST_CASE];
450+
const secp256k1_silentpayments_recipient *recipient_ptrs[MAX_OUTPUTS_PER_TEST_CASE];
451+
secp256k1_xonly_pubkey generated_outputs[MAX_OUTPUTS_PER_TEST_CASE];
452+
secp256k1_xonly_pubkey *generated_output_ptrs[MAX_OUTPUTS_PER_TEST_CASE];
453+
secp256k1_keypair taproot_keypairs[MAX_INPUTS_PER_TEST_CASE];
454+
secp256k1_keypair const *taproot_keypair_ptrs[MAX_INPUTS_PER_TEST_CASE];
455+
unsigned char const *plain_seckeys[MAX_INPUTS_PER_TEST_CASE];
456+
unsigned char created_output[32];
457+
size_t i, j, k;
458+
int match, ret;
459+
460+
/* Check that sender creates expected outputs */
461+
for (i = 0; i < test->num_outputs; i++) {
462+
CHECK(secp256k1_ec_pubkey_parse(CTX, &recipients[i].scan_pubkey, test->recipient_pubkeys[i].scan_pubkey, 33));
463+
CHECK(secp256k1_ec_pubkey_parse(CTX, &recipients[i].spend_pubkey, test->recipient_pubkeys[i].spend_pubkey, 33));
464+
recipients[i].index = i;
465+
recipient_ptrs[i] = &recipients[i];
466+
generated_output_ptrs[i] = &generated_outputs[i];
467+
}
468+
for (i = 0; i < test->num_plain_inputs; i++) {
469+
plain_seckeys[i] = test->plain_seckeys[i];
470+
}
471+
for (i = 0; i < test->num_taproot_inputs; i++) {
472+
CHECK(secp256k1_keypair_create(CTX, &taproot_keypairs[i], test->taproot_seckeys[i]));
473+
taproot_keypair_ptrs[i] = &taproot_keypairs[i];
474+
}
475+
ret = secp256k1_silentpayments_sender_create_outputs(CTX,
476+
generated_output_ptrs,
477+
recipient_ptrs,
478+
test->num_outputs,
479+
test->outpoint_smallest,
480+
test->num_taproot_inputs > 0 ? taproot_keypair_ptrs : NULL, test->num_taproot_inputs,
481+
test->num_plain_inputs > 0 ? plain_seckeys : NULL, test->num_plain_inputs
482+
);
483+
/* If we are unable to create outputs, e.g., the input keys sum to zero, check that the
484+
* expected number of recipient outputs for this test case is zero
485+
*/
486+
if (!ret) {
487+
CHECK(test->num_recipient_outputs == 0);
488+
return;
489+
}
490+
491+
match = 0;
492+
for (i = 0; i < test->num_output_sets; i++) {
493+
size_t n_matches = 0;
494+
for (j = 0; j < test->num_outputs; j++) {
495+
CHECK(secp256k1_xonly_pubkey_serialize(CTX, created_output, &generated_outputs[j]));
496+
/* Loop over both lists to ensure tests don't fail due to different orderings of outputs */
497+
for (k = 0; k < test->num_recipient_outputs; k++) {
498+
if (secp256k1_memcmp_var(created_output, test->recipient_outputs[i][k], 32) == 0) {
499+
n_matches++;
500+
break;
501+
}
502+
}
503+
}
504+
if (n_matches == test->num_recipient_outputs) {
505+
match = 1;
506+
break;
507+
}
508+
}
509+
CHECK(match);
510+
}
511+
512+
void run_silentpayments_test_vector_receive(const struct bip352_test_vector *test) {
513+
secp256k1_pubkey plain_pubkeys_objs[MAX_INPUTS_PER_TEST_CASE];
514+
secp256k1_xonly_pubkey xonly_pubkeys_objs[MAX_INPUTS_PER_TEST_CASE];
515+
secp256k1_xonly_pubkey tx_output_objs[MAX_OUTPUTS_PER_TEST_CASE];
516+
secp256k1_silentpayments_found_output found_output_objs[MAX_OUTPUTS_PER_TEST_CASE];
517+
secp256k1_pubkey const *plain_pubkeys[MAX_INPUTS_PER_TEST_CASE];
518+
secp256k1_xonly_pubkey const *xonly_pubkeys[MAX_INPUTS_PER_TEST_CASE];
519+
secp256k1_xonly_pubkey const *tx_outputs[MAX_OUTPUTS_PER_TEST_CASE];
520+
secp256k1_silentpayments_found_output *found_outputs[MAX_OUTPUTS_PER_TEST_CASE];
521+
unsigned char found_outputs_light_client[MAX_OUTPUTS_PER_TEST_CASE][32];
522+
secp256k1_pubkey recipient_scan_pubkey;
523+
secp256k1_pubkey recipient_spend_pubkey;
524+
secp256k1_pubkey label;
525+
size_t len = 33;
526+
size_t i,j;
527+
int match, ret;
528+
size_t n_found = 0;
529+
unsigned char found_output[32];
530+
unsigned char found_signatures[10][64];
531+
secp256k1_silentpayments_recipient_public_data public_data, public_data_index;
532+
unsigned char shared_secret_lightclient[33];
533+
unsigned char light_client_data[33];
534+
535+
536+
/* prepare the inputs */
537+
{
538+
for (i = 0; i < test->num_plain_inputs; i++) {
539+
CHECK(secp256k1_ec_pubkey_parse(CTX, &plain_pubkeys_objs[i], test->plain_pubkeys[i], 33));
540+
plain_pubkeys[i] = &plain_pubkeys_objs[i];
541+
}
542+
for (i = 0; i < test->num_taproot_inputs; i++) {
543+
CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pubkeys_objs[i], test->xonly_pubkeys[i]));
544+
xonly_pubkeys[i] = &xonly_pubkeys_objs[i];
545+
}
546+
ret = secp256k1_silentpayments_recipient_public_data_create(CTX, &public_data,
547+
test->outpoint_smallest,
548+
test->num_taproot_inputs > 0 ? xonly_pubkeys : NULL, test->num_taproot_inputs,
549+
test->num_plain_inputs > 0 ? plain_pubkeys : NULL, test->num_plain_inputs
550+
);
551+
/* If we are unable to create the public_data object, e.g., the input public keys sum to
552+
* zero, check that the expected number of recipient outputs for this test case is zero
553+
*/
554+
if (!ret) {
555+
CHECK(test->num_found_output_pubkeys == 0);
556+
return;
557+
}
558+
}
559+
/* prepare the outputs */
560+
{
561+
for (i = 0; i < test->num_to_scan_outputs; i++) {
562+
CHECK(secp256k1_xonly_pubkey_parse(CTX, &tx_output_objs[i], test->to_scan_outputs[i]));
563+
tx_outputs[i] = &tx_output_objs[i];
564+
}
565+
for (i = 0; i < test->num_found_output_pubkeys; i++) {
566+
found_outputs[i] = &found_output_objs[i];
567+
}
568+
}
569+
570+
/* scan / spend pubkeys are not in the given data of the recipient part, so let's compute them */
571+
CHECK(secp256k1_ec_pubkey_create(CTX, &recipient_scan_pubkey, test->scan_seckey));
572+
CHECK(secp256k1_ec_pubkey_create(CTX, &recipient_spend_pubkey, test->spend_seckey));
573+
574+
/* create labels cache */
575+
labels_cache.entries_used = 0;
576+
for (i = 0; i < test->num_labels; i++) {
577+
unsigned int m = test->label_integers[i];
578+
struct label_cache_entry *cache_entry = &labels_cache.entries[labels_cache.entries_used];
579+
CHECK(secp256k1_silentpayments_recipient_create_label(CTX, &label, cache_entry->label_tweak, test->scan_seckey, m));
580+
CHECK(secp256k1_ec_pubkey_serialize(CTX, cache_entry->label, &len, &label, SECP256K1_EC_COMPRESSED));
581+
labels_cache.entries_used++;
582+
}
583+
CHECK(secp256k1_silentpayments_recipient_scan_outputs(CTX,
584+
found_outputs, &n_found,
585+
tx_outputs, test->num_to_scan_outputs,
586+
test->scan_seckey,
587+
&public_data,
588+
&recipient_spend_pubkey,
589+
label_lookup, &labels_cache)
590+
);
591+
for (i = 0; i < n_found; i++) {
592+
unsigned char full_seckey[32];
593+
secp256k1_keypair keypair;
594+
unsigned char signature[64];
595+
memcpy(&full_seckey, test->spend_seckey, 32);
596+
CHECK(secp256k1_ec_seckey_tweak_add(CTX, full_seckey, found_outputs[i]->tweak));
597+
CHECK(secp256k1_keypair_create(CTX, &keypair, full_seckey));
598+
CHECK(secp256k1_schnorrsig_sign32(CTX, signature, MSG32, &keypair, AUX32));
599+
memcpy(found_signatures[i], signature, 64);
600+
}
601+
602+
/* compare expected and scanned outputs (including calculated seckey tweaks and signatures) */
603+
match = 0;
604+
for (i = 0; i < n_found; i++) {
605+
CHECK(secp256k1_xonly_pubkey_serialize(CTX, found_output, &found_outputs[i]->output));
606+
for (j = 0; j < test->num_found_output_pubkeys; j++) {
607+
if (secp256k1_memcmp_var(&found_output, test->found_output_pubkeys[j], 32) == 0) {
608+
CHECK(secp256k1_memcmp_var(found_outputs[i]->tweak, test->found_seckey_tweaks[j], 32) == 0);
609+
CHECK(secp256k1_memcmp_var(found_signatures[i], test->found_signatures[j], 64) == 0);
610+
match = 1;
611+
break;
612+
}
613+
}
614+
CHECK(match);
615+
}
616+
CHECK(n_found == test->num_found_output_pubkeys);
617+
/* Scan as a light client
618+
* it is not recommended to use labels as a light client so here we are only
619+
* running this on tests that do not involve labels. Primarily, this test is to
620+
* ensure that _recipient_created_shared_secret and _create_shared_secret are the same
621+
*/
622+
if (test->num_labels == 0) {
623+
CHECK(secp256k1_silentpayments_recipient_public_data_serialize(CTX, light_client_data, &public_data));
624+
CHECK(secp256k1_silentpayments_recipient_public_data_parse(CTX, &public_data_index, light_client_data));
625+
CHECK(secp256k1_silentpayments_recipient_create_shared_secret(CTX, shared_secret_lightclient, test->scan_seckey, &public_data_index));
626+
n_found = 0;
627+
{
628+
int found = 0;
629+
size_t k = 0;
630+
secp256k1_xonly_pubkey potential_output;
631+
632+
while(1) {
633+
634+
CHECK(secp256k1_silentpayments_recipient_create_output_pubkey(CTX,
635+
&potential_output,
636+
shared_secret_lightclient,
637+
&recipient_spend_pubkey,
638+
k
639+
));
640+
/* At this point, we check that the utxo exists with a light client protocol.
641+
* For this example, we'll just iterate through the list of pubkeys */
642+
found = 0;
643+
for (i = 0; i < test->num_to_scan_outputs; i++) {
644+
if (secp256k1_xonly_pubkey_cmp(CTX, &potential_output, tx_outputs[i]) == 0) {
645+
secp256k1_xonly_pubkey_serialize(CTX, found_outputs_light_client[n_found], &potential_output);
646+
found = 1;
647+
n_found++;
648+
k++;
649+
break;
650+
}
651+
}
652+
if (!found) {
653+
break;
654+
}
655+
}
656+
}
657+
CHECK(n_found == test->num_found_output_pubkeys);
658+
for (i = 0; i < n_found; i++) {
659+
match = 0;
660+
for (j = 0; j < test->num_found_output_pubkeys; j++) {
661+
if (secp256k1_memcmp_var(&found_outputs_light_client[i], test->found_output_pubkeys[j], 32) == 0) {
662+
match = 1;
663+
break;
664+
}
665+
}
666+
CHECK(match);
667+
}
668+
}
669+
}
670+
671+
void run_silentpayments_test_vectors(void) {
672+
size_t i;
673+
674+
675+
for (i = 0; i < sizeof(bip352_test_vectors) / sizeof(bip352_test_vectors[0]); i++) {
676+
const struct bip352_test_vector *test = &bip352_test_vectors[i];
677+
run_silentpayments_test_vector_send(test);
678+
run_silentpayments_test_vector_receive(test);
679+
}
680+
}
681+
433682
void run_silentpayments_tests(void) {
434683
test_recipient_sort();
435684
test_send_api();
436685
test_label_api();
437686
test_recipient_api();
687+
run_silentpayments_test_vectors();
438688
}
439689

440690
#endif

0 commit comments

Comments
 (0)