Repository: bitcoin/bitcoin
File: src/test/key_tests.cpp (C++)

Code before:
        {{"0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"}},
    };

    for (const auto& [sec_hex, pub_hex, aux_hex, msg_hex, sig_hex] : SIGN_VECTORS) {
        auto sec = ParseHex(sec_hex);
        auto pub = ParseHex(pub_hex);
        uint256 aux256(ParseHex(aux_hex));
        uint256 msg256(ParseHex(msg_hex));
        auto sig = ParseHex(sig_hex);
        unsigned char sig64[64];

        // Run the untweaked test vectors above, comparing with exact expected signature.
        CKey key;
        key.Set(sec.begin(), sec.end(), true);
        XOnlyPubKey pubkey(key.GetPubKey());
        BOOST_CHECK(std::equal(pubkey.begin(), pubkey.end(), pub.begin(), pub.end()));
        bool ok = key.SignSchnorr(msg256, sig64, nullptr, aux256);
        BOOST_CHECK(ok);
        BOOST_CHECK(std::vector<unsigned char>(sig64, sig64 + 64) == sig);
        // Verify those signatures for good measure.
        BOOST_CHECK(pubkey.VerifySchnorr(msg256, sig64));

        // Repeat the same check, but use the KeyPair directly without any merkle tweak
        KeyPair keypair = key.ComputeKeyPair(/*merkle_root=*/nullptr);
        CKey keypair_seckey;
        BOOST_CHECK(keypair.GetKey(keypair_seckey));
        BOOST_CHECK(key == keypair_seckey);
        bool kp_ok = keypair.SignSchnorr(msg256, sig64, aux256);
        BOOST_CHECK(kp_ok);
        XOnlyPubKey keypair_xonly{keypair_seckey.GetPubKey()};
        BOOST_CHECK(keypair_xonly.VerifySchnorr(msg256, sig64));

        // Do 10 iterations where we sign with a random Merkle root to tweak,
        // and compare against the resulting tweaked keys, with random aux.
        // In iteration i=0 we tweak with empty Merkle tree.
        for (int i = 0; i < 10; ++i) {
            uint256 merkle_root;
            if (i) merkle_root = InsecureRand256();
            auto tweaked = pubkey.CreateTapTweak(i ? &merkle_root : nullptr);
            BOOST_CHECK(tweaked);
            XOnlyPubKey tweaked_key = tweaked->first;
            aux256 = InsecureRand256();
            bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, aux256);
            BOOST_CHECK(ok);
            BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64));

            // Repeat the same check, but use the KeyPair class directly
            KeyPair keypair = key.ComputeKeyPair(&merkle_root);
            CKey keypair_seckey;
            BOOST_CHECK(keypair.GetKey(keypair_seckey));
            XOnlyPubKey keypair_xonly{keypair_seckey.GetPubKey()};

Review comment (bug):
nit: BOOST_CHECK_MESSAGE often produces more readable results, but it may be inconvenient to set it up properly. If you think it adds value, it may be helpful to add some debug info as a message in case of likely failures.

Code after:
        BOOST_CHECK(std::vector<unsigned char>(sig64, sig64 + 64) == sig);

        // Do 10 iterations where we sign with a random Merkle root to tweak,
        // and compare against the resulting tweaked keys, with random aux.
        // In iteration i=0 we tweak with empty Merkle tree.
        for (int i = 0; i < 10; ++i) {
            uint256 merkle_root;
            if (i) merkle_root = InsecureRand256();
            auto tweaked = pubkey.CreateTapTweak(i ? &merkle_root : nullptr);
            BOOST_CHECK(tweaked);
            XOnlyPubKey tweaked_key = tweaked->first;
            aux256 = InsecureRand256();
            bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, aux256);
            BOOST_CHECK(ok);
            BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64));

            // Repeat the same check, but use the KeyPair class directly
            KeyPair keypair = key.ComputeKeyPair(&merkle_root);
            bool kp_ok = keypair.SignSchnorr(msg256, sig64, aux256);
            BOOST_CHECK(kp_ok);
            BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64));
        }
    }
}

BOOST_AUTO_TEST_CASE(key_ellswift)
{
    for (const auto& secret : {strSecret1, strSecret2, strSecret1C, strSecret2C}) {
        CKey key = DecodeSecret(secret);
        BOOST_CHECK(key.IsValid());

        uint256 ent32 = InsecureRand256();
        auto ellswift = key.EllSwiftCreate(AsBytes(Span{ent32}));

        CPubKey decoded_pubkey = ellswift.Decode();
        if (!key.IsCompressed()) {
            // The decoding constructor returns a compressed pubkey. If the
            // original was uncompressed, we must decompress the decoded one
            // to compare.
            decoded_pubkey.Decompress();
        }
        BOOST_CHECK(key.GetPubKey() == decoded_pubkey);
    }
}

BOOST_AUTO_TEST_CASE(bip341_test_h)
{
    std::vector<unsigned char> G_uncompressed = ParseHex("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8");
    HashWriter hw;
    hw.write(MakeByteSpan(G_uncompressed));
    XOnlyPubKey H{hw.GetSHA256()};