|
7 | 7 | #define SECP256K1_MODULE_SILENTPAYMENTS_TESTS_H
|
8 | 8 |
|
9 | 9 | #include "../../../include/secp256k1_silentpayments.h"
|
| 10 | +#include "../../../src/modules/silentpayments/vectors.h" |
10 | 11 |
|
11 | 12 | /** Constants
|
12 | 13 | *
|
@@ -85,6 +86,20 @@ static unsigned char ALICE_SECKEY[32] = {
|
85 | 86 | 0x8a,0x4c,0x53,0xf6,0xe0,0x50,0x7b,0x42,
|
86 | 87 | 0x15,0x42,0x01,0xb8,0xe5,0xdf,0xf3,0xb1
|
87 | 88 | };
|
| 89 | +/* sha256("message") */ |
| 90 | +static unsigned char MSG32[32] = { |
| 91 | + 0xab,0x53,0x0a,0x13,0xe4,0x59,0x14,0x98, |
| 92 | + 0x2b,0x79,0xf9,0xb7,0xe3,0xfb,0xa9,0x94, |
| 93 | + 0xcf,0xd1,0xf3,0xfb,0x22,0xf7,0x1c,0xea, |
| 94 | + 0x1a,0xfb,0xf0,0x2b,0x46,0x0c,0x6d,0x1d |
| 95 | +}; |
| 96 | +/* sha256("random auxiliary data") */ |
| 97 | +static unsigned char AUX32[32] = { |
| 98 | + 0x0b,0x3f,0xdd,0xfd,0x67,0xbf,0x76,0xae, |
| 99 | + 0x76,0x39,0xee,0x73,0x5b,0x70,0xff,0x15, |
| 100 | + 0x83,0xfd,0x92,0x48,0xc0,0x57,0xd2,0x86, |
| 101 | + 0x07,0xa2,0x15,0xf4,0x0b,0x0a,0x3e,0xcc |
| 102 | +}; |
88 | 103 |
|
89 | 104 | struct label_cache_entry {
|
90 | 105 | unsigned char label[33];
|
@@ -368,11 +383,246 @@ static void test_recipient_api(void) {
|
368 | 383 | CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_scan_outputs(CTX, fp, &n_f, tp, 1, ALICE_SECKEY, &pd, &p, NULL, &labels_cache));
|
369 | 384 | }
|
370 | 385 |
|
| 386 | +void run_silentpayments_test_vector_send(const struct bip352_test_vector *test) { |
| 387 | + secp256k1_silentpayments_recipient recipients[MAX_OUTPUTS_PER_TEST_CASE]; |
| 388 | + const secp256k1_silentpayments_recipient *recipient_ptrs[MAX_OUTPUTS_PER_TEST_CASE]; |
| 389 | + secp256k1_xonly_pubkey generated_outputs[MAX_OUTPUTS_PER_TEST_CASE]; |
| 390 | + secp256k1_xonly_pubkey *generated_output_ptrs[MAX_OUTPUTS_PER_TEST_CASE]; |
| 391 | + secp256k1_keypair taproot_keypairs[MAX_INPUTS_PER_TEST_CASE]; |
| 392 | + secp256k1_keypair const *taproot_keypair_ptrs[MAX_INPUTS_PER_TEST_CASE]; |
| 393 | + unsigned char const *plain_seckeys[MAX_INPUTS_PER_TEST_CASE]; |
| 394 | + unsigned char created_output[32]; |
| 395 | + size_t i, j, k; |
| 396 | + int match, ret; |
| 397 | + |
| 398 | + /* Check that sender creates expected outputs */ |
| 399 | + for (i = 0; i < test->num_outputs; i++) { |
| 400 | + CHECK(secp256k1_ec_pubkey_parse(CTX, &recipients[i].scan_pubkey, test->recipient_pubkeys[i].scan_pubkey, 33)); |
| 401 | + CHECK(secp256k1_ec_pubkey_parse(CTX, &recipients[i].labeled_spend_pubkey, test->recipient_pubkeys[i].labeled_spend_pubkey, 33)); |
| 402 | + recipients[i].index = i; |
| 403 | + recipient_ptrs[i] = &recipients[i]; |
| 404 | + generated_output_ptrs[i] = &generated_outputs[i]; |
| 405 | + } |
| 406 | + for (i = 0; i < test->num_plain_inputs; i++) { |
| 407 | + plain_seckeys[i] = test->plain_seckeys[i]; |
| 408 | + } |
| 409 | + for (i = 0; i < test->num_taproot_inputs; i++) { |
| 410 | + CHECK(secp256k1_keypair_create(CTX, &taproot_keypairs[i], test->taproot_seckeys[i])); |
| 411 | + taproot_keypair_ptrs[i] = &taproot_keypairs[i]; |
| 412 | + } |
| 413 | + ret = secp256k1_silentpayments_sender_create_outputs(CTX, |
| 414 | + generated_output_ptrs, |
| 415 | + recipient_ptrs, |
| 416 | + test->num_outputs, |
| 417 | + test->outpoint_smallest, |
| 418 | + test->num_taproot_inputs > 0 ? taproot_keypair_ptrs : NULL, test->num_taproot_inputs, |
| 419 | + test->num_plain_inputs > 0 ? plain_seckeys : NULL, test->num_plain_inputs |
| 420 | + ); |
| 421 | + /* If we are unable to create outputs, e.g., the input keys sum to zero, check that the |
| 422 | + * expected number of recipient outputs for this test case is zero |
| 423 | + */ |
| 424 | + if (!ret) { |
| 425 | + CHECK(test->num_recipient_outputs == 0); |
| 426 | + return; |
| 427 | + } |
| 428 | + |
| 429 | + match = 0; |
| 430 | + for (i = 0; i < test->num_output_sets; i++) { |
| 431 | + size_t n_matches = 0; |
| 432 | + for (j = 0; j < test->num_outputs; j++) { |
| 433 | + CHECK(secp256k1_xonly_pubkey_serialize(CTX, created_output, &generated_outputs[j])); |
| 434 | + /* Loop over both lists to ensure tests don't fail due to different orderings of outputs */ |
| 435 | + for (k = 0; k < test->num_recipient_outputs; k++) { |
| 436 | + if (secp256k1_memcmp_var(created_output, test->recipient_outputs[i][k], 32) == 0) { |
| 437 | + n_matches++; |
| 438 | + break; |
| 439 | + } |
| 440 | + } |
| 441 | + } |
| 442 | + if (n_matches == test->num_recipient_outputs) { |
| 443 | + match = 1; |
| 444 | + break; |
| 445 | + } |
| 446 | + } |
| 447 | + CHECK(match); |
| 448 | +} |
| 449 | + |
| 450 | +void run_silentpayments_test_vector_receive(const struct bip352_test_vector *test) { |
| 451 | + secp256k1_pubkey plain_pubkeys_objs[MAX_INPUTS_PER_TEST_CASE]; |
| 452 | + secp256k1_xonly_pubkey xonly_pubkeys_objs[MAX_INPUTS_PER_TEST_CASE]; |
| 453 | + secp256k1_xonly_pubkey tx_output_objs[MAX_OUTPUTS_PER_TEST_CASE]; |
| 454 | + secp256k1_silentpayments_found_output found_output_objs[MAX_OUTPUTS_PER_TEST_CASE]; |
| 455 | + secp256k1_pubkey const *plain_pubkeys[MAX_INPUTS_PER_TEST_CASE]; |
| 456 | + secp256k1_xonly_pubkey const *xonly_pubkeys[MAX_INPUTS_PER_TEST_CASE]; |
| 457 | + secp256k1_xonly_pubkey const *tx_outputs[MAX_OUTPUTS_PER_TEST_CASE]; |
| 458 | + secp256k1_silentpayments_found_output *found_outputs[MAX_OUTPUTS_PER_TEST_CASE]; |
| 459 | + unsigned char found_outputs_light_client[MAX_OUTPUTS_PER_TEST_CASE][32]; |
| 460 | + secp256k1_pubkey recipient_scan_pubkey; |
| 461 | + secp256k1_pubkey recipient_spend_pubkey; |
| 462 | + secp256k1_pubkey label; |
| 463 | + size_t len = 33; |
| 464 | + size_t i,j; |
| 465 | + int match, ret; |
| 466 | + size_t n_found = 0; |
| 467 | + unsigned char found_output[32]; |
| 468 | + unsigned char found_signatures[10][64]; |
| 469 | + secp256k1_silentpayments_recipient_public_data public_data, public_data_index; |
| 470 | + unsigned char shared_secret_lightclient[33]; |
| 471 | + unsigned char light_client_data[33]; |
| 472 | + |
| 473 | + |
| 474 | + /* prepare the inputs */ |
| 475 | + { |
| 476 | + for (i = 0; i < test->num_plain_inputs; i++) { |
| 477 | + CHECK(secp256k1_ec_pubkey_parse(CTX, &plain_pubkeys_objs[i], test->plain_pubkeys[i], 33)); |
| 478 | + plain_pubkeys[i] = &plain_pubkeys_objs[i]; |
| 479 | + } |
| 480 | + for (i = 0; i < test->num_taproot_inputs; i++) { |
| 481 | + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pubkeys_objs[i], test->xonly_pubkeys[i])); |
| 482 | + xonly_pubkeys[i] = &xonly_pubkeys_objs[i]; |
| 483 | + } |
| 484 | + ret = secp256k1_silentpayments_recipient_public_data_create(CTX, &public_data, |
| 485 | + test->outpoint_smallest, |
| 486 | + test->num_taproot_inputs > 0 ? xonly_pubkeys : NULL, test->num_taproot_inputs, |
| 487 | + test->num_plain_inputs > 0 ? plain_pubkeys : NULL, test->num_plain_inputs |
| 488 | + ); |
| 489 | + /* If we are unable to create the public_data object, e.g., the input public keys sum to |
| 490 | + * zero, check that the expected number of recipient outputs for this test case is zero |
| 491 | + */ |
| 492 | + if (!ret) { |
| 493 | + CHECK(test->num_found_output_pubkeys == 0); |
| 494 | + return; |
| 495 | + } |
| 496 | + } |
| 497 | + /* prepare the outputs */ |
| 498 | + { |
| 499 | + for (i = 0; i < test->num_to_scan_outputs; i++) { |
| 500 | + CHECK(secp256k1_xonly_pubkey_parse(CTX, &tx_output_objs[i], test->to_scan_outputs[i])); |
| 501 | + tx_outputs[i] = &tx_output_objs[i]; |
| 502 | + } |
| 503 | + for (i = 0; i < test->num_found_output_pubkeys; i++) { |
| 504 | + found_outputs[i] = &found_output_objs[i]; |
| 505 | + } |
| 506 | + } |
| 507 | + |
| 508 | + /* scan / spend pubkeys are not in the given data of the recipient part, so let's compute them */ |
| 509 | + CHECK(secp256k1_ec_pubkey_create(CTX, &recipient_scan_pubkey, test->scan_seckey)); |
| 510 | + CHECK(secp256k1_ec_pubkey_create(CTX, &recipient_spend_pubkey, test->spend_seckey)); |
| 511 | + |
| 512 | + /* create labels cache */ |
| 513 | + labels_cache.entries_used = 0; |
| 514 | + for (i = 0; i < test->num_labels; i++) { |
| 515 | + unsigned int m = test->label_integers[i]; |
| 516 | + struct label_cache_entry *cache_entry = &labels_cache.entries[labels_cache.entries_used]; |
| 517 | + CHECK(secp256k1_silentpayments_recipient_create_label(CTX, &label, cache_entry->label_tweak, test->scan_seckey, m)); |
| 518 | + CHECK(secp256k1_ec_pubkey_serialize(CTX, cache_entry->label, &len, &label, SECP256K1_EC_COMPRESSED)); |
| 519 | + labels_cache.entries_used++; |
| 520 | + } |
| 521 | + CHECK(secp256k1_silentpayments_recipient_scan_outputs(CTX, |
| 522 | + found_outputs, &n_found, |
| 523 | + tx_outputs, test->num_to_scan_outputs, |
| 524 | + test->scan_seckey, |
| 525 | + &public_data, |
| 526 | + &recipient_spend_pubkey, |
| 527 | + label_lookup, &labels_cache) |
| 528 | + ); |
| 529 | + for (i = 0; i < n_found; i++) { |
| 530 | + unsigned char full_seckey[32]; |
| 531 | + secp256k1_keypair keypair; |
| 532 | + unsigned char signature[64]; |
| 533 | + memcpy(&full_seckey, test->spend_seckey, 32); |
| 534 | + CHECK(secp256k1_ec_seckey_tweak_add(CTX, full_seckey, found_outputs[i]->tweak)); |
| 535 | + CHECK(secp256k1_keypair_create(CTX, &keypair, full_seckey)); |
| 536 | + CHECK(secp256k1_schnorrsig_sign32(CTX, signature, MSG32, &keypair, AUX32)); |
| 537 | + memcpy(found_signatures[i], signature, 64); |
| 538 | + } |
| 539 | + |
| 540 | + /* compare expected and scanned outputs (including calculated seckey tweaks and signatures) */ |
| 541 | + match = 0; |
| 542 | + for (i = 0; i < n_found; i++) { |
| 543 | + CHECK(secp256k1_xonly_pubkey_serialize(CTX, found_output, &found_outputs[i]->output)); |
| 544 | + for (j = 0; j < test->num_found_output_pubkeys; j++) { |
| 545 | + if (secp256k1_memcmp_var(&found_output, test->found_output_pubkeys[j], 32) == 0) { |
| 546 | + CHECK(secp256k1_memcmp_var(found_outputs[i]->tweak, test->found_seckey_tweaks[j], 32) == 0); |
| 547 | + CHECK(secp256k1_memcmp_var(found_signatures[i], test->found_signatures[j], 64) == 0); |
| 548 | + match = 1; |
| 549 | + break; |
| 550 | + } |
| 551 | + } |
| 552 | + CHECK(match); |
| 553 | + } |
| 554 | + CHECK(n_found == test->num_found_output_pubkeys); |
| 555 | + /* Scan as a light client |
| 556 | + * it is not recommended to use labels as a light client so here we are only |
| 557 | + * running this on tests that do not involve labels. Primarily, this test is to |
| 558 | + * ensure that _recipient_created_shared_secret and _create_shared_secret are the same |
| 559 | + */ |
| 560 | + if (test->num_labels == 0) { |
| 561 | + CHECK(secp256k1_silentpayments_recipient_public_data_serialize(CTX, light_client_data, &public_data)); |
| 562 | + CHECK(secp256k1_silentpayments_recipient_public_data_parse(CTX, &public_data_index, light_client_data)); |
| 563 | + CHECK(secp256k1_silentpayments_recipient_create_shared_secret(CTX, shared_secret_lightclient, test->scan_seckey, &public_data_index)); |
| 564 | + n_found = 0; |
| 565 | + { |
| 566 | + int found = 0; |
| 567 | + size_t k = 0; |
| 568 | + secp256k1_xonly_pubkey potential_output; |
| 569 | + |
| 570 | + while(1) { |
| 571 | + |
| 572 | + CHECK(secp256k1_silentpayments_recipient_create_output_pubkey(CTX, |
| 573 | + &potential_output, |
| 574 | + shared_secret_lightclient, |
| 575 | + &recipient_spend_pubkey, |
| 576 | + k |
| 577 | + )); |
| 578 | + /* At this point, we check that the utxo exists with a light client protocol. |
| 579 | + * For this example, we'll just iterate through the list of pubkeys */ |
| 580 | + found = 0; |
| 581 | + for (i = 0; i < test->num_to_scan_outputs; i++) { |
| 582 | + if (secp256k1_xonly_pubkey_cmp(CTX, &potential_output, tx_outputs[i]) == 0) { |
| 583 | + secp256k1_xonly_pubkey_serialize(CTX, found_outputs_light_client[n_found], &potential_output); |
| 584 | + found = 1; |
| 585 | + n_found++; |
| 586 | + k++; |
| 587 | + break; |
| 588 | + } |
| 589 | + } |
| 590 | + if (!found) { |
| 591 | + break; |
| 592 | + } |
| 593 | + } |
| 594 | + } |
| 595 | + CHECK(n_found == test->num_found_output_pubkeys); |
| 596 | + for (i = 0; i < n_found; i++) { |
| 597 | + match = 0; |
| 598 | + for (j = 0; j < test->num_found_output_pubkeys; j++) { |
| 599 | + if (secp256k1_memcmp_var(&found_outputs_light_client[i], test->found_output_pubkeys[j], 32) == 0) { |
| 600 | + match = 1; |
| 601 | + break; |
| 602 | + } |
| 603 | + } |
| 604 | + CHECK(match); |
| 605 | + } |
| 606 | + } |
| 607 | +} |
| 608 | + |
| 609 | +void run_silentpayments_test_vectors(void) { |
| 610 | + size_t i; |
| 611 | + |
| 612 | + |
| 613 | + for (i = 0; i < sizeof(bip352_test_vectors) / sizeof(bip352_test_vectors[0]); i++) { |
| 614 | + const struct bip352_test_vector *test = &bip352_test_vectors[i]; |
| 615 | + run_silentpayments_test_vector_send(test); |
| 616 | + run_silentpayments_test_vector_receive(test); |
| 617 | + } |
| 618 | +} |
| 619 | + |
371 | 620 | void run_silentpayments_tests(void) {
|
372 | 621 | test_recipient_sort();
|
373 | 622 | test_send_api();
|
374 | 623 | test_label_api();
|
375 | 624 | test_recipient_api();
|
| 625 | + run_silentpayments_test_vectors(); |
376 | 626 | }
|
377 | 627 |
|
378 | 628 | #endif
|
0 commit comments