From 38e9a5d7b2085ed8417e8652600dbbdc163738df Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Fri, 12 Jul 2024 00:09:31 +0200 Subject: [PATCH] refactor: Make ParseHex(const char*) consteval Introduces FixedVec type - useful because ParseHex() supports whitespace intermingled with the hex string, resulting in a smaller size, making std::array use problematic. FixedVec allows returning a smaller size() than what is given in the template parameter. --- src/bench/bech32.cpp | 2 +- src/kernel/chainparams.cpp | 2 +- src/pubkey.cpp | 9 +- src/pubkey.h | 6 +- src/script/script.h | 2 +- src/test/base58_tests.cpp | 2 +- src/test/bloom_tests.cpp | 8 +- src/test/descriptor_tests.cpp | 10 +-- src/test/key_tests.cpp | 2 +- src/test/miniscript_tests.cpp | 4 +- src/test/util_tests.cpp | 61 ++++++++------ src/util/strencodings.h | 106 ++++++++++++++++++++++++ src/wallet/crypter.cpp | 6 +- src/wallet/crypter.h | 6 +- src/wallet/test/wallet_crypto_tests.cpp | 20 ++--- 15 files changed, 178 insertions(+), 68 deletions(-) diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp index 9922653766fdb5..663717d84044bb 100644 --- a/src/bench/bech32.cpp +++ b/src/bench/bech32.cpp @@ -13,7 +13,7 @@ static void Bech32Encode(benchmark::Bench& bench) { - std::vector v = ParseHex("c97f5a67ec381b760aeaf67573bc164845ff39a3bb26a1cee401ac67243b48db"); + constexpr FixedVec v = ParseHex("c97f5a67ec381b760aeaf67573bc164845ff39a3bb26a1cee401ac67243b48db"); std::vector tmp = {0}; tmp.reserve(1 + 32 * 8 / 5); ConvertBits<8, 5, true>([&](unsigned char c) { tmp.push_back(c); }, v.begin(), v.end()); diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 2b729e3b7a9b43..df0fece9198ae5 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -297,7 +297,7 @@ class SigNetParams : public CChainParams { vSeeds.clear(); if (!options.challenge) { - bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"); + bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae").to_dynamic(); vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl."); vSeeds.emplace_back("seed.signet.achownodes.xyz."); // Ava Chow, only supports x1, x5, x9, x49, x809, x849, xd, x400, x404, x408, x448, xc08, xc48, x40c diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 13e3c2dbe07d88..356dd69a1f2a01 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -190,14 +190,7 @@ int ecdsa_signature_parse_der_lax(secp256k1_ecdsa_signature* sig, const unsigned * For an example script for calculating H, refer to the unit tests in * ./test/functional/test_framework/crypto/secp256k1.py */ -static const std::vector NUMS_H_DATA{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")}; -const XOnlyPubKey XOnlyPubKey::NUMS_H{NUMS_H_DATA}; - -XOnlyPubKey::XOnlyPubKey(Span bytes) -{ - assert(bytes.size() == 32); - std::copy(bytes.begin(), bytes.end(), m_keydata.begin()); -} +constexpr XOnlyPubKey XOnlyPubKey::NUMS_H{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")}; std::vector XOnlyPubKey::GetKeyIDs() const { diff --git a/src/pubkey.h b/src/pubkey.h index ae34ddd0afddcf..44d94b9aa72ffd 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -254,7 +254,11 @@ class XOnlyPubKey bool IsNull() const { return m_keydata.IsNull(); } /** Construct an x-only pubkey from exactly 32 bytes. */ - explicit XOnlyPubKey(Span bytes); + constexpr explicit XOnlyPubKey(Span bytes) + { + assert(bytes.size() == 32); + std::copy(bytes.begin(), bytes.end(), m_keydata.begin()); + } /** Construct an x-only pubkey from a normal pubkey. */ explicit XOnlyPubKey(const CPubKey& pubkey) : XOnlyPubKey(Span{pubkey}.subspan(1, 32)) {} diff --git a/src/script/script.h b/src/script/script.h index 035152ee5104ac..1e3d66c326448f 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -463,7 +463,7 @@ class CScript : public CScriptBase return *this; } - CScript& operator<<(const std::vector& b) LIFETIMEBOUND + CScript& operator<<(std::span b) LIFETIMEBOUND { if (b.size() < OP_PUSHDATA1) { diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index bb3defb93efd93..805523834a44f3 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) // check that DecodeBase58 skips whitespace, but still fails with unexpected non-whitespace at the end. BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result, 3)); BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result, 3)); - std::vector expected = ParseHex("971a55"); + constexpr FixedVec expected = ParseHex("971a55"); BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); BOOST_CHECK(DecodeBase58Check("3vQB7B6MrGQZaxCuFg4oh"s, result, 100)); diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 6699afdbfabd4c..ea1c52b1b6fd86 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -43,7 +43,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize) DataStream stream{}; stream << filter; - std::vector expected = ParseHex("03614e9b050000000000000001"); + constexpr FixedVec expected = ParseHex("03614e9b050000000000000001"); auto result{MakeUCharSpan(stream)}; BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak) DataStream stream{}; stream << filter; - std::vector expected = ParseHex("03ce4299050000000100008001"); + constexpr FixedVec expected = ParseHex("03ce4299050000000100008001"); auto result{MakeUCharSpan(stream)}; BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); @@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key) DataStream stream{}; stream << filter; - std::vector expected = ParseHex("038fc16b080000000000000001"); + constexpr FixedVec expected = ParseHex("038fc16b080000000000000001"); auto result{MakeUCharSpan(stream)}; BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); @@ -352,7 +352,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize) DataStream merkleStream{}; merkleStream << merkleBlock; - std::vector expected = ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101"); + constexpr FixedVec expected = ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101"); auto result{MakeUCharSpan(merkleStream)}; BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), result.begin(), result.end()); diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index e6821dd321884f..b4246979057bfd 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -622,16 +622,16 @@ BOOST_AUTO_TEST_CASE(descriptor_test) // We can't sign for a script requiring a ripemd160 preimage without providing it. Check("wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"002001549deda34cbc4a5982263191380f522695a2ddc2f99fc3a65c736264bd6cab"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("1fed6fbd0e408eb4bddfefa075289dc7061e7a3240c84f6ba5b9f294d96a21f4"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {}); // But if we provide it, we can. - Check("wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"002001549deda34cbc4a5982263191380f522695a2ddc2f99fc3a65c736264bd6cab"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("1fed6fbd0e408eb4bddfefa075289dc7061e7a3240c84f6ba5b9f294d96a21f4"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("ff9aa1829c90d26e73301383f549e1497b7d6325"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}}); + Check("wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:ripemd160(ff9aa1829c90d26e73301383f549e1497b7d6325),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"002001549deda34cbc4a5982263191380f522695a2ddc2f99fc3a65c736264bd6cab"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("1fed6fbd0e408eb4bddfefa075289dc7061e7a3240c84f6ba5b9f294d96a21f4"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("ff9aa1829c90d26e73301383f549e1497b7d6325").to_dynamic(), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").to_dynamic()}}); // Same for sha256 Check("wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"002071f7283dbbb9a55ed43a54cda16ba0efd0f16dc48fe200f299e57bb5d7be8dd4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("a1809a65ba5ca2f09a06c114d4881eed95d1b62f38743cf126cf71b2dd411374"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {}); - Check("wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"002071f7283dbbb9a55ed43a54cda16ba0efd0f16dc48fe200f299e57bb5d7be8dd4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("a1809a65ba5ca2f09a06c114d4881eed95d1b62f38743cf126cf71b2dd411374"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}}); + Check("wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:sha256(7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"002071f7283dbbb9a55ed43a54cda16ba0efd0f16dc48fe200f299e57bb5d7be8dd4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("a1809a65ba5ca2f09a06c114d4881eed95d1b62f38743cf126cf71b2dd411374"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("7426ba0604c3f8682c7016b44673f85c5bd9da2fa6c1080810cf53ae320c9863").to_dynamic(), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").to_dynamic()}}); // Same for hash160 Check("wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"00209b9d5b45735d0e15df5b41d6594602d3de472262f7b75edc6cf5f3e3fa4e3ae4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("d7bdbc680503a585925eec72d11fc99396f51855d0a03fce53c90bed4c2e319f"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {}); - Check("wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"00209b9d5b45735d0e15df5b41d6594602d3de472262f7b75edc6cf5f3e3fa4e3ae4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("d7bdbc680503a585925eec72d11fc99396f51855d0a03fce53c90bed4c2e319f"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("292e2df59e3a22109200beed0cdc84b12e66793e"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}}); + Check("wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash160(292e2df59e3a22109200beed0cdc84b12e66793e),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"00209b9d5b45735d0e15df5b41d6594602d3de472262f7b75edc6cf5f3e3fa4e3ae4"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("d7bdbc680503a585925eec72d11fc99396f51855d0a03fce53c90bed4c2e319f"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("292e2df59e3a22109200beed0cdc84b12e66793e").to_dynamic(), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").to_dynamic()}}); // Same for hash256 Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {}); - Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}}); + Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"), {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{ParseHex("ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588").to_dynamic(), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").to_dynamic()}}); // Can have a Miniscript expression under tr() if it's alone. Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV),s:pk(Kz3iCBy3HNGP5CZWDsAMmnCMFNwqdDohudVN9fvkrN7tAkzKNtM7),adv:older(42)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766),adv:older(42)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766),adv:older(42)))", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"512033982eebe204dc66508e4b19cfc31b5ffc6e1bfcbf6e5597dfc2521a52270795"}}, OutputType::BECH32M); // Can have a pkh() expression alone as tr() script path (because pkh() is valid Miniscript). @@ -643,7 +643,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test) // Can sign for a Miniscript expression containing a hash challenge inside a Taproot tree. (Fails without the // preimages and the sequence, passes with.) Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,KztMyyi1pXUtuZfJSB7JzVdmJMAz7wfGVFoSRUR5CVZxXxULXuGR)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE | SIGNABLE_FAILS, {{"51209a3d79db56fbe3ba4d905d827b62e1ed31cd6df1198b8c759d589c0f4efc27bd"}}, OutputType::BECH32M); - Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,KztMyyi1pXUtuZfJSB7JzVdmJMAz7wfGVFoSRUR5CVZxXxULXuGR)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"51209a3d79db56fbe3ba4d905d827b62e1ed31cd6df1198b8c759d589c0f4efc27bd"}}, OutputType::BECH32M, /*op_desc_id=*/{}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/42, /*preimages=*/{{ParseHex("ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}}); + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(KykUPmR5967F4URzMUeCv9kNMU9CNRWycrPmx3ZvfkWoQLabbimL)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,KztMyyi1pXUtuZfJSB7JzVdmJMAz7wfGVFoSRUR5CVZxXxULXuGR)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{and_v(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),v:pk(1c9bc926084382e76da33b5a52d17b1fa153c072aae5fb5228ecc2ccf89d79d5)),older(42)),multi_a(2,adf586a32ad4b0674a86022b000348b681b4c97a811f67eefe4a6e066e55080c,14fa4ad085cdee1e2fc73d491b36a96c192382b1d9a21108eb3533f630364f9f)})", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"51209a3d79db56fbe3ba4d905d827b62e1ed31cd6df1198b8c759d589c0f4efc27bd"}}, OutputType::BECH32M, /*op_desc_id=*/{}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/42, /*preimages=*/{{ParseHex("ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588").to_dynamic(), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").to_dynamic()}}); // Basic sh(pkh()) with key origin CheckInferDescriptor("a9141a31ad23bf49c247dd531a623c2ef57da3c400c587", "sh(pkh([deadbeef/0h/0h/0]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", {"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}, {{"03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd", "deadbeef/0h/0h/0"}}); diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index b897a0a1539b86..f3c6c3def71904 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -338,7 +338,7 @@ BOOST_AUTO_TEST_CASE(key_ellswift) BOOST_AUTO_TEST_CASE(bip341_test_h) { - std::vector G_uncompressed = ParseHex("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"); + constexpr FixedVec G_uncompressed = ParseHex("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"); HashWriter hw; hw.write(MakeByteSpan(G_uncompressed)); XOnlyPubKey H{hw.GetSHA256()}; diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp index c99a4594cee348..39db5749dfc08e 100644 --- a/src/test/miniscript_tests.cpp +++ b/src/test/miniscript_tests.cpp @@ -642,12 +642,12 @@ BOOST_AUTO_TEST_CASE(fixed_tests) // Misc unit tests // A Script with a non minimal push is invalid - std::vector nonminpush = ParseHex("0000210232780000feff00ffffffffffff21ff005f00ae21ae00000000060602060406564c2102320000060900fe00005f00ae21ae00100000060606060606000000000000000000000000000000000000000000000000000000000000000000"); + constexpr FixedVec nonminpush = ParseHex("0000210232780000feff00ffffffffffff21ff005f00ae21ae00000000060602060406564c2102320000060900fe00005f00ae21ae00100000060606060606000000000000000000000000000000000000000000000000000000000000000000"); const CScript nonminpush_script(nonminpush.begin(), nonminpush.end()); BOOST_CHECK(miniscript::FromScript(nonminpush_script, wsh_converter) == nullptr); BOOST_CHECK(miniscript::FromScript(nonminpush_script, tap_converter) == nullptr); // A non-minimal VERIFY ( CHECKSIG VERIFY 1) - std::vector nonminverify = ParseHex("2103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ac6951"); + constexpr FixedVec nonminverify = ParseHex("2103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ac6951"); const CScript nonminverify_script(nonminverify.begin(), nonminverify.end()); BOOST_CHECK(miniscript::FromScript(nonminverify_script, wsh_converter) == nullptr); BOOST_CHECK(miniscript::FromScript(nonminverify_script, tap_converter) == nullptr); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 5654c8b0a84aab..000e4be216be95 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -145,56 +145,65 @@ static const unsigned char ParseHex_expected[65] = { }; BOOST_AUTO_TEST_CASE(parse_hex) { + // Forces compile time evaluation so we use the correct ParseHex() overload. + auto compile_time = [] (auto input) consteval { + return input; + }; + std::vector result; std::vector expected(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected)); // Basic test vector - result = ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"); + result = compile_time(ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f")).to_dynamic(); + BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); + result = ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"sv); BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); result = TryParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").value(); BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); // Spaces between bytes must be supported - result = ParseHex("12 34 56 78"); - BOOST_CHECK(result.size() == 4 && result[0] == 0x12 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); - result = TryParseHex("12 34 56 78").value(); - BOOST_CHECK(result.size() == 4 && result[0] == 0x12 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); + BOOST_CHECK(std::vector({0x12, 0x34, 0x56, 0x78}) == compile_time(ParseHex("12 34 56 78"))); + BOOST_CHECK(std::vector({0x12, 0x34, 0x56, 0x78}) == ParseHex("12 34 56 78"sv)); + BOOST_CHECK(std::vector({0x12, 0x34, 0x56, 0x78}) == TryParseHex("12 34 56 78").value()); // Leading space must be supported (used in BerkeleyEnvironment::Salvage) - result = ParseHex(" 89 34 56 78"); - BOOST_CHECK(result.size() == 4 && result[0] == 0x89 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); - result = TryParseHex(" 89 34 56 78").value(); - BOOST_CHECK(result.size() == 4 && result[0] == 0x89 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); + BOOST_CHECK(std::vector({0x89, 0x34, 0x56, 0x78}) == compile_time(ParseHex(" 89 34 56 78"))); + BOOST_CHECK(std::vector({0x89, 0x34, 0x56, 0x78}) == ParseHex(" 89 34 56 78"sv)); + BOOST_CHECK(std::vector({0x89, 0x34, 0x56, 0x78}) == TryParseHex(" 89 34 56 78").value()); // Mixed case and spaces are supported - result = ParseHex(" Ff aA "); - BOOST_CHECK(result.size() == 2 && result[0] == 0xff && result[1] == 0xaa); - result = TryParseHex(" Ff aA ").value(); - BOOST_CHECK(result.size() == 2 && result[0] == 0xff && result[1] == 0xaa); + BOOST_CHECK(std::vector({0xff, 0xaa}) == compile_time(ParseHex(" Ff aA "))); + BOOST_CHECK(std::vector({0xff, 0xaa}) == ParseHex(" Ff aA "sv)); + BOOST_CHECK(std::vector({0xff, 0xaa}) == TryParseHex(" Ff aA ").value()); // Empty string is supported - result = ParseHex(""); - BOOST_CHECK(result.size() == 0); - result = TryParseHex("").value(); - BOOST_CHECK(result.size() == 0); + BOOST_CHECK(compile_time(ParseHex("")).size() == 0); + BOOST_CHECK(ParseHex(""sv).size() == 0); + BOOST_CHECK(TryParseHex("").value().size() == 0); // Spaces between nibbles is treated as invalid - BOOST_CHECK_EQUAL(ParseHex("AAF F").size(), 0); + BOOST_CHECK_EQUAL(compile_time(ParseHex("AAF F")).size(), 0); + BOOST_CHECK_EQUAL(ParseHex("AAF F"sv).size(), 0); BOOST_CHECK(!TryParseHex("AAF F").has_value()); // Embedded null is treated as invalid - const std::string with_embedded_null{" 11 "s - " \0 " - " 22 "s}; - BOOST_CHECK_EQUAL(with_embedded_null.size(), 11); - BOOST_CHECK_EQUAL(ParseHex(with_embedded_null).size(), 0); - BOOST_CHECK(!TryParseHex(with_embedded_null).has_value()); + constexpr char with_embedded_null[] = " 11 " + " \0 " + " 22 "; + const std::string_view with_embedded_null_str(with_embedded_null, 11); + BOOST_CHECK_EQUAL(sizeof(with_embedded_null), 12); + BOOST_CHECK_EQUAL(with_embedded_null_str.size(), 11); + BOOST_CHECK_EQUAL(compile_time(ParseHex(with_embedded_null)).size(), 0); + BOOST_CHECK_EQUAL(ParseHex(with_embedded_null_str).size(), 0); + BOOST_CHECK(!TryParseHex(with_embedded_null_str).has_value()); // Non-hex is treated as invalid - BOOST_CHECK_EQUAL(ParseHex("1234 invalid 1234").size(), 0); + BOOST_CHECK_EQUAL(compile_time(ParseHex("1234 invalid 1234")).size(), 0); + BOOST_CHECK_EQUAL(ParseHex("1234 invalid 1234"sv).size(), 0); BOOST_CHECK(!TryParseHex("1234 invalid 1234").has_value()); // Truncated input is treated as invalid - BOOST_CHECK_EQUAL(ParseHex("12 3").size(), 0); + BOOST_CHECK_EQUAL(compile_time(ParseHex("12 3")).size(), 0); + BOOST_CHECK_EQUAL(ParseHex("12 3"sv).size(), 0); BOOST_CHECK(!TryParseHex("12 3").has_value()); } diff --git a/src/util/strencodings.h b/src/util/strencodings.h index e5c2d3ddf2ddf6..8e8a0b2d670314 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -67,6 +68,111 @@ std::vector ParseHex(std::string_view hex_str) { return TryParseHex(hex_str).value_or(std::vector{}); } + +namespace fixed_vec_detail { + +// Enables support FixedVec, inspired by std::array. +template +struct Inner +{ + using Data = T[N]; +}; +template +struct Inner +{ + struct Data + { + // Indexing is undefined. + [[noreturn]] constexpr T& operator[](size_t) const noexcept { std::exit(1); } + + // Conversion to a pointer produces a null pointer. + constexpr explicit operator T*() const noexcept { return nullptr; } + }; +}; + +} // namespace fixed_vec_detail + +/** + * Max size fixed at compile time. + * Created to be used on the border between runtime and compile time which a + * dynamically allocating std::vector cannot cross. + * The type was created in preference over std::array to be able to parse hex + * strings at compile time which may contain whitespace, making exact size() + * unknown ahead of template instantiation. + */ +template +class FixedVec +{ + typename fixed_vec_detail::Inner::Data m_data{}; + size_t m_size{0}; + +public: + constexpr FixedVec() = default; + constexpr const T* data() const { return (const T*)(m_data); } + constexpr size_t size() const { return m_size; } + constexpr const T* begin() const { return data(); } + constexpr const T* end() const { return data() + m_size; } + constexpr void push_back(const T& v) + { + assert(m_size < N); + m_data[m_size] = v; + ++m_size; + } + + /// Enables creation of a runtime allocated vector when necessary. + // NOT just a plain `operator std::vector()` since we want to expose + // allocations. + constexpr std::vector to_dynamic() const + { + return {begin(), end()}; + } +}; + +template +constexpr bool operator==(const std::vector& a, const FixedVec& b) +{ + return std::ranges::equal(a, b); +} + +constexpr inline bool IsSpace(char c) noexcept; +template +consteval FixedVec ParseHex(const char (&hex_str)[Size]) +{ + // Non lookup table version of HexDigit(). + auto from_hex = [](const uint8_t c) -> std::optional { + if (c >= '0' && c <= '9') + return std::make_optional(c - '0'); + else if (c >= 'a' && c <= 'f') + return std::make_optional(c - uint8_t{'a' - 0xA}); + else if (c >= 'A' && c <= 'F') + return std::make_optional(c - uint8_t{'A' - 0xA}); + else + return std::nullopt; + }; + + FixedVec rv; + size_t it = 0; + while (it < (Size - 1)) { // -1 - Assumes null-term at the end. + if (IsSpace(hex_str[it])) { + ++it; + continue; + } + auto c1 = from_hex(hex_str[it++]); + if (it >= Size) + return {}; + if (!c1.has_value()) + return {}; + auto c1_value = c1.value(); + c1_value <<= 4; + Byte elem{c1_value}; + auto c2 = from_hex(hex_str[it++]); + if (!c2.has_value()) + return {}; + elem |= Byte{c2.value()}; + rv.push_back(elem); + } + return rv; +} /* Returns true if each character in str is a hex character, and has an even * number of hex digits.*/ bool IsHex(std::string_view str); diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 57a19fb5f2b69c..36e438eb55d393 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -11,7 +11,7 @@ #include namespace wallet { -int CCrypter::BytesToKeySHA512AES(const std::vector& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const +int CCrypter::BytesToKeySHA512AES(std::span chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const { // This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc // cipher and sha512 message digest. Because sha512's output size (64b) is @@ -37,7 +37,7 @@ int CCrypter::BytesToKeySHA512AES(const std::vector& chSalt, cons return WALLET_CRYPTO_KEY_SIZE; } -bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) +bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, std::span chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) { if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) return false; @@ -87,7 +87,7 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext) const +bool CCrypter::Decrypt(std::span vchCiphertext, CKeyingMaterial& vchPlaintext) const { if (!fKeySet) return false; diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index b776a9c4979635..47cd60b251548b 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -75,12 +75,12 @@ friend class wallet_crypto_tests::TestCrypter; // for test access to chKey/chIV std::vector> vchIV; bool fKeySet; - int BytesToKeySHA512AES(const std::vector& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const; + int BytesToKeySHA512AES(std::span chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const; public: - bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod); + bool SetKeyFromPassphrase(const SecureString& strKeyData, std::span chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod); bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector &vchCiphertext) const; - bool Decrypt(const std::vector& vchCiphertext, CKeyingMaterial& vchPlaintext) const; + bool Decrypt(std::span vchCiphertext, CKeyingMaterial& vchPlaintext) const; bool SetKey(const CKeyingMaterial& chNewKey, const std::vector& chNewIV); void CleanKey() diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp index d5e75bb892fae5..b505a761ee706b 100644 --- a/src/wallet/test/wallet_crypto_tests.cpp +++ b/src/wallet/test/wallet_crypto_tests.cpp @@ -17,9 +17,8 @@ BOOST_FIXTURE_TEST_SUITE(wallet_crypto_tests, BasicTestingSetup) class TestCrypter { public: -static void TestPassphraseSingle(const std::vector& vchSalt, const SecureString& passphrase, uint32_t rounds, - const std::vector& correctKey = std::vector(), - const std::vector& correctIV=std::vector()) +static void TestPassphraseSingle(std::span vchSalt, const SecureString& passphrase, uint32_t rounds, + std::span correctKey = {}, std::span correctIV = {}) { CCrypter crypt; crypt.SetKeyFromPassphrase(passphrase, vchSalt, rounds, 0); @@ -32,16 +31,15 @@ static void TestPassphraseSingle(const std::vector& vchSalt, cons HexStr(crypt.vchIV) + std::string(" != ") + HexStr(correctIV)); } -static void TestPassphrase(const std::vector& vchSalt, const SecureString& passphrase, uint32_t rounds, - const std::vector& correctKey = std::vector(), - const std::vector& correctIV=std::vector()) +static void TestPassphrase(std::span vchSalt, const SecureString& passphrase, uint32_t rounds, + std::span correctKey = {}, std::span correctIV = {}) { TestPassphraseSingle(vchSalt, passphrase, rounds, correctKey, correctIV); for(SecureString::const_iterator i(passphrase.begin()); i != passphrase.end(); ++i) TestPassphraseSingle(vchSalt, SecureString(i, passphrase.end()), rounds); } -static void TestDecrypt(const CCrypter& crypt, const std::vector& vchCiphertext, \ +static void TestDecrypt(const CCrypter& crypt, std::span vchCiphertext, \ const std::vector& vchPlaintext = std::vector()) { CKeyingMaterial vchDecrypted; @@ -63,11 +61,11 @@ static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& vchP TestDecrypt(crypt, vchCiphertext, vchPlaintext2); } -static void TestEncrypt(const CCrypter& crypt, const std::vector& vchPlaintextIn, \ +static void TestEncrypt(const CCrypter& crypt, std::span vchPlaintextIn, \ const std::vector& vchCiphertextCorrect = std::vector()) { TestEncryptSingle(crypt, CKeyingMaterial(vchPlaintextIn.begin(), vchPlaintextIn.end()), vchCiphertextCorrect); - for(std::vector::const_iterator i(vchPlaintextIn.begin()); i != vchPlaintextIn.end(); ++i) + for (auto i{vchPlaintextIn.begin()}; i != vchPlaintextIn.end(); ++i) TestEncryptSingle(crypt, CKeyingMaterial(i, vchPlaintextIn.end())); } @@ -90,7 +88,7 @@ BOOST_AUTO_TEST_CASE(passphrase) { } BOOST_AUTO_TEST_CASE(encrypt) { - std::vector vchSalt = ParseHex("0000deadbeef0000"); + constexpr FixedVec vchSalt = ParseHex("0000deadbeef0000"); BOOST_CHECK(vchSalt.size() == WALLET_CRYPTO_SALT_SIZE); CCrypter crypt; crypt.SetKeyFromPassphrase("passphrase", vchSalt, 25000, 0); @@ -105,7 +103,7 @@ BOOST_AUTO_TEST_CASE(encrypt) { } BOOST_AUTO_TEST_CASE(decrypt) { - std::vector vchSalt = ParseHex("0000deadbeef0000"); + constexpr FixedVec vchSalt = ParseHex("0000deadbeef0000"); BOOST_CHECK(vchSalt.size() == WALLET_CRYPTO_SALT_SIZE); CCrypter crypt; crypt.SetKeyFromPassphrase("passphrase", vchSalt, 25000, 0);