|
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 | *
|
@@ -89,6 +90,20 @@ static unsigned char ALICE_SECKEY[32] = {
|
89 | 90 | 0x8a, 0x4c, 0x53, 0xf6, 0xe0, 0x50, 0x7b, 0x42,
|
90 | 91 | 0x15, 0x42, 0x01, 0xb8, 0xe5, 0xdf, 0xf3, 0xb1
|
91 | 92 | };
|
| 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 | +}; |
92 | 107 |
|
93 | 108 | struct label_cache_entry {
|
94 | 109 | unsigned char label[33];
|
@@ -430,11 +445,246 @@ static void test_recipient_api(void) {
|
430 | 445 | CHECK_ILLEGAL(CTX, secp256k1_silentpayments_recipient_scan_outputs(CTX, fp, &n_f, tp, 1, ALICE_SECKEY, &pd, &p, NULL, NULL));
|
431 | 446 | }
|
432 | 447 |
|
| 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 | + |
433 | 682 | void run_silentpayments_tests(void) {
|
434 | 683 | test_recipient_sort();
|
435 | 684 | test_send_api();
|
436 | 685 | test_label_api();
|
437 | 686 | test_recipient_api();
|
| 687 | + run_silentpayments_test_vectors(); |
438 | 688 | }
|
439 | 689 |
|
440 | 690 | #endif
|
0 commit comments