diff options
Diffstat (limited to 'src')
299 files changed, 7740 insertions, 4521 deletions
diff --git a/src/.bear-tidy-config b/src/.bear-tidy-config index 111ef6ee44..9b6b9d2cf7 100644 --- a/src/.bear-tidy-config +++ b/src/.bear-tidy-config @@ -4,7 +4,11 @@ "include_only_existing_source": true, "paths_to_include": [], "paths_to_exclude": [ - "src/leveldb" + "src/crc32c", + "src/crypto/ctaes", + "src/leveldb", + "src/minisketch", + "src/secp256k1" ] }, "format": { diff --git a/src/.clang-tidy b/src/.clang-tidy index 39566c0cb7..84c9d5fb3a 100644 --- a/src/.clang-tidy +++ b/src/.clang-tidy @@ -4,6 +4,7 @@ bugprone-argument-comment, bugprone-use-after-move, misc-unused-using-decls, modernize-use-default-member-init, +modernize-use-noexcept, modernize-use-nullptr, performance-*, -performance-inefficient-string-concatenation, diff --git a/src/Makefile.am b/src/Makefile.am index b4ff556eb6..dfea7146aa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -205,6 +205,7 @@ BITCOIN_CORE_H = \ netbase.h \ netgroup.h \ netmessagemaker.h \ + node/abort.h \ node/blockmanager_args.h \ node/blockstorage.h \ node/caches.h \ @@ -223,6 +224,7 @@ BITCOIN_CORE_H = \ node/miner.h \ node/mini_miner.h \ node/minisketchwrapper.h \ + node/peerman_args.h \ node/psbt.h \ node/transaction.h \ node/txreconciliation.h \ @@ -310,10 +312,10 @@ BITCOIN_CORE_H = \ util/readwritefile.h \ util/result.h \ util/serfloat.h \ + util/signalinterrupt.h \ util/sock.h \ util/spanparsing.h \ util/string.h \ - util/syscall_sandbox.h \ util/syserror.h \ util/thread.h \ util/threadinterrupt.h \ @@ -400,6 +402,7 @@ libbitcoin_node_a_SOURCES = \ net.cpp \ net_processing.cpp \ netgroup.cpp \ + node/abort.cpp \ node/blockmanager_args.cpp \ node/blockstorage.cpp \ node/caches.cpp \ @@ -419,6 +422,7 @@ libbitcoin_node_a_SOURCES = \ node/miner.cpp \ node/mini_miner.cpp \ node/minisketchwrapper.cpp \ + node/peerman_args.cpp \ node/psbt.cpp \ node/transaction.cpp \ node/txreconciliation.cpp \ @@ -734,6 +738,7 @@ libbitcoin_util_a_SOURCES = \ util/moneystr.cpp \ util/rbf.cpp \ util/readwritefile.cpp \ + util/signalinterrupt.cpp \ util/thread.cpp \ util/threadinterrupt.cpp \ util/threadnames.cpp \ @@ -741,7 +746,6 @@ libbitcoin_util_a_SOURCES = \ util/spanparsing.cpp \ util/strencodings.cpp \ util/string.cpp \ - util/syscall_sandbox.cpp \ util/time.cpp \ util/tokenpipe.cpp \ $(BITCOIN_CORE_H) @@ -894,8 +898,8 @@ if BUILD_BITCOIN_KERNEL_LIB lib_LTLIBRARIES += $(LIBBITCOINKERNEL) libbitcoinkernel_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) $(PTHREAD_FLAGS) -libbitcoinkernel_la_LIBADD = $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) $(LIBSECP256K1) -libbitcoinkernel_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) +libbitcoinkernel_la_LIBADD = $(LIBBITCOIN_CRYPTO) $(LIBLEVELDB) $(LIBMEMENV) $(LIBSECP256K1) +libbitcoinkernel_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) # libbitcoinkernel requires default symbol visibility, explicitly specify that # here so that things still work even when user configures with @@ -935,7 +939,6 @@ libbitcoinkernel_la_SOURCES = \ logging.cpp \ node/blockstorage.cpp \ node/chainstate.cpp \ - node/interface_ui.cpp \ node/utxo_snapshot.cpp \ policy/feerate.cpp \ policy/fees.cpp \ @@ -955,7 +958,6 @@ libbitcoinkernel_la_SOURCES = \ script/script_error.cpp \ script/sigcache.cpp \ script/standard.cpp \ - shutdown.cpp \ signet.cpp \ support/cleanse.cpp \ support/lockedpool.cpp \ @@ -974,9 +976,9 @@ libbitcoinkernel_la_SOURCES = \ util/moneystr.cpp \ util/rbf.cpp \ util/serfloat.cpp \ + util/signalinterrupt.cpp \ util/strencodings.cpp \ util/string.cpp \ - util/syscall_sandbox.cpp \ util/syserror.cpp \ util/thread.cpp \ util/threadnames.cpp \ diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index c8e510b482..10c8389c80 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -18,6 +18,7 @@ bench_bench_bitcoin_SOURCES = \ bench/bench.cpp \ bench/bench.h \ bench/bench_bitcoin.cpp \ + bench/bip324_ecdh.cpp \ bench/block_assemble.cpp \ bench/ccoins_caching.cpp \ bench/chacha20.cpp \ @@ -29,6 +30,7 @@ bench_bench_bitcoin_SOURCES = \ bench/data.h \ bench/descriptors.cpp \ bench/duplicate_inputs.cpp \ + bench/ellswift.cpp \ bench/examples.cpp \ bench/gcs_filter.cpp \ bench/hashpadding.cpp \ diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include index 11b93ad13e..6a1fd712bd 100644 --- a/src/Makefile.test_util.include +++ b/src/Makefile.test_util.include @@ -11,6 +11,7 @@ TEST_UTIL_H = \ test/util/blockfilter.h \ test/util/chainstate.h \ test/util/coins.h \ + test/util/index.h \ test/util/json.h \ test/util/logging.h \ test/util/mining.h \ @@ -34,10 +35,12 @@ libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libtest_util_a_SOURCES = \ test/util/blockfilter.cpp \ test/util/coins.cpp \ + test/util/index.cpp \ test/util/json.cpp \ test/util/logging.cpp \ test/util/mining.cpp \ test/util/net.cpp \ + test/util/random.cpp \ test/util/script.cpp \ test/util/setup_common.cpp \ test/util/str.cpp \ diff --git a/src/addrdb.cpp b/src/addrdb.cpp index cb1c49050e..0fcb5ed5c9 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -210,7 +210,7 @@ util::Result<std::unique_ptr<AddrMan>> LoadAddrman(const NetGroupManager& netgro return util::Error{strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."), e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))}; } - return {std::move(addrman)}; // std::move should be unneccessary but is temporarily needed to work around clang bug + return {std::move(addrman)}; // std::move should be unnecessary but is temporarily needed to work around clang bug // (https://github.com/bitcoin/bitcoin/pull/25977#issuecomment-1561270092) } diff --git a/src/addrman.cpp b/src/addrman.cpp index cdfd079fcd..19a4f4fa63 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -757,16 +757,14 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::option // Iterate over the positions of that bucket, starting at the initial one, // and looping around. - int i; + int i, position, node_id; for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) { - int position = (initial_position + i) % ADDRMAN_BUCKET_SIZE; - int node_id = GetEntry(search_tried, bucket, position); + position = (initial_position + i) % ADDRMAN_BUCKET_SIZE; + node_id = GetEntry(search_tried, bucket, position); if (node_id != -1) { if (network.has_value()) { const auto it{mapInfo.find(node_id)}; - assert(it != mapInfo.end()); - const auto info{it->second}; - if (info.GetNetwork() == *network) break; + if (Assume(it != mapInfo.end()) && it->second.GetNetwork() == *network) break; } else { break; } @@ -777,9 +775,7 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::option if (i == ADDRMAN_BUCKET_SIZE) continue; // Find the entry to return. - int position = (initial_position + i) % ADDRMAN_BUCKET_SIZE; - int nId = GetEntry(search_tried, bucket, position); - const auto it_found{mapInfo.find(nId)}; + const auto it_found{mapInfo.find(node_id)}; assert(it_found != mapInfo.end()); const AddrInfo& info{it_found->second}; @@ -798,15 +794,17 @@ int AddrManImpl::GetEntry(bool use_tried, size_t bucket, size_t position) const { AssertLockHeld(cs); - assert(position < ADDRMAN_BUCKET_SIZE); - if (use_tried) { - assert(bucket < ADDRMAN_TRIED_BUCKET_COUNT); - return vvTried[bucket][position]; + if (Assume(position < ADDRMAN_BUCKET_SIZE) && Assume(bucket < ADDRMAN_TRIED_BUCKET_COUNT)) { + return vvTried[bucket][position]; + } } else { - assert(bucket < ADDRMAN_NEW_BUCKET_COUNT); - return vvNew[bucket][position]; + if (Assume(position < ADDRMAN_BUCKET_SIZE) && Assume(bucket < ADDRMAN_NEW_BUCKET_COUNT)) { + return vvNew[bucket][position]; + } } + + return -1; } std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const diff --git a/src/addrman.h b/src/addrman.h index 6284b80a52..f41687dcff 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -148,8 +148,10 @@ public: * * @param[in] new_only Whether to only select addresses from the new table. Passing `true` returns * an address from the new table or an empty pair. Passing `false` will return an - * address from either the new or tried table (it does not guarantee a tried entry). - * @param[in] network Select only addresses of this network (nullopt = all) + * empty pair or an address from either the new or tried table (it does not + * guarantee a tried entry). + * @param[in] network Select only addresses of this network (nullopt = all). Passing a network may + * slow down the search. * @return CAddress The record for the selected peer. * seconds The last time we attempted to connect to that peer. */ diff --git a/src/addrman_impl.h b/src/addrman_impl.h index 7aead2812b..9aff408e34 100644 --- a/src/addrman_impl.h +++ b/src/addrman_impl.h @@ -255,7 +255,7 @@ private: /** Helper to generalize looking up an addrman entry from either table. * - * @return int The nid of the entry or -1 if the addrman position is empty. + * @return int The nid of the entry. If the addrman position is empty or not found, returns -1. * */ int GetEntry(bool use_tried, size_t bucket, size_t position) const EXCLUSIVE_LOCKS_REQUIRED(cs); diff --git a/src/bench/bip324_ecdh.cpp b/src/bench/bip324_ecdh.cpp new file mode 100644 index 0000000000..659da0f08e --- /dev/null +++ b/src/bench/bip324_ecdh.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <bench/bench.h> + +#include <key.h> +#include <pubkey.h> +#include <random.h> +#include <span.h> + +#include <array> +#include <cstddef> + +static void BIP324_ECDH(benchmark::Bench& bench) +{ + ECC_Start(); + FastRandomContext rng; + + std::array<std::byte, 32> key_data; + std::array<std::byte, EllSwiftPubKey::size()> our_ellswift_data; + std::array<std::byte, EllSwiftPubKey::size()> their_ellswift_data; + + rng.fillrand(key_data); + rng.fillrand(our_ellswift_data); + rng.fillrand(their_ellswift_data); + + bench.batch(1).unit("ecdh").run([&] { + CKey key; + key.Set(UCharCast(key_data.data()), UCharCast(key_data.data()) + 32, true); + EllSwiftPubKey our_ellswift(our_ellswift_data); + EllSwiftPubKey their_ellswift(their_ellswift_data); + + auto ret = key.ComputeBIP324ECDHSecret(their_ellswift, our_ellswift, true); + + // To make sure that the computation is not the same on every iteration (ellswift decoding + // is variable-time), distribute bytes from the shared secret over the 3 inputs. The most + // important one is their_ellswift, because that one is actually decoded, so it's given most + // bytes. The data is copied into the middle, so that both halves are affected: + // - Copy 8 bytes from the resulting shared secret into middle of the private key. + std::copy(ret.begin(), ret.begin() + 8, key_data.begin() + 12); + // - Copy 8 bytes from the resulting shared secret into the middle of our ellswift key. + std::copy(ret.begin() + 8, ret.begin() + 16, our_ellswift_data.begin() + 28); + // - Copy 16 bytes from the resulting shared secret into the middle of their ellswift key. + std::copy(ret.begin() + 16, ret.end(), their_ellswift_data.begin() + 24); + }); + + ECC_Stop(); +} + +BENCHMARK(BIP324_ECDH, benchmark::PriorityLevel::HIGH); diff --git a/src/bench/chacha20.cpp b/src/bench/chacha20.cpp index 115cd064bd..3b57e29f39 100644 --- a/src/bench/chacha20.cpp +++ b/src/bench/chacha20.cpp @@ -15,8 +15,7 @@ static void CHACHA20(benchmark::Bench& bench, size_t buffersize) { std::vector<uint8_t> key(32,0); ChaCha20 ctx(key.data()); - ctx.SetIV(0); - ctx.Seek64(0); + ctx.Seek64({0, 0}, 0); std::vector<uint8_t> in(buffersize,0); std::vector<uint8_t> out(buffersize,0); bench.batch(in.size()).unit("byte").run([&] { diff --git a/src/bench/chacha_poly_aead.cpp b/src/bench/chacha_poly_aead.cpp index db88841c32..9149eb683a 100644 --- a/src/bench/chacha_poly_aead.cpp +++ b/src/bench/chacha_poly_aead.cpp @@ -23,8 +23,8 @@ static ChaCha20Poly1305AEAD aead(k1, 32, k2, 32); static void CHACHA20_POLY1305_AEAD(benchmark::Bench& bench, size_t buffersize, bool include_decryption) { - std::vector<unsigned char> in(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - std::vector<unsigned char> out(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + std::vector<unsigned char> in(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); + std::vector<unsigned char> out(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); uint64_t seqnr_payload = 0; uint64_t seqnr_aad = 0; int aad_pos = 0; diff --git a/src/bench/ellswift.cpp b/src/bench/ellswift.cpp new file mode 100644 index 0000000000..f0348421b3 --- /dev/null +++ b/src/bench/ellswift.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2022-2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <bench/bench.h> + +#include <key.h> +#include <random.h> + +static void EllSwiftCreate(benchmark::Bench& bench) +{ + ECC_Start(); + + CKey key; + key.MakeNewKey(true); + + uint256 entropy = GetRandHash(); + + bench.batch(1).unit("pubkey").run([&] { + auto ret = key.EllSwiftCreate(MakeByteSpan(entropy)); + /* Use the first 32 bytes of the ellswift encoded public key as next private key. */ + key.Set(UCharCast(ret.data()), UCharCast(ret.data()) + 32, true); + assert(key.IsValid()); + /* Use the last 32 bytes of the ellswift encoded public key as next entropy. */ + std::copy(ret.begin() + 32, ret.begin() + 64, MakeWritableByteSpan(entropy).begin()); + }); + + ECC_Stop(); +} + +BENCHMARK(EllSwiftCreate, benchmark::PriorityLevel::HIGH); diff --git a/src/bench/load_external.cpp b/src/bench/load_external.cpp index 2ff72a3012..1378a7b20a 100644 --- a/src/bench/load_external.cpp +++ b/src/bench/load_external.cpp @@ -34,7 +34,7 @@ static void LoadExternalBlockFile(benchmark::Bench& bench) ss << static_cast<uint32_t>(benchmark::data::block413567.size()); // We can't use the streaming serialization (ss << benchmark::data::block413567) // because that first writes a compact size. - ss.write(MakeByteSpan(benchmark::data::block413567)); + ss << Span{benchmark::data::block413567}; // Create the test file. { diff --git a/src/bench/poly1305.cpp b/src/bench/poly1305.cpp index f7d17dfa96..be0e5f2c2d 100644 --- a/src/bench/poly1305.cpp +++ b/src/bench/poly1305.cpp @@ -6,6 +6,8 @@ #include <bench/bench.h> #include <crypto/poly1305.h> +#include <span.h> + /* Number of bytes to process per iteration */ static constexpr uint64_t BUFFER_SIZE_TINY = 64; static constexpr uint64_t BUFFER_SIZE_SMALL = 256; @@ -13,11 +15,11 @@ static constexpr uint64_t BUFFER_SIZE_LARGE = 1024*1024; static void POLY1305(benchmark::Bench& bench, size_t buffersize) { - std::vector<unsigned char> tag(POLY1305_TAGLEN, 0); - std::vector<unsigned char> key(POLY1305_KEYLEN, 0); - std::vector<unsigned char> in(buffersize, 0); + std::vector<std::byte> tag(Poly1305::TAGLEN, {}); + std::vector<std::byte> key(Poly1305::KEYLEN, {}); + std::vector<std::byte> in(buffersize, {}); bench.batch(in.size()).unit("byte").run([&] { - poly1305_auth(tag.data(), in.data(), in.size(), key.data()); + Poly1305{key}.Update(in).Finalize(tag); }); } diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp index 59c4af086e..2524e215e4 100644 --- a/src/bench/prevector.cpp +++ b/src/bench/prevector.cpp @@ -80,6 +80,30 @@ static void PrevectorDeserialize(benchmark::Bench& bench) }); } +template <typename T> +static void PrevectorFillVectorDirect(benchmark::Bench& bench) +{ + bench.run([&] { + std::vector<prevector<28, T>> vec; + for (size_t i = 0; i < 260; ++i) { + vec.emplace_back(); + } + }); +} + + +template <typename T> +static void PrevectorFillVectorIndirect(benchmark::Bench& bench) +{ + bench.run([&] { + std::vector<prevector<28, T>> vec; + for (size_t i = 0; i < 260; ++i) { + // force allocation + vec.emplace_back(29, T{}); + } + }); +} + #define PREVECTOR_TEST(name) \ static void Prevector##name##Nontrivial(benchmark::Bench& bench) \ { \ @@ -96,3 +120,5 @@ PREVECTOR_TEST(Clear) PREVECTOR_TEST(Destructor) PREVECTOR_TEST(Resize) PREVECTOR_TEST(Deserialize) +PREVECTOR_TEST(FillVectorDirect) +PREVECTOR_TEST(FillVectorIndirect) diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 432bdc8e33..580a6badd6 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -81,9 +81,10 @@ int main(int argc, char* argv[]) class KernelNotifications : public kernel::Notifications { public: - void blockTip(SynchronizationState, CBlockIndex&) override + kernel::InterruptResult blockTip(SynchronizationState, CBlockIndex&) override { std::cout << "Block tip changed" << std::endl; + return {}; } void headerTip(SynchronizationState, int64_t height, int64_t timestamp, bool presync) override { @@ -97,6 +98,15 @@ int main(int argc, char* argv[]) { std::cout << "Warning: " << warning.original << std::endl; } + void flushError(const std::string& debug_message) override + { + std::cerr << "Error flushing block data to disk: " << debug_message << std::endl; + } + void fatalError(const std::string& debug_message, const bilingual_str& user_message) override + { + std::cerr << "Error: " << debug_message << std::endl; + std::cerr << (user_message.empty() ? "A fatal internal error occurred." : user_message.original) << std::endl; + } }; auto notifications = std::make_unique<KernelNotifications>(); @@ -112,8 +122,9 @@ int main(int argc, char* argv[]) const node::BlockManager::Options blockman_opts{ .chainparams = chainman_opts.chainparams, .blocks_dir = abs_datadir / "blocks", + .notifications = chainman_opts.notifications, }; - ChainstateManager chainman{chainman_opts, blockman_opts}; + ChainstateManager chainman{kernel_context.interrupt, chainman_opts, blockman_opts}; node::CacheSizes cache_sizes; cache_sizes.block_tree_db = 2 << 20; @@ -277,7 +288,7 @@ epilogue: // Without this precise shutdown sequence, there will be a lot of nullptr // dereferencing and UB. scheduler.stop(); - if (chainman.m_load_block.joinable()) chainman.m_load_block.join(); + if (chainman.m_thread_load.joinable()) chainman.m_thread_load.join(); StopScriptCheckWorkerThreads(); GetMainSignals().FlushBackgroundCallbacks(); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 0c25ddf373..103d8885db 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -562,6 +562,16 @@ static CAmount AmountFromValue(const UniValue& value) return amount; } +static std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName) +{ + std::string strHex; + if (v.isStr()) + strHex = v.getValStr(); + if (!IsHex(strHex)) + throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')"); + return ParseHex(strHex); +} + static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) { int nHashType = SIGHASH_ALL; diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index c561f9aa14..e2224befef 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -24,7 +24,6 @@ #include <util/check.h> #include <util/exception.h> #include <util/strencodings.h> -#include <util/syscall_sandbox.h> #include <util/syserror.h> #include <util/threadnames.h> #include <util/tokenpipe.h> @@ -242,7 +241,6 @@ static bool AppInit(NodeContext& node) daemon_ep.Close(); } #endif - SetSyscallSandboxPolicy(SyscallSandboxPolicy::SHUTOFF); return fRet; } diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp index 88c7526b9e..2f465f1119 100644 --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -27,7 +27,7 @@ static const std::map<BlockFilterType, std::string> g_filter_types = { uint64_t GCSFilter::HashToRange(const Element& element) const { uint64_t hash = CSipHasher(m_params.m_siphash_k0, m_params.m_siphash_k1) - .Write(element.data(), element.size()) + .Write(element) .Finalize(); return FastRange64(hash, m_F); } diff --git a/src/checkqueue.h b/src/checkqueue.h index 2c21e5f7d0..a3299fb3fe 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -7,7 +7,6 @@ #include <sync.h> #include <tinyformat.h> -#include <util/syscall_sandbox.h> #include <util/threadnames.h> #include <algorithm> @@ -149,7 +148,6 @@ public: for (int n = 0; n < threads_num; ++n) { m_worker_threads.emplace_back([this, n]() { util::ThreadRename(strprintf("scriptch.%i", n)); - SetSyscallSandboxPolicy(SyscallSandboxPolicy::VALIDATION_SCRIPT_CHECK); Loop(false /* worker thread */); }); } diff --git a/src/common/run_command.cpp b/src/common/run_command.cpp index 6ad9f75b5d..8bd5febd53 100644 --- a/src/common/run_command.cpp +++ b/src/common/run_command.cpp @@ -12,16 +12,7 @@ #include <univalue.h> #ifdef ENABLE_EXTERNAL_SIGNER -#if defined(__GNUC__) -// Boost 1.78 requires the following workaround. -// See: https://github.com/boostorg/process/issues/235 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnarrowing" -#endif #include <boost/process.hpp> -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif #endif // ENABLE_EXTERNAL_SIGNER UniValue RunCommandParseJSON(const std::string& str_command, const std::string& str_std_in) diff --git a/src/core_io.h b/src/core_io.h index 997f3bfd5b..1f5ecbaea6 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -6,6 +6,7 @@ #define BITCOIN_CORE_IO_H #include <consensus/amount.h> +#include <util/result.h> #include <string> #include <vector> @@ -45,8 +46,7 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header); * @see ParseHashV for an RPC-oriented version of this */ bool ParseHashStr(const std::string& strHex, uint256& result); -std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName); -int ParseSighashString(const UniValue& sighash); +[[nodiscard]] util::Result<int> SighashFromStr(const std::string& sighash); // core_write.cpp UniValue ValueFromAmount(const CAmount amount); diff --git a/src/core_read.cpp b/src/core_read.cpp index 84cd559b7f..dfabf3a0c2 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -10,7 +10,7 @@ #include <script/sign.h> #include <serialize.h> #include <streams.h> -#include <univalue.h> +#include <util/result.h> #include <util/strencodings.h> #include <version.h> @@ -242,36 +242,21 @@ bool ParseHashStr(const std::string& strHex, uint256& result) return true; } -std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName) +util::Result<int> SighashFromStr(const std::string& sighash) { - std::string strHex; - if (v.isStr()) - strHex = v.getValStr(); - if (!IsHex(strHex)) - throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')"); - return ParseHex(strHex); -} - -int ParseSighashString(const UniValue& sighash) -{ - int hash_type = SIGHASH_DEFAULT; - if (!sighash.isNull()) { - static std::map<std::string, int> map_sighash_values = { - {std::string("DEFAULT"), int(SIGHASH_DEFAULT)}, - {std::string("ALL"), int(SIGHASH_ALL)}, - {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)}, - {std::string("NONE"), int(SIGHASH_NONE)}, - {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)}, - {std::string("SINGLE"), int(SIGHASH_SINGLE)}, - {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)}, - }; - const std::string& strHashType = sighash.get_str(); - const auto& it = map_sighash_values.find(strHashType); - if (it != map_sighash_values.end()) { - hash_type = it->second; - } else { - throw std::runtime_error(strHashType + " is not a valid sighash parameter."); - } + static std::map<std::string, int> map_sighash_values = { + {std::string("DEFAULT"), int(SIGHASH_DEFAULT)}, + {std::string("ALL"), int(SIGHASH_ALL)}, + {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)}, + {std::string("NONE"), int(SIGHASH_NONE)}, + {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)}, + {std::string("SINGLE"), int(SIGHASH_SINGLE)}, + {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)}, + }; + const auto& it = map_sighash_values.find(sighash); + if (it != map_sighash_values.end()) { + return it->second; + } else { + return util::Error{Untranslated(sighash + " is not a valid sighash parameter.")}; } - return hash_type; } diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp index 6934cef163..fafd783ab1 100644 --- a/src/crypto/chacha20.cpp +++ b/src/crypto/chacha20.cpp @@ -47,16 +47,12 @@ ChaCha20Aligned::ChaCha20Aligned(const unsigned char* key32) SetKey32(key32); } -void ChaCha20Aligned::SetIV(uint64_t iv) +void ChaCha20Aligned::Seek64(Nonce96 nonce, uint32_t block_counter) { - input[10] = iv; - input[11] = iv >> 32; -} - -void ChaCha20Aligned::Seek64(uint64_t pos) -{ - input[8] = pos; - input[9] = pos >> 32; + input[8] = block_counter; + input[9] = nonce.first; + input[10] = nonce.second; + input[11] = nonce.second >> 32; } inline void ChaCha20Aligned::Keystream64(unsigned char* c, size_t blocks) diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h index b286ef59fe..f2ec21d82e 100644 --- a/src/crypto/chacha20.h +++ b/src/crypto/chacha20.h @@ -7,9 +7,15 @@ #include <cstdlib> #include <stdint.h> +#include <utility> // classes for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein -// https://cr.yp.to/chacha/chacha-20080128.pdf */ +// https://cr.yp.to/chacha/chacha-20080128.pdf. +// +// The 128-bit input is here implemented as a 96-bit nonce and a 32-bit block +// counter, as in RFC8439 Section 2.3. When the 32-bit block counter overflows +// the first 32-bit part of the nonce is automatically incremented, making it +// conceptually compatible with variants that use a 64/64 split instead. /** ChaCha20 cipher that only operates on multiples of 64 bytes. */ class ChaCha20Aligned @@ -26,11 +32,22 @@ public: /** set 32-byte key. */ void SetKey32(const unsigned char* key32); - /** set the 64-bit nonce. */ - void SetIV(uint64_t iv); + /** Type for 96-bit nonces used by the Set function below. + * + * The first field corresponds to the LE32-encoded first 4 bytes of the nonce, also referred + * to as the '32-bit fixed-common part' in Example 2.8.2 of RFC8439. + * + * The second field corresponds to the LE64-encoded last 8 bytes of the nonce. + * + */ + using Nonce96 = std::pair<uint32_t, uint64_t>; - /** set the 64bit block counter (pos seeks to byte position 64*pos). */ - void Seek64(uint64_t pos); + /** Set the 96-bit nonce and 32-bit block counter. + * + * Block_counter selects a position to seek to (to byte 64*block_counter). After 256 GiB, the + * block counter overflows, and nonce.first is incremented. + */ + void Seek64(Nonce96 nonce, uint32_t block_counter); /** outputs the keystream of size <64*blocks> into <c> */ void Keystream64(unsigned char* c, size_t blocks); @@ -62,13 +79,13 @@ public: m_bufleft = 0; } - /** set the 64-bit nonce. */ - void SetIV(uint64_t iv) { m_aligned.SetIV(iv); } + /** 96-bit nonce type. */ + using Nonce96 = ChaCha20Aligned::Nonce96; - /** set the 64bit block counter (pos seeks to byte position 64*pos). */ - void Seek64(uint64_t pos) + /** Set the 96-bit nonce and 32-bit block counter. */ + void Seek64(Nonce96 nonce, uint32_t block_counter) { - m_aligned.Seek64(pos); + m_aligned.Seek64(nonce, block_counter); m_bufleft = 0; } diff --git a/src/crypto/chacha_poly_aead.cpp b/src/crypto/chacha_poly_aead.cpp index 119ad6902f..0d82cf3d74 100644 --- a/src/crypto/chacha_poly_aead.cpp +++ b/src/crypto/chacha_poly_aead.cpp @@ -50,43 +50,43 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int // check buffer boundaries if ( // if we encrypt, make sure the source contains at least the expected AAD and the destination has at least space for the source + MAC - (is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN || dest_len < src_len + POLY1305_TAGLEN)) || + (is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN || dest_len < src_len + Poly1305::TAGLEN)) || // if we decrypt, make sure the source contains at least the expected AAD+MAC and the destination has at least space for the source - MAC - (!is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN || dest_len < src_len - POLY1305_TAGLEN))) { + (!is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN || dest_len < src_len - Poly1305::TAGLEN))) { return false; } - unsigned char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; + unsigned char expected_tag[Poly1305::TAGLEN], poly_key[Poly1305::KEYLEN]; memset(poly_key, 0, sizeof(poly_key)); - m_chacha_main.SetIV(seqnr_payload); // block counter 0 for the poly1305 key // use lower 32bytes for the poly1305 key // (throws away 32 unused bytes (upper 32) from this ChaCha20 round) - m_chacha_main.Seek64(0); + m_chacha_main.Seek64({0, seqnr_payload}, 0); m_chacha_main.Crypt(poly_key, poly_key, sizeof(poly_key)); // if decrypting, verify the tag prior to decryption if (!is_encrypt) { - const unsigned char* tag = src + src_len - POLY1305_TAGLEN; - poly1305_auth(expected_tag, src, src_len - POLY1305_TAGLEN, poly_key); + const unsigned char* tag = src + src_len - Poly1305::TAGLEN; + Poly1305{MakeByteSpan(poly_key)} + .Update(AsBytes(Span{src, src_len - Poly1305::TAGLEN})) + .Finalize(MakeWritableByteSpan(expected_tag)); // constant time compare the calculated MAC with the provided MAC - if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) { + if (timingsafe_bcmp(expected_tag, tag, Poly1305::TAGLEN) != 0) { memory_cleanse(expected_tag, sizeof(expected_tag)); memory_cleanse(poly_key, sizeof(poly_key)); return false; } memory_cleanse(expected_tag, sizeof(expected_tag)); // MAC has been successfully verified, make sure we don't convert it in decryption - src_len -= POLY1305_TAGLEN; + src_len -= Poly1305::TAGLEN; } // calculate and cache the next 64byte keystream block if requested sequence number is not yet the cache if (m_cached_aad_seqnr != seqnr_aad) { m_cached_aad_seqnr = seqnr_aad; - m_chacha_header.SetIV(seqnr_aad); - m_chacha_header.Seek64(0); + m_chacha_header.Seek64({0, seqnr_aad}, 0); m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); } // crypt the AAD (3 bytes message length) with given position in AAD cipher instance keystream @@ -95,13 +95,15 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int dest[2] = src[2] ^ m_aad_keystream_buffer[aad_pos + 2]; // Set the playload ChaCha instance block counter to 1 and crypt the payload - m_chacha_main.Seek64(1); + m_chacha_main.Seek64({0, seqnr_payload}, 1); m_chacha_main.Crypt(src + CHACHA20_POLY1305_AEAD_AAD_LEN, dest + CHACHA20_POLY1305_AEAD_AAD_LEN, src_len - CHACHA20_POLY1305_AEAD_AAD_LEN); // If encrypting, calculate and append tag if (is_encrypt) { // the poly1305 tag expands over the AAD (3 bytes length) & encrypted payload - poly1305_auth(dest + src_len, dest, src_len, poly_key); + Poly1305{MakeByteSpan(poly_key)} + .Update(AsBytes(Span{dest, src_len})) + .Finalize(AsWritableBytes(Span{dest + src_len, Poly1305::TAGLEN})); } // cleanse no longer required MAC and polykey @@ -117,8 +119,7 @@ bool ChaCha20Poly1305AEAD::GetLength(uint32_t* len24_out, uint64_t seqnr_aad, in if (m_cached_aad_seqnr != seqnr_aad) { // we need to calculate the 64 keystream bytes since we reached a new aad sequence number m_cached_aad_seqnr = seqnr_aad; - m_chacha_header.SetIV(seqnr_aad); // use LE for the nonce - m_chacha_header.Seek64(0); // block counter 0 + m_chacha_header.Seek64({0, seqnr_aad}, 0); // use LE for the nonce m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); // write keystream to the cache } diff --git a/src/crypto/poly1305.cpp b/src/crypto/poly1305.cpp index 8a86c9601c..7fcbab4108 100644 --- a/src/crypto/poly1305.cpp +++ b/src/crypto/poly1305.cpp @@ -2,140 +2,222 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -// Based on the public domain implementation by Andrew Moon -// poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna - #include <crypto/common.h> #include <crypto/poly1305.h> #include <string.h> -#define mul32x32_64(a,b) ((uint64_t)(a) * (b)) +namespace poly1305_donna { -void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) { - uint32_t t0,t1,t2,t3; - uint32_t h0,h1,h2,h3,h4; +// Based on the public domain implementation by Andrew Moon +// poly1305-donna-32.h from https://github.com/floodyberry/poly1305-donna + +void poly1305_init(poly1305_context *st, const unsigned char key[32]) noexcept { + /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ + st->r[0] = (ReadLE32(&key[ 0]) ) & 0x3ffffff; + st->r[1] = (ReadLE32(&key[ 3]) >> 2) & 0x3ffff03; + st->r[2] = (ReadLE32(&key[ 6]) >> 4) & 0x3ffc0ff; + st->r[3] = (ReadLE32(&key[ 9]) >> 6) & 0x3f03fff; + st->r[4] = (ReadLE32(&key[12]) >> 8) & 0x00fffff; + + /* h = 0 */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + + /* save pad for later */ + st->pad[0] = ReadLE32(&key[16]); + st->pad[1] = ReadLE32(&key[20]); + st->pad[2] = ReadLE32(&key[24]); + st->pad[3] = ReadLE32(&key[28]); + + st->leftover = 0; + st->final = 0; +} + +static void poly1305_blocks(poly1305_context *st, const unsigned char *m, size_t bytes) noexcept { + const uint32_t hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */ uint32_t r0,r1,r2,r3,r4; uint32_t s1,s2,s3,s4; - uint32_t b, nb; - size_t j; - uint64_t t[5]; - uint64_t f0,f1,f2,f3; - uint64_t g0,g1,g2,g3,g4; - uint64_t c; - unsigned char mp[16]; - - /* clamp key */ - t0 = ReadLE32(key+0); - t1 = ReadLE32(key+4); - t2 = ReadLE32(key+8); - t3 = ReadLE32(key+12); - - /* precompute multipliers */ - r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6; - r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12; - r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18; - r3 = t2 & 0x3f03fff; t3 >>= 8; - r4 = t3 & 0x00fffff; + uint32_t h0,h1,h2,h3,h4; + uint64_t d0,d1,d2,d3,d4; + uint32_t c; + + r0 = st->r[0]; + r1 = st->r[1]; + r2 = st->r[2]; + r3 = st->r[3]; + r4 = st->r[4]; s1 = r1 * 5; s2 = r2 * 5; s3 = r3 * 5; s4 = r4 * 5; - /* init state */ - h0 = 0; - h1 = 0; - h2 = 0; - h3 = 0; - h4 = 0; - - /* full blocks */ - if (inlen < 16) goto poly1305_donna_atmost15bytes; -poly1305_donna_16bytes: - m += 16; - inlen -= 16; - - t0 = ReadLE32(m-16); - t1 = ReadLE32(m-12); - t2 = ReadLE32(m-8); - t3 = ReadLE32(m-4); - - h0 += t0 & 0x3ffffff; - h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff; - h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff; - h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff; - h4 += (t3 >> 8) | (1 << 24); - - -poly1305_donna_mul: - t[0] = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1); - t[1] = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2); - t[2] = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3); - t[3] = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4); - t[4] = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0); - - h0 = (uint32_t)t[0] & 0x3ffffff; c = (t[0] >> 26); - t[1] += c; h1 = (uint32_t)t[1] & 0x3ffffff; b = (uint32_t)(t[1] >> 26); - t[2] += b; h2 = (uint32_t)t[2] & 0x3ffffff; b = (uint32_t)(t[2] >> 26); - t[3] += b; h3 = (uint32_t)t[3] & 0x3ffffff; b = (uint32_t)(t[3] >> 26); - t[4] += b; h4 = (uint32_t)t[4] & 0x3ffffff; b = (uint32_t)(t[4] >> 26); - h0 += b * 5; - - if (inlen >= 16) goto poly1305_donna_16bytes; - - /* final bytes */ -poly1305_donna_atmost15bytes: - if (!inlen) goto poly1305_donna_finish; - - for (j = 0; j < inlen; j++) mp[j] = m[j]; - mp[j++] = 1; - for (; j < 16; j++) mp[j] = 0; - inlen = 0; - - t0 = ReadLE32(mp+0); - t1 = ReadLE32(mp+4); - t2 = ReadLE32(mp+8); - t3 = ReadLE32(mp+12); - - h0 += t0 & 0x3ffffff; - h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff; - h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff; - h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff; - h4 += (t3 >> 8); - - goto poly1305_donna_mul; - -poly1305_donna_finish: - b = h0 >> 26; h0 = h0 & 0x3ffffff; - h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff; - h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff; - h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff; - h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff; - h0 += b * 5; b = h0 >> 26; h0 = h0 & 0x3ffffff; - h1 += b; - - g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff; - g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff; - g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff; - g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff; - g4 = h4 + b - (1 << 26); - - b = (g4 >> 31) - 1; - nb = ~b; - h0 = (h0 & nb) | (g0 & b); - h1 = (h1 & nb) | (g1 & b); - h2 = (h2 & nb) | (g2 & b); - h3 = (h3 & nb) | (g3 & b); - h4 = (h4 & nb) | (g4 & b); - - f0 = ((h0 ) | (h1 << 26)) + (uint64_t)ReadLE32(&key[16]); - f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t)ReadLE32(&key[20]); - f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t)ReadLE32(&key[24]); - f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t)ReadLE32(&key[28]); - - WriteLE32(&out[ 0], f0); f1 += (f0 >> 32); - WriteLE32(&out[ 4], f1); f2 += (f1 >> 32); - WriteLE32(&out[ 8], f2); f3 += (f2 >> 32); - WriteLE32(&out[12], f3); + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + while (bytes >= POLY1305_BLOCK_SIZE) { + /* h += m[i] */ + h0 += (ReadLE32(m+ 0) ) & 0x3ffffff; + h1 += (ReadLE32(m+ 3) >> 2) & 0x3ffffff; + h2 += (ReadLE32(m+ 6) >> 4) & 0x3ffffff; + h3 += (ReadLE32(m+ 9) >> 6) & 0x3ffffff; + h4 += (ReadLE32(m+12) >> 8) | hibit; + + /* h *= r */ + d0 = ((uint64_t)h0 * r0) + ((uint64_t)h1 * s4) + ((uint64_t)h2 * s3) + ((uint64_t)h3 * s2) + ((uint64_t)h4 * s1); + d1 = ((uint64_t)h0 * r1) + ((uint64_t)h1 * r0) + ((uint64_t)h2 * s4) + ((uint64_t)h3 * s3) + ((uint64_t)h4 * s2); + d2 = ((uint64_t)h0 * r2) + ((uint64_t)h1 * r1) + ((uint64_t)h2 * r0) + ((uint64_t)h3 * s4) + ((uint64_t)h4 * s3); + d3 = ((uint64_t)h0 * r3) + ((uint64_t)h1 * r2) + ((uint64_t)h2 * r1) + ((uint64_t)h3 * r0) + ((uint64_t)h4 * s4); + d4 = ((uint64_t)h0 * r4) + ((uint64_t)h1 * r3) + ((uint64_t)h2 * r2) + ((uint64_t)h3 * r1) + ((uint64_t)h4 * r0); + + /* (partial) h %= p */ + c = (uint32_t)(d0 >> 26); h0 = (uint32_t)d0 & 0x3ffffff; + d1 += c; c = (uint32_t)(d1 >> 26); h1 = (uint32_t)d1 & 0x3ffffff; + d2 += c; c = (uint32_t)(d2 >> 26); h2 = (uint32_t)d2 & 0x3ffffff; + d3 += c; c = (uint32_t)(d3 >> 26); h3 = (uint32_t)d3 & 0x3ffffff; + d4 += c; c = (uint32_t)(d4 >> 26); h4 = (uint32_t)d4 & 0x3ffffff; + h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff; + h1 += c; + + m += POLY1305_BLOCK_SIZE; + bytes -= POLY1305_BLOCK_SIZE; + } + + st->h[0] = h0; + st->h[1] = h1; + st->h[2] = h2; + st->h[3] = h3; + st->h[4] = h4; +} + +void poly1305_finish(poly1305_context *st, unsigned char mac[16]) noexcept { + uint32_t h0,h1,h2,h3,h4,c; + uint32_t g0,g1,g2,g3,g4; + uint64_t f; + uint32_t mask; + + /* process the remaining block */ + if (st->leftover) { + size_t i = st->leftover; + st->buffer[i++] = 1; + for (; i < POLY1305_BLOCK_SIZE; i++) { + st->buffer[i] = 0; + } + st->final = 1; + poly1305_blocks(st, st->buffer, POLY1305_BLOCK_SIZE); + } + + /* fully carry h */ + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + c = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += c; + + /* compute h + -p */ + g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff; + g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff; + g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff; + g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff; + g4 = h4 + c - (1UL << 26); + + /* select h if h < p, or h + -p if h >= p */ + mask = (g4 >> ((sizeof(uint32_t) * 8) - 1)) - 1; + g0 &= mask; + g1 &= mask; + g2 &= mask; + g3 &= mask; + g4 &= mask; + mask = ~mask; + h0 = (h0 & mask) | g0; + h1 = (h1 & mask) | g1; + h2 = (h2 & mask) | g2; + h3 = (h3 & mask) | g3; + h4 = (h4 & mask) | g4; + + /* h = h % (2^128) */ + h0 = ((h0 ) | (h1 << 26)) & 0xffffffff; + h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; + h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; + h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; + + /* mac = (h + pad) % (2^128) */ + f = (uint64_t)h0 + st->pad[0] ; h0 = (uint32_t)f; + f = (uint64_t)h1 + st->pad[1] + (f >> 32); h1 = (uint32_t)f; + f = (uint64_t)h2 + st->pad[2] + (f >> 32); h2 = (uint32_t)f; + f = (uint64_t)h3 + st->pad[3] + (f >> 32); h3 = (uint32_t)f; + + WriteLE32(mac + 0, h0); + WriteLE32(mac + 4, h1); + WriteLE32(mac + 8, h2); + WriteLE32(mac + 12, h3); + + /* zero out the state */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + st->r[0] = 0; + st->r[1] = 0; + st->r[2] = 0; + st->r[3] = 0; + st->r[4] = 0; + st->pad[0] = 0; + st->pad[1] = 0; + st->pad[2] = 0; + st->pad[3] = 0; } + +void poly1305_update(poly1305_context *st, const unsigned char *m, size_t bytes) noexcept { + size_t i; + + /* handle leftover */ + if (st->leftover) { + size_t want = (POLY1305_BLOCK_SIZE - st->leftover); + if (want > bytes) { + want = bytes; + } + for (i = 0; i < want; i++) { + st->buffer[st->leftover + i] = m[i]; + } + bytes -= want; + m += want; + st->leftover += want; + if (st->leftover < POLY1305_BLOCK_SIZE) return; + poly1305_blocks(st, st->buffer, POLY1305_BLOCK_SIZE); + st->leftover = 0; + } + + /* process full blocks */ + if (bytes >= POLY1305_BLOCK_SIZE) { + size_t want = (bytes & ~(POLY1305_BLOCK_SIZE - 1)); + poly1305_blocks(st, m, want); + m += want; + bytes -= want; + } + + /* store leftover */ + if (bytes) { + for (i = 0; i < bytes; i++) { + st->buffer[st->leftover + i] = m[i]; + } + st->leftover += bytes; + } +} + +} // namespace poly1305_donna diff --git a/src/crypto/poly1305.h b/src/crypto/poly1305.h index 650e35bbca..592c090f57 100644 --- a/src/crypto/poly1305.h +++ b/src/crypto/poly1305.h @@ -5,13 +5,66 @@ #ifndef BITCOIN_CRYPTO_POLY1305_H #define BITCOIN_CRYPTO_POLY1305_H +#include <span.h> + +#include <cassert> #include <cstdlib> #include <stdint.h> -#define POLY1305_KEYLEN 32 -#define POLY1305_TAGLEN 16 +#define POLY1305_BLOCK_SIZE 16 + +namespace poly1305_donna { + +// Based on the public domain implementation by Andrew Moon +// poly1305-donna-32.h from https://github.com/floodyberry/poly1305-donna + +typedef struct { + uint32_t r[5]; + uint32_t h[5]; + uint32_t pad[4]; + size_t leftover; + unsigned char buffer[POLY1305_BLOCK_SIZE]; + unsigned char final; +} poly1305_context; + +void poly1305_init(poly1305_context *st, const unsigned char key[32]) noexcept; +void poly1305_update(poly1305_context *st, const unsigned char *m, size_t bytes) noexcept; +void poly1305_finish(poly1305_context *st, unsigned char mac[16]) noexcept; + +} // namespace poly1305_donna + +/** C++ wrapper with std::byte Span interface around poly1305_donna code. */ +class Poly1305 +{ + poly1305_donna::poly1305_context m_ctx; + +public: + /** Length of the output produced by Finalize(). */ + static constexpr unsigned TAGLEN{16}; + + /** Length of the keys expected by the constructor. */ + static constexpr unsigned KEYLEN{32}; + + /** Construct a Poly1305 object with a given 32-byte key. */ + Poly1305(Span<const std::byte> key) noexcept + { + assert(key.size() == KEYLEN); + poly1305_donna::poly1305_init(&m_ctx, UCharCast(key.data())); + } + + /** Process message bytes. */ + Poly1305& Update(Span<const std::byte> msg) noexcept + { + poly1305_donna::poly1305_update(&m_ctx, UCharCast(msg.data()), msg.size()); + return *this; + } -void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, - const unsigned char key[POLY1305_KEYLEN]); + /** Write authentication tag to 16-byte out. */ + void Finalize(Span<std::byte> out) noexcept + { + assert(out.size() == TAGLEN); + poly1305_donna::poly1305_finish(&m_ctx, UCharCast(out.data())); + } +}; #endif // BITCOIN_CRYPTO_POLY1305_H diff --git a/src/crypto/siphash.cpp b/src/crypto/siphash.cpp index 2f7555d02e..2c328d0840 100644 --- a/src/crypto/siphash.cpp +++ b/src/crypto/siphash.cpp @@ -45,14 +45,14 @@ CSipHasher& CSipHasher::Write(uint64_t data) return *this; } -CSipHasher& CSipHasher::Write(const unsigned char* data, size_t size) +CSipHasher& CSipHasher::Write(Span<const unsigned char> data) { uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t t = tmp; uint8_t c = count; - while (size--) { - t |= ((uint64_t)(*(data++))) << (8 * (c % 8)); + while (data.size() > 0) { + t |= uint64_t{data.front()} << (8 * (c % 8)); c++; if ((c & 7) == 0) { v3 ^= t; @@ -61,6 +61,7 @@ CSipHasher& CSipHasher::Write(const unsigned char* data, size_t size) v0 ^= t; t = 0; } + data = data.subspan(1); } v[0] = v0; diff --git a/src/crypto/siphash.h b/src/crypto/siphash.h index b573526932..4fb3dc2f25 100644 --- a/src/crypto/siphash.h +++ b/src/crypto/siphash.h @@ -7,6 +7,7 @@ #include <stdint.h> +#include <span.h> #include <uint256.h> /** SipHash-2-4 */ @@ -26,7 +27,7 @@ public: */ CSipHasher& Write(uint64_t data); /** Hash arbitrary bytes. */ - CSipHasher& Write(const unsigned char* data, size_t size); + CSipHasher& Write(Span<const unsigned char> data); /** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */ uint64_t Finalize() const; }; diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 128c4e3c56..a83f4421d7 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -18,7 +18,6 @@ #include <shutdown.h> #include <sync.h> #include <util/strencodings.h> -#include <util/syscall_sandbox.h> #include <util/threadnames.h> #include <util/translation.h> @@ -222,8 +221,10 @@ static void http_request_cb(struct evhttp_request* req, void* arg) }, nullptr); } - // Disable reading to work around a libevent bug, fixed in 2.2.0. - if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { + // Disable reading to work around a libevent bug, fixed in 2.1.9 + // See https://github.com/libevent/libevent/commit/5ff8eb26371c4dc56f384b2de35bea2d87814779 + // and https://github.com/bitcoin/bitcoin/pull/11593. + if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02010900) { evhttp_connection* conn = evhttp_request_get_connection(req); if (conn) { bufferevent* bev = evhttp_connection_get_bufferevent(conn); @@ -297,7 +298,6 @@ static void http_reject_request_cb(struct evhttp_request* req, void*) static void ThreadHTTP(struct event_base* base) { util::ThreadRename("http"); - SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_HTTP_SERVER); LogPrint(BCLog::HTTP, "Entering http event loop\n"); event_base_dispatch(base); // Event loop will be interrupted by InterruptHTTPServer() @@ -350,7 +350,6 @@ static bool HTTPBindAddresses(struct evhttp* http) static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num) { util::ThreadRename(strprintf("httpworker.%i", worker_num)); - SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_HTTP_SERVER_WORKER); queue->Run(); } @@ -604,7 +603,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) evhttp_send_reply(req_copy, nStatus, nullptr, nullptr); // Re-enable reading from the socket. This is the second part of the libevent // workaround above. - if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { + if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02010900) { evhttp_connection* conn = evhttp_request_get_connection(req_copy); if (conn) { bufferevent* bev = evhttp_connection_get_bufferevent(conn); diff --git a/src/index/base.cpp b/src/index/base.cpp index a713be3480..55fb154d99 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -8,13 +8,13 @@ #include <interfaces/chain.h> #include <kernel/chain.h> #include <logging.h> +#include <node/abort.h> #include <node/blockstorage.h> #include <node/context.h> #include <node/database_args.h> #include <node/interface_ui.h> #include <shutdown.h> #include <tinyformat.h> -#include <util/syscall_sandbox.h> #include <util/thread.h> #include <util/translation.h> #include <validation.h> // For g_chainman @@ -23,17 +23,16 @@ #include <string> #include <utility> -using node::g_indexes_ready_to_sync; - constexpr uint8_t DB_BEST_BLOCK{'B'}; constexpr auto SYNC_LOG_INTERVAL{30s}; constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s}; template <typename... Args> -static void FatalError(const char* fmt, const Args&... args) +void BaseIndex::FatalErrorf(const char* fmt, const Args&... args) { - AbortNode(tfm::format(fmt, args...)); + auto message = tfm::format(fmt, args...); + node::AbortNode(m_chain->context()->exit_status, message); } CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash) @@ -80,6 +79,13 @@ BaseIndex::~BaseIndex() bool BaseIndex::Init() { + // m_chainstate member gives indexing code access to node internals. It is + // removed in followup https://github.com/bitcoin/bitcoin/pull/24230 + m_chainstate = &m_chain->context()->chainman->ActiveChainstate(); + // Register to validation interface before setting the 'm_synced' flag, so that + // callbacks are not missed once m_synced is true. + RegisterValidationInterface(this); + CBlockLocator locator; if (!GetDB().ReadBestBlock(locator)) { locator.SetNull(); @@ -99,45 +105,8 @@ bool BaseIndex::Init() SetBestBlockIndex(locator_index); } - // Skip pruning check if indexes are not ready to sync (because reindex-chainstate has wiped the chain). - const CBlockIndex* start_block = m_best_block_index.load(); - bool synced = start_block == active_chain.Tip(); - if (!synced && g_indexes_ready_to_sync) { - bool prune_violation = false; - if (!start_block) { - // index is not built yet - // make sure we have all block data back to the genesis - prune_violation = m_chainstate->m_blockman.GetFirstStoredBlock(*active_chain.Tip()) != active_chain.Genesis(); - } - // in case the index has a best block set and is not fully synced - // check if we have the required blocks to continue building the index - else { - const CBlockIndex* block_to_test = start_block; - if (!active_chain.Contains(block_to_test)) { - // if the bestblock is not part of the mainchain, find the fork - // and make sure we have all data down to the fork - block_to_test = active_chain.FindFork(block_to_test); - } - const CBlockIndex* block = active_chain.Tip(); - prune_violation = true; - // check backwards from the tip if we have all block data until we reach the indexes bestblock - while (block_to_test && block && (block->nStatus & BLOCK_HAVE_DATA)) { - if (block_to_test == block) { - prune_violation = false; - break; - } - // block->pprev must exist at this point, since block_to_test is part of the chain - // and thus must be encountered when going backwards from the tip - assert(block->pprev); - block = block->pprev; - } - } - if (prune_violation) { - return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), GetName())); - } - } - // Child init + const CBlockIndex* start_block = m_best_block_index.load(); if (!CustomInit(start_block ? std::make_optional(interfaces::BlockKey{start_block->GetBlockHash(), start_block->nHeight}) : std::nullopt)) { return false; } @@ -145,7 +114,8 @@ bool BaseIndex::Init() // Note: this will latch to true immediately if the user starts up with an empty // datadir and an index enabled. If this is the case, indexation will happen solely // via `BlockConnected` signals until, possibly, the next restart. - m_synced = synced; + m_synced = start_block == active_chain.Tip(); + m_init = true; return true; } @@ -167,13 +137,6 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain& void BaseIndex::ThreadSync() { - SetSyscallSandboxPolicy(SyscallSandboxPolicy::TX_INDEX); - // Wait for a possible reindex-chainstate to finish until continuing - // with the index sync - while (!g_indexes_ready_to_sync) { - if (!m_interrupt.sleep_for(std::chrono::milliseconds(500))) return; - } - const CBlockIndex* pindex = m_best_block_index.load(); if (!m_synced) { std::chrono::steady_clock::time_point last_log_time{0s}; @@ -199,7 +162,7 @@ void BaseIndex::ThreadSync() break; } if (pindex_next->pprev != pindex && !Rewind(pindex, pindex_next->pprev)) { - FatalError("%s: Failed to rewind index %s to a previous chain tip", + FatalErrorf("%s: Failed to rewind index %s to a previous chain tip", __func__, GetName()); return; } @@ -223,14 +186,14 @@ void BaseIndex::ThreadSync() CBlock block; interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex); if (!m_chainstate->m_blockman.ReadBlockFromDisk(block, *pindex)) { - FatalError("%s: Failed to read block %s from disk", + FatalErrorf("%s: Failed to read block %s from disk", __func__, pindex->GetBlockHash().ToString()); return; } else { block_info.data = █ } if (!CustomAppend(block_info)) { - FatalError("%s: Failed to write block %s to index database", + FatalErrorf("%s: Failed to write block %s to index database", __func__, pindex->GetBlockHash().ToString()); return; } @@ -296,7 +259,7 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const const CBlockIndex* best_block_index = m_best_block_index.load(); if (!best_block_index) { if (pindex->nHeight != 0) { - FatalError("%s: First block connected is not the genesis block (height=%d)", + FatalErrorf("%s: First block connected is not the genesis block (height=%d)", __func__, pindex->nHeight); return; } @@ -314,7 +277,7 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const return; } if (best_block_index != pindex->pprev && !Rewind(best_block_index, pindex->pprev)) { - FatalError("%s: Failed to rewind index %s to a previous chain tip", + FatalErrorf("%s: Failed to rewind index %s to a previous chain tip", __func__, GetName()); return; } @@ -327,7 +290,7 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const // processed, and the index object being safe to delete. SetBestBlockIndex(pindex); } else { - FatalError("%s: Failed to write block %s to index", + FatalErrorf("%s: Failed to write block %s to index", __func__, pindex->GetBlockHash().ToString()); return; } @@ -347,7 +310,7 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator) } if (!locator_tip_index) { - FatalError("%s: First block (hash=%s) in locator was not found", + FatalErrorf("%s: First block (hash=%s) in locator was not found", __func__, locator_tip_hash.ToString()); return; } @@ -401,15 +364,9 @@ void BaseIndex::Interrupt() m_interrupt(); } -bool BaseIndex::Start() +bool BaseIndex::StartBackgroundSync() { - // m_chainstate member gives indexing code access to node internals. It is - // removed in followup https://github.com/bitcoin/bitcoin/pull/24230 - m_chainstate = &m_chain->context()->chainman->ActiveChainstate(); - // Need to register this ValidationInterface before running Init(), so that - // callbacks are not missed if Init sets m_synced to true. - RegisterValidationInterface(this); - if (!Init()) return false; + if (!m_init) throw std::logic_error("Error: Cannot start a non-initialized index"); m_thread_sync = std::thread(&util::TraceThread, GetName(), [this] { ThreadSync(); }); return true; @@ -429,7 +386,13 @@ IndexSummary BaseIndex::GetSummary() const IndexSummary summary{}; summary.name = GetName(); summary.synced = m_synced; - summary.best_block_height = m_best_block_index ? m_best_block_index.load()->nHeight : 0; + if (const auto& pindex = m_best_block_index.load()) { + summary.best_block_height = pindex->nHeight; + summary.best_block_hash = pindex->GetBlockHash(); + } else { + summary.best_block_height = 0; + summary.best_block_hash = m_chain->getBlockHash(0); + } return summary; } diff --git a/src/index/base.h b/src/index/base.h index 231f36b605..9b2a41dc92 100644 --- a/src/index/base.h +++ b/src/index/base.h @@ -23,6 +23,7 @@ struct IndexSummary { std::string name; bool synced{false}; int best_block_height{0}; + uint256 best_block_hash; }; /** @@ -54,6 +55,8 @@ protected: }; private: + /// Whether the index has been initialized or not. + std::atomic<bool> m_init{false}; /// Whether the index is in sync with the main chain. The flag is flipped /// from false to true once, after which point this starts processing /// ValidationInterface notifications to stay in sync. @@ -69,9 +72,6 @@ private: std::thread m_thread_sync; CThreadInterrupt m_interrupt; - /// Read best block locator and check that data needed to sync has not been pruned. - bool Init(); - /// Sync the index with the block index starting from the current best block. /// Intended to be run in its own thread, m_thread_sync, and can be /// interrupted with m_interrupt. Once the index gets in sync, the m_synced @@ -94,6 +94,9 @@ private: virtual bool AllowPrune() const = 0; + template <typename... Args> + void FatalErrorf(const char* fmt, const Args&... args); + protected: std::unique_ptr<interfaces::Chain> m_chain; Chainstate* m_chainstate{nullptr}; @@ -139,9 +142,12 @@ public: void Interrupt(); - /// Start initializes the sync state and registers the instance as a - /// ValidationInterface so that it stays in sync with blockchain updates. - [[nodiscard]] bool Start(); + /// Initializes the sync state and registers the instance to the + /// validation interface so that it stays in sync with blockchain updates. + [[nodiscard]] bool Init(); + + /// Starts the initial sync process. + [[nodiscard]] bool StartBackgroundSync(); /// Stops the instance from staying in sync with blockchain updates. void Stop(); diff --git a/src/init.cpp b/src/init.cpp index e86180017a..997996ef12 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -50,7 +50,7 @@ #include <node/mempool_args.h> #include <node/mempool_persist_args.h> #include <node/miner.h> -#include <node/txreconciliation.h> +#include <node/peerman_args.h> #include <node/validation_cache_args.h> #include <policy/feerate.h> #include <policy/fees.h> @@ -80,7 +80,6 @@ #include <util/result.h> #include <util/strencodings.h> #include <util/string.h> -#include <util/syscall_sandbox.h> #include <util/syserror.h> #include <util/thread.h> #include <util/threadnames.h> @@ -115,7 +114,6 @@ #include <zmq/zmqrpc.h> #endif -using kernel::DEFAULT_STOPAFTERBLOCKIMPORT; using kernel::DumpMempool; using kernel::ValidationCacheSizes; @@ -125,19 +123,20 @@ using node::CacheSizes; using node::CalculateCacheSizes; using node::DEFAULT_PERSIST_MEMPOOL; using node::DEFAULT_PRINTPRIORITY; +using node::DEFAULT_STOPATHEIGHT; using node::fReindex; -using node::g_indexes_ready_to_sync; using node::KernelNotifications; using node::LoadChainstate; using node::MempoolPath; using node::NodeContext; using node::ShouldPersistMempool; -using node::ThreadImport; +using node::ImportBlocks; using node::VerifyLoadedChainstate; static constexpr bool DEFAULT_PROXYRANDOMIZE{true}; static constexpr bool DEFAULT_REST_ENABLE{false}; static constexpr bool DEFAULT_I2P_ACCEPT_INCOMING{true}; +static constexpr bool DEFAULT_STOPAFTERBLOCKIMPORT{false}; #ifdef WIN32 // Win32 LevelDB doesn't use filedescriptors, and the ones used for @@ -269,7 +268,7 @@ void Shutdown(NodeContext& node) // After everything has been shut down, but before things get flushed, stop the // CScheduler/checkqueue, scheduler and load block thread. if (node.scheduler) node.scheduler->stop(); - if (node.chainman && node.chainman->m_load_block.joinable()) node.chainman->m_load_block.join(); + if (node.chainman && node.chainman->m_thread_load.joinable()) node.chainman->m_thread_load.join(); StopScriptCheckWorkerThreads(); // After the threads that potentially access these pointers have been stopped, @@ -630,10 +629,6 @@ void SetupServerArgs(ArgsManager& argsman) hidden_args.emplace_back("-daemonwait"); #endif -#if defined(USE_SYSCALL_SANDBOX) - argsman.AddArg("-sandbox=<mode>", "Use the experimental syscall sandbox in the specified mode (-sandbox=log-and-abort or -sandbox=abort). Allow only expected syscalls to be used by bitcoind. Note that this is an experimental new feature that may cause bitcoind to exit or crash unexpectedly: use with caution. In the \"log-and-abort\" mode the invocation of an unexpected syscall results in a debug handler being invoked which will log the incident and terminate the program (without executing the unexpected syscall). In the \"abort\" mode the invocation of an unexpected syscall results in the entire process being killed immediately by the kernel without executing the unexpected syscall.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); -#endif // USE_SYSCALL_SANDBOX - // Add the hidden options argsman.AddHiddenArgs(hidden_args); } @@ -817,10 +812,6 @@ bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status) // Enable heap terminate-on-corruption HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0); #endif - if (!InitShutdownState(exit_status)) { - return InitError(Untranslated("Initializing wait-for-shutdown state failed.")); - } - if (!SetupNetworking()) { return InitError(Untranslated("Initializing networking failed.")); } @@ -844,7 +835,7 @@ bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status) return true; } -bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandbox) +bool AppInitParameterInteraction(const ArgsManager& args) { const CChainParams& chainparams = Params(); // ********************************************************* Step 2: parameter interactions @@ -991,43 +982,9 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb if (args.GetIntArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1) return InitError(Untranslated("Unknown rpcserialversion requested.")); -#if defined(USE_SYSCALL_SANDBOX) - if (args.IsArgSet("-sandbox") && !args.IsArgNegated("-sandbox")) { - const std::string sandbox_arg{args.GetArg("-sandbox", "")}; - bool log_syscall_violation_before_terminating{false}; - if (sandbox_arg == "log-and-abort") { - log_syscall_violation_before_terminating = true; - } else if (sandbox_arg == "abort") { - // log_syscall_violation_before_terminating is false by default. - } else { - return InitError(Untranslated("Unknown syscall sandbox mode (-sandbox=<mode>). Available modes are \"log-and-abort\" and \"abort\".")); - } - // execve(...) is not allowed by the syscall sandbox. - const std::vector<std::string> features_using_execve{ - "-alertnotify", - "-blocknotify", - "-signer", - "-startupnotify", - "-walletnotify", - }; - for (const std::string& feature_using_execve : features_using_execve) { - if (!args.GetArg(feature_using_execve, "").empty()) { - return InitError(Untranslated(strprintf("The experimental syscall sandbox feature (-sandbox=<mode>) is incompatible with %s (which uses execve).", feature_using_execve))); - } - } - if (!SetupSyscallSandbox(log_syscall_violation_before_terminating)) { - return InitError(Untranslated("Installation of the syscall sandbox failed.")); - } - if (use_syscall_sandbox) { - SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION); - } - LogPrintf("Experimental syscall sandbox enabled (-sandbox=%s): bitcoind will terminate if an unexpected (not allowlisted) syscall is invoked.\n", sandbox_arg); - } -#endif // USE_SYSCALL_SANDBOX - // Also report errors from parsing before daemonization { - KernelNotifications notifications{}; + kernel::Notifications notifications{}; ChainstateManager::Options chainman_opts_dummy{ .chainparams = chainparams, .datadir = args.GetDataDirNet(), @@ -1040,6 +997,7 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb BlockManager::Options blockman_opts_dummy{ .chainparams = chainman_opts_dummy.chainparams, .blocks_dir = args.GetBlocksDirPath(), + .notifications = chainman_opts_dummy.notifications, }; auto blockman_result{ApplyArgsManOptions(args, blockman_opts_dummy)}; if (!blockman_result) { @@ -1450,7 +1408,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 7: load block chain - node.notifications = std::make_unique<KernelNotifications>(); + node.notifications = std::make_unique<KernelNotifications>(node.exit_status); + ReadNotificationArgs(args, *node.notifications); fReindex = args.GetBoolArg("-reindex", false); bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false); ChainstateManager::Options chainman_opts{ @@ -1464,6 +1423,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) BlockManager::Options blockman_opts{ .chainparams = chainman_opts.chainparams, .blocks_dir = args.GetBlocksDirPath(), + .notifications = chainman_opts.notifications, }; Assert(ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction @@ -1503,7 +1463,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) { node.mempool = std::make_unique<CTxMemPool>(mempool_opts); - node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts); + node.chainman = std::make_unique<ChainstateManager>(node.kernel->interrupt, chainman_opts, blockman_opts); ChainstateManager& chainman = *node.chainman; node::ChainstateLoadOptions options; @@ -1579,15 +1539,20 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) ChainstateManager& chainman = *Assert(node.chainman); + + PeerManager::Options peerman_opts{ + .ignore_incoming_txs = ignores_incoming_txs, + }; + ApplyArgsManOptions(args, peerman_opts); + assert(!node.peerman); - node.peerman = PeerManager::make(*node.connman, *node.addrman, node.banman.get(), - chainman, *node.mempool, ignores_incoming_txs); + node.peerman = PeerManager::make(*node.connman, *node.addrman, + node.banman.get(), chainman, + *node.mempool, peerman_opts); RegisterValidationInterface(node.peerman.get()); // ********************************************************* Step 8: start indexers - // If reindex-chainstate was specified, delay syncing indexes until ThreadImport has reindexed the chain - if (!fReindexChainState) g_indexes_ready_to_sync = true; if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { auto result{WITH_LOCK(cs_main, return CheckLegacyTxindex(*Assert(chainman.m_blockman.m_block_tree_db)))}; if (!result) { @@ -1595,25 +1560,22 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } g_txindex = std::make_unique<TxIndex>(interfaces::MakeChain(node), cache_sizes.tx_index, false, fReindex); - if (!g_txindex->Start()) { - return false; - } + node.indexes.emplace_back(g_txindex.get()); } for (const auto& filter_type : g_enabled_filter_types) { InitBlockFilterIndex([&]{ return interfaces::MakeChain(node); }, filter_type, cache_sizes.filter_index, false, fReindex); - if (!GetBlockFilterIndex(filter_type)->Start()) { - return false; - } + node.indexes.emplace_back(GetBlockFilterIndex(filter_type)); } if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) { g_coin_stats_index = std::make_unique<CoinStatsIndex>(interfaces::MakeChain(node), /*cache_size=*/0, false, fReindex); - if (!g_coin_stats_index->Start()) { - return false; - } + node.indexes.emplace_back(g_coin_stats_index.get()); } + // Init indexes + for (auto index : node.indexes) if (!index->Init()) return false; + // ********************************************************* Step 9: load wallet for (const auto& client : node.chain_clients) { if (!client->load()) { @@ -1697,15 +1659,30 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) vImportFiles.push_back(fs::PathFromString(strFile)); } - chainman.m_load_block = std::thread(&util::TraceThread, "loadblk", [=, &chainman, &args] { - ThreadImport(chainman, vImportFiles, ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{}); + chainman.m_thread_load = std::thread(&util::TraceThread, "initload", [=, &chainman, &args, &node] { + // Import blocks + ImportBlocks(chainman, vImportFiles); + if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { + LogPrintf("Stopping after block import\n"); + StartShutdown(); + return; + } + + // Start indexes initial sync + if (!StartIndexBackgroundSync(node)) { + bilingual_str err_str = _("Failed to start indexes, shutting down.."); + chainman.GetNotifications().fatalError(err_str.original, err_str); + return; + } + // Load mempool from disk + chainman.ActiveChainstate().LoadMempool(ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{}); }); // Wait for genesis block to be processed { WAIT_LOCK(g_genesis_wait_mutex, lock); // We previously could hang here if StartShutdown() is called prior to - // ThreadImport getting started, so instead we just wait on a timer to + // ImportBlocks getting started, so instead we just wait on a timer to // check ShutdownRequested() regularly. while (!fHaveGenesis && !ShutdownRequested()) { g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500)); @@ -1916,3 +1893,47 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) return true; } + +bool StartIndexBackgroundSync(NodeContext& node) +{ + // Find the oldest block among all indexes. + // This block is used to verify that we have the required blocks' data stored on disk, + // starting from that point up to the current tip. + // indexes_start_block='nullptr' means "start from height 0". + std::optional<const CBlockIndex*> indexes_start_block; + std::string older_index_name; + + ChainstateManager& chainman = *Assert(node.chainman); + for (auto index : node.indexes) { + const IndexSummary& summary = index->GetSummary(); + if (summary.synced) continue; + + // Get the last common block between the index best block and the active chain + LOCK(::cs_main); + const CChain& active_chain = chainman.ActiveChain(); + const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(summary.best_block_hash); + if (!active_chain.Contains(pindex)) { + pindex = active_chain.FindFork(pindex); + } + + if (!indexes_start_block || !pindex || pindex->nHeight < indexes_start_block.value()->nHeight) { + indexes_start_block = pindex; + older_index_name = summary.name; + if (!pindex) break; // Starting from genesis so no need to look for earlier block. + } + }; + + // Verify all blocks needed to sync to current tip are present. + if (indexes_start_block) { + LOCK(::cs_main); + const CBlockIndex* start_block = *indexes_start_block; + if (!start_block) start_block = chainman.ActiveChain().Genesis(); + if (!chainman.m_blockman.CheckBlockDataAvailability(*chainman.ActiveChain().Tip(), *Assert(start_block))) { + return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), older_index_name)); + } + } + + // Start threads + for (auto index : node.indexes) if (!index->StartBackgroundSync()) return false; + return true; +} diff --git a/src/init.h b/src/init.h index fabb4f66d6..f27d6120ef 100644 --- a/src/init.h +++ b/src/init.h @@ -44,7 +44,7 @@ bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status); * @note This can be done before daemonization. Do not call Shutdown() if this function fails. * @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called. */ -bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandbox = true); +bool AppInitParameterInteraction(const ArgsManager& args); /** * Initialization sanity checks. * @note This can be done before daemonization. Do not call Shutdown() if this function fails. @@ -73,4 +73,7 @@ bool AppInitMain(node::NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip */ void SetupServerArgs(ArgsManager& argsman); +/** Validates requirements to run the indexes and spawns each index initial sync thread */ +bool StartIndexBackgroundSync(node::NodeContext& node); + #endif // BITCOIN_INIT_H diff --git a/src/kernel/blockmanager_opts.h b/src/kernel/blockmanager_opts.h index 8f26422f72..deeba7e318 100644 --- a/src/kernel/blockmanager_opts.h +++ b/src/kernel/blockmanager_opts.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H #define BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H +#include <kernel/notifications_interface.h> #include <util/fs.h> #include <cstdint> @@ -13,8 +14,6 @@ class CChainParams; namespace kernel { -static constexpr bool DEFAULT_STOPAFTERBLOCKIMPORT{false}; - /** * An options struct for `BlockManager`, more ergonomically referred to as * `BlockManager::Options` due to the using-declaration in `BlockManager`. @@ -23,8 +22,8 @@ struct BlockManagerOpts { const CChainParams& chainparams; uint64_t prune_target{0}; bool fast_prune{false}; - bool stop_after_block_import{DEFAULT_STOPAFTERBLOCKIMPORT}; const fs::path blocks_dir; + Notifications& notifications; }; } // namespace kernel diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h index 035a913d10..917f7d226c 100644 --- a/src/kernel/chainstatemanager_opts.h +++ b/src/kernel/chainstatemanager_opts.h @@ -21,7 +21,6 @@ class CChainParams; static constexpr bool DEFAULT_CHECKPOINTS_ENABLED{true}; static constexpr auto DEFAULT_MAX_TIP_AGE{24h}; -static constexpr int DEFAULT_STOPATHEIGHT{0}; namespace kernel { @@ -46,7 +45,6 @@ struct ChainstateManagerOpts { DBOptions coins_db{}; CoinsViewOptions coins_view{}; Notifications& notifications; - int stop_at_height{DEFAULT_STOPATHEIGHT}; }; } // namespace kernel diff --git a/src/kernel/context.cpp b/src/kernel/context.cpp index 1205da869e..3f4a423531 100644 --- a/src/kernel/context.cpp +++ b/src/kernel/context.cpp @@ -14,9 +14,12 @@ namespace kernel { +Context* g_context; Context::Context() { + assert(!g_context); + g_context = this; std::string sha256_algo = SHA256AutoDetect(); LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo); RandomInit(); @@ -26,6 +29,8 @@ Context::Context() Context::~Context() { ECC_Stop(); + assert(g_context); + g_context = nullptr; } } // namespace kernel diff --git a/src/kernel/context.h b/src/kernel/context.h index f11b7b54f0..e74e0a6f08 100644 --- a/src/kernel/context.h +++ b/src/kernel/context.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_KERNEL_CONTEXT_H #define BITCOIN_KERNEL_CONTEXT_H +#include <util/signalinterrupt.h> + #include <memory> namespace kernel { @@ -16,12 +18,24 @@ namespace kernel { //! State stored directly in this struct should be simple. More complex state //! should be stored to std::unique_ptr members pointing to opaque types. struct Context { + //! Interrupt object that can be used to stop long-running kernel operations. + util::SignalInterrupt interrupt; + //! Declare default constructor and destructor that are not inline, so code //! instantiating the kernel::Context struct doesn't need to #include class //! definitions for all the unique_ptr members. Context(); ~Context(); }; + +//! Global pointer to kernel::Context for legacy code. New code should avoid +//! using this, and require state it needs to be passed to it directly. +//! +//! Having this pointer is useful because it allows state be moved out of global +//! variables into the kernel::Context struct before all global references to +//! that state are removed. This allows the global references to be removed +//! incrementally, instead of all at once. +extern Context* g_context; } // namespace kernel #endif // BITCOIN_KERNEL_CONTEXT_H diff --git a/src/kernel/mempool_persist.cpp b/src/kernel/mempool_persist.cpp index 71f3aac366..d060e45af3 100644 --- a/src/kernel/mempool_persist.cpp +++ b/src/kernel/mempool_persist.cpp @@ -9,13 +9,13 @@ #include <logging.h> #include <primitives/transaction.h> #include <serialize.h> -#include <shutdown.h> #include <streams.h> #include <sync.h> #include <txmempool.h> #include <uint256.h> #include <util/fs.h> #include <util/fs_helpers.h> +#include <util/signalinterrupt.h> #include <util/time.h> #include <validation.h> @@ -95,7 +95,7 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active } else { ++expired; } - if (ShutdownRequested()) + if (active_chainstate.m_chainman.m_interrupt) return false; } std::map<uint256, CAmount> mapDeltas; diff --git a/src/kernel/notifications_interface.h b/src/kernel/notifications_interface.h index 48248e9aa0..c5e77b0df9 100644 --- a/src/kernel/notifications_interface.h +++ b/src/kernel/notifications_interface.h @@ -5,15 +5,29 @@ #ifndef BITCOIN_KERNEL_NOTIFICATIONS_INTERFACE_H #define BITCOIN_KERNEL_NOTIFICATIONS_INTERFACE_H +#include <util/translation.h> + #include <cstdint> #include <string> +#include <variant> class CBlockIndex; enum class SynchronizationState; -struct bilingual_str; namespace kernel { +//! Result type for use with std::variant to indicate that an operation should be interrupted. +struct Interrupted{}; + +//! Simple result type for functions that need to propagate an interrupt status and don't have other return values. +using InterruptResult = std::variant<std::monostate, Interrupted>; + +template <typename T> +bool IsInterrupted(const T& result) +{ + return std::holds_alternative<kernel::Interrupted>(result); +} + /** * A base class defining functions for notifying about certain kernel * events. @@ -23,10 +37,27 @@ class Notifications public: virtual ~Notifications(){}; - virtual void blockTip(SynchronizationState state, CBlockIndex& index) {} + [[nodiscard]] virtual InterruptResult blockTip(SynchronizationState state, CBlockIndex& index) { return {}; } virtual void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {} virtual void progress(const bilingual_str& title, int progress_percent, bool resume_possible) {} virtual void warning(const bilingual_str& warning) {} + + //! The flush error notification is sent to notify the user that an error + //! occurred while flushing block data to disk. Kernel code may ignore flush + //! errors that don't affect the immediate operation it is trying to + //! perform. Applications can choose to handle the flush error notification + //! by logging the error, or notifying the user, or triggering an early + //! shutdown as a precaution against causing more errors. + virtual void flushError(const std::string& debug_message) {} + + //! The fatal error notification is sent to notify the user when an error + //! occurs in kernel code that can't be recovered from. After this + //! notification is sent, whatever function triggered the error should also + //! return an error code or raise an exception. Applications can choose to + //! handle the fatal error notification by logging the error, or notifying + //! the user, or triggering an early shutdown as a precaution against + //! causing more errors. + virtual void fatalError(const std::string& debug_message, const bilingual_str& user_message = {}) {} }; } // namespace kernel diff --git a/src/key.cpp b/src/key.cpp index 3a3f0b2bc2..efaea5b1b3 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -11,6 +11,7 @@ #include <random.h> #include <secp256k1.h> +#include <secp256k1_ellswift.h> #include <secp256k1_extrakeys.h> #include <secp256k1_recovery.h> #include <secp256k1_schnorrsig.h> @@ -331,6 +332,42 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const return ret; } +EllSwiftPubKey CKey::EllSwiftCreate(Span<const std::byte> ent32) const +{ + assert(fValid); + assert(ent32.size() == 32); + std::array<std::byte, EllSwiftPubKey::size()> encoded_pubkey; + + auto success = secp256k1_ellswift_create(secp256k1_context_sign, + UCharCast(encoded_pubkey.data()), + keydata.data(), + UCharCast(ent32.data())); + + // Should always succeed for valid keys (asserted above). + assert(success); + return {encoded_pubkey}; +} + +ECDHSecret CKey::ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, const EllSwiftPubKey& our_ellswift, bool initiating) const +{ + assert(fValid); + + ECDHSecret output; + // BIP324 uses the initiator as party A, and the responder as party B. Remap the inputs + // accordingly: + bool success = secp256k1_ellswift_xdh(secp256k1_context_sign, + UCharCast(output.data()), + UCharCast(initiating ? our_ellswift.data() : their_ellswift.data()), + UCharCast(initiating ? their_ellswift.data() : our_ellswift.data()), + keydata.data(), + initiating ? 0 : 1, + secp256k1_ellswift_xdh_hash_function_bip324, + nullptr); + // Should always succeed for valid keys (assert above). + assert(success); + return output; +} + bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const { if (nDepth == std::numeric_limits<unsigned char>::max()) return false; out.nDepth = nDepth + 1; @@ -22,6 +22,12 @@ */ typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey; +/** Size of ECDH shared secrets. */ +constexpr static size_t ECDH_SECRET_SIZE = CSHA256::OUTPUT_SIZE; + +// Used to represent ECDH shared secret (ECDH_SECRET_SIZE bytes) +using ECDHSecret = std::array<std::byte, ECDH_SECRET_SIZE>; + /** An encapsulated private key. */ class CKey { @@ -156,6 +162,27 @@ public: //! Load private key and check that public key matches. bool Load(const CPrivKey& privkey, const CPubKey& vchPubKey, bool fSkipCheck); + + /** Create an ellswift-encoded public key for this key, with specified entropy. + * + * entropy must be a 32-byte span with additional entropy to use in the encoding. Every + * public key has ~2^256 different encodings, and this function will deterministically pick + * one of them, based on entropy. Note that even without truly random entropy, the + * resulting encoding will be indistinguishable from uniform to any adversary who does not + * know the private key (because the private key itself is always used as entropy as well). + */ + EllSwiftPubKey EllSwiftCreate(Span<const std::byte> entropy) const; + + /** Compute a BIP324-style ECDH shared secret. + * + * - their_ellswift: EllSwiftPubKey that was received from the other side. + * - our_ellswift: EllSwiftPubKey that was sent to the other side (must have been generated + * from *this using EllSwiftCreate()). + * - initiating: whether we are the initiating party (true) or responding party (false). + */ + ECDHSecret ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, + const EllSwiftPubKey& our_ellswift, + bool initiating) const; }; struct CExtKey { diff --git a/src/mapport.cpp b/src/mapport.cpp index 118827901a..08b365db4b 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -14,7 +14,6 @@ #include <net.h> #include <netaddress.h> #include <netbase.h> -#include <util/syscall_sandbox.h> #include <util/thread.h> #include <util/threadinterrupt.h> @@ -219,7 +218,6 @@ static bool ProcessUpnp() static void ThreadMapPort() { - SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION_MAP_PORT); bool ok; do { ok = false; diff --git a/src/net.cpp b/src/net.cpp index 282c8fb741..1eda51e9df 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -30,7 +30,6 @@ #include <util/fs.h> #include <util/sock.h> #include <util/strencodings.h> -#include <util/syscall_sandbox.h> #include <util/thread.h> #include <util/threadinterrupt.h> #include <util/trace.h> @@ -38,18 +37,12 @@ #ifdef WIN32 #include <string.h> -#else -#include <fcntl.h> #endif #if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS #include <ifaddrs.h> #endif -#ifdef USE_POLL -#include <poll.h> -#endif - #include <algorithm> #include <array> #include <cstdint> @@ -153,7 +146,7 @@ uint16_t GetListenPort() } // find 'best' local address for a particular peer -bool GetLocal(CService& addr, const CNetAddr *paddrPeer) +bool GetLocal(CService& addr, const CNode& peer) { if (!fListen) return false; @@ -164,8 +157,18 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) LOCK(g_maplocalhost_mutex); for (const auto& entry : mapLocalHost) { + // For privacy reasons, don't advertise our privacy-network address + // to other networks and don't advertise our other-network address + // to privacy networks. + const Network our_net{entry.first.GetNetwork()}; + const Network peers_net{peer.ConnectedThroughNetwork()}; + if (our_net != peers_net && + (our_net == NET_ONION || our_net == NET_I2P || + peers_net == NET_ONION || peers_net == NET_I2P)) { + continue; + } int nScore = entry.second.nScore; - int nReachability = entry.first.GetReachabilityFrom(paddrPeer); + int nReachability = entry.first.GetReachabilityFrom(peer.addr); if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) { addr = CService(entry.first, entry.second.nPort); @@ -203,10 +206,10 @@ static std::vector<CAddress> ConvertSeeds(const std::vector<uint8_t> &vSeedsIn) // Otherwise, return the unroutable 0.0.0.0 but filled in with // the normal parameters, since the IP may be changed to a useful // one by discovery. -CService GetLocalAddress(const CNetAddr& addrPeer) +CService GetLocalAddress(const CNode& peer) { CService addr; - if (GetLocal(addr, &addrPeer)) { + if (GetLocal(addr, peer)) { return addr; } return CService{CNetAddr(), GetListenPort()}; @@ -229,7 +232,7 @@ bool IsPeerAddrLocalGood(CNode *pnode) std::optional<CService> GetLocalAddrForPeer(CNode& node) { - CService addrLocal{GetLocalAddress(node.addr)}; + CService addrLocal{GetLocalAddress(node)}; if (gArgs.GetBoolArg("-addrmantest", false)) { // use IPv4 loopback during addrmantest addrLocal = CService(LookupNumeric("127.0.0.1", GetListenPort())); @@ -1381,7 +1384,6 @@ void CConnman::ThreadSocketHandler() { AssertLockNotHeld(m_total_bytes_sent_mutex); - SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET); while (!interruptNet) { DisconnectNodes(); @@ -1401,7 +1403,6 @@ void CConnman::WakeMessageHandler() void CConnman::ThreadDNSAddressSeed() { - SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION_DNS_SEED); FastRandomContext rng; std::vector<std::string> seeds = Params().DNSSeeds(); Shuffle(seeds.begin(), seeds.end(), rng); @@ -1607,7 +1608,6 @@ std::unordered_set<Network> CConnman::GetReachableEmptyNetworks() const void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) { AssertLockNotHeld(m_unused_i2p_sessions_mutex); - SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_OPEN_CONNECTION); FastRandomContext rng; // Connect to specific addresses if (!connect.empty()) @@ -1637,6 +1637,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) auto next_extra_block_relay = GetExponentialRand(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL); const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED); bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS); + const bool use_seednodes{gArgs.IsArgSet("-seednode")}; if (!add_fixed_seeds) { LogPrintf("Fixed seeds are disabled\n"); @@ -1666,12 +1667,12 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) LogPrintf("Adding fixed seeds as 60 seconds have passed and addrman is empty for at least one reachable network\n"); } - // Checking !dnsseed is cheaper before locking 2 mutexes. - if (!add_fixed_seeds_now && !dnsseed) { - LOCK2(m_addr_fetches_mutex, m_added_nodes_mutex); - if (m_addr_fetches.empty() && m_added_nodes.empty()) { + // Perform cheap checks before locking a mutex. + else if (!dnsseed && !use_seednodes) { + LOCK(m_added_nodes_mutex); + if (m_added_nodes.empty()) { add_fixed_seeds_now = true; - LogPrintf("Adding fixed seeds as -dnsseed=0 (or IPv4/IPv6 connections are disabled via -onlynet), -addnode is not provided and all -seednode(s) attempted\n"); + LogPrintf("Adding fixed seeds as -dnsseed=0 (or IPv4/IPv6 connections are disabled via -onlynet) and neither -addnode nor -seednode are provided\n"); } } @@ -1853,7 +1854,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) // Require outbound IPv4/IPv6 connections, other than feelers, to be to distinct network groups if (!fFeeler && outbound_ipv46_peer_netgroups.count(m_netgroupman.GetGroup(addr))) { - break; + continue; } // if we selected an invalid or local address, restart @@ -1975,7 +1976,6 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const void CConnman::ThreadOpenAddedConnections() { AssertLockNotHeld(m_unused_i2p_sessions_mutex); - SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET_ADD_CONNECTION); while (true) { CSemaphoreGrant grant(*semAddnode); @@ -2044,7 +2044,6 @@ void CConnman::ThreadMessageHandler() { LOCK(NetEventsInterface::g_msgproc_mutex); - SetSyscallSandboxPolicy(SyscallSandboxPolicy::MESSAGE_HANDLER); while (!flagInterruptMsgProc) { bool fMoreWork = false; @@ -2530,7 +2529,7 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres auto local_socket_bytes = requestor.addrBind.GetAddrBytes(); uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE) .Write(requestor.ConnectedThroughNetwork()) - .Write(local_socket_bytes.data(), local_socket_bytes.size()) + .Write(local_socket_bytes) // For outbound connections, the port of the bound address is randomly // assigned by the OS and would therefore not be useful for seeding. .Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0) @@ -2913,7 +2912,7 @@ uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& address) const { std::vector<unsigned char> vchNetGroup(m_netgroupman.GetGroup(address)); - return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize(); + return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup).Finalize(); } void CaptureMessageToFile(const CAddress& addr, @@ -2938,13 +2937,13 @@ void CaptureMessageToFile(const CAddress& addr, AutoFile f{fsbridge::fopen(path, "ab")}; ser_writedata64(f, now.count()); - f.write(MakeByteSpan(msg_type)); + f << Span{msg_type}; for (auto i = msg_type.length(); i < CMessageHeader::COMMAND_SIZE; ++i) { f << uint8_t{'\0'}; } uint32_t size = data.size(); ser_writedata32(f, size); - f.write(AsBytes(data)); + f << data; } std::function<void(const CAddress& addr, @@ -164,8 +164,8 @@ bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); void RemoveLocal(const CService& addr); bool SeenLocal(const CService& addr); bool IsLocal(const CService& addr); -bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr); -CService GetLocalAddress(const CNetAddr& addrPeer); +bool GetLocal(CService& addr, const CNode& peer); +CService GetLocalAddress(const CNode& peer); CService MaybeFlipIPv6toCJDNS(const CService& service); @@ -1219,7 +1219,6 @@ private: std::vector<CNode*> m_nodes_copy; }; - friend struct CConnmanTest; friend struct ConnmanTestMsg; }; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 6597019797..be6777d14b 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -10,7 +10,6 @@ #include <blockencodings.h> #include <blockfilter.h> #include <chainparams.h> -#include <common/args.h> #include <consensus/amount.h> #include <consensus/validation.h> #include <deploymentstatus.h> @@ -487,7 +486,7 @@ class PeerManagerImpl final : public PeerManager public: PeerManagerImpl(CConnman& connman, AddrMan& addrman, BanMan* banman, ChainstateManager& chainman, - CTxMemPool& pool, bool ignore_incoming_txs); + CTxMemPool& pool, Options opts); /** Overridden from CValidationInterface. */ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override @@ -515,7 +514,7 @@ public: std::optional<std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); - bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; } + bool IgnoresIncomingTxs() override { return m_opts.ignore_incoming_txs; } void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void RelayTransaction(const uint256& txid, const uint256& wtxid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void SetBestHeight(int height) override { m_best_height = height; }; @@ -568,7 +567,7 @@ private: * * @return Returns true if the peer was punished (probably disconnected) */ - bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "") + bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); /** Maybe disconnect a peer and discourage future connections from its address. @@ -718,8 +717,7 @@ private: /** Next time to check for stale tip */ std::chrono::seconds m_stale_tip_check_time GUARDED_BY(cs_main){0s}; - /** Whether this node is running in -blocksonly mode */ - const bool m_ignore_incoming_txs; + const Options m_opts; bool RejectIncomingTxs(const CNode& peer) const; @@ -918,6 +916,10 @@ private: /** Process a new block. Perform any post-processing housekeeping */ void ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked); + /** Process compact block txns */ + void ProcessCompactBlockTxns(CNode& pfrom, Peer& peer, const BlockTransactions& block_transactions) + EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex, !m_most_recent_block_mutex); + /** * When a peer sends us a valid block, instruct it to announce blocks to us * using CMPCTBLOCK if possible by adding its nodeid to the end of @@ -1208,7 +1210,7 @@ void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid) // When in -blocksonly mode, never request high-bandwidth mode from peers. Our // mempool will not contain the transactions necessary to reconstruct the // compact block. - if (m_ignore_incoming_txs) return; + if (m_opts.ignore_incoming_txs) return; CNodeState* nodestate = State(nodeid); if (!nodestate || !nodestate->m_provides_cmpctblocks) { @@ -1646,13 +1648,12 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx) { - size_t max_extra_txn = gArgs.GetIntArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); - if (max_extra_txn <= 0) + if (m_opts.max_extra_txs <= 0) return; if (!vExtraTxnForCompact.size()) - vExtraTxnForCompact.resize(max_extra_txn); + vExtraTxnForCompact.resize(m_opts.max_extra_txs); vExtraTxnForCompact[vExtraTxnForCompactIt] = std::make_pair(tx->GetWitnessHash(), tx); - vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn; + vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % m_opts.max_extra_txs; } void PeerManagerImpl::Misbehaving(Peer& peer, int howmuch, const std::string& message) @@ -1731,7 +1732,7 @@ bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati return false; } -bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message) +bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state) { PeerRef peer{GetPeerRef(nodeid)}; switch (state.GetResult()) { @@ -1739,7 +1740,7 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat break; // The node is providing invalid data: case TxValidationResult::TX_CONSENSUS: - if (peer) Misbehaving(*peer, 100, message); + if (peer) Misbehaving(*peer, 100, ""); return true; // Conflicting (but not necessarily invalid) data or different policy: case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE: @@ -1754,9 +1755,6 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat case TxValidationResult::TX_NO_MEMPOOL: break; } - if (message != "") { - LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message); - } return false; } @@ -1808,25 +1806,25 @@ std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBl std::unique_ptr<PeerManager> PeerManager::make(CConnman& connman, AddrMan& addrman, BanMan* banman, ChainstateManager& chainman, - CTxMemPool& pool, bool ignore_incoming_txs) + CTxMemPool& pool, Options opts) { - return std::make_unique<PeerManagerImpl>(connman, addrman, banman, chainman, pool, ignore_incoming_txs); + return std::make_unique<PeerManagerImpl>(connman, addrman, banman, chainman, pool, opts); } PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman, BanMan* banman, ChainstateManager& chainman, - CTxMemPool& pool, bool ignore_incoming_txs) + CTxMemPool& pool, Options opts) : m_chainparams(chainman.GetParams()), m_connman(connman), m_addrman(addrman), m_banman(banman), m_chainman(chainman), m_mempool(pool), - m_ignore_incoming_txs(ignore_incoming_txs) + m_opts{opts} { // While Erlay support is incomplete, it must be enabled explicitly via -txreconciliation. // This argument can go away after Erlay support is complete. - if (gArgs.GetBoolArg("-txreconciliation", DEFAULT_TXRECONCILIATION_ENABLE)) { + if (opts.reconcile_txs) { m_txreconciliation = std::make_unique<TxReconciliationTracker>(TXRECONCILIATION_VERSION); } } @@ -2728,7 +2726,7 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c last_header.nHeight); } if (vGetData.size() > 0) { - if (!m_ignore_incoming_txs && + if (!m_opts.ignore_incoming_txs && nodestate->m_provides_cmpctblocks && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && @@ -3204,6 +3202,93 @@ void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlo } } +void PeerManagerImpl::ProcessCompactBlockTxns(CNode& pfrom, Peer& peer, const BlockTransactions& block_transactions) +{ + std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); + bool fBlockRead{false}; + const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); + { + LOCK(cs_main); + + auto range_flight = mapBlocksInFlight.equal_range(block_transactions.blockhash); + size_t already_in_flight = std::distance(range_flight.first, range_flight.second); + bool requested_block_from_this_peer{false}; + + // Multimap ensures ordering of outstanding requests. It's either empty or first in line. + bool first_in_flight = already_in_flight == 0 || (range_flight.first->second.first == pfrom.GetId()); + + while (range_flight.first != range_flight.second) { + auto [node_id, block_it] = range_flight.first->second; + if (node_id == pfrom.GetId() && block_it->partialBlock) { + requested_block_from_this_peer = true; + break; + } + range_flight.first++; + } + + if (!requested_block_from_this_peer) { + LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom.GetId()); + return; + } + + PartiallyDownloadedBlock& partialBlock = *range_flight.first->second.second->partialBlock; + ReadStatus status = partialBlock.FillBlock(*pblock, block_transactions.txn); + if (status == READ_STATUS_INVALID) { + RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId()); // Reset in-flight state in case Misbehaving does not result in a disconnect + Misbehaving(peer, 100, "invalid compact block/non-matching block transactions"); + return; + } else if (status == READ_STATUS_FAILED) { + if (first_in_flight) { + // Might have collided, fall back to getdata now :( + std::vector<CInv> invs; + invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(peer), block_transactions.blockhash)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); + } else { + RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId()); + LogPrint(BCLog::NET, "Peer %d sent us a compact block but it failed to reconstruct, waiting on first download to complete\n", pfrom.GetId()); + return; + } + } else { + // Block is either okay, or possibly we received + // READ_STATUS_CHECKBLOCK_FAILED. + // Note that CheckBlock can only fail for one of a few reasons: + // 1. bad-proof-of-work (impossible here, because we've already + // accepted the header) + // 2. merkleroot doesn't match the transactions given (already + // caught in FillBlock with READ_STATUS_FAILED, so + // impossible here) + // 3. the block is otherwise invalid (eg invalid coinbase, + // block is too big, too many legacy sigops, etc). + // So if CheckBlock failed, #3 is the only possibility. + // Under BIP 152, we don't discourage the peer unless proof of work is + // invalid (we don't require all the stateless checks to have + // been run). This is handled below, so just treat this as + // though the block was successfully read, and rely on the + // handling in ProcessNewBlock to ensure the block index is + // updated, etc. + RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId()); // it is now an empty pointer + fBlockRead = true; + // mapBlockSource is used for potentially punishing peers and + // updating which peers send us compact blocks, so the race + // between here and cs_main in ProcessNewBlock is fine. + // BIP 152 permits peers to relay compact blocks after validating + // the header only; we should not punish peers if the block turns + // out to be invalid. + mapBlockSource.emplace(block_transactions.blockhash, std::make_pair(pfrom.GetId(), false)); + } + } // Don't hold cs_main when we call into ProcessNewBlock + if (fBlockRead) { + // Since we requested this block (it was in mapBlocksInFlight), force it to be processed, + // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc) + // This bypasses some anti-DoS logic in AcceptBlock (eg to prevent + // disk-space attacks), but this should be safe due to the + // protections in the compact block handler -- see related comment + // in compact block optimistic reconstruction handling. + ProcessBlock(pfrom, pblock, /*force_processing=*/true, /*min_pow_checked=*/true); + } + return; +} + void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) @@ -3346,7 +3431,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, // - we are not in -blocksonly mode. const auto* tx_relay = peer->GetTxRelay(); if (tx_relay && WITH_LOCK(tx_relay->m_bloom_filter_mutex, return tx_relay->m_relay_txs) && - !pfrom.IsAddrFetchConn() && !m_ignore_incoming_txs) { + !pfrom.IsAddrFetchConn() && !m_opts.ignore_incoming_txs) { const uint64_t recon_salt = m_txreconciliation->PreRegisterPeer(pfrom.GetId()); m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDTXRCNCL, TXRECONCILIATION_VERSION, recon_salt)); @@ -4151,8 +4236,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, m_txrequest.ForgetTxHash(tx.GetWitnessHash()); // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789) - unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetIntArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); - m_orphanage.LimitOrphans(nMaxOrphanTx); + m_orphanage.LimitOrphans(m_opts.max_orphan_txs); } else { LogPrint(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString()); // We will continue to reject this tx since it has rejected @@ -4276,12 +4360,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, blockhash.ToString(), pfrom.GetId()); } - // When we succeed in decoding a block's txids from a cmpctblock - // message we typically jump to the BLOCKTXN handling code, with a - // dummy (empty) BLOCKTXN message, to re-use the logic there in - // completing processing of the putative block (without cs_main). bool fProcessBLOCKTXN = false; - CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION); // If we end up treating this as a plain headers message, call that as well // without cs_main. @@ -4382,10 +4461,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, req.indexes.push_back(i); } if (req.indexes.empty()) { - // Dirty hack to jump to BLOCKTXN code (TODO: move message handling into their own functions) - BlockTransactions txn; - txn.blockhash = blockhash; - blockTxnMsg << txn; fProcessBLOCKTXN = true; } else if (first_in_flight) { // We will try to round-trip any compact blocks we get on failure, @@ -4440,7 +4515,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, } // cs_main if (fProcessBLOCKTXN) { - return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, interruptMsgProc); + BlockTransactions txn; + txn.blockhash = blockhash; + return ProcessCompactBlockTxns(pfrom, *peer, txn); } if (fRevertToHeaderProcessing) { @@ -4492,88 +4569,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, BlockTransactions resp; vRecv >> resp; - std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); - bool fBlockRead = false; - { - LOCK(cs_main); - - auto range_flight = mapBlocksInFlight.equal_range(resp.blockhash); - size_t already_in_flight = std::distance(range_flight.first, range_flight.second); - bool requested_block_from_this_peer{false}; - - // Multimap ensures ordering of outstanding requests. It's either empty or first in line. - bool first_in_flight = already_in_flight == 0 || (range_flight.first->second.first == pfrom.GetId()); - - while (range_flight.first != range_flight.second) { - auto [node_id, block_it] = range_flight.first->second; - if (node_id == pfrom.GetId() && block_it->partialBlock) { - requested_block_from_this_peer = true; - break; - } - range_flight.first++; - } - - if (!requested_block_from_this_peer) { - LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom.GetId()); - return; - } - - PartiallyDownloadedBlock& partialBlock = *range_flight.first->second.second->partialBlock; - ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn); - if (status == READ_STATUS_INVALID) { - RemoveBlockRequest(resp.blockhash, pfrom.GetId()); // Reset in-flight state in case Misbehaving does not result in a disconnect - Misbehaving(*peer, 100, "invalid compact block/non-matching block transactions"); - return; - } else if (status == READ_STATUS_FAILED) { - if (first_in_flight) { - // Might have collided, fall back to getdata now :( - std::vector<CInv> invs; - invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(*peer), resp.blockhash)); - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); - } else { - RemoveBlockRequest(resp.blockhash, pfrom.GetId()); - LogPrint(BCLog::NET, "Peer %d sent us a compact block but it failed to reconstruct, waiting on first download to complete\n", pfrom.GetId()); - return; - } - } else { - // Block is either okay, or possibly we received - // READ_STATUS_CHECKBLOCK_FAILED. - // Note that CheckBlock can only fail for one of a few reasons: - // 1. bad-proof-of-work (impossible here, because we've already - // accepted the header) - // 2. merkleroot doesn't match the transactions given (already - // caught in FillBlock with READ_STATUS_FAILED, so - // impossible here) - // 3. the block is otherwise invalid (eg invalid coinbase, - // block is too big, too many legacy sigops, etc). - // So if CheckBlock failed, #3 is the only possibility. - // Under BIP 152, we don't discourage the peer unless proof of work is - // invalid (we don't require all the stateless checks to have - // been run). This is handled below, so just treat this as - // though the block was successfully read, and rely on the - // handling in ProcessNewBlock to ensure the block index is - // updated, etc. - RemoveBlockRequest(resp.blockhash, pfrom.GetId()); // it is now an empty pointer - fBlockRead = true; - // mapBlockSource is used for potentially punishing peers and - // updating which peers send us compact blocks, so the race - // between here and cs_main in ProcessNewBlock is fine. - // BIP 152 permits peers to relay compact blocks after validating - // the header only; we should not punish peers if the block turns - // out to be invalid. - mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom.GetId(), false)); - } - } // Don't hold cs_main when we call into ProcessNewBlock - if (fBlockRead) { - // Since we requested this block (it was in mapBlocksInFlight), force it to be processed, - // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc) - // This bypasses some anti-DoS logic in AcceptBlock (eg to prevent - // disk-space attacks), but this should be safe due to the - // protections in the compact block handler -- see related comment - // in compact block optimistic reconstruction handling. - ProcessBlock(pfrom, pblock, /*force_processing=*/true, /*min_pow_checked=*/true); - } - return; + return ProcessCompactBlockTxns(pfrom, *peer, resp); } if (msg_type == NetMsgType::HEADERS) @@ -5008,7 +5004,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt msg.m_recv.data() ); - if (gArgs.GetBoolArg("-capturemessages", false)) { + if (m_opts.capture_messages) { CaptureMessage(pfrom->addr, msg.m_type, MakeUCharSpan(msg.m_recv), /*is_incoming=*/true); } @@ -5358,7 +5354,7 @@ void PeerManagerImpl::MaybeSendSendHeaders(CNode& node, Peer& peer) void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::microseconds current_time) { - if (m_ignore_incoming_txs) return; + if (m_opts.ignore_incoming_txs) return; if (pto.GetCommonVersion() < FEEFILTER_VERSION) return; // peers with the forcerelay permission should not filter txs to us if (pto.HasPermission(NetPermissionFlags::ForceRelay)) return; @@ -5426,7 +5422,7 @@ bool PeerManagerImpl::RejectIncomingTxs(const CNode& peer) const if (peer.IsBlockOnlyConn()) return true; if (peer.IsFeelerConn()) return true; // In -blocksonly mode, peers need the 'relay' permission to send txs to us - if (m_ignore_incoming_txs && !peer.HasPermission(NetPermissionFlags::Relay)) return true; + if (m_opts.ignore_incoming_txs && !peer.HasPermission(NetPermissionFlags::Relay)) return true; return false; } diff --git a/src/net_processing.h b/src/net_processing.h index deebb24c94..a0cbe92289 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -14,6 +14,8 @@ class CChainParams; class CTxMemPool; class ChainstateManager; +/** Whether transaction reconciliation protocol should be enabled by default. */ +static constexpr bool DEFAULT_TXRECONCILIATION_ENABLE{false}; /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; /** Default number of orphan+recently-replaced txn to keep around for block reconstruction */ @@ -43,9 +45,18 @@ struct CNodeStateStats { class PeerManager : public CValidationInterface, public NetEventsInterface { public: + struct Options { + /** Whether this node is running in -blocksonly mode */ + bool ignore_incoming_txs{DEFAULT_BLOCKSONLY}; + bool reconcile_txs{DEFAULT_TXRECONCILIATION_ENABLE}; + uint32_t max_orphan_txs{DEFAULT_MAX_ORPHAN_TRANSACTIONS}; + size_t max_extra_txs{DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN}; + bool capture_messages{false}; + }; + static std::unique_ptr<PeerManager> make(CConnman& connman, AddrMan& addrman, BanMan* banman, ChainstateManager& chainman, - CTxMemPool& pool, bool ignore_incoming_txs); + CTxMemPool& pool, Options opts); virtual ~PeerManager() { } /** diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 85ae8fab36..4758f24680 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -723,19 +723,16 @@ std::vector<unsigned char> CNetAddr::GetAddrBytes() const // private extensions to enum Network, only returned by GetExtNetwork, // and only used in GetReachabilityFrom -static const int NET_UNKNOWN = NET_MAX + 0; -static const int NET_TEREDO = NET_MAX + 1; -int static GetExtNetwork(const CNetAddr *addr) +static const int NET_TEREDO = NET_MAX; +int static GetExtNetwork(const CNetAddr& addr) { - if (addr == nullptr) - return NET_UNKNOWN; - if (addr->IsRFC4380()) + if (addr.IsRFC4380()) return NET_TEREDO; - return addr->GetNetwork(); + return addr.GetNetwork(); } /** Calculates a metric for how reachable (*this) is from a given partner */ -int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const +int CNetAddr::GetReachabilityFrom(const CNetAddr& paddrPartner) const { enum Reachability { REACH_UNREACHABLE, @@ -750,7 +747,7 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const if (!IsRoutable() || IsInternal()) return REACH_UNREACHABLE; - int ourNet = GetExtNetwork(this); + int ourNet = GetExtNetwork(*this); int theirNet = GetExtNetwork(paddrPartner); bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145(); @@ -790,7 +787,6 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const case NET_IPV6: return REACH_IPV6_WEAK; case NET_IPV4: return REACH_IPV4; } - case NET_UNKNOWN: case NET_UNROUTABLE: default: switch(ourNet) { diff --git a/src/netaddress.h b/src/netaddress.h index 3d15b0b123..7cba6c00d0 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -203,7 +203,7 @@ public: bool HasLinkedIPv4() const; std::vector<unsigned char> GetAddrBytes() const; - int GetReachabilityFrom(const CNetAddr* paddrPartner = nullptr) const; + int GetReachabilityFrom(const CNetAddr& paddrPartner) const; explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0); bool GetIn6Addr(struct in6_addr* pipv6Addr) const; @@ -564,7 +564,7 @@ public: CSipHasher hasher(m_salt_k0, m_salt_k1); hasher.Write(a.m_net); hasher.Write(a.port); - hasher.Write(a.m_addr.data(), a.m_addr.size()); + hasher.Write(a.m_addr); return static_cast<size_t>(hasher.Finalize()); } diff --git a/src/netbase.cpp b/src/netbase.cpp index 8f6f92ea7d..a8419217f4 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -21,14 +21,6 @@ #include <limits> #include <memory> -#ifndef WIN32 -#include <fcntl.h> -#endif - -#ifdef USE_POLL -#include <poll.h> -#endif - // Settings static GlobalMutex g_proxyinfo_mutex; static Proxy proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex); diff --git a/src/node/abort.cpp b/src/node/abort.cpp new file mode 100644 index 0000000000..a554126b86 --- /dev/null +++ b/src/node/abort.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <node/abort.h> + +#include <logging.h> +#include <node/interface_ui.h> +#include <shutdown.h> +#include <util/translation.h> +#include <warnings.h> + +#include <atomic> +#include <cstdlib> +#include <string> + +namespace node { + +void AbortNode(std::atomic<int>& exit_status, const std::string& debug_message, const bilingual_str& user_message, bool shutdown) +{ + SetMiscWarning(Untranslated(debug_message)); + LogPrintf("*** %s\n", debug_message); + InitError(user_message.empty() ? _("A fatal internal error occurred, see debug.log for details") : user_message); + exit_status.store(EXIT_FAILURE); + if (shutdown) StartShutdown(); +} +} // namespace node diff --git a/src/node/abort.h b/src/node/abort.h new file mode 100644 index 0000000000..d6bb0c14d5 --- /dev/null +++ b/src/node/abort.h @@ -0,0 +1,17 @@ +// Copyright (c) 2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_ABORT_H +#define BITCOIN_NODE_ABORT_H + +#include <util/translation.h> + +#include <atomic> +#include <string> + +namespace node { +void AbortNode(std::atomic<int>& exit_status, const std::string& debug_message, const bilingual_str& user_message = {}, bool shutdown = true); +} // namespace node + +#endif // BITCOIN_NODE_ABORT_H diff --git a/src/node/blockmanager_args.cpp b/src/node/blockmanager_args.cpp index 4b296db1b0..fa76566652 100644 --- a/src/node/blockmanager_args.cpp +++ b/src/node/blockmanager_args.cpp @@ -32,7 +32,6 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, BlockManager::Op opts.prune_target = nPruneTarget; if (auto value{args.GetBoolArg("-fastprune")}) opts.fast_prune = *value; - if (auto value{args.GetBoolArg("-stopafterblockimport")}) opts.stop_after_block_import = *value; return {}; } diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index 87cd291c7e..78416ec576 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -13,13 +13,12 @@ #include <logging.h> #include <pow.h> #include <reverse_iterator.h> -#include <shutdown.h> #include <signet.h> #include <streams.h> #include <undo.h> #include <util/batchpriority.h> #include <util/fs.h> -#include <util/syscall_sandbox.h> +#include <util/signalinterrupt.h> #include <validation.h> #include <map> @@ -27,7 +26,6 @@ namespace node { std::atomic_bool fReindex(false); -std::atomic_bool g_indexes_ready_to_sync{false}; bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const { @@ -251,7 +249,8 @@ CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash) bool BlockManager::LoadBlockIndex() { - if (!m_block_tree_db->LoadBlockIndexGuts(GetConsensus(), [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) { + if (!m_block_tree_db->LoadBlockIndexGuts( + GetConsensus(), [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }, m_interrupt)) { return false; } @@ -261,7 +260,7 @@ bool BlockManager::LoadBlockIndex() CBlockIndexHeightOnlyComparator()); for (CBlockIndex* pindex : vSortedByHeight) { - if (ShutdownRequested()) return false; + if (m_interrupt) return false; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime); @@ -403,16 +402,31 @@ bool BlockManager::IsBlockPruned(const CBlockIndex* pblockindex) return (m_have_pruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0); } -const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& start_block) +const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& upper_block, const CBlockIndex* lower_block) { AssertLockHeld(::cs_main); - const CBlockIndex* last_block = &start_block; + const CBlockIndex* last_block = &upper_block; + assert(last_block->nStatus & BLOCK_HAVE_DATA); // 'upper_block' must have data while (last_block->pprev && (last_block->pprev->nStatus & BLOCK_HAVE_DATA)) { + if (lower_block) { + // Return if we reached the lower_block + if (last_block == lower_block) return lower_block; + // if range was surpassed, means that 'lower_block' is not part of the 'upper_block' chain + // and so far this is not allowed. + assert(last_block->nHeight >= lower_block->nHeight); + } last_block = last_block->pprev; } + assert(last_block != nullptr); return last_block; } +bool BlockManager::CheckBlockDataAvailability(const CBlockIndex& upper_block, const CBlockIndex& lower_block) +{ + if (!(upper_block.nStatus & BLOCK_HAVE_DATA)) return false; + return GetFirstStoredBlock(upper_block, &lower_block) == &lower_block; +} + // If we're using -prune with -reindex, then delete block files that will be ignored by the // reindex. Since reindexing works by starting at block file 0 and looping until a blockfile // is missing, do the same here to delete any later block files after a gap. Also delete all @@ -528,7 +542,7 @@ void BlockManager::FlushUndoFile(int block_file, bool finalize) { FlatFilePos undo_pos_old(block_file, m_blockfile_info[block_file].nUndoSize); if (!UndoFileSeq().Flush(undo_pos_old, finalize)) { - AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error."); + m_opts.notifications.flushError("Flushing undo file to disk failed. This is likely the result of an I/O error."); } } @@ -547,7 +561,7 @@ void BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo) FlatFilePos block_pos_old(m_last_blockfile, m_blockfile_info[m_last_blockfile].nSize); if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) { - AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error."); + m_opts.notifications.flushError("Flushing block file to disk failed. This is likely the result of an I/O error."); } // we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks, // e.g. during IBD or a sync after a node going offline @@ -659,7 +673,8 @@ bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigne bool out_of_space; size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space); if (out_of_space) { - return AbortNode("Disk space is too low!", _("Disk space is too low!")); + m_opts.notifications.fatalError("Disk space is too low!", _("Disk space is too low!")); + return false; } if (bytes_allocated != 0 && IsPruneMode()) { m_check_for_pruning = true; @@ -683,7 +698,7 @@ bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFileP bool out_of_space; size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space); if (out_of_space) { - return AbortNode(state, "Disk space is too low!", _("Disk space is too low!")); + return FatalError(m_opts.notifications, state, "Disk space is too low!", _("Disk space is too low!")); } if (bytes_allocated != 0 && IsPruneMode()) { m_check_for_pruning = true; @@ -725,7 +740,7 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid return error("ConnectBlock(): FindUndoPos failed"); } if (!UndoWriteToDisk(blockundo, _pos, block.pprev->GetBlockHash(), GetParams().MessageStart())) { - return AbortNode(state, "Failed to write undo data"); + return FatalError(m_opts.notifications, state, "Failed to write undo data"); } // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order) // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height @@ -843,7 +858,7 @@ FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CCha } if (!position_known) { if (!WriteBlockToDisk(block, blockPos, GetParams().MessageStart())) { - AbortNode("Failed to write block"); + m_opts.notifications.fatalError("Failed to write block"); return FlatFilePos(); } } @@ -867,9 +882,8 @@ public: } }; -void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const fs::path& mempool_path) +void ImportBlocks(ChainstateManager& chainman, std::vector<fs::path> vImportFiles) { - SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION_LOAD_BLOCKS); ScheduleBatchPriority(); { @@ -892,8 +906,8 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile } LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); chainman.ActiveChainstate().LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent); - if (ShutdownRequested()) { - LogPrintf("Shutdown requested. Exit %s\n", __func__); + if (chainman.m_interrupt) { + LogPrintf("Interrupt requested. Exit %s\n", __func__); return; } nFile++; @@ -911,8 +925,8 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile if (file) { LogPrintf("Importing blocks file %s...\n", fs::PathToString(path)); chainman.ActiveChainstate().LoadExternalBlockFile(file); - if (ShutdownRequested()) { - LogPrintf("Shutdown requested. Exit %s\n", __func__); + if (chainman.m_interrupt) { + LogPrintf("Interrupt requested. Exit %s\n", __func__); return; } } else { @@ -928,18 +942,10 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) { BlockValidationState state; if (!chainstate->ActivateBestChain(state, nullptr)) { - AbortNode(strprintf("Failed to connect best block (%s)", state.ToString())); + chainman.GetNotifications().fatalError(strprintf("Failed to connect best block (%s)", state.ToString())); return; } } - - if (chainman.m_blockman.StopAfterBlockImport()) { - LogPrintf("Stopping after block import\n"); - StartShutdown(); - return; - } } // End scope of ImportingNow - chainman.ActiveChainstate().LoadMempool(mempool_path); - g_indexes_ready_to_sync = true; } } // namespace node diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index a1ebb0df0a..c2e903e470 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -33,6 +33,9 @@ struct FlatFilePos; namespace Consensus { struct Params; } +namespace util { +class SignalInterrupt; +} // namespace util namespace node { @@ -47,7 +50,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE = CMessageHeader::MESSAGE_START_SIZE + sizeof(unsigned int); extern std::atomic_bool fReindex; -extern std::atomic_bool g_indexes_ready_to_sync; // Because validation code takes pointers to the map's CBlockIndex objects, if // we ever switch to another associative container, we need to either use a @@ -153,10 +155,12 @@ private: public: using Options = kernel::BlockManagerOpts; - explicit BlockManager(Options opts) + explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts) : m_prune_mode{opts.prune_target > 0}, - m_opts{std::move(opts)} {}; + m_opts{std::move(opts)}, + m_interrupt{interrupt} {}; + const util::SignalInterrupt& m_interrupt; std::atomic<bool> m_importing{false}; BlockMap m_block_index GUARDED_BY(cs_main); @@ -209,16 +213,21 @@ public: [[nodiscard]] bool LoadingBlocks() const { return m_importing || fReindex; } - [[nodiscard]] bool StopAfterBlockImport() const { return m_opts.stop_after_block_import; } - /** Calculate the amount of disk space the block & undo files currently use */ uint64_t CalculateCurrentUsage(); //! Returns last CBlockIndex* that is a checkpoint const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - //! Find the first block that is not pruned - const CBlockIndex* GetFirstStoredBlock(const CBlockIndex& start_block LIFETIMEBOUND) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + //! Check if all blocks in the [upper_block, lower_block] range have data available. + //! The caller is responsible for ensuring that lower_block is an ancestor of upper_block + //! (part of the same chain). + bool CheckBlockDataAvailability(const CBlockIndex& upper_block LIFETIMEBOUND, const CBlockIndex& lower_block LIFETIMEBOUND) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + + //! Find the first stored ancestor of start_block immediately after the last + //! pruned ancestor. Return value will never be null. Caller is responsible + //! for ensuring that start_block has data is not pruned. + const CBlockIndex* GetFirstStoredBlock(const CBlockIndex& start_block LIFETIMEBOUND, const CBlockIndex* lower_block=nullptr) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); /** True if any block files have ever been pruned. */ bool m_have_pruned = false; @@ -250,7 +259,7 @@ public: void CleanupBlockRevFiles() const; }; -void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const fs::path& mempool_path); +void ImportBlocks(ChainstateManager& chainman, std::vector<fs::path> vImportFiles); } // namespace node #endif // BITCOIN_NODE_BLOCKSTORAGE_H diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 3900d2e620..255d8be0ec 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -82,7 +82,7 @@ static ChainstateLoadResult CompleteChainstateInitialization( // At this point blocktree args are consistent with what's on disk. // If we're not mid-reindex (based on disk + args), add a genesis block on disk // (otherwise we use the one already on disk). - // This is called again in ThreadImport after the reindex completes. + // This is called again in ImportBlocks after the reindex completes. if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock()) { return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")}; } diff --git a/src/node/chainstatemanager_args.cpp b/src/node/chainstatemanager_args.cpp index a7f7303348..87d9238c18 100644 --- a/src/node/chainstatemanager_args.cpp +++ b/src/node/chainstatemanager_args.cpp @@ -37,8 +37,6 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManage if (auto value{args.GetIntArg("-maxtipage")}) opts.max_tip_age = std::chrono::seconds{*value}; - if (auto value{args.GetIntArg("-stopatheight")}) opts.stop_at_height = *value; - ReadDatabaseArgs(args, opts.block_tree_db); ReadDatabaseArgs(args, opts.coins_db); ReadCoinsViewArgs(args, opts.coins_view); diff --git a/src/node/context.h b/src/node/context.h index 91b68fa5bb..dae900ff7f 100644 --- a/src/node/context.h +++ b/src/node/context.h @@ -15,8 +15,9 @@ #include <vector> class ArgsManager; -class BanMan; class AddrMan; +class BanMan; +class BaseIndex; class CBlockPolicyEstimator; class CConnman; class CScheduler; @@ -58,6 +59,7 @@ struct NodeContext { std::unique_ptr<ChainstateManager> chainman; std::unique_ptr<BanMan> banman; ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct + std::vector<BaseIndex*> indexes; // raw pointers because memory is not managed by this struct std::unique_ptr<interfaces::Chain> chain; //! List of all chain clients (wallet processes or other client) connected to node. std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 94b607b1de..9c98e4cf0c 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -94,7 +94,7 @@ public: bool baseInitialize() override { if (!AppInitBasicSetup(args(), Assert(context())->exit_status)) return false; - if (!AppInitParameterInteraction(args(), /*use_syscall_sandbox=*/false)) return false; + if (!AppInitParameterInteraction(args())) return false; m_context->kernel = std::make_unique<kernel::Context>(); if (!AppInitSanityChecks(*m_context->kernel)) return false; diff --git a/src/node/kernel_notifications.cpp b/src/node/kernel_notifications.cpp index 926b157f3b..7224127c72 100644 --- a/src/node/kernel_notifications.cpp +++ b/src/node/kernel_notifications.cpp @@ -8,9 +8,15 @@ #include <config/bitcoin-config.h> #endif +#include <chain.h> #include <common/args.h> #include <common/system.h> +#include <kernel/context.h> +#include <logging.h> +#include <node/abort.h> #include <node/interface_ui.h> +#include <shutdown.h> +#include <util/check.h> #include <util/strencodings.h> #include <util/string.h> #include <util/translation.h> @@ -52,9 +58,14 @@ static void DoWarning(const bilingual_str& warning) namespace node { -void KernelNotifications::blockTip(SynchronizationState state, CBlockIndex& index) +kernel::InterruptResult KernelNotifications::blockTip(SynchronizationState state, CBlockIndex& index) { uiInterface.NotifyBlockTip(state, &index); + if (m_stop_at_height && index.nHeight >= m_stop_at_height) { + StartShutdown(); + return kernel::Interrupted{}; + } + return {}; } void KernelNotifications::headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) @@ -72,4 +83,19 @@ void KernelNotifications::warning(const bilingual_str& warning) DoWarning(warning); } +void KernelNotifications::flushError(const std::string& debug_message) +{ + AbortNode(m_exit_status, debug_message); +} + +void KernelNotifications::fatalError(const std::string& debug_message, const bilingual_str& user_message) +{ + node::AbortNode(m_exit_status, debug_message, user_message, m_shutdown_on_fatal_error); +} + +void ReadNotificationArgs(const ArgsManager& args, KernelNotifications& notifications) +{ + if (auto value{args.GetIntArg("-stopatheight")}) notifications.m_stop_at_height = *value; +} + } // namespace node diff --git a/src/node/kernel_notifications.h b/src/node/kernel_notifications.h index 3e665bbf14..b2dfc03398 100644 --- a/src/node/kernel_notifications.h +++ b/src/node/kernel_notifications.h @@ -7,25 +7,46 @@ #include <kernel/notifications_interface.h> +#include <atomic> #include <cstdint> #include <string> +class ArgsManager; class CBlockIndex; enum class SynchronizationState; struct bilingual_str; namespace node { + +static constexpr int DEFAULT_STOPATHEIGHT{0}; + class KernelNotifications : public kernel::Notifications { public: - void blockTip(SynchronizationState state, CBlockIndex& index) override; + KernelNotifications(std::atomic<int>& exit_status) : m_exit_status{exit_status} {} + + [[nodiscard]] kernel::InterruptResult blockTip(SynchronizationState state, CBlockIndex& index) override; void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) override; void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override; void warning(const bilingual_str& warning) override; + + void flushError(const std::string& debug_message) override; + + void fatalError(const std::string& debug_message, const bilingual_str& user_message = {}) override; + + //! Block height after which blockTip notification will return Interrupted{}, if >0. + int m_stop_at_height{DEFAULT_STOPATHEIGHT}; + //! Useful for tests, can be set to false to avoid shutdown on fatal error. + bool m_shutdown_on_fatal_error{true}; +private: + std::atomic<int>& m_exit_status; }; + +void ReadNotificationArgs(const ArgsManager& args, KernelNotifications& notifications); + } // namespace node #endif // BITCOIN_NODE_KERNEL_NOTIFICATIONS_H diff --git a/src/node/peerman_args.cpp b/src/node/peerman_args.cpp new file mode 100644 index 0000000000..e5868ead12 --- /dev/null +++ b/src/node/peerman_args.cpp @@ -0,0 +1,24 @@ +#include <node/peerman_args.h> + +#include <common/args.h> +#include <net_processing.h> + +namespace node { + +void ApplyArgsManOptions(const ArgsManager& argsman, PeerManager::Options& options) +{ + if (auto value{argsman.GetBoolArg("-txreconciliation")}) options.reconcile_txs = *value; + + if (auto value{argsman.GetIntArg("-maxorphantx")}) { + options.max_orphan_txs = uint32_t(std::max(int64_t{0}, *value)); + } + + if (auto value{argsman.GetIntArg("-blockreconstructionextratxn")}) { + options.max_extra_txs = size_t(std::max(int64_t{0}, *value)); + } + + if (auto value{argsman.GetBoolArg("-capturemessages")}) options.capture_messages = *value; +} + +} // namespace node + diff --git a/src/node/peerman_args.h b/src/node/peerman_args.h new file mode 100644 index 0000000000..73dbdb446c --- /dev/null +++ b/src/node/peerman_args.h @@ -0,0 +1,12 @@ +#ifndef BITCOIN_NODE_PEERMAN_ARGS_H +#define BITCOIN_NODE_PEERMAN_ARGS_H + +#include <net_processing.h> + +class ArgsManager; + +namespace node { +void ApplyArgsManOptions(const ArgsManager& argsman, PeerManager::Options& options); +} // namespace node + +#endif // BITCOIN_NODE_PEERMAN_ARGS_H diff --git a/src/node/txreconciliation.h b/src/node/txreconciliation.h index 4591dd5df7..3bbb077366 100644 --- a/src/node/txreconciliation.h +++ b/src/node/txreconciliation.h @@ -11,8 +11,6 @@ #include <memory> #include <tuple> -/** Whether transaction reconciliation protocol should be enabled by default. */ -static constexpr bool DEFAULT_TXRECONCILIATION_ENABLE{false}; /** Supported transaction reconciliation protocol version */ static constexpr uint32_t TXRECONCILIATION_VERSION{1}; diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 6f859e2d0d..41f4a4d06b 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -62,7 +62,7 @@ public: /** * Return the fee in satoshis for a vsize of 1000 vbytes */ - CAmount GetFeePerK() const { return GetFee(1000); } + CAmount GetFeePerK() const { return nSatoshisPerK; } friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; } friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; } friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; } diff --git a/src/prevector.h b/src/prevector.h index f36cfe4ff6..bcab1ff00c 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -264,8 +264,10 @@ public: fill(item_ptr(0), other.begin(), other.end()); } - prevector(prevector<N, T, Size, Diff>&& other) { - swap(other); + prevector(prevector<N, T, Size, Diff>&& other) noexcept + : _union(std::move(other._union)), _size(other._size) + { + other._size = 0; } prevector& operator=(const prevector<N, T, Size, Diff>& other) { @@ -276,8 +278,13 @@ public: return *this; } - prevector& operator=(prevector<N, T, Size, Diff>&& other) { - swap(other); + prevector& operator=(prevector<N, T, Size, Diff>&& other) noexcept { + if (!is_direct()) { + free(_union.indirect_contents.indirect); + } + _union = std::move(other._union); + _size = other._size; + other._size = 0; return *this; } diff --git a/src/protocol.cpp b/src/protocol.cpp index 5ecaabec36..2105480c72 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -12,47 +12,47 @@ static std::atomic<bool> g_initial_block_download_completed(false); namespace NetMsgType { -const char *VERSION="version"; -const char *VERACK="verack"; -const char *ADDR="addr"; -const char *ADDRV2="addrv2"; -const char *SENDADDRV2="sendaddrv2"; -const char *INV="inv"; -const char *GETDATA="getdata"; -const char *MERKLEBLOCK="merkleblock"; -const char *GETBLOCKS="getblocks"; -const char *GETHEADERS="getheaders"; -const char *TX="tx"; -const char *HEADERS="headers"; -const char *BLOCK="block"; -const char *GETADDR="getaddr"; -const char *MEMPOOL="mempool"; -const char *PING="ping"; -const char *PONG="pong"; -const char *NOTFOUND="notfound"; -const char *FILTERLOAD="filterload"; -const char *FILTERADD="filteradd"; -const char *FILTERCLEAR="filterclear"; -const char *SENDHEADERS="sendheaders"; -const char *FEEFILTER="feefilter"; -const char *SENDCMPCT="sendcmpct"; -const char *CMPCTBLOCK="cmpctblock"; -const char *GETBLOCKTXN="getblocktxn"; -const char *BLOCKTXN="blocktxn"; -const char *GETCFILTERS="getcfilters"; -const char *CFILTER="cfilter"; -const char *GETCFHEADERS="getcfheaders"; -const char *CFHEADERS="cfheaders"; -const char *GETCFCHECKPT="getcfcheckpt"; -const char *CFCHECKPT="cfcheckpt"; -const char *WTXIDRELAY="wtxidrelay"; -const char *SENDTXRCNCL="sendtxrcncl"; +const char* VERSION = "version"; +const char* VERACK = "verack"; +const char* ADDR = "addr"; +const char* ADDRV2 = "addrv2"; +const char* SENDADDRV2 = "sendaddrv2"; +const char* INV = "inv"; +const char* GETDATA = "getdata"; +const char* MERKLEBLOCK = "merkleblock"; +const char* GETBLOCKS = "getblocks"; +const char* GETHEADERS = "getheaders"; +const char* TX = "tx"; +const char* HEADERS = "headers"; +const char* BLOCK = "block"; +const char* GETADDR = "getaddr"; +const char* MEMPOOL = "mempool"; +const char* PING = "ping"; +const char* PONG = "pong"; +const char* NOTFOUND = "notfound"; +const char* FILTERLOAD = "filterload"; +const char* FILTERADD = "filteradd"; +const char* FILTERCLEAR = "filterclear"; +const char* SENDHEADERS = "sendheaders"; +const char* FEEFILTER = "feefilter"; +const char* SENDCMPCT = "sendcmpct"; +const char* CMPCTBLOCK = "cmpctblock"; +const char* GETBLOCKTXN = "getblocktxn"; +const char* BLOCKTXN = "blocktxn"; +const char* GETCFILTERS = "getcfilters"; +const char* CFILTER = "cfilter"; +const char* GETCFHEADERS = "getcfheaders"; +const char* CFHEADERS = "cfheaders"; +const char* GETCFCHECKPT = "getcfcheckpt"; +const char* CFCHECKPT = "cfcheckpt"; +const char* WTXIDRELAY = "wtxidrelay"; +const char* SENDTXRCNCL = "sendtxrcncl"; } // namespace NetMsgType /** All known message types. Keep this in the same order as the list of * messages above and in protocol.h. */ -const static std::string allNetMessageTypes[] = { +const static std::vector<std::string> g_all_net_message_types{ NetMsgType::VERSION, NetMsgType::VERACK, NetMsgType::ADDR, @@ -89,7 +89,6 @@ const static std::string allNetMessageTypes[] = { NetMsgType::WTXIDRELAY, NetMsgType::SENDTXRCNCL, }; -const static std::vector<std::string> allNetMessageTypesVec(std::begin(allNetMessageTypes), std::end(allNetMessageTypes)); CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn) { @@ -182,7 +181,7 @@ std::string CInv::ToString() const const std::vector<std::string> &getAllNetMessageTypes() { - return allNetMessageTypesVec; + return g_all_net_message_types; } /** diff --git a/src/pubkey.cpp b/src/pubkey.cpp index ae5dccfb5a..4866feed67 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -7,6 +7,7 @@ #include <hash.h> #include <secp256k1.h> +#include <secp256k1_ellswift.h> #include <secp256k1_extrakeys.h> #include <secp256k1_recovery.h> #include <secp256k1_schnorrsig.h> @@ -335,6 +336,20 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi return true; } +CPubKey EllSwiftPubKey::Decode() const +{ + secp256k1_pubkey pubkey; + secp256k1_ellswift_decode(secp256k1_context_static, &pubkey, UCharCast(m_pubkey.data())); + + size_t sz = CPubKey::COMPRESSED_SIZE; + std::array<uint8_t, CPubKey::COMPRESSED_SIZE> vch_bytes; + + secp256k1_ec_pubkey_serialize(secp256k1_context_static, vch_bytes.data(), &sz, &pubkey, SECP256K1_EC_COMPRESSED); + assert(sz == vch_bytes.size()); + + return CPubKey{vch_bytes.begin(), vch_bytes.end()}; +} + void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const { code[0] = nDepth; memcpy(code+1, vchFingerprint, 4); diff --git a/src/pubkey.h b/src/pubkey.h index b3edafea7f..7d37504b01 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -142,14 +142,14 @@ public: { unsigned int len = size(); ::WriteCompactSize(s, len); - s.write(AsBytes(Span{vch, len})); + s << Span{vch, len}; } template <typename Stream> void Unserialize(Stream& s) { const unsigned int len(::ReadCompactSize(s)); if (len <= SIZE) { - s.read(AsWritableBytes(Span{vch, len})); + s >> Span{vch, len}; if (len != size()) { Invalidate(); } @@ -291,6 +291,38 @@ public: SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); } }; +/** An ElligatorSwift-encoded public key. */ +struct EllSwiftPubKey +{ +private: + static constexpr size_t SIZE = 64; + std::array<std::byte, SIZE> m_pubkey; + +public: + /** Construct a new ellswift public key from a given serialization. */ + EllSwiftPubKey(const std::array<std::byte, SIZE>& ellswift) : + m_pubkey(ellswift) {} + + /** Decode to normal compressed CPubKey (for debugging purposes). */ + CPubKey Decode() const; + + // Read-only access for serialization. + const std::byte* data() const { return m_pubkey.data(); } + static constexpr size_t size() { return SIZE; } + auto begin() const { return m_pubkey.cbegin(); } + auto end() const { return m_pubkey.cend(); } + + bool friend operator==(const EllSwiftPubKey& a, const EllSwiftPubKey& b) + { + return a.m_pubkey == b.m_pubkey; + } + + bool friend operator!=(const EllSwiftPubKey& a, const EllSwiftPubKey& b) + { + return a.m_pubkey != b.m_pubkey; + } +}; + struct CExtPubKey { unsigned char version[4]; unsigned char nDepth; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f201d8fa01..fd329faf18 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -412,6 +412,7 @@ void BitcoinGUI::createActions() connect(action, &QAction::triggered, [this, path] { auto activity = new OpenWalletActivity(m_wallet_controller, this); connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet, Qt::QueuedConnection); + connect(activity, &OpenWalletActivity::opened, rpcConsole, &RPCConsole::setCurrentWallet, Qt::QueuedConnection); activity->open(path); }); } @@ -441,6 +442,7 @@ void BitcoinGUI::createActions() auto activity = new RestoreWalletActivity(m_wallet_controller, this); connect(activity, &RestoreWalletActivity::restored, this, &BitcoinGUI::setCurrentWallet, Qt::QueuedConnection); + connect(activity, &RestoreWalletActivity::restored, rpcConsole, &RPCConsole::setCurrentWallet, Qt::QueuedConnection); auto backup_file_path = fs::PathFromString(backup_file.toStdString()); activity->restore(backup_file_path, wallet_name.toStdString()); @@ -451,6 +453,7 @@ void BitcoinGUI::createActions() connect(m_create_wallet_action, &QAction::triggered, [this] { auto activity = new CreateWalletActivity(m_wallet_controller, this); connect(activity, &CreateWalletActivity::created, this, &BitcoinGUI::setCurrentWallet); + connect(activity, &CreateWalletActivity::created, rpcConsole, &RPCConsole::setCurrentWallet); activity->create(); }); connect(m_close_all_wallets_action, &QAction::triggered, [this] { diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 50d3c2e559..38c7656e00 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -70,7 +70,6 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); - QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this); QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); connect(clipboardQuantityAction, &QAction::triggered, this, &CoinControlDialog::clipboardQuantity); @@ -78,7 +77,6 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m connect(clipboardFeeAction, &QAction::triggered, this, &CoinControlDialog::clipboardFee); connect(clipboardAfterFeeAction, &QAction::triggered, this, &CoinControlDialog::clipboardAfterFee); connect(clipboardBytesAction, &QAction::triggered, this, &CoinControlDialog::clipboardBytes); - connect(clipboardLowOutputAction, &QAction::triggered, this, &CoinControlDialog::clipboardLowOutput); connect(clipboardChangeAction, &QAction::triggered, this, &CoinControlDialog::clipboardChange); ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); @@ -86,7 +84,6 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m ui->labelCoinControlFee->addAction(clipboardFeeAction); ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); ui->labelCoinControlBytes->addAction(clipboardBytesAction); - ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); ui->labelCoinControlChange->addAction(clipboardChangeAction); // toggle tree/list mode @@ -294,12 +291,6 @@ void CoinControlDialog::clipboardBytes() GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, "")); } -// copy label "Dust" to clipboard -void CoinControlDialog::clipboardLowOutput() -{ - GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text()); -} - // copy label "Change" to clipboard void CoinControlDialog::clipboardChange() { @@ -390,17 +381,8 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * // nPayAmount CAmount nPayAmount = 0; - bool fDust = false; - for (const CAmount &amount : CoinControlDialog::payAmounts) - { + for (const CAmount &amount : CoinControlDialog::payAmounts) { nPayAmount += amount; - - if (amount > 0) - { - // Assumes a p2pkh script size - CTxOut txout(amount, CScript() << std::vector<unsigned char>(24, 0)); - fDust |= IsDust(txout, model->node().getDustRelayFee()); - } } CAmount nAmount = 0; @@ -515,12 +497,9 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee"); QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee"); QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes"); - QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput"); QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange"); - // enable/disable "dust" and "change" - dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0); - dialog->findChild<QLabel *>("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0); + // enable/disable "change" dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0); dialog->findChild<QLabel *>("labelCoinControlChange") ->setEnabled(nPayAmount > 0); @@ -530,7 +509,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes - l7->setText(fDust ? tr("yes") : tr("no")); // Dust l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change if (nPayFee > 0) { @@ -540,12 +518,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * l8->setText(ASYMP_UTF8 + l8->text()); } - // turn label red when dust - l7->setStyleSheet((fDust) ? "color:red;" : ""); - - // tool tips - QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold."); - // how many satoshis the estimated fee can vary per byte we guess wrong double dFeeVary = (nBytes != 0) ? (double)nPayFee / nBytes : 0; @@ -553,12 +525,10 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * l3->setToolTip(toolTip4); l4->setToolTip(toolTip4); - l7->setToolTip(toolTipDust); l8->setToolTip(toolTip4); dialog->findChild<QLabel *>("labelCoinControlFeeText") ->setToolTip(l3->toolTip()); dialog->findChild<QLabel *>("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip()); dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip()); - dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip()); dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip()); // Insufficient funds diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index ccceff10e8..5fc2cdca9b 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -105,7 +105,6 @@ private Q_SLOTS: void clipboardFee(); void clipboardAfterFee(); void clipboardBytes(); - void clipboardLowOutput(); void clipboardChange(); void radioTreeMode(bool); void radioListMode(bool); diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui index 5ce469ee96..f053adba0f 100644 --- a/src/qt/forms/coincontroldialog.ui +++ b/src/qt/forms/coincontroldialog.ui @@ -24,6 +24,9 @@ </property> <item> <layout class="QFormLayout" name="formLayoutCoinControl1"> + <property name="labelAlignment"> + <set>Qt::AlignLeft|Qt::AlignVCenter</set> + </property> <property name="horizontalSpacing"> <number>10</number> </property> @@ -139,41 +142,6 @@ </property> </widget> </item> - <item row="1" column="0"> - <widget class="QLabel" name="labelCoinControlLowOutputText"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>Dust:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLabel" name="labelCoinControlLowOutput"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="contextMenuPolicy"> - <enum>Qt::ActionsContextMenu</enum> - </property> - <property name="text"> - <string notr="true">no</string> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> </layout> </item> <item> @@ -223,6 +191,9 @@ </item> <item> <layout class="QFormLayout" name="formLayoutCoinControl4"> + <property name="labelAlignment"> + <set>Qt::AlignLeft|Qt::AlignVCenter</set> + </property> <property name="horizontalSpacing"> <number>10</number> </property> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 37da6fedda..1ca92440c8 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -201,6 +201,9 @@ </property> <item> <layout class="QFormLayout" name="formLayoutCoinControl1"> + <property name="labelAlignment"> + <set>Qt::AlignLeft|Qt::AlignVCenter</set> + </property> <property name="horizontalSpacing"> <number>10</number> </property> @@ -331,35 +334,6 @@ </property> </widget> </item> - <item row="1" column="0"> - <widget class="QLabel" name="labelCoinControlLowOutputText"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>Dust:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLabel" name="labelCoinControlLowOutput"> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="contextMenuPolicy"> - <enum>Qt::ActionsContextMenu</enum> - </property> - <property name="text"> - <string notr="true">no</string> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> </layout> </item> <item> @@ -415,6 +389,9 @@ </item> <item> <layout class="QFormLayout" name="formLayoutCoinControl4"> + <property name="labelAlignment"> + <set>Qt::AlignLeft|Qt::AlignVCenter</set> + </property> <property name="horizontalSpacing"> <number>10</number> </property> diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index fe52e11157..26f1696de1 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -182,6 +182,8 @@ std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransac tx_description.append(tr(" * Sends %1 to %2") .arg(BitcoinUnits::formatWithUnit(BitcoinUnit::BTC, out.nValue)) .arg(QString::fromStdString(EncodeDestination(address)))); + // Check if the address is one of ours + if (m_wallet_model != nullptr && m_wallet_model->wallet().txoutIsMine(out)) tx_description.append(" (" + tr("own address") + ")"); tx_description.append("<br>"); } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 90aae0219e..cb4ecfb6fb 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -797,6 +797,12 @@ void RPCConsole::removeWallet(WalletModel * const walletModel) ui->WalletSelectorLabel->setVisible(false); } } + +void RPCConsole::setCurrentWallet(WalletModel* const wallet_model) +{ + QVariant data = QVariant::fromValue(wallet_model); + ui->WalletSelector->setCurrentIndex(ui->WalletSelector->findData(data)); +} #endif static QString categoryClass(int category) diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 32a7520491..65eef7fbba 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -136,6 +136,10 @@ public Q_SLOTS: void unbanSelectedNode(); /** set which tab has the focus (is visible) */ void setTabFocus(enum TabTypes tabType); +#ifdef ENABLE_WALLET + /** Set the current (ie - active) wallet */ + void setCurrentWallet(WalletModel* const wallet_model); +#endif // ENABLE_WALLET private: struct TranslatedStrings { diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 48f7fb6ad1..dea9b92395 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -97,21 +97,18 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); - QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this); QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); connect(clipboardQuantityAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardQuantity); connect(clipboardAmountAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAmount); connect(clipboardFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardFee); connect(clipboardAfterFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAfterFee); connect(clipboardBytesAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardBytes); - connect(clipboardLowOutputAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardLowOutput); connect(clipboardChangeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardChange); ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); ui->labelCoinControlAmount->addAction(clipboardAmountAction); ui->labelCoinControlFee->addAction(clipboardFeeAction); ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); ui->labelCoinControlBytes->addAction(clipboardBytesAction); - ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); ui->labelCoinControlChange->addAction(clipboardChangeAction); // init transaction fee section @@ -919,12 +916,6 @@ void SendCoinsDialog::coinControlClipboardBytes() GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, "")); } -// Coin Control: copy label "Dust" to clipboard -void SendCoinsDialog::coinControlClipboardLowOutput() -{ - GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text()); -} - // Coin Control: copy label "Change" to clipboard void SendCoinsDialog::coinControlClipboardChange() { diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index ac05cd98e5..4e861e55bb 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -111,7 +111,6 @@ private Q_SLOTS: void coinControlClipboardFee(); void coinControlClipboardAfterFee(); void coinControlClipboardBytes(); - void coinControlClipboardLowOutput(); void coinControlClipboardChange(); void updateFeeSectionControls(); void updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state); diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp index e918e84184..fb8029cb65 100644 --- a/src/qt/test/apptests.cpp +++ b/src/qt/test/apptests.cpp @@ -86,7 +86,6 @@ void AppTests::appTests() // Reset global state to avoid interfering with later tests. LogInstance().DisconnectTestLogger(); - AbortShutdown(); } //! Entry point for BitcoinGUI tests. diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp index b87d3b21ca..935f078e3d 100644 --- a/src/qt/test/uritests.cpp +++ b/src/qt/test/uritests.cpp @@ -58,9 +58,35 @@ void URITests::uriTests() uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-message=Wikipedia Example Address")); QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + // Commas in amounts are not allowed. uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000&label=Wikipedia Example")); QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000.0&label=Wikipedia Example")); QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + + // There are two amount specifications. The last value wins. + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=100&amount=200&label=Wikipedia Example")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.amount == 20000000000LL); + QVERIFY(rv.label == QString("Wikipedia Example")); + + // The first amount value is correct. However, the second amount value is not valid. Hence, the URI is not valid. + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=100&amount=1,000&label=Wikipedia Example")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + + // Test label containing a question mark ('?'). + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=100&label=?")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.amount == 10000000000LL); + QVERIFY(rv.label == QString("?")); + + // Escape sequences are not supported. + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=100&label=%3F")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.amount == 10000000000LL); + QVERIFY(rv.label == QString("%3F")); } diff --git a/src/random.cpp b/src/random.cpp index 54500e6cc6..5ff6f573b8 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -589,15 +589,24 @@ uint256 FastRandomContext::rand256() noexcept return ret; } -std::vector<unsigned char> FastRandomContext::randbytes(size_t len) +template <typename B> +std::vector<B> FastRandomContext::randbytes(size_t len) { if (requires_seed) RandomSeed(); - std::vector<unsigned char> ret(len); + std::vector<B> ret(len); if (len > 0) { - rng.Keystream(ret.data(), len); + rng.Keystream(UCharCast(ret.data()), len); } return ret; } +template std::vector<unsigned char> FastRandomContext::randbytes(size_t); +template std::vector<std::byte> FastRandomContext::randbytes(size_t); + +void FastRandomContext::fillrand(Span<std::byte> output) +{ + if (requires_seed) RandomSeed(); + rng.Keystream(UCharCast(output.data()), output.size()); +} FastRandomContext::FastRandomContext(const uint256& seed) noexcept : requires_seed(false), bitbuf_size(0) { diff --git a/src/random.h b/src/random.h index 49c0dff5bf..3b15477ae9 100644 --- a/src/random.h +++ b/src/random.h @@ -211,7 +211,11 @@ public: } /** Generate random bytes. */ - std::vector<unsigned char> randbytes(size_t len); + template <typename B = unsigned char> + std::vector<B> randbytes(size_t len); + + /** Fill a byte Span with random bytes. */ + void fillrand(Span<std::byte> output); /** Generate a random 32-bit integer. */ uint32_t rand32() noexcept { return randbits(32); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index ee3237638e..717a119b56 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -812,9 +812,7 @@ static RPCHelpMan pruneblockchain() PruneBlockFilesManual(active_chainstate, height); const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())}; - const CBlockIndex* last_block{active_chainstate.m_blockman.GetFirstStoredBlock(block)}; - - return static_cast<int64_t>(last_block->nHeight - 1); + return block.nStatus & BLOCK_HAVE_DATA ? active_chainstate.m_blockman.GetFirstStoredBlock(block)->nHeight - 1 : block.nHeight; }, }; } @@ -1267,7 +1265,8 @@ RPCHelpMan getblockchaininfo() obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage()); obj.pushKV("pruned", chainman.m_blockman.IsPruneMode()); if (chainman.m_blockman.IsPruneMode()) { - obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight); + bool has_tip_data = tip.nStatus & BLOCK_HAVE_DATA; + obj.pushKV("pruneheight", has_tip_data ? chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight : tip.nHeight + 1); const bool automatic_pruning{chainman.m_blockman.GetPruneTarget() != BlockManager::PRUNE_TARGET_MANUAL}; obj.pushKV("automatic_pruning", automatic_pruning); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 5f58eef1db..d289a9240e 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -259,11 +259,13 @@ static const CRPCConvertParam vRPCConvertParams[] = { "bumpfee", 1, "fee_rate"}, { "bumpfee", 1, "replaceable"}, { "bumpfee", 1, "outputs"}, + { "bumpfee", 1, "reduce_output"}, { "psbtbumpfee", 1, "options" }, { "psbtbumpfee", 1, "conf_target"}, { "psbtbumpfee", 1, "fee_rate"}, { "psbtbumpfee", 1, "replaceable"}, { "psbtbumpfee", 1, "outputs"}, + { "psbtbumpfee", 1, "reduce_output"}, { "logging", 0, "include" }, { "logging", 1, "exclude" }, { "disconnectnode", 1, "nodeid" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 074cecadd2..1f9b264626 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -566,6 +566,8 @@ static RPCHelpMan getblocktemplate() {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"}, {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"}, }}, + {"longpollid", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "delay processing request until the result would vary significantly from the \"longpollid\" of a prior template"}, + {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "proposed block data to check, encoded in hexadecimal; valid only for mode=\"proposal\""}, }, RPCArgOptions{.oneline_description="\"template_request\""}}, }, diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp index 45d46d223b..3828401642 100644 --- a/src/rpc/node.cpp +++ b/src/rpc/node.cpp @@ -21,7 +21,6 @@ #include <univalue.h> #include <util/any.h> #include <util/check.h> -#include <util/syscall_sandbox.h> #include <stdint.h> #ifdef HAVE_MALLOC_INFO @@ -70,27 +69,6 @@ static RPCHelpMan setmocktime() }; } -#if defined(USE_SYSCALL_SANDBOX) -static RPCHelpMan invokedisallowedsyscall() -{ - return RPCHelpMan{ - "invokedisallowedsyscall", - "\nInvoke a disallowed syscall to trigger a syscall sandbox violation. Used for testing purposes.\n", - {}, - RPCResult{RPCResult::Type::NONE, "", ""}, - RPCExamples{ - HelpExampleCli("invokedisallowedsyscall", "") + HelpExampleRpc("invokedisallowedsyscall", "")}, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - if (!Params().IsTestChain()) { - throw std::runtime_error("invokedisallowedsyscall is used for testing only."); - } - TestDisallowedSandboxCall(); - return UniValue::VNULL; - }, - }; -} -#endif // USE_SYSCALL_SANDBOX - static RPCHelpMan mockscheduler() { return RPCHelpMan{"mockscheduler", @@ -428,9 +406,6 @@ void RegisterNodeRPCCommands(CRPCTable& t) {"hidden", &echo}, {"hidden", &echojson}, {"hidden", &echoipc}, -#if defined(USE_SYSCALL_SANDBOX) - {"hidden", &invokedisallowedsyscall}, -#endif // USE_SYSCALL_SANDBOX }; for (const auto& c : commands) { t.appendCommand(c.name, &c); diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 19e14f88df..377181dd81 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -3,8 +3,10 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <clientversion.h> +#include <core_io.h> #include <common/args.h> #include <consensus/amount.h> +#include <script/interpreter.h> #include <key_io.h> #include <outputtype.h> #include <rpc/util.h> @@ -12,6 +14,7 @@ #include <script/signingprovider.h> #include <tinyformat.h> #include <util/check.h> +#include <util/result.h> #include <util/strencodings.h> #include <util/string.h> #include <util/translation.h> @@ -310,6 +313,21 @@ UniValue DescribeAddress(const CTxDestination& dest) return std::visit(DescribeAddressVisitor(), dest); } +int ParseSighashString(const UniValue& sighash) +{ + if (sighash.isNull()) { + return SIGHASH_DEFAULT; + } + if (!sighash.isStr()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "sighash needs to be null or string"); + } + const auto result{SighashFromStr(sighash.get_str())}; + if (!result) { + throw JSONRPCError(RPC_INVALID_PARAMETER, util::ErrorString(result).original); + } + return result.value(); +} + unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target) { const int target{value.getInt<int>()}; diff --git a/src/rpc/util.h b/src/rpc/util.h index 4cba5a9818..02d26f1ab7 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -100,6 +100,9 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto UniValue DescribeAddress(const CTxDestination& dest); +/** Parse a sighash string representation and raise an RPC error if it is invalid. */ +int ParseSighashString(const UniValue& sighash); + //! Parse a confirm target option and raise an RPC error if it is invalid. unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 1c9aedc10b..6c6f644142 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -5,7 +5,6 @@ #include <scheduler.h> #include <sync.h> -#include <util/syscall_sandbox.h> #include <util/time.h> #include <cassert> @@ -23,7 +22,6 @@ CScheduler::~CScheduler() void CScheduler::serviceQueue() { - SetSyscallSandboxPolicy(SyscallSandboxPolicy::SCHEDULER); WAIT_LOCK(newTaskMutex, lock); ++nThreadsServicingQueue; diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index b8ade1684a..09ded5fc61 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -15,6 +15,7 @@ #include <common/args.h> #include <span.h> #include <util/bip32.h> +#include <util/check.h> #include <util/spanparsing.h> #include <util/strencodings.h> #include <util/vector.h> @@ -191,8 +192,13 @@ public: /** Get the size of the generated public key(s) in bytes (33 or 65). */ virtual size_t GetSize() const = 0; + enum class StringType { + PUBLIC, + COMPAT // string calculation that mustn't change over time to stay compatible with previous software versions + }; + /** Get the descriptor string form. */ - virtual std::string ToString() const = 0; + virtual std::string ToString(StringType type=StringType::PUBLIC) const = 0; /** Get the descriptor string form including private data (if available in arg). */ virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0; @@ -212,9 +218,11 @@ class OriginPubkeyProvider final : public PubkeyProvider std::unique_ptr<PubkeyProvider> m_provider; bool m_apostrophe; - std::string OriginString(bool normalized=false) const + std::string OriginString(StringType type, bool normalized=false) const { - return HexStr(m_origin.fingerprint) + FormatHDKeypath(m_origin.path, /*apostrophe=*/!normalized && m_apostrophe); + // If StringType==COMPAT, always use the apostrophe to stay compatible with previous versions + bool use_apostrophe = (!normalized && m_apostrophe) || type == StringType::COMPAT; + return HexStr(m_origin.fingerprint) + FormatHDKeypath(m_origin.path, use_apostrophe); } public: @@ -228,12 +236,12 @@ public: } bool IsRange() const override { return m_provider->IsRange(); } size_t GetSize() const override { return m_provider->GetSize(); } - std::string ToString() const override { return "[" + OriginString() + "]" + m_provider->ToString(); } + std::string ToString(StringType type) const override { return "[" + OriginString(type) + "]" + m_provider->ToString(type); } bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override { std::string sub; if (!m_provider->ToPrivateString(arg, sub)) return false; - ret = "[" + OriginString() + "]" + std::move(sub); + ret = "[" + OriginString(StringType::PUBLIC) + "]" + std::move(sub); return true; } bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override @@ -245,9 +253,9 @@ public: // and append that to our own origin string. if (sub[0] == '[') { sub = sub.substr(9); - ret = "[" + OriginString(/*normalized=*/true) + std::move(sub); + ret = "[" + OriginString(StringType::PUBLIC, /*normalized=*/true) + std::move(sub); } else { - ret = "[" + OriginString(/*normalized=*/true) + "]" + std::move(sub); + ret = "[" + OriginString(StringType::PUBLIC, /*normalized=*/true) + "]" + std::move(sub); } return true; } @@ -275,7 +283,7 @@ public: } bool IsRange() const override { return false; } size_t GetSize() const override { return m_pubkey.size(); } - std::string ToString() const override { return m_xonly ? HexStr(m_pubkey).substr(2) : HexStr(m_pubkey); } + std::string ToString(StringType type) const override { return m_xonly ? HexStr(m_pubkey).substr(2) : HexStr(m_pubkey); } bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override { CKey key; @@ -293,7 +301,7 @@ public: } bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override { - ret = ToString(); + ret = ToString(StringType::PUBLIC); return true; } bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override @@ -421,9 +429,10 @@ public: return true; } - std::string ToString(bool normalized) const + std::string ToString(StringType type, bool normalized) const { - const bool use_apostrophe = !normalized && m_apostrophe; + // If StringType==COMPAT, always use the apostrophe to stay compatible with previous versions + const bool use_apostrophe = (!normalized && m_apostrophe) || type == StringType::COMPAT; std::string ret = EncodeExtPubKey(m_root_extkey) + FormatHDKeypath(m_path, /*apostrophe=*/use_apostrophe); if (IsRange()) { ret += "/*"; @@ -431,9 +440,9 @@ public: } return ret; } - std::string ToString() const override + std::string ToString(StringType type=StringType::PUBLIC) const override { - return ToString(/*normalized=*/false); + return ToString(type, /*normalized=*/false); } bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { @@ -449,7 +458,7 @@ public: bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache) const override { if (m_derive == DeriveType::HARDENED) { - out = ToString(/*normalized=*/true); + out = ToString(StringType::PUBLIC, /*normalized=*/true); return true; } @@ -556,6 +565,7 @@ public: PUBLIC, PRIVATE, NORMALIZED, + COMPAT, // string calculation that mustn't change over time to stay compatible with previous software versions }; bool IsSolvable() const override @@ -607,6 +617,9 @@ public: case StringType::PUBLIC: tmp = pubkey->ToString(); break; + case StringType::COMPAT: + tmp = pubkey->ToString(PubkeyProvider::StringType::COMPAT); + break; } ret += tmp; } @@ -617,10 +630,10 @@ public: return true; } - std::string ToString() const final + std::string ToString(bool compat_format) const final { std::string ret; - ToStringHelper(nullptr, ret, StringType::PUBLIC); + ToStringHelper(nullptr, ret, compat_format ? StringType::COMPAT : StringType::PUBLIC); return AddChecksum(ret); } @@ -1541,14 +1554,14 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const error = std::move(parser.m_key_parsing_error); return nullptr; } - if (!node->IsSane()) { + if (!node->IsSane() || node->IsNotSatisfiable()) { // Try to find the first insane sub for better error reporting. auto insane_node = node.get(); if (const auto sub = node->FindInsaneSub()) insane_node = sub; if (const auto str = insane_node->ToString(parser)) error = *str; if (!insane_node->IsValid()) { error += " is invalid"; - } else { + } else if (!node->IsSane()) { error += " is not sane"; if (!insane_node->IsNonMalleable()) { error += ": malleable witnesses exist"; @@ -1561,9 +1574,14 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const } else if (!insane_node->ValidSatisfactions()) { error += ": needs witnesses that may exceed resource limits"; } + } else { + error += " is not satisfiable"; } return nullptr; } + // A signature check is required for a miniscript to be sane. Therefore no sane miniscript + // may have an empty list of public keys. + CHECK_NONFATAL(!parser.m_keys.empty()); return std::make_unique<MiniscriptDescriptor>(std::move(parser.m_keys), std::move(node)); } } @@ -1705,6 +1723,10 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo } } + // The following descriptors are all top-level only descriptors. + // So if we are not at the top level, return early. + if (ctx != ParseScriptContext::TOP) return nullptr; + CTxDestination dest; if (ExtractDestination(script, dest)) { if (GetScriptForDestination(dest) == script) { @@ -1778,6 +1800,14 @@ std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const Signing return InferScript(script, ParseScriptContext::TOP, provider); } +uint256 DescriptorID(const Descriptor& desc) +{ + std::string desc_str = desc.ToString(/*compat_format=*/true); + uint256 id; + CSHA256().Write((unsigned char*)desc_str.data(), desc_str.size()).Finalize(id.begin()); + return id; +} + void DescriptorCache::CacheParentExtPubKey(uint32_t key_exp_pos, const CExtPubKey& xpub) { m_parent_xpubs[key_exp_pos] = xpub; diff --git a/src/script/descriptor.h b/src/script/descriptor.h index 39b1a37f9a..c6860c5cf6 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -106,7 +106,7 @@ struct Descriptor { virtual bool IsSolvable() const = 0; /** Convert the descriptor back to a string, undoing parsing. */ - virtual std::string ToString() const = 0; + virtual std::string ToString(bool compat_format=false) const = 0; /** Whether this descriptor will return one scriptPubKey or multiple (aka is or is not combo) */ virtual bool IsSingleType() const = 0; @@ -182,4 +182,9 @@ std::string GetDescriptorChecksum(const std::string& descriptor); */ std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider); +/** Unique identifier that may not change over time, unless explicitly marked as not backwards compatible. +* This is not part of BIP 380, not guaranteed to be interoperable and should not be exposed to the user. +*/ +uint256 DescriptorID(const Descriptor& desc); + #endif // BITCOIN_SCRIPT_DESCRIPTOR_H diff --git a/src/script/miniscript.h b/src/script/miniscript.h index 7c1a87a7dc..b58740a125 100644 --- a/src/script/miniscript.h +++ b/src/script/miniscript.h @@ -1134,20 +1134,35 @@ public: size_t ScriptSize() const { return scriptlen; } //! Return the maximum number of ops needed to satisfy this script non-malleably. - uint32_t GetOps() const { return ops.count + ops.sat.value; } + std::optional<uint32_t> GetOps() const { + if (!ops.sat.valid) return {}; + return ops.count + ops.sat.value; + } //! Return the number of ops in the script (not counting the dynamic ones that depend on execution). uint32_t GetStaticOps() const { return ops.count; } //! Check the ops limit of this script against the consensus limit. - bool CheckOpsLimit() const { return GetOps() <= MAX_OPS_PER_SCRIPT; } + bool CheckOpsLimit() const { + if (const auto ops = GetOps()) return *ops <= MAX_OPS_PER_SCRIPT; + return true; + } /** Return the maximum number of stack elements needed to satisfy this script non-malleably, including * the script push. */ - uint32_t GetStackSize() const { return ss.sat.value + 1; } + std::optional<uint32_t> GetStackSize() const { + if (!ss.sat.valid) return {}; + return ss.sat.value + 1; + } //! Check the maximum stack size for this script against the policy limit. - bool CheckStackSize() const { return GetStackSize() - 1 <= MAX_STANDARD_P2WSH_STACK_ITEMS; } + bool CheckStackSize() const { + if (const auto ss = GetStackSize()) return *ss - 1 <= MAX_STANDARD_P2WSH_STACK_ITEMS; + return true; + } + + //! Whether no satisfaction exists for this node. + bool IsNotSatisfiable() const { return !GetStackSize(); } //! Return the expression type. Type GetType() const { return typ; } diff --git a/src/secp256k1/.cirrus.yml b/src/secp256k1/.cirrus.yml index 0b904a4e38..ae205c624c 100644 --- a/src/secp256k1/.cirrus.yml +++ b/src/secp256k1/.cirrus.yml @@ -21,6 +21,7 @@ env: ECDH: no RECOVERY: no SCHNORRSIG: no + ELLSWIFT: no ### test options SECP256K1_TEST_ITERS: BENCH: yes @@ -53,12 +54,6 @@ cat_logs_snippet: &CAT_LOGS cat_ci_env_script: - env -merge_base_script_snippet: &MERGE_BASE - merge_base_script: - - if [ "$CIRRUS_PR" = "" ]; then exit 0; fi - - git fetch --depth=1 $CIRRUS_REPO_CLONE_URL "pull/${CIRRUS_PR}/merge" - - git checkout FETCH_HEAD # Use merged changes to detect silent merge conflicts - linux_container_snippet: &LINUX_CONTAINER container: dockerfile: ci/linux-debian.Dockerfile @@ -67,24 +62,25 @@ linux_container_snippet: &LINUX_CONTAINER # Gives us more CPUs for free if they're available. greedy: true # More than enough for our scripts. - memory: 1G + memory: 2G task: name: "x86_64: Linux (Debian stable)" << : *LINUX_CONTAINER - matrix: &ENV_MATRIX + matrix: - env: {WIDEMUL: int64, RECOVERY: yes} - - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes} - env: {WIDEMUL: int128} - - env: {WIDEMUL: int128_struct} - - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int128_struct, ELLSWIFT: yes} + - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes, ELLSWIFT: yes} - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes} - - env: {WIDEMUL: int128, ASM: x86_64} + - env: {WIDEMUL: int128, ASM: x86_64 , ELLSWIFT: yes} - env: { RECOVERY: yes, SCHNORRSIG: yes} - env: {CTIMETESTS: no, RECOVERY: yes, ECDH: yes, SCHNORRSIG: yes, CPPFLAGS: -DVERIFY} - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETESTS: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} - env: {CFLAGS: -O0, CTIMETESTS: no} + - env: {CFLAGS: -O1, RECOVERY: yes, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes} - env: { ECMULTGENPRECISION: 2, ECMULTWINDOW: 2 } - env: { ECMULTGENPRECISION: 8, ECMULTWINDOW: 4 } matrix: @@ -92,7 +88,10 @@ task: CC: gcc - env: CC: clang - << : *MERGE_BASE + - env: + CC: gcc-snapshot + - env: + CC: clang-snapshot test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -110,7 +109,6 @@ task: CC: i686-linux-gnu-gcc - env: CC: clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include - << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -124,20 +122,22 @@ task: HOMEBREW_NO_INSTALL_CLEANUP: 1 # Cirrus gives us a fixed number of 4 virtual CPUs. Not that we even have that many jobs at the moment... MAKEFLAGS: -j5 - matrix: - << : *ENV_MATRIX env: ASM: no WITH_VALGRIND: no CTIMETESTS: no + CC: clang matrix: - - env: - CC: gcc - - env: - CC: clang + - env: {WIDEMUL: int64, RECOVERY: yes, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes} + - env: {WIDEMUL: int64, RECOVERY: yes, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes, CC: gcc} + - env: {WIDEMUL: int128_struct, ECMULTGENPRECISION: 2, ECMULTWINDOW: 4} + - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes} + - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int128, RECOVERY: yes, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes, CC: gcc} + - env: {WIDEMUL: int128, RECOVERY: yes, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes, CPPFLAGS: -DVERIFY} + - env: {BUILD: distcheck} brew_script: - brew install automake libtool gcc - << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -154,8 +154,8 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETESTS: no - << : *MERGE_BASE test_script: # https://sourceware.org/bugzilla/show_bug.cgi?id=27008 - rm /etc/ld.so.cache @@ -173,11 +173,11 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETESTS: no matrix: - env: {} - - env: {EXPERIMENTAL: yes, ASM: arm} - << : *MERGE_BASE + - env: {EXPERIMENTAL: yes, ASM: arm32} test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -193,8 +193,8 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETESTS: no - << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -210,8 +210,8 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETESTS: no - << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -232,7 +232,6 @@ task: - name: "i686 (mingw32-w64): Windows (Debian stable, Wine)" env: HOST: i686-w64-mingw32 - << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -247,6 +246,7 @@ task: RECOVERY: yes EXPERIMENTAL: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETESTS: no # Use a MinGW-w64 host to tell ./configure we're building for Windows. # This will detect some MinGW-w64 tools but then make will need only @@ -274,7 +274,6 @@ task: CC: /opt/msvc/bin/x86/cl AR: /opt/msvc/bin/x86/lib NM: /opt/msvc/bin/x86/dumpbin -symbols -headers - << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -286,6 +285,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETESTS: no matrix: - name: "Valgrind (memcheck)" @@ -318,7 +318,6 @@ task: - env: HOST: i686-linux-gnu CC: i686-linux-gnu-gcc - << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -345,7 +344,6 @@ task: ECMULTGENPRECISION: 2 ECMULTWINDOW: 2 CFLAGS: "-fsanitize=memory -g -O3" - << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -361,7 +359,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes - << : *MERGE_BASE + ELLSWIFT: yes test_script: - ./ci/cirrus.sh << : *CAT_LOGS @@ -393,17 +391,23 @@ task: # Ignore MSBuild warning MSB8029. # See: https://learn.microsoft.com/en-us/visualstudio/msbuild/errors/msb8029?view=vs-2022 IgnoreWarnIntDirInTempDetected: 'true' - merge_script: - - PowerShell -NoLogo -Command if ($env:CIRRUS_PR -ne $null) { git fetch $env:CIRRUS_REPO_CLONE_URL pull/$env:CIRRUS_PR/merge; git reset --hard FETCH_HEAD; } + matrix: + - env: + BUILD_SHARED_LIBS: ON + - env: + BUILD_SHARED_LIBS: OFF + git_show_script: + # Print commit to allow reproducing the job outside of CI. + - git show --no-patch configure_script: - '%x64_NATIVE_TOOLS%' - - cmake -G "Visual Studio 17 2022" -A x64 -S . -B build -DSECP256K1_ENABLE_MODULE_RECOVERY=ON -DSECP256K1_BUILD_EXAMPLES=ON + - cmake -E env CFLAGS="/WX" cmake -A x64 -B build -DSECP256K1_ENABLE_MODULE_RECOVERY=ON -DSECP256K1_BUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% build_script: - '%x64_NATIVE_TOOLS%' - cmake --build build --config RelWithDebInfo -- -property:UseMultiToolTask=true;CL_MPcount=5 check_script: - '%x64_NATIVE_TOOLS%' - - ctest --test-dir build -j 5 + - ctest -C RelWithDebInfo --test-dir build -j 5 - build\src\RelWithDebInfo\bench_ecmult.exe - build\src\RelWithDebInfo\bench_internal.exe - build\src\RelWithDebInfo\bench.exe diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore index bc7e499de7..574902b8b5 100644 --- a/src/secp256k1/.gitignore +++ b/src/secp256k1/.gitignore @@ -59,5 +59,7 @@ build-aux/compile build-aux/test-driver libsecp256k1.pc +### CMake +/CMakeUserPresets.json # Default CMake build directory. /build diff --git a/src/secp256k1/CHANGELOG.md b/src/secp256k1/CHANGELOG.md index 6d23662a93..dacb0a3f48 100644 --- a/src/secp256k1/CHANGELOG.md +++ b/src/secp256k1/CHANGELOG.md @@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +#### Added + - New module `ellswift` implements ElligatorSwift encoding for public keys and x-only Diffie-Hellman key exchange for them. + ElligatorSwift permits representing secp256k1 public keys as 64-byte arrays which cannot be distinguished from uniformly random. See: + - Header file `include/secp256k1_ellswift.h` which defines the new API. + - Document `doc/ellswift.md` which explains the mathematical background of the scheme. + - The [paper](https://eprint.iacr.org/2022/759) on which the scheme is based. + +#### Changed + - When consuming libsecp256k1 as a static library on Windows, the user must now define the `SECP256K1_STATIC` macro before including `secp256k1.h`. + +## [0.3.2] - 2023-05-13 +We strongly recommend updating to 0.3.2 if you use or plan to use GCC >=13 to compile libsecp256k1. When in doubt, check the GCC version using `gcc -v`. + +#### Security + - Module `ecdh`: Fix "constant-timeness" issue with GCC 13.1 (and potentially future versions of GCC) that could leave applications using libsecp256k1's ECDH module vulnerable to a timing side-channel attack. The fix avoids secret-dependent control flow during ECDH computations when libsecp256k1 is compiled with GCC 13.1. + +#### Fixed + - Fixed an old bug that permitted compilers to potentially output bad assembly code on x86_64. In theory, it could lead to a crash or a read of unrelated memory, but this has never been observed on any compilers so far. + +#### Changed + - Various improvements and changes to CMake builds. CMake builds remain experimental. + - Made API versioning consistent with GNU Autotools builds. + - Switched to `BUILD_SHARED_LIBS` variable for controlling whether to build a static or a shared library. + - Added `SECP256K1_INSTALL` variable for the controlling whether to install the build artefacts. + - Renamed asm build option `arm` to `arm32`. Use `--with-asm=arm32` instead of `--with-asm=arm` (GNU Autotools), and `-DSECP256K1_ASM=arm32` instead of `-DSECP256K1_ASM=arm` (CMake). + +#### ABI Compatibility +The ABI is compatible with versions 0.3.0 and 0.3.1. + ## [0.3.1] - 2023-04-10 We strongly recommend updating to 0.3.1 if you use or plan to use Clang >=14 to compile libsecp256k1, e.g., Xcode >=14 on macOS has Clang >=14. When in doubt, check the Clang version using `clang -v`. @@ -68,7 +97,8 @@ This version was in fact never released. The number was given by the build system since the introduction of autotools in Jan 2014 (ea0fe5a5bf0c04f9cc955b2966b614f5f378c6f6). Therefore, this version number does not uniquely identify a set of source files. -[unreleased]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.1...HEAD +[unreleased]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.2...HEAD +[0.3.2]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.1...v0.3.2 [0.3.1]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/bitcoin-core/secp256k1/compare/423b6d19d373f1224fd671a982584d7e7900bc93..v0.2.0 diff --git a/src/secp256k1/CMakeLists.txt b/src/secp256k1/CMakeLists.txt index a70165e356..3107eb3bf1 100644 --- a/src/secp256k1/CMakeLists.txt +++ b/src/secp256k1/CMakeLists.txt @@ -1,16 +1,33 @@ cmake_minimum_required(VERSION 3.13) -if(CMAKE_VERSION VERSION_GREATER 3.14) +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15) # MSVC runtime library flags are selected by the CMAKE_MSVC_RUNTIME_LIBRARY abstraction. cmake_policy(SET CMP0091 NEW) # MSVC warning flags are not in CMAKE_<LANG>_FLAGS by default. cmake_policy(SET CMP0092 NEW) endif() -# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of -# the API. All changes in experimental modules are treated as -# backwards-compatible and therefore at most increase the minor version. -project(libsecp256k1 VERSION 0.3.2 LANGUAGES C) +project(libsecp256k1 + # The package (a.k.a. release) version is based on semantic versioning 2.0.0 of + # the API. All changes in experimental modules are treated as + # backwards-compatible and therefore at most increase the minor version. + VERSION 0.3.3 + DESCRIPTION "Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1." + HOMEPAGE_URL "https://github.com/bitcoin-core/secp256k1" + LANGUAGES C +) + +if(CMAKE_VERSION VERSION_LESS 3.21) + get_directory_property(parent_directory PARENT_DIRECTORY) + if(parent_directory) + set(PROJECT_IS_TOP_LEVEL OFF CACHE INTERNAL "Emulates CMake 3.21+ behavior.") + set(${PROJECT_NAME}_IS_TOP_LEVEL OFF CACHE INTERNAL "Emulates CMake 3.21+ behavior.") + else() + set(PROJECT_IS_TOP_LEVEL ON CACHE INTERNAL "Emulates CMake 3.21+ behavior.") + set(${PROJECT_NAME}_IS_TOP_LEVEL ON CACHE INTERNAL "Emulates CMake 3.21+ behavior.") + endif() + unset(parent_directory) +endif() # The library version is based on libtool versioning of the ABI. The set of # rules for updating the version can be found here: @@ -18,7 +35,7 @@ project(libsecp256k1 VERSION 0.3.2 LANGUAGES C) # All changes in experimental modules are treated as if they don't affect the # interface and therefore only increase the revision. set(${PROJECT_NAME}_LIB_VERSION_CURRENT 2) -set(${PROJECT_NAME}_LIB_VERSION_REVISION 2) +set(${PROJECT_NAME}_LIB_VERSION_REVISION 3) set(${PROJECT_NAME}_LIB_VERSION_AGE 0) set(CMAKE_C_STANDARD 90) @@ -26,36 +43,42 @@ set(CMAKE_C_EXTENSIONS OFF) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -# We do not use CMake's BUILD_SHARED_LIBS option. -option(SECP256K1_BUILD_SHARED "Build shared library." ON) -option(SECP256K1_BUILD_STATIC "Build static library." ON) -if(NOT SECP256K1_BUILD_SHARED AND NOT SECP256K1_BUILD_STATIC) - message(FATAL_ERROR "At least one of SECP256K1_BUILD_SHARED and SECP256K1_BUILD_STATIC must be enabled.") +option(BUILD_SHARED_LIBS "Build shared libraries." ON) +option(SECP256K1_DISABLE_SHARED "Disable shared library. Overrides BUILD_SHARED_LIBS." OFF) +if(SECP256K1_DISABLE_SHARED) + set(BUILD_SHARED_LIBS OFF) endif() +option(SECP256K1_INSTALL "Enable installation." ${PROJECT_IS_TOP_LEVEL}) + option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON) if(SECP256K1_ENABLE_MODULE_ECDH) - add_definitions(-DENABLE_MODULE_ECDH=1) + add_compile_definitions(ENABLE_MODULE_ECDH=1) endif() option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF) if(SECP256K1_ENABLE_MODULE_RECOVERY) - add_definitions(-DENABLE_MODULE_RECOVERY=1) + add_compile_definitions(ENABLE_MODULE_RECOVERY=1) endif() option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON) option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON) if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON) - add_definitions(-DENABLE_MODULE_SCHNORRSIG=1) + add_compile_definitions(ENABLE_MODULE_SCHNORRSIG=1) endif() if(SECP256K1_ENABLE_MODULE_EXTRAKEYS) - add_definitions(-DENABLE_MODULE_EXTRAKEYS=1) + add_compile_definitions(ENABLE_MODULE_EXTRAKEYS=1) +endif() + +option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) +if(SECP256K1_ENABLE_MODULE_ELLSWIFT) + add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1) endif() option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF) if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS) - add_definitions(-DUSE_EXTERNAL_DEFAULT_CALLBACKS=1) + add_compile_definitions(USE_EXTERNAL_DEFAULT_CALLBACKS=1) endif() set(SECP256K1_ECMULT_WINDOW_SIZE "AUTO" CACHE STRING "Window size for ecmult precomputation for verification, specified as integer in range [2..24]. \"AUTO\" is a reasonable setting for desktop machines (currently 15). [default=AUTO]") @@ -65,7 +88,7 @@ check_string_option_value(SECP256K1_ECMULT_WINDOW_SIZE) if(SECP256K1_ECMULT_WINDOW_SIZE STREQUAL "AUTO") set(SECP256K1_ECMULT_WINDOW_SIZE 15) endif() -add_definitions(-DECMULT_WINDOW_SIZE=${SECP256K1_ECMULT_WINDOW_SIZE}) +add_compile_definitions(ECMULT_WINDOW_SIZE=${SECP256K1_ECMULT_WINDOW_SIZE}) set(SECP256K1_ECMULT_GEN_PREC_BITS "AUTO" CACHE STRING "Precision bits to tune the precomputed table size for signing, specified as integer 2, 4 or 8. \"AUTO\" is a reasonable setting for desktop machines (currently 4). [default=AUTO]") set_property(CACHE SECP256K1_ECMULT_GEN_PREC_BITS PROPERTY STRINGS "AUTO" 2 4 8) @@ -73,29 +96,35 @@ check_string_option_value(SECP256K1_ECMULT_GEN_PREC_BITS) if(SECP256K1_ECMULT_GEN_PREC_BITS STREQUAL "AUTO") set(SECP256K1_ECMULT_GEN_PREC_BITS 4) endif() -add_definitions(-DECMULT_GEN_PREC_BITS=${SECP256K1_ECMULT_GEN_PREC_BITS}) +add_compile_definitions(ECMULT_GEN_PREC_BITS=${SECP256K1_ECMULT_GEN_PREC_BITS}) set(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY "OFF" CACHE STRING "Test-only override of the (autodetected by the C code) \"widemul\" setting. Legal values are: \"OFF\", \"int128_struct\", \"int128\" or \"int64\". [default=OFF]") set_property(CACHE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY PROPERTY STRINGS "OFF" "int128_struct" "int128" "int64") check_string_option_value(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) if(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) string(TOUPPER "${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}" widemul_upper_value) - add_definitions(-DUSE_FORCE_WIDEMUL_${widemul_upper_value}=1) + add_compile_definitions(USE_FORCE_WIDEMUL_${widemul_upper_value}=1) endif() mark_as_advanced(FORCE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) -set(SECP256K1_ASM "AUTO" CACHE STRING "Assembly optimizations to use: \"AUTO\", \"OFF\", \"x86_64\" or \"arm\" (experimental). [default=AUTO]") -set_property(CACHE SECP256K1_ASM PROPERTY STRINGS "AUTO" "OFF" "x86_64" "arm") +set(SECP256K1_ASM "AUTO" CACHE STRING "Assembly optimizations to use: \"AUTO\", \"OFF\", \"x86_64\" or \"arm32\" (experimental). [default=AUTO]") +set_property(CACHE SECP256K1_ASM PROPERTY STRINGS "AUTO" "OFF" "x86_64" "arm32") check_string_option_value(SECP256K1_ASM) -if(SECP256K1_ASM STREQUAL "arm") +if(SECP256K1_ASM STREQUAL "arm32") enable_language(ASM) - add_definitions(-DUSE_EXTERNAL_ASM=1) + include(CheckArm32Assembly) + check_arm32_assembly() + if(HAVE_ARM32_ASM) + add_compile_definitions(USE_EXTERNAL_ASM=1) + else() + message(FATAL_ERROR "ARM32 assembly optimization requested but not available.") + endif() elseif(SECP256K1_ASM) - include(Check64bitAssembly) - check_64bit_assembly() - if(HAS_64BIT_ASM) + include(CheckX86_64Assembly) + check_x86_64_assembly() + if(HAVE_X86_64_ASM) set(SECP256K1_ASM "x86_64") - add_definitions(-DUSE_ASM_X86_64=1) + add_compile_definitions(USE_ASM_X86_64=1) elseif(SECP256K1_ASM STREQUAL "AUTO") set(SECP256K1_ASM "OFF") else() @@ -105,8 +134,8 @@ endif() option(SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF) if(NOT SECP256K1_EXPERIMENTAL) - if(SECP256K1_ASM STREQUAL "arm") - message(FATAL_ERROR "ARM assembly optimization is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") + if(SECP256K1_ASM STREQUAL "arm32") + message(FATAL_ERROR "ARM32 assembly optimization is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") endif() endif() @@ -118,7 +147,7 @@ if(SECP256K1_VALGRIND) if(Valgrind_FOUND) set(SECP256K1_VALGRIND ON) include_directories(${Valgrind_INCLUDE_DIR}) - add_definitions(-DVALGRIND) + add_compile_definitions(VALGRIND) elseif(SECP256K1_VALGRIND STREQUAL "AUTO") set(SECP256K1_VALGRIND OFF) else() @@ -165,42 +194,51 @@ mark_as_advanced( CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) -if(CMAKE_CONFIGURATION_TYPES) - set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo" "Release" "Debug" "MinSizeRel" "Coverage") -endif() - -get_property(cached_cmake_build_type CACHE CMAKE_BUILD_TYPE PROPERTY TYPE) -if(cached_cmake_build_type) +get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +set(default_build_type "RelWithDebInfo") +if(is_multi_config) + set(CMAKE_CONFIGURATION_TYPES "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" CACHE STRING + "Supported configuration types." + FORCE + ) +else() set_property(CACHE CMAKE_BUILD_TYPE PROPERTY - STRINGS "RelWithDebInfo" "Release" "Debug" "MinSizeRel" "Coverage" + STRINGS "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" ) + if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to \"${default_build_type}\" as none was specified") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING + "Choose the type of build." + FORCE + ) + endif() endif() -set(default_build_type "RelWithDebInfo") -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to \"${default_build_type}\" as none was specified") - set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) -endif() - -include(TryAddCompileOption) +include(TryAppendCFlags) if(MSVC) - try_add_compile_option(/W2) - try_add_compile_option(/wd4146) + # Keep the following commands ordered lexicographically. + try_append_c_flags(/W3) # Production quality warning level. + try_append_c_flags(/wd4146) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned". + try_append_c_flags(/wd4244) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data". + try_append_c_flags(/wd4267) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data". + # Eliminate deprecation warnings for the older, less secure functions. + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) else() - try_add_compile_option(-pedantic) - try_add_compile_option(-Wall) - try_add_compile_option(-Wcast-align) - try_add_compile_option(-Wcast-align=strict) - try_add_compile_option(-Wconditional-uninitialized) - try_add_compile_option(-Wextra) - try_add_compile_option(-Wnested-externs) - try_add_compile_option(-Wno-long-long) - try_add_compile_option(-Wno-overlength-strings) - try_add_compile_option(-Wno-unused-function) - try_add_compile_option(-Wreserved-identifier) - try_add_compile_option(-Wshadow) - try_add_compile_option(-Wstrict-prototypes) - try_add_compile_option(-Wundef) + # Keep the following commands ordered lexicographically. + try_append_c_flags(-pedantic) + try_append_c_flags(-Wall) # GCC >= 2.95 and probably many other compilers. + try_append_c_flags(-Wcast-align) # GCC >= 2.95. + try_append_c_flags(-Wcast-align=strict) # GCC >= 8.0. + try_append_c_flags(-Wconditional-uninitialized) # Clang >= 3.0 only. + try_append_c_flags(-Wextra) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions. + try_append_c_flags(-Wnested-externs) + try_append_c_flags(-Wno-long-long) # GCC >= 3.0, -Wlong-long is implied by -pedantic. + try_append_c_flags(-Wno-overlength-strings) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic. + try_append_c_flags(-Wno-unused-function) # GCC >= 3.0, -Wunused-function is implied by -Wall. + try_append_c_flags(-Wreserved-identifier) # Clang >= 13.0 only. + try_append_c_flags(-Wshadow) + try_append_c_flags(-Wstrict-prototypes) + try_append_c_flags(-Wundef) endif() set(CMAKE_C_VISIBILITY_PRESET hidden) @@ -225,13 +263,19 @@ message("\n") message("secp256k1 configure summary") message("===========================") message("Build artifacts:") -message(" shared library ...................... ${SECP256K1_BUILD_SHARED}") -message(" static library ...................... ${SECP256K1_BUILD_STATIC}") +if(BUILD_SHARED_LIBS) + set(library_type "Shared") +else() + set(library_type "Static") +endif() + +message(" library type ........................ ${library_type}") message("Optional modules:") message(" ECDH ................................ ${SECP256K1_ENABLE_MODULE_ECDH}") message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOVERY}") message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}") message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}") +message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}") message("Parameters:") message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}") message(" ecmult gen precision bits ........... ${SECP256K1_ECMULT_GEN_PREC_BITS}") @@ -268,7 +312,7 @@ message("CFLAGS ................................ ${CMAKE_C_FLAGS}") get_directory_property(compile_options COMPILE_OPTIONS) string(REPLACE ";" " " compile_options "${compile_options}") message("Compile options ....................... " ${compile_options}) -if(DEFINED CMAKE_BUILD_TYPE) +if(NOT is_multi_config) message("Build type:") message(" - CMAKE_BUILD_TYPE ................... ${CMAKE_BUILD_TYPE}") string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type) @@ -276,7 +320,7 @@ if(DEFINED CMAKE_BUILD_TYPE) message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_${build_type}}") message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_${build_type}}") else() - message("Available configurations .............. ${CMAKE_CONFIGURATION_TYPES}") + message("Supported configurations .............. ${CMAKE_CONFIGURATION_TYPES}") message("RelWithDebInfo configuration:") message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_RELWITHDEBINFO}") message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}") diff --git a/src/secp256k1/CMakePresets.json b/src/secp256k1/CMakePresets.json new file mode 100644 index 0000000000..b35cd80579 --- /dev/null +++ b/src/secp256k1/CMakePresets.json @@ -0,0 +1,19 @@ +{ + "cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0}, + "version": 3, + "configurePresets": [ + { + "name": "dev-mode", + "displayName": "Development mode (intended only for developers of the library)", + "cacheVariables": { + "SECP256K1_EXPERIMENTAL": "ON", + "SECP256K1_ENABLE_MODULE_RECOVERY": "ON", + "SECP256K1_BUILD_EXAMPLES": "ON" + }, + "warnings": { + "dev": true, + "uninitialized": true + } + } + ] +} diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am index 36e26e3e8a..32bc729a41 100644 --- a/src/secp256k1/Makefile.am +++ b/src/secp256k1/Makefile.am @@ -1,5 +1,3 @@ -.PHONY: clean-precomp precomp - ACLOCAL_AMFLAGS = -I build-aux/m4 # AM_CFLAGS will be automatically prepended to CFLAGS by Automake when compiling some foo @@ -65,6 +63,7 @@ noinst_HEADERS += src/hash_impl.h noinst_HEADERS += src/field.h noinst_HEADERS += src/field_impl.h noinst_HEADERS += src/bench.h +noinst_HEADERS += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h noinst_HEADERS += contrib/lax_der_parsing.h noinst_HEADERS += contrib/lax_der_parsing.c noinst_HEADERS += contrib/lax_der_privatekey_parsing.h @@ -154,7 +153,7 @@ endif if USE_EXAMPLES noinst_PROGRAMS += ecdsa_example ecdsa_example_SOURCES = examples/ecdsa.c -ecdsa_example_CPPFLAGS = -I$(top_srcdir)/include +ecdsa_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC ecdsa_example_LDADD = libsecp256k1.la ecdsa_example_LDFLAGS = -static if BUILD_WINDOWS @@ -164,7 +163,7 @@ TESTS += ecdsa_example if ENABLE_MODULE_ECDH noinst_PROGRAMS += ecdh_example ecdh_example_SOURCES = examples/ecdh.c -ecdh_example_CPPFLAGS = -I$(top_srcdir)/include +ecdh_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC ecdh_example_LDADD = libsecp256k1.la ecdh_example_LDFLAGS = -static if BUILD_WINDOWS @@ -175,7 +174,7 @@ endif if ENABLE_MODULE_SCHNORRSIG noinst_PROGRAMS += schnorr_example schnorr_example_SOURCES = examples/schnorr.c -schnorr_example_CPPFLAGS = -I$(top_srcdir)/include +schnorr_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC schnorr_example_LDADD = libsecp256k1.la schnorr_example_LDFLAGS = -static if BUILD_WINDOWS @@ -190,11 +189,11 @@ EXTRA_PROGRAMS = precompute_ecmult precompute_ecmult_gen CLEANFILES = $(EXTRA_PROGRAMS) precompute_ecmult_SOURCES = src/precompute_ecmult.c -precompute_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES) +precompute_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES) -DVERIFY precompute_ecmult_LDADD = $(COMMON_LIB) precompute_ecmult_gen_SOURCES = src/precompute_ecmult_gen.c -precompute_ecmult_gen_CPPFLAGS = $(SECP_CONFIG_DEFINES) +precompute_ecmult_gen_CPPFLAGS = $(SECP_CONFIG_DEFINES) -DVERIFY precompute_ecmult_gen_LDADD = $(COMMON_LIB) # See Automake manual, Section "Errors with distclean". @@ -202,7 +201,7 @@ precompute_ecmult_gen_LDADD = $(COMMON_LIB) # otherwise make's decision whether to rebuild them (even in the first # build by a normal user) depends on mtimes, and thus is very fragile. # This means that rebuilds of the prebuilt files always need to be -# forced by deleting them, e.g., by invoking `make clean-precomp`. +# forced by deleting them. src/precomputed_ecmult.c: $(MAKE) $(AM_MAKEFLAGS) precompute_ecmult$(EXEEXT) ./precompute_ecmult$(EXEEXT) @@ -217,11 +216,29 @@ precomp: $(PRECOMP) # e.g., after `make maintainer-clean`). BUILT_SOURCES = $(PRECOMP) -maintainer-clean-local: clean-precomp - +.PHONY: clean-precomp clean-precomp: rm -f $(PRECOMP) +maintainer-clean-local: clean-precomp + +### Pregenerated test vectors +### (see the comments in the previous section for detailed rationale) +TESTVECTORS = src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h + +src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h: + mkdir -p $(@D) + python3 $(top_srcdir)/tools/tests_wycheproof_generate.py $(top_srcdir)/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json > $@ +testvectors: $(TESTVECTORS) + +BUILT_SOURCES += $(TESTVECTORS) + +.PHONY: clean-testvectors +clean-testvectors: + rm -f $(TESTVECTORS) +maintainer-clean-local: clean-testvectors + +### Additional files to distribute EXTRA_DIST = autogen.sh CHANGELOG.md SECURITY.md EXTRA_DIST += doc/release-process.md doc/safegcd_implementation.md EXTRA_DIST += examples/EXAMPLES_COPYING @@ -231,6 +248,9 @@ EXTRA_DIST += sage/group_prover.sage EXTRA_DIST += sage/prove_group_implementations.sage EXTRA_DIST += sage/secp256k1_params.sage EXTRA_DIST += sage/weierstrass_prover.sage +EXTRA_DIST += src/wycheproof/WYCHEPROOF_COPYING +EXTRA_DIST += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json +EXTRA_DIST += tools/tests_wycheproof_generate.py if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include @@ -248,19 +268,6 @@ if ENABLE_MODULE_SCHNORRSIG include src/modules/schnorrsig/Makefile.am.include endif -EXTRA_DIST += src/wycheproof/WYCHEPROOF_COPYING -EXTRA_DIST += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h -EXTRA_DIST += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json -EXTRA_DIST += tools/tests_wycheproof_generate.py - -TESTVECTORS = src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h - -src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h: - python3 tools/tests_wycheproof_generate.py src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json > $@ - -testvectors: $(TESTVECTORS) - -maintainer-clean-testvectors: clean-testvectors - -clean-testvectors: - rm -f $(TESTVECTORS) +if ENABLE_MODULE_ELLSWIFT +include src/modules/ellswift/Makefile.am.include +endif diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 index 624f5e956e..11adef4f22 100644 --- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 +++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 @@ -1,12 +1,31 @@ dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell. -AC_DEFUN([SECP_64BIT_ASM_CHECK],[ +AC_DEFUN([SECP_X86_64_ASM_CHECK],[ AC_MSG_CHECKING(for x86_64 assembly availability) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <stdint.h>]],[[ uint64_t a = 11, tmp; __asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx"); - ]])],[has_64bit_asm=yes],[has_64bit_asm=no]) -AC_MSG_RESULT([$has_64bit_asm]) + ]])], [has_x86_64_asm=yes], [has_x86_64_asm=no]) +AC_MSG_RESULT([$has_x86_64_asm]) +]) + +AC_DEFUN([SECP_ARM32_ASM_CHECK], [ + AC_MSG_CHECKING(for ARM32 assembly availability) + SECP_ARM32_ASM_CHECK_CFLAGS_saved_CFLAGS="$CFLAGS" + CFLAGS="-x assembler" + AC_LINK_IFELSE([AC_LANG_SOURCE([[ + .syntax unified + .eabi_attribute 24, 1 + .eabi_attribute 25, 1 + .text + .global main + main: + ldr r0, =0x002A + mov r7, #1 + swi 0 + ]])], [has_arm32_asm=yes], [has_arm32_asm=no]) + AC_MSG_RESULT([$has_arm32_asm]) + CFLAGS="$SECP_ARM32_ASM_CHECK_CFLAGS_saved_CFLAGS" ]) AC_DEFUN([SECP_VALGRIND_CHECK],[ @@ -21,6 +40,7 @@ if test x"$has_valgrind" != x"yes"; then # error "Valgrind does not support this platform." #endif ]])], [has_valgrind=yes]) + CPPFLAGS="$CPPFLAGS_TEMP" fi AC_MSG_RESULT($has_valgrind) ]) diff --git a/src/secp256k1/ci/cirrus.sh b/src/secp256k1/ci/cirrus.sh index b2af03bb5d..48a9783cc4 100755 --- a/src/secp256k1/ci/cirrus.sh +++ b/src/secp256k1/ci/cirrus.sh @@ -4,7 +4,8 @@ set -eux export LC_ALL=C -# Print relevant CI environment to allow reproducing the job outside of CI. +# Print commit and relevant CI environment to allow reproducing the job outside of CI. +git show --no-patch print_environment() { # Turn off -x because it messes up the output set +x @@ -36,8 +37,7 @@ case "$WRAPPER_CMD" in *wine*) # Make sure to shutdown wineserver whenever we exit. trap "wineserver -k || true" EXIT INT HUP - # This is apparently only reliable when we run a dummy command such as "hh.exe" afterwards. - wineserver -p && wine hh.exe + wineserver -p ;; esac @@ -54,6 +54,22 @@ if [ -n "$WRAPPER_CMD" ]; then $WRAPPER_CMD --version fi +# Workaround for https://bugs.kde.org/show_bug.cgi?id=452758 (fixed in valgrind 3.20.0). +case "${CC:-undefined}" in + clang*) + if [ "$CTIMETESTS" = "yes" ] && [ "$WITH_VALGRIND" = "yes" ] + then + export CFLAGS="${CFLAGS:+$CFLAGS }-gdwarf-4" + else + case "$WRAPPER_CMD" in + valgrind*) + export CFLAGS="${CFLAGS:+$CFLAGS }-gdwarf-4" + ;; + esac + fi + ;; +esac + ./autogen.sh ./configure \ @@ -62,6 +78,7 @@ fi --with-ecmult-window="$ECMULTWINDOW" \ --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-ellswift="$ELLSWIFT" \ --enable-module-schnorrsig="$SCHNORRSIG" \ --enable-examples="$EXAMPLES" \ --enable-ctime-tests="$CTIMETESTS" \ diff --git a/src/secp256k1/ci/linux-debian.Dockerfile b/src/secp256k1/ci/linux-debian.Dockerfile index a83a4e36db..dbb1dd2919 100644 --- a/src/secp256k1/ci/linux-debian.Dockerfile +++ b/src/secp256k1/ci/linux-debian.Dockerfile @@ -1,5 +1,7 @@ FROM debian:stable +SHELL ["/bin/bash", "-c"] + RUN dpkg --add-architecture i386 && \ dpkg --add-architecture s390x && \ dpkg --add-architecture armhf && \ @@ -9,11 +11,11 @@ RUN dpkg --add-architecture i386 && \ # dkpg-dev: to make pkg-config work in cross-builds # llvm: for llvm-symbolizer, which is used by clang's UBSan for symbolized stack traces RUN apt-get update && apt-get install --no-install-recommends -y \ - git ca-certificates \ + git ca-certificates wget \ make automake libtool pkg-config dpkg-dev valgrind qemu-user \ - gcc clang llvm libc6-dbg \ + gcc clang llvm libclang-rt-dev libc6-dbg \ g++ \ - gcc-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 libubsan1:i386 libasan6:i386 \ + gcc-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 libubsan1:i386 libasan8:i386 \ gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \ gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \ gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \ @@ -23,15 +25,51 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ sagemath WORKDIR /root -# The "wine" package provides a convience wrapper that we need + +# Build and install gcc snapshot +ARG GCC_SNAPSHOT_MAJOR=14 +RUN wget --progress=dot:giga --https-only --recursive --accept '*.tar.xz' --level 1 --no-directories "https://gcc.gnu.org/pub/gcc/snapshots/LATEST-${GCC_SNAPSHOT_MAJOR}" && \ + wget "https://gcc.gnu.org/pub/gcc/snapshots/LATEST-${GCC_SNAPSHOT_MAJOR}/sha512.sum" && \ + sha512sum --check --ignore-missing sha512.sum && \ + # We should have downloaded exactly one tar.xz file + ls && \ + [[ $(ls *.tar.xz | wc -l) -eq "1" ]] && \ + tar xf *.tar.xz && \ + mkdir gcc-build && cd gcc-build && \ + apt-get update && apt-get install --no-install-recommends -y libgmp-dev libmpfr-dev libmpc-dev flex && \ + ../*/configure --prefix=/opt/gcc-snapshot --enable-languages=c --disable-bootstrap --disable-multilib --without-isl && \ + make -j $(nproc) && \ + make install && \ + ln -s /opt/gcc-snapshot/bin/gcc /usr/bin/gcc-snapshot + +# Install clang snapshot +RUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && \ + # Add repository for this Debian release + . /etc/os-release && echo "deb http://apt.llvm.org/${VERSION_CODENAME} llvm-toolchain-${VERSION_CODENAME} main" >> /etc/apt/sources.list && \ + # Install clang snapshot + apt-get update && apt-get install --no-install-recommends -y clang && \ + # Remove just the "clang" symlink again + apt-get remove -y clang && \ + # We should have exactly two clang versions now + ls /usr/bin/clang* && \ + [[ $(ls /usr/bin/clang-?? | sort | wc -l) -eq "2" ]] && \ + # Create symlinks for them + ln -s $(ls /usr/bin/clang-?? | sort | tail -1) /usr/bin/clang-snapshot && \ + ln -s $(ls /usr/bin/clang-?? | sort | head -1) /usr/bin/clang + +# The "wine" package provides a convenience wrapper that we need RUN apt-get update && apt-get install --no-install-recommends -y \ git ca-certificates wine64 wine python3-simplejson python3-six msitools winbind procps && \ +# Workaround for `wine` package failure to employ the Debian alternatives system properly. + ln -s /usr/lib/wine/wine64 /usr/bin/wine64 && \ +# Set of tools for using MSVC on Linux. git clone https://github.com/mstorsjo/msvc-wine && \ mkdir /opt/msvc && \ python3 msvc-wine/vsdownload.py --accept-license --dest /opt/msvc Microsoft.VisualStudio.Workload.VCTools && \ - msvc-wine/install.sh /opt/msvc - -# Initialize the wine environment. Wait until the wineserver process has -# exited before closing the session, to avoid corrupting the wine prefix. -RUN wine64 wineboot --init && \ +# Since commit 2146cbfaf037e21de56c7157ec40bb6372860f51, the +# msvc-wine effectively initializes the wine prefix when running +# the install.sh script. + msvc-wine/install.sh /opt/msvc && \ +# Wait until the wineserver process has exited before closing the session, +# to avoid corrupting the wine prefix. while (ps -A | grep wineserver) > /dev/null; do sleep 1; done diff --git a/src/secp256k1/cmake/CheckArm32Assembly.cmake b/src/secp256k1/cmake/CheckArm32Assembly.cmake new file mode 100644 index 0000000000..15c44b24b0 --- /dev/null +++ b/src/secp256k1/cmake/CheckArm32Assembly.cmake @@ -0,0 +1,6 @@ +function(check_arm32_assembly) + try_compile(HAVE_ARM32_ASM + ${CMAKE_BINARY_DIR}/check_arm32_assembly + SOURCES ${CMAKE_SOURCE_DIR}/cmake/source_arm32.s + ) +endfunction() diff --git a/src/secp256k1/cmake/CheckStringOptionValue.cmake b/src/secp256k1/cmake/CheckStringOptionValue.cmake index bc4d7b5749..5a4d939b9e 100644 --- a/src/secp256k1/cmake/CheckStringOptionValue.cmake +++ b/src/secp256k1/cmake/CheckStringOptionValue.cmake @@ -1,11 +1,9 @@ function(check_string_option_value option) get_property(expected_values CACHE ${option} PROPERTY STRINGS) if(expected_values) - foreach(value IN LISTS expected_values) - if(value STREQUAL "${${option}}") - return() - endif() - endforeach() + if(${option} IN_LIST expected_values) + return() + endif() message(FATAL_ERROR "${option} value is \"${${option}}\", but must be one of ${expected_values}.") endif() message(AUTHOR_WARNING "The STRINGS property must be set before invoking `check_string_option_value' function.") diff --git a/src/secp256k1/cmake/Check64bitAssembly.cmake b/src/secp256k1/cmake/CheckX86_64Assembly.cmake index 3f65887765..ae82cd476e 100644 --- a/src/secp256k1/cmake/Check64bitAssembly.cmake +++ b/src/secp256k1/cmake/CheckX86_64Assembly.cmake @@ -1,6 +1,6 @@ include(CheckCSourceCompiles) -function(check_64bit_assembly) +function(check_x86_64_assembly) check_c_source_compiles(" #include <stdint.h> @@ -9,6 +9,6 @@ function(check_64bit_assembly) uint64_t a = 11, tmp; __asm__ __volatile__(\"movq $0x100000000,%1; mulq %%rsi\" : \"+a\"(a) : \"S\"(tmp) : \"cc\", \"%rdx\"); } - " HAS_64BIT_ASM) - set(HAS_64BIT_ASM ${HAS_64BIT_ASM} PARENT_SCOPE) + " HAVE_X86_64_ASM) + set(HAVE_X86_64_ASM ${HAVE_X86_64_ASM} PARENT_SCOPE) endfunction() diff --git a/src/secp256k1/cmake/FindValgrind.cmake b/src/secp256k1/cmake/FindValgrind.cmake index f6c1f58649..3af5e691e4 100644 --- a/src/secp256k1/cmake/FindValgrind.cmake +++ b/src/secp256k1/cmake/FindValgrind.cmake @@ -1,4 +1,4 @@ -if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") +if(CMAKE_HOST_APPLE) find_program(BREW_COMMAND brew) execute_process( COMMAND ${BREW_COMMAND} --prefix valgrind diff --git a/src/secp256k1/cmake/TryAddCompileOption.cmake b/src/secp256k1/cmake/TryAddCompileOption.cmake deleted file mode 100644 index f53c252c2d..0000000000 --- a/src/secp256k1/cmake/TryAddCompileOption.cmake +++ /dev/null @@ -1,23 +0,0 @@ -include(CheckCCompilerFlag) - -function(try_add_compile_option option) - string(MAKE_C_IDENTIFIER ${option} result) - string(TOUPPER ${result} result) - set(result "C_SUPPORTS${result}") - set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - if(NOT MSVC) - set(CMAKE_REQUIRED_FLAGS "-Werror") - endif() - check_c_compiler_flag(${option} ${result}) - if(${result}) - get_property(compile_options - DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - PROPERTY COMPILE_OPTIONS - ) - list(APPEND compile_options "${option}") - set_property( - DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - PROPERTY COMPILE_OPTIONS "${compile_options}" - ) - endif() -endfunction() diff --git a/src/secp256k1/cmake/TryAppendCFlags.cmake b/src/secp256k1/cmake/TryAppendCFlags.cmake new file mode 100644 index 0000000000..1d81a9317a --- /dev/null +++ b/src/secp256k1/cmake/TryAppendCFlags.cmake @@ -0,0 +1,24 @@ +include(CheckCCompilerFlag) + +function(secp256k1_check_c_flags_internal flags output) + string(MAKE_C_IDENTIFIER "${flags}" result) + string(TOUPPER "${result}" result) + set(result "C_SUPPORTS_${result}") + if(NOT MSVC) + set(CMAKE_REQUIRED_FLAGS "-Werror") + endif() + + # This avoids running a linker. + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + check_c_compiler_flag("${flags}" ${result}) + + set(${output} ${${result}} PARENT_SCOPE) +endfunction() + +# Append flags to the COMPILE_OPTIONS directory property if CC accepts them. +macro(try_append_c_flags) + secp256k1_check_c_flags_internal("${ARGV}" result) + if(result) + add_compile_options(${ARGV}) + endif() +endmacro() diff --git a/src/secp256k1/cmake/source_arm32.s b/src/secp256k1/cmake/source_arm32.s new file mode 100644 index 0000000000..d3d9347057 --- /dev/null +++ b/src/secp256k1/cmake/source_arm32.s @@ -0,0 +1,9 @@ +.syntax unified +.eabi_attribute 24, 1 +.eabi_attribute 25, 1 +.text +.global main +main: + ldr r0, =0x002A + mov r7, #1 + swi 0 diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac index 0b555eac67..a502d1304a 100644 --- a/src/secp256k1/configure.ac +++ b/src/secp256k1/configure.ac @@ -5,7 +5,7 @@ AC_PREREQ([2.60]) # backwards-compatible and therefore at most increase the minor version. define(_PKG_VERSION_MAJOR, 0) define(_PKG_VERSION_MINOR, 3) -define(_PKG_VERSION_PATCH, 2) +define(_PKG_VERSION_PATCH, 3) define(_PKG_VERSION_IS_RELEASE, false) # The library version is based on libtool versioning of the ABI. The set of @@ -14,7 +14,7 @@ define(_PKG_VERSION_IS_RELEASE, false) # All changes in experimental modules are treated as if they don't affect the # interface and therefore only increase the revision. define(_LIB_VERSION_CURRENT, 2) -define(_LIB_VERSION_REVISION, 2) +define(_LIB_VERSION_REVISION, 3) define(_LIB_VERSION_AGE, 0) AC_INIT([libsecp256k1],m4_join([.], _PKG_VERSION_MAJOR, _PKG_VERSION_MINOR, _PKG_VERSION_PATCH)m4_if(_PKG_VERSION_IS_RELEASE, [true], [], [-dev]),[https://github.com/bitcoin-core/secp256k1/issues],[libsecp256k1],[https://github.com/bitcoin-core/secp256k1]) @@ -121,14 +121,12 @@ AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [ # libtool makes the same assumption internally. # Note that "/opt" and "-opt" are equivalent for MSVC; we use "-opt" because "/opt" looks like a path. if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then - SECP_TRY_APPEND_CFLAGS([-W2 -wd4146], $1) # Moderate warning level, disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned" - SECP_TRY_APPEND_CFLAGS([-external:anglebrackets -external:W0], $1) # Suppress warnings from #include <...> files - # We pass -ignore:4217 to the MSVC linker to suppress warning 4217 when - # importing variables from a statically linked secp256k1. - # (See the libtool manual, section "Windows DLLs" for background.) - # Unfortunately, libtool tries to be too clever and strips "-Xlinker arg" - # into "arg", so this will be " -Xlinker -ignore:4217" after stripping. - LDFLAGS="-Xlinker -Xlinker -Xlinker -ignore:4217 $LDFLAGS" + SECP_TRY_APPEND_CFLAGS([-W3], $1) # Production quality warning level. + SECP_TRY_APPEND_CFLAGS([-wd4146], $1) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned". + SECP_TRY_APPEND_CFLAGS([-wd4244], $1) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data". + SECP_TRY_APPEND_CFLAGS([-wd4267], $1) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data". + # Eliminate deprecation warnings for the older, less secure functions. + CPPFLAGS="-D_CRT_SECURE_NO_WARNINGS $CPPFLAGS" fi ]) SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS) @@ -186,6 +184,10 @@ AC_ARG_ENABLE(module_schnorrsig, AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=yes]]), [], [SECP_SET_DEFAULT([enable_module_schnorrsig], [yes], [yes])]) +AC_ARG_ENABLE(module_ellswift, + AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module [default=yes]]), [], + [SECP_SET_DEFAULT([enable_module_ellswift], [yes], [yes])]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) @@ -198,8 +200,8 @@ AC_ARG_ENABLE(external_default_callbacks, # * and auto (the default). AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto]) -AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto], -[assembly optimizations to use (experimental: arm) [default=auto]])],[req_asm=$withval], [req_asm=auto]) +AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm32|no|auto], +[assembly optimizations to use (experimental: arm32) [default=auto]])],[req_asm=$withval], [req_asm=auto]) AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE|auto], [window size for ecmult precomputation for verification, specified as integer in range [2..24].] @@ -264,8 +266,8 @@ else fi if test x"$req_asm" = x"auto"; then - SECP_64BIT_ASM_CHECK - if test x"$has_64bit_asm" = x"yes"; then + SECP_X86_64_ASM_CHECK + if test x"$has_x86_64_asm" = x"yes"; then set_asm=x86_64 fi if test x"$set_asm" = x; then @@ -275,12 +277,16 @@ else set_asm=$req_asm case $set_asm in x86_64) - SECP_64BIT_ASM_CHECK - if test x"$has_64bit_asm" != x"yes"; then + SECP_X86_64_ASM_CHECK + if test x"$has_x86_64_asm" != x"yes"; then AC_MSG_ERROR([x86_64 assembly optimization requested but not available]) fi ;; - arm) + arm32) + SECP_ARM32_ASM_CHECK + if test x"$has_arm32_asm" != x"yes"; then + AC_MSG_ERROR([ARM32 assembly optimization requested but not available]) + fi ;; no) ;; @@ -297,7 +303,7 @@ case $set_asm in x86_64) SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_ASM_X86_64=1" ;; -arm) +arm32) enable_external_asm=yes ;; no) @@ -394,6 +400,10 @@ if test x"$enable_module_schnorrsig" = x"yes"; then enable_module_extrakeys=yes fi +if test x"$enable_module_ellswift" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ELLSWIFT, 1, [Define this symbol to enable the ElligatorSwift module]) +fi + # Test if extrakeys is set after the schnorrsig module to allow the schnorrsig # module to set enable_module_extrakeys=yes if test x"$enable_module_extrakeys" = x"yes"; then @@ -414,8 +424,8 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) AC_MSG_NOTICE([******]) else - if test x"$set_asm" = x"arm"; then - AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) + if test x"$set_asm" = x"arm32"; then + AC_MSG_ERROR([ARM32 assembly optimization is experimental. Use --enable-experimental to allow.]) fi fi @@ -436,8 +446,9 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) -AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) +AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm32"]) AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT) AC_SUBST(LIB_VERSION_REVISION, _LIB_VERSION_REVISION) @@ -457,6 +468,7 @@ echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" +echo " module ellswift = $enable_module_ellswift" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/src/secp256k1/doc/ellswift.md b/src/secp256k1/doc/ellswift.md new file mode 100644 index 0000000000..9d60e6be0b --- /dev/null +++ b/src/secp256k1/doc/ellswift.md @@ -0,0 +1,483 @@ +# ElligatorSwift for secp256k1 explained + +In this document we explain how the `ellswift` module implementation is related to the +construction in the +["SwiftEC: Shallue–van de Woestijne Indifferentiable Function To Elliptic Curves"](https://eprint.iacr.org/2022/759) +paper by Jorge Chávez-Saab, Francisco RodrÃguez-HenrÃquez, and Mehdi Tibouchi. + +* [1. Introduction](#1-introduction) +* [2. The decoding function](#2-the-decoding-function) + + [2.1 Decoding for `secp256k1`](#21-decoding-for-secp256k1) +* [3. The encoding function](#3-the-encoding-function) + + [3.1 Switching to *v, w* coordinates](#31-switching-to-v-w-coordinates) + + [3.2 Avoiding computing all inverses](#32-avoiding-computing-all-inverses) + + [3.3 Finding the inverse](#33-finding-the-inverse) + + [3.4 Dealing with special cases](#34-dealing-with-special-cases) + + [3.5 Encoding for `secp256k1`](#35-encoding-for-secp256k1) +* [4. Encoding and decoding full *(x, y)* coordinates](#4-encoding-and-decoding-full-x-y-coordinates) + + [4.1 Full *(x, y)* coordinates for `secp256k1`](#41-full-x-y-coordinates-for-secp256k1) + +## 1. Introduction + +The `ellswift` module effectively introduces a new 64-byte public key format, with the property +that (uniformly random) public keys can be encoded as 64-byte arrays which are computationally +indistinguishable from uniform byte arrays. The module provides functions to convert public keys +from and to this format, as well as convenience functions for key generation and ECDH that operate +directly on ellswift-encoded keys. + +The encoding consists of the concatenation of two (32-byte big endian) encoded field elements $u$ +and $t.$ Together they encode an x-coordinate on the curve $x$, or (see further) a full point $(x, y)$ on +the curve. + +**Decoding** consists of decoding the field elements $u$ and $t$ (values above the field size $p$ +are taken modulo $p$), and then evaluating $F_u(t)$, which for every $u$ and $t$ results in a valid +x-coordinate on the curve. The functions $F_u$ will be defined in [Section 2](#2-the-decoding-function). + +**Encoding** a given $x$ coordinate is conceptually done as follows: +* Loop: + * Pick a uniformly random field element $u.$ + * Compute the set $L = F_u^{-1}(x)$ of $t$ values for which $F_u(t) = x$, which may have up to *8* elements. + * With probability $1 - \dfrac{\\#L}{8}$, restart the loop. + * Select a uniformly random $t \in L$ and return $(u, t).$ + +This is the *ElligatorSwift* algorithm, here given for just x-coordinates. An extension to full +$(x, y)$ points will be given in [Section 4](#4-encoding-and-decoding-full-x-y-coordinates). +The algorithm finds a uniformly random $(u, t)$ among (almost all) those +for which $F_u(t) = x.$ Section 3.2 in the paper proves that the number of such encodings for +almost all x-coordinates on the curve (all but at most 39) is close to two times the field size +(specifically, it lies in the range $2q \pm (22\sqrt{q} + O(1))$, where $q$ is the size of the field). + +## 2. The decoding function + +First some definitions: +* $\mathbb{F}$ is the finite field of size $q$, of characteristic 5 or more, and $q \equiv 1 \mod 3.$ + * For `secp256k1`, $q = 2^{256} - 2^{32} - 977$, which satisfies that requirement. +* Let $E$ be the elliptic curve of points $(x, y) \in \mathbb{F}^2$ for which $y^2 = x^3 + ax + b$, with $a$ and $b$ + public constants, for which $\Delta_E = -16(4a^3 + 27b^2)$ is a square, and at least one of $(-b \pm \sqrt{-3 \Delta_E} / 36)/2$ is a square. + This implies that the order of $E$ is either odd, or a multiple of *4*. + If $a=0$, this condition is always fulfilled. + * For `secp256k1`, $a=0$ and $b=7.$ +* Let the function $g(x) = x^3 + ax + b$, so the $E$ curve equation is also $y^2 = g(x).$ +* Let the function $h(x) = 3x^3 + 4a.$ +* Define $V$ as the set of solutions $(x_1, x_2, x_3, z)$ to $z^2 = g(x_1)g(x_2)g(x_3).$ +* Define $S_u$ as the set of solutions $(X, Y)$ to $X^2 + h(u)Y^2 = -g(u)$ and $Y \neq 0.$ +* $P_u$ is a function from $\mathbb{F}$ to $S_u$ that will be defined below. +* $\psi_u$ is a function from $S_u$ to $V$ that will be defined below. + +**Note**: In the paper: +* $F_u$ corresponds to $F_{0,u}$ there. +* $P_u(t)$ is called $P$ there. +* All $S_u$ sets together correspond to $S$ there. +* All $\psi_u$ functions together (operating on elements of $S$) correspond to $\psi$ there. + +Note that for $V$, the left hand side of the equation $z^2$ is square, and thus the right +hand must also be square. As multiplying non-squares results in a square in $\mathbb{F}$, +out of the three right-hand side factors an even number must be non-squares. +This implies that exactly *1* or exactly *3* out of +$\\{g(x_1), g(x_2), g(x_3)\\}$ must be square, and thus that for any $(x_1,x_2,x_3,z) \in V$, +at least one of $\\{x_1, x_2, x_3\\}$ must be a valid x-coordinate on $E.$ There is one exception +to this, namely when $z=0$, but even then one of the three values is a valid x-coordinate. + +**Define** the decoding function $F_u(t)$ as: +* Let $(x_1, x_2, x_3, z) = \psi_u(P_u(t)).$ +* Return the first element $x$ of $(x_3, x_2, x_1)$ which is a valid x-coordinate on $E$ (i.e., $g(x)$ is square). + +$P_u(t) = (X(u, t), Y(u, t))$, where: + +$$ +\begin{array}{lcl} +X(u, t) & = & \left\\{\begin{array}{ll} + \dfrac{g(u) - t^2}{2t} & a = 0 \\ + \dfrac{g(u) + h(u)(Y_0(u) - X_0(u)t)^2}{X_0(u)(1 + h(u)t^2)} & a \neq 0 +\end{array}\right. \\ +Y(u, t) & = & \left\\{\begin{array}{ll} + \dfrac{X(u, t) + t}{u \sqrt{-3}} = \dfrac{g(u) + t^2}{2tu\sqrt{-3}} & a = 0 \\ + Y_0(u) + t(X(u, t) - X_0(u)) & a \neq 0 +\end{array}\right. +\end{array} +$$ + +$P_u(t)$ is defined: +* For $a=0$, unless: + * $u = 0$ or $t = 0$ (division by zero) + * $g(u) = -t^2$ (would give $Y=0$). +* For $a \neq 0$, unless: + * $X_0(u) = 0$ or $h(u)t^2 = -1$ (division by zero) + * $Y_0(u) (1 - h(u)t^2) = 2X_0(u)t$ (would give $Y=0$). + +The functions $X_0(u)$ and $Y_0(u)$ are defined in Appendix A of the paper, and depend on various properties of $E.$ + +The function $\psi_u$ is the same for all curves: $\psi_u(X, Y) = (x_1, x_2, x_3, z)$, where: + +$$ +\begin{array}{lcl} + x_1 & = & \dfrac{X}{2Y} - \dfrac{u}{2} && \\ + x_2 & = & -\dfrac{X}{2Y} - \dfrac{u}{2} && \\ + x_3 & = & u + 4Y^2 && \\ + z & = & \dfrac{g(x_3)}{2Y}(u^2 + ux_1 + x_1^2 + a) = \dfrac{-g(u)g(x_3)}{8Y^3} +\end{array} +$$ + +### 2.1 Decoding for `secp256k1` + +Put together and specialized for $a=0$ curves, decoding $(u, t)$ to an x-coordinate is: + +**Define** $F_u(t)$ as: +* Let $X = \dfrac{u^3 + b - t^2}{2t}.$ +* Let $Y = \dfrac{X + t}{u\sqrt{-3}}.$ +* Return the first $x$ in $(u + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u}{2}, \dfrac{X}{2Y} - \dfrac{u}{2})$ for which $g(x)$ is square. + +To make sure that every input decodes to a valid x-coordinate, we remap the inputs in case +$P_u$ is not defined (when $u=0$, $t=0$, or $g(u) = -t^2$): + +**Define** $F_u(t)$ as: +* Let $u'=u$ if $u \neq 0$; $1$ otherwise (guaranteeing $u' \neq 0$). +* Let $t'=t$ if $t \neq 0$; $1$ otherwise (guaranteeing $t' \neq 0$). +* Let $t''=t'$ if $g(u') \neq -t'^2$; $2t'$ otherwise (guaranteeing $t'' \neq 0$ and $g(u') \neq -t''^2$). +* Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$ +* Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$ +* Return the first $x$ in $(u' + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u'}{2}, \dfrac{X}{2Y} - \dfrac{u'}{2})$ for which $x^3 + b$ is square. + +The choices here are not strictly necessary. Just returning a fixed constant in any of the undefined cases would suffice, +but the approach here is simple enough and gives fairly uniform output even in these cases. + +**Note**: in the paper these conditions result in $\infty$ as output, due to the use of projective coordinates there. +We wish to avoid the need for callers to deal with this special case. + +This is implemented in `secp256k1_ellswift_xswiftec_frac_var` (which decodes to an x-coordinate represented as a fraction), and +in `secp256k1_ellswift_xswiftec_var` (which outputs the actual x-coordinate). + +## 3. The encoding function + +To implement $F_u^{-1}(x)$, the function to find the set of inverses $t$ for which $F_u(t) = x$, we have to reverse the process: +* Find all the $(X, Y) \in S_u$ that could have given rise to $x$, through the $x_1$, $x_2$, or $x_3$ formulas in $\psi_u.$ +* Map those $(X, Y)$ solutions to $t$ values using $P_u^{-1}(X, Y).$ +* For each of the found $t$ values, verify that $F_u(t) = x.$ +* Return the remaining $t$ values. + +The function $P_u^{-1}$, which finds $t$ given $(X, Y) \in S_u$, is significantly simpler than $P_u:$ + +$$ +P_u^{-1}(X, Y) = \left\\{\begin{array}{ll} +Yu\sqrt{-3} - X & a = 0 \\ +\dfrac{Y-Y_0(u)}{X-X_0(u)} & a \neq 0 \land X \neq X_0(u) \\ +\dfrac{-X_0(u)}{h(u)Y_0(u)} & a \neq 0 \land X = X_0(u) \land Y = Y_0(u) +\end{array}\right. +$$ + +The third step above, verifying that $F_u(t) = x$, is necessary because for the $(X, Y)$ values found through the $x_1$ and $x_2$ expressions, +it is possible that decoding through $\psi_u(X, Y)$ yields a valid $x_3$ on the curve, which would take precedence over the +$x_1$ or $x_2$ decoding. These $(X, Y)$ solutions must be rejected. + +Since we know that exactly one or exactly three out of $\\{x_1, x_2, x_3\\}$ are valid x-coordinates for any $t$, +the case where either $x_1$ or $x_2$ is valid and in addition also $x_3$ is valid must mean that all three are valid. +This means that instead of checking whether $x_3$ is on the curve, it is also possible to check whether the other one out of +$x_1$ and $x_2$ is on the curve. This is significantly simpler, as it turns out. + +Observe that $\psi_u$ guarantees that $x_1 + x_2 = -u.$ So given either $x = x_1$ or $x = x_2$, the other one of the two can be computed as +$-u - x.$ Thus, when encoding $x$ through the $x_1$ or $x_2$ expressions, one can simply check whether $g(-u-x)$ is a square, +and if so, not include the corresponding $t$ values in the returned set. As this does not need $X$, $Y$, or $t$, this condition can be determined +before those values are computed. + +It is not possible that an encoding found through the $x_1$ expression decodes to a different valid x-coordinate using $x_2$ (which would +take precedence), for the same reason: if both $x_1$ and $x_2$ decodings were valid, $x_3$ would be valid as well, and thus take +precedence over both. Because of this, the $g(-u-x)$ being square test for $x_1$ and $x_2$ is the only test necessary to guarantee the found $t$ +values round-trip back to the input $x$ correctly. This is the reason for choosing the $(x_3, x_2, x_1)$ precedence order in the decoder; +any order which does not place $x_3$ first requires more complicated round-trip checks in the encoder. + +### 3.1 Switching to *v, w* coordinates + +Before working out the formulas for all this, we switch to different variables for $S_u.$ Let $v = (X/Y - u)/2$, and +$w = 2Y.$ Or in the other direction, $X = w(u/2 + v)$ and $Y = w/2:$ +* $S_u'$ becomes the set of $(v, w)$ for which $w^2 (u^2 + uv + v^2 + a) = -g(u)$ and $w \neq 0.$ +* For $a=0$ curves, $P_u^{-1}$ can be stated for $(v,w)$ as $P_u^{'-1}(v, w) = w\left(\frac{\sqrt{-3}-1}{2}u - v\right).$ +* $\psi_u$ can be stated for $(v, w)$ as $\psi_u'(v, w) = (x_1, x_2, x_3, z)$, where + +$$ +\begin{array}{lcl} + x_1 & = & v \\ + x_2 & = & -u - v \\ + x_3 & = & u + w^2 \\ + z & = & \dfrac{g(x_3)}{w}(u^2 + uv + v^2 + a) = \dfrac{-g(u)g(x_3)}{w^3} +\end{array} +$$ + +We can now write the expressions for finding $(v, w)$ given $x$ explicitly, by solving each of the $\\{x_1, x_2, x_3\\}$ +expressions for $v$ or $w$, and using the $S_u'$ equation to find the other variable: +* Assuming $x = x_1$, we find $v = x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions). +* Assuming $x = x_2$, we find $v = -u-x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions). +* Assuming $x = x_3$, we find $w = \pm\sqrt{x-u}$ and $v = -u/2 \pm \sqrt{-w^2(4g(u) + w^2h(u))}/(2w^2)$ (four solutions). + +### 3.2 Avoiding computing all inverses + +The *ElligatorSwift* algorithm as stated in Section 1 requires the computation of $L = F_u^{-1}(x)$ (the +set of all $t$ such that $(u, t)$ decode to $x$) in full. This is unnecessary. + +Observe that the procedure of restarting with probability $(1 - \frac{\\#L}{8})$ and otherwise returning a +uniformly random element from $L$ is actually equivalent to always padding $L$ with $\bot$ values up to length 8, +picking a uniformly random element from that, restarting whenever $\bot$ is picked: + +**Define** *ElligatorSwift(x)* as: +* Loop: + * Pick a uniformly random field element $u.$ + * Compute the set $L = F_u^{-1}(x).$ + * Let $T$ be the 8-element vector consisting of the elements of $L$, plus $8 - \\#L$ times $\\{\bot\\}.$ + * Select a uniformly random $t \in T.$ + * If $t \neq \bot$, return $(u, t)$; restart loop otherwise. + +Now notice that the order of elements in $T$ does not matter, as all we do is pick a uniformly +random element in it, so we do not need to have all $\bot$ values at the end. +As we have 8 distinct formulas for finding $(v, w)$ (taking the variants due to $\pm$ into account), +we can associate every index in $T$ with exactly one of those formulas, making sure that: +* Formulas that yield no solutions (due to division by zero or non-existing square roots) or invalid solutions are made to return $\bot.$ +* For the $x_1$ and $x_2$ cases, if $g(-u-x)$ is a square, $\bot$ is returned instead (the round-trip check). +* In case multiple formulas would return the same non- $\bot$ result, all but one of those must be turned into $\bot$ to avoid biasing those. + +The last condition above only occurs with negligible probability for cryptographically-sized curves, but is interesting +to take into account as it allows exhaustive testing in small groups. See [Section 3.4](#34-dealing-with-special-cases) +for an analysis of all the negligible cases. + +If we define $T = (G_{0,u}(x), G_{1,u}(x), \ldots, G_{7,u}(x))$, with each $G_{i,u}$ matching one of the formulas, +the loop can be simplified to only compute one of the inverses instead of all of them: + +**Define** *ElligatorSwift(x)* as: +* Loop: + * Pick a uniformly random field element $u.$ + * Pick a uniformly random integer $c$ in $[0,8).$ + * Let $t = G_{c,u}(x).$ + * If $t \neq \bot$, return $(u, t)$; restart loop otherwise. + +This is implemented in `secp256k1_ellswift_xelligatorswift_var`. + +### 3.3 Finding the inverse + +To implement $G_{c,u}$, we map $c=0$ to the $x_1$ formula, $c=1$ to the $x_2$ formula, and $c=2$ and $c=3$ to the $x_3$ formula. +Those are then repeated as $c=4$ through $c=7$ for the other sign of $w$ (noting that in each formula, $w$ is a square root of some expression). +Ignoring the negligible cases, we get: + +**Define** $G_{c,u}(x)$ as: +* If $c \in \\{0, 1, 4, 5\\}$ (for $x_1$ and $x_2$ formulas): + * If $g(-u-x)$ is square, return $\bot$ (as $x_3$ would be valid and take precedence). + * If $c \in \\{0, 4\\}$ (the $x_1$ formula) let $v = x$, otherwise let $v = -u-x$ (the $x_2$ formula) + * Let $s = -g(u)/(u^2 + uv + v^2 + a)$ (using $s = w^2$ in what follows). +* Otherwise, when $c \in \\{2, 3, 6, 7\\}$ (for $x_3$ formulas): + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}.$ + * Let $v = (r/s - u)/2$ if $c \in \\{3, 7\\}$; $(-r/s - u)/2$ otherwise. +* Let $w = \sqrt{s}.$ +* Depending on $c:$ + * If $c \in \\{0, 1, 2, 3\\}:$ return $P_u^{'-1}(v, w).$ + * If $c \in \\{4, 5, 6, 7\\}:$ return $P_u^{'-1}(v, -w).$ + +Whenever a square root of a non-square is taken, $\bot$ is returned; for both square roots this happens with roughly +50% on random inputs. Similarly, when a division by 0 would occur, $\bot$ is returned as well; this will only happen +with negligible probability. A division by 0 in the first branch in fact cannot occur at all, because $u^2 + uv + v^2 + a = 0$ +implies $g(-u-x) = g(x)$ which would mean the $g(-u-x)$ is square condition has triggered +and $\bot$ would have been returned already. + +**Note**: In the paper, the $case$ variable corresponds roughly to the $c$ above, but only takes on 4 possible values (1 to 4). +The conditional negation of $w$ at the end is done randomly, which is equivalent, but makes testing harder. We choose to +have the $G_{c,u}$ be deterministic, and capture all choices in $c.$ + +Now observe that the $c \in \\{1, 5\\}$ and $c \in \\{3, 7\\}$ conditions effectively perform the same $v \rightarrow -u-v$ +transformation. Furthermore, that transformation has no effect on $s$ in the first branch +as $u^2 + ux + x^2 + a = u^2 + u(-u-x) + (-u-x)^2 + a.$ Thus we can extract it out and move it down: + +**Define** $G_{c,u}(x)$ as: +* If $c \in \\{0, 1, 4, 5\\}:$ + * If $g(-u-x)$ is square, return $\bot.$ + * Let $s = -g(u)/(u^2 + ux + x^2 + a).$ + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}.$ +* Depending on $c:$ + * If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w).$ + * If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w).$ + * If $c \in \\{4, 6\\}:$ return $P_u^{'-1}(v, -w).$ + * If $c \in \\{5, 7\\}:$ return $P_u^{'-1}(-u-v, -w).$ + +This shows there will always be exactly 0, 4, or 8 $t$ values for a given $(u, x)$ input. +There can be 0, 1, or 2 $(v, w)$ pairs before invoking $P_u^{'-1}$, and each results in 4 distinct $t$ values. + +### 3.4 Dealing with special cases + +As mentioned before there are a few cases to deal with which only happen in a negligibly small subset of inputs. +For cryptographically sized fields, if only random inputs are going to be considered, it is unnecessary to deal with these. Still, for completeness +we analyse them here. They generally fall into two categories: cases in which the encoder would produce $t$ values that +do not decode back to $x$ (or at least cannot guarantee that they do), and cases in which the encoder might produce the same +$t$ value for multiple $c$ inputs (thereby biasing that encoding): + +* In the branch for $x_1$ and $x_2$ (where $c \in \\{0, 1, 4, 5\\}$): + * When $g(u) = 0$, we would have $s=w=Y=0$, which is not on $S_u.$ This is only possible on even-ordered curves. + Excluding this also removes the one condition under which the simplified check for $x_3$ on the curve + fails (namely when $g(x_1)=g(x_2)=0$ but $g(x_3)$ is not square). + This does exclude some valid encodings: when both $g(u)=0$ and $u^2+ux+x^2+a=0$ (also implying $g(x)=0$), + the $S_u'$ equation degenerates to $0 = 0$, and many valid $t$ values may exist. Yet, these cannot be targeted uniformly by the + encoder anyway as there will generally be more than 8. + * When $g(x) = 0$, the same $t$ would be produced as in the $x_3$ branch (where $c \in \\{2, 3, 6, 7\\}$) which we give precedence + as it can deal with $g(u)=0$. + This is again only possible on even-ordered curves. +* In the branch for $x_3$ (where $c \in \\{2, 3, 6, 7\\}$): + * When $s=0$, a division by zero would occur. + * When $v = -u-v$ and $c \in \\{3, 7\\}$, the same $t$ would be returned as in the $c \in \\{2, 6\\}$ cases. + It is equivalent to checking whether $r=0$. + This cannot occur in the $x_1$ or $x_2$ branches, as it would trigger the $g(-u-x)$ is square condition. + A similar concern for $w = -w$ does not exist, as $w=0$ is already impossible in both branches: in the first + it requires $g(u)=0$ which is already outlawed on even-ordered curves and impossible on others; in the second it would trigger division by zero. +* Curve-specific special cases also exist that need to be rejected, because they result in $(u,t)$ which is invalid to the decoder, or because of division by zero in the encoder: + * For $a=0$ curves, when $u=0$ or when $t=0$. The latter can only be reached by the encoder when $g(u)=0$, which requires an even-ordered curve. + * For $a \neq 0$ curves, when $X_0(u)=0$, when $h(u)t^2 = -1$, or when $w(u + 2v) = 2X_0(u)$ while also either $w \neq 2Y_0(u)$ or $h(u)=0$. + +**Define** a version of $G_{c,u}(x)$ which deals with all these cases: +* If $a=0$ and $u=0$, return $\bot.$ +* If $a \neq 0$ and $X_0(u)=0$, return $\bot.$ +* If $c \in \\{0, 1, 4, 5\\}:$ + * If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only). + * If $g(-u-x)$ is square, return $\bot.$ + * Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero). + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square. + * If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$ + * If $s = 0$, return $\bot.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}$; return $\bot$ if not square. +* If $a \neq 0$ and $w(u+2v) = 2X_0(u)$ and either $w \neq 2Y_0(u)$ or $h(u) = 0$, return $\bot.$ +* Depending on $c:$ + * If $c \in \\{0, 2\\}$, let $t = P_u^{'-1}(v, w).$ + * If $c \in \\{1, 3\\}$, let $t = P_u^{'-1}(-u-v, w).$ + * If $c \in \\{4, 6\\}$, let $t = P_u^{'-1}(v, -w).$ + * If $c \in \\{5, 7\\}$, let $t = P_u^{'-1}(-u-v, -w).$ +* If $a=0$ and $t=0$, return $\bot$ (even curves only). +* If $a \neq 0$ and $h(u)t^2 = -1$, return $\bot.$ +* Return $t.$ + +Given any $u$, using this algorithm over all $x$ and $c$ values, every $t$ value will be reached exactly once, +for an $x$ for which $F_u(t) = x$ holds, except for these cases that will not be reached: +* All cases where $P_u(t)$ is not defined: + * For $a=0$ curves, when $u=0$, $t=0$, or $g(u) = -t^2.$ + * For $a \neq 0$ curves, when $h(u)t^2 = -1$, $X_0(u) = 0$, or $Y_0(u) (1 - h(u) t^2) = 2X_0(u)t.$ +* When $g(u)=0$, the potentially many $t$ values that decode to an $x$ satisfying $g(x)=0$ using the $x_2$ formula. These were excluded by the $g(u)=0$ condition in the $c \in \\{0, 1, 4, 5\\}$ branch. + +These cases form a negligible subset of all $(u, t)$ for cryptographically sized curves. + +### 3.5 Encoding for `secp256k1` + +Specialized for odd-ordered $a=0$ curves: + +**Define** $G_{c,u}(x)$ as: +* If $u=0$, return $\bot.$ +* If $c \in \\{0, 1, 4, 5\\}:$ + * If $(-u-x)^3 + b$ is square, return $\bot$ + * Let $s = -(u^3 + b)/(u^2 + ux + x^2)$ (cannot cause division by 0). + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4(u^3 + b) + 3su^2)}$; return $\bot$ if not square. + * If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$ + * If $s = 0$, return $\bot.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}$; return $\bot$ if not square. +* Depending on $c:$ + * If $c \in \\{0, 2\\}:$ return $w(\frac{\sqrt{-3}-1}{2}u - v).$ + * If $c \in \\{1, 3\\}:$ return $w(\frac{\sqrt{-3}+1}{2}u + v).$ + * If $c \in \\{4, 6\\}:$ return $w(\frac{-\sqrt{-3}+1}{2}u + v).$ + * If $c \in \\{5, 7\\}:$ return $w(\frac{-\sqrt{-3}-1}{2}u - v).$ + +This is implemented in `secp256k1_ellswift_xswiftec_inv_var`. + +And the x-only ElligatorSwift encoding algorithm is still: + +**Define** *ElligatorSwift(x)* as: +* Loop: + * Pick a uniformly random field element $u.$ + * Pick a uniformly random integer $c$ in $[0,8).$ + * Let $t = G_{c,u}(x).$ + * If $t \neq \bot$, return $(u, t)$; restart loop otherwise. + +Note that this logic does not take the remapped $u=0$, $t=0$, and $g(u) = -t^2$ cases into account; it just avoids them. +While it is not impossible to make the encoder target them, this would increase the maximum number of $t$ values for a given $(u, x)$ +combination beyond 8, and thereby slow down the ElligatorSwift loop proportionally, for a negligible gain in uniformity. + +## 4. Encoding and decoding full *(x, y)* coordinates + +So far we have only addressed encoding and decoding x-coordinates, but in some cases an encoding +for full points with $(x, y)$ coordinates is desirable. It is possible to encode this information +in $t$ as well. + +Note that for any $(X, Y) \in S_u$, $(\pm X, \pm Y)$ are all on $S_u.$ Moreover, all of these are +mapped to the same x-coordinate. Negating $X$ or negating $Y$ just results in $x_1$ and $x_2$ +being swapped, and does not affect $x_3.$ This will not change the outcome x-coordinate as the order +of $x_1$ and $x_2$ only matters if both were to be valid, and in that case $x_3$ would be used instead. + +Still, these four $(X, Y)$ combinations all correspond to distinct $t$ values, so we can encode +the sign of the y-coordinate in the sign of $X$ or the sign of $Y.$ They correspond to the +four distinct $P_u^{'-1}$ calls in the definition of $G_{u,c}.$ + +**Note**: In the paper, the sign of the y coordinate is encoded in a separately-coded bit. + +To encode the sign of $y$ in the sign of $Y:$ + +**Define** *Decode(u, t)* for full $(x, y)$ as: +* Let $(X, Y) = P_u(t).$ +* Let $x$ be the first value in $(u + 4Y^2, \frac{-X}{2Y} - \frac{u}{2}, \frac{X}{2Y} - \frac{u}{2})$ for which $g(x)$ is square. +* Let $y = \sqrt{g(x)}.$ +* If $sign(y) = sign(Y)$, return $(x, y)$; otherwise return $(x, -y).$ + +And encoding would be done using a $G_{c,u}(x, y)$ function defined as: + +**Define** $G_{c,u}(x, y)$ as: +* If $c \in \\{0, 1\\}:$ + * If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only). + * If $g(-u-x)$ is square, return $\bot.$ + * Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero). + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square. + * If $c = 3$ and $r = 0$, return $\bot.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}$; return $\bot$ if not square. +* Let $w' = w$ if $sign(w/2) = sign(y)$; $-w$ otherwise. +* Depending on $c:$ + * If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w').$ + * If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w').$ + +Note that $c$ now only ranges $[0,4)$, as the sign of $w'$ is decided based on that of $y$, rather than on $c.$ +This change makes some valid encodings unreachable: when $y = 0$ and $sign(Y) \neq sign(0)$. + +In the above logic, $sign$ can be implemented in several ways, such as parity of the integer representation +of the input field element (for prime-sized fields) or the quadratic residuosity (for fields where +$-1$ is not square). The choice does not matter, as long as it only takes on two possible values, and for $x \neq 0$ it holds that $sign(x) \neq sign(-x)$. + +### 4.1 Full *(x, y)* coordinates for `secp256k1` + +For $a=0$ curves, there is another option. Note that for those, +the $P_u(t)$ function translates negations of $t$ to negations of (both) $X$ and $Y.$ Thus, we can use $sign(t)$ to +encode the y-coordinate directly. Combined with the earlier remapping to guarantee all inputs land on the curve, we get +as decoder: + +**Define** *Decode(u, t)* as: +* Let $u'=u$ if $u \neq 0$; $1$ otherwise. +* Let $t'=t$ if $t \neq 0$; $1$ otherwise. +* Let $t''=t'$ if $u'^3 + b + t'^2 \neq 0$; $2t'$ otherwise. +* Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$ +* Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$ +* Let $x$ be the first element of $(u' + 4Y^2, \frac{-X}{2Y} - \frac{u'}{2}, \frac{X}{2Y} - \frac{u'}{2})$ for which $g(x)$ is square. +* Let $y = \sqrt{g(x)}.$ +* Return $(x, y)$ if $sign(y) = sign(t)$; $(x, -y)$ otherwise. + +This is implemented in `secp256k1_ellswift_swiftec_var`. The used $sign(x)$ function is the parity of $x$ when represented as in integer in $[0,q).$ + +The corresponding encoder would invoke the x-only one, but negating the output $t$ if $sign(t) \neq sign(y).$ + +This is implemented in `secp256k1_ellswift_elligatorswift_var`. + +Note that this is only intended for encoding points where both the x-coordinate and y-coordinate are unpredictable. When encoding x-only points +where the y-coordinate is implicitly even (or implicitly square, or implicitly in $[0,q/2]$), the encoder in +[Section 3.5](#35-encoding-for-secp256k1) must be used, or a bias is reintroduced that undoes all the benefit of using ElligatorSwift +in the first place. diff --git a/src/secp256k1/doc/release-process.md b/src/secp256k1/doc/release-process.md index 70a35f0910..ea6087c9ff 100644 --- a/src/secp256k1/doc/release-process.md +++ b/src/secp256k1/doc/release-process.md @@ -12,12 +12,40 @@ It is best if the maintainers are present during the release, so they can help e This process also assumes that there will be no minor releases for old major releases. +We aim to cut a regular release every 3-4 months, approximately twice as frequent as major Bitcoin Core releases. Every second release should be published one month before the feature freeze of the next major Bitcoin Core release, allowing sufficient time to update the library in Core. + +## Sanity Checks +Perform these checks before creating a release: + +1. Ensure `make distcheck` doesn't fail. +```shell +./autogen.sh && ./configure --enable-dev-mode && make distcheck +``` +2. Check installation with autotools: +```shell +dir=$(mktemp -d) +./autogen.sh && ./configure --prefix=$dir && make clean && make install && ls -l $dir/include $dir/lib +gcc -o ecdsa examples/ecdsa.c $(PKG_CONFIG_PATH=$dir/lib/pkgconfig pkg-config --cflags --libs libsecp256k1) -Wl,-rpath,"$dir/lib" && ./ecdsa +``` +3. Check installation with CMake: +```shell +dir=$(mktemp -d) +build=$(mktemp -d) +cmake -B $build -DCMAKE_INSTALL_PREFIX=$dir && cmake --build $build --target install && ls -l $dir/include $dir/lib* +gcc -o ecdsa examples/ecdsa.c -I $dir/include -L $dir/lib*/ -l secp256k1 -Wl,-rpath,"$dir/lib",-rpath,"$dir/lib64" && ./ecdsa +``` + ## Regular release 1. Open a PR to the master branch with a commit (using message `"release: prepare for $MAJOR.$MINOR.$PATCH"`, for example) that - * finalizes the release notes in [CHANGELOG.md](../CHANGELOG.md) (make sure to include an entry for `### ABI Compatibility`), - * updates `_PKG_VERSION_*` and `_LIB_VERSION_*` and sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and - * updates `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_*` in `CMakeLists.txt`. + * finalizes the release notes in [CHANGELOG.md](../CHANGELOG.md) by + * adding a section for the release (make sure that the version number is a link to a diff between the previous and new version), + * removing the `[Unreleased]` section header, and + * including an entry for `### ABI Compatibility` if it doesn't exist that mentions the library soname of the release, + * sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and + * if this is not a patch release + * updates `_PKG_VERSION_*` and `_LIB_VERSION_*` in `configure.ac` and + * updates `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_*` in `CMakeLists.txt`. 2. After the PR is merged, tag the commit and push it: ``` RELEASE_COMMIT=<merge commit of step 1> @@ -25,8 +53,9 @@ This process also assumes that there will be no minor releases for old major rel git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH ``` 3. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that - * sets `_PKG_VERSION_IS_RELEASE` to `false` and increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`, and - * increments the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`. + * sets `_PKG_VERSION_IS_RELEASE` to `false` and increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`, + * increments the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`, and + * adds an `[Unreleased]` section header to the [CHANGELOG.md](../CHANGELOG.md). If other maintainers are not present to approve the PR, it can be merged without ACKs. 4. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). @@ -35,14 +64,14 @@ This process also assumes that there will be no minor releases for old major rel Note that bugfixes only need to be backported to releases for which no compatible release without the bug exists. -1. If `$PATCH = 1`, create maintenance branch `$MAJOR.$MINOR`: +1. If there's no maintenance branch `$MAJOR.$MINOR`, create one: ``` - git checkout -b $MAJOR.$MINOR v$MAJOR.$MINOR.0 + git checkout -b $MAJOR.$MINOR v$MAJOR.$MINOR.$((PATCH - 1)) git push git@github.com:bitcoin-core/secp256k1.git $MAJOR.$MINOR ``` 2. Open a pull request to the `$MAJOR.$MINOR` branch that * includes the bugfixes, - * finalizes the release notes, + * finalizes the release notes similar to a regular release, * increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac` and the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt` (with commit message `"release: bump versions for $MAJOR.$MINOR.$PATCH"`, for example). diff --git a/src/secp256k1/examples/CMakeLists.txt b/src/secp256k1/examples/CMakeLists.txt index 0884b645e0..e2ea473008 100644 --- a/src/secp256k1/examples/CMakeLists.txt +++ b/src/secp256k1/examples/CMakeLists.txt @@ -2,33 +2,23 @@ add_library(example INTERFACE) target_include_directories(example INTERFACE ${PROJECT_SOURCE_DIR}/include ) -target_compile_options(example INTERFACE - $<$<C_COMPILER_ID:MSVC>:/wd4005> -) target_link_libraries(example INTERFACE + secp256k1 $<$<PLATFORM_ID:Windows>:bcrypt> ) -if(SECP256K1_BUILD_SHARED) - target_link_libraries(example INTERFACE secp256k1) -elseif(SECP256K1_BUILD_STATIC) - target_link_libraries(example INTERFACE secp256k1_static) - if(MSVC) - target_link_options(example INTERFACE /IGNORE:4217) - endif() -endif() add_executable(ecdsa_example ecdsa.c) target_link_libraries(ecdsa_example example) -add_test(ecdsa_example ecdsa_example) +add_test(NAME ecdsa_example COMMAND ecdsa_example) if(SECP256K1_ENABLE_MODULE_ECDH) add_executable(ecdh_example ecdh.c) target_link_libraries(ecdh_example example) - add_test(ecdh_example ecdh_example) + add_test(NAME ecdh_example COMMAND ecdh_example) endif() if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) add_executable(schnorr_example schnorr.c) target_link_libraries(schnorr_example example) - add_test(schnorr_example schnorr_example) + add_test(NAME schnorr_example COMMAND schnorr_example) endif() diff --git a/src/secp256k1/examples/examples_util.h b/src/secp256k1/examples/examples_util.h index a52b1fa115..3293b64032 100644 --- a/src/secp256k1/examples/examples_util.h +++ b/src/secp256k1/examples/examples_util.h @@ -17,7 +17,13 @@ */ #if defined(_WIN32) +/* + * The defined WIN32_NO_STATUS macro disables return code definitions in + * windows.h, which avoids "macro redefinition" MSVC warnings in ntstatus.h. + */ +#define WIN32_NO_STATUS #include <windows.h> +#undef WIN32_NO_STATUS #include <ntstatus.h> #include <bcrypt.h> #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) @@ -77,7 +83,7 @@ static void print_hex(unsigned char* data, size_t size) { #include <Windows.h> #endif /* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. */ -static SECP256K1_INLINE void secure_erase(void *ptr, size_t len) { +static void secure_erase(void *ptr, size_t len) { #if defined(_MSC_VER) /* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */ SecureZeroMemory(ptr, len); @@ -89,7 +95,7 @@ static SECP256K1_INLINE void secure_erase(void *ptr, size_t len) { * As best as we can tell, this is sufficient to break any optimisations that * might try to eliminate "superfluous" memsets. * This method used in memzero_explicit() the Linux kernel, too. Its advantage is that it is - * pretty efficient, because the compiler can still implement the memset() efficently, + * pretty efficient, because the compiler can still implement the memset() efficiently, * just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by * Yang et al. (USENIX Security 2017) for more background. */ diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h index 4ce3e500ba..936f0b42b7 100644 --- a/src/secp256k1/include/secp256k1.h +++ b/src/secp256k1/include/secp256k1.h @@ -122,18 +122,6 @@ typedef int (*secp256k1_nonce_function)( # endif # endif -# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) -# if SECP256K1_GNUC_PREREQ(2,7) -# define SECP256K1_INLINE __inline__ -# elif (defined(_MSC_VER)) -# define SECP256K1_INLINE __inline -# else -# define SECP256K1_INLINE -# endif -# else -# define SECP256K1_INLINE inline -# endif - /* When this header is used at build-time the SECP256K1_BUILD define needs to be set * to correctly setup export attributes and nullness checks. This is normally done * by secp256k1.c but to guard against this header being included before secp256k1.c @@ -145,28 +133,35 @@ typedef int (*secp256k1_nonce_function)( # define SECP256K1_NO_BUILD #endif -/* Symbol visibility. See libtool manual, section "Windows DLLs". */ -#if defined(_WIN32) && !defined(__GNUC__) -# ifdef SECP256K1_BUILD -# ifdef DLL_EXPORT -# define SECP256K1_API __declspec (dllexport) -# define SECP256K1_API_VAR extern __declspec (dllexport) +/* Symbol visibility. */ +#if defined(_WIN32) + /* GCC for Windows (e.g., MinGW) accepts the __declspec syntax + * for MSVC compatibility. A __declspec declaration implies (but is not + * exactly equivalent to) __attribute__ ((visibility("default"))), and so we + * actually want __declspec even on GCC, see "Microsoft Windows Function + * Attributes" in the GCC manual and the recommendations in + * https://gcc.gnu.org/wiki/Visibility. */ +# if defined(SECP256K1_BUILD) +# if defined(DLL_EXPORT) || defined(SECP256K1_DLL_EXPORT) + /* Building libsecp256k1 as a DLL. + * 1. If using Libtool, it defines DLL_EXPORT automatically. + * 2. In other cases, SECP256K1_DLL_EXPORT must be defined. */ +# define SECP256K1_API extern __declspec (dllexport) # endif -# elif defined _MSC_VER -# define SECP256K1_API -# define SECP256K1_API_VAR extern __declspec (dllimport) -# elif defined DLL_EXPORT -# define SECP256K1_API __declspec (dllimport) -# define SECP256K1_API_VAR extern __declspec (dllimport) + /* The user must define SECP256K1_STATIC when consuming libsecp256k1 as a static + * library on Windows. */ +# elif !defined(SECP256K1_STATIC) + /* Consuming libsecp256k1 as a DLL. */ +# define SECP256K1_API extern __declspec (dllimport) # endif #endif #ifndef SECP256K1_API # if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD) -# define SECP256K1_API __attribute__ ((visibility ("default"))) -# define SECP256K1_API_VAR extern __attribute__ ((visibility ("default"))) + /* Building libsecp256k1 on non-Windows using GCC or compatible. */ +# define SECP256K1_API extern __attribute__ ((visibility ("default"))) # else -# define SECP256K1_API -# define SECP256K1_API_VAR extern + /* All cases not captured above. */ +# define SECP256K1_API extern # endif #endif @@ -238,10 +233,10 @@ typedef int (*secp256k1_nonce_function)( * * It is highly recommended to call secp256k1_selftest before using this context. */ -SECP256K1_API_VAR const secp256k1_context *secp256k1_context_static; +SECP256K1_API const secp256k1_context *secp256k1_context_static; /** Deprecated alias for secp256k1_context_static. */ -SECP256K1_API_VAR const secp256k1_context *secp256k1_context_no_precomp +SECP256K1_API const secp256k1_context *secp256k1_context_no_precomp SECP256K1_DEPRECATED("Use secp256k1_context_static instead"); /** Perform basic self tests (to be used in conjunction with secp256k1_context_static) @@ -638,10 +633,10 @@ SECP256K1_API int secp256k1_ecdsa_signature_normalize( * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of * extra entropy. */ -SECP256K1_API_VAR const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; +SECP256K1_API const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; /** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ -SECP256K1_API_VAR const secp256k1_nonce_function secp256k1_nonce_function_default; +SECP256K1_API const secp256k1_nonce_function secp256k1_nonce_function_default; /** Create an ECDSA signature. * @@ -745,10 +740,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate( * invalid according to secp256k1_ec_seckey_verify, this * function returns 0. seckey will be set to some unspecified * value if this function returns 0. - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to - * secp256k1_ec_seckey_verify, this function returns 0. For - * uniformly random 32-byte arrays the chance of being invalid - * is negligible (around 1 in 2^128). + * In: tweak32: pointer to a 32-byte tweak, which must be valid according to + * secp256k1_ec_seckey_verify or 32 zero bytes. For uniformly + * random 32-byte tweaks, the chance of being invalid is + * negligible (around 1 in 2^128). */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add( const secp256k1_context *ctx, @@ -773,10 +768,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( * Args: ctx: pointer to a context object. * In/Out: pubkey: pointer to a public key object. pubkey will be set to an * invalid value if this function returns 0. - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to - * secp256k1_ec_seckey_verify, this function returns 0. For - * uniformly random 32-byte arrays the chance of being invalid - * is negligible (around 1 in 2^128). + * In: tweak32: pointer to a 32-byte tweak, which must be valid according to + * secp256k1_ec_seckey_verify or 32 zero bytes. For uniformly + * random 32-byte tweaks, the chance of being invalid is + * negligible (around 1 in 2^128). */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( const secp256k1_context *ctx, diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h index 837ae2abe5..515e174299 100644 --- a/src/secp256k1/include/secp256k1_ecdh.h +++ b/src/secp256k1/include/secp256k1_ecdh.h @@ -27,11 +27,11 @@ typedef int (*secp256k1_ecdh_hash_function)( /** An implementation of SHA256 hash function that applies to compressed public key. * Populates the output parameter with 32 bytes. */ -SECP256K1_API_VAR const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256; +SECP256K1_API const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256; /** A default ECDH hash function (currently equal to secp256k1_ecdh_hash_function_sha256). * Populates the output parameter with 32 bytes. */ -SECP256K1_API_VAR const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default; +SECP256K1_API const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default; /** Compute an EC Diffie-Hellman secret in constant time * diff --git a/src/secp256k1/include/secp256k1_ellswift.h b/src/secp256k1/include/secp256k1_ellswift.h new file mode 100644 index 0000000000..f79bd88396 --- /dev/null +++ b/src/secp256k1/include/secp256k1_ellswift.h @@ -0,0 +1,200 @@ +#ifndef SECP256K1_ELLSWIFT_H +#define SECP256K1_ELLSWIFT_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This module provides an implementation of ElligatorSwift as well as a + * version of x-only ECDH using it (including compatibility with BIP324). + * + * ElligatorSwift is described in https://eprint.iacr.org/2022/759 by + * Chavez-Saab, Rodriguez-Henriquez, and Tibouchi. It permits encoding + * uniformly chosen public keys as 64-byte arrays which are indistinguishable + * from uniformly random arrays. + * + * Let f be the function from pairs of field elements to point X coordinates, + * defined as follows (all operations modulo p = 2^256 - 2^32 - 977) + * f(u,t): + * - Let C = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852, + * a square root of -3. + * - If u=0, set u=1 instead. + * - If t=0, set t=1 instead. + * - If u^3 + t^2 + 7 = 0, multiply t by 2. + * - Let X = (u^3 + 7 - t^2) / (2 * t) + * - Let Y = (X + t) / (C * u) + * - Return the first in [u + 4 * Y^2, (-X/Y - u) / 2, (X/Y - u) / 2] that is an + * X coordinate on the curve (at least one of them is, for any u and t). + * + * Then an ElligatorSwift encoding of x consists of the 32-byte big-endian + * encodings of field elements u and t concatenated, where f(u,t) = x. + * The encoding algorithm is described in the paper, and effectively picks a + * uniformly random pair (u,t) among those which encode x. + * + * If the Y coordinate is relevant, it is given the same parity as t. + * + * Changes w.r.t. the the paper: + * - The u=0, t=0, and u^3+t^2+7=0 conditions result in decoding to the point + * at infinity in the paper. Here they are remapped to finite points. + * - The paper uses an additional encoding bit for the parity of y. Here the + * parity of t is used (negating t does not affect the decoded x coordinate, + * so this is possible). + * + * For mathematical background about the scheme, see the doc/ellswift.md file. + */ + +/** A pointer to a function used by secp256k1_ellswift_xdh to hash the shared X + * coordinate along with the encoded public keys to a uniform shared secret. + * + * Returns: 1 if a shared secret was successfully computed. + * 0 will cause secp256k1_ellswift_xdh to fail and return 0. + * Other return values are not allowed, and the behaviour of + * secp256k1_ellswift_xdh is undefined for other return values. + * Out: output: pointer to an array to be filled by the function + * In: x32: pointer to the 32-byte serialized X coordinate + * of the resulting shared point (will not be NULL) + * ell_a64: pointer to the 64-byte encoded public key of party A + * (will not be NULL) + * ell_b64: pointer to the 64-byte encoded public key of party B + * (will not be NULL) + * data: arbitrary data pointer that is passed through + */ +typedef int (*secp256k1_ellswift_xdh_hash_function)( + unsigned char *output, + const unsigned char *x32, + const unsigned char *ell_a64, + const unsigned char *ell_b64, + void *data +); + +/** An implementation of an secp256k1_ellswift_xdh_hash_function which uses + * SHA256(prefix64 || ell_a64 || ell_b64 || x32), where prefix64 is the 64-byte + * array pointed to by data. */ +SECP256K1_API const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_prefix; + +/** An implementation of an secp256k1_ellswift_xdh_hash_function compatible with + * BIP324. It returns H_tag(ell_a64 || ell_b64 || x32), where H_tag is the + * BIP340 tagged hash function with tag "bip324_ellswift_xonly_ecdh". Equivalent + * to secp256k1_ellswift_xdh_hash_function_prefix with prefix64 set to + * SHA256("bip324_ellswift_xonly_ecdh")||SHA256("bip324_ellswift_xonly_ecdh"). + * The data argument is ignored. */ +SECP256K1_API const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_bip324; + +/** Construct a 64-byte ElligatorSwift encoding of a given pubkey. + * + * Returns: 1 always. + * Args: ctx: pointer to a context object + * Out: ell64: pointer to a 64-byte array to be filled + * In: pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key + * rnd32: pointer to 32 bytes of randomness + * + * It is recommended that rnd32 consists of 32 uniformly random bytes, not + * known to any adversary trying to detect whether public keys are being + * encoded, though 16 bytes of randomness (padded to an array of 32 bytes, + * e.g., with zeros) suffice to make the result indistinguishable from + * uniform. The randomness in rnd32 must not be a deterministic function of + * the pubkey (it can be derived from the private key, though). + * + * It is not guaranteed that the computed encoding is stable across versions + * of the library, even if all arguments to this function (including rnd32) + * are the same. + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellswift_encode( + const secp256k1_context *ctx, + unsigned char *ell64, + const secp256k1_pubkey *pubkey, + const unsigned char *rnd32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Decode a 64-bytes ElligatorSwift encoded public key. + * + * Returns: always 1 + * Args: ctx: pointer to a context object + * Out: pubkey: pointer to a secp256k1_pubkey that will be filled + * In: ell64: pointer to a 64-byte array to decode + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellswift_decode( + const secp256k1_context *ctx, + secp256k1_pubkey *pubkey, + const unsigned char *ell64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute an ElligatorSwift public key for a secret key. + * + * Returns: 1: secret was valid, public key was stored. + * 0: secret was invalid, try again. + * Args: ctx: pointer to a context object + * Out: ell64: pointer to a 64-byte array to receive the ElligatorSwift + * public key + * In: seckey32: pointer to a 32-byte secret key + * auxrnd32: (optional) pointer to 32 bytes of randomness + * + * Constant time in seckey and auxrnd32, but not in the resulting public key. + * + * It is recommended that auxrnd32 contains 32 uniformly random bytes, though + * it is optional (and does result in encodings that are indistinguishable from + * uniform even without any auxrnd32). It differs from the (mandatory) rnd32 + * argument to secp256k1_ellswift_encode in this regard. + * + * This function can be used instead of calling secp256k1_ec_pubkey_create + * followed by secp256k1_ellswift_encode. It is safer, as it uses the secret + * key as entropy for the encoding (supplemented with auxrnd32, if provided). + * + * Like secp256k1_ellswift_encode, this function does not guarantee that the + * computed encoding is stable across versions of the library, even if all + * arguments (including auxrnd32) are the same. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_create( + const secp256k1_context *ctx, + unsigned char *ell64, + const unsigned char *seckey32, + const unsigned char *auxrnd32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Given a private key, and ElligatorSwift public keys sent in both directions, + * compute a shared secret using x-only Elliptic Curve Diffie-Hellman (ECDH). + * + * Returns: 1: shared secret was successfully computed + * 0: secret was invalid or hashfp returned 0 + * Args: ctx: pointer to a context object. + * Out: output: pointer to an array to be filled by hashfp. + * In: ell_a64: pointer to the 64-byte encoded public key of party A + * (will not be NULL) + * ell_b64: pointer to the 64-byte encoded public key of party B + * (will not be NULL) + * seckey32: a pointer to our 32-byte secret key + * party: boolean indicating which party we are: zero if we are + * party A, non-zero if we are party B. seckey32 must be + * the private key corresponding to that party's ell_?64. + * This correspondence is not checked. + * hashfp: pointer to a hash function. + * data: arbitrary data pointer passed through to hashfp. + * + * Constant time in seckey32. + * + * This function is more efficient than decoding the public keys, and performing + * ECDH on them. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_xdh( + const secp256k1_context *ctx, + unsigned char *output, + const unsigned char *ell_a64, + const unsigned char *ell_b64, + const unsigned char *seckey32, + int party, + secp256k1_ellswift_xdh_hash_function hashfp, + void *data +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ELLSWIFT_H */ diff --git a/src/secp256k1/include/secp256k1_extrakeys.h b/src/secp256k1/include/secp256k1_extrakeys.h index 52bba240b4..7fcce68e68 100644 --- a/src/secp256k1/include/secp256k1_extrakeys.h +++ b/src/secp256k1/include/secp256k1_extrakeys.h @@ -112,10 +112,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubke * Out: output_pubkey: pointer to a public key to store the result. Will be set * to an invalid value if this function returns 0. * In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to. - * tweak32: pointer to a 32-byte tweak. If the tweak is invalid - * according to secp256k1_ec_seckey_verify, this function - * returns 0. For uniformly random 32-byte arrays the - * chance of being invalid is negligible (around 1 in 2^128). + * tweak32: pointer to a 32-byte tweak, which must be valid + * according to secp256k1_ec_seckey_verify or 32 zero + * bytes. For uniformly random 32-byte tweaks, the chance of + * being invalid is negligible (around 1 in 2^128). */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add( const secp256k1_context *ctx, @@ -185,9 +185,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec( /** Get the public key from a keypair. * * Returns: 1 always. - * Args: ctx: pointer to a context object. - * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to - * the keypair public key. If not, it's set to an invalid value. + * Args: ctx: pointer to a context object. + * Out: pubkey: pointer to a pubkey object, set to the keypair public key. * In: keypair: pointer to a keypair. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub( @@ -203,9 +202,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub( * * Returns: 1 always. * Args: ctx: pointer to a context object. - * Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set - * to the keypair public key after converting it to an - * xonly_pubkey. If not, it's set to an invalid value. + * Out: pubkey: pointer to an xonly_pubkey object, set to the keypair + * public key after converting it to an xonly_pubkey. * pk_parity: Ignored if NULL. Otherwise, pointer to an integer that will be set to the * pk_parity argument of secp256k1_xonly_pubkey_from_pubkey. * In: keypair: pointer to a keypair. @@ -231,10 +229,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub( * Args: ctx: pointer to a context object. * In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to * an invalid value if this function returns 0. - * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according - * to secp256k1_ec_seckey_verify, this function returns 0. For - * uniformly random 32-byte arrays the chance of being invalid - * is negligible (around 1 in 2^128). + * In: tweak32: pointer to a 32-byte tweak, which must be valid according to + * secp256k1_ec_seckey_verify or 32 zero bytes. For uniformly + * random 32-byte tweaks, the chance of being invalid is + * negligible (around 1 in 2^128). */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add( const secp256k1_context *ctx, diff --git a/src/secp256k1/include/secp256k1_schnorrsig.h b/src/secp256k1/include/secp256k1_schnorrsig.h index 733fee5282..26358533f6 100644 --- a/src/secp256k1/include/secp256k1_schnorrsig.h +++ b/src/secp256k1/include/secp256k1_schnorrsig.h @@ -61,7 +61,7 @@ typedef int (*secp256k1_nonce_function_hardened)( * Therefore, to create BIP-340 compliant signatures, algo must be set to * "BIP0340/nonce" and algolen to 13. */ -SECP256K1_API_VAR const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340; +SECP256K1_API const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340; /** Data structure that contains additional arguments for schnorrsig_sign_custom. * @@ -141,12 +141,20 @@ SECP256K1_API int secp256k1_schnorrsig_sign( * variable length messages and accepts a pointer to an extraparams object that * allows customizing signing by passing additional arguments. * - * Creates the same signatures as schnorrsig_sign if msglen is 32 and the - * extraparams.ndata is the same as aux_rand32. + * Equivalent to secp256k1_schnorrsig_sign32(..., aux_rand32) if msglen is 32 + * and extraparams is initialized as follows: + * ``` + * secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; + * extraparams.ndata = (unsigned char*)aux_rand32; + * ``` * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object (not secp256k1_context_static). + * Out: sig64: pointer to a 64-byte array to store the serialized signature. * In: msg: the message being signed. Can only be NULL if msglen is 0. - * msglen: length of the message - * extraparams: pointer to a extraparams object (can be NULL) + * msglen: length of the message. + * keypair: pointer to an initialized keypair. + * extraparams: pointer to an extraparams object (can be NULL). */ SECP256K1_API int secp256k1_schnorrsig_sign_custom( const secp256k1_context *ctx, diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage index 9305c215d5..bb09295369 100644 --- a/src/secp256k1/sage/group_prover.sage +++ b/src/secp256k1/sage/group_prover.sage @@ -198,7 +198,7 @@ def normalize_factor(p): (8) * (-bx + ax)^3 ``` """ - # Assert p is not 0 and that its non-zero coeffients are coprime. + # Assert p is not 0 and that its non-zero coefficients are coprime. # (We could just work with the primitive part p/p.content() but we want to be # aware if factor() does not return a primitive part in future sage versions.) assert p.content() == 1 diff --git a/src/secp256k1/src/CMakeLists.txt b/src/secp256k1/src/CMakeLists.txt index 26272d0950..b305751b08 100644 --- a/src/secp256k1/src/CMakeLists.txt +++ b/src/secp256k1/src/CMakeLists.txt @@ -1,151 +1,165 @@ # Must be included before CMAKE_INSTALL_INCLUDEDIR is used. include(GNUInstallDirs) -set(${PROJECT_NAME}_installables "") -if(SECP256K1_ASM STREQUAL "arm") - add_library(common OBJECT - asm/field_10x26_arm.s - ) - set(common_obj "$<TARGET_OBJECTS:common>") -else() - set(common_obj "") -endif() - -add_library(precomputed OBJECT +add_library(secp256k1_precomputed OBJECT EXCLUDE_FROM_ALL precomputed_ecmult.c precomputed_ecmult_gen.c ) -set(internal_obj "$<TARGET_OBJECTS:precomputed>" "${common_obj}") -add_library(secp256k1 SHARED EXCLUDE_FROM_ALL - secp256k1.c - ${internal_obj} -) -target_include_directories(secp256k1 INTERFACE - $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> -) -target_compile_definitions(secp256k1 PRIVATE - $<$<PLATFORM_ID:Windows>:DLL_EXPORT> -) -set_target_properties(secp256k1 PROPERTIES - VERSION "${${PROJECT_NAME}_LIB_VERSION_CURRENT}.${${PROJECT_NAME}_LIB_VERSION_AGE}.${${PROJECT_NAME}_LIB_VERSION_REVISION}" - SOVERSION "${${PROJECT_NAME}_LIB_VERSION_CURRENT}" -) -if(SECP256K1_BUILD_SHARED) - get_target_property(use_pic secp256k1 POSITION_INDEPENDENT_CODE) - set_target_properties(precomputed PROPERTIES POSITION_INDEPENDENT_CODE ${use_pic}) - set_target_properties(secp256k1 PROPERTIES EXCLUDE_FROM_ALL FALSE) - list(APPEND ${PROJECT_NAME}_installables secp256k1) -endif() +# Add objects explicitly rather than linking to the object libs to keep them +# from being exported. +add_library(secp256k1 secp256k1.c $<TARGET_OBJECTS:secp256k1_precomputed>) -add_library(secp256k1_static STATIC EXCLUDE_FROM_ALL - secp256k1.c - ${internal_obj} -) -target_include_directories(secp256k1_static INTERFACE - $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> -) -if(NOT MSVC) - set_target_properties(secp256k1_static PROPERTIES - OUTPUT_NAME secp256k1 +add_library(secp256k1_asm INTERFACE) +if(SECP256K1_ASM STREQUAL "arm32") + add_library(secp256k1_asm_arm OBJECT EXCLUDE_FROM_ALL) + target_sources(secp256k1_asm_arm PUBLIC + asm/field_10x26_arm.s ) + target_sources(secp256k1 PRIVATE $<TARGET_OBJECTS:secp256k1_asm_arm>) + target_link_libraries(secp256k1_asm INTERFACE secp256k1_asm_arm) endif() -if(SECP256K1_BUILD_STATIC) - set_target_properties(secp256k1_static PROPERTIES EXCLUDE_FROM_ALL FALSE) - list(APPEND ${PROJECT_NAME}_installables secp256k1_static) + +if(WIN32) + # Define our export symbol only for shared libs. + set_target_properties(secp256k1 PROPERTIES DEFINE_SYMBOL SECP256K1_DLL_EXPORT) + target_compile_definitions(secp256k1 INTERFACE $<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:SECP256K1_STATIC>) endif() -add_library(binary_interface INTERFACE) -target_compile_definitions(binary_interface INTERFACE - $<$<C_COMPILER_ID:MSVC>:_CRT_SECURE_NO_WARNINGS> +# Object libs don't know if they're being built for a shared or static lib. +# Grab the PIC property from secp256k1 which knows. +get_target_property(use_pic secp256k1 POSITION_INDEPENDENT_CODE) +set_target_properties(secp256k1_precomputed PROPERTIES POSITION_INDEPENDENT_CODE ${use_pic}) + +target_include_directories(secp256k1 INTERFACE + # Add the include path for parent projects so that they don't have to manually add it. + $<BUILD_INTERFACE:$<$<NOT:$<BOOL:${PROJECT_IS_TOP_LEVEL}>>:${PROJECT_SOURCE_DIR}/include>> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) -add_library(link_library INTERFACE) -if(SECP256K1_BUILD_SHARED) - target_link_libraries(link_library INTERFACE secp256k1) -elseif(SECP256K1_BUILD_STATIC) - target_link_libraries(link_library INTERFACE secp256k1_static) +# This emulates Libtool to make sure Libtool and CMake agree on the ABI version, +# see below "Calculate the version variables" in build-aux/ltmain.sh. +math(EXPR ${PROJECT_NAME}_soversion "${${PROJECT_NAME}_LIB_VERSION_CURRENT} - ${${PROJECT_NAME}_LIB_VERSION_AGE}") +set_target_properties(secp256k1 PROPERTIES + SOVERSION ${${PROJECT_NAME}_soversion} +) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set_target_properties(secp256k1 PROPERTIES + VERSION ${${PROJECT_NAME}_soversion}.${${PROJECT_NAME}_LIB_VERSION_AGE}.${${PROJECT_NAME}_LIB_VERSION_REVISION} + ) +elseif(APPLE) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) + math(EXPR ${PROJECT_NAME}_compatibility_version "${${PROJECT_NAME}_LIB_VERSION_CURRENT} + 1") + set_target_properties(secp256k1 PROPERTIES + MACHO_COMPATIBILITY_VERSION ${${PROJECT_NAME}_compatibility_version} + MACHO_CURRENT_VERSION ${${PROJECT_NAME}_compatibility_version}.${${PROJECT_NAME}_LIB_VERSION_REVISION} + ) + unset(${PROJECT_NAME}_compatibility_version) + elseif(BUILD_SHARED_LIBS) + message(WARNING + "The 'compatibility version' and 'current version' values of the DYLIB " + "will diverge from the values set by the GNU Libtool. To ensure " + "compatibility, it is recommended to upgrade CMake to at least version 3.17." + ) + endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(${PROJECT_NAME}_windows "secp256k1") + if(MSVC) + set(${PROJECT_NAME}_windows "${PROJECT_NAME}") + endif() + set_target_properties(secp256k1 PROPERTIES + ARCHIVE_OUTPUT_NAME "${${PROJECT_NAME}_windows}" + RUNTIME_OUTPUT_NAME "${${PROJECT_NAME}_windows}-${${PROJECT_NAME}_soversion}" + ) + unset(${PROJECT_NAME}_windows) endif() +unset(${PROJECT_NAME}_soversion) if(SECP256K1_BUILD_BENCHMARK) add_executable(bench bench.c) - target_link_libraries(bench binary_interface link_library) - add_executable(bench_internal bench_internal.c ${internal_obj}) - target_link_libraries(bench_internal binary_interface) - add_executable(bench_ecmult bench_ecmult.c ${internal_obj}) - target_link_libraries(bench_ecmult binary_interface) + target_link_libraries(bench secp256k1) + add_executable(bench_internal bench_internal.c) + target_link_libraries(bench_internal secp256k1_precomputed secp256k1_asm) + add_executable(bench_ecmult bench_ecmult.c) + target_link_libraries(bench_ecmult secp256k1_precomputed secp256k1_asm) endif() if(SECP256K1_BUILD_TESTS) - add_executable(noverify_tests tests.c ${internal_obj}) - target_link_libraries(noverify_tests binary_interface) - add_test(noverify_tests noverify_tests) + add_executable(noverify_tests tests.c) + target_link_libraries(noverify_tests secp256k1_precomputed secp256k1_asm) + add_test(NAME noverify_tests COMMAND noverify_tests) if(NOT CMAKE_BUILD_TYPE STREQUAL "Coverage") - add_executable(tests tests.c ${internal_obj}) + add_executable(tests tests.c) target_compile_definitions(tests PRIVATE VERIFY) - target_link_libraries(tests binary_interface) - add_test(tests tests) + target_link_libraries(tests secp256k1_precomputed secp256k1_asm) + add_test(NAME tests COMMAND tests) endif() endif() if(SECP256K1_BUILD_EXHAUSTIVE_TESTS) - # Note: do not include $<TARGET_OBJECTS:precomputed> in exhaustive_tests (it uses runtime-generated tables). - add_executable(exhaustive_tests tests_exhaustive.c ${common_obj}) + # Note: do not include secp256k1_precomputed in exhaustive_tests (it uses runtime-generated tables). + add_executable(exhaustive_tests tests_exhaustive.c) + target_link_libraries(exhaustive_tests secp256k1_asm) target_compile_definitions(exhaustive_tests PRIVATE $<$<NOT:$<CONFIG:Coverage>>:VERIFY>) - target_link_libraries(exhaustive_tests binary_interface) - add_test(exhaustive_tests exhaustive_tests) + add_test(NAME exhaustive_tests COMMAND exhaustive_tests) endif() if(SECP256K1_BUILD_CTIME_TESTS) add_executable(ctime_tests ctime_tests.c) - target_link_libraries(ctime_tests binary_interface link_library) + target_link_libraries(ctime_tests secp256k1) endif() -install(TARGETS ${${PROJECT_NAME}_installables} - EXPORT ${PROJECT_NAME}-targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) -set(${PROJECT_NAME}_headers - "${PROJECT_SOURCE_DIR}/include/secp256k1.h" - "${PROJECT_SOURCE_DIR}/include/secp256k1_preallocated.h" -) -if(SECP256K1_ENABLE_MODULE_ECDH) - list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ecdh.h") -endif() -if(SECP256K1_ENABLE_MODULE_RECOVERY) - list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_recovery.h") -endif() -if(SECP256K1_ENABLE_MODULE_EXTRAKEYS) - list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_extrakeys.h") -endif() -if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) - list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig.h") -endif() -install(FILES ${${PROJECT_NAME}_headers} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) +if(SECP256K1_INSTALL) + install(TARGETS secp256k1 + EXPORT ${PROJECT_NAME}-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + set(${PROJECT_NAME}_headers + "${PROJECT_SOURCE_DIR}/include/secp256k1.h" + "${PROJECT_SOURCE_DIR}/include/secp256k1_preallocated.h" + ) + if(SECP256K1_ENABLE_MODULE_ECDH) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ecdh.h") + endif() + if(SECP256K1_ENABLE_MODULE_RECOVERY) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_recovery.h") + endif() + if(SECP256K1_ENABLE_MODULE_EXTRAKEYS) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_extrakeys.h") + endif() + if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig.h") + endif() + if(SECP256K1_ENABLE_MODULE_ELLSWIFT) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ellswift.h") + endif() + install(FILES ${${PROJECT_NAME}_headers} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) -install(EXPORT ${PROJECT_NAME}-targets - FILE ${PROJECT_NAME}-targets.cmake - NAMESPACE ${PROJECT_NAME}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -) + install(EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}-targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) -include(CMakePackageConfigHelpers) -configure_package_config_file( - ${PROJECT_SOURCE_DIR}/cmake/config.cmake.in - ${PROJECT_NAME}-config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} - NO_SET_AND_CHECK_MACRO -) -write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake - COMPATIBILITY SameMajorVersion -) -install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + include(CMakePackageConfigHelpers) + configure_package_config_file( + ${PROJECT_SOURCE_DIR}/cmake/config.cmake.in + ${PROJECT_NAME}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + NO_SET_AND_CHECK_MACRO + ) + write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake + COMPATIBILITY SameMinorVersion + ) + + install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} ) +endif() diff --git a/src/secp256k1/src/bench.c b/src/secp256k1/src/bench.c index 833f70718b..1127df67ae 100644 --- a/src/secp256k1/src/bench.c +++ b/src/secp256k1/src/bench.c @@ -38,6 +38,8 @@ static void help(int default_iters) { printf(" ecdsa : all ECDSA algorithms--sign, verify, recovery (if enabled)\n"); printf(" ecdsa_sign : ECDSA siging algorithm\n"); printf(" ecdsa_verify : ECDSA verification algorithm\n"); + printf(" ec : all EC public key algorithms (keygen)\n"); + printf(" ec_keygen : EC public key generation\n"); #ifdef ENABLE_MODULE_RECOVERY printf(" ecdsa_recover : ECDSA public key recovery algorithm\n"); @@ -53,6 +55,14 @@ static void help(int default_iters) { printf(" schnorrsig_verify : Schnorr verification algorithm\n"); #endif +#ifdef ENABLE_MODULE_ELLSWIFT + printf(" ellswift : all ElligatorSwift benchmarks (encode, decode, keygen, ecdh)\n"); + printf(" ellswift_encode : ElligatorSwift encoding\n"); + printf(" ellswift_decode : ElligatorSwift decoding\n"); + printf(" ellswift_keygen : ElligatorSwift key generation\n"); + printf(" ellswift_ecdh : ECDH on ElligatorSwift keys\n"); +#endif + printf("\n"); } @@ -64,11 +74,11 @@ typedef struct { size_t siglen; unsigned char pubkey[33]; size_t pubkeylen; -} bench_verify_data; +} bench_data; static void bench_verify(void* arg, int iters) { int i; - bench_verify_data* data = (bench_verify_data*)arg; + bench_data* data = (bench_data*)arg; for (i = 0; i < iters; i++) { secp256k1_pubkey pubkey; @@ -85,15 +95,9 @@ static void bench_verify(void* arg, int iters) { } } -typedef struct { - secp256k1_context* ctx; - unsigned char msg[32]; - unsigned char key[32]; -} bench_sign_data; - static void bench_sign_setup(void* arg) { int i; - bench_sign_data *data = (bench_sign_data*)arg; + bench_data *data = (bench_data*)arg; for (i = 0; i < 32; i++) { data->msg[i] = i + 1; @@ -105,7 +109,7 @@ static void bench_sign_setup(void* arg) { static void bench_sign_run(void* arg, int iters) { int i; - bench_sign_data *data = (bench_sign_data*)arg; + bench_data *data = (bench_data*)arg; unsigned char sig[74]; for (i = 0; i < iters; i++) { @@ -121,6 +125,30 @@ static void bench_sign_run(void* arg, int iters) { } } +static void bench_keygen_setup(void* arg) { + int i; + bench_data *data = (bench_data*)arg; + + for (i = 0; i < 32; i++) { + data->key[i] = i + 65; + } +} + +static void bench_keygen_run(void *arg, int iters) { + int i; + bench_data *data = (bench_data*)arg; + + for (i = 0; i < iters; i++) { + unsigned char pub33[33]; + size_t len = 33; + secp256k1_pubkey pubkey; + CHECK(secp256k1_ec_pubkey_create(data->ctx, &pubkey, data->key)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pub33, &len, &pubkey, SECP256K1_EC_COMPRESSED)); + memcpy(data->key, pub33 + 1, 32); + } +} + + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/bench_impl.h" #endif @@ -133,11 +161,15 @@ static void bench_sign_run(void* arg, int iters) { # include "modules/schnorrsig/bench_impl.h" #endif +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/bench_impl.h" +#endif + int main(int argc, char** argv) { int i; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature sig; - bench_verify_data data; + bench_data data; int d = argc == 1; int default_iters = 20000; @@ -145,7 +177,9 @@ int main(int argc, char** argv) { /* Check for invalid user arguments */ char* valid_args[] = {"ecdsa", "verify", "ecdsa_verify", "sign", "ecdsa_sign", "ecdh", "recover", - "ecdsa_recover", "schnorrsig", "schnorrsig_verify", "schnorrsig_sign"}; + "ecdsa_recover", "schnorrsig", "schnorrsig_verify", "schnorrsig_sign", "ec", + "keygen", "ec_keygen", "ellswift", "encode", "ellswift_encode", "decode", + "ellswift_decode", "ellswift_keygen", "ellswift_ecdh"}; size_t valid_args_size = sizeof(valid_args)/sizeof(valid_args[0]); int invalid_args = have_invalid_args(argc, argv, valid_args, valid_args_size); @@ -187,6 +221,16 @@ int main(int argc, char** argv) { } #endif +#ifndef ENABLE_MODULE_ELLSWIFT + if (have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "ellswift_encode") || have_flag(argc, argv, "ellswift_decode") || + have_flag(argc, argv, "encode") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellswift_keygen") || + have_flag(argc, argv, "ellswift_ecdh")) { + fprintf(stderr, "./bench: ElligatorSwift module not enabled.\n"); + fprintf(stderr, "Use ./configure --enable-module-ellswift.\n\n"); + return 1; + } +#endif + /* ECDSA benchmark */ data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); @@ -207,6 +251,7 @@ int main(int argc, char** argv) { if (d || have_flag(argc, argv, "ecdsa") || have_flag(argc, argv, "verify") || have_flag(argc, argv, "ecdsa_verify")) run_benchmark("ecdsa_verify", bench_verify, NULL, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "ecdsa") || have_flag(argc, argv, "sign") || have_flag(argc, argv, "ecdsa_sign")) run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ec") || have_flag(argc, argv, "keygen") || have_flag(argc, argv, "ec_keygen")) run_benchmark("ec_keygen", bench_keygen_run, bench_keygen_setup, NULL, &data, 10, iters); secp256k1_context_destroy(data.ctx); @@ -225,5 +270,10 @@ int main(int argc, char** argv) { run_schnorrsig_bench(iters, argc, argv); #endif +#ifdef ENABLE_MODULE_ELLSWIFT + /* ElligatorSwift benchmarks */ + run_ellswift_bench(iters, argc, argv); +#endif + return 0; } diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h index bf9a932ff4..1564b1a176 100644 --- a/src/secp256k1/src/bench.h +++ b/src/secp256k1/src/bench.h @@ -15,7 +15,7 @@ #if (defined(_MSC_VER) && _MSC_VER >= 1900) # include <time.h> #else -# include "sys/time.h" +# include <sys/time.h> #endif static int64_t gettime_i64(void) { diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c index 98fb798d82..8818aa81b5 100644 --- a/src/secp256k1/src/bench_ecmult.c +++ b/src/secp256k1/src/bench_ecmult.c @@ -113,7 +113,7 @@ static void bench_ecmult_const(void* arg, int iters) { int i; for (i = 0; i < iters; ++i) { - secp256k1_ecmult_const(&data->output[i], &data->pubkeys[(data->offset1+i) % POINTS], &data->scalars[(data->offset2+i) % POINTS], 256); + secp256k1_ecmult_const(&data->output[i], &data->pubkeys[(data->offset1+i) % POINTS], &data->scalars[(data->offset2+i) % POINTS]); } } @@ -138,12 +138,10 @@ static void bench_ecmult_1p_teardown(void* arg, int iters) { static void bench_ecmult_0p_g(void* arg, int iters) { bench_data* data = (bench_data*)arg; - secp256k1_scalar zero; int i; - secp256k1_scalar_set_int(&zero, 0); for (i = 0; i < iters; ++i) { - secp256k1_ecmult(&data->output[i], NULL, &zero, &data->scalars[(data->offset1+i) % POINTS]); + secp256k1_ecmult(&data->output[i], NULL, &secp256k1_scalar_zero, &data->scalars[(data->offset1+i) % POINTS]); } } diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c index c248ab8ebc..f3686dd289 100644 --- a/src/secp256k1/src/bench_internal.c +++ b/src/secp256k1/src/bench_internal.c @@ -65,10 +65,10 @@ static void bench_setup(void* arg) { secp256k1_scalar_set_b32(&data->scalar[0], init[0], NULL); secp256k1_scalar_set_b32(&data->scalar[1], init[1], NULL); - secp256k1_fe_set_b32(&data->fe[0], init[0]); - secp256k1_fe_set_b32(&data->fe[1], init[1]); - secp256k1_fe_set_b32(&data->fe[2], init[2]); - secp256k1_fe_set_b32(&data->fe[3], init[3]); + secp256k1_fe_set_b32_limit(&data->fe[0], init[0]); + secp256k1_fe_set_b32_limit(&data->fe[1], init[1]); + secp256k1_fe_set_b32_limit(&data->fe[2], init[2]); + secp256k1_fe_set_b32_limit(&data->fe[3], init[3]); CHECK(secp256k1_ge_set_xo_var(&data->ge[0], &data->fe[0], 0)); CHECK(secp256k1_ge_set_xo_var(&data->ge[1], &data->fe[1], 1)); secp256k1_gej_set_ge(&data->gej[0], &data->ge[0]); diff --git a/src/secp256k1/src/ctime_tests.c b/src/secp256k1/src/ctime_tests.c index 713eb427d3..af7891a91c 100644 --- a/src/secp256k1/src/ctime_tests.c +++ b/src/secp256k1/src/ctime_tests.c @@ -30,6 +30,10 @@ #include "../include/secp256k1_schnorrsig.h" #endif +#ifdef ENABLE_MODULE_ELLSWIFT +#include "../include/secp256k1_ellswift.h" +#endif + static void run_tests(secp256k1_context *ctx, unsigned char *key); int main(void) { @@ -80,6 +84,10 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { #ifdef ENABLE_MODULE_EXTRAKEYS secp256k1_keypair keypair; #endif +#ifdef ENABLE_MODULE_ELLSWIFT + unsigned char ellswift[64]; + static const unsigned char prefix[64] = {'t', 'e', 's', 't'}; +#endif for (i = 0; i < 32; i++) { msg[i] = i + 1; @@ -171,4 +179,31 @@ static void run_tests(secp256k1_context *ctx, unsigned char *key) { SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); CHECK(ret == 1); #endif + +#ifdef ENABLE_MODULE_ELLSWIFT + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + ret = secp256k1_ellswift_create(ctx, ellswift, key, NULL); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + ret = secp256k1_ellswift_create(ctx, ellswift, key, ellswift); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + for (i = 0; i < 2; i++) { + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + VALGRIND_MAKE_MEM_DEFINED(&ellswift, sizeof(ellswift)); + ret = secp256k1_ellswift_xdh(ctx, msg, ellswift, ellswift, key, i, secp256k1_ellswift_xdh_hash_function_bip324, NULL); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + VALGRIND_MAKE_MEM_DEFINED(&ellswift, sizeof(ellswift)); + ret = secp256k1_ellswift_xdh(ctx, msg, ellswift, ellswift, key, i, secp256k1_ellswift_xdh_hash_function_prefix, (void *)prefix); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + } + +#endif } diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h index 90b4b22b77..e71254d9f9 100644 --- a/src/secp256k1/src/ecdsa_impl.h +++ b/src/secp256k1/src/ecdsa_impl.h @@ -16,17 +16,8 @@ #include "ecdsa.h" /** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1 - * sage: for t in xrange(1023, -1, -1): - * .. p = 2**256 - 2**32 - t - * .. if p.is_prime(): - * .. print '%x'%p - * .. break - * 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' - * sage: a = 0 - * sage: b = 7 - * sage: F = FiniteField (p) - * sage: '%x' % (EllipticCurve ([F (a), F (b)]).order()) - * 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' + * $ sage -c 'load("secp256k1_params.sage"); print(hex(N))' + * 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 */ static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST( 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, @@ -35,12 +26,8 @@ static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST /** Difference between field and order, values 'p' and 'n' values defined in * "Standards for Efficient Cryptography" (SEC2) 2.7.1. - * sage: p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F - * sage: a = 0 - * sage: b = 7 - * sage: F = FiniteField (p) - * sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order()) - * '14551231950b75fc4402da1722fc9baee' + * $ sage -c 'load("secp256k1_params.sage"); print(hex(P-N))' + * 0x14551231950b75fc4402da1722fc9baee */ static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST( 0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL @@ -239,7 +226,8 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_scalar *sigr, const secp25 } #else secp256k1_scalar_get_b32(c, sigr); - secp256k1_fe_set_b32(&xr, c); + /* we can ignore the fe_set_b32_limit return value, because we know the input is in range */ + (void)secp256k1_fe_set_b32_limit(&xr, c); /** We now have the recomputed R point in pr, and its claimed x coordinate (modulo n) * in xr. Naively, we would extract the x coordinate from pr (requiring a inversion modulo p), diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h index e0506d3e2b..121966f8b5 100644 --- a/src/secp256k1/src/eckey_impl.h +++ b/src/secp256k1/src/eckey_impl.h @@ -17,10 +17,10 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) { if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) { secp256k1_fe x; - return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD); + return secp256k1_fe_set_b32_limit(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD); } else if (size == 65 && (pub[0] == SECP256K1_TAG_PUBKEY_UNCOMPRESSED || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) { secp256k1_fe x, y; - if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { + if (!secp256k1_fe_set_b32_limit(&x, pub+1) || !secp256k1_fe_set_b32_limit(&y, pub+33)) { return 0; } secp256k1_ge_set_xy(elem, &x, &y); @@ -59,10 +59,8 @@ static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp25 static int secp256k1_eckey_pubkey_tweak_add(secp256k1_ge *key, const secp256k1_scalar *tweak) { secp256k1_gej pt; - secp256k1_scalar one; secp256k1_gej_set_ge(&pt, key); - secp256k1_scalar_set_int(&one, 1); - secp256k1_ecmult(&pt, &pt, &one, tweak); + secp256k1_ecmult(&pt, &pt, &secp256k1_scalar_one, tweak); if (secp256k1_gej_is_infinity(&pt)) { return 0; @@ -80,15 +78,13 @@ static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp25 } static int secp256k1_eckey_pubkey_tweak_mul(secp256k1_ge *key, const secp256k1_scalar *tweak) { - secp256k1_scalar zero; secp256k1_gej pt; if (secp256k1_scalar_is_zero(tweak)) { return 0; } - secp256k1_scalar_set_int(&zero, 0); secp256k1_gej_set_ge(&pt, key); - secp256k1_ecmult(&pt, &pt, tweak, &zero); + secp256k1_ecmult(&pt, &pt, tweak, &secp256k1_scalar_zero); secp256k1_ge_set_gej(key, &pt); return 1; } diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h index e28c602506..326a5eeb43 100644 --- a/src/secp256k1/src/ecmult.h +++ b/src/secp256k1/src/ecmult.h @@ -22,7 +22,7 @@ # pragma message DEBUG_CONFIG_DEF(ECMULT_WINDOW_SIZE) #endif -/* Noone will ever need more than a window size of 24. The code might +/* No one will ever need more than a window size of 24. The code might * be correct for larger values of ECMULT_WINDOW_SIZE but this is not * tested. * diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h index 417f328535..080e04bc88 100644 --- a/src/secp256k1/src/ecmult_const.h +++ b/src/secp256k1/src/ecmult_const.h @@ -11,12 +11,9 @@ #include "group.h" /** - * Multiply: R = q*A (in constant-time) - * Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus - * one because we internally sometimes add 2 to the number during the WNAF conversion. - * A must not be infinity. + * Multiply: R = q*A (in constant-time for q) */ -static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits); +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); /** * Same as secp256k1_ecmult_const, but takes in an x coordinate of the base point @@ -35,7 +32,6 @@ static int secp256k1_ecmult_const_xonly( const secp256k1_fe *n, const secp256k1_fe *d, const secp256k1_scalar *q, - int bits, int known_on_curve ); diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h index f23e0ec89d..06f9e53ffd 100644 --- a/src/secp256k1/src/ecmult_const_impl.h +++ b/src/secp256k1/src/ecmult_const_impl.h @@ -29,7 +29,7 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *p #define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ int m = 0; \ /* Extract the sign-bit for a constant time absolute-value. */ \ - int mask = (n) >> (sizeof(n) * CHAR_BIT - 1); \ + int volatile mask = (n) >> (sizeof(n) * CHAR_BIT - 1); \ int abs_n = ((n) + mask) ^ mask; \ int idx_n = abs_n >> 1; \ secp256k1_fe neg_y; \ @@ -130,7 +130,7 @@ static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w return skew; } -static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar, int size) { +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar) { secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; secp256k1_ge tmpa; secp256k1_fe Z; @@ -144,20 +144,17 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons int i; - /* build wnaf representation for q. */ - int rsize = size; - if (size > 128) { - rsize = 128; - /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ - secp256k1_scalar_split_lambda(&q_1, &q_lam, scalar); - skew_1 = secp256k1_wnaf_const(wnaf_1, &q_1, WINDOW_A - 1, 128); - skew_lam = secp256k1_wnaf_const(wnaf_lam, &q_lam, WINDOW_A - 1, 128); - } else - { - skew_1 = secp256k1_wnaf_const(wnaf_1, scalar, WINDOW_A - 1, size); - skew_lam = 0; + if (secp256k1_ge_is_infinity(a)) { + secp256k1_gej_set_infinity(r); + return; } + /* build wnaf representation for q. */ + /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&q_1, &q_lam, scalar); + skew_1 = secp256k1_wnaf_const(wnaf_1, &q_1, WINDOW_A - 1, 128); + skew_lam = secp256k1_wnaf_const(wnaf_lam, &q_lam, WINDOW_A - 1, 128); + /* Calculate odd multiples of a. * All multiples are brought to the same Z 'denominator', which is stored * in Z. Due to secp256k1' isomorphism we can do all operations pretending @@ -170,28 +167,23 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { secp256k1_fe_normalize_weak(&pre_a[i].y); } - if (size > 128) { - for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { - secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); - } - + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); } /* first loop iteration (separated out so we can directly set r, rather * than having it start at infinity, get doubled several times, then have * its new value added to it) */ - i = wnaf_1[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)]; + i = wnaf_1[WNAF_SIZE_BITS(128, WINDOW_A - 1)]; VERIFY_CHECK(i != 0); ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); secp256k1_gej_set_ge(r, &tmpa); - if (size > 128) { - i = wnaf_lam[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)]; - VERIFY_CHECK(i != 0); - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A); - secp256k1_gej_add_ge(r, r, &tmpa); - } + i = wnaf_lam[WNAF_SIZE_BITS(128, WINDOW_A - 1)]; + VERIFY_CHECK(i != 0); + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A); + secp256k1_gej_add_ge(r, r, &tmpa); /* remaining loop iterations */ - for (i = WNAF_SIZE_BITS(rsize, WINDOW_A - 1) - 1; i >= 0; i--) { + for (i = WNAF_SIZE_BITS(128, WINDOW_A - 1) - 1; i >= 0; i--) { int n; int j; for (j = 0; j < WINDOW_A - 1; ++j) { @@ -202,12 +194,10 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); VERIFY_CHECK(n != 0); secp256k1_gej_add_ge(r, r, &tmpa); - if (size > 128) { - n = wnaf_lam[i]; - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); - VERIFY_CHECK(n != 0); - secp256k1_gej_add_ge(r, r, &tmpa); - } + n = wnaf_lam[i]; + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); + VERIFY_CHECK(n != 0); + secp256k1_gej_add_ge(r, r, &tmpa); } { @@ -218,17 +208,15 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons secp256k1_gej_add_ge(&tmpj, r, &tmpa); secp256k1_gej_cmov(r, &tmpj, skew_1); - if (size > 128) { - secp256k1_ge_neg(&tmpa, &pre_a_lam[0]); - secp256k1_gej_add_ge(&tmpj, r, &tmpa); - secp256k1_gej_cmov(r, &tmpj, skew_lam); - } + secp256k1_ge_neg(&tmpa, &pre_a_lam[0]); + secp256k1_gej_add_ge(&tmpj, r, &tmpa); + secp256k1_gej_cmov(r, &tmpj, skew_lam); } secp256k1_fe_mul(&r->z, &r->z, &Z); } -static int secp256k1_ecmult_const_xonly(secp256k1_fe* r, const secp256k1_fe *n, const secp256k1_fe *d, const secp256k1_scalar *q, int bits, int known_on_curve) { +static int secp256k1_ecmult_const_xonly(secp256k1_fe* r, const secp256k1_fe *n, const secp256k1_fe *d, const secp256k1_scalar *q, int known_on_curve) { /* This algorithm is a generalization of Peter Dettman's technique for * avoiding the square root in a random-basepoint x-only multiplication @@ -288,7 +276,7 @@ static int secp256k1_ecmult_const_xonly(secp256k1_fe* r, const secp256k1_fe *n, * * It is easy to verify that both (n*g, g^2, v) and its negation (n*g, -g^2, v) have affine X * coordinate n/d, and this holds even when the square root function doesn't have a - * determinstic sign. We choose the (n*g, g^2, v) version. + * deterministic sign. We choose the (n*g, g^2, v) version. * * Now switch to the effective affine curve using phi_v, where the input point has coordinates * (n*g, g^2). Compute (X, Y, Z) = q * (n*g, g^2) there. @@ -346,7 +334,7 @@ static int secp256k1_ecmult_const_xonly(secp256k1_fe* r, const secp256k1_fe *n, #ifdef VERIFY VERIFY_CHECK(!secp256k1_scalar_is_zero(q)); #endif - secp256k1_ecmult_const(&rj, &p, q, bits); + secp256k1_ecmult_const(&rj, &p, q); #ifdef VERIFY VERIFY_CHECK(!secp256k1_gej_is_infinity(&rj)); #endif diff --git a/src/secp256k1/src/ecmult_gen_compute_table_impl.h b/src/secp256k1/src/ecmult_gen_compute_table_impl.h index ff6a2992dc..dfbacdbfde 100644 --- a/src/secp256k1/src/ecmult_gen_compute_table_impl.h +++ b/src/secp256k1/src/ecmult_gen_compute_table_impl.h @@ -22,6 +22,9 @@ static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, cons secp256k1_gej nums_gej; int i, j; + VERIFY_CHECK(g > 0); + VERIFY_CHECK(n > 0); + /* get the generator */ secp256k1_gej_set_ge(&gj, gen); @@ -31,7 +34,7 @@ static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, cons secp256k1_fe nums_x; secp256k1_ge nums_ge; int r; - r = secp256k1_fe_set_b32(&nums_x, nums_b32); + r = secp256k1_fe_set_b32_limit(&nums_x, nums_b32); (void)r; VERIFY_CHECK(r); r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0); diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h index 4f5ea9f3c0..af412173e9 100644 --- a/src/secp256k1/src/ecmult_gen_impl.h +++ b/src/secp256k1/src/ecmult_gen_impl.h @@ -87,7 +87,6 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_fe s; unsigned char nonce32[32]; secp256k1_rfc6979_hmac_sha256 rng; - int overflow; unsigned char keydata[64]; if (seed32 == NULL) { /* When seed is NULL, reset the initial point and blinding value. */ @@ -106,11 +105,9 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const memcpy(keydata + 32, seed32, 32); secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64); memset(keydata, 0, sizeof(keydata)); - /* Accept unobservably small non-uniformity. */ secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); - overflow = !secp256k1_fe_set_b32(&s, nonce32); - overflow |= secp256k1_fe_is_zero(&s); - secp256k1_fe_cmov(&s, &secp256k1_fe_one, overflow); + secp256k1_fe_set_b32_mod(&s, nonce32); + secp256k1_fe_cmov(&s, &secp256k1_fe_one, secp256k1_fe_normalizes_to_zero(&s)); /* Randomize the projection to defend against multiplier sidechannels. Do this before our own call to secp256k1_ecmult_gen below. */ secp256k1_gej_rescale(&ctx->initial, &s); diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h index a9a63850ef..6d14c7ac4b 100644 --- a/src/secp256k1/src/ecmult_impl.h +++ b/src/secp256k1/src/ecmult_impl.h @@ -279,9 +279,6 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * */ tmp = a[np]; if (no) { -#ifdef VERIFY - secp256k1_fe_normalize_var(&Z); -#endif secp256k1_gej_rescale(&tmp, &Z); } secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a + no * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), &Z, &tmp); @@ -291,7 +288,9 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * } /* Bring them to the same Z denominator. */ - secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->aux); + if (no) { + secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->aux); + } for (np = 0; np < no; ++np) { for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { @@ -683,7 +682,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call } state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps)); state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int)); - buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, (1<<bucket_window) * sizeof(*buckets)); + buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, ((size_t)1 << bucket_window) * sizeof(*buckets)); if (state_space->ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) { secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 0; @@ -773,14 +772,12 @@ static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_cal * require a scratch space */ static int secp256k1_ecmult_multi_simple_var(secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) { size_t point_idx; - secp256k1_scalar szero; secp256k1_gej tmpj; - secp256k1_scalar_set_int(&szero, 0); secp256k1_gej_set_infinity(r); secp256k1_gej_set_infinity(&tmpj); /* r = inp_g_sc*G */ - secp256k1_ecmult(r, &tmpj, &szero, inp_g_sc); + secp256k1_ecmult(r, &tmpj, &secp256k1_scalar_zero, inp_g_sc); for (point_idx = 0; point_idx < n_points; point_idx++) { secp256k1_ge point; secp256k1_gej pointj; @@ -828,9 +825,7 @@ static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, if (inp_g_sc == NULL && n == 0) { return 1; } else if (n == 0) { - secp256k1_scalar szero; - secp256k1_scalar_set_int(&szero, 0); - secp256k1_ecmult(r, r, &szero, inp_g_sc); + secp256k1_ecmult(r, r, &secp256k1_scalar_zero, inp_g_sc); return 1; } if (scratch == NULL) { diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h index 64ceead4d2..bb99f948ef 100644 --- a/src/secp256k1/src/field.h +++ b/src/secp256k1/src/field.h @@ -7,19 +7,36 @@ #ifndef SECP256K1_FIELD_H #define SECP256K1_FIELD_H -/** Field element module. - * - * Field elements can be represented in several ways, but code accessing - * it (and implementations) need to take certain properties into account: - * - Each field element can be normalized or not. - * - Each field element has a magnitude, which represents how far away - * its representation is away from normalization. Normalized elements - * always have a magnitude of 0 or 1, but a magnitude of 1 doesn't - * imply normality. - */ - #include "util.h" +/* This file defines the generic interface for working with secp256k1_fe + * objects, which represent field elements (integers modulo 2^256 - 2^32 - 977). + * + * The actual definition of the secp256k1_fe type depends on the chosen field + * implementation; see the field_5x52.h and field_10x26.h files for details. + * + * All secp256k1_fe objects have implicit properties that determine what + * operations are permitted on it. These are purely a function of what + * secp256k1_fe_ operations are applied on it, generally (implicitly) fixed at + * compile time, and do not depend on the chosen field implementation. Despite + * that, what these properties actually entail for the field representation + * values depends on the chosen field implementation. These properties are: + * - magnitude: an integer in [0,32] + * - normalized: 0 or 1; normalized=1 implies magnitude <= 1. + * + * In VERIFY mode, they are materialized explicitly as fields in the struct, + * allowing run-time verification of these properties. In that case, the field + * implementation also provides a secp256k1_fe_verify routine to verify that + * these fields match the run-time value and perform internal consistency + * checks. */ +#ifdef VERIFY +# define SECP256K1_FE_VERIFY_FIELDS \ + int magnitude; \ + int normalized; +#else +# define SECP256K1_FE_VERIFY_FIELDS +#endif + #if defined(SECP256K1_WIDEMUL_INT128) #include "field_5x52.h" #elif defined(SECP256K1_WIDEMUL_INT64) @@ -28,117 +45,311 @@ #error "Please select wide multiplication implementation" #endif +#ifdef VERIFY +/* Magnitude and normalized value for constants. */ +#define SECP256K1_FE_VERIFY_CONST(d7, d6, d5, d4, d3, d2, d1, d0) \ + /* Magnitude is 0 for constant 0; 1 otherwise. */ \ + , (((d7) | (d6) | (d5) | (d4) | (d3) | (d2) | (d1) | (d0)) != 0) \ + /* Normalized is 1 unless sum(d_i<<(32*i) for i=0..7) exceeds field modulus. */ \ + , (!(((d7) & (d6) & (d5) & (d4) & (d3) & (d2)) == 0xfffffffful && ((d1) == 0xfffffffful || ((d1) == 0xfffffffe && (d0 >= 0xfffffc2f))))) +#else +#define SECP256K1_FE_VERIFY_CONST(d7, d6, d5, d4, d3, d2, d1, d0) +#endif + +/** This expands to an initializer for a secp256k1_fe valued sum((i*32) * d_i, i=0..7) mod p. + * + * It has magnitude 1, unless d_i are all 0, in which case the magnitude is 0. + * It is normalized, unless sum(2^(i*32) * d_i, i=0..7) >= p. + * + * SECP256K1_FE_CONST_INNER is provided by the implementation. + */ +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)) SECP256K1_FE_VERIFY_CONST((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)) } + static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); static const secp256k1_fe secp256k1_const_beta = SECP256K1_FE_CONST( 0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul, 0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul ); -/** Normalize a field element. This brings the field element to a canonical representation, reduces - * its magnitude to 1, and reduces it modulo field size `p`. +#ifndef VERIFY +/* In non-VERIFY mode, we #define the fe operations to be identical to their + * internal field implementation, to avoid the potential overhead of a + * function call (even though presumably inlinable). */ +# define secp256k1_fe_normalize secp256k1_fe_impl_normalize +# define secp256k1_fe_normalize_weak secp256k1_fe_impl_normalize_weak +# define secp256k1_fe_normalize_var secp256k1_fe_impl_normalize_var +# define secp256k1_fe_normalizes_to_zero secp256k1_fe_impl_normalizes_to_zero +# define secp256k1_fe_normalizes_to_zero_var secp256k1_fe_impl_normalizes_to_zero_var +# define secp256k1_fe_set_int secp256k1_fe_impl_set_int +# define secp256k1_fe_clear secp256k1_fe_impl_clear +# define secp256k1_fe_is_zero secp256k1_fe_impl_is_zero +# define secp256k1_fe_is_odd secp256k1_fe_impl_is_odd +# define secp256k1_fe_cmp_var secp256k1_fe_impl_cmp_var +# define secp256k1_fe_set_b32_mod secp256k1_fe_impl_set_b32_mod +# define secp256k1_fe_set_b32_limit secp256k1_fe_impl_set_b32_limit +# define secp256k1_fe_get_b32 secp256k1_fe_impl_get_b32 +# define secp256k1_fe_negate_unchecked secp256k1_fe_impl_negate_unchecked +# define secp256k1_fe_mul_int_unchecked secp256k1_fe_impl_mul_int_unchecked +# define secp256k1_fe_add secp256k1_fe_impl_add +# define secp256k1_fe_mul secp256k1_fe_impl_mul +# define secp256k1_fe_sqr secp256k1_fe_impl_sqr +# define secp256k1_fe_cmov secp256k1_fe_impl_cmov +# define secp256k1_fe_to_storage secp256k1_fe_impl_to_storage +# define secp256k1_fe_from_storage secp256k1_fe_impl_from_storage +# define secp256k1_fe_inv secp256k1_fe_impl_inv +# define secp256k1_fe_inv_var secp256k1_fe_impl_inv_var +# define secp256k1_fe_get_bounds secp256k1_fe_impl_get_bounds +# define secp256k1_fe_half secp256k1_fe_impl_half +# define secp256k1_fe_add_int secp256k1_fe_impl_add_int +# define secp256k1_fe_is_square_var secp256k1_fe_impl_is_square_var +#endif /* !defined(VERIFY) */ + +/** Normalize a field element. + * + * On input, r must be a valid field element. + * On output, r represents the same value but has normalized=1 and magnitude=1. */ static void secp256k1_fe_normalize(secp256k1_fe *r); -/** Weakly normalize a field element: reduce its magnitude to 1, but don't fully normalize. */ +/** Give a field element magnitude 1. + * + * On input, r must be a valid field element. + * On output, r represents the same value but has magnitude=1. Normalized is unchanged. + */ static void secp256k1_fe_normalize_weak(secp256k1_fe *r); -/** Normalize a field element, without constant-time guarantee. */ +/** Normalize a field element, without constant-time guarantee. + * + * Identical in behavior to secp256k1_fe_normalize, but not constant time in r. + */ static void secp256k1_fe_normalize_var(secp256k1_fe *r); -/** Verify whether a field element represents zero i.e. would normalize to a zero value. */ +/** Determine whether r represents field element 0. + * + * On input, r must be a valid field element. + * Returns whether r = 0 (mod p). + */ static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r); -/** Verify whether a field element represents zero i.e. would normalize to a zero value, - * without constant-time guarantee. */ +/** Determine whether r represents field element 0, without constant-time guarantee. + * + * Identical in behavior to secp256k1_normalizes_to_zero, but not constant time in r. + */ static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r); -/** Set a field element equal to a small (not greater than 0x7FFF), non-negative integer. - * Resulting field element is normalized; it has magnitude 0 if a == 0, and magnitude 1 otherwise. +/** Set a field element to an integer in range [0,0x7FFF]. + * + * On input, r does not need to be initialized, a must be in [0,0x7FFF]. + * On output, r represents value a, is normalized and has magnitude (a!=0). */ static void secp256k1_fe_set_int(secp256k1_fe *r, int a); -/** Sets a field element equal to zero, initializing all fields. */ +/** Set a field element to 0. + * + * On input, a does not need to be initialized. + * On output, a represents 0, is normalized and has magnitude 0. + */ static void secp256k1_fe_clear(secp256k1_fe *a); -/** Verify whether a field element is zero. Requires the input to be normalized. */ +/** Determine whether a represents field element 0. + * + * On input, a must be a valid normalized field element. + * Returns whether a = 0 (mod p). + * + * This behaves identical to secp256k1_normalizes_to_zero{,_var}, but requires + * normalized input (and is much faster). + */ static int secp256k1_fe_is_zero(const secp256k1_fe *a); -/** Check the "oddness" of a field element. Requires the input to be normalized. */ +/** Determine whether a (mod p) is odd. + * + * On input, a must be a valid normalized field element. + * Returns (int(a) mod p) & 1. + */ static int secp256k1_fe_is_odd(const secp256k1_fe *a); -/** Compare two field elements. Requires magnitude-1 inputs. */ +/** Determine whether two field elements are equal. + * + * On input, a and b must be valid field elements with magnitudes not exceeding + * 1 and 31, respectively. + * Returns a = b (mod p). + */ static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b); -/** Same as secp256k1_fe_equal, but may be variable time. */ +/** Determine whether two field elements are equal, without constant-time guarantee. + * + * Identical in behavior to secp256k1_fe_equal, but not constant time in either a or b. + */ static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b); -/** Compare two field elements. Requires both inputs to be normalized */ +/** Compare the values represented by 2 field elements, without constant-time guarantee. + * + * On input, a and b must be valid normalized field elements. + * Returns 1 if a > b, -1 if a < b, and 0 if a = b (comparisons are done as integers + * in range 0..p-1). + */ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b); -/** Set a field element equal to 32-byte big endian value. If successful, the resulting field element is normalized. */ -static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a); +/** Set a field element equal to a provided 32-byte big endian value, reducing it. + * + * On input, r does not need to be initialized. a must be a pointer to an initialized 32-byte array. + * On output, r = a (mod p). It will have magnitude 1, and not be normalized. + */ +static void secp256k1_fe_set_b32_mod(secp256k1_fe *r, const unsigned char *a); -/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +/** Set a field element equal to a provided 32-byte big endian value, checking for overflow. + * + * On input, r does not need to be initialized. a must be a pointer to an initialized 32-byte array. + * On output, r = a if (a < p), it will be normalized with magnitude 1, and 1 is returned. + * If a >= p, 0 is returned, and r will be made invalid (and must not be used without overwriting). + */ +static int secp256k1_fe_set_b32_limit(secp256k1_fe *r, const unsigned char *a); + +/** Convert a field element to 32-byte big endian byte array. + * On input, a must be a valid normalized field element, and r a pointer to a 32-byte array. + * On output, r = a (mod p). + */ static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a); -/** Set a field element equal to the additive inverse of another. Takes a maximum magnitude of the input - * as an argument. The magnitude of the output is one higher. */ -static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m); +/** Negate a field element. + * + * On input, r does not need to be initialized. a must be a valid field element with + * magnitude not exceeding m. m must be an integer constant expression in [0,31]. + * Performs {r = -a}. + * On output, r will not be normalized, and will have magnitude m+1. + */ +#define secp256k1_fe_negate(r, a, m) ASSERT_INT_CONST_AND_DO(m, secp256k1_fe_negate_unchecked(r, a, m)) + +/** Like secp256k1_fe_negate_unchecked but m is not checked to be an integer constant expression. + * + * Should not be called directly outside of tests. + */ +static void secp256k1_fe_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m); -/** Adds a small integer (up to 0x7FFF) to r. The resulting magnitude increases by one. */ +/** Add a small integer to a field element. + * + * Performs {r += a}. The magnitude of r increases by 1, and normalized is cleared. + * a must be in range [0,0x7FFF]. + */ static void secp256k1_fe_add_int(secp256k1_fe *r, int a); -/** Multiplies the passed field element with a small integer constant. Multiplies the magnitude by that - * small integer. */ -static void secp256k1_fe_mul_int(secp256k1_fe *r, int a); +/** Multiply a field element with a small integer. + * + * On input, r must be a valid field element. a must be an integer constant expression in [0,32]. + * The magnitude of r times a must not exceed 32. + * Performs {r *= a}. + * On output, r's magnitude is multiplied by a, and r will not be normalized. + */ +#define secp256k1_fe_mul_int(r, a) ASSERT_INT_CONST_AND_DO(a, secp256k1_fe_mul_int_unchecked(r, a)) + +/** Like secp256k1_fe_mul_int but a is not checked to be an integer constant expression. + * + * Should not be called directly outside of tests. + */ +static void secp256k1_fe_mul_int_unchecked(secp256k1_fe *r, int a); -/** Adds a field element to another. The result has the sum of the inputs' magnitudes as magnitude. */ +/** Increment a field element by another. + * + * On input, r and a must be valid field elements, not necessarily normalized. + * The sum of their magnitudes must not exceed 32. + * Performs {r += a}. + * On output, r will not be normalized, and will have magnitude incremented by a's. + */ static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a); -/** Sets a field element to be the product of two others. Requires the inputs' magnitudes to be at most 8. - * The output magnitude is 1 (but not guaranteed to be normalized). */ +/** Multiply two field elements. + * + * On input, a and b must be valid field elements; r does not need to be initialized. + * r and a may point to the same object, but neither can be equal to b. The magnitudes + * of a and b must not exceed 8. + * Performs {r = a * b} + * On output, r will have magnitude 1, but won't be normalized. + */ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b); -/** Sets a field element to be the square of another. Requires the input's magnitude to be at most 8. - * The output magnitude is 1 (but not guaranteed to be normalized). */ +/** Square a field element. + * + * On input, a must be a valid field element; r does not need to be initialized. The magnitude + * of a must not exceed 8. + * Performs {r = a**2} + * On output, r will have magnitude 1, but won't be normalized. + */ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a); -/** If a has a square root, it is computed in r and 1 is returned. If a does not - * have a square root, the root of its negation is computed and 0 is returned. - * The input's magnitude can be at most 8. The output magnitude is 1 (but not - * guaranteed to be normalized). The result in r will always be a square - * itself. */ -static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a); +/** Compute a square root of a field element. + * + * On input, a must be a valid field element with magnitude<=8; r need not be initialized. + * If sqrt(a) exists, performs {r = sqrt(a)} and returns 1. + * Otherwise, sqrt(-a) exists. The function performs {r = sqrt(-a)} and returns 0. + * The resulting value represented by r will be a square itself. + * Variables r and a must not point to the same object. + * On output, r will have magnitude 1 but will not be normalized. + */ +static int secp256k1_fe_sqrt(secp256k1_fe * SECP256K1_RESTRICT r, const secp256k1_fe * SECP256K1_RESTRICT a); -/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be - * at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */ +/** Compute the modular inverse of a field element. + * + * On input, a must be a valid field element; r need not be initialized. + * Performs {r = a**(p-2)} (which maps 0 to 0, and every other element to its + * inverse). + * On output, r will have magnitude (a.magnitude != 0) and be normalized. + */ static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a); -/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */ +/** Compute the modular inverse of a field element, without constant-time guarantee. + * + * Behaves identically to secp256k1_fe_inv, but is not constant-time in a. + */ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a); -/** Convert a field element to the storage type. */ +/** Convert a field element to secp256k1_fe_storage. + * + * On input, a must be a valid normalized field element. + * Performs {r = a}. + */ static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); -/** Convert a field element back from the storage type. */ +/** Convert a field element back from secp256k1_fe_storage. + * + * On input, r need not be initialized. + * Performs {r = a}. + * On output, r will be normalized and will have magnitude 1. + */ static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a); /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag); -/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ +/** Conditionally move a field element in constant time. + * + * On input, both r and a must be valid field elements. Flag must be 0 or 1. + * Performs {r = flag ? a : r}. + * + * On output, r's magnitude will be the maximum of both input magnitudes. + * It will be normalized if and only if both inputs were normalized. + */ static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); -/** Halves the value of a field element modulo the field prime. Constant-time. - * For an input magnitude 'm', the output magnitude is set to 'floor(m/2) + 1'. - * The output is not guaranteed to be normalized, regardless of the input. */ +/** Halve the value of a field element modulo the field prime in constant-time. + * + * On input, r must be a valid field element. + * On output, r will be normalized and have magnitude floor(m/2) + 1 where m is + * the magnitude of r on input. + */ static void secp256k1_fe_half(secp256k1_fe *r); -/** Sets each limb of 'r' to its upper bound at magnitude 'm'. The output will also have its - * magnitude set to 'm' and is normalized if (and only if) 'm' is zero. */ +/** Sets r to a field element with magnitude m, normalized if (and only if) m==0. + * The value is chosen so that it is likely to trigger edge cases related to + * internal overflows. */ static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m); -/** Determine whether a is a square (modulo p). */ +/** Determine whether a is a square (modulo p). + * + * On input, a must be a valid field element. + */ static int secp256k1_fe_is_square_var(const secp256k1_fe *a); +/** Check invariants on a field element (no-op unless VERIFY is enabled). */ +static void secp256k1_fe_verify(const secp256k1_fe *a); + #endif /* SECP256K1_FIELD_H */ diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h index 9eb65607f1..203c10167c 100644 --- a/src/secp256k1/src/field_10x26.h +++ b/src/secp256k1/src/field_10x26.h @@ -9,15 +9,28 @@ #include <stdint.h> +/** This field implementation represents the value as 10 uint32_t limbs in base + * 2^26. */ typedef struct { - /* X = sum(i=0..9, n[i]*2^(i*26)) mod p - * where p = 2^256 - 0x1000003D1 - */ + /* A field element f represents the sum(i=0..9, f.n[i] << (i*26)) mod p, + * where p is the field modulus, 2^256 - 2^32 - 977. + * + * The individual limbs f.n[i] can exceed 2^26; the field's magnitude roughly + * corresponds to how much excess is allowed. The value + * sum(i=0..9, f.n[i] << (i*26)) may exceed p, unless the field element is + * normalized. */ uint32_t n[10]; -#ifdef VERIFY - int magnitude; - int normalized; -#endif + /* + * Magnitude m requires: + * n[i] <= 2 * m * (2^26 - 1) for i=0..8 + * n[9] <= 2 * m * (2^22 - 1) + * + * Normalized requires: + * n[i] <= (2^26 - 1) for i=0..8 + * sum(i=0..9, n[i] << (i*26)) < p + * (together these imply n[9] <= 2^22 - 1) + */ + SECP256K1_FE_VERIFY_FIELDS } secp256k1_fe; /* Unpacks a constant into a overlapping multi-limbed FE element. */ @@ -34,12 +47,6 @@ typedef struct { (((uint32_t)d7) >> 10) \ } -#ifdef VERIFY -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} -#else -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} -#endif - typedef struct { uint32_t n[8]; } secp256k1_fe_storage; diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h index 3b7f4d2480..8445db1639 100644 --- a/src/secp256k1/src/field_10x26_impl.h +++ b/src/secp256k1/src/field_10x26_impl.h @@ -12,47 +12,32 @@ #include "field.h" #include "modinv32_impl.h" -/** See the comment at the top of field_5x52_impl.h for more details. - * - * Here, we represent field elements as 10 uint32_t's in base 2^26, least significant first, - * where limbs can contain >26 bits. - * A magnitude M means: - * - 2*M*(2^22-1) is the max (inclusive) of the most significant limb - * - 2*M*(2^26-1) is the max (inclusive) of the remaining limbs - */ - #ifdef VERIFY -static void secp256k1_fe_verify(const secp256k1_fe *a) { +static void secp256k1_fe_impl_verify(const secp256k1_fe *a) { const uint32_t *d = a->n; - int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; - r &= (d[0] <= 0x3FFFFFFUL * m); - r &= (d[1] <= 0x3FFFFFFUL * m); - r &= (d[2] <= 0x3FFFFFFUL * m); - r &= (d[3] <= 0x3FFFFFFUL * m); - r &= (d[4] <= 0x3FFFFFFUL * m); - r &= (d[5] <= 0x3FFFFFFUL * m); - r &= (d[6] <= 0x3FFFFFFUL * m); - r &= (d[7] <= 0x3FFFFFFUL * m); - r &= (d[8] <= 0x3FFFFFFUL * m); - r &= (d[9] <= 0x03FFFFFUL * m); - r &= (a->magnitude >= 0); - r &= (a->magnitude <= 32); + int m = a->normalized ? 1 : 2 * a->magnitude; + VERIFY_CHECK(d[0] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[1] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[2] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[3] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[4] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[5] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[6] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[7] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[8] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[9] <= 0x03FFFFFUL * m); if (a->normalized) { - r &= (a->magnitude <= 1); - if (r && (d[9] == 0x03FFFFFUL)) { + if (d[9] == 0x03FFFFFUL) { uint32_t mid = d[8] & d[7] & d[6] & d[5] & d[4] & d[3] & d[2]; if (mid == 0x3FFFFFFUL) { - r &= ((d[1] + 0x40UL + ((d[0] + 0x3D1UL) >> 26)) <= 0x3FFFFFFUL); + VERIFY_CHECK((d[1] + 0x40UL + ((d[0] + 0x3D1UL) >> 26)) <= 0x3FFFFFFUL); } } } - VERIFY_CHECK(r == 1); } #endif -static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m) { - VERIFY_CHECK(m >= 0); - VERIFY_CHECK(m <= 2048); +static void secp256k1_fe_impl_get_bounds(secp256k1_fe *r, int m) { r->n[0] = 0x3FFFFFFUL * 2 * m; r->n[1] = 0x3FFFFFFUL * 2 * m; r->n[2] = 0x3FFFFFFUL * 2 * m; @@ -63,14 +48,9 @@ static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m) { r->n[7] = 0x3FFFFFFUL * 2 * m; r->n[8] = 0x3FFFFFFUL * 2 * m; r->n[9] = 0x03FFFFFUL * 2 * m; -#ifdef VERIFY - r->magnitude = m; - r->normalized = (m == 0); - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize(secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; @@ -117,15 +97,9 @@ static void secp256k1_fe_normalize(secp256k1_fe *r) { r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize_weak(secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; @@ -149,14 +123,9 @@ static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; - -#ifdef VERIFY - r->magnitude = 1; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize_var(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize_var(secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; @@ -204,15 +173,9 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) { r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) { +static int secp256k1_fe_impl_normalizes_to_zero(const secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; @@ -241,7 +204,7 @@ static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) { return (z0 == 0) | (z1 == 0x3FFFFFFUL); } -static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) { +static int secp256k1_fe_impl_normalizes_to_zero_var(const secp256k1_fe *r) { uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; uint32_t z0, z1; uint32_t x; @@ -293,53 +256,29 @@ static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) { return (z0 == 0) | (z1 == 0x3FFFFFFUL); } -SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { - VERIFY_CHECK(0 <= a && a <= 0x7FFF); +SECP256K1_INLINE static void secp256k1_fe_impl_set_int(secp256k1_fe *r, int a) { r->n[0] = a; r->n[1] = r->n[2] = r->n[3] = r->n[4] = r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; -#ifdef VERIFY - r->magnitude = (a != 0); - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { +SECP256K1_INLINE static int secp256k1_fe_impl_is_zero(const secp256k1_fe *a) { const uint32_t *t = a->n; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif return (t[0] | t[1] | t[2] | t[3] | t[4] | t[5] | t[6] | t[7] | t[8] | t[9]) == 0; } -SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static int secp256k1_fe_impl_is_odd(const secp256k1_fe *a) { return a->n[0] & 1; } -SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { +SECP256K1_INLINE static void secp256k1_fe_impl_clear(secp256k1_fe *a) { int i; -#ifdef VERIFY - a->magnitude = 0; - a->normalized = 1; -#endif for (i=0; i<10; i++) { a->n[i] = 0; } } -static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { +static int secp256k1_fe_impl_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { int i; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - VERIFY_CHECK(b->normalized); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); -#endif for (i = 9; i >= 0; i--) { if (a->n[i] > b->n[i]) { return 1; @@ -351,8 +290,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { return 0; } -static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { - int ret; +static void secp256k1_fe_impl_set_b32_mod(secp256k1_fe *r, const unsigned char *a) { r->n[0] = (uint32_t)a[31] | ((uint32_t)a[30] << 8) | ((uint32_t)a[29] << 16) | ((uint32_t)(a[28] & 0x3) << 24); r->n[1] = (uint32_t)((a[28] >> 2) & 0x3f) | ((uint32_t)a[27] << 6) | ((uint32_t)a[26] << 14) | ((uint32_t)(a[25] & 0xf) << 22); r->n[2] = (uint32_t)((a[25] >> 4) & 0xf) | ((uint32_t)a[24] << 4) | ((uint32_t)a[23] << 12) | ((uint32_t)(a[22] & 0x3f) << 20); @@ -363,26 +301,15 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { r->n[7] = (uint32_t)((a[9] >> 6) & 0x3) | ((uint32_t)a[8] << 2) | ((uint32_t)a[7] << 10) | ((uint32_t)a[6] << 18); r->n[8] = (uint32_t)a[5] | ((uint32_t)a[4] << 8) | ((uint32_t)a[3] << 16) | ((uint32_t)(a[2] & 0x3) << 24); r->n[9] = (uint32_t)((a[2] >> 2) & 0x3f) | ((uint32_t)a[1] << 6) | ((uint32_t)a[0] << 14); +} - ret = !((r->n[9] == 0x3FFFFFUL) & ((r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL) & ((r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); -#ifdef VERIFY - r->magnitude = 1; - if (ret) { - r->normalized = 1; - secp256k1_fe_verify(r); - } else { - r->normalized = 0; - } -#endif - return ret; +static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char *a) { + secp256k1_fe_impl_set_b32_mod(r, a); + return !((r->n[9] == 0x3FFFFFUL) & ((r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL) & ((r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); } /** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ -static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif +static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) { r[0] = (a->n[9] >> 14) & 0xff; r[1] = (a->n[9] >> 6) & 0xff; r[2] = ((a->n[9] & 0x3F) << 2) | ((a->n[8] >> 24) & 0x3); @@ -417,15 +344,15 @@ static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { r[31] = a->n[0] & 0xff; } -SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= m); - secp256k1_fe_verify(a); +SECP256K1_INLINE static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) { + /* For all legal values of m (0..31), the following properties hold: */ VERIFY_CHECK(0x3FFFC2FUL * 2 * (m + 1) >= 0x3FFFFFFUL * 2 * m); VERIFY_CHECK(0x3FFFFBFUL * 2 * (m + 1) >= 0x3FFFFFFUL * 2 * m); VERIFY_CHECK(0x3FFFFFFUL * 2 * (m + 1) >= 0x3FFFFFFUL * 2 * m); VERIFY_CHECK(0x03FFFFFUL * 2 * (m + 1) >= 0x03FFFFFUL * 2 * m); -#endif + + /* Due to the properties above, the left hand in the subtractions below is never less than + * the right hand. */ r->n[0] = 0x3FFFC2FUL * 2 * (m + 1) - a->n[0]; r->n[1] = 0x3FFFFBFUL * 2 * (m + 1) - a->n[1]; r->n[2] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[2]; @@ -436,14 +363,9 @@ SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k r->n[7] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[7]; r->n[8] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[8]; r->n[9] = 0x03FFFFFUL * 2 * (m + 1) - a->n[9]; -#ifdef VERIFY - r->magnitude = m + 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_impl_mul_int_unchecked(secp256k1_fe *r, int a) { r->n[0] *= a; r->n[1] *= a; r->n[2] *= a; @@ -454,17 +376,9 @@ SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { r->n[7] *= a; r->n[8] *= a; r->n[9] *= a; -#ifdef VERIFY - r->magnitude *= a; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_add(secp256k1_fe *r, const secp256k1_fe *a) { r->n[0] += a->n[0]; r->n[1] += a->n[1]; r->n[2] += a->n[2]; @@ -475,25 +389,10 @@ SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_f r->n[7] += a->n[7]; r->n[8] += a->n[8]; r->n[9] += a->n[9]; -#ifdef VERIFY - r->magnitude += a->magnitude; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_add_int(secp256k1_fe *r, int a) { -#ifdef VERIFY - secp256k1_fe_verify(r); - VERIFY_CHECK(a >= 0); - VERIFY_CHECK(a <= 0x7FFF); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_add_int(secp256k1_fe *r, int a) { r->n[0] += a; -#ifdef VERIFY - r->magnitude += 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } #if defined(USE_EXTERNAL_ASM) @@ -1115,37 +1014,15 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t } #endif -static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - VERIFY_CHECK(b->magnitude <= 8); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); - VERIFY_CHECK(r != b); - VERIFY_CHECK(a != b); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { secp256k1_fe_mul_inner(r->n, a->n, b->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_sqr(secp256k1_fe *r, const secp256k1_fe *a) { secp256k1_fe_sqr_inner(r->n, a->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { +SECP256K1_INLINE static void secp256k1_fe_impl_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint32_t mask0, mask1; volatile int vflag = flag; SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); @@ -1161,25 +1038,14 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_ r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1); r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1); -#ifdef VERIFY - if (flag) { - r->magnitude = a->magnitude; - r->normalized = a->normalized; - } -#endif } -static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { +static SECP256K1_INLINE void secp256k1_fe_impl_half(secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; uint32_t one = (uint32_t)1; uint32_t mask = -(t0 & one) >> 6; -#ifdef VERIFY - secp256k1_fe_verify(r); - VERIFY_CHECK(r->magnitude < 32); -#endif - /* Bounds analysis (over the rationals). * * Let m = r->magnitude @@ -1226,10 +1092,8 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { * * Current bounds: t0..t8 <= C * (m/2 + 1/2) * t9 <= D * (m/2 + 1/4) - */ - -#ifdef VERIFY - /* Therefore the output magnitude (M) has to be set such that: + * + * Therefore the output magnitude (M) has to be set such that: * t0..t8: C * M >= C * (m/2 + 1/2) * t9: D * M >= D * (m/2 + 1/4) * @@ -1239,10 +1103,6 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { * and since we want the smallest such integer value for M: * M == floor(m/2) + 1 */ - r->magnitude = (r->magnitude >> 1) + 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { @@ -1261,10 +1121,7 @@ static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); } -static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif +static void secp256k1_fe_impl_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { r->n[0] = a->n[0] | a->n[1] << 26; r->n[1] = a->n[1] >> 6 | a->n[2] << 20; r->n[2] = a->n[2] >> 12 | a->n[3] << 14; @@ -1275,7 +1132,7 @@ static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe r->n[7] = a->n[8] >> 16 | a->n[9] << 10; } -static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { +static SECP256K1_INLINE void secp256k1_fe_impl_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { r->n[0] = a->n[0] & 0x3FFFFFFUL; r->n[1] = a->n[0] >> 26 | ((a->n[1] << 6) & 0x3FFFFFFUL); r->n[2] = a->n[1] >> 20 | ((a->n[2] << 12) & 0x3FFFFFFUL); @@ -1286,11 +1143,6 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se r->n[7] = a->n[5] >> 22 | ((a->n[6] << 10) & 0x3FFFFFFUL); r->n[8] = a->n[6] >> 16 | ((a->n[7] << 16) & 0x3FFFFFFUL); r->n[9] = a->n[7] >> 10; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } static void secp256k1_fe_from_signed30(secp256k1_fe *r, const secp256k1_modinv32_signed30 *a) { @@ -1321,12 +1173,6 @@ static void secp256k1_fe_from_signed30(secp256k1_fe *r, const secp256k1_modinv32 r->n[7] = (a6 >> 2 ) & M26; r->n[8] = (a6 >> 28 | a7 << 2) & M26; r->n[9] = (a7 >> 24 | a8 << 6); - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } static void secp256k1_fe_to_signed30(secp256k1_modinv32_signed30 *r, const secp256k1_fe *a) { @@ -1334,10 +1180,6 @@ static void secp256k1_fe_to_signed30(secp256k1_modinv32_signed30 *r, const secp2 const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4], a5 = a->n[5], a6 = a->n[6], a7 = a->n[7], a8 = a->n[8], a9 = a->n[9]; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif - r->v[0] = (a0 | a1 << 26) & M30; r->v[1] = (a1 >> 4 | a2 << 22) & M30; r->v[2] = (a2 >> 8 | a3 << 18) & M30; @@ -1355,37 +1197,27 @@ static const secp256k1_modinv32_modinfo secp256k1_const_modinfo_fe = { 0x2DDACACFL }; -static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) { - secp256k1_fe tmp; +static void secp256k1_fe_impl_inv(secp256k1_fe *r, const secp256k1_fe *x) { + secp256k1_fe tmp = *x; secp256k1_modinv32_signed30 s; - tmp = *x; secp256k1_fe_normalize(&tmp); secp256k1_fe_to_signed30(&s, &tmp); secp256k1_modinv32(&s, &secp256k1_const_modinfo_fe); secp256k1_fe_from_signed30(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); -#endif } -static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { - secp256k1_fe tmp; +static void secp256k1_fe_impl_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { + secp256k1_fe tmp = *x; secp256k1_modinv32_signed30 s; - tmp = *x; secp256k1_fe_normalize_var(&tmp); secp256k1_fe_to_signed30(&s, &tmp); secp256k1_modinv32_var(&s, &secp256k1_const_modinfo_fe); secp256k1_fe_from_signed30(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); -#endif } -static int secp256k1_fe_is_square_var(const secp256k1_fe *x) { +static int secp256k1_fe_impl_is_square_var(const secp256k1_fe *x) { secp256k1_fe tmp; secp256k1_modinv32_signed30 s; int jac, ret; @@ -1403,10 +1235,6 @@ static int secp256k1_fe_is_square_var(const secp256k1_fe *x) { secp256k1_fe dummy; ret = secp256k1_fe_sqrt(&dummy, &tmp); } else { -#ifdef VERIFY - secp256k1_fe dummy; - VERIFY_CHECK(jac == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); -#endif ret = jac >= 0; } return ret; diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h index 50ee3f9ec9..f20c246fdd 100644 --- a/src/secp256k1/src/field_5x52.h +++ b/src/secp256k1/src/field_5x52.h @@ -9,15 +9,28 @@ #include <stdint.h> +/** This field implementation represents the value as 5 uint64_t limbs in base + * 2^52. */ typedef struct { - /* X = sum(i=0..4, n[i]*2^(i*52)) mod p - * where p = 2^256 - 0x1000003D1 - */ + /* A field element f represents the sum(i=0..4, f.n[i] << (i*52)) mod p, + * where p is the field modulus, 2^256 - 2^32 - 977. + * + * The individual limbs f.n[i] can exceed 2^52; the field's magnitude roughly + * corresponds to how much excess is allowed. The value + * sum(i=0..4, f.n[i] << (i*52)) may exceed p, unless the field element is + * normalized. */ uint64_t n[5]; -#ifdef VERIFY - int magnitude; - int normalized; -#endif + /* + * Magnitude m requires: + * n[i] <= 2 * m * (2^52 - 1) for i=0..3 + * n[4] <= 2 * m * (2^48 - 1) + * + * Normalized requires: + * n[i] <= (2^52 - 1) for i=0..3 + * sum(i=0..4, n[i] << (i*52)) < p + * (together these imply n[4] <= 2^48 - 1) + */ + SECP256K1_FE_VERIFY_FIELDS } secp256k1_fe; /* Unpacks a constant into a overlapping multi-limbed FE element. */ @@ -29,12 +42,6 @@ typedef struct { ((uint64_t)(d6) >> 16) | (((uint64_t)(d7)) << 16) \ } -#ifdef VERIFY -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} -#else -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} -#endif - typedef struct { uint64_t n[4]; } secp256k1_fe_storage; diff --git a/src/secp256k1/src/field_5x52_asm_impl.h b/src/secp256k1/src/field_5x52_asm_impl.h index a2118044ab..04a9af2105 100644 --- a/src/secp256k1/src/field_5x52_asm_impl.h +++ b/src/secp256k1/src/field_5x52_asm_impl.h @@ -14,6 +14,8 @@ #ifndef SECP256K1_FIELD_INNER5X52_IMPL_H #define SECP256K1_FIELD_INNER5X52_IMPL_H +#include "util.h" + SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { /** * Registers: rdx:rax = multiplication accumulator @@ -278,7 +280,7 @@ __asm__ __volatile__( "addq %%rsi,%%r8\n" /* r[4] = c */ "movq %%r8,32(%%rdi)\n" -: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "+S"(a), "=&m"(tmp1), "=&m"(tmp2), "=&m"(tmp3) : "b"(b), "D"(r) : "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" ); @@ -493,7 +495,7 @@ __asm__ __volatile__( "addq %%rsi,%%r8\n" /* r[4] = c */ "movq %%r8,32(%%rdi)\n" -: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "+S"(a), "=&m"(tmp1), "=&m"(tmp2), "=&m"(tmp3) : "D"(r) : "%rax", "%rbx", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" ); diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h index 6b97157d0f..ecb70502c2 100644 --- a/src/secp256k1/src/field_5x52_impl.h +++ b/src/secp256k1/src/field_5x52_impl.h @@ -18,59 +18,33 @@ #include "field_5x52_int128_impl.h" #endif -/** Implements arithmetic modulo FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F, - * represented as 5 uint64_t's in base 2^52, least significant first. Note that the limbs are allowed to - * contain >52 bits each. - * - * Each field element has a 'magnitude' associated with it. Internally, a magnitude M means: - * - 2*M*(2^48-1) is the max (inclusive) of the most significant limb - * - 2*M*(2^52-1) is the max (inclusive) of the remaining limbs - * - * Operations have different rules for propagating magnitude to their outputs. If an operation takes a - * magnitude M as a parameter, that means the magnitude of input field elements can be at most M (inclusive). - * - * Each field element also has a 'normalized' flag. A field element is normalized if its magnitude is either - * 0 or 1, and its value is already reduced modulo the order of the field. - */ - #ifdef VERIFY -static void secp256k1_fe_verify(const secp256k1_fe *a) { +static void secp256k1_fe_impl_verify(const secp256k1_fe *a) { const uint64_t *d = a->n; - int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; + int m = a->normalized ? 1 : 2 * a->magnitude; /* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ - r &= (d[0] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[1] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[2] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[3] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[4] <= 0x0FFFFFFFFFFFFULL * m); - r &= (a->magnitude >= 0); - r &= (a->magnitude <= 2048); + VERIFY_CHECK(d[0] <= 0xFFFFFFFFFFFFFULL * m); + VERIFY_CHECK(d[1] <= 0xFFFFFFFFFFFFFULL * m); + VERIFY_CHECK(d[2] <= 0xFFFFFFFFFFFFFULL * m); + VERIFY_CHECK(d[3] <= 0xFFFFFFFFFFFFFULL * m); + VERIFY_CHECK(d[4] <= 0x0FFFFFFFFFFFFULL * m); if (a->normalized) { - r &= (a->magnitude <= 1); - if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { - r &= (d[0] < 0xFFFFEFFFFFC2FULL); + if ((d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { + VERIFY_CHECK(d[0] < 0xFFFFEFFFFFC2FULL); } } - VERIFY_CHECK(r == 1); } #endif -static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m) { - VERIFY_CHECK(m >= 0); - VERIFY_CHECK(m <= 2048); +static void secp256k1_fe_impl_get_bounds(secp256k1_fe *r, int m) { r->n[0] = 0xFFFFFFFFFFFFFULL * 2 * m; r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * m; r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * m; r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * m; r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * m; -#ifdef VERIFY - r->magnitude = m; - r->normalized = (m == 0); - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize(secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; /* Reduce t4 at the start so there will be at most a single carry from the first pass */ @@ -105,15 +79,9 @@ static void secp256k1_fe_normalize(secp256k1_fe *r) { t4 &= 0x0FFFFFFFFFFFFULL; r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize_weak(secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; /* Reduce t4 at the start so there will be at most a single carry from the first pass */ @@ -130,14 +98,9 @@ static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { VERIFY_CHECK(t4 >> 49 == 0); r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - -#ifdef VERIFY - r->magnitude = 1; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize_var(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize_var(secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; /* Reduce t4 at the start so there will be at most a single carry from the first pass */ @@ -173,15 +136,9 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) { } r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) { +static int secp256k1_fe_impl_normalizes_to_zero(const secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ @@ -204,7 +161,7 @@ static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) { return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); } -static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) { +static int secp256k1_fe_impl_normalizes_to_zero_var(const secp256k1_fe *r) { uint64_t t0, t1, t2, t3, t4; uint64_t z0, z1; uint64_t x; @@ -245,53 +202,29 @@ static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) { return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); } -SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { - VERIFY_CHECK(0 <= a && a <= 0x7FFF); +SECP256K1_INLINE static void secp256k1_fe_impl_set_int(secp256k1_fe *r, int a) { r->n[0] = a; r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; -#ifdef VERIFY - r->magnitude = (a != 0); - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { +SECP256K1_INLINE static int secp256k1_fe_impl_is_zero(const secp256k1_fe *a) { const uint64_t *t = a->n; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif return (t[0] | t[1] | t[2] | t[3] | t[4]) == 0; } -SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static int secp256k1_fe_impl_is_odd(const secp256k1_fe *a) { return a->n[0] & 1; } -SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { +SECP256K1_INLINE static void secp256k1_fe_impl_clear(secp256k1_fe *a) { int i; -#ifdef VERIFY - a->magnitude = 0; - a->normalized = 1; -#endif for (i=0; i<5; i++) { a->n[i] = 0; } } -static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { +static int secp256k1_fe_impl_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { int i; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - VERIFY_CHECK(b->normalized); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); -#endif for (i = 4; i >= 0; i--) { if (a->n[i] > b->n[i]) { return 1; @@ -303,8 +236,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { return 0; } -static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { - int ret; +static void secp256k1_fe_impl_set_b32_mod(secp256k1_fe *r, const unsigned char *a) { r->n[0] = (uint64_t)a[31] | ((uint64_t)a[30] << 8) | ((uint64_t)a[29] << 16) @@ -339,25 +271,15 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { | ((uint64_t)a[2] << 24) | ((uint64_t)a[1] << 32) | ((uint64_t)a[0] << 40); - ret = !((r->n[4] == 0x0FFFFFFFFFFFFULL) & ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL) & (r->n[0] >= 0xFFFFEFFFFFC2FULL)); -#ifdef VERIFY - r->magnitude = 1; - if (ret) { - r->normalized = 1; - secp256k1_fe_verify(r); - } else { - r->normalized = 0; - } -#endif - return ret; +} + +static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char *a) { + secp256k1_fe_impl_set_b32_mod(r, a); + return !((r->n[4] == 0x0FFFFFFFFFFFFULL) & ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL) & (r->n[0] >= 0xFFFFEFFFFFC2FULL)); } /** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ -static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif +static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) { r[0] = (a->n[4] >> 40) & 0xFF; r[1] = (a->n[4] >> 32) & 0xFF; r[2] = (a->n[4] >> 24) & 0xFF; @@ -392,100 +314,50 @@ static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { r[31] = a->n[0] & 0xFF; } -SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= m); - secp256k1_fe_verify(a); +SECP256K1_INLINE static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) { + /* For all legal values of m (0..31), the following properties hold: */ VERIFY_CHECK(0xFFFFEFFFFFC2FULL * 2 * (m + 1) >= 0xFFFFFFFFFFFFFULL * 2 * m); VERIFY_CHECK(0xFFFFFFFFFFFFFULL * 2 * (m + 1) >= 0xFFFFFFFFFFFFFULL * 2 * m); VERIFY_CHECK(0x0FFFFFFFFFFFFULL * 2 * (m + 1) >= 0x0FFFFFFFFFFFFULL * 2 * m); -#endif + + /* Due to the properties above, the left hand in the subtractions below is never less than + * the right hand. */ r->n[0] = 0xFFFFEFFFFFC2FULL * 2 * (m + 1) - a->n[0]; r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[1]; r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[2]; r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[3]; r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * (m + 1) - a->n[4]; -#ifdef VERIFY - r->magnitude = m + 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_impl_mul_int_unchecked(secp256k1_fe *r, int a) { r->n[0] *= a; r->n[1] *= a; r->n[2] *= a; r->n[3] *= a; r->n[4] *= a; -#ifdef VERIFY - r->magnitude *= a; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_add_int(secp256k1_fe *r, int a) { -#ifdef VERIFY - secp256k1_fe_verify(r); - VERIFY_CHECK(a >= 0); - VERIFY_CHECK(a <= 0x7FFF); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_add_int(secp256k1_fe *r, int a) { r->n[0] += a; -#ifdef VERIFY - r->magnitude += 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_add(secp256k1_fe *r, const secp256k1_fe *a) { r->n[0] += a->n[0]; r->n[1] += a->n[1]; r->n[2] += a->n[2]; r->n[3] += a->n[3]; r->n[4] += a->n[4]; -#ifdef VERIFY - r->magnitude += a->magnitude; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - VERIFY_CHECK(b->magnitude <= 8); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); - VERIFY_CHECK(r != b); - VERIFY_CHECK(a != b); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { secp256k1_fe_mul_inner(r->n, a->n, b->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_sqr(secp256k1_fe *r, const secp256k1_fe *a) { secp256k1_fe_sqr_inner(r->n, a->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { +SECP256K1_INLINE static void secp256k1_fe_impl_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint64_t mask0, mask1; volatile int vflag = flag; SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); @@ -496,24 +368,13 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_ r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); -#ifdef VERIFY - if (flag) { - r->magnitude = a->magnitude; - r->normalized = a->normalized; - } -#endif } -static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { +static SECP256K1_INLINE void secp256k1_fe_impl_half(secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; uint64_t one = (uint64_t)1; uint64_t mask = -(t0 & one) >> 12; -#ifdef VERIFY - secp256k1_fe_verify(r); - VERIFY_CHECK(r->magnitude < 32); -#endif - /* Bounds analysis (over the rationals). * * Let m = r->magnitude @@ -550,10 +411,8 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { * * Current bounds: t0..t3 <= C * (m/2 + 1/2) * t4 <= D * (m/2 + 1/4) - */ - -#ifdef VERIFY - /* Therefore the output magnitude (M) has to be set such that: + * + * Therefore the output magnitude (M) has to be set such that: * t0..t3: C * M >= C * (m/2 + 1/2) * t4: D * M >= D * (m/2 + 1/4) * @@ -563,10 +422,6 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { * and since we want the smallest such integer value for M: * M == floor(m/2) + 1 */ - r->magnitude = (r->magnitude >> 1) + 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { @@ -581,27 +436,19 @@ static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); } -static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif +static void secp256k1_fe_impl_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { r->n[0] = a->n[0] | a->n[1] << 52; r->n[1] = a->n[1] >> 12 | a->n[2] << 40; r->n[2] = a->n[2] >> 24 | a->n[3] << 28; r->n[3] = a->n[3] >> 36 | a->n[4] << 16; } -static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { +static SECP256K1_INLINE void secp256k1_fe_impl_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { r->n[0] = a->n[0] & 0xFFFFFFFFFFFFFULL; r->n[1] = a->n[0] >> 52 | ((a->n[1] << 12) & 0xFFFFFFFFFFFFFULL); r->n[2] = a->n[1] >> 40 | ((a->n[2] << 24) & 0xFFFFFFFFFFFFFULL); r->n[3] = a->n[2] >> 28 | ((a->n[3] << 36) & 0xFFFFFFFFFFFFFULL); r->n[4] = a->n[3] >> 16; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } static void secp256k1_fe_from_signed62(secp256k1_fe *r, const secp256k1_modinv64_signed62 *a) { @@ -622,22 +469,12 @@ static void secp256k1_fe_from_signed62(secp256k1_fe *r, const secp256k1_modinv64 r->n[2] = (a1 >> 42 | a2 << 20) & M52; r->n[3] = (a2 >> 32 | a3 << 30) & M52; r->n[4] = (a3 >> 22 | a4 << 40); - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } static void secp256k1_fe_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_fe *a) { const uint64_t M62 = UINT64_MAX >> 2; const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4]; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif - r->v[0] = (a0 | a1 << 52) & M62; r->v[1] = (a1 >> 10 | a2 << 42) & M62; r->v[2] = (a2 >> 20 | a3 << 32) & M62; @@ -650,37 +487,27 @@ static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_fe = { 0x27C7F6E22DDACACFLL }; -static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) { - secp256k1_fe tmp; +static void secp256k1_fe_impl_inv(secp256k1_fe *r, const secp256k1_fe *x) { + secp256k1_fe tmp = *x; secp256k1_modinv64_signed62 s; - tmp = *x; secp256k1_fe_normalize(&tmp); secp256k1_fe_to_signed62(&s, &tmp); secp256k1_modinv64(&s, &secp256k1_const_modinfo_fe); secp256k1_fe_from_signed62(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); -#endif } -static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { - secp256k1_fe tmp; +static void secp256k1_fe_impl_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { + secp256k1_fe tmp = *x; secp256k1_modinv64_signed62 s; - tmp = *x; secp256k1_fe_normalize_var(&tmp); secp256k1_fe_to_signed62(&s, &tmp); secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_fe); secp256k1_fe_from_signed62(r, &s); - -#ifdef VERIFY - VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); -#endif } -static int secp256k1_fe_is_square_var(const secp256k1_fe *x) { +static int secp256k1_fe_impl_is_square_var(const secp256k1_fe *x) { secp256k1_fe tmp; secp256k1_modinv64_signed62 s; int jac, ret; @@ -698,10 +525,6 @@ static int secp256k1_fe_is_square_var(const secp256k1_fe *x) { secp256k1_fe dummy; ret = secp256k1_fe_sqrt(&dummy, &tmp); } else { -#ifdef VERIFY - secp256k1_fe dummy; - VERIFY_CHECK(jac == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); -#endif ret = jac >= 0; } return ret; diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h index 18567b95f3..b2a391dec9 100644 --- a/src/secp256k1/src/field_5x52_int128_impl.h +++ b/src/secp256k1/src/field_5x52_int128_impl.h @@ -10,6 +10,7 @@ #include <stdint.h> #include "int128.h" +#include "util.h" #ifdef VERIFY #define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h index 0a03076bbc..7f18ebdc94 100644 --- a/src/secp256k1/src/field_impl.h +++ b/src/secp256k1/src/field_impl.h @@ -7,6 +7,7 @@ #ifndef SECP256K1_FIELD_IMPL_H #define SECP256K1_FIELD_IMPL_H +#include "field.h" #include "util.h" #if defined(SECP256K1_WIDEMUL_INT128) @@ -19,6 +20,12 @@ SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { secp256k1_fe na; +#ifdef VERIFY + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(a->magnitude <= 1); + VERIFY_CHECK(b->magnitude <= 31); +#endif secp256k1_fe_negate(&na, a, 1); secp256k1_fe_add(&na, b); return secp256k1_fe_normalizes_to_zero(&na); @@ -26,12 +33,18 @@ SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) { secp256k1_fe na; +#ifdef VERIFY + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(a->magnitude <= 1); + VERIFY_CHECK(b->magnitude <= 31); +#endif secp256k1_fe_negate(&na, a, 1); secp256k1_fe_add(&na, b); return secp256k1_fe_normalizes_to_zero_var(&na); } -static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { +static int secp256k1_fe_sqrt(secp256k1_fe * SECP256K1_RESTRICT r, const secp256k1_fe * SECP256K1_RESTRICT a) { /** Given that p is congruent to 3 mod 4, we can compute the square root of * a mod p as the (p+1)/4'th power of a. * @@ -42,9 +55,13 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { * itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)). */ secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; - int j; + int j, ret; +#ifdef VERIFY VERIFY_CHECK(r != a); + secp256k1_fe_verify(a); + VERIFY_CHECK(a->magnitude <= 8); +#endif /** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in * { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: @@ -128,7 +145,286 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { /* Check that a square root was actually calculated */ secp256k1_fe_sqr(&t1, r); - return secp256k1_fe_equal(&t1, a); + ret = secp256k1_fe_equal(&t1, a); + +#ifdef VERIFY + if (!ret) { + secp256k1_fe_negate(&t1, &t1, 1); + secp256k1_fe_normalize_var(&t1); + VERIFY_CHECK(secp256k1_fe_equal_var(&t1, a)); + } +#endif + return ret; +} + +#ifndef VERIFY +static void secp256k1_fe_verify(const secp256k1_fe *a) { (void)a; } +#else +static void secp256k1_fe_impl_verify(const secp256k1_fe *a); +static void secp256k1_fe_verify(const secp256k1_fe *a) { + /* Magnitude between 0 and 32. */ + VERIFY_CHECK((a->magnitude >= 0) && (a->magnitude <= 32)); + /* Normalized is 0 or 1. */ + VERIFY_CHECK((a->normalized == 0) || (a->normalized == 1)); + /* If normalized, magnitude must be 0 or 1. */ + if (a->normalized) VERIFY_CHECK(a->magnitude <= 1); + /* Invoke implementation-specific checks. */ + secp256k1_fe_impl_verify(a); +} + +static void secp256k1_fe_impl_normalize(secp256k1_fe *r); +SECP256K1_INLINE static void secp256k1_fe_normalize(secp256k1_fe *r) { + secp256k1_fe_verify(r); + secp256k1_fe_impl_normalize(r); + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_normalize_weak(secp256k1_fe *r); +SECP256K1_INLINE static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { + secp256k1_fe_verify(r); + secp256k1_fe_impl_normalize_weak(r); + r->magnitude = 1; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_normalize_var(secp256k1_fe *r); +SECP256K1_INLINE static void secp256k1_fe_normalize_var(secp256k1_fe *r) { + secp256k1_fe_verify(r); + secp256k1_fe_impl_normalize_var(r); + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +} + +static int secp256k1_fe_impl_normalizes_to_zero(const secp256k1_fe *r); +SECP256K1_INLINE static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) { + secp256k1_fe_verify(r); + return secp256k1_fe_impl_normalizes_to_zero(r); +} + +static int secp256k1_fe_impl_normalizes_to_zero_var(const secp256k1_fe *r); +SECP256K1_INLINE static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) { + secp256k1_fe_verify(r); + return secp256k1_fe_impl_normalizes_to_zero_var(r); +} + +static void secp256k1_fe_impl_set_int(secp256k1_fe *r, int a); +SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { + VERIFY_CHECK(0 <= a && a <= 0x7FFF); + secp256k1_fe_impl_set_int(r, a); + r->magnitude = (a != 0); + r->normalized = 1; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_add_int(secp256k1_fe *r, int a); +SECP256K1_INLINE static void secp256k1_fe_add_int(secp256k1_fe *r, int a) { + VERIFY_CHECK(0 <= a && a <= 0x7FFF); + secp256k1_fe_verify(r); + secp256k1_fe_impl_add_int(r, a); + r->magnitude += 1; + r->normalized = 0; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_clear(secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { + a->magnitude = 0; + a->normalized = 1; + secp256k1_fe_impl_clear(a); + secp256k1_fe_verify(a); +} + +static int secp256k1_fe_impl_is_zero(const secp256k1_fe *a); +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { + secp256k1_fe_verify(a); + VERIFY_CHECK(a->normalized); + return secp256k1_fe_impl_is_zero(a); +} + +static int secp256k1_fe_impl_is_odd(const secp256k1_fe *a); +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { + secp256k1_fe_verify(a); + VERIFY_CHECK(a->normalized); + return secp256k1_fe_impl_is_odd(a); +} + +static int secp256k1_fe_impl_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b); +SECP256K1_INLINE static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(a->normalized); + VERIFY_CHECK(b->normalized); + return secp256k1_fe_impl_cmp_var(a, b); +} + +static void secp256k1_fe_impl_set_b32_mod(secp256k1_fe *r, const unsigned char *a); +SECP256K1_INLINE static void secp256k1_fe_set_b32_mod(secp256k1_fe *r, const unsigned char *a) { + secp256k1_fe_impl_set_b32_mod(r, a); + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); } +static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char *a); +SECP256K1_INLINE static int secp256k1_fe_set_b32_limit(secp256k1_fe *r, const unsigned char *a) { + if (secp256k1_fe_impl_set_b32_limit(r, a)) { + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); + return 1; + } else { + /* Mark the output field element as invalid. */ + r->magnitude = -1; + return 0; + } +} + +static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { + secp256k1_fe_verify(a); + VERIFY_CHECK(a->normalized); + secp256k1_fe_impl_get_b32(r, a); +} + +static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m); +SECP256K1_INLINE static void secp256k1_fe_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) { + secp256k1_fe_verify(a); + VERIFY_CHECK(m >= 0 && m <= 31); + VERIFY_CHECK(a->magnitude <= m); + secp256k1_fe_impl_negate_unchecked(r, a, m); + r->magnitude = m + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_mul_int_unchecked(secp256k1_fe *r, int a); +SECP256K1_INLINE static void secp256k1_fe_mul_int_unchecked(secp256k1_fe *r, int a) { + secp256k1_fe_verify(r); + VERIFY_CHECK(a >= 0 && a <= 32); + VERIFY_CHECK(a*r->magnitude <= 32); + secp256k1_fe_impl_mul_int_unchecked(r, a); + r->magnitude *= a; + r->normalized = 0; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_add(secp256k1_fe *r, const secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { + secp256k1_fe_verify(r); + secp256k1_fe_verify(a); + VERIFY_CHECK(r->magnitude + a->magnitude <= 32); + secp256k1_fe_impl_add(r, a); + r->magnitude += a->magnitude; + r->normalized = 0; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b); +SECP256K1_INLINE static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(a->magnitude <= 8); + VERIFY_CHECK(b->magnitude <= 8); + VERIFY_CHECK(r != b); + VERIFY_CHECK(a != b); + secp256k1_fe_impl_mul(r, a, b); + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_sqr(secp256k1_fe *r, const secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { + secp256k1_fe_verify(a); + VERIFY_CHECK(a->magnitude <= 8); + secp256k1_fe_impl_sqr(r, a); + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); +SECP256K1_INLINE static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { + VERIFY_CHECK(flag == 0 || flag == 1); + secp256k1_fe_verify(a); + secp256k1_fe_verify(r); + secp256k1_fe_impl_cmov(r, a, flag); + if (a->magnitude > r->magnitude) r->magnitude = a->magnitude; + if (!a->normalized) r->normalized = 0; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { + secp256k1_fe_verify(a); + VERIFY_CHECK(a->normalized); + secp256k1_fe_impl_to_storage(r, a); +} + +static void secp256k1_fe_impl_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a); +SECP256K1_INLINE static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { + secp256k1_fe_impl_from_storage(r, a); + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_inv(secp256k1_fe *r, const secp256k1_fe *x); +SECP256K1_INLINE static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) { + int input_is_zero = secp256k1_fe_normalizes_to_zero(x); + secp256k1_fe_verify(x); + secp256k1_fe_impl_inv(r, x); + r->magnitude = x->magnitude > 0; + r->normalized = 1; + VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == input_is_zero); + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_inv_var(secp256k1_fe *r, const secp256k1_fe *x); +SECP256K1_INLINE static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { + int input_is_zero = secp256k1_fe_normalizes_to_zero(x); + secp256k1_fe_verify(x); + secp256k1_fe_impl_inv_var(r, x); + r->magnitude = x->magnitude > 0; + r->normalized = 1; + VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == input_is_zero); + secp256k1_fe_verify(r); +} + +static int secp256k1_fe_impl_is_square_var(const secp256k1_fe *x); +SECP256K1_INLINE static int secp256k1_fe_is_square_var(const secp256k1_fe *x) { + int ret; + secp256k1_fe tmp = *x, sqrt; + secp256k1_fe_verify(x); + ret = secp256k1_fe_impl_is_square_var(x); + secp256k1_fe_normalize_weak(&tmp); + VERIFY_CHECK(ret == secp256k1_fe_sqrt(&sqrt, &tmp)); + return ret; +} + +static void secp256k1_fe_impl_get_bounds(secp256k1_fe* r, int m); +SECP256K1_INLINE static void secp256k1_fe_get_bounds(secp256k1_fe* r, int m) { + VERIFY_CHECK(m >= 0); + VERIFY_CHECK(m <= 32); + secp256k1_fe_impl_get_bounds(r, m); + r->magnitude = m; + r->normalized = (m == 0); + secp256k1_fe_verify(r); +} + +static void secp256k1_fe_impl_half(secp256k1_fe *r); +SECP256K1_INLINE static void secp256k1_fe_half(secp256k1_fe *r) { + secp256k1_fe_verify(r); + VERIFY_CHECK(r->magnitude < 32); + secp256k1_fe_impl_half(r); + r->magnitude = (r->magnitude >> 1) + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +} + +#endif /* defined(VERIFY) */ + #endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h index b79ba597db..1cc6137161 100644 --- a/src/secp256k1/src/group.h +++ b/src/secp256k1/src/group.h @@ -51,6 +51,12 @@ static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const se * for Y. Return value indicates whether the result is valid. */ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); +/** Determine whether x is a valid X coordinate on the curve. */ +static int secp256k1_ge_x_on_curve_var(const secp256k1_fe *x); + +/** Determine whether fraction xn/xd is a valid X coordinate on the curve (xd != 0). */ +static int secp256k1_ge_x_frac_on_curve_var(const secp256k1_fe *xn, const secp256k1_fe *xd); + /** Check whether a group element is the point at infinity. */ static int secp256k1_ge_is_infinity(const secp256k1_ge *a); @@ -100,7 +106,8 @@ static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a); /** Check two group elements (jacobian) for equality in variable time. */ static int secp256k1_gej_eq_var(const secp256k1_gej *a, const secp256k1_gej *b); -/** Compare the X coordinate of a group element (jacobian). */ +/** Compare the X coordinate of a group element (jacobian). + * The magnitude of the group element's X coordinate must not exceed 31. */ static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a); /** Set r equal to the inverse of a (i.e., mirrored around the X axis) */ @@ -164,4 +171,10 @@ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); */ static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge); +/** Check invariants on an affine group element (no-op unless VERIFY is enabled). */ +static void secp256k1_ge_verify(const secp256k1_ge *a); + +/** Check invariants on a Jacobian group element (no-op unless VERIFY is enabled). */ +static void secp256k1_gej_verify(const secp256k1_gej *a); + #endif /* SECP256K1_GROUP_H */ diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h index 82ce3f8d8b..ffdfeaa10a 100644 --- a/src/secp256k1/src/group_impl.h +++ b/src/secp256k1/src/group_impl.h @@ -9,6 +9,7 @@ #include "field.h" #include "group.h" +#include "util.h" /* Begin of section generated by sage/gen_exhaustive_groups.sage. */ #define SECP256K1_G_ORDER_7 SECP256K1_GE_CONST(\ @@ -72,37 +73,80 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G; #endif /* End of section generated by sage/gen_exhaustive_groups.sage. */ -static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, SECP256K1_B); +static void secp256k1_ge_verify(const secp256k1_ge *a) { +#ifdef VERIFY + secp256k1_fe_verify(&a->x); + secp256k1_fe_verify(&a->y); + VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); +#endif + (void)a; +} +static void secp256k1_gej_verify(const secp256k1_gej *a) { +#ifdef VERIFY + secp256k1_fe_verify(&a->x); + secp256k1_fe_verify(&a->y); + secp256k1_fe_verify(&a->z); + VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); +#endif + (void)a; +} + +/* Set r to the affine coordinates of Jacobian point (a.x, a.y, 1/zi). */ static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) { secp256k1_fe zi2; secp256k1_fe zi3; + secp256k1_gej_verify(a); + secp256k1_fe_verify(zi); + VERIFY_CHECK(!a->infinity); + secp256k1_fe_sqr(&zi2, zi); + secp256k1_fe_mul(&zi3, &zi2, zi); + secp256k1_fe_mul(&r->x, &a->x, &zi2); + secp256k1_fe_mul(&r->y, &a->y, &zi3); + r->infinity = a->infinity; + secp256k1_ge_verify(r); +} + +/* Set r to the affine coordinates of Jacobian point (a.x, a.y, 1/zi). */ +static void secp256k1_ge_set_ge_zinv(secp256k1_ge *r, const secp256k1_ge *a, const secp256k1_fe *zi) { + secp256k1_fe zi2; + secp256k1_fe zi3; + secp256k1_ge_verify(a); + secp256k1_fe_verify(zi); VERIFY_CHECK(!a->infinity); secp256k1_fe_sqr(&zi2, zi); secp256k1_fe_mul(&zi3, &zi2, zi); secp256k1_fe_mul(&r->x, &a->x, &zi2); secp256k1_fe_mul(&r->y, &a->y, &zi3); r->infinity = a->infinity; + secp256k1_ge_verify(r); } static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) { + secp256k1_fe_verify(x); + secp256k1_fe_verify(y); r->infinity = 0; r->x = *x; r->y = *y; + secp256k1_ge_verify(r); } static int secp256k1_ge_is_infinity(const secp256k1_ge *a) { + secp256k1_ge_verify(a); return a->infinity; } static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a) { + secp256k1_ge_verify(a); *r = *a; secp256k1_fe_normalize_weak(&r->y); secp256k1_fe_negate(&r->y, &r->y, 1); + secp256k1_ge_verify(r); } static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { secp256k1_fe z2, z3; + secp256k1_gej_verify(a); r->infinity = a->infinity; secp256k1_fe_inv(&a->z, &a->z); secp256k1_fe_sqr(&z2, &a->z); @@ -112,14 +156,17 @@ static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { secp256k1_fe_set_int(&a->z, 1); r->x = a->x; r->y = a->y; + secp256k1_ge_verify(r); } static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { secp256k1_fe z2, z3; - if (a->infinity) { + secp256k1_gej_verify(a); + if (secp256k1_gej_is_infinity(a)) { secp256k1_ge_set_infinity(r); return; } + r->infinity = 0; secp256k1_fe_inv_var(&a->z, &a->z); secp256k1_fe_sqr(&z2, &a->z); secp256k1_fe_mul(&z3, &a->z, &z2); @@ -127,6 +174,7 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { secp256k1_fe_mul(&a->y, &a->y, &z3); secp256k1_fe_set_int(&a->z, 1); secp256k1_ge_set_xy(r, &a->x, &a->y); + secp256k1_ge_verify(r); } static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) { @@ -135,6 +183,7 @@ static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a size_t last_i = SIZE_MAX; for (i = 0; i < len; i++) { + secp256k1_gej_verify(&a[i]); if (a[i].infinity) { secp256k1_ge_set_infinity(&r[i]); } else { @@ -168,6 +217,7 @@ static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a if (!a[i].infinity) { secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x); } + secp256k1_ge_verify(&r[i]); } } @@ -176,21 +226,25 @@ static void secp256k1_ge_table_set_globalz(size_t len, secp256k1_ge *a, const se secp256k1_fe zs; if (len > 0) { + /* Verify inputs a[len-1] and zr[len-1]. */ + secp256k1_ge_verify(&a[i]); + secp256k1_fe_verify(&zr[i]); /* Ensure all y values are in weak normal form for fast negation of points */ secp256k1_fe_normalize_weak(&a[i].y); zs = zr[i]; /* Work our way backwards, using the z-ratios to scale the x/y values. */ while (i > 0) { - secp256k1_gej tmpa; + /* Verify all inputs a[i] and zr[i]. */ + secp256k1_fe_verify(&zr[i]); + secp256k1_ge_verify(&a[i]); if (i != len - 1) { secp256k1_fe_mul(&zs, &zs, &zr[i]); } i--; - tmpa.x = a[i].x; - tmpa.y = a[i].y; - tmpa.infinity = 0; - secp256k1_ge_set_gej_zinv(&a[i], &tmpa, &zs); + secp256k1_ge_set_ge_zinv(&a[i], &a[i], &zs); + /* Verify the output a[i]. */ + secp256k1_ge_verify(&a[i]); } } } @@ -200,12 +254,14 @@ static void secp256k1_gej_set_infinity(secp256k1_gej *r) { secp256k1_fe_clear(&r->x); secp256k1_fe_clear(&r->y); secp256k1_fe_clear(&r->z); + secp256k1_gej_verify(r); } static void secp256k1_ge_set_infinity(secp256k1_ge *r) { r->infinity = 1; secp256k1_fe_clear(&r->x); secp256k1_fe_clear(&r->y); + secp256k1_ge_verify(r); } static void secp256k1_gej_clear(secp256k1_gej *r) { @@ -223,59 +279,73 @@ static void secp256k1_ge_clear(secp256k1_ge *r) { static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { secp256k1_fe x2, x3; + int ret; + secp256k1_fe_verify(x); r->x = *x; secp256k1_fe_sqr(&x2, x); secp256k1_fe_mul(&x3, x, &x2); r->infinity = 0; secp256k1_fe_add_int(&x3, SECP256K1_B); - if (!secp256k1_fe_sqrt(&r->y, &x3)) { - return 0; - } + ret = secp256k1_fe_sqrt(&r->y, &x3); secp256k1_fe_normalize_var(&r->y); if (secp256k1_fe_is_odd(&r->y) != odd) { secp256k1_fe_negate(&r->y, &r->y, 1); } - return 1; - + secp256k1_ge_verify(r); + return ret; } static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) { + secp256k1_ge_verify(a); r->infinity = a->infinity; r->x = a->x; r->y = a->y; secp256k1_fe_set_int(&r->z, 1); + secp256k1_gej_verify(r); } static int secp256k1_gej_eq_var(const secp256k1_gej *a, const secp256k1_gej *b) { secp256k1_gej tmp; + secp256k1_gej_verify(b); + secp256k1_gej_verify(a); secp256k1_gej_neg(&tmp, a); secp256k1_gej_add_var(&tmp, &tmp, b, NULL); return secp256k1_gej_is_infinity(&tmp); } static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a) { - secp256k1_fe r, r2; + secp256k1_fe r; + +#ifdef VERIFY + secp256k1_fe_verify(x); + VERIFY_CHECK(a->x.magnitude <= 31); + secp256k1_gej_verify(a); VERIFY_CHECK(!a->infinity); +#endif + secp256k1_fe_sqr(&r, &a->z); secp256k1_fe_mul(&r, &r, x); - r2 = a->x; secp256k1_fe_normalize_weak(&r2); - return secp256k1_fe_equal_var(&r, &r2); + return secp256k1_fe_equal_var(&r, &a->x); } static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a) { + secp256k1_gej_verify(a); r->infinity = a->infinity; r->x = a->x; r->y = a->y; r->z = a->z; secp256k1_fe_normalize_weak(&r->y); secp256k1_fe_negate(&r->y, &r->y, 1); + secp256k1_gej_verify(r); } static int secp256k1_gej_is_infinity(const secp256k1_gej *a) { + secp256k1_gej_verify(a); return a->infinity; } static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { secp256k1_fe y2, x3; + secp256k1_ge_verify(a); if (a->infinity) { return 0; } @@ -283,7 +353,6 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { secp256k1_fe_sqr(&y2, &a->y); secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); secp256k1_fe_add_int(&x3, SECP256K1_B); - secp256k1_fe_normalize_weak(&x3); return secp256k1_fe_equal_var(&y2, &x3); } @@ -291,6 +360,7 @@ static SECP256K1_INLINE void secp256k1_gej_double(secp256k1_gej *r, const secp25 /* Operations: 3 mul, 4 sqr, 8 add/half/mul_int/negate */ secp256k1_fe l, s, t; + secp256k1_gej_verify(a); r->infinity = a->infinity; /* Formula used: @@ -317,6 +387,7 @@ static SECP256K1_INLINE void secp256k1_gej_double(secp256k1_gej *r, const secp25 secp256k1_fe_mul(&r->y, &t, &l); /* Y3 = L*(X3 + T) (1) */ secp256k1_fe_add(&r->y, &s); /* Y3 = L*(X3 + T) + S^2 (2) */ secp256k1_fe_negate(&r->y, &r->y, 2); /* Y3 = -(L*(X3 + T) + S^2) (3) */ + secp256k1_gej_verify(r); } static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { @@ -330,6 +401,7 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s * the infinity flag even though the point doubles to infinity, and the result * point will be gibberish (z = 0 but infinity = 0). */ + secp256k1_gej_verify(a); if (a->infinity) { secp256k1_gej_set_infinity(r); if (rzr != NULL) { @@ -344,12 +416,15 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s } secp256k1_gej_double(r, a); + secp256k1_gej_verify(r); } static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) { /* 12 mul, 4 sqr, 11 add/negate/normalizes_to_zero (ignoring special cases) */ secp256k1_fe z22, z12, u1, u2, s1, s2, h, i, h2, h3, t; + secp256k1_gej_verify(a); + secp256k1_gej_verify(b); if (a->infinity) { VERIFY_CHECK(rzr == NULL); *r = *b; @@ -404,11 +479,14 @@ static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, cons secp256k1_fe_mul(&r->y, &t, &i); secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_add(&r->y, &h3); + secp256k1_gej_verify(r); } static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr) { /* 8 mul, 3 sqr, 13 add/negate/normalize_weak/normalizes_to_zero (ignoring special cases) */ secp256k1_fe z12, u1, u2, s1, s2, h, i, h2, h3, t; + secp256k1_gej_verify(a); + secp256k1_ge_verify(b); if (a->infinity) { VERIFY_CHECK(rzr == NULL); secp256k1_gej_set_ge(r, b); @@ -461,12 +539,16 @@ static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, c secp256k1_fe_mul(&r->y, &t, &i); secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_add(&r->y, &h3); + secp256k1_gej_verify(r); + if (rzr != NULL) secp256k1_fe_verify(rzr); } static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv) { /* 9 mul, 3 sqr, 13 add/negate/normalize_weak/normalizes_to_zero (ignoring special cases) */ secp256k1_fe az, z12, u1, u2, s1, s2, h, i, h2, h3, t; + secp256k1_ge_verify(b); + secp256k1_fe_verify(bzinv); if (a->infinity) { secp256k1_fe bzinv2, bzinv3; r->infinity = b->infinity; @@ -525,6 +607,7 @@ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe_mul(&r->y, &t, &i); secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_add(&r->y, &h3); + secp256k1_gej_verify(r); } @@ -533,6 +616,8 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; secp256k1_fe m_alt, rr_alt; int degenerate; + secp256k1_gej_verify(a); + secp256k1_ge_verify(b); VERIFY_CHECK(!b->infinity); VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); @@ -658,21 +743,28 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const * We have degenerate = false, r->z = (y1 + y2) * Z. * Then r->infinity = ((y1 + y2)Z == 0) = (y1 == -y2) = false. */ r->infinity = secp256k1_fe_normalizes_to_zero(&r->z); + secp256k1_gej_verify(r); } static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *s) { /* Operations: 4 mul, 1 sqr */ secp256k1_fe zz; - VERIFY_CHECK(!secp256k1_fe_is_zero(s)); + secp256k1_gej_verify(r); + secp256k1_fe_verify(s); +#ifdef VERIFY + VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(s)); +#endif secp256k1_fe_sqr(&zz, s); secp256k1_fe_mul(&r->x, &r->x, &zz); /* r->x *= s^2 */ secp256k1_fe_mul(&r->y, &r->y, &zz); secp256k1_fe_mul(&r->y, &r->y, s); /* r->y *= s^3 */ secp256k1_fe_mul(&r->z, &r->z, s); /* r->z *= s */ + secp256k1_gej_verify(r); } static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a) { secp256k1_fe x, y; + secp256k1_ge_verify(a); VERIFY_CHECK(!a->infinity); x = a->x; secp256k1_fe_normalize(&x); @@ -686,14 +778,18 @@ static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storag secp256k1_fe_from_storage(&r->x, &a->x); secp256k1_fe_from_storage(&r->y, &a->y); r->infinity = 0; + secp256k1_ge_verify(r); } static SECP256K1_INLINE void secp256k1_gej_cmov(secp256k1_gej *r, const secp256k1_gej *a, int flag) { + secp256k1_gej_verify(r); + secp256k1_gej_verify(a); secp256k1_fe_cmov(&r->x, &a->x, flag); secp256k1_fe_cmov(&r->y, &a->y, flag); secp256k1_fe_cmov(&r->z, &a->z, flag); r->infinity ^= (r->infinity ^ a->infinity) & flag; + secp256k1_gej_verify(r); } static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag) { @@ -703,7 +799,9 @@ static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { *r = *a; + secp256k1_ge_verify(a); secp256k1_fe_mul(&r->x, &r->x, &secp256k1_const_beta); + secp256k1_ge_verify(r); } static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) { @@ -711,6 +809,7 @@ static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) { secp256k1_gej out; int i; + secp256k1_ge_verify(ge); /* A very simple EC multiplication ladder that avoids a dependency on ecmult. */ secp256k1_gej_set_infinity(&out); for (i = 0; i < 32; ++i) { @@ -727,4 +826,32 @@ static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) { #endif } +static int secp256k1_ge_x_on_curve_var(const secp256k1_fe *x) { + secp256k1_fe c; + secp256k1_fe_sqr(&c, x); + secp256k1_fe_mul(&c, &c, x); + secp256k1_fe_add_int(&c, SECP256K1_B); + return secp256k1_fe_is_square_var(&c); +} + +static int secp256k1_ge_x_frac_on_curve_var(const secp256k1_fe *xn, const secp256k1_fe *xd) { + /* We want to determine whether (xn/xd) is on the curve. + * + * (xn/xd)^3 + 7 is square <=> xd*xn^3 + 7*xd^4 is square (multiplying by xd^4, a square). + */ + secp256k1_fe r, t; +#ifdef VERIFY + VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(xd)); +#endif + secp256k1_fe_mul(&r, xd, xn); /* r = xd*xn */ + secp256k1_fe_sqr(&t, xn); /* t = xn^2 */ + secp256k1_fe_mul(&r, &r, &t); /* r = xd*xn^3 */ + secp256k1_fe_sqr(&t, xd); /* t = xd^2 */ + secp256k1_fe_sqr(&t, &t); /* t = xd^4 */ + VERIFY_CHECK(SECP256K1_B <= 31); + secp256k1_fe_mul_int(&t, SECP256K1_B); /* t = 7*xd^4 */ + secp256k1_fe_add(&r, &t); /* r = xd*xn^3 + 7*xd^4 */ + return secp256k1_fe_is_square_var(&r); +} + #endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/secp256k1/src/int128_native_impl.h b/src/secp256k1/src/int128_native_impl.h index 996e542cf9..7f02e1590b 100644 --- a/src/secp256k1/src/int128_native_impl.h +++ b/src/secp256k1/src/int128_native_impl.h @@ -2,6 +2,7 @@ #define SECP256K1_INT128_NATIVE_IMPL_H #include "int128.h" +#include "util.h" static SECP256K1_INLINE void secp256k1_u128_load(secp256k1_uint128 *r, uint64_t hi, uint64_t lo) { *r = (((uint128_t)hi) << 64) + lo; diff --git a/src/secp256k1/src/int128_struct_impl.h b/src/secp256k1/src/int128_struct_impl.h index cc17bad167..962a71d13b 100644 --- a/src/secp256k1/src/int128_struct_impl.h +++ b/src/secp256k1/src/int128_struct_impl.h @@ -2,6 +2,7 @@ #define SECP256K1_INT128_STRUCT_IMPL_H #include "int128.h" +#include "util.h" #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) /* MSVC */ # include <intrin.h> @@ -79,7 +80,12 @@ static SECP256K1_INLINE void secp256k1_u128_rshift(secp256k1_uint128 *r, unsigne r->lo = r->hi >> (n-64); r->hi = 0; } else if (n > 0) { +#if defined(_MSC_VER) && defined(_M_X64) + VERIFY_CHECK(n < 64); + r->lo = __shiftright128(r->lo, r->hi, n); +#else r->lo = ((1U * r->hi) << (64-n)) | r->lo >> n; +#endif r->hi >>= n; } } diff --git a/src/secp256k1/src/modinv32_impl.h b/src/secp256k1/src/modinv32_impl.h index 8e400b697b..0ea2699863 100644 --- a/src/secp256k1/src/modinv32_impl.h +++ b/src/secp256k1/src/modinv32_impl.h @@ -64,7 +64,7 @@ static void secp256k1_modinv32_normalize_30(secp256k1_modinv32_signed30 *r, int3 const int32_t M30 = (int32_t)(UINT32_MAX >> 2); int32_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4], r5 = r->v[5], r6 = r->v[6], r7 = r->v[7], r8 = r->v[8]; - int32_t cond_add, cond_negate; + volatile int32_t cond_add, cond_negate; #ifdef VERIFY /* Verify that all limbs are in range (-2^30,2^30). */ @@ -186,7 +186,8 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ * being inside [-2^31,2^31) means that casting to signed works correctly. */ uint32_t u = 1, v = 0, q = 0, r = 1; - uint32_t c1, c2, f = f0, g = g0, x, y, z; + volatile uint32_t c1, c2; + uint32_t mask1, mask2, f = f0, g = g0, x, y, z; int i; for (i = 0; i < 30; ++i) { @@ -195,23 +196,25 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ VERIFY_CHECK((q * f0 + r * g0) == g << i); /* Compute conditional masks for (zeta < 0) and for (g & 1). */ c1 = zeta >> 31; - c2 = -(g & 1); + mask1 = c1; + c2 = g & 1; + mask2 = -c2; /* Compute x,y,z, conditionally negated versions of f,u,v. */ - x = (f ^ c1) - c1; - y = (u ^ c1) - c1; - z = (v ^ c1) - c1; + x = (f ^ mask1) - mask1; + y = (u ^ mask1) - mask1; + z = (v ^ mask1) - mask1; /* Conditionally add x,y,z to g,q,r. */ - g += x & c2; - q += y & c2; - r += z & c2; - /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */ - c1 &= c2; + g += x & mask2; + q += y & mask2; + r += z & mask2; + /* In what follows, mask1 is a condition mask for (zeta < 0) and (g & 1). */ + mask1 &= mask2; /* Conditionally change zeta into -zeta-2 or zeta-1. */ - zeta = (zeta ^ c1) - 1; + zeta = (zeta ^ mask1) - 1; /* Conditionally add g,q,r to f,u,v. */ - f += g & c1; - u += q & c1; - v += r & c1; + f += g & mask1; + u += q & mask1; + v += r & mask1; /* Shifts */ g >>= 1; u <<= 1; diff --git a/src/secp256k1/src/modinv64_impl.h b/src/secp256k1/src/modinv64_impl.h index e33727d385..c7cef872a4 100644 --- a/src/secp256k1/src/modinv64_impl.h +++ b/src/secp256k1/src/modinv64_impl.h @@ -88,7 +88,7 @@ static int secp256k1_modinv64_det_check_pow2(const secp256k1_modinv64_trans2x2 * static void secp256k1_modinv64_normalize_62(secp256k1_modinv64_signed62 *r, int64_t sign, const secp256k1_modinv64_modinfo *modinfo) { const int64_t M62 = (int64_t)(UINT64_MAX >> 2); int64_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4]; - int64_t cond_add, cond_negate; + volatile int64_t cond_add, cond_negate; #ifdef VERIFY /* Verify that all limbs are in range (-2^62,2^62). */ @@ -175,7 +175,8 @@ static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_ * being inside [-2^63,2^63) means that casting to signed works correctly. */ uint64_t u = 8, v = 0, q = 0, r = 8; - uint64_t c1, c2, f = f0, g = g0, x, y, z; + volatile uint64_t c1, c2; + uint64_t mask1, mask2, f = f0, g = g0, x, y, z; int i; for (i = 3; i < 62; ++i) { @@ -184,23 +185,25 @@ static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_ VERIFY_CHECK((q * f0 + r * g0) == g << i); /* Compute conditional masks for (zeta < 0) and for (g & 1). */ c1 = zeta >> 63; - c2 = -(g & 1); + mask1 = c1; + c2 = g & 1; + mask2 = -c2; /* Compute x,y,z, conditionally negated versions of f,u,v. */ - x = (f ^ c1) - c1; - y = (u ^ c1) - c1; - z = (v ^ c1) - c1; + x = (f ^ mask1) - mask1; + y = (u ^ mask1) - mask1; + z = (v ^ mask1) - mask1; /* Conditionally add x,y,z to g,q,r. */ - g += x & c2; - q += y & c2; - r += z & c2; + g += x & mask2; + q += y & mask2; + r += z & mask2; /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */ - c1 &= c2; + mask1 &= mask2; /* Conditionally change zeta into -zeta-2 or zeta-1. */ - zeta = (zeta ^ c1) - 1; + zeta = (zeta ^ mask1) - 1; /* Conditionally add g,q,r to f,u,v. */ - f += g & c1; - u += q & c1; - v += r & c1; + f += g & mask1; + u += q & mask1; + v += r & mask1; /* Shifts */ g >>= 1; u <<= 1; diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h index 5408c9de70..82b082a9f0 100644 --- a/src/secp256k1/src/modules/ecdh/main_impl.h +++ b/src/secp256k1/src/modules/ecdh/main_impl.h @@ -50,7 +50,7 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const se overflow |= secp256k1_scalar_is_zero(&s); secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow); - secp256k1_ecmult_const(&res, &pt, &s, 256); + secp256k1_ecmult_const(&res, &pt, &s); secp256k1_ge_set_gej(&pt, &res); /* Compute a hash of the point */ diff --git a/src/secp256k1/src/modules/ellswift/Makefile.am.include b/src/secp256k1/src/modules/ellswift/Makefile.am.include new file mode 100644 index 0000000000..8251231ea3 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/Makefile.am.include @@ -0,0 +1,5 @@ +include_HEADERS += include/secp256k1_ellswift.h +noinst_HEADERS += src/modules/ellswift/bench_impl.h +noinst_HEADERS += src/modules/ellswift/main_impl.h +noinst_HEADERS += src/modules/ellswift/tests_impl.h +noinst_HEADERS += src/modules/ellswift/tests_exhaustive_impl.h diff --git a/src/secp256k1/src/modules/ellswift/bench_impl.h b/src/secp256k1/src/modules/ellswift/bench_impl.h new file mode 100644 index 0000000000..b16a3a3687 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/bench_impl.h @@ -0,0 +1,106 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_BENCH_H +#define SECP256K1_MODULE_ELLSWIFT_BENCH_H + +#include "../../../include/secp256k1_ellswift.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point[256]; + unsigned char rnd64[64]; +} bench_ellswift_data; + +static void bench_ellswift_setup(void *arg) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + static const unsigned char init[64] = { + 0x78, 0x1f, 0xb7, 0xd4, 0x67, 0x7f, 0x08, 0x68, + 0xdb, 0xe3, 0x1d, 0x7f, 0x1b, 0xb0, 0xf6, 0x9e, + 0x0a, 0x64, 0xca, 0x32, 0x9e, 0xc6, 0x20, 0x79, + 0x03, 0xf3, 0xd0, 0x46, 0x7a, 0x0f, 0xd2, 0x21, + 0xb0, 0x2c, 0x46, 0xd8, 0xba, 0xca, 0x26, 0x4f, + 0x8f, 0x8c, 0xd4, 0xdd, 0x2d, 0x04, 0xbe, 0x30, + 0x48, 0x51, 0x1e, 0xd4, 0x16, 0xfd, 0x42, 0x85, + 0x62, 0xc9, 0x02, 0xf9, 0x89, 0x84, 0xff, 0xdc + }; + memcpy(data->rnd64, init, 64); + for (i = 0; i < 256; ++i) { + int j; + CHECK(secp256k1_ellswift_decode(data->ctx, &data->point[i], data->rnd64)); + for (j = 0; j < 64; ++j) { + data->rnd64[j] += 1; + } + } + CHECK(secp256k1_ellswift_encode(data->ctx, data->rnd64, &data->point[255], init + 16)); +} + +static void bench_ellswift_encode(void *arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + CHECK(secp256k1_ellswift_encode(data->ctx, data->rnd64, &data->point[i & 255], data->rnd64 + 16)); + } +} + +static void bench_ellswift_create(void *arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + unsigned char buf[64]; + CHECK(secp256k1_ellswift_create(data->ctx, buf, data->rnd64, data->rnd64 + 32)); + memcpy(data->rnd64, buf, 64); + } +} + +static void bench_ellswift_decode(void *arg, int iters) { + int i; + secp256k1_pubkey out; + size_t len; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + CHECK(secp256k1_ellswift_decode(data->ctx, &out, data->rnd64) == 1); + len = 33; + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, data->rnd64 + (i % 32), &len, &out, SECP256K1_EC_COMPRESSED)); + } +} + +static void bench_ellswift_xdh(void *arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + int party = i & 1; + CHECK(secp256k1_ellswift_xdh(data->ctx, + data->rnd64 + (i % 33), + data->rnd64, + data->rnd64, + data->rnd64 + ((i + 16) % 33), + party, + secp256k1_ellswift_xdh_hash_function_bip324, + NULL) == 1); + } +} + +void run_ellswift_bench(int iters, int argc, char **argv) { + bench_ellswift_data data; + int d = argc == 1; + + /* create a context with signing capabilities */ + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "encode") || have_flag(argc, argv, "ellswift_encode")) run_benchmark("ellswift_encode", bench_ellswift_encode, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellswift_decode")) run_benchmark("ellswift_decode", bench_ellswift_decode, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "keygen") || have_flag(argc, argv, "ellswift_keygen")) run_benchmark("ellswift_keygen", bench_ellswift_create, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "ecdh") || have_flag(argc, argv, "ellswift_ecdh")) run_benchmark("ellswift_ecdh", bench_ellswift_xdh, bench_ellswift_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); +} + +#endif diff --git a/src/secp256k1/src/modules/ellswift/main_impl.h b/src/secp256k1/src/modules/ellswift/main_impl.h new file mode 100644 index 0000000000..00bb8a3da5 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/main_impl.h @@ -0,0 +1,589 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_MAIN_H +#define SECP256K1_MODULE_ELLSWIFT_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_ellswift.h" +#include "../../eckey.h" +#include "../../hash.h" + +/** c1 = (sqrt(-3)-1)/2 */ +static const secp256k1_fe secp256k1_ellswift_c1 = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40); +/** c2 = (-sqrt(-3)-1)/2 = -(c1+1) */ +static const secp256k1_fe secp256k1_ellswift_c2 = SECP256K1_FE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ee); +/** c3 = (-sqrt(-3)+1)/2 = -c1 = c2+1 */ +static const secp256k1_fe secp256k1_ellswift_c3 = SECP256K1_FE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ef); +/** c4 = (sqrt(-3)+1)/2 = -c2 = c1+1 */ +static const secp256k1_fe secp256k1_ellswift_c4 = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa41); + +/** Decode ElligatorSwift encoding (u, t) to a fraction xn/xd representing a curve X coordinate. */ +static void secp256k1_ellswift_xswiftec_frac_var(secp256k1_fe *xn, secp256k1_fe *xd, const secp256k1_fe *u, const secp256k1_fe *t) { + /* The implemented algorithm is the following (all operations in GF(p)): + * + * - Let c0 = sqrt(-3) = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852. + * - If u = 0, set u = 1. + * - If t = 0, set t = 1. + * - If u^3+7+t^2 = 0, set t = 2*t. + * - Let X = (u^3+7-t^2)/(2*t). + * - Let Y = (X+t)/(c0*u). + * - If x3 = u+4*Y^2 is a valid x coordinate, return it. + * - If x2 = (-X/Y-u)/2 is a valid x coordinate, return it. + * - Return x1 = (X/Y-u)/2 (which is now guaranteed to be a valid x coordinate). + * + * Introducing s=t^2, g=u^3+7, and simplifying x1=-(x2+u) we get: + * + * - Let c0 = ... + * - If u = 0, set u = 1. + * - If t = 0, set t = 1. + * - Let s = t^2 + * - Let g = u^3+7 + * - If g+s = 0, set t = 2*t, s = 4*s + * - Let X = (g-s)/(2*t). + * - Let Y = (X+t)/(c0*u) = (g+s)/(2*c0*t*u). + * - If x3 = u+4*Y^2 is a valid x coordinate, return it. + * - If x2 = (-X/Y-u)/2 is a valid x coordinate, return it. + * - Return x1 = -(x2+u). + * + * Now substitute Y^2 = -(g+s)^2/(12*s*u^2) and X/Y = c0*u*(g-s)/(g+s). This + * means X and Y do not need to be evaluated explicitly anymore. + * + * - ... + * - If g+s = 0, set s = 4*s. + * - If x3 = u-(g+s)^2/(3*s*u^2) is a valid x coordinate, return it. + * - If x2 = (-c0*u*(g-s)/(g+s)-u)/2 is a valid x coordinate, return it. + * - Return x1 = -(x2+u). + * + * Simplifying x2 using 2 additional constants: + * + * - Let c1 = (c0-1)/2 = 0x851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40. + * - Let c2 = (-c0-1)/2 = 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee. + * - ... + * - If x2 = u*(c1*s+c2*g)/(g+s) is a valid x coordinate, return it. + * - ... + * + * Writing x3 as a fraction: + * + * - ... + * - If x3 = (3*s*u^3-(g+s)^2)/(3*s*u^2) ... + * - ... + + * Overall, we get: + * + * - Let c1 = 0x851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40. + * - Let c2 = 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee. + * - If u = 0, set u = 1. + * - If t = 0, set s = 1, else set s = t^2. + * - Let g = u^3+7. + * - If g+s = 0, set s = 4*s. + * - If x3 = (3*s*u^3-(g+s)^2)/(3*s*u^2) is a valid x coordinate, return it. + * - If x2 = u*(c1*s+c2*g)/(g+s) is a valid x coordinate, return it. + * - Return x1 = -(x2+u). + */ + secp256k1_fe u1, s, g, p, d, n, l; + u1 = *u; + if (EXPECT(secp256k1_fe_normalizes_to_zero_var(&u1), 0)) u1 = secp256k1_fe_one; + secp256k1_fe_sqr(&s, t); + if (EXPECT(secp256k1_fe_normalizes_to_zero_var(t), 0)) s = secp256k1_fe_one; + secp256k1_fe_sqr(&l, &u1); /* l = u^2 */ + secp256k1_fe_mul(&g, &l, &u1); /* g = u^3 */ + secp256k1_fe_add_int(&g, SECP256K1_B); /* g = u^3 + 7 */ + p = g; /* p = g */ + secp256k1_fe_add(&p, &s); /* p = g+s */ + if (EXPECT(secp256k1_fe_normalizes_to_zero_var(&p), 0)) { + secp256k1_fe_mul_int(&s, 4); + /* Recompute p = g+s */ + p = g; /* p = g */ + secp256k1_fe_add(&p, &s); /* p = g+s */ + } + secp256k1_fe_mul(&d, &s, &l); /* d = s*u^2 */ + secp256k1_fe_mul_int(&d, 3); /* d = 3*s*u^2 */ + secp256k1_fe_sqr(&l, &p); /* l = (g+s)^2 */ + secp256k1_fe_negate(&l, &l, 1); /* l = -(g+s)^2 */ + secp256k1_fe_mul(&n, &d, &u1); /* n = 3*s*u^3 */ + secp256k1_fe_add(&n, &l); /* n = 3*s*u^3-(g+s)^2 */ + if (secp256k1_ge_x_frac_on_curve_var(&n, &d)) { + /* Return x3 = n/d = (3*s*u^3-(g+s)^2)/(3*s*u^2) */ + *xn = n; + *xd = d; + return; + } + *xd = p; + secp256k1_fe_mul(&l, &secp256k1_ellswift_c1, &s); /* l = c1*s */ + secp256k1_fe_mul(&n, &secp256k1_ellswift_c2, &g); /* n = c2*g */ + secp256k1_fe_add(&n, &l); /* n = c1*s+c2*g */ + secp256k1_fe_mul(&n, &n, &u1); /* n = u*(c1*s+c2*g) */ + /* Possible optimization: in the invocation below, p^2 = (g+s)^2 is computed, + * which we already have computed above. This could be deduplicated. */ + if (secp256k1_ge_x_frac_on_curve_var(&n, &p)) { + /* Return x2 = n/p = u*(c1*s+c2*g)/(g+s) */ + *xn = n; + return; + } + secp256k1_fe_mul(&l, &p, &u1); /* l = u*(g+s) */ + secp256k1_fe_add(&n, &l); /* n = u*(c1*s+c2*g)+u*(g+s) */ + secp256k1_fe_negate(xn, &n, 2); /* n = -u*(c1*s+c2*g)-u*(g+s) */ +#ifdef VERIFY + VERIFY_CHECK(secp256k1_ge_x_frac_on_curve_var(xn, &p)); +#endif + /* Return x3 = n/p = -(u*(c1*s+c2*g)/(g+s)+u) */ +} + +/** Decode ElligatorSwift encoding (u, t) to X coordinate. */ +static void secp256k1_ellswift_xswiftec_var(secp256k1_fe *x, const secp256k1_fe *u, const secp256k1_fe *t) { + secp256k1_fe xn, xd; + secp256k1_ellswift_xswiftec_frac_var(&xn, &xd, u, t); + secp256k1_fe_inv_var(&xd, &xd); + secp256k1_fe_mul(x, &xn, &xd); +} + +/** Decode ElligatorSwift encoding (u, t) to point P. */ +static void secp256k1_ellswift_swiftec_var(secp256k1_ge *p, const secp256k1_fe *u, const secp256k1_fe *t) { + secp256k1_fe x; + secp256k1_ellswift_xswiftec_var(&x, u, t); + secp256k1_ge_set_xo_var(p, &x, secp256k1_fe_is_odd(t)); +} + +/* Try to complete an ElligatorSwift encoding (u, t) for X coordinate x, given u and x. + * + * There may be up to 8 distinct t values such that (u, t) decodes back to x, but also + * fewer, or none at all. Each such partial inverse can be accessed individually using a + * distinct input argument c (in range 0-7), and some or all of these may return failure. + * The following guarantees exist: + * - Given (x, u), no two distinct c values give the same successful result t. + * - Every successful result maps back to x through secp256k1_ellswift_xswiftec_var. + * - Given (x, u), all t values that map back to x can be reached by combining the + * successful results from this function over all c values, with the exception of: + * - this function cannot be called with u=0 + * - no result with t=0 will be returned + * - no result for which u^3 + t^2 + 7 = 0 will be returned. + * + * The rather unusual encoding of bits in c (a large "if" based on the middle bit, and then + * using the low and high bits to pick signs of square roots) is to match the paper's + * encoding more closely: c=0 through c=3 match branches 1..4 in the paper, while c=4 through + * c=7 are copies of those with an additional negation of sqrt(w). + */ +static int secp256k1_ellswift_xswiftec_inv_var(secp256k1_fe *t, const secp256k1_fe *x_in, const secp256k1_fe *u_in, int c) { + /* The implemented algorithm is this (all arithmetic, except involving c, is mod p): + * + * - If (c & 2) = 0: + * - If (-x-u) is a valid X coordinate, fail. + * - Let s=-(u^3+7)/(u^2+u*x+x^2). + * - If s is not square, fail. + * - Let v=x. + * - If (c & 2) = 2: + * - Let s=x-u. + * - If s is not square, fail. + * - Let r=sqrt(-s*(4*(u^3+7)+3*u^2*s)); fail if it doesn't exist. + * - If (c & 1) = 1 and r = 0, fail. + * - If s=0, fail. + * - Let v=(r/s-u)/2. + * - Let w=sqrt(s). + * - If (c & 5) = 0: return -w*(c3*u + v). + * - If (c & 5) = 1: return w*(c4*u + v). + * - If (c & 5) = 4: return w*(c3*u + v). + * - If (c & 5) = 5: return -w*(c4*u + v). + */ + secp256k1_fe x = *x_in, u = *u_in, g, v, s, m, r, q; + int ret; + + secp256k1_fe_normalize_weak(&x); + secp256k1_fe_normalize_weak(&u); + +#ifdef VERIFY + VERIFY_CHECK(c >= 0 && c < 8); + VERIFY_CHECK(secp256k1_ge_x_on_curve_var(&x)); +#endif + + if (!(c & 2)) { + /* c is in {0, 1, 4, 5}. In this case we look for an inverse under the x1 (if c=0 or + * c=4) formula, or x2 (if c=1 or c=5) formula. */ + + /* If -u-x is a valid X coordinate, fail. This would yield an encoding that roundtrips + * back under the x3 formula instead (which has priority over x1 and x2, so the decoding + * would not match x). */ + m = x; /* m = x */ + secp256k1_fe_add(&m, &u); /* m = u+x */ + secp256k1_fe_negate(&m, &m, 2); /* m = -u-x */ + /* Test if (-u-x) is a valid X coordinate. If so, fail. */ + if (secp256k1_ge_x_on_curve_var(&m)) return 0; + + /* Let s = -(u^3 + 7)/(u^2 + u*x + x^2) [first part] */ + secp256k1_fe_sqr(&s, &m); /* s = (u+x)^2 */ + secp256k1_fe_negate(&s, &s, 1); /* s = -(u+x)^2 */ + secp256k1_fe_mul(&m, &u, &x); /* m = u*x */ + secp256k1_fe_add(&s, &m); /* s = -(u^2 + u*x + x^2) */ + + /* Note that at this point, s = 0 is impossible. If it were the case: + * s = -(u^2 + u*x + x^2) = 0 + * => u^2 + u*x + x^2 = 0 + * => (u + 2*x) * (u^2 + u*x + x^2) = 0 + * => 2*x^3 + 3*x^2*u + 3*x*u^2 + u^3 = 0 + * => (x + u)^3 + x^3 = 0 + * => x^3 = -(x + u)^3 + * => x^3 + B = (-u - x)^3 + B + * + * However, we know x^3 + B is square (because x is on the curve) and + * that (-u-x)^3 + B is not square (the secp256k1_ge_x_on_curve_var(&m) + * test above would have failed). This is a contradiction, and thus the + * assumption s=0 is false. */ +#ifdef VERIFY + VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(&s)); +#endif + + /* If s is not square, fail. We have not fully computed s yet, but s is square iff + * -(u^3+7)*(u^2+u*x+x^2) is square (because a/b is square iff a*b is square and b is + * nonzero). */ + secp256k1_fe_sqr(&g, &u); /* g = u^2 */ + secp256k1_fe_mul(&g, &g, &u); /* g = u^3 */ + secp256k1_fe_add_int(&g, SECP256K1_B); /* g = u^3+7 */ + secp256k1_fe_mul(&m, &s, &g); /* m = -(u^3 + 7)*(u^2 + u*x + x^2) */ + if (!secp256k1_fe_is_square_var(&m)) return 0; + + /* Let s = -(u^3 + 7)/(u^2 + u*x + x^2) [second part] */ + secp256k1_fe_inv_var(&s, &s); /* s = -1/(u^2 + u*x + x^2) [no div by 0] */ + secp256k1_fe_mul(&s, &s, &g); /* s = -(u^3 + 7)/(u^2 + u*x + x^2) */ + + /* Let v = x. */ + v = x; + } else { + /* c is in {2, 3, 6, 7}. In this case we look for an inverse under the x3 formula. */ + + /* Let s = x-u. */ + secp256k1_fe_negate(&m, &u, 1); /* m = -u */ + s = m; /* s = -u */ + secp256k1_fe_add(&s, &x); /* s = x-u */ + + /* If s is not square, fail. */ + if (!secp256k1_fe_is_square_var(&s)) return 0; + + /* Let r = sqrt(-s*(4*(u^3+7)+3*u^2*s)); fail if it doesn't exist. */ + secp256k1_fe_sqr(&g, &u); /* g = u^2 */ + secp256k1_fe_mul(&q, &s, &g); /* q = s*u^2 */ + secp256k1_fe_mul_int(&q, 3); /* q = 3*s*u^2 */ + secp256k1_fe_mul(&g, &g, &u); /* g = u^3 */ + secp256k1_fe_mul_int(&g, 4); /* g = 4*u^3 */ + secp256k1_fe_add_int(&g, 4 * SECP256K1_B); /* g = 4*(u^3+7) */ + secp256k1_fe_add(&q, &g); /* q = 4*(u^3+7)+3*s*u^2 */ + secp256k1_fe_mul(&q, &q, &s); /* q = s*(4*(u^3+7)+3*u^2*s) */ + secp256k1_fe_negate(&q, &q, 1); /* q = -s*(4*(u^3+7)+3*u^2*s) */ + if (!secp256k1_fe_is_square_var(&q)) return 0; + ret = secp256k1_fe_sqrt(&r, &q); /* r = sqrt(-s*(4*(u^3+7)+3*u^2*s)) */ + VERIFY_CHECK(ret); + + /* If (c & 1) = 1 and r = 0, fail. */ + if (EXPECT((c & 1) && secp256k1_fe_normalizes_to_zero_var(&r), 0)) return 0; + + /* If s = 0, fail. */ + if (EXPECT(secp256k1_fe_normalizes_to_zero_var(&s), 0)) return 0; + + /* Let v = (r/s-u)/2. */ + secp256k1_fe_inv_var(&v, &s); /* v = 1/s [no div by 0] */ + secp256k1_fe_mul(&v, &v, &r); /* v = r/s */ + secp256k1_fe_add(&v, &m); /* v = r/s-u */ + secp256k1_fe_half(&v); /* v = (r/s-u)/2 */ + } + + /* Let w = sqrt(s). */ + ret = secp256k1_fe_sqrt(&m, &s); /* m = sqrt(s) = w */ + VERIFY_CHECK(ret); + + /* Return logic. */ + if ((c & 5) == 0 || (c & 5) == 5) { + secp256k1_fe_negate(&m, &m, 1); /* m = -w */ + } + /* Now m = {-w if c&5=0 or c&5=5; w otherwise}. */ + secp256k1_fe_mul(&u, &u, c&1 ? &secp256k1_ellswift_c4 : &secp256k1_ellswift_c3); + /* u = {c4 if c&1=1; c3 otherwise}*u */ + secp256k1_fe_add(&u, &v); /* u = {c4 if c&1=1; c3 otherwise}*u + v */ + secp256k1_fe_mul(t, &m, &u); + return 1; +} + +/** Use SHA256 as a PRNG, returning SHA256(hasher || cnt). + * + * hasher is a SHA256 object to which an incrementing 4-byte counter is written to generate randomness. + * Writing 13 bytes (4 bytes for counter, plus 9 bytes for the SHA256 padding) cannot cross a + * 64-byte block size boundary (to make sure it only triggers a single SHA256 compression). */ +static void secp256k1_ellswift_prng(unsigned char* out32, const secp256k1_sha256 *hasher, uint32_t cnt) { + secp256k1_sha256 hash = *hasher; + unsigned char buf4[4]; +#ifdef VERIFY + size_t blocks = hash.bytes >> 6; +#endif + buf4[0] = cnt; + buf4[1] = cnt >> 8; + buf4[2] = cnt >> 16; + buf4[3] = cnt >> 24; + secp256k1_sha256_write(&hash, buf4, 4); + secp256k1_sha256_finalize(&hash, out32); +#ifdef VERIFY + /* Writing and finalizing together should trigger exactly one SHA256 compression. */ + VERIFY_CHECK(((hash.bytes) >> 6) == (blocks + 1)); +#endif +} + +/** Find an ElligatorSwift encoding (u, t) for X coordinate x, and random Y coordinate. + * + * u32 is the 32-byte big endian encoding of u; t is the output field element t that still + * needs encoding. + * + * hasher is a hasher in the secp256k1_ellswift_prng sense, with the same restrictions. */ +static void secp256k1_ellswift_xelligatorswift_var(unsigned char *u32, secp256k1_fe *t, const secp256k1_fe *x, const secp256k1_sha256 *hasher) { + /* Pool of 3-bit branch values. */ + unsigned char branch_hash[32]; + /* Number of 3-bit values in branch_hash left. */ + int branches_left = 0; + /* Field elements u and branch values are extracted from RNG based on hasher for consecutive + * values of cnt. cnt==0 is first used to populate a pool of 64 4-bit branch values. The 64 + * cnt values that follow are used to generate field elements u. cnt==65 (and multiples + * thereof) are used to repopulate the pool and start over, if that were ever necessary. + * On average, 4 iterations are needed. */ + uint32_t cnt = 0; + while (1) { + int branch; + secp256k1_fe u; + /* If the pool of branch values is empty, populate it. */ + if (branches_left == 0) { + secp256k1_ellswift_prng(branch_hash, hasher, cnt++); + branches_left = 64; + } + /* Take a 3-bit branch value from the branch pool (top bit is discarded). */ + --branches_left; + branch = (branch_hash[branches_left >> 1] >> ((branches_left & 1) << 2)) & 7; + /* Compute a new u value by hashing. */ + secp256k1_ellswift_prng(u32, hasher, cnt++); + /* overflow is not a problem (we prefer uniform u32 over uniform u). */ + secp256k1_fe_set_b32_mod(&u, u32); + /* Since u is the output of a hash, it should practically never be 0. We could apply the + * u=0 to u=1 correction here too to deal with that case still, but it's such a low + * probability event that we do not bother. */ +#ifdef VERIFY + VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(&u)); +#endif + /* Find a remainder t, and return it if found. */ + if (EXPECT(secp256k1_ellswift_xswiftec_inv_var(t, x, &u, branch), 0)) break; + } +} + +/** Find an ElligatorSwift encoding (u, t) for point P. + * + * This is similar secp256k1_ellswift_xelligatorswift_var, except it takes a full group element p + * as input, and returns an encoding that matches the provided Y coordinate rather than a random + * one. + */ +static void secp256k1_ellswift_elligatorswift_var(unsigned char *u32, secp256k1_fe *t, const secp256k1_ge *p, const secp256k1_sha256 *hasher) { + secp256k1_ellswift_xelligatorswift_var(u32, t, &p->x, hasher); + secp256k1_fe_normalize_var(t); + if (secp256k1_fe_is_odd(t) != secp256k1_fe_is_odd(&p->y)) { + secp256k1_fe_negate(t, t, 1); + secp256k1_fe_normalize_var(t); + } +} + +/** Set hash state to the BIP340 tagged hash midstate for "secp256k1_ellswift_encode". */ +static void secp256k1_ellswift_sha256_init_encode(secp256k1_sha256* hash) { + secp256k1_sha256_initialize(hash); + hash->s[0] = 0xd1a6524bul; + hash->s[1] = 0x028594b3ul; + hash->s[2] = 0x96e42f4eul; + hash->s[3] = 0x1037a177ul; + hash->s[4] = 0x1b8fcb8bul; + hash->s[5] = 0x56023885ul; + hash->s[6] = 0x2560ede1ul; + hash->s[7] = 0xd626b715ul; + + hash->bytes = 64; +} + +int secp256k1_ellswift_encode(const secp256k1_context *ctx, unsigned char *ell64, const secp256k1_pubkey *pubkey, const unsigned char *rnd32) { + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(ell64 != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(rnd32 != NULL); + + if (secp256k1_pubkey_load(ctx, &p, pubkey)) { + secp256k1_fe t; + unsigned char p64[64] = {0}; + size_t ser_size; + int ser_ret; + secp256k1_sha256 hash; + + /* Set up hasher state; the used RNG is H(pubkey || "\x00"*31 || rnd32 || cnt++), using + * BIP340 tagged hash with tag "secp256k1_ellswift_encode". */ + secp256k1_ellswift_sha256_init_encode(&hash); + ser_ret = secp256k1_eckey_pubkey_serialize(&p, p64, &ser_size, 1); + VERIFY_CHECK(ser_ret && ser_size == 33); + secp256k1_sha256_write(&hash, p64, sizeof(p64)); + secp256k1_sha256_write(&hash, rnd32, 32); + + /* Compute ElligatorSwift encoding and construct output. */ + secp256k1_ellswift_elligatorswift_var(ell64, &t, &p, &hash); /* puts u in ell64[0..32] */ + secp256k1_fe_get_b32(ell64 + 32, &t); /* puts t in ell64[32..64] */ + return 1; + } + /* Only reached in case the provided pubkey is invalid. */ + memset(ell64, 0, 64); + return 0; +} + +/** Set hash state to the BIP340 tagged hash midstate for "secp256k1_ellswift_create". */ +static void secp256k1_ellswift_sha256_init_create(secp256k1_sha256* hash) { + secp256k1_sha256_initialize(hash); + hash->s[0] = 0xd29e1bf5ul; + hash->s[1] = 0xf7025f42ul; + hash->s[2] = 0x9b024773ul; + hash->s[3] = 0x094cb7d5ul; + hash->s[4] = 0xe59ed789ul; + hash->s[5] = 0x03bc9786ul; + hash->s[6] = 0x68335b35ul; + hash->s[7] = 0x4e363b53ul; + + hash->bytes = 64; +} + +int secp256k1_ellswift_create(const secp256k1_context *ctx, unsigned char *ell64, const unsigned char *seckey32, const unsigned char *auxrnd32) { + secp256k1_ge p; + secp256k1_fe t; + secp256k1_sha256 hash; + secp256k1_scalar seckey_scalar; + int ret; + static const unsigned char zero32[32] = {0}; + + /* Sanity check inputs. */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(ell64 != NULL); + memset(ell64, 0, 64); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey32 != NULL); + + /* Compute (affine) public key */ + ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey32); + secp256k1_declassify(ctx, &p, sizeof(p)); /* not constant time in produced pubkey */ + secp256k1_fe_normalize_var(&p.x); + secp256k1_fe_normalize_var(&p.y); + + /* Set up hasher state. The used RNG is H(privkey || "\x00"*32 [|| auxrnd32] || cnt++), + * using BIP340 tagged hash with tag "secp256k1_ellswift_create". */ + secp256k1_ellswift_sha256_init_create(&hash); + secp256k1_sha256_write(&hash, seckey32, 32); + secp256k1_sha256_write(&hash, zero32, sizeof(zero32)); + secp256k1_declassify(ctx, &hash, sizeof(hash)); /* private key is hashed now */ + if (auxrnd32) secp256k1_sha256_write(&hash, auxrnd32, 32); + + /* Compute ElligatorSwift encoding and construct output. */ + secp256k1_ellswift_elligatorswift_var(ell64, &t, &p, &hash); /* puts u in ell64[0..32] */ + secp256k1_fe_get_b32(ell64 + 32, &t); /* puts t in ell64[32..64] */ + + secp256k1_memczero(ell64, 64, !ret); + secp256k1_scalar_clear(&seckey_scalar); + + return ret; +} + +int secp256k1_ellswift_decode(const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const unsigned char *ell64) { + secp256k1_fe u, t; + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(ell64 != NULL); + + secp256k1_fe_set_b32_mod(&u, ell64); + secp256k1_fe_set_b32_mod(&t, ell64 + 32); + secp256k1_fe_normalize_var(&t); + secp256k1_ellswift_swiftec_var(&p, &u, &t); + secp256k1_pubkey_save(pubkey, &p); + return 1; +} + +static int ellswift_xdh_hash_function_prefix(unsigned char *output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) { + secp256k1_sha256 sha; + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, data, 64); + secp256k1_sha256_write(&sha, ell_a64, 64); + secp256k1_sha256_write(&sha, ell_b64, 64); + secp256k1_sha256_write(&sha, x32, 32); + secp256k1_sha256_finalize(&sha, output); + + return 1; +} + +/** Set hash state to the BIP340 tagged hash midstate for "bip324_ellswift_xonly_ecdh". */ +static void secp256k1_ellswift_sha256_init_bip324(secp256k1_sha256* hash) { + secp256k1_sha256_initialize(hash); + hash->s[0] = 0x8c12d730ul; + hash->s[1] = 0x827bd392ul; + hash->s[2] = 0x9e4fb2eeul; + hash->s[3] = 0x207b373eul; + hash->s[4] = 0x2292bd7aul; + hash->s[5] = 0xaa5441bcul; + hash->s[6] = 0x15c3779ful; + hash->s[7] = 0xcfb52549ul; + + hash->bytes = 64; +} + +static int ellswift_xdh_hash_function_bip324(unsigned char* output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) { + secp256k1_sha256 sha; + + (void)data; + + secp256k1_ellswift_sha256_init_bip324(&sha); + secp256k1_sha256_write(&sha, ell_a64, 64); + secp256k1_sha256_write(&sha, ell_b64, 64); + secp256k1_sha256_write(&sha, x32, 32); + secp256k1_sha256_finalize(&sha, output); + + return 1; +} + +const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_prefix = ellswift_xdh_hash_function_prefix; +const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_bip324 = ellswift_xdh_hash_function_bip324; + +int secp256k1_ellswift_xdh(const secp256k1_context *ctx, unsigned char *output, const unsigned char *ell_a64, const unsigned char *ell_b64, const unsigned char *seckey32, int party, secp256k1_ellswift_xdh_hash_function hashfp, void *data) { + int ret = 0; + int overflow; + secp256k1_scalar s; + secp256k1_fe xn, xd, px, u, t; + unsigned char sx[32]; + const unsigned char* theirs64; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(ell_a64 != NULL); + ARG_CHECK(ell_b64 != NULL); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(hashfp != NULL); + + /* Load remote public key (as fraction). */ + theirs64 = party ? ell_a64 : ell_b64; + secp256k1_fe_set_b32_mod(&u, theirs64); + secp256k1_fe_set_b32_mod(&t, theirs64 + 32); + secp256k1_ellswift_xswiftec_frac_var(&xn, &xd, &u, &t); + + /* Load private key (using one if invalid). */ + secp256k1_scalar_set_b32(&s, seckey32, &overflow); + overflow = secp256k1_scalar_is_zero(&s); + secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow); + + /* Compute shared X coordinate. */ + secp256k1_ecmult_const_xonly(&px, &xn, &xd, &s, 1); + secp256k1_fe_normalize(&px); + secp256k1_fe_get_b32(sx, &px); + + /* Invoke hasher */ + ret = hashfp(output, sx, ell_a64, ell_b64, data); + + memset(sx, 0, 32); + secp256k1_fe_clear(&px); + secp256k1_scalar_clear(&s); + + return !!ret & !overflow; +} + +#endif diff --git a/src/secp256k1/src/modules/ellswift/tests_exhaustive_impl.h b/src/secp256k1/src/modules/ellswift/tests_exhaustive_impl.h new file mode 100644 index 0000000000..e002a8c008 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/tests_exhaustive_impl.h @@ -0,0 +1,39 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_TESTS_EXHAUSTIVE_H +#define SECP256K1_MODULE_ELLSWIFT_TESTS_EXHAUSTIVE_H + +#include "../../../include/secp256k1_ellswift.h" +#include "main_impl.h" + +static void test_exhaustive_ellswift(const secp256k1_context *ctx, const secp256k1_ge *group) { + int i; + + /* Note that SwiftEC/ElligatorSwift are inherently curve operations, not + * group operations, and this test only checks the curve points which are in + * a tiny subgroup. In that sense it can't be really seen as exhaustive as + * it doesn't (and for computational reasons obviously cannot) test the + * entire domain ellswift operates under. */ + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { + secp256k1_scalar scalar_i; + unsigned char sec32[32]; + unsigned char ell64[64]; + secp256k1_pubkey pub_decoded; + secp256k1_ge ge_decoded; + + /* Construct ellswift pubkey from exhaustive loop scalar i. */ + secp256k1_scalar_set_int(&scalar_i, i); + secp256k1_scalar_get_b32(sec32, &scalar_i); + CHECK(secp256k1_ellswift_create(ctx, ell64, sec32, NULL)); + + /* Decode ellswift pubkey and check that it matches the precomputed group element. */ + secp256k1_ellswift_decode(ctx, &pub_decoded, ell64); + secp256k1_pubkey_load(ctx, &ge_decoded, &pub_decoded); + ge_equals_ge(&ge_decoded, &group[i]); + } +} + +#endif diff --git a/src/secp256k1/src/modules/ellswift/tests_impl.h b/src/secp256k1/src/modules/ellswift/tests_impl.h new file mode 100644 index 0000000000..47f443d980 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/tests_impl.h @@ -0,0 +1,436 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_TESTS_H +#define SECP256K1_MODULE_ELLSWIFT_TESTS_H + +#include "../../../include/secp256k1_ellswift.h" + +struct ellswift_xswiftec_inv_test { + int enc_bitmap; + secp256k1_fe u; + secp256k1_fe x; + secp256k1_fe encs[8]; +}; + +struct ellswift_decode_test { + unsigned char enc[64]; + secp256k1_fe x; + int odd_y; +}; + +struct ellswift_xdh_test { + unsigned char priv_ours[32]; + unsigned char ellswift_ours[64]; + unsigned char ellswift_theirs[64]; + int initiating; + unsigned char shared_secret[32]; +}; + +/* Set of (point, encodings) test vectors, selected to maximize branch coverage, part of the BIP324 + * test vectors. Created using an independent implementation, and tested decoding against paper + * authors' code. */ +static const struct ellswift_xswiftec_inv_test ellswift_xswiftec_inv_tests[] = { + {0xcc, SECP256K1_FE_CONST(0x05ff6bda, 0xd900fc32, 0x61bc7fe3, 0x4e2fb0f5, 0x69f06e09, 0x1ae437d3, 0xa52e9da0, 0xcbfb9590), SECP256K1_FE_CONST(0x80cdf637, 0x74ec7022, 0xc89a5a85, 0x58e373a2, 0x79170285, 0xe0ab2741, 0x2dbce510, 0xbdfe23fc), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x45654798, 0xece071ba, 0x79286d04, 0xf7f3eb1c, 0x3f1d17dd, 0x883610f2, 0xad2efd82, 0xa287466b), SECP256K1_FE_CONST(0x0aeaa886, 0xf6b76c71, 0x58452418, 0xcbf5033a, 0xdc5747e9, 0xe9b5d3b2, 0x303db969, 0x36528557), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xba9ab867, 0x131f8e45, 0x86d792fb, 0x080c14e3, 0xc0e2e822, 0x77c9ef0d, 0x52d1027c, 0x5d78b5c4), SECP256K1_FE_CONST(0xf5155779, 0x0948938e, 0xa7badbe7, 0x340afcc5, 0x23a8b816, 0x164a2c4d, 0xcfc24695, 0xc9ad76d8)}}, + {0x33, SECP256K1_FE_CONST(0x1737a85f, 0x4c8d146c, 0xec96e3ff, 0xdca76d99, 0x03dcf3bd, 0x53061868, 0xd478c78c, 0x63c2aa9e), SECP256K1_FE_CONST(0x39e48dd1, 0x50d2f429, 0xbe088dfd, 0x5b61882e, 0x7e840748, 0x3702ae9a, 0x5ab35927, 0xb15f85ea), {SECP256K1_FE_CONST(0x1be8cc0b, 0x04be0c68, 0x1d0c6a68, 0xf733f82c, 0x6c896e0c, 0x8a262fcd, 0x392918e3, 0x03a7abf4), SECP256K1_FE_CONST(0x605b5814, 0xbf9b8cb0, 0x66667c9e, 0x5480d22d, 0xc5b6c92f, 0x14b4af3e, 0xe0a9eb83, 0xb03685e3), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xe41733f4, 0xfb41f397, 0xe2f39597, 0x08cc07d3, 0x937691f3, 0x75d9d032, 0xc6d6e71b, 0xfc58503b), SECP256K1_FE_CONST(0x9fa4a7eb, 0x4064734f, 0x99998361, 0xab7f2dd2, 0x3a4936d0, 0xeb4b50c1, 0x1f56147b, 0x4fc9764c), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0x1aaa1cce, 0xbf9c7241, 0x91033df3, 0x66b36f69, 0x1c4d902c, 0x228033ff, 0x4516d122, 0xb2564f68), SECP256K1_FE_CONST(0xc7554125, 0x9d3ba98f, 0x207eaa30, 0xc69634d1, 0x87d0b6da, 0x594e719e, 0x420f4898, 0x638fc5b0), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0x2323a1d0, 0x79b0fd72, 0xfc8bb62e, 0xc34230a8, 0x15cb0596, 0xc2bfac99, 0x8bd6b842, 0x60f5dc26), SECP256K1_FE_CONST(0x239342df, 0xb675500a, 0x34a19631, 0x0b8d87d5, 0x4f49dcac, 0x9da50c17, 0x43ceab41, 0xa7b249ff), {SECP256K1_FE_CONST(0xf63580b8, 0xaa49c484, 0x6de56e39, 0xe1b3e73f, 0x171e881e, 0xba8c66f6, 0x14e67e5c, 0x975dfc07), SECP256K1_FE_CONST(0xb6307b33, 0x2e699f1c, 0xf77841d9, 0x0af25365, 0x404deb7f, 0xed5edb30, 0x90db49e6, 0x42a156b6), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x09ca7f47, 0x55b63b7b, 0x921a91c6, 0x1e4c18c0, 0xe8e177e1, 0x45739909, 0xeb1981a2, 0x68a20028), SECP256K1_FE_CONST(0x49cf84cc, 0xd19660e3, 0x0887be26, 0xf50dac9a, 0xbfb21480, 0x12a124cf, 0x6f24b618, 0xbd5ea579), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0x2dc90e64, 0x0cb646ae, 0x9164c0b5, 0xa9ef0169, 0xfebe34dc, 0x4437d6e4, 0x6acb0e27, 0xe219d1e8), SECP256K1_FE_CONST(0xd236f19b, 0xf349b951, 0x6e9b3f4a, 0x5610fe96, 0x0141cb23, 0xbbc8291b, 0x9534f1d7, 0x1de62a47), {SECP256K1_FE_CONST(0xe69df7d9, 0xc026c366, 0x00ebdf58, 0x80726758, 0x47c0c431, 0xc8eb7306, 0x82533e96, 0x4b6252c9), SECP256K1_FE_CONST(0x4f18bbdf, 0x7c2d6c5f, 0x818c1880, 0x2fa35cd0, 0x69eaa79f, 0xff74e4fc, 0x837c80d9, 0x3fece2f8), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x19620826, 0x3fd93c99, 0xff1420a7, 0x7f8d98a7, 0xb83f3bce, 0x37148cf9, 0x7dacc168, 0xb49da966), SECP256K1_FE_CONST(0xb0e74420, 0x83d293a0, 0x7e73e77f, 0xd05ca32f, 0x96155860, 0x008b1b03, 0x7c837f25, 0xc0131937), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0x3edd7b39, 0x80e2f2f3, 0x4d1409a2, 0x07069f88, 0x1fda5f96, 0xf08027ac, 0x4465b63d, 0xc278d672), SECP256K1_FE_CONST(0x053a98de, 0x4a27b196, 0x1155822b, 0x3a3121f0, 0x3b2a1445, 0x8bd80eb4, 0xa560c4c7, 0xa85c149c), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb3dae4b7, 0xdcf858e4, 0xc6968057, 0xcef2b156, 0x46543152, 0x6538199c, 0xf52dc1b2, 0xd62fda30), SECP256K1_FE_CONST(0x4aa77dd5, 0x5d6b6d3c, 0xfa10cc9d, 0x0fe42f79, 0x232e4575, 0x661049ae, 0x36779c1d, 0x0c666d88), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x4c251b48, 0x2307a71b, 0x39697fa8, 0x310d4ea9, 0xb9abcead, 0x9ac7e663, 0x0ad23e4c, 0x29d021ff), SECP256K1_FE_CONST(0xb558822a, 0xa29492c3, 0x05ef3362, 0xf01bd086, 0xdcd1ba8a, 0x99efb651, 0xc98863e1, 0xf3998ea7)}}, + {0x00, SECP256K1_FE_CONST(0x4295737e, 0xfcb1da6f, 0xb1d96b9c, 0xa7dcd1e3, 0x20024b37, 0xa736c494, 0x8b625981, 0x73069f70), SECP256K1_FE_CONST(0xfa7ffe4f, 0x25f88362, 0x831c087a, 0xfe2e8a9b, 0x0713e2ca, 0xc1ddca6a, 0x383205a2, 0x66f14307), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xff, SECP256K1_FE_CONST(0x587c1a0c, 0xee91939e, 0x7f784d23, 0xb963004a, 0x3bf44f5d, 0x4e32a008, 0x1995ba20, 0xb0fca59e), SECP256K1_FE_CONST(0x2ea98853, 0x0715e8d1, 0x0363907f, 0xf2512452, 0x4d471ba2, 0x454d5ce3, 0xbe3f0419, 0x4dfd3a3c), {SECP256K1_FE_CONST(0xcfd5a094, 0xaa0b9b88, 0x91b76c6a, 0xb9438f66, 0xaa1c095a, 0x65f9f701, 0x35e81712, 0x92245e74), SECP256K1_FE_CONST(0xa89057d7, 0xc6563f0d, 0x6efa19ae, 0x84412b8a, 0x7b47e791, 0xa191ecdf, 0xdf2af84f, 0xd97bc339), SECP256K1_FE_CONST(0x475d0ae9, 0xef46920d, 0xf07b3411, 0x7be5a081, 0x7de1023e, 0x3cc32689, 0xe9be145b, 0x406b0aef), SECP256K1_FE_CONST(0xa0759178, 0xad802324, 0x54f827ef, 0x05ea3e72, 0xad8d7541, 0x8e6d4cc1, 0xcd4f5306, 0xc5e7c453), SECP256K1_FE_CONST(0x302a5f6b, 0x55f46477, 0x6e489395, 0x46bc7099, 0x55e3f6a5, 0x9a0608fe, 0xca17e8ec, 0x6ddb9dbb), SECP256K1_FE_CONST(0x576fa828, 0x39a9c0f2, 0x9105e651, 0x7bbed475, 0x84b8186e, 0x5e6e1320, 0x20d507af, 0x268438f6), SECP256K1_FE_CONST(0xb8a2f516, 0x10b96df2, 0x0f84cbee, 0x841a5f7e, 0x821efdc1, 0xc33cd976, 0x1641eba3, 0xbf94f140), SECP256K1_FE_CONST(0x5f8a6e87, 0x527fdcdb, 0xab07d810, 0xfa15c18d, 0x52728abe, 0x7192b33e, 0x32b0acf8, 0x3a1837dc)}}, + {0xcc, SECP256K1_FE_CONST(0x5fa88b33, 0x65a635cb, 0xbcee003c, 0xce9ef51d, 0xd1a310de, 0x277e441a, 0xbccdb7be, 0x1e4ba249), SECP256K1_FE_CONST(0x79461ff6, 0x2bfcbcac, 0x4249ba84, 0xdd040f2c, 0xec3c63f7, 0x25204dc7, 0xf464c16b, 0xf0ff3170), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x6bb700e1, 0xf4d7e236, 0xe8d193ff, 0x4a76c1b3, 0xbcd4e2b2, 0x5acac3d5, 0x1c8dac65, 0x3fe909a0), SECP256K1_FE_CONST(0xf4c73410, 0x633da7f6, 0x3a4f1d55, 0xaec6dd32, 0xc4c6d89e, 0xe74075ed, 0xb5515ed9, 0x0da9e683), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x9448ff1e, 0x0b281dc9, 0x172e6c00, 0xb5893e4c, 0x432b1d4d, 0xa5353c2a, 0xe3725399, 0xc016f28f), SECP256K1_FE_CONST(0x0b38cbef, 0x9cc25809, 0xc5b0e2aa, 0x513922cd, 0x3b392761, 0x18bf8a12, 0x4aaea125, 0xf25615ac)}}, + {0xcc, SECP256K1_FE_CONST(0x6fb31c75, 0x31f03130, 0xb42b155b, 0x952779ef, 0xbb46087d, 0xd9807d24, 0x1a48eac6, 0x3c3d96d6), SECP256K1_FE_CONST(0x56f81be7, 0x53e8d4ae, 0x4940ea6f, 0x46f6ec9f, 0xda66a6f9, 0x6cc95f50, 0x6cb2b574, 0x90e94260), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x59059774, 0x795bdb7a, 0x837fbe11, 0x40a5fa59, 0x984f48af, 0x8df95d57, 0xdd6d1c05, 0x437dcec1), SECP256K1_FE_CONST(0x22a644db, 0x79376ad4, 0xe7b3a009, 0xe58b3f13, 0x137c54fd, 0xf911122c, 0xc93667c4, 0x7077d784), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xa6fa688b, 0x86a42485, 0x7c8041ee, 0xbf5a05a6, 0x67b0b750, 0x7206a2a8, 0x2292e3f9, 0xbc822d6e), SECP256K1_FE_CONST(0xdd59bb24, 0x86c8952b, 0x184c5ff6, 0x1a74c0ec, 0xec83ab02, 0x06eeedd3, 0x36c9983a, 0x8f8824ab)}}, + {0x00, SECP256K1_FE_CONST(0x704cd226, 0xe71cb682, 0x6a590e80, 0xdac90f2d, 0x2f5830f0, 0xfdf135a3, 0xeae3965b, 0xff25ff12), SECP256K1_FE_CONST(0x138e0afa, 0x68936ee6, 0x70bd2b8d, 0xb53aedbb, 0x7bea2a85, 0x97388b24, 0xd0518edd, 0x22ad66ec), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0x725e9147, 0x92cb8c89, 0x49e7e116, 0x8b7cdd8a, 0x8094c91c, 0x6ec2202c, 0xcd53a6a1, 0x8771edeb), SECP256K1_FE_CONST(0x8da16eb8, 0x6d347376, 0xb6181ee9, 0x74832275, 0x7f6b36e3, 0x913ddfd3, 0x32ac595d, 0x788e0e44), {SECP256K1_FE_CONST(0xdd357786, 0xb9f68733, 0x30391aa5, 0x62580965, 0x4e43116e, 0x82a5a5d8, 0x2ffd1d66, 0x24101fc4), SECP256K1_FE_CONST(0xa0b7efca, 0x01814594, 0xc59c9aae, 0x8e497001, 0x86ca5d95, 0xe88bcc80, 0x399044d9, 0xc2d8613d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x22ca8879, 0x460978cc, 0xcfc6e55a, 0x9da7f69a, 0xb1bcee91, 0x7d5a5a27, 0xd002e298, 0xdbefdc6b), SECP256K1_FE_CONST(0x5f481035, 0xfe7eba6b, 0x3a636551, 0x71b68ffe, 0x7935a26a, 0x1774337f, 0xc66fbb25, 0x3d279af2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0x78fe6b71, 0x7f2ea4a3, 0x2708d79c, 0x151bf503, 0xa5312a18, 0xc0963437, 0xe865cc6e, 0xd3f6ae97), SECP256K1_FE_CONST(0x8701948e, 0x80d15b5c, 0xd8f72863, 0xeae40afc, 0x5aced5e7, 0x3f69cbc8, 0x179a3390, 0x2c094d98), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x44, SECP256K1_FE_CONST(0x7c37bb9c, 0x5061dc07, 0x413f11ac, 0xd5a34006, 0xe64c5c45, 0x7fdb9a43, 0x8f217255, 0xa961f50d), SECP256K1_FE_CONST(0x5c1a76b4, 0x4568eb59, 0xd6789a74, 0x42d9ed7c, 0xdc6226b7, 0x752b4ff8, 0xeaf8e1a9, 0x5736e507), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb94d30cd, 0x7dbff60b, 0x64620c17, 0xca0fafaa, 0x40b3d1f5, 0x2d077a60, 0xa2e0cafd, 0x145086c2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x46b2cf32, 0x824009f4, 0x9b9df3e8, 0x35f05055, 0xbf4c2e0a, 0xd2f8859f, 0x5d1f3501, 0xebaf756d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0x82388888, 0x967f82a6, 0xb444438a, 0x7d44838e, 0x13c0d478, 0xb9ca060d, 0xa95a41fb, 0x94303de6), SECP256K1_FE_CONST(0x29e96541, 0x70628fec, 0x8b497289, 0x8b113cf9, 0x8807f460, 0x9274f4f3, 0x140d0674, 0x157c90a0), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0x91298f57, 0x70af7a27, 0xf0a47188, 0xd24c3b7b, 0xf98ab299, 0x0d84b0b8, 0x98507e3c, 0x561d6472), SECP256K1_FE_CONST(0x144f4ccb, 0xd9a74698, 0xa88cbf6f, 0xd00ad886, 0xd339d29e, 0xa19448f2, 0xc572cac0, 0xa07d5562), {SECP256K1_FE_CONST(0xe6a0ffa3, 0x807f09da, 0xdbe71e0f, 0x4be4725f, 0x2832e76c, 0xad8dc1d9, 0x43ce8393, 0x75eff248), SECP256K1_FE_CONST(0x837b8e68, 0xd4917544, 0x764ad090, 0x3cb11f86, 0x15d2823c, 0xefbb06d8, 0x9049dbab, 0xc69befda), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x195f005c, 0x7f80f625, 0x2418e1f0, 0xb41b8da0, 0xd7cd1893, 0x52723e26, 0xbc317c6b, 0x8a1009e7), SECP256K1_FE_CONST(0x7c847197, 0x2b6e8abb, 0x89b52f6f, 0xc34ee079, 0xea2d7dc3, 0x1044f927, 0x6fb62453, 0x39640c55), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0xb682f3d0, 0x3bbb5dee, 0x4f54b5eb, 0xfba931b4, 0xf52f6a19, 0x1e5c2f48, 0x3c73c66e, 0x9ace97e1), SECP256K1_FE_CONST(0x904717bf, 0x0bc0cb78, 0x73fcdc38, 0xaa97f19e, 0x3a626309, 0x72acff92, 0xb24cc6dd, 0xa197cb96), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x77, SECP256K1_FE_CONST(0xc17ec69e, 0x665f0fb0, 0xdbab48d9, 0xc2f94d12, 0xec8a9d7e, 0xacb58084, 0x83309180, 0x1eb0b80b), SECP256K1_FE_CONST(0x147756e6, 0x6d96e31c, 0x426d3cc8, 0x5ed0c4cf, 0xbef6341d, 0xd8b28558, 0x5aa574ea, 0x0204b55e), {SECP256K1_FE_CONST(0x6f4aea43, 0x1a0043bd, 0xd03134d6, 0xd9159119, 0xce034b88, 0xc32e50e8, 0xe36c4ee4, 0x5eac7ae9), SECP256K1_FE_CONST(0xfd5be16d, 0x4ffa2690, 0x126c67c3, 0xef7cb9d2, 0x9b74d397, 0xc78b06b3, 0x605fda34, 0xdc9696a6), SECP256K1_FE_CONST(0x5e9c6079, 0x2a2f000e, 0x45c6250f, 0x296f875e, 0x174efc0e, 0x9703e628, 0x706103a9, 0xdd2d82c7), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x90b515bc, 0xe5ffbc42, 0x2fcecb29, 0x26ea6ee6, 0x31fcb477, 0x3cd1af17, 0x1c93b11a, 0xa1538146), SECP256K1_FE_CONST(0x02a41e92, 0xb005d96f, 0xed93983c, 0x1083462d, 0x648b2c68, 0x3874f94c, 0x9fa025ca, 0x23696589), SECP256K1_FE_CONST(0xa1639f86, 0xd5d0fff1, 0xba39daf0, 0xd69078a1, 0xe8b103f1, 0x68fc19d7, 0x8f9efc55, 0x22d27968), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0xc25172fc, 0x3f29b6fc, 0x4a1155b8, 0x57523315, 0x5486b274, 0x64b74b8b, 0x260b499a, 0x3f53cb14), SECP256K1_FE_CONST(0x1ea9cbdb, 0x35cf6e03, 0x29aa31b0, 0xbb0a702a, 0x65123ed0, 0x08655a93, 0xb7dcd528, 0x0e52e1ab), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x7422edc7, 0x843136af, 0x0053bb88, 0x54448a82, 0x99994f9d, 0xdcefd3a9, 0xa92d4546, 0x2c59298a), SECP256K1_FE_CONST(0x78c7774a, 0x266f8b97, 0xea23d05d, 0x064f033c, 0x77319f92, 0x3f6b78bc, 0xe4e20bf0, 0x5fa5398d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x8bdd1238, 0x7bcec950, 0xffac4477, 0xabbb757d, 0x6666b062, 0x23102c56, 0x56d2bab8, 0xd3a6d2a5), SECP256K1_FE_CONST(0x873888b5, 0xd9907468, 0x15dc2fa2, 0xf9b0fcc3, 0x88ce606d, 0xc0948743, 0x1b1df40e, 0xa05ac2a2)}}, + {0x00, SECP256K1_FE_CONST(0xcab6626f, 0x832a4b12, 0x80ba7add, 0x2fc5322f, 0xf011caed, 0xedf7ff4d, 0xb6735d50, 0x26dc0367), SECP256K1_FE_CONST(0x2b2bef08, 0x52c6f7c9, 0x5d72ac99, 0xa23802b8, 0x75029cd5, 0x73b248d1, 0xf1b3fc80, 0x33788eb6), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0xd8621b4f, 0xfc85b9ed, 0x56e99d8d, 0xd1dd24ae, 0xdcecb147, 0x63b861a1, 0x7112dc77, 0x1a104fd2), SECP256K1_FE_CONST(0x812cabe9, 0x72a22aa6, 0x7c7da0c9, 0x4d8a9362, 0x96eb9949, 0xd70c37cb, 0x2b248757, 0x4cb3ce58), {SECP256K1_FE_CONST(0xfbc5febc, 0x6fdbc9ae, 0x3eb88a93, 0xb982196e, 0x8b6275a6, 0xd5a73c17, 0x387e000c, 0x711bd0e3), SECP256K1_FE_CONST(0x8724c96b, 0xd4e5527f, 0x2dd195a5, 0x1c468d2d, 0x211ba2fa, 0xc7cbe0b4, 0xb3434253, 0x409fb42d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x043a0143, 0x90243651, 0xc147756c, 0x467de691, 0x749d8a59, 0x2a58c3e8, 0xc781fff2, 0x8ee42b4c), SECP256K1_FE_CONST(0x78db3694, 0x2b1aad80, 0xd22e6a5a, 0xe3b972d2, 0xdee45d05, 0x38341f4b, 0x4cbcbdab, 0xbf604802), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0xda463164, 0xc6f4bf71, 0x29ee5f0e, 0xc00f65a6, 0x75a8adf1, 0xbd931b39, 0xb64806af, 0xdcda9a22), SECP256K1_FE_CONST(0x25b9ce9b, 0x390b408e, 0xd611a0f1, 0x3ff09a59, 0x8a57520e, 0x426ce4c6, 0x49b7f94f, 0x2325620d), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0xdafc971e, 0x4a3a7b6d, 0xcfb42a08, 0xd9692d82, 0xad9e7838, 0x523fcbda, 0x1d4827e1, 0x4481ae2d), SECP256K1_FE_CONST(0x250368e1, 0xb5c58492, 0x304bd5f7, 0x2696d27d, 0x526187c7, 0xadc03425, 0xe2b7d81d, 0xbb7e4e02), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x370c28f1, 0xbe665efa, 0xcde6aa43, 0x6bf86fe2, 0x1e6e314c, 0x1e53dd04, 0x0e6c73a4, 0x6b4c8c49), SECP256K1_FE_CONST(0xcd8acee9, 0x8ffe5653, 0x1a84d7eb, 0x3e48fa40, 0x34206ce8, 0x25ace907, 0xd0edf0ea, 0xeb5e9ca2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xc8f3d70e, 0x4199a105, 0x321955bc, 0x9407901d, 0xe191ceb3, 0xe1ac22fb, 0xf1938c5a, 0x94b36fe6), SECP256K1_FE_CONST(0x32753116, 0x7001a9ac, 0xe57b2814, 0xc1b705bf, 0xcbdf9317, 0xda5316f8, 0x2f120f14, 0x14a15f8d)}}, + {0x44, SECP256K1_FE_CONST(0xe0294c8b, 0xc1a36b41, 0x66ee92bf, 0xa70a5c34, 0x976fa982, 0x9405efea, 0x8f9cd54d, 0xcb29b99e), SECP256K1_FE_CONST(0xae9690d1, 0x3b8d20a0, 0xfbbf37be, 0xd8474f67, 0xa04e142f, 0x56efd787, 0x70a76b35, 0x9165d8a1), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xdcd45d93, 0x5613916a, 0xf167b029, 0x058ba3a7, 0x00d37150, 0xb9df3472, 0x8cb05412, 0xc16d4182), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x232ba26c, 0xa9ec6e95, 0x0e984fd6, 0xfa745c58, 0xff2c8eaf, 0x4620cb8d, 0x734fabec, 0x3e92baad), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0xe148441c, 0xd7b92b8b, 0x0e4fa3bd, 0x68712cfd, 0x0d709ad1, 0x98cace61, 0x1493c10e, 0x97f5394e), SECP256K1_FE_CONST(0x164a6397, 0x94d74c53, 0xafc4d329, 0x4e79cdb3, 0xcd25f99f, 0x6df45c00, 0x0f758aba, 0x54d699c0), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xff, SECP256K1_FE_CONST(0xe4b00ec9, 0x7aadcca9, 0x7644d3b0, 0xc8a931b1, 0x4ce7bcf7, 0xbc877954, 0x6d6e35aa, 0x5937381c), SECP256K1_FE_CONST(0x94e9588d, 0x41647b3f, 0xcc772dc8, 0xd83c67ce, 0x3be00353, 0x8517c834, 0x103d2cd4, 0x9d62ef4d), {SECP256K1_FE_CONST(0xc88d25f4, 0x1407376b, 0xb2c03a7f, 0xffeb3ec7, 0x811cc434, 0x91a0c3aa, 0xc0378cdc, 0x78357bee), SECP256K1_FE_CONST(0x51c02636, 0xce00c234, 0x5ecd89ad, 0xb6089fe4, 0xd5e18ac9, 0x24e3145e, 0x6669501c, 0xd37a00d4), SECP256K1_FE_CONST(0x205b3512, 0xdb40521c, 0xb200952e, 0x67b46f67, 0xe09e7839, 0xe0de4400, 0x4138329e, 0xbd9138c5), SECP256K1_FE_CONST(0x58aab390, 0xab6fb55c, 0x1d1b8089, 0x7a207ce9, 0x4a78fa5b, 0x4aa61a33, 0x398bcae9, 0xadb20d3e), SECP256K1_FE_CONST(0x3772da0b, 0xebf8c894, 0x4d3fc580, 0x0014c138, 0x7ee33bcb, 0x6e5f3c55, 0x3fc87322, 0x87ca8041), SECP256K1_FE_CONST(0xae3fd9c9, 0x31ff3dcb, 0xa1327652, 0x49f7601b, 0x2a1e7536, 0xdb1ceba1, 0x9996afe2, 0x2c85fb5b), SECP256K1_FE_CONST(0xdfa4caed, 0x24bfade3, 0x4dff6ad1, 0x984b9098, 0x1f6187c6, 0x1f21bbff, 0xbec7cd60, 0x426ec36a), SECP256K1_FE_CONST(0xa7554c6f, 0x54904aa3, 0xe2e47f76, 0x85df8316, 0xb58705a4, 0xb559e5cc, 0xc6743515, 0x524deef1)}}, + {0x00, SECP256K1_FE_CONST(0xe5bbb9ef, 0x360d0a50, 0x1618f006, 0x7d36dceb, 0x75f5be9a, 0x620232aa, 0x9fd5139d, 0x0863fde5), SECP256K1_FE_CONST(0xe5bbb9ef, 0x360d0a50, 0x1618f006, 0x7d36dceb, 0x75f5be9a, 0x620232aa, 0x9fd5139d, 0x0863fde5), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xff, SECP256K1_FE_CONST(0xe6bcb5c3, 0xd63467d4, 0x90bfa54f, 0xbbc6092a, 0x7248c25e, 0x11b248dc, 0x2964a6e1, 0x5edb1457), SECP256K1_FE_CONST(0x19434a3c, 0x29cb982b, 0x6f405ab0, 0x4439f6d5, 0x8db73da1, 0xee4db723, 0xd69b591d, 0xa124e7d8), {SECP256K1_FE_CONST(0x67119877, 0x832ab8f4, 0x59a82165, 0x6d8261f5, 0x44a553b8, 0x9ae4f25c, 0x52a97134, 0xb70f3426), SECP256K1_FE_CONST(0xffee02f5, 0xe649c07f, 0x0560eff1, 0x867ec7b3, 0x2d0e595e, 0x9b1c0ea6, 0xe2a4fc70, 0xc97cd71f), SECP256K1_FE_CONST(0xb5e0c189, 0xeb5b4bac, 0xd025b744, 0x4d74178b, 0xe8d5246c, 0xfa4a9a20, 0x7964a057, 0xee969992), SECP256K1_FE_CONST(0x5746e459, 0x1bf7f4c3, 0x044609ea, 0x372e9086, 0x03975d27, 0x9fdef834, 0x9f0b08d3, 0x2f07619d), SECP256K1_FE_CONST(0x98ee6788, 0x7cd5470b, 0xa657de9a, 0x927d9e0a, 0xbb5aac47, 0x651b0da3, 0xad568eca, 0x48f0c809), SECP256K1_FE_CONST(0x0011fd0a, 0x19b63f80, 0xfa9f100e, 0x7981384c, 0xd2f1a6a1, 0x64e3f159, 0x1d5b038e, 0x36832510), SECP256K1_FE_CONST(0x4a1f3e76, 0x14a4b453, 0x2fda48bb, 0xb28be874, 0x172adb93, 0x05b565df, 0x869b5fa7, 0x1169629d), SECP256K1_FE_CONST(0xa8b91ba6, 0xe4080b3c, 0xfbb9f615, 0xc8d16f79, 0xfc68a2d8, 0x602107cb, 0x60f4f72b, 0xd0f89a92)}}, + {0x33, SECP256K1_FE_CONST(0xf28fba64, 0xaf766845, 0xeb2f4302, 0x456e2b9f, 0x8d80affe, 0x57e7aae4, 0x2738d7cd, 0xdb1c2ce6), SECP256K1_FE_CONST(0xf28fba64, 0xaf766845, 0xeb2f4302, 0x456e2b9f, 0x8d80affe, 0x57e7aae4, 0x2738d7cd, 0xdb1c2ce6), {SECP256K1_FE_CONST(0x4f867ad8, 0xbb3d8404, 0x09d26b67, 0x307e6210, 0x0153273f, 0x72fa4b74, 0x84becfa1, 0x4ebe7408), SECP256K1_FE_CONST(0x5bbc4f59, 0xe452cc5f, 0x22a99144, 0xb10ce898, 0x9a89a995, 0xec3cea1c, 0x91ae10e8, 0xf721bb5d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb0798527, 0x44c27bfb, 0xf62d9498, 0xcf819def, 0xfeacd8c0, 0x8d05b48b, 0x7b41305d, 0xb1418827), SECP256K1_FE_CONST(0xa443b0a6, 0x1bad33a0, 0xdd566ebb, 0x4ef31767, 0x6576566a, 0x13c315e3, 0x6e51ef16, 0x08de40d2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0xf455605b, 0xc85bf48e, 0x3a908c31, 0x023faf98, 0x381504c6, 0xc6d3aeb9, 0xede55f8d, 0xd528924d), SECP256K1_FE_CONST(0xd31fbcd5, 0xcdb798f6, 0xc00db669, 0x2f8fe896, 0x7fa9c79d, 0xd10958f4, 0xa194f013, 0x74905e99), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x0c00c571, 0x5b56fe63, 0x2d814ad8, 0xa77f8e66, 0x628ea47a, 0x6116834f, 0x8c1218f3, 0xa03cbd50), SECP256K1_FE_CONST(0xdf88e44f, 0xac84fa52, 0xdf4d59f4, 0x8819f18f, 0x6a8cd415, 0x1d162afa, 0xf773166f, 0x57c7ff46), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xf3ff3a8e, 0xa4a9019c, 0xd27eb527, 0x58807199, 0x9d715b85, 0x9ee97cb0, 0x73ede70b, 0x5fc33edf), SECP256K1_FE_CONST(0x20771bb0, 0x537b05ad, 0x20b2a60b, 0x77e60e70, 0x95732bea, 0xe2e9d505, 0x088ce98f, 0xa837fce9)}}, + {0xff, SECP256K1_FE_CONST(0xf58cd4d9, 0x830bad32, 0x2699035e, 0x8246007d, 0x4be27e19, 0xb6f53621, 0x317b4f30, 0x9b3daa9d), SECP256K1_FE_CONST(0x78ec2b3d, 0xc0948de5, 0x60148bbc, 0x7c6dc963, 0x3ad5df70, 0xa5a5750c, 0xbed72180, 0x4f082a3b), {SECP256K1_FE_CONST(0x6c4c580b, 0x76c75940, 0x43569f9d, 0xae16dc28, 0x01c16a1f, 0xbe128608, 0x81b75f8e, 0xf929bce5), SECP256K1_FE_CONST(0x94231355, 0xe7385c5f, 0x25ca436a, 0xa6419147, 0x1aea4393, 0xd6e86ab7, 0xa35fe2af, 0xacaefd0d), SECP256K1_FE_CONST(0xdff2a195, 0x1ada6db5, 0x74df8340, 0x48149da3, 0x397a75b8, 0x29abf58c, 0x7e69db1b, 0x41ac0989), SECP256K1_FE_CONST(0xa52b66d3, 0xc9070355, 0x48028bf8, 0x04711bf4, 0x22aba95f, 0x1a666fc8, 0x6f4648e0, 0x5f29caae), SECP256K1_FE_CONST(0x93b3a7f4, 0x8938a6bf, 0xbca96062, 0x51e923d7, 0xfe3e95e0, 0x41ed79f7, 0x7e48a070, 0x06d63f4a), SECP256K1_FE_CONST(0x6bdcecaa, 0x18c7a3a0, 0xda35bc95, 0x59be6eb8, 0xe515bc6c, 0x29179548, 0x5ca01d4f, 0x5350ff22), SECP256K1_FE_CONST(0x200d5e6a, 0xe525924a, 0x8b207cbf, 0xb7eb625c, 0xc6858a47, 0xd6540a73, 0x819624e3, 0xbe53f2a6), SECP256K1_FE_CONST(0x5ad4992c, 0x36f8fcaa, 0xb7fd7407, 0xfb8ee40b, 0xdd5456a0, 0xe5999037, 0x90b9b71e, 0xa0d63181)}}, + {0x00, SECP256K1_FE_CONST(0xfd7d912a, 0x40f182a3, 0x588800d6, 0x9ebfb504, 0x8766da20, 0x6fd7ebc8, 0xd2436c81, 0xcbef6421), SECP256K1_FE_CONST(0x8d37c862, 0x054debe7, 0x31694536, 0xff46b273, 0xec122b35, 0xa9bf1445, 0xac3c4ff9, 0xf262c952), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, +}; + +/* Set of (encoding, xcoord) test vectors, selected to maximize branch coverage, part of the BIP324 + * test vectors. Created using an independent implementation, and tested decoding against the paper + * authors' code. */ +static const struct ellswift_decode_test ellswift_decode_tests[] = { + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd3, 0x47, 0x5b, 0xf7, 0x65, 0x5b, 0x0f, 0xb2, 0xd8, 0x52, 0x92, 0x10, 0x35, 0xb2, 0xef, 0x60, 0x7f, 0x49, 0x06, 0x9b, 0x97, 0x45, 0x4e, 0x67, 0x95, 0x25, 0x10, 0x62, 0x74, 0x17, 0x71}, SECP256K1_FE_CONST(0xb5da00b7, 0x3cd65605, 0x20e7c364, 0x086e7cd2, 0x3a34bf60, 0xd0e707be, 0x9fc34d4c, 0xd5fdfa2c), 1}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x27, 0x7c, 0x4a, 0x71, 0xf9, 0xd2, 0x2e, 0x66, 0xec, 0xe5, 0x23, 0xf8, 0xfa, 0x08, 0x74, 0x1a, 0x7c, 0x09, 0x12, 0xc6, 0x6a, 0x69, 0xce, 0x68, 0x51, 0x4b, 0xfd, 0x35, 0x15, 0xb4, 0x9f}, SECP256K1_FE_CONST(0xf482f2e2, 0x41753ad0, 0xfb89150d, 0x8491dc1e, 0x34ff0b8a, 0xcfbb442c, 0xfe999e2e, 0x5e6fd1d2), 1}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x21, 0xcc, 0x93, 0x0e, 0x77, 0xc9, 0xf5, 0x14, 0xb6, 0x91, 0x5c, 0x3d, 0xbe, 0x2a, 0x94, 0xc6, 0xd8, 0xf6, 0x90, 0xb5, 0xb7, 0x39, 0x86, 0x4b, 0xa6, 0x78, 0x9f, 0xb8, 0xa5, 0x5d, 0xd0}, SECP256K1_FE_CONST(0x9f59c402, 0x75f5085a, 0x006f05da, 0xe77eb98c, 0x6fd0db1a, 0xb4a72ac4, 0x7eae90a4, 0xfc9e57e0), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb, 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41}, SECP256K1_FE_CONST(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaa9, 0xfffffd6b), 1}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x9c, 0x18, 0x2d, 0x27, 0x59, 0xcd, 0x99, 0x82, 0x42, 0x28, 0xd9, 0x47, 0x99, 0xf8, 0xc6, 0x55, 0x7c, 0x38, 0xa1, 0xc0, 0xd6, 0x77, 0x9b, 0x9d, 0x4b, 0x72, 0x9c, 0x6f, 0x1c, 0xcc, 0x42}, SECP256K1_FE_CONST(0x70720db7, 0xe238d041, 0x21f5b1af, 0xd8cc5ad9, 0xd18944c6, 0xbdc94881, 0xf502b7a3, 0xaf3aecff), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x64, 0xbb, 0xd5}, SECP256K1_FE_CONST(0x50873db3, 0x1badcc71, 0x890e4f67, 0x753a6575, 0x7f97aaa7, 0xdd5f1e82, 0xb753ace3, 0x2219064b), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x28, 0xde, 0x7d}, SECP256K1_FE_CONST(0x1eea9cc5, 0x9cfcf2fa, 0x151ac6c2, 0x74eea411, 0x0feb4f7b, 0x68c59657, 0x32e9992e, 0x976ef68e), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xcf, 0xb7, 0xe7}, SECP256K1_FE_CONST(0x12303941, 0xaedc2088, 0x80735b1f, 0x1795c8e5, 0x5be520ea, 0x93e10335, 0x7b5d2adb, 0x7ed59b8e), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x11, 0x3a, 0xd9}, SECP256K1_FE_CONST(0x7eed6b70, 0xe7b0767c, 0x7d7feac0, 0x4e57aa2a, 0x12fef5e0, 0xf48f878f, 0xcbb88b3b, 0x6b5e0783), 0}, + {{0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c, 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x532167c1, 0x1200b08c, 0x0e84a354, 0xe74dcc40, 0xf8b25f4f, 0xe686e308, 0x69526366, 0x278a0688), 0}, + {{0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c, 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x532167c1, 0x1200b08c, 0x0e84a354, 0xe74dcc40, 0xf8b25f4f, 0xe686e308, 0x69526366, 0x278a0688), 0}, + {{0x0f, 0xfd, 0xe9, 0xca, 0x81, 0xd7, 0x51, 0xe9, 0xcd, 0xaf, 0xfc, 0x1a, 0x50, 0x77, 0x92, 0x45, 0x32, 0x0b, 0x28, 0x99, 0x6d, 0xba, 0xf3, 0x2f, 0x82, 0x2f, 0x20, 0x11, 0x7c, 0x22, 0xfb, 0xd6, 0xc7, 0x4d, 0x99, 0xef, 0xce, 0xaa, 0x55, 0x0f, 0x1a, 0xd1, 0xc0, 0xf4, 0x3f, 0x46, 0xe7, 0xff, 0x1e, 0xe3, 0xbd, 0x01, 0x62, 0xb7, 0xbf, 0x55, 0xf2, 0x96, 0x5d, 0xa9, 0xc3, 0x45, 0x06, 0x46}, SECP256K1_FE_CONST(0x74e880b3, 0xffd18fe3, 0xcddf7902, 0x522551dd, 0xf97fa4a3, 0x5a3cfda8, 0x197f9470, 0x81a57b8f), 0}, + {{0x0f, 0xfd, 0xe9, 0xca, 0x81, 0xd7, 0x51, 0xe9, 0xcd, 0xaf, 0xfc, 0x1a, 0x50, 0x77, 0x92, 0x45, 0x32, 0x0b, 0x28, 0x99, 0x6d, 0xba, 0xf3, 0x2f, 0x82, 0x2f, 0x20, 0x11, 0x7c, 0x22, 0xfb, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x6c, 0xa8, 0x96}, SECP256K1_FE_CONST(0x377b643f, 0xce2271f6, 0x4e5c8101, 0x566107c1, 0xbe498074, 0x50917838, 0x04f65478, 0x1ac9217c), 1}, + {{0x12, 0x36, 0x58, 0x44, 0x4f, 0x32, 0xbe, 0x8f, 0x02, 0xea, 0x20, 0x34, 0xaf, 0xa7, 0xef, 0x4b, 0xbe, 0x8a, 0xdc, 0x91, 0x8c, 0xeb, 0x49, 0xb1, 0x27, 0x73, 0xb6, 0x25, 0xf4, 0x90, 0xb3, 0x68, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8d, 0xc5, 0xfe, 0x11}, SECP256K1_FE_CONST(0xed16d65c, 0xf3a9538f, 0xcb2c139f, 0x1ecbc143, 0xee148271, 0x20cbc265, 0x9e667256, 0x800b8142), 0}, + {{0x14, 0x6f, 0x92, 0x46, 0x4d, 0x15, 0xd3, 0x6e, 0x35, 0x38, 0x2b, 0xd3, 0xca, 0x5b, 0x0f, 0x97, 0x6c, 0x95, 0xcb, 0x08, 0xac, 0xdc, 0xf2, 0xd5, 0xb3, 0x57, 0x06, 0x17, 0x99, 0x08, 0x39, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x31, 0x45, 0xe9, 0x3b}, SECP256K1_FE_CONST(0x0d5cd840, 0x427f941f, 0x65193079, 0xab8e2e83, 0x024ef2ee, 0x7ca558d8, 0x8879ffd8, 0x79fb6657), 0}, + {{0x15, 0xfd, 0xf5, 0xcf, 0x09, 0xc9, 0x07, 0x59, 0xad, 0xd2, 0x27, 0x2d, 0x57, 0x4d, 0x2b, 0xb5, 0xfe, 0x14, 0x29, 0xf9, 0xf3, 0xc1, 0x4c, 0x65, 0xe3, 0x19, 0x4b, 0xf6, 0x1b, 0x82, 0xaa, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0xcf, 0xd9, 0x06}, SECP256K1_FE_CONST(0x16d0e439, 0x46aec93f, 0x62d57eb8, 0xcde68951, 0xaf136cf4, 0xb307938d, 0xd1447411, 0xe07bffe1), 1}, + {{0x1f, 0x67, 0xed, 0xf7, 0x79, 0xa8, 0xa6, 0x49, 0xd6, 0xde, 0xf6, 0x00, 0x35, 0xf2, 0xfa, 0x22, 0xd0, 0x22, 0xdd, 0x35, 0x90, 0x79, 0xa1, 0xa1, 0x44, 0x07, 0x3d, 0x84, 0xf1, 0x9b, 0x92, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x025661f9, 0xaba9d15c, 0x3118456b, 0xbe980e3e, 0x1b8ba2e0, 0x47c737a4, 0xeb48a040, 0xbb566f6c), 0}, + {{0x1f, 0x67, 0xed, 0xf7, 0x79, 0xa8, 0xa6, 0x49, 0xd6, 0xde, 0xf6, 0x00, 0x35, 0xf2, 0xfa, 0x22, 0xd0, 0x22, 0xdd, 0x35, 0x90, 0x79, 0xa1, 0xa1, 0x44, 0x07, 0x3d, 0x84, 0xf1, 0x9b, 0x92, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x025661f9, 0xaba9d15c, 0x3118456b, 0xbe980e3e, 0x1b8ba2e0, 0x47c737a4, 0xeb48a040, 0xbb566f6c), 0}, + {{0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x98bec3b2, 0xa351fa96, 0xcfd191c1, 0x77835193, 0x1b9e9ba9, 0xad1149f6, 0xd9eadca8, 0x0981b801), 0}, + {{0x40, 0x56, 0xa3, 0x4a, 0x21, 0x0e, 0xec, 0x78, 0x92, 0xe8, 0x82, 0x06, 0x75, 0xc8, 0x60, 0x09, 0x9f, 0x85, 0x7b, 0x26, 0xaa, 0xd8, 0x54, 0x70, 0xee, 0x6d, 0x3c, 0xf1, 0x30, 0x4a, 0x9d, 0xcf, 0x37, 0x5e, 0x70, 0x37, 0x42, 0x71, 0xf2, 0x0b, 0x13, 0xc9, 0x98, 0x6e, 0xd7, 0xd3, 0xc1, 0x77, 0x99, 0x69, 0x8c, 0xfc, 0x43, 0x5d, 0xbe, 0xd3, 0xa9, 0xf3, 0x4b, 0x38, 0xc8, 0x23, 0xc2, 0xb4}, SECP256K1_FE_CONST(0x868aac20, 0x03b29dbc, 0xad1a3e80, 0x3855e078, 0xa89d1654, 0x3ac64392, 0xd1224172, 0x98cec76e), 0}, + {{0x41, 0x97, 0xec, 0x37, 0x23, 0xc6, 0x54, 0xcf, 0xdd, 0x32, 0xab, 0x07, 0x55, 0x06, 0x64, 0x8b, 0x2f, 0xf5, 0x07, 0x03, 0x62, 0xd0, 0x1a, 0x4f, 0xff, 0x14, 0xb3, 0x36, 0xb7, 0x8f, 0x96, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb3, 0xab, 0x1e, 0x95}, SECP256K1_FE_CONST(0xba5a6314, 0x502a8952, 0xb8f456e0, 0x85928105, 0xf665377a, 0x8ce27726, 0xa5b0eb7e, 0xc1ac0286), 0}, + {{0x47, 0xeb, 0x3e, 0x20, 0x8f, 0xed, 0xcd, 0xf8, 0x23, 0x4c, 0x94, 0x21, 0xe9, 0xcd, 0x9a, 0x7a, 0xe8, 0x73, 0xbf, 0xbd, 0xbc, 0x39, 0x37, 0x23, 0xd1, 0xba, 0x1e, 0x1e, 0x6a, 0x8e, 0x6b, 0x24, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7c, 0xd1, 0x2c, 0xb1}, SECP256K1_FE_CONST(0xd192d520, 0x07e541c9, 0x807006ed, 0x0468df77, 0xfd214af0, 0xa795fe11, 0x9359666f, 0xdcf08f7c), 0}, + {{0x5e, 0xb9, 0x69, 0x6a, 0x23, 0x36, 0xfe, 0x2c, 0x3c, 0x66, 0x6b, 0x02, 0xc7, 0x55, 0xdb, 0x4c, 0x0c, 0xfd, 0x62, 0x82, 0x5c, 0x7b, 0x58, 0x9a, 0x7b, 0x7b, 0xb4, 0x42, 0xe1, 0x41, 0xc1, 0xd6, 0x93, 0x41, 0x3f, 0x00, 0x52, 0xd4, 0x9e, 0x64, 0xab, 0xec, 0x6d, 0x58, 0x31, 0xd6, 0x6c, 0x43, 0x61, 0x28, 0x30, 0xa1, 0x7d, 0xf1, 0xfe, 0x43, 0x83, 0xdb, 0x89, 0x64, 0x68, 0x10, 0x02, 0x21}, SECP256K1_FE_CONST(0xef6e1da6, 0xd6c7627e, 0x80f7a723, 0x4cb08a02, 0x2c1ee1cf, 0x29e4d0f9, 0x642ae924, 0xcef9eb38), 1}, + {{0x7b, 0xf9, 0x6b, 0x7b, 0x6d, 0xa1, 0x5d, 0x34, 0x76, 0xa2, 0xb1, 0x95, 0x93, 0x4b, 0x69, 0x0a, 0x3a, 0x3d, 0xe3, 0xe8, 0xab, 0x84, 0x74, 0x85, 0x68, 0x63, 0xb0, 0xde, 0x3a, 0xf9, 0x0b, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x50851dfc, 0x9f418c31, 0x4a437295, 0xb24feeea, 0x27af3d0c, 0xd2308348, 0xfda6e21c, 0x463e46ff), 0}, + {{0x7b, 0xf9, 0x6b, 0x7b, 0x6d, 0xa1, 0x5d, 0x34, 0x76, 0xa2, 0xb1, 0x95, 0x93, 0x4b, 0x69, 0x0a, 0x3a, 0x3d, 0xe3, 0xe8, 0xab, 0x84, 0x74, 0x85, 0x68, 0x63, 0xb0, 0xde, 0x3a, 0xf9, 0x0b, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x50851dfc, 0x9f418c31, 0x4a437295, 0xb24feeea, 0x27af3d0c, 0xd2308348, 0xfda6e21c, 0x463e46ff), 0}, + {{0x85, 0x1b, 0x1c, 0xa9, 0x45, 0x49, 0x37, 0x1c, 0x4f, 0x1f, 0x71, 0x87, 0x32, 0x1d, 0x39, 0xbf, 0x51, 0xc6, 0xb7, 0xfb, 0x61, 0xf7, 0xcb, 0xf0, 0x27, 0xc9, 0xda, 0x62, 0x02, 0x1b, 0x7a, 0x65, 0xfc, 0x54, 0xc9, 0x68, 0x37, 0xfb, 0x22, 0xb3, 0x62, 0xed, 0xa6, 0x3e, 0xc5, 0x2e, 0xc8, 0x3d, 0x81, 0xbe, 0xdd, 0x16, 0x0c, 0x11, 0xb2, 0x2d, 0x96, 0x5d, 0x9f, 0x4a, 0x6d, 0x64, 0xd2, 0x51}, SECP256K1_FE_CONST(0x3e731051, 0xe12d3323, 0x7eb324f2, 0xaa5b16bb, 0x868eb49a, 0x1aa1fadc, 0x19b6e876, 0x1b5a5f7b), 1}, + {{0x94, 0x3c, 0x2f, 0x77, 0x51, 0x08, 0xb7, 0x37, 0xfe, 0x65, 0xa9, 0x53, 0x1e, 0x19, 0xf2, 0xfc, 0x2a, 0x19, 0x7f, 0x56, 0x03, 0xe3, 0xa2, 0x88, 0x1d, 0x1d, 0x83, 0xe4, 0x00, 0x8f, 0x91, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x311c61f0, 0xab2f32b7, 0xb1f0223f, 0xa72f0a78, 0x752b8146, 0xe46107f8, 0x876dd9c4, 0xf92b2942), 0}, + {{0x94, 0x3c, 0x2f, 0x77, 0x51, 0x08, 0xb7, 0x37, 0xfe, 0x65, 0xa9, 0x53, 0x1e, 0x19, 0xf2, 0xfc, 0x2a, 0x19, 0x7f, 0x56, 0x03, 0xe3, 0xa2, 0x88, 0x1d, 0x1d, 0x83, 0xe4, 0x00, 0x8f, 0x91, 0x25, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x311c61f0, 0xab2f32b7, 0xb1f0223f, 0xa72f0a78, 0x752b8146, 0xe46107f8, 0x876dd9c4, 0xf92b2942), 0}, + {{0xa0, 0xf1, 0x84, 0x92, 0x18, 0x3e, 0x61, 0xe8, 0x06, 0x3e, 0x57, 0x36, 0x06, 0x59, 0x14, 0x21, 0xb0, 0x6b, 0xc3, 0x51, 0x36, 0x31, 0x57, 0x8a, 0x73, 0xa3, 0x9c, 0x1c, 0x33, 0x06, 0x23, 0x9f, 0x2f, 0x32, 0x90, 0x4f, 0x0d, 0x2a, 0x33, 0xec, 0xca, 0x8a, 0x54, 0x51, 0x70, 0x5b, 0xb5, 0x37, 0xd3, 0xbf, 0x44, 0xe0, 0x71, 0x22, 0x60, 0x25, 0xcd, 0xbf, 0xd2, 0x49, 0xfe, 0x0f, 0x7a, 0xd6}, SECP256K1_FE_CONST(0x97a09cf1, 0xa2eae7c4, 0x94df3c6f, 0x8a9445bf, 0xb8c09d60, 0x832f9b0b, 0x9d5eabe2, 0x5fbd14b9), 0}, + {{0xa1, 0xed, 0x0a, 0x0b, 0xd7, 0x9d, 0x8a, 0x23, 0xcf, 0xe4, 0xec, 0x5f, 0xef, 0x5b, 0xa5, 0xcc, 0xcf, 0xd8, 0x44, 0xe4, 0xff, 0x5c, 0xb4, 0xb0, 0xf2, 0xe7, 0x16, 0x27, 0x34, 0x1f, 0x1c, 0x5b, 0x17, 0xc4, 0x99, 0x24, 0x9e, 0x0a, 0xc0, 0x8d, 0x5d, 0x11, 0xea, 0x1c, 0x2c, 0x8c, 0xa7, 0x00, 0x16, 0x16, 0x55, 0x9a, 0x79, 0x94, 0xea, 0xde, 0xc9, 0xca, 0x10, 0xfb, 0x4b, 0x85, 0x16, 0xdc}, SECP256K1_FE_CONST(0x65a89640, 0x744192cd, 0xac64b2d2, 0x1ddf989c, 0xdac75007, 0x25b645be, 0xf8e2200a, 0xe39691f2), 0}, + {{0xba, 0x94, 0x59, 0x4a, 0x43, 0x27, 0x21, 0xaa, 0x35, 0x80, 0xb8, 0x4c, 0x16, 0x1d, 0x0d, 0x13, 0x4b, 0xc3, 0x54, 0xb6, 0x90, 0x40, 0x4d, 0x7c, 0xd4, 0xec, 0x57, 0xc1, 0x6d, 0x3f, 0xbe, 0x98, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0x50, 0x7d, 0xd7}, SECP256K1_FE_CONST(0x5e0d7656, 0x4aae92cb, 0x347e01a6, 0x2afd389a, 0x9aa401c7, 0x6c8dd227, 0x543dc9cd, 0x0efe685a), 0}, + {{0xbc, 0xaf, 0x72, 0x19, 0xf2, 0xf6, 0xfb, 0xf5, 0x5f, 0xe5, 0xe0, 0x62, 0xdc, 0xe0, 0xe4, 0x8c, 0x18, 0xf6, 0x81, 0x03, 0xf1, 0x0b, 0x81, 0x98, 0xe9, 0x74, 0xc1, 0x84, 0x75, 0x0e, 0x1b, 0xe3, 0x93, 0x20, 0x16, 0xcb, 0xf6, 0x9c, 0x44, 0x71, 0xbd, 0x1f, 0x65, 0x6c, 0x6a, 0x10, 0x7f, 0x19, 0x73, 0xde, 0x4a, 0xf7, 0x08, 0x6d, 0xb8, 0x97, 0x27, 0x70, 0x60, 0xe2, 0x56, 0x77, 0xf1, 0x9a}, SECP256K1_FE_CONST(0x2d97f96c, 0xac882dfe, 0x73dc44db, 0x6ce0f1d3, 0x1d624135, 0x8dd5d74e, 0xb3d3b500, 0x03d24c2b), 0}, + {{0xbc, 0xaf, 0x72, 0x19, 0xf2, 0xf6, 0xfb, 0xf5, 0x5f, 0xe5, 0xe0, 0x62, 0xdc, 0xe0, 0xe4, 0x8c, 0x18, 0xf6, 0x81, 0x03, 0xf1, 0x0b, 0x81, 0x98, 0xe9, 0x74, 0xc1, 0x84, 0x75, 0x0e, 0x1b, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x65, 0x07, 0xd0, 0x9a}, SECP256K1_FE_CONST(0xe7008afe, 0x6e8cbd50, 0x55df120b, 0xd748757c, 0x686dadb4, 0x1cce75e4, 0xaddcc5e0, 0x2ec02b44), 1}, + {{0xc5, 0x98, 0x1b, 0xae, 0x27, 0xfd, 0x84, 0x40, 0x1c, 0x72, 0xa1, 0x55, 0xe5, 0x70, 0x7f, 0xbb, 0x81, 0x1b, 0x2b, 0x62, 0x06, 0x45, 0xd1, 0x02, 0x8e, 0xa2, 0x70, 0xcb, 0xe0, 0xee, 0x22, 0x5d, 0x4b, 0x62, 0xaa, 0x4d, 0xca, 0x65, 0x06, 0xc1, 0xac, 0xdb, 0xec, 0xc0, 0x55, 0x25, 0x69, 0xb4, 0xb2, 0x14, 0x36, 0xa5, 0x69, 0x2e, 0x25, 0xd9, 0x0d, 0x3b, 0xc2, 0xeb, 0x7c, 0xe2, 0x40, 0x78}, SECP256K1_FE_CONST(0x948b40e7, 0x181713bc, 0x018ec170, 0x2d3d054d, 0x15746c59, 0xa7020730, 0xdd13ecf9, 0x85a010d7), 0}, + {{0xc8, 0x94, 0xce, 0x48, 0xbf, 0xec, 0x43, 0x30, 0x14, 0xb9, 0x31, 0xa6, 0xad, 0x42, 0x26, 0xd7, 0xdb, 0xd8, 0xea, 0xa7, 0xb6, 0xe3, 0xfa, 0xa8, 0xd0, 0xef, 0x94, 0x05, 0x2b, 0xcf, 0x8c, 0xff, 0x33, 0x6e, 0xeb, 0x39, 0x19, 0xe2, 0xb4, 0xef, 0xb7, 0x46, 0xc7, 0xf7, 0x1b, 0xbc, 0xa7, 0xe9, 0x38, 0x32, 0x30, 0xfb, 0xbc, 0x48, 0xff, 0xaf, 0xe7, 0x7e, 0x8b, 0xcc, 0x69, 0x54, 0x24, 0x71}, SECP256K1_FE_CONST(0xf1c91acd, 0xc2525330, 0xf9b53158, 0x434a4d43, 0xa1c547cf, 0xf29f1550, 0x6f5da4eb, 0x4fe8fa5a), 1}, + {{0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x872d81ed, 0x8831d999, 0x8b67cb71, 0x05243edb, 0xf86c10ed, 0xfebb786c, 0x110b02d0, 0x7b2e67cd), 0}, + {{0xd9, 0x17, 0xb7, 0x86, 0xda, 0xc3, 0x56, 0x70, 0xc3, 0x30, 0xc9, 0xc5, 0xae, 0x59, 0x71, 0xdf, 0xb4, 0x95, 0xc8, 0xae, 0x52, 0x3e, 0xd9, 0x7e, 0xe2, 0x42, 0x01, 0x17, 0xb1, 0x71, 0xf4, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0x01, 0xf6, 0xf6}, SECP256K1_FE_CONST(0xe45b71e1, 0x10b831f2, 0xbdad8651, 0x994526e5, 0x8393fde4, 0x328b1ec0, 0x4d598971, 0x42584691), 1}, + {{0xe2, 0x8b, 0xd8, 0xf5, 0x92, 0x9b, 0x46, 0x7e, 0xb7, 0x0e, 0x04, 0x33, 0x23, 0x74, 0xff, 0xb7, 0xe7, 0x18, 0x02, 0x18, 0xad, 0x16, 0xea, 0xa4, 0x6b, 0x71, 0x61, 0xaa, 0x67, 0x9e, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x66b8c980, 0xa75c72e5, 0x98d383a3, 0x5a62879f, 0x844242ad, 0x1e73ff12, 0xedaa59f4, 0xe58632b5), 0}, + {{0xe2, 0x8b, 0xd8, 0xf5, 0x92, 0x9b, 0x46, 0x7e, 0xb7, 0x0e, 0x04, 0x33, 0x23, 0x74, 0xff, 0xb7, 0xe7, 0x18, 0x02, 0x18, 0xad, 0x16, 0xea, 0xa4, 0x6b, 0x71, 0x61, 0xaa, 0x67, 0x9e, 0xb4, 0x26, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x66b8c980, 0xa75c72e5, 0x98d383a3, 0x5a62879f, 0x844242ad, 0x1e73ff12, 0xedaa59f4, 0xe58632b5), 0}, + {{0xe7, 0xee, 0x58, 0x14, 0xc1, 0x70, 0x6b, 0xf8, 0xa8, 0x93, 0x96, 0xa9, 0xb0, 0x32, 0xbc, 0x01, 0x4c, 0x2c, 0xac, 0x9c, 0x12, 0x11, 0x27, 0xdb, 0xf6, 0xc9, 0x92, 0x78, 0xf8, 0xbb, 0x53, 0xd1, 0xdf, 0xd0, 0x4d, 0xbc, 0xda, 0x8e, 0x35, 0x24, 0x66, 0xb6, 0xfc, 0xd5, 0xf2, 0xde, 0xa3, 0xe1, 0x7d, 0x5e, 0x13, 0x31, 0x15, 0x88, 0x6e, 0xda, 0x20, 0xdb, 0x8a, 0x12, 0xb5, 0x4d, 0xe7, 0x1b}, SECP256K1_FE_CONST(0xe842c6e3, 0x529b2342, 0x70a5e977, 0x44edc34a, 0x04d7ba94, 0xe44b6d25, 0x23c9cf01, 0x95730a50), 1}, + {{0xf2, 0x92, 0xe4, 0x68, 0x25, 0xf9, 0x22, 0x5a, 0xd2, 0x3d, 0xc0, 0x57, 0xc1, 0xd9, 0x1c, 0x4f, 0x57, 0xfc, 0xb1, 0x38, 0x6f, 0x29, 0xef, 0x10, 0x48, 0x1c, 0xb1, 0xd2, 0x25, 0x18, 0x59, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x11, 0xc9, 0x89}, SECP256K1_FE_CONST(0x3cea2c53, 0xb8b01701, 0x66ac7da6, 0x7194694a, 0xdacc84d5, 0x6389225e, 0x330134da, 0xb85a4d55), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x01, 0xd3, 0x47, 0x5b, 0xf7, 0x65, 0x5b, 0x0f, 0xb2, 0xd8, 0x52, 0x92, 0x10, 0x35, 0xb2, 0xef, 0x60, 0x7f, 0x49, 0x06, 0x9b, 0x97, 0x45, 0x4e, 0x67, 0x95, 0x25, 0x10, 0x62, 0x74, 0x17, 0x71}, SECP256K1_FE_CONST(0xb5da00b7, 0x3cd65605, 0x20e7c364, 0x086e7cd2, 0x3a34bf60, 0xd0e707be, 0x9fc34d4c, 0xd5fdfa2c), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee}, SECP256K1_FE_CONST(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaa9, 0xfffffd6b), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x82, 0x27, 0x7c, 0x4a, 0x71, 0xf9, 0xd2, 0x2e, 0x66, 0xec, 0xe5, 0x23, 0xf8, 0xfa, 0x08, 0x74, 0x1a, 0x7c, 0x09, 0x12, 0xc6, 0x6a, 0x69, 0xce, 0x68, 0x51, 0x4b, 0xfd, 0x35, 0x15, 0xb4, 0x9f}, SECP256K1_FE_CONST(0xf482f2e2, 0x41753ad0, 0xfb89150d, 0x8491dc1e, 0x34ff0b8a, 0xcfbb442c, 0xfe999e2e, 0x5e6fd1d2), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x84, 0x21, 0xcc, 0x93, 0x0e, 0x77, 0xc9, 0xf5, 0x14, 0xb6, 0x91, 0x5c, 0x3d, 0xbe, 0x2a, 0x94, 0xc6, 0xd8, 0xf6, 0x90, 0xb5, 0xb7, 0x39, 0x86, 0x4b, 0xa6, 0x78, 0x9f, 0xb8, 0xa5, 0x5d, 0xd0}, SECP256K1_FE_CONST(0x9f59c402, 0x75f5085a, 0x006f05da, 0xe77eb98c, 0x6fd0db1a, 0xb4a72ac4, 0x7eae90a4, 0xfc9e57e0), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xd1, 0x9c, 0x18, 0x2d, 0x27, 0x59, 0xcd, 0x99, 0x82, 0x42, 0x28, 0xd9, 0x47, 0x99, 0xf8, 0xc6, 0x55, 0x7c, 0x38, 0xa1, 0xc0, 0xd6, 0x77, 0x9b, 0x9d, 0x4b, 0x72, 0x9c, 0x6f, 0x1c, 0xcc, 0x42}, SECP256K1_FE_CONST(0x70720db7, 0xe238d041, 0x21f5b1af, 0xd8cc5ad9, 0xd18944c6, 0xbdc94881, 0xf502b7a3, 0xaf3aecff), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x64, 0xbb, 0xd5}, SECP256K1_FE_CONST(0x50873db3, 0x1badcc71, 0x890e4f67, 0x753a6575, 0x7f97aaa7, 0xdd5f1e82, 0xb753ace3, 0x2219064b), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x28, 0xde, 0x7d}, SECP256K1_FE_CONST(0x1eea9cc5, 0x9cfcf2fa, 0x151ac6c2, 0x74eea411, 0x0feb4f7b, 0x68c59657, 0x32e9992e, 0x976ef68e), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xcf, 0xb7, 0xe7}, SECP256K1_FE_CONST(0x12303941, 0xaedc2088, 0x80735b1f, 0x1795c8e5, 0x5be520ea, 0x93e10335, 0x7b5d2adb, 0x7ed59b8e), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x11, 0x3a, 0xd9}, SECP256K1_FE_CONST(0x7eed6b70, 0xe7b0767c, 0x7d7feac0, 0x4e57aa2a, 0x12fef5e0, 0xf48f878f, 0xcbb88b3b, 0x6b5e0783), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13, 0xce, 0xa4, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x64998443, 0x5b62b4a2, 0x5d40c613, 0x3e8d9ab8, 0xc53d4b05, 0x9ee8a154, 0xa3be0fcf, 0x4e892edb), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13, 0xce, 0xa4, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x64998443, 0x5b62b4a2, 0x5d40c613, 0x3e8d9ab8, 0xc53d4b05, 0x9ee8a154, 0xa3be0fcf, 0x4e892edb), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x02, 0x8c, 0x59, 0x00, 0x63, 0xf6, 0x4d, 0x5a, 0x7f, 0x1c, 0x14, 0x91, 0x5c, 0xd6, 0x1e, 0xac, 0x88, 0x6a, 0xb2, 0x95, 0xbe, 0xbd, 0x91, 0x99, 0x25, 0x04, 0xcf, 0x77, 0xed, 0xb0, 0x28, 0xbd, 0xd6, 0x26, 0x7f}, SECP256K1_FE_CONST(0x3fde5713, 0xf8282eea, 0xd7d39d42, 0x01f44a7c, 0x85a5ac8a, 0x0681f35e, 0x54085c6b, 0x69543374), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x27, 0x15, 0xde, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x3524f77f, 0xa3a6eb43, 0x89c3cb5d, 0x27f1f914, 0x62086429, 0xcd6c0cb0, 0xdf43ea8f, 0x1e7b3fb4), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x27, 0x15, 0xde, 0x86, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x3524f77f, 0xa3a6eb43, 0x89c3cb5d, 0x27f1f914, 0x62086429, 0xcd6c0cb0, 0xdf43ea8f, 0x1e7b3fb4), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x2c, 0x57, 0x09, 0xe7, 0x15, 0x6c, 0x41, 0x77, 0x17, 0xf2, 0xfe, 0xab, 0x14, 0x71, 0x41, 0xec, 0x3d, 0xa1, 0x9f, 0xb7, 0x59, 0x57, 0x5c, 0xc6, 0xe3, 0x7b, 0x2e, 0xa5, 0xac, 0x93, 0x09, 0xf2, 0x6f, 0x0f, 0x66}, SECP256K1_FE_CONST(0xd2469ab3, 0xe04acbb2, 0x1c65a180, 0x9f39caaf, 0xe7a77c13, 0xd10f9dd3, 0x8f391c01, 0xdc499c52), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x08, 0xcc, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x60, 0xe9, 0xf0}, SECP256K1_FE_CONST(0x38e2a5ce, 0x6a93e795, 0xe16d2c39, 0x8bc99f03, 0x69202ce2, 0x1e8f09d5, 0x6777b40f, 0xc512bccc), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0x91, 0x25, 0x7d, 0x93, 0x20, 0x16, 0xcb, 0xf6, 0x9c, 0x44, 0x71, 0xbd, 0x1f, 0x65, 0x6c, 0x6a, 0x10, 0x7f, 0x19, 0x73, 0xde, 0x4a, 0xf7, 0x08, 0x6d, 0xb8, 0x97, 0x27, 0x70, 0x60, 0xe2, 0x56, 0x77, 0xf1, 0x9a}, SECP256K1_FE_CONST(0x864b3dc9, 0x02c37670, 0x9c10a93a, 0xd4bbe29f, 0xce0012f3, 0xdc8672c6, 0x286bba28, 0xd7d6d6fc), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0x5d, 0x6c, 0x1c, 0x32, 0x2c, 0xad, 0xf5, 0x99, 0xdb, 0xb8, 0x64, 0x81, 0x52, 0x2b, 0x3c, 0xc5, 0x5f, 0x15, 0xa6, 0x79, 0x32, 0xdb, 0x2a, 0xfa, 0x01, 0x11, 0xd9, 0xed, 0x69, 0x81, 0xbc, 0xd1, 0x24, 0xbf, 0x44}, SECP256K1_FE_CONST(0x766dfe4a, 0x700d9bee, 0x288b903a, 0xd58870e3, 0xd4fe2f0e, 0xf780bcac, 0x5c823f32, 0x0d9a9bef), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8e, 0x42, 0x6f, 0x03, 0x92, 0x38, 0x90, 0x78, 0xc1, 0x2b, 0x1a, 0x89, 0xe9, 0x54, 0x2f, 0x05, 0x93, 0xbc, 0x96, 0xb6, 0xbf, 0xde, 0x82, 0x24, 0xf8, 0x65, 0x4e, 0xf5, 0xd5, 0xcd, 0xa9, 0x35, 0xa3, 0x58, 0x21, 0x94}, SECP256K1_FE_CONST(0xfaec7bc1, 0x987b6323, 0x3fbc5f95, 0x6edbf37d, 0x54404e74, 0x61c58ab8, 0x631bc68e, 0x451a0478), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x91, 0x19, 0x21, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x45, 0xf0, 0xf1, 0xeb}, SECP256K1_FE_CONST(0xec29a50b, 0xae138dbf, 0x7d8e2482, 0x5006bb5f, 0xc1a2cc12, 0x43ba335b, 0xc6116fb9, 0xe498ec1f), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x98, 0xeb, 0x9a, 0xb7, 0x6e, 0x84, 0x49, 0x9c, 0x48, 0x3b, 0x3b, 0xf0, 0x62, 0x14, 0xab, 0xfe, 0x06, 0x5d, 0xdd, 0xf4, 0x3b, 0x86, 0x01, 0xde, 0x59, 0x6d, 0x63, 0xb9, 0xe4, 0x5a, 0x16, 0x6a, 0x58, 0x05, 0x41, 0xfe}, SECP256K1_FE_CONST(0x1e0ff2de, 0xe9b09b13, 0x6292a9e9, 0x10f0d6ac, 0x3e552a64, 0x4bba39e6, 0x4e9dd3e3, 0xbbd3d4d4), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, 0x77, 0xb7, 0xf2, 0xc7, 0x4d, 0x99, 0xef, 0xce, 0xaa, 0x55, 0x0f, 0x1a, 0xd1, 0xc0, 0xf4, 0x3f, 0x46, 0xe7, 0xff, 0x1e, 0xe3, 0xbd, 0x01, 0x62, 0xb7, 0xbf, 0x55, 0xf2, 0x96, 0x5d, 0xa9, 0xc3, 0x45, 0x06, 0x46}, SECP256K1_FE_CONST(0x8b7dd5c3, 0xedba9ee9, 0x7b70eff4, 0x38f22dca, 0x9849c825, 0x4a2f3345, 0xa0a572ff, 0xeaae0928), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, 0x77, 0xb7, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x6c, 0xa8, 0x96}, SECP256K1_FE_CONST(0x0881950c, 0x8f51d6b9, 0xa6387465, 0xd5f12609, 0xef1bb254, 0x12a08a74, 0xcb2dfb20, 0x0c74bfbf), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa2, 0xf5, 0xcd, 0x83, 0x88, 0x16, 0xc1, 0x6c, 0x4f, 0xe8, 0xa1, 0x66, 0x1d, 0x60, 0x6f, 0xdb, 0x13, 0xcf, 0x9a, 0xf0, 0x4b, 0x97, 0x9a, 0x2e, 0x15, 0x9a, 0x09, 0x40, 0x9e, 0xbc, 0x86, 0x45, 0xd5, 0x8f, 0xde, 0x02}, SECP256K1_FE_CONST(0x2f083207, 0xb9fd9b55, 0x0063c31c, 0xd62b8746, 0xbd543bdc, 0x5bbf10e3, 0xa35563e9, 0x27f440c8), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x3f, 0x75, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x4f51e0be, 0x078e0cdd, 0xab274215, 0x6adba7e7, 0xa148e731, 0x57072fd6, 0x18cd6094, 0x2b146bd0), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x3f, 0x75, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x4f51e0be, 0x078e0cdd, 0xab274215, 0x6adba7e7, 0xa148e731, 0x57072fd6, 0x18cd6094, 0x2b146bd0), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xbc, 0x1f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x16c2ccb5, 0x4352ff4b, 0xd794f6ef, 0xd613c721, 0x97ab7082, 0xda5b563b, 0xdf9cb3ed, 0xaafe74c2), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xbc, 0x1f, 0x8d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x16c2ccb5, 0x4352ff4b, 0xd794f6ef, 0xd613c721, 0x97ab7082, 0xda5b563b, 0xdf9cb3ed, 0xaafe74c2), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x64, 0xd1, 0x62, 0x75, 0x05, 0x46, 0xce, 0x42, 0xb0, 0x43, 0x13, 0x61, 0xe5, 0x2d, 0x4f, 0x52, 0x42, 0xd8, 0xf2, 0x4f, 0x33, 0xe6, 0xb1, 0xf9, 0x9b, 0x59, 0x16, 0x47, 0xcb, 0xc8, 0x08, 0xf4, 0x62, 0xaf, 0x51}, SECP256K1_FE_CONST(0xd41244d1, 0x1ca4f652, 0x40687759, 0xf95ca9ef, 0xbab767ed, 0xedb38fd1, 0x8c36e18c, 0xd3b6f6a9), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xe5, 0xbe, 0x52, 0x37, 0x2d, 0xd6, 0xe8, 0x94, 0xb2, 0xa3, 0x26, 0xfc, 0x36, 0x05, 0xa6, 0xe8, 0xf3, 0xc6, 0x9c, 0x71, 0x0b, 0xf2, 0x7d, 0x63, 0x0d, 0xfe, 0x20, 0x04, 0x98, 0x8b, 0x78, 0xeb, 0x6e, 0xab, 0x36}, SECP256K1_FE_CONST(0x64bf84dd, 0x5e03670f, 0xdb24c0f5, 0xd3c2c365, 0x736f51db, 0x6c92d950, 0x10716ad2, 0xd36134c8), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfb, 0xb9, 0x82, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xd6, 0xdb, 0x1f}, SECP256K1_FE_CONST(0x1c92ccdf, 0xcf4ac550, 0xc28db57c, 0xff0c8515, 0xcb26936c, 0x786584a7, 0x0114008d, 0x6c33a34b), 0}, +}; + +/* Set of expected ellswift_xdh BIP324 shared secrets, given private key, encodings, initiating, + * taken from the BIP324 test vectors. Created using an independent implementation, and tested + * against the paper authors' decoding code. */ +static const struct ellswift_xdh_test ellswift_xdh_tests_bip324[] = { + {{0x61, 0x06, 0x2e, 0xa5, 0x07, 0x1d, 0x80, 0x0b, 0xbf, 0xd5, 0x9e, 0x2e, 0x8b, 0x53, 0xd4, 0x7d, 0x19, 0x4b, 0x09, 0x5a, 0xe5, 0xa4, 0xdf, 0x04, 0x93, 0x6b, 0x49, 0x77, 0x2e, 0xf0, 0xd4, 0xd7}, {0xec, 0x0a, 0xdf, 0xf2, 0x57, 0xbb, 0xfe, 0x50, 0x0c, 0x18, 0x8c, 0x80, 0xb4, 0xfd, 0xd6, 0x40, 0xf6, 0xb4, 0x5a, 0x48, 0x2b, 0xbc, 0x15, 0xfc, 0x7c, 0xef, 0x59, 0x31, 0xde, 0xff, 0x0a, 0xa1, 0x86, 0xf6, 0xeb, 0x9b, 0xba, 0x7b, 0x85, 0xdc, 0x4d, 0xcc, 0x28, 0xb2, 0x87, 0x22, 0xde, 0x1e, 0x3d, 0x91, 0x08, 0xb9, 0x85, 0xe2, 0x96, 0x70, 0x45, 0x66, 0x8f, 0x66, 0x09, 0x8e, 0x47, 0x5b}, {0xa4, 0xa9, 0x4d, 0xfc, 0xe6, 0x9b, 0x4a, 0x2a, 0x0a, 0x09, 0x93, 0x13, 0xd1, 0x0f, 0x9f, 0x7e, 0x7d, 0x64, 0x9d, 0x60, 0x50, 0x1c, 0x9e, 0x1d, 0x27, 0x4c, 0x30, 0x0e, 0x0d, 0x89, 0xaa, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xaf, 0x88, 0xd5}, 1, {0xc6, 0x99, 0x2a, 0x11, 0x7f, 0x5e, 0xdb, 0xea, 0x70, 0xc3, 0xf5, 0x11, 0xd3, 0x2d, 0x26, 0xb9, 0x79, 0x8b, 0xe4, 0xb8, 0x1a, 0x62, 0xea, 0xee, 0x1a, 0x5a, 0xca, 0xa8, 0x45, 0x9a, 0x35, 0x92}}, + {{0x1f, 0x9c, 0x58, 0x1b, 0x35, 0x23, 0x18, 0x38, 0xf0, 0xf1, 0x7c, 0xf0, 0xc9, 0x79, 0x83, 0x5b, 0xac, 0xcb, 0x7f, 0x3a, 0xbb, 0xbb, 0x96, 0xff, 0xcc, 0x31, 0x8a, 0xb7, 0x1e, 0x6e, 0x12, 0x6f}, {0xa1, 0x85, 0x5e, 0x10, 0xe9, 0x4e, 0x00, 0xba, 0xa2, 0x30, 0x41, 0xd9, 0x16, 0xe2, 0x59, 0xf7, 0x04, 0x4e, 0x49, 0x1d, 0xa6, 0x17, 0x12, 0x69, 0x69, 0x47, 0x63, 0xf0, 0x18, 0xc7, 0xe6, 0x36, 0x93, 0xd2, 0x95, 0x75, 0xdc, 0xb4, 0x64, 0xac, 0x81, 0x6b, 0xaa, 0x1b, 0xe3, 0x53, 0xba, 0x12, 0xe3, 0x87, 0x6c, 0xba, 0x76, 0x28, 0xbd, 0x0b, 0xd8, 0xe7, 0x55, 0xe7, 0x21, 0xeb, 0x01, 0x40}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0, {0xa0, 0x13, 0x8f, 0x56, 0x4f, 0x74, 0xd0, 0xad, 0x70, 0xbc, 0x33, 0x7d, 0xac, 0xc9, 0xd0, 0xbf, 0x1d, 0x23, 0x49, 0x36, 0x4c, 0xaf, 0x11, 0x88, 0xa1, 0xe6, 0xe8, 0xdd, 0xb3, 0xb7, 0xb1, 0x84}}, + {{0x02, 0x86, 0xc4, 0x1c, 0xd3, 0x09, 0x13, 0xdb, 0x0f, 0xdf, 0xf7, 0xa6, 0x4e, 0xbd, 0xa5, 0xc8, 0xe3, 0xe7, 0xce, 0xf1, 0x0f, 0x2a, 0xeb, 0xc0, 0x0a, 0x76, 0x50, 0x44, 0x3c, 0xf4, 0xc6, 0x0d}, {0xd1, 0xee, 0x8a, 0x93, 0xa0, 0x11, 0x30, 0xcb, 0xf2, 0x99, 0x24, 0x9a, 0x25, 0x8f, 0x94, 0xfe, 0xb5, 0xf4, 0x69, 0xe7, 0xd0, 0xf2, 0xf2, 0x8f, 0x69, 0xee, 0x5e, 0x9a, 0xa8, 0xf9, 0xb5, 0x4a, 0x60, 0xf2, 0xc3, 0xff, 0x2d, 0x02, 0x36, 0x34, 0xec, 0x7f, 0x41, 0x27, 0xa9, 0x6c, 0xc1, 0x16, 0x62, 0xe4, 0x02, 0x89, 0x4c, 0xf1, 0xf6, 0x94, 0xfb, 0x9a, 0x7e, 0xaa, 0x5f, 0x1d, 0x92, 0x44}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22, 0xd5, 0xe4, 0x41, 0x52, 0x4d, 0x57, 0x1a, 0x52, 0xb3, 0xde, 0xf1, 0x26, 0x18, 0x9d, 0x3f, 0x41, 0x68, 0x90, 0xa9, 0x9d, 0x4d, 0xa6, 0xed, 0xe2, 0xb0, 0xcd, 0xe1, 0x76, 0x0c, 0xe2, 0xc3, 0xf9, 0x84, 0x57, 0xae}, 1, {0x25, 0x0b, 0x93, 0x57, 0x0d, 0x41, 0x11, 0x49, 0x10, 0x5a, 0xb8, 0xcb, 0x0b, 0xc5, 0x07, 0x99, 0x14, 0x90, 0x63, 0x06, 0x36, 0x8c, 0x23, 0xe9, 0xd7, 0x7c, 0x2a, 0x33, 0x26, 0x5b, 0x99, 0x4c}}, + {{0x6c, 0x77, 0x43, 0x2d, 0x1f, 0xda, 0x31, 0xe9, 0xf9, 0x42, 0xf8, 0xaf, 0x44, 0x60, 0x7e, 0x10, 0xf3, 0xad, 0x38, 0xa6, 0x5f, 0x8a, 0x4b, 0xdd, 0xae, 0x82, 0x3e, 0x5e, 0xff, 0x90, 0xdc, 0x38}, {0xd2, 0x68, 0x50, 0x70, 0xc1, 0xe6, 0x37, 0x6e, 0x63, 0x3e, 0x82, 0x52, 0x96, 0x63, 0x4f, 0xd4, 0x61, 0xfa, 0x9e, 0x5b, 0xdf, 0x21, 0x09, 0xbc, 0xeb, 0xd7, 0x35, 0xe5, 0xa9, 0x1f, 0x3e, 0x58, 0x7c, 0x5c, 0xb7, 0x82, 0xab, 0xb7, 0x97, 0xfb, 0xf6, 0xbb, 0x50, 0x74, 0xfd, 0x15, 0x42, 0xa4, 0x74, 0xf2, 0xa4, 0x5b, 0x67, 0x37, 0x63, 0xec, 0x2d, 0xb7, 0xfb, 0x99, 0xb7, 0x37, 0xbb, 0xb9}, {0x56, 0xbd, 0x0c, 0x06, 0xf1, 0x03, 0x52, 0xc3, 0xa1, 0xa9, 0xf4, 0xb4, 0xc9, 0x2f, 0x6f, 0xa2, 0xb2, 0x6d, 0xf1, 0x24, 0xb5, 0x78, 0x78, 0x35, 0x3c, 0x1f, 0xc6, 0x91, 0xc5, 0x1a, 0xbe, 0xa7, 0x7c, 0x88, 0x17, 0xda, 0xee, 0xb9, 0xfa, 0x54, 0x6b, 0x77, 0xc8, 0xda, 0xf7, 0x9d, 0x89, 0xb2, 0x2b, 0x0e, 0x1b, 0x87, 0x57, 0x4e, 0xce, 0x42, 0x37, 0x1f, 0x00, 0x23, 0x7a, 0xa9, 0xd8, 0x3a}, 0, {0x19, 0x18, 0xb7, 0x41, 0xef, 0x5f, 0x9d, 0x1d, 0x76, 0x70, 0xb0, 0x50, 0xc1, 0x52, 0xb4, 0xa4, 0xea, 0xd2, 0xc3, 0x1b, 0xe9, 0xae, 0xcb, 0x06, 0x81, 0xc0, 0xcd, 0x43, 0x24, 0x15, 0x08, 0x53}}, + {{0xa6, 0xec, 0x25, 0x12, 0x7c, 0xa1, 0xaa, 0x4c, 0xf1, 0x6b, 0x20, 0x08, 0x4b, 0xa1, 0xe6, 0x51, 0x6b, 0xaa, 0xe4, 0xd3, 0x24, 0x22, 0x28, 0x8e, 0x9b, 0x36, 0xd8, 0xbd, 0xdd, 0x2d, 0xe3, 0x5a}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x3d, 0x7e, 0xcc, 0xa5, 0x3e, 0x33, 0xe1, 0x85, 0xa8, 0xb9, 0xbe, 0x4e, 0x76, 0x99, 0xa9, 0x7c, 0x6f, 0xf4, 0xc7, 0x95, 0x52, 0x2e, 0x59, 0x18, 0xab, 0x7c, 0xd6, 0xb6, 0x88, 0x4f, 0x67, 0xe6, 0x83, 0xf3, 0xdc}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x73, 0x0b, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 1, {0xdd, 0x21, 0x0a, 0xa6, 0x62, 0x9f, 0x20, 0xbb, 0x32, 0x8e, 0x5d, 0x89, 0xda, 0xa6, 0xeb, 0x2a, 0xc3, 0xd1, 0xc6, 0x58, 0xa7, 0x25, 0x53, 0x6f, 0xf1, 0x54, 0xf3, 0x1b, 0x53, 0x6c, 0x23, 0xb2}}, + {{0x0a, 0xf9, 0x52, 0x65, 0x9e, 0xd7, 0x6f, 0x80, 0xf5, 0x85, 0x96, 0x6b, 0x95, 0xab, 0x6e, 0x6f, 0xd6, 0x86, 0x54, 0x67, 0x28, 0x27, 0x87, 0x86, 0x84, 0xc8, 0xb5, 0x47, 0xb1, 0xb9, 0x4f, 0x5a}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0x10, 0x17, 0xfd, 0x92, 0xfd, 0x31, 0x63, 0x7c, 0x26, 0xc9, 0x06, 0xb4, 0x20, 0x92, 0xe1, 0x1c, 0xc0, 0xd3, 0xaf, 0xae, 0x8d, 0x90, 0x19, 0xd2, 0x57, 0x8a, 0xf2, 0x27, 0x35, 0xce, 0x7b, 0xc4, 0x69, 0xc7, 0x2d}, {0x96, 0x52, 0xd7, 0x8b, 0xae, 0xfc, 0x02, 0x8c, 0xd3, 0x7a, 0x6a, 0x92, 0x62, 0x5b, 0x8b, 0x8f, 0x85, 0xfd, 0xe1, 0xe4, 0xc9, 0x44, 0xad, 0x3f, 0x20, 0xe1, 0x98, 0xbe, 0xf8, 0xc0, 0x2f, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xe9, 0x18, 0x70}, 0, {0x35, 0x68, 0xf2, 0xae, 0xa2, 0xe1, 0x4e, 0xf4, 0xee, 0x4a, 0x3c, 0x2a, 0x8b, 0x8d, 0x31, 0xbc, 0x5e, 0x31, 0x87, 0xba, 0x86, 0xdb, 0x10, 0x73, 0x9b, 0x4f, 0xf8, 0xec, 0x92, 0xff, 0x66, 0x55}}, + {{0xf9, 0x0e, 0x08, 0x0c, 0x64, 0xb0, 0x58, 0x24, 0xc5, 0xa2, 0x4b, 0x25, 0x01, 0xd5, 0xae, 0xaf, 0x08, 0xaf, 0x38, 0x72, 0xee, 0x86, 0x0a, 0xa8, 0x0b, 0xdc, 0xd4, 0x30, 0xf7, 0xb6, 0x34, 0x94}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x51, 0x73, 0x76, 0x5d, 0xc2, 0x02, 0xcf, 0x02, 0x9a, 0xd3, 0xf1, 0x54, 0x79, 0x73, 0x5d, 0x57, 0x69, 0x7a, 0xf1, 0x2b, 0x01, 0x31, 0xdd, 0x21, 0x43, 0x0d, 0x57, 0x72, 0xe4, 0xef, 0x11, 0x47, 0x4d, 0x58, 0xb9}, {0x12, 0xa5, 0x0f, 0x3f, 0xaf, 0xea, 0x7c, 0x1e, 0xea, 0xda, 0x4c, 0xf8, 0xd3, 0x37, 0x77, 0x70, 0x4b, 0x77, 0x36, 0x14, 0x53, 0xaf, 0xc8, 0x3b, 0xda, 0x91, 0xee, 0xf3, 0x49, 0xae, 0x04, 0x4d, 0x20, 0x12, 0x6c, 0x62, 0x00, 0x54, 0x7e, 0xa5, 0xa6, 0x91, 0x17, 0x76, 0xc0, 0x5d, 0xee, 0x2a, 0x7f, 0x1a, 0x9b, 0xa7, 0xdf, 0xba, 0xbb, 0xbd, 0x27, 0x3c, 0x3e, 0xf2, 0x9e, 0xf4, 0x6e, 0x46}, 1, {0xe2, 0x54, 0x61, 0xfb, 0x0e, 0x4c, 0x16, 0x2e, 0x18, 0x12, 0x3e, 0xcd, 0xe8, 0x83, 0x42, 0xd5, 0x4d, 0x44, 0x96, 0x31, 0xe9, 0xb7, 0x5a, 0x26, 0x6f, 0xd9, 0x26, 0x0c, 0x2b, 0xb2, 0xf4, 0x1d}}, +}; + +/** This is a hasher for ellswift_xdh which just returns the shared X coordinate. + * + * This is generally a bad idea as it means changes to the encoding of the + * exchanged public keys do not affect the shared secret. However, it's used here + * in tests to be able to verify the X coordinate through other means. + */ +static int ellswift_xdh_hash_x32(unsigned char *output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) { + (void)ell_a64; + (void)ell_b64; + (void)data; + memcpy(output, x32, 32); + return 1; +} + +void run_ellswift_tests(void) { + int i = 0; + /* Test vectors. */ + for (i = 0; (unsigned)i < sizeof(ellswift_xswiftec_inv_tests) / sizeof(ellswift_xswiftec_inv_tests[0]); ++i) { + const struct ellswift_xswiftec_inv_test *testcase = &ellswift_xswiftec_inv_tests[i]; + int c; + for (c = 0; c < 8; ++c) { + secp256k1_fe t; + int ret = secp256k1_ellswift_xswiftec_inv_var(&t, &testcase->x, &testcase->u, c); + CHECK(ret == ((testcase->enc_bitmap >> c) & 1)); + if (ret) { + secp256k1_fe x2; + CHECK(check_fe_equal(&t, &testcase->encs[c])); + secp256k1_ellswift_xswiftec_var(&x2, &testcase->u, &testcase->encs[c]); + CHECK(check_fe_equal(&testcase->x, &x2)); + } + } + } + for (i = 0; (unsigned)i < sizeof(ellswift_decode_tests) / sizeof(ellswift_decode_tests[0]); ++i) { + const struct ellswift_decode_test *testcase = &ellswift_decode_tests[i]; + secp256k1_pubkey pubkey; + secp256k1_ge ge; + int ret; + ret = secp256k1_ellswift_decode(CTX, &pubkey, testcase->enc); + CHECK(ret); + ret = secp256k1_pubkey_load(CTX, &ge, &pubkey); + CHECK(ret); + CHECK(check_fe_equal(&testcase->x, &ge.x)); + CHECK(secp256k1_fe_is_odd(&ge.y) == testcase->odd_y); + } + for (i = 0; (unsigned)i < sizeof(ellswift_xdh_tests_bip324) / sizeof(ellswift_xdh_tests_bip324[0]); ++i) { + const struct ellswift_xdh_test *test = &ellswift_xdh_tests_bip324[i]; + unsigned char shared_secret[32]; + int ret; + int party = !test->initiating; + const unsigned char* ell_a64 = party ? test->ellswift_theirs : test->ellswift_ours; + const unsigned char* ell_b64 = party ? test->ellswift_ours : test->ellswift_theirs; + ret = secp256k1_ellswift_xdh(CTX, shared_secret, + ell_a64, ell_b64, + test->priv_ours, + party, + secp256k1_ellswift_xdh_hash_function_bip324, + NULL); + CHECK(ret); + CHECK(secp256k1_memcmp_var(shared_secret, test->shared_secret, 32) == 0); + } + /* Verify that secp256k1_ellswift_encode + decode roundtrips. */ + for (i = 0; i < 1000 * COUNT; i++) { + unsigned char rnd32[32]; + unsigned char ell64[64]; + secp256k1_ge g, g2; + secp256k1_pubkey pubkey, pubkey2; + /* Generate random public key and random randomizer. */ + random_group_element_test(&g); + secp256k1_pubkey_save(&pubkey, &g); + secp256k1_testrand256(rnd32); + /* Convert the public key to ElligatorSwift and back. */ + secp256k1_ellswift_encode(CTX, ell64, &pubkey, rnd32); + secp256k1_ellswift_decode(CTX, &pubkey2, ell64); + secp256k1_pubkey_load(CTX, &g2, &pubkey2); + /* Compare with original. */ + ge_equals_ge(&g, &g2); + } + /* Verify the behavior of secp256k1_ellswift_create */ + for (i = 0; i < 400 * COUNT; i++) { + unsigned char auxrnd32[32], sec32[32]; + secp256k1_scalar sec; + secp256k1_gej res; + secp256k1_ge dec; + secp256k1_pubkey pub; + unsigned char ell64[64]; + int ret; + /* Generate random secret key and random randomizer. */ + if (i & 1) secp256k1_testrand256_test(auxrnd32); + random_scalar_order_test(&sec); + secp256k1_scalar_get_b32(sec32, &sec); + /* Construct ElligatorSwift-encoded public keys for that key. */ + ret = secp256k1_ellswift_create(CTX, ell64, sec32, (i & 1) ? auxrnd32 : NULL); + CHECK(ret); + /* Decode it, and compare with traditionally-computed public key. */ + secp256k1_ellswift_decode(CTX, &pub, ell64); + secp256k1_pubkey_load(CTX, &dec, &pub); + secp256k1_ecmult(&res, NULL, &secp256k1_scalar_zero, &sec); + ge_equals_gej(&dec, &res); + } + /* Verify that secp256k1_ellswift_xdh computes the right shared X coordinate. */ + for (i = 0; i < 800 * COUNT; i++) { + unsigned char ell64[64], sec32[32], share32[32]; + secp256k1_scalar sec; + secp256k1_ge dec, res; + secp256k1_fe share_x; + secp256k1_gej decj, resj; + secp256k1_pubkey pub; + int ret; + /* Generate random secret key. */ + random_scalar_order_test(&sec); + secp256k1_scalar_get_b32(sec32, &sec); + /* Generate random ElligatorSwift encoding for the remote key and decode it. */ + secp256k1_testrand256_test(ell64); + secp256k1_testrand256_test(ell64 + 32); + secp256k1_ellswift_decode(CTX, &pub, ell64); + secp256k1_pubkey_load(CTX, &dec, &pub); + secp256k1_gej_set_ge(&decj, &dec); + /* Compute the X coordinate of seckey*pubkey using ellswift_xdh. Note that we + * pass ell64 as claimed (but incorrect) encoding for sec32 here; this works + * because the "hasher" function we use here ignores the ell64 arguments. */ + ret = secp256k1_ellswift_xdh(CTX, share32, ell64, ell64, sec32, i & 1, &ellswift_xdh_hash_x32, NULL); + CHECK(ret); + (void)secp256k1_fe_set_b32_limit(&share_x, share32); /* no overflow is possible */ + secp256k1_fe_verify(&share_x); + /* Compute seckey*pubkey directly. */ + secp256k1_ecmult(&resj, &decj, &sec, NULL); + secp256k1_ge_set_gej(&res, &resj); + /* Compare. */ + CHECK(check_fe_equal(&res.x, &share_x)); + } + /* Verify the joint behavior of secp256k1_ellswift_xdh */ + for (i = 0; i < 200 * COUNT; i++) { + unsigned char auxrnd32a[32], auxrnd32b[32], auxrnd32a_bad[32], auxrnd32b_bad[32]; + unsigned char sec32a[32], sec32b[32], sec32a_bad[32], sec32b_bad[32]; + secp256k1_scalar seca, secb; + unsigned char ell64a[64], ell64b[64], ell64a_bad[64], ell64b_bad[64]; + unsigned char share32a[32], share32b[32], share32_bad[32]; + unsigned char prefix64[64]; + secp256k1_ellswift_xdh_hash_function hash_function; + void* data; + int ret; + + /* Pick hasher to use. */ + if ((i % 3) == 0) { + hash_function = ellswift_xdh_hash_x32; + data = NULL; + } else if ((i % 3) == 1) { + hash_function = secp256k1_ellswift_xdh_hash_function_bip324; + data = NULL; + } else { + hash_function = secp256k1_ellswift_xdh_hash_function_prefix; + secp256k1_testrand256_test(prefix64); + secp256k1_testrand256_test(prefix64 + 32); + data = prefix64; + } + + /* Generate random secret keys and random randomizers. */ + secp256k1_testrand256_test(auxrnd32a); + secp256k1_testrand256_test(auxrnd32b); + random_scalar_order_test(&seca); + /* Draw secb uniformly at random to make sure that the secret keys + * differ */ + random_scalar_order(&secb); + secp256k1_scalar_get_b32(sec32a, &seca); + secp256k1_scalar_get_b32(sec32b, &secb); + + /* Construct ElligatorSwift-encoded public keys for those keys. */ + /* For A: */ + ret = secp256k1_ellswift_create(CTX, ell64a, sec32a, auxrnd32a); + CHECK(ret); + /* For B: */ + ret = secp256k1_ellswift_create(CTX, ell64b, sec32b, auxrnd32b); + CHECK(ret); + + /* Compute the shared secret both ways and compare with each other. */ + /* For A: */ + ret = secp256k1_ellswift_xdh(CTX, share32a, ell64a, ell64b, sec32a, 0, hash_function, data); + CHECK(ret); + /* For B: */ + ret = secp256k1_ellswift_xdh(CTX, share32b, ell64a, ell64b, sec32b, 1, hash_function, data); + CHECK(ret); + /* And compare: */ + CHECK(secp256k1_memcmp_var(share32a, share32b, 32) == 0); + + /* Verify that the shared secret doesn't match if other side's public key is incorrect. */ + /* For A (using a bad public key for B): */ + memcpy(ell64b_bad, ell64b, sizeof(ell64a_bad)); + secp256k1_testrand_flip(ell64b_bad, sizeof(ell64b_bad)); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b_bad, sec32a, 0, hash_function, data); + CHECK(ret); /* Mismatching encodings don't get detected by secp256k1_ellswift_xdh. */ + CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); + /* For B (using a bad public key for A): */ + memcpy(ell64a_bad, ell64a, sizeof(ell64a_bad)); + secp256k1_testrand_flip(ell64a_bad, sizeof(ell64a_bad)); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a_bad, ell64b, sec32b, 1, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); + + /* Verify that the shared secret doesn't match if the private key is incorrect. */ + /* For A: */ + memcpy(sec32a_bad, sec32a, sizeof(sec32a_bad)); + secp256k1_testrand_flip(sec32a_bad, sizeof(sec32a_bad)); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32a_bad, 0, hash_function, data); + CHECK(!ret || secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); + /* For B: */ + memcpy(sec32b_bad, sec32b, sizeof(sec32b_bad)); + secp256k1_testrand_flip(sec32b_bad, sizeof(sec32b_bad)); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32b_bad, 1, hash_function, data); + CHECK(!ret || secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); + + if (hash_function != ellswift_xdh_hash_x32) { + /* Verify that the shared secret doesn't match when a different encoding of the same public key is used. */ + /* For A (changing B's public key): */ + memcpy(auxrnd32b_bad, auxrnd32b, sizeof(auxrnd32b_bad)); + secp256k1_testrand_flip(auxrnd32b_bad, sizeof(auxrnd32b_bad)); + ret = secp256k1_ellswift_create(CTX, ell64b_bad, sec32b, auxrnd32b_bad); + CHECK(ret); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b_bad, sec32a, 0, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); + /* For B (changing A's public key): */ + memcpy(auxrnd32a_bad, auxrnd32a, sizeof(auxrnd32a_bad)); + secp256k1_testrand_flip(auxrnd32a_bad, sizeof(auxrnd32a_bad)); + ret = secp256k1_ellswift_create(CTX, ell64a_bad, sec32a, auxrnd32a_bad); + CHECK(ret); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a_bad, ell64b, sec32b, 1, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); + + /* Verify that swapping sides changes the shared secret. */ + /* For A (claiming to be B): */ + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32a, 1, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); + /* For B (claiming to be A): */ + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32b, 0, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); + } + } + + /* Test hash initializers. */ + { + secp256k1_sha256 sha, sha_optimized; + static const unsigned char encode_tag[25] = "secp256k1_ellswift_encode"; + static const unsigned char create_tag[25] = "secp256k1_ellswift_create"; + static const unsigned char bip324_tag[26] = "bip324_ellswift_xonly_ecdh"; + + /* Check that hash initialized by + * secp256k1_ellswift_sha256_init_encode has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, encode_tag, sizeof(encode_tag)); + secp256k1_ellswift_sha256_init_encode(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_ellswift_sha256_init_create has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, create_tag, sizeof(create_tag)); + secp256k1_ellswift_sha256_init_create(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_ellswift_sha256_init_bip324 has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, bip324_tag, sizeof(bip324_tag)); + secp256k1_ellswift_sha256_init_bip324(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + } +} + +#endif diff --git a/src/secp256k1/src/modules/extrakeys/main_impl.h b/src/secp256k1/src/modules/extrakeys/main_impl.h index e1003052f4..0c7e266777 100644 --- a/src/secp256k1/src/modules/extrakeys/main_impl.h +++ b/src/secp256k1/src/modules/extrakeys/main_impl.h @@ -9,6 +9,7 @@ #include "../../../include/secp256k1.h" #include "../../../include/secp256k1_extrakeys.h" +#include "../../util.h" static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) { return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey); @@ -27,7 +28,7 @@ int secp256k1_xonly_pubkey_parse(const secp256k1_context* ctx, secp256k1_xonly_p memset(pubkey, 0, sizeof(*pubkey)); ARG_CHECK(input32 != NULL); - if (!secp256k1_fe_set_b32(&x, input32)) { + if (!secp256k1_fe_set_b32_limit(&x, input32)) { return 0; } if (!secp256k1_ge_set_xo_var(&pk, &x, 0)) { diff --git a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h index 5ecc90d50f..d3d817a131 100644 --- a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h +++ b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h @@ -47,7 +47,7 @@ static void test_exhaustive_extrakeys(const secp256k1_context *ctx, const secp25 CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], buf, 32) == 0); /* Compare the xonly_pubkey bytes against the precomputed group. */ - secp256k1_fe_set_b32(&fe, xonly_pubkey_bytes[i - 1]); + secp256k1_fe_set_b32_mod(&fe, xonly_pubkey_bytes[i - 1]); CHECK(secp256k1_fe_equal_var(&fe, &group[i].x)); /* Check the parity against the precomputed group. */ diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h index e7906eb62e..76a005e017 100644 --- a/src/secp256k1/src/modules/recovery/main_impl.h +++ b/src/secp256k1/src/modules/recovery/main_impl.h @@ -98,7 +98,7 @@ static int secp256k1_ecdsa_sig_recover(const secp256k1_scalar *sigr, const secp2 } secp256k1_scalar_get_b32(brx, sigr); - r = secp256k1_fe_set_b32(&fx, brx); + r = secp256k1_fe_set_b32_limit(&fx, brx); (void)r; VERIFY_CHECK(r); /* brx comes from a scalar, so is less than the order; certainly less than p */ if (recid & 2) { diff --git a/src/secp256k1/src/modules/schnorrsig/main_impl.h b/src/secp256k1/src/modules/schnorrsig/main_impl.h index cd651591c4..4e7b45a045 100644 --- a/src/secp256k1/src/modules/schnorrsig/main_impl.h +++ b/src/secp256k1/src/modules/schnorrsig/main_impl.h @@ -232,7 +232,7 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha ARG_CHECK(msg != NULL || msglen == 0); ARG_CHECK(pubkey != NULL); - if (!secp256k1_fe_set_b32(&rx, &sig64[0])) { + if (!secp256k1_fe_set_b32_limit(&rx, &sig64[0])) { return 0; } diff --git a/src/secp256k1/src/modules/schnorrsig/tests_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_impl.h index 062005ee63..90337ff03e 100644 --- a/src/secp256k1/src/modules/schnorrsig/tests_impl.h +++ b/src/secp256k1/src/modules/schnorrsig/tests_impl.h @@ -20,17 +20,6 @@ static void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, s CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0); } -/* Tests for the equality of two sha256 structs. This function only produces a - * correct result if an integer multiple of 64 many bytes have been written - * into the hash functions. */ -static void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { - /* Is buffer fully consumed? */ - CHECK((sha1->bytes & 0x3F) == 0); - - CHECK(sha1->bytes == sha2->bytes); - CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0); -} - static void run_nonce_function_bip340_tests(void) { unsigned char tag[13] = "BIP0340/nonce"; unsigned char aux_tag[11] = "BIP0340/aux"; @@ -215,28 +204,36 @@ static void test_schnorrsig_sha256_tagged(void) { /* Helper function for schnorrsig_bip_vectors * Signs the message and checks that it's the same as expected_sig. */ -static void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *aux_rand, const unsigned char *msg32, const unsigned char *expected_sig) { +static void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *aux_rand, const unsigned char *msg, size_t msglen, const unsigned char *expected_sig) { unsigned char sig[64]; secp256k1_keypair keypair; secp256k1_xonly_pubkey pk, pk_expected; + secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; + extraparams.ndata = (unsigned char*)aux_rand; + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); - CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg32, &keypair, aux_rand)); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, msglen, &keypair, &extraparams)); CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0); + if (msglen == 32) { + memset(sig, 0, 64); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypair, aux_rand)); + CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0); + } CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk_expected, pk_serialized)); CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair)); CHECK(secp256k1_memcmp_var(&pk, &pk_expected, sizeof(pk)) == 0); - CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg32, 32, &pk)); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, msglen, &pk)); } /* Helper function for schnorrsig_bip_vectors * Checks that both verify and verify_batch (TODO) return the same value as expected. */ -static void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int expected) { +static void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg, size_t msglen, const unsigned char *sig, int expected) { secp256k1_xonly_pubkey pk; CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk, pk_serialized)); - CHECK(expected == secp256k1_schnorrsig_verify(CTX, sig, msg32, 32, &pk)); + CHECK(expected == secp256k1_schnorrsig_verify(CTX, sig, msg, msglen, &pk)); } /* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See @@ -256,7 +253,7 @@ static void test_schnorrsig_bip_vectors(void) { 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }; - unsigned char aux_rand[32] = { + const unsigned char aux_rand[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -278,8 +275,8 @@ static void test_schnorrsig_bip_vectors(void) { 0xEB, 0xEE, 0xE8, 0xFD, 0xB2, 0x17, 0x2F, 0x47, 0x7D, 0xF4, 0x90, 0x0D, 0x31, 0x05, 0x36, 0xC0 }; - test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); } { /* Test vector 1 */ @@ -295,7 +292,7 @@ static void test_schnorrsig_bip_vectors(void) { 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 }; - unsigned char aux_rand[32] = { + const unsigned char aux_rand[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -317,8 +314,8 @@ static void test_schnorrsig_bip_vectors(void) { 0x89, 0x7E, 0xFC, 0xB6, 0x39, 0xEA, 0x87, 0x1C, 0xFA, 0x95, 0xF6, 0xDE, 0x33, 0x9E, 0x4B, 0x0A }; - test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); } { /* Test vector 2 */ @@ -334,7 +331,7 @@ static void test_schnorrsig_bip_vectors(void) { 0x01, 0x39, 0x71, 0x53, 0x09, 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 }; - unsigned char aux_rand[32] = { + const unsigned char aux_rand[32] = { 0xC8, 0x7A, 0xA5, 0x38, 0x24, 0xB4, 0xD7, 0xAE, 0x2E, 0xB0, 0x35, 0xA2, 0xB5, 0xBB, 0xBC, 0xCC, 0x08, 0x0E, 0x76, 0xCD, 0xC6, 0xD1, 0x69, 0x2C, @@ -356,8 +353,8 @@ static void test_schnorrsig_bip_vectors(void) { 0x7A, 0xDE, 0xA9, 0x8D, 0x82, 0xF8, 0x48, 0x1E, 0x0E, 0x1E, 0x03, 0x67, 0x4A, 0x6F, 0x3F, 0xB7 }; - test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); } { /* Test vector 3 */ @@ -373,7 +370,7 @@ static void test_schnorrsig_bip_vectors(void) { 0x3A, 0x0D, 0x95, 0xFB, 0xF2, 0x1D, 0x46, 0x8A, 0x1B, 0x33, 0xF8, 0xC1, 0x60, 0xD8, 0xF5, 0x17 }; - unsigned char aux_rand[32] = { + const unsigned char aux_rand[32] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -395,8 +392,8 @@ static void test_schnorrsig_bip_vectors(void) { 0xF2, 0x5F, 0xD7, 0x88, 0x81, 0xEB, 0xB3, 0x27, 0x71, 0xFC, 0x59, 0x22, 0xEF, 0xC6, 0x6E, 0xA3 }; - test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); } { /* Test vector 4 */ @@ -422,7 +419,7 @@ static void test_schnorrsig_bip_vectors(void) { 0x60, 0xCB, 0x71, 0xC0, 0x4E, 0x80, 0xF5, 0x93, 0x06, 0x0B, 0x07, 0xD2, 0x83, 0x08, 0xD7, 0xF4 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); } { /* Test vector 5 */ @@ -460,7 +457,7 @@ static void test_schnorrsig_bip_vectors(void) { 0x7A, 0x73, 0xC6, 0x43, 0xE1, 0x66, 0xBE, 0x5E, 0xBE, 0xAF, 0xA3, 0x4B, 0x1A, 0xC5, 0x53, 0xE2 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); } { /* Test vector 7 */ @@ -486,7 +483,7 @@ static void test_schnorrsig_bip_vectors(void) { 0x62, 0x2A, 0x95, 0x4C, 0xFE, 0x54, 0x57, 0x35, 0xAA, 0xEA, 0x51, 0x34, 0xFC, 0xCD, 0xB2, 0xBD }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); } { /* Test vector 8 */ @@ -512,7 +509,7 @@ static void test_schnorrsig_bip_vectors(void) { 0xE8, 0xD7, 0xC9, 0x3E, 0x00, 0xC5, 0xED, 0x0C, 0x18, 0x34, 0xFF, 0x0D, 0x0C, 0x2E, 0x6D, 0xA6 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); } { /* Test vector 9 */ @@ -538,7 +535,7 @@ static void test_schnorrsig_bip_vectors(void) { 0x4F, 0xB7, 0x34, 0x76, 0xF0, 0xD5, 0x94, 0xDC, 0xB6, 0x5C, 0x64, 0x25, 0xBD, 0x18, 0x60, 0x51 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); } { /* Test vector 10 */ @@ -564,7 +561,7 @@ static void test_schnorrsig_bip_vectors(void) { 0xDB, 0xA8, 0x7F, 0x11, 0xAC, 0x67, 0x54, 0xF9, 0x37, 0x80, 0xD5, 0xA1, 0x83, 0x7C, 0xF1, 0x97 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); } { /* Test vector 11 */ @@ -590,7 +587,7 @@ static void test_schnorrsig_bip_vectors(void) { 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F, 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); } { /* Test vector 12 */ @@ -616,7 +613,7 @@ static void test_schnorrsig_bip_vectors(void) { 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F, 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); } { /* Test vector 13 */ @@ -642,7 +639,7 @@ static void test_schnorrsig_bip_vectors(void) { 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); } { /* Test vector 14 */ @@ -656,6 +653,147 @@ static void test_schnorrsig_bip_vectors(void) { /* No need to check the signature of the test vector as parsing the pubkey already fails */ CHECK(!secp256k1_xonly_pubkey_parse(CTX, &pk_parsed, pk)); } + { + /* Test vector 15 */ + const unsigned char sk[32] = { + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + }; + const unsigned char pk[32] = { + 0x77, 0x8C, 0xAA, 0x53, 0xB4, 0x39, 0x3A, 0xC4, + 0x67, 0x77, 0x4D, 0x09, 0x49, 0x7A, 0x87, 0x22, + 0x4B, 0xF9, 0xFA, 0xB6, 0xF6, 0xE6, 0x8B, 0x23, + 0x08, 0x64, 0x97, 0x32, 0x4D, 0x6F, 0xD1, 0x17, + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + /* const unsigned char msg[0] = {}; */ + const unsigned char sig[64] = { + 0x71, 0x53, 0x5D, 0xB1, 0x65, 0xEC, 0xD9, 0xFB, + 0xBC, 0x04, 0x6E, 0x5F, 0xFA, 0xEA, 0x61, 0x18, + 0x6B, 0xB6, 0xAD, 0x43, 0x67, 0x32, 0xFC, 0xCC, + 0x25, 0x29, 0x1A, 0x55, 0x89, 0x54, 0x64, 0xCF, + 0x60, 0x69, 0xCE, 0x26, 0xBF, 0x03, 0x46, 0x62, + 0x28, 0xF1, 0x9A, 0x3A, 0x62, 0xDB, 0x8A, 0x64, + 0x9F, 0x2D, 0x56, 0x0F, 0xAC, 0x65, 0x28, 0x27, + 0xD1, 0xAF, 0x05, 0x74, 0xE4, 0x27, 0xAB, 0x63, + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, NULL, 0, sig); + test_schnorrsig_bip_vectors_check_verify(pk, NULL, 0, sig, 1); + } + { + /* Test vector 16 */ + const unsigned char sk[32] = { + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + }; + const unsigned char pk[32] = { + 0x77, 0x8C, 0xAA, 0x53, 0xB4, 0x39, 0x3A, 0xC4, + 0x67, 0x77, 0x4D, 0x09, 0x49, 0x7A, 0x87, 0x22, + 0x4B, 0xF9, 0xFA, 0xB6, 0xF6, 0xE6, 0x8B, 0x23, + 0x08, 0x64, 0x97, 0x32, 0x4D, 0x6F, 0xD1, 0x17, + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + const unsigned char msg[] = { 0x11 }; + const unsigned char sig[64] = { + 0x08, 0xA2, 0x0A, 0x0A, 0xFE, 0xF6, 0x41, 0x24, + 0x64, 0x92, 0x32, 0xE0, 0x69, 0x3C, 0x58, 0x3A, + 0xB1, 0xB9, 0x93, 0x4A, 0xE6, 0x3B, 0x4C, 0x35, + 0x11, 0xF3, 0xAE, 0x11, 0x34, 0xC6, 0xA3, 0x03, + 0xEA, 0x31, 0x73, 0xBF, 0xEA, 0x66, 0x83, 0xBD, + 0x10, 0x1F, 0xA5, 0xAA, 0x5D, 0xBC, 0x19, 0x96, + 0xFE, 0x7C, 0xAC, 0xFC, 0x5A, 0x57, 0x7D, 0x33, + 0xEC, 0x14, 0x56, 0x4C, 0xEC, 0x2B, 0xAC, 0xBF, + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } + { + /* Test vector 17 */ + const unsigned char sk[32] = { + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + }; + const unsigned char pk[32] = { + 0x77, 0x8C, 0xAA, 0x53, 0xB4, 0x39, 0x3A, 0xC4, + 0x67, 0x77, 0x4D, 0x09, 0x49, 0x7A, 0x87, 0x22, + 0x4B, 0xF9, 0xFA, 0xB6, 0xF6, 0xE6, 0x8B, 0x23, + 0x08, 0x64, 0x97, 0x32, 0x4D, 0x6F, 0xD1, 0x17, + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + const unsigned char msg[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, + }; + const unsigned char sig[64] = { + 0x51, 0x30, 0xF3, 0x9A, 0x40, 0x59, 0xB4, 0x3B, + 0xC7, 0xCA, 0xC0, 0x9A, 0x19, 0xEC, 0xE5, 0x2B, + 0x5D, 0x86, 0x99, 0xD1, 0xA7, 0x1E, 0x3C, 0x52, + 0xDA, 0x9A, 0xFD, 0xB6, 0xB5, 0x0A, 0xC3, 0x70, + 0xC4, 0xA4, 0x82, 0xB7, 0x7B, 0xF9, 0x60, 0xF8, + 0x68, 0x15, 0x40, 0xE2, 0x5B, 0x67, 0x71, 0xEC, + 0xE1, 0xE5, 0xA3, 0x7F, 0xD8, 0x0E, 0x5A, 0x51, + 0x89, 0x7C, 0x55, 0x66, 0xA9, 0x7E, 0xA5, 0xA5, + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } + { + /* Test vector 18 */ + const unsigned char sk[32] = { + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + }; + const unsigned char pk[32] = { + 0x77, 0x8C, 0xAA, 0x53, 0xB4, 0x39, 0x3A, 0xC4, + 0x67, 0x77, 0x4D, 0x09, 0x49, 0x7A, 0x87, 0x22, + 0x4B, 0xF9, 0xFA, 0xB6, 0xF6, 0xE6, 0x8B, 0x23, + 0x08, 0x64, 0x97, 0x32, 0x4D, 0x6F, 0xD1, 0x17, + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + const unsigned char sig[64] = { + 0x40, 0x3B, 0x12, 0xB0, 0xD8, 0x55, 0x5A, 0x34, + 0x41, 0x75, 0xEA, 0x7E, 0xC7, 0x46, 0x56, 0x63, + 0x03, 0x32, 0x1E, 0x5D, 0xBF, 0xA8, 0xBE, 0x6F, + 0x09, 0x16, 0x35, 0x16, 0x3E, 0xCA, 0x79, 0xA8, + 0x58, 0x5E, 0xD3, 0xE3, 0x17, 0x08, 0x07, 0xE7, + 0xC0, 0x3B, 0x72, 0x0F, 0xC5, 0x4C, 0x7B, 0x23, + 0x89, 0x7F, 0xCB, 0xA0, 0xE9, 0xD0, 0xB4, 0xA0, + 0x68, 0x94, 0xCF, 0xD2, 0x49, 0xF2, 0x23, 0x67, + }; + unsigned char msg[100]; + memset(msg, 0x99, sizeof(msg)); + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } } /* Nonce function that returns constant 0 */ diff --git a/src/secp256k1/src/precompute_ecmult.c b/src/secp256k1/src/precompute_ecmult.c index 10aba5b97d..5ef198a770 100644 --- a/src/secp256k1/src/precompute_ecmult.c +++ b/src/secp256k1/src/precompute_ecmult.c @@ -56,11 +56,12 @@ static void print_two_tables(FILE *fp, int window_g) { int main(void) { /* Always compute all tables for window sizes up to 15. */ int window_g = (ECMULT_WINDOW_SIZE < 15) ? 15 : ECMULT_WINDOW_SIZE; + const char outfile[] = "src/precomputed_ecmult.c"; FILE* fp; - fp = fopen("src/precomputed_ecmult.c","w"); + fp = fopen(outfile, "w"); if (fp == NULL) { - fprintf(stderr, "Could not open src/precomputed_ecmult.h for writing!\n"); + fprintf(stderr, "Could not open %s for writing!\n", outfile); return -1; } @@ -68,7 +69,6 @@ int main(void) { fprintf(fp, "/* This file contains an array secp256k1_pre_g with odd multiples of the base point G and\n"); fprintf(fp, " * an array secp256k1_pre_g_128 with odd multiples of 2^128*G for accelerating the computation of a*P + b*G.\n"); fprintf(fp, " */\n"); - fprintf(fp, "#include \"../include/secp256k1.h\"\n"); fprintf(fp, "#include \"group.h\"\n"); fprintf(fp, "#include \"ecmult.h\"\n"); fprintf(fp, "#include \"precomputed_ecmult.h\"\n"); diff --git a/src/secp256k1/src/precompute_ecmult_gen.c b/src/secp256k1/src/precompute_ecmult_gen.c index bfe212fdd2..ce648cb9b0 100644 --- a/src/secp256k1/src/precompute_ecmult_gen.c +++ b/src/secp256k1/src/precompute_ecmult_gen.c @@ -33,7 +33,6 @@ int main(int argc, char **argv) { fprintf(fp, "/* This file was automatically generated by precompute_ecmult_gen. */\n"); fprintf(fp, "/* See ecmult_gen_impl.h for details about the contents of this file. */\n"); - fprintf(fp, "#include \"../include/secp256k1.h\"\n"); fprintf(fp, "#include \"group.h\"\n"); fprintf(fp, "#include \"ecmult_gen.h\"\n"); fprintf(fp, "#include \"precomputed_ecmult_gen.h\"\n"); diff --git a/src/secp256k1/src/precomputed_ecmult.c b/src/secp256k1/src/precomputed_ecmult.c index fbc634ef1b..cbd030ce50 100644 --- a/src/secp256k1/src/precomputed_ecmult.c +++ b/src/secp256k1/src/precomputed_ecmult.c @@ -2,7 +2,6 @@ /* This file contains an array secp256k1_pre_g with odd multiples of the base point G and * an array secp256k1_pre_g_128 with odd multiples of 2^128*G for accelerating the computation of a*P + b*G. */ -#include "../include/secp256k1.h" #include "group.h" #include "ecmult.h" #include "precomputed_ecmult.h" diff --git a/src/secp256k1/src/precomputed_ecmult.h b/src/secp256k1/src/precomputed_ecmult.h index a4aa83e4ca..17df102967 100644 --- a/src/secp256k1/src/precomputed_ecmult.h +++ b/src/secp256k1/src/precomputed_ecmult.h @@ -11,6 +11,7 @@ extern "C" { #endif +#include "ecmult.h" #include "group.h" #if defined(EXHAUSTIVE_TEST_ORDER) # if EXHAUSTIVE_TEST_ORDER == 7 diff --git a/src/secp256k1/src/precomputed_ecmult_gen.c b/src/secp256k1/src/precomputed_ecmult_gen.c index e9d62a1c1b..75ec59c27a 100644 --- a/src/secp256k1/src/precomputed_ecmult_gen.c +++ b/src/secp256k1/src/precomputed_ecmult_gen.c @@ -1,6 +1,5 @@ /* This file was automatically generated by precompute_ecmult_gen. */ /* See ecmult_gen_impl.h for details about the contents of this file. */ -#include "../include/secp256k1.h" #include "group.h" #include "ecmult_gen.h" #include "precomputed_ecmult_gen.h" diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h index 1959dae986..1d14740577 100644 --- a/src/secp256k1/src/scalar_4x64_impl.h +++ b/src/secp256k1/src/scalar_4x64_impl.h @@ -10,6 +10,7 @@ #include "checkmem.h" #include "int128.h" #include "modinv64_impl.h" +#include "util.h" /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) @@ -110,8 +111,9 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { secp256k1_uint128 t; + volatile int vflag = flag; VERIFY_CHECK(bit < 256); - bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ + bit += ((uint32_t) vflag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ secp256k1_u128_from_u64(&t, r->d[0]); secp256k1_u128_accum_u64(&t, ((uint64_t)((bit >> 6) == 0)) << (bit & 0x3F)); r->d[0] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); @@ -131,10 +133,10 @@ static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { int over; - r->d[0] = (uint64_t)b32[31] | (uint64_t)b32[30] << 8 | (uint64_t)b32[29] << 16 | (uint64_t)b32[28] << 24 | (uint64_t)b32[27] << 32 | (uint64_t)b32[26] << 40 | (uint64_t)b32[25] << 48 | (uint64_t)b32[24] << 56; - r->d[1] = (uint64_t)b32[23] | (uint64_t)b32[22] << 8 | (uint64_t)b32[21] << 16 | (uint64_t)b32[20] << 24 | (uint64_t)b32[19] << 32 | (uint64_t)b32[18] << 40 | (uint64_t)b32[17] << 48 | (uint64_t)b32[16] << 56; - r->d[2] = (uint64_t)b32[15] | (uint64_t)b32[14] << 8 | (uint64_t)b32[13] << 16 | (uint64_t)b32[12] << 24 | (uint64_t)b32[11] << 32 | (uint64_t)b32[10] << 40 | (uint64_t)b32[9] << 48 | (uint64_t)b32[8] << 56; - r->d[3] = (uint64_t)b32[7] | (uint64_t)b32[6] << 8 | (uint64_t)b32[5] << 16 | (uint64_t)b32[4] << 24 | (uint64_t)b32[3] << 32 | (uint64_t)b32[2] << 40 | (uint64_t)b32[1] << 48 | (uint64_t)b32[0] << 56; + r->d[0] = secp256k1_read_be64(&b32[24]); + r->d[1] = secp256k1_read_be64(&b32[16]); + r->d[2] = secp256k1_read_be64(&b32[8]); + r->d[3] = secp256k1_read_be64(&b32[0]); over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); if (overflow) { *overflow = over; @@ -142,10 +144,10 @@ static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b } static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { - bin[0] = a->d[3] >> 56; bin[1] = a->d[3] >> 48; bin[2] = a->d[3] >> 40; bin[3] = a->d[3] >> 32; bin[4] = a->d[3] >> 24; bin[5] = a->d[3] >> 16; bin[6] = a->d[3] >> 8; bin[7] = a->d[3]; - bin[8] = a->d[2] >> 56; bin[9] = a->d[2] >> 48; bin[10] = a->d[2] >> 40; bin[11] = a->d[2] >> 32; bin[12] = a->d[2] >> 24; bin[13] = a->d[2] >> 16; bin[14] = a->d[2] >> 8; bin[15] = a->d[2]; - bin[16] = a->d[1] >> 56; bin[17] = a->d[1] >> 48; bin[18] = a->d[1] >> 40; bin[19] = a->d[1] >> 32; bin[20] = a->d[1] >> 24; bin[21] = a->d[1] >> 16; bin[22] = a->d[1] >> 8; bin[23] = a->d[1]; - bin[24] = a->d[0] >> 56; bin[25] = a->d[0] >> 48; bin[26] = a->d[0] >> 40; bin[27] = a->d[0] >> 32; bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; + secp256k1_write_be64(&bin[0], a->d[3]); + secp256k1_write_be64(&bin[8], a->d[2]); + secp256k1_write_be64(&bin[16], a->d[1]); + secp256k1_write_be64(&bin[24], a->d[0]); } SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { @@ -188,7 +190,8 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { /* If we are flag = 0, mask = 00...00 and this is a no-op; * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ - uint64_t mask = !flag - 1; + volatile int vflag = flag; + uint64_t mask = -vflag; uint64_t nonzero = (secp256k1_scalar_is_zero(r) != 0) - 1; secp256k1_uint128 t; secp256k1_u128_from_u64(&t, r->d[0] ^ mask); @@ -380,7 +383,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) "movq %%r10, %q5\n" /* extract m6 */ "movq %%r8, %q6\n" - : "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) + : "=&g"(m0), "=&g"(m1), "=&g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) : "S"(l), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1) : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h index a2555cbbcd..80ef3ef248 100644 --- a/src/secp256k1/src/scalar_8x32_impl.h +++ b/src/secp256k1/src/scalar_8x32_impl.h @@ -9,6 +9,7 @@ #include "checkmem.h" #include "modinv32_impl.h" +#include "util.h" /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) @@ -141,8 +142,9 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { uint64_t t; + volatile int vflag = flag; VERIFY_CHECK(bit < 256); - bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ + bit += ((uint32_t) vflag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ t = (uint64_t)r->d[0] + (((uint32_t)((bit >> 5) == 0)) << (bit & 0x1F)); r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; t += (uint64_t)r->d[1] + (((uint32_t)((bit >> 5) == 1)) << (bit & 0x1F)); @@ -167,14 +169,14 @@ static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { int over; - r->d[0] = (uint32_t)b32[31] | (uint32_t)b32[30] << 8 | (uint32_t)b32[29] << 16 | (uint32_t)b32[28] << 24; - r->d[1] = (uint32_t)b32[27] | (uint32_t)b32[26] << 8 | (uint32_t)b32[25] << 16 | (uint32_t)b32[24] << 24; - r->d[2] = (uint32_t)b32[23] | (uint32_t)b32[22] << 8 | (uint32_t)b32[21] << 16 | (uint32_t)b32[20] << 24; - r->d[3] = (uint32_t)b32[19] | (uint32_t)b32[18] << 8 | (uint32_t)b32[17] << 16 | (uint32_t)b32[16] << 24; - r->d[4] = (uint32_t)b32[15] | (uint32_t)b32[14] << 8 | (uint32_t)b32[13] << 16 | (uint32_t)b32[12] << 24; - r->d[5] = (uint32_t)b32[11] | (uint32_t)b32[10] << 8 | (uint32_t)b32[9] << 16 | (uint32_t)b32[8] << 24; - r->d[6] = (uint32_t)b32[7] | (uint32_t)b32[6] << 8 | (uint32_t)b32[5] << 16 | (uint32_t)b32[4] << 24; - r->d[7] = (uint32_t)b32[3] | (uint32_t)b32[2] << 8 | (uint32_t)b32[1] << 16 | (uint32_t)b32[0] << 24; + r->d[0] = secp256k1_read_be32(&b32[28]); + r->d[1] = secp256k1_read_be32(&b32[24]); + r->d[2] = secp256k1_read_be32(&b32[20]); + r->d[3] = secp256k1_read_be32(&b32[16]); + r->d[4] = secp256k1_read_be32(&b32[12]); + r->d[5] = secp256k1_read_be32(&b32[8]); + r->d[6] = secp256k1_read_be32(&b32[4]); + r->d[7] = secp256k1_read_be32(&b32[0]); over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); if (overflow) { *overflow = over; @@ -182,14 +184,14 @@ static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b } static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { - bin[0] = a->d[7] >> 24; bin[1] = a->d[7] >> 16; bin[2] = a->d[7] >> 8; bin[3] = a->d[7]; - bin[4] = a->d[6] >> 24; bin[5] = a->d[6] >> 16; bin[6] = a->d[6] >> 8; bin[7] = a->d[6]; - bin[8] = a->d[5] >> 24; bin[9] = a->d[5] >> 16; bin[10] = a->d[5] >> 8; bin[11] = a->d[5]; - bin[12] = a->d[4] >> 24; bin[13] = a->d[4] >> 16; bin[14] = a->d[4] >> 8; bin[15] = a->d[4]; - bin[16] = a->d[3] >> 24; bin[17] = a->d[3] >> 16; bin[18] = a->d[3] >> 8; bin[19] = a->d[3]; - bin[20] = a->d[2] >> 24; bin[21] = a->d[2] >> 16; bin[22] = a->d[2] >> 8; bin[23] = a->d[2]; - bin[24] = a->d[1] >> 24; bin[25] = a->d[1] >> 16; bin[26] = a->d[1] >> 8; bin[27] = a->d[1]; - bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; + secp256k1_write_be32(&bin[0], a->d[7]); + secp256k1_write_be32(&bin[4], a->d[6]); + secp256k1_write_be32(&bin[8], a->d[5]); + secp256k1_write_be32(&bin[12], a->d[4]); + secp256k1_write_be32(&bin[16], a->d[3]); + secp256k1_write_be32(&bin[20], a->d[2]); + secp256k1_write_be32(&bin[24], a->d[1]); + secp256k1_write_be32(&bin[28], a->d[0]); } SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { @@ -241,7 +243,8 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { /* If we are flag = 0, mask = 00...00 and this is a no-op; * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ - uint32_t mask = !flag - 1; + volatile int vflag = flag; + uint32_t mask = -vflag; uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); r->d[0] = t & nonzero; t >>= 32; diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h index bfd1139110..428a5deb33 100644 --- a/src/secp256k1/src/scalar_low_impl.h +++ b/src/secp256k1/src/scalar_low_impl.h @@ -9,6 +9,7 @@ #include "checkmem.h" #include "scalar.h" +#include "util.h" #include <string.h> diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c index 7af333ca90..4c11e7f0b8 100644 --- a/src/secp256k1/src/secp256k1.c +++ b/src/secp256k1/src/secp256k1.c @@ -247,8 +247,8 @@ static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, } else { /* Otherwise, fall back to 32-byte big endian for X and Y. */ secp256k1_fe x, y; - secp256k1_fe_set_b32(&x, pubkey->data); - secp256k1_fe_set_b32(&y, pubkey->data + 32); + ARG_CHECK(secp256k1_fe_set_b32_limit(&x, pubkey->data)); + ARG_CHECK(secp256k1_fe_set_b32_limit(&y, pubkey->data + 32)); secp256k1_ge_set_xy(ge, &x, &y); } ARG_CHECK(!secp256k1_fe_is_zero(&ge->x)); @@ -811,3 +811,7 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, #ifdef ENABLE_MODULE_SCHNORRSIG # include "modules/schnorrsig/main_impl.h" #endif + +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/main_impl.h" +#endif diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h index d109bb9f8b..721099d039 100644 --- a/src/secp256k1/src/testrand.h +++ b/src/secp256k1/src/testrand.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_TESTRAND_H #define SECP256K1_TESTRAND_H +#include "util.h" + /* A non-cryptographic RNG used only for test infrastructure. */ /** Seed the pseudorandom number generator for testing. */ diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h index e9b9d7ded4..fe97620435 100644 --- a/src/secp256k1/src/testrand_impl.h +++ b/src/secp256k1/src/testrand_impl.h @@ -13,10 +13,9 @@ #include "testrand.h" #include "hash.h" +#include "util.h" static uint64_t secp256k1_test_state[4]; -static uint64_t secp256k1_test_rng_integer; -static int secp256k1_test_rng_integer_bits_left = 0; SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16) { static const unsigned char PREFIX[19] = "secp256k1 test init"; @@ -35,7 +34,6 @@ SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16 for (j = 0; j < 8; ++j) s = (s << 8) | out32[8*i + j]; secp256k1_test_state[i] = s; } - secp256k1_test_rng_integer_bits_left = 0; } SECP256K1_INLINE static uint64_t rotl(const uint64_t x, int k) { @@ -56,58 +54,30 @@ SECP256K1_INLINE static uint64_t secp256k1_testrand64(void) { } SECP256K1_INLINE static uint64_t secp256k1_testrand_bits(int bits) { - uint64_t ret; - if (secp256k1_test_rng_integer_bits_left < bits) { - secp256k1_test_rng_integer = secp256k1_testrand64(); - secp256k1_test_rng_integer_bits_left = 64; - } - ret = secp256k1_test_rng_integer; - secp256k1_test_rng_integer >>= bits; - secp256k1_test_rng_integer_bits_left -= bits; - ret &= ((~((uint64_t)0)) >> (64 - bits)); - return ret; + if (bits == 0) return 0; + return secp256k1_testrand64() >> (64 - bits); } SECP256K1_INLINE static uint32_t secp256k1_testrand32(void) { - return secp256k1_testrand_bits(32); + return secp256k1_testrand64() >> 32; } static uint32_t secp256k1_testrand_int(uint32_t range) { - /* We want a uniform integer between 0 and range-1, inclusive. - * B is the smallest number such that range <= 2**B. - * two mechanisms implemented here: - * - generate B bits numbers until one below range is found, and return it - * - find the largest multiple M of range that is <= 2**(B+A), generate B+A - * bits numbers until one below M is found, and return it modulo range - * The second mechanism consumes A more bits of entropy in every iteration, - * but may need fewer iterations due to M being closer to 2**(B+A) then - * range is to 2**B. The array below (indexed by B) contains a 0 when the - * first mechanism is to be used, and the number A otherwise. - */ - static const int addbits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0}; - uint32_t trange, mult; - int bits = 0; - if (range <= 1) { - return 0; - } - trange = range - 1; - while (trange > 0) { - trange >>= 1; - bits++; + uint32_t mask = 0; + uint32_t range_copy; + /* Reduce range by 1, changing its meaning to "maximum value". */ + VERIFY_CHECK(range != 0); + range -= 1; + /* Count the number of bits in range. */ + range_copy = range; + while (range_copy) { + mask = (mask << 1) | 1U; + range_copy >>= 1; } - if (addbits[bits]) { - bits = bits + addbits[bits]; - mult = ((~((uint32_t)0)) >> (32 - bits)) / range; - trange = range * mult; - } else { - trange = range; - mult = 1; - } - while(1) { - uint32_t x = secp256k1_testrand_bits(bits); - if (x < trange) { - return (mult == 1) ? x : (x % range); - } + /* Generation loop. */ + while (1) { + uint32_t val = secp256k1_testrand64() & mask; + if (val <= range) return val; } } diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c index 7f61f737c6..920d31b481 100644 --- a/src/secp256k1/src/tests.c +++ b/src/secp256k1/src/tests.c @@ -10,7 +10,15 @@ #include <time.h> +#ifdef USE_EXTERNAL_DEFAULT_CALLBACKS + #pragma message("Ignoring USE_EXTERNAL_CALLBACKS in tests.") + #undef USE_EXTERNAL_DEFAULT_CALLBACKS +#endif +#if defined(VERIFY) && defined(COVERAGE) + #pragma message("Defining VERIFY for tests being built for coverage analysis support is meaningless.") +#endif #include "secp256k1.c" + #include "../include/secp256k1.h" #include "../include/secp256k1_preallocated.h" #include "testrand_impl.h" @@ -81,16 +89,6 @@ static void uncounting_illegal_callback_fn(const char* str, void* data) { (*p)--; } -static void random_field_element_test(secp256k1_fe *fe) { - do { - unsigned char b32[32]; - secp256k1_testrand256_test(b32); - if (secp256k1_fe_set_b32(fe, b32)) { - break; - } - } while(1); -} - static void random_field_element_magnitude(secp256k1_fe *fe) { secp256k1_fe zero; int n = secp256k1_testrand_int(9); @@ -100,17 +98,33 @@ static void random_field_element_magnitude(secp256k1_fe *fe) { } secp256k1_fe_clear(&zero); secp256k1_fe_negate(&zero, &zero, 0); - secp256k1_fe_mul_int(&zero, n - 1); + secp256k1_fe_mul_int_unchecked(&zero, n - 1); secp256k1_fe_add(fe, &zero); #ifdef VERIFY CHECK(fe->magnitude == n); #endif } +static void random_fe_test(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_testrand256_test(bin); + if (secp256k1_fe_set_b32_limit(x, bin)) { + return; + } + } while(1); +} + +static void random_fe_non_zero_test(secp256k1_fe *fe) { + do { + random_fe_test(fe); + } while(secp256k1_fe_is_zero(fe)); +} + static void random_group_element_test(secp256k1_ge *ge) { secp256k1_fe fe; do { - random_field_element_test(&fe); + random_fe_test(&fe); if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_testrand_bits(1))) { secp256k1_fe_normalize(&ge->y); break; @@ -121,12 +135,7 @@ static void random_group_element_test(secp256k1_ge *ge) { static void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { secp256k1_fe z2, z3; - do { - random_field_element_test(&gej->z); - if (!secp256k1_fe_is_zero(&gej->z)) { - break; - } - } while(1); + random_fe_non_zero_test(&gej->z); secp256k1_fe_sqr(&z2, &gej->z); secp256k1_fe_mul(&z3, &z2, &gej->z); secp256k1_fe_mul(&gej->x, &ge->x, &z2); @@ -172,6 +181,35 @@ static void random_scalar_order_b32(unsigned char *b32) { secp256k1_scalar_get_b32(b32, &num); } +static void run_xoshiro256pp_tests(void) { + { + size_t i; + /* Sanity check that we run before the actual seeding. */ + for (i = 0; i < sizeof(secp256k1_test_state)/sizeof(secp256k1_test_state[0]); i++) { + CHECK(secp256k1_test_state[i] == 0); + } + } + { + int i; + unsigned char buf32[32]; + unsigned char seed16[16] = { + 'C', 'H', 'I', 'C', 'K', 'E', 'N', '!', + 'C', 'H', 'I', 'C', 'K', 'E', 'N', '!', + }; + unsigned char buf32_expected[32] = { + 0xAF, 0xCC, 0xA9, 0x16, 0xB5, 0x6C, 0xE3, 0xF0, + 0x44, 0x3F, 0x45, 0xE0, 0x47, 0xA5, 0x08, 0x36, + 0x4C, 0xCC, 0xC1, 0x18, 0xB2, 0xD8, 0x8F, 0xEF, + 0x43, 0x26, 0x15, 0x57, 0x37, 0x00, 0xEF, 0x30, + }; + secp256k1_testrand_seed(seed16); + for (i = 0; i < 17; i++) { + secp256k1_testrand256(buf32); + } + CHECK(secp256k1_memcmp_var(buf32, buf32_expected, sizeof(buf32)) == 0); + } +} + static void run_selftest_tests(void) { /* Test public API */ secp256k1_selftest(); @@ -689,6 +727,17 @@ static void run_sha256_counter_tests(void) { } } +/* Tests for the equality of two sha256 structs. This function only produces a + * correct result if an integer multiple of 64 many bytes have been written + * into the hash functions. This function is used by some module tests. */ +static void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { + /* Is buffer fully consumed? */ + CHECK((sha1->bytes & 0x3F) == 0); + + CHECK(sha1->bytes == sha2->bytes); + CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0); +} + static void run_hmac_sha256_tests(void) { static const char *keys[6] = { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", @@ -804,78 +853,6 @@ static void run_tagged_sha256_tests(void) { CHECK(secp256k1_memcmp_var(hash32, hash_expected, sizeof(hash32)) == 0); } -/***** RANDOM TESTS *****/ - -static void test_rand_bits(int rand32, int bits) { - /* (1-1/2^B)^rounds[B] < 1/10^9, so rounds is the number of iterations to - * get a false negative chance below once in a billion */ - static const unsigned int rounds[7] = {1, 30, 73, 156, 322, 653, 1316}; - /* We try multiplying the results with various odd numbers, which shouldn't - * influence the uniform distribution modulo a power of 2. */ - static const uint32_t mults[6] = {1, 3, 21, 289, 0x9999, 0x80402011}; - /* We only select up to 6 bits from the output to analyse */ - unsigned int usebits = bits > 6 ? 6 : bits; - unsigned int maxshift = bits - usebits; - /* For each of the maxshift+1 usebits-bit sequences inside a bits-bit - number, track all observed outcomes, one per bit in a uint64_t. */ - uint64_t x[6][27] = {{0}}; - unsigned int i, shift, m; - /* Multiply the output of all rand calls with the odd number m, which - should not change the uniformity of its distribution. */ - for (i = 0; i < rounds[usebits]; i++) { - uint32_t r = (rand32 ? secp256k1_testrand32() : secp256k1_testrand_bits(bits)); - CHECK((((uint64_t)r) >> bits) == 0); - for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { - uint32_t rm = r * mults[m]; - for (shift = 0; shift <= maxshift; shift++) { - x[m][shift] |= (((uint64_t)1) << ((rm >> shift) & ((1 << usebits) - 1))); - } - } - } - for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { - for (shift = 0; shift <= maxshift; shift++) { - /* Test that the lower usebits bits of x[shift] are 1 */ - CHECK(((~x[m][shift]) << (64 - (1 << usebits))) == 0); - } - } -} - -/* Subrange must be a whole divisor of range, and at most 64 */ -static void test_rand_int(uint32_t range, uint32_t subrange) { - /* (1-1/subrange)^rounds < 1/10^9 */ - int rounds = (subrange * 2073) / 100; - int i; - uint64_t x = 0; - CHECK((range % subrange) == 0); - for (i = 0; i < rounds; i++) { - uint32_t r = secp256k1_testrand_int(range); - CHECK(r < range); - r = r % subrange; - x |= (((uint64_t)1) << r); - } - /* Test that the lower subrange bits of x are 1. */ - CHECK(((~x) << (64 - subrange)) == 0); -} - -static void run_rand_bits(void) { - size_t b; - test_rand_bits(1, 32); - for (b = 1; b <= 32; b++) { - test_rand_bits(0, b); - } -} - -static void run_rand_int(void) { - static const uint32_t ms[] = {1, 3, 17, 1000, 13771, 999999, 33554432}; - static const uint32_t ss[] = {1, 3, 6, 9, 13, 31, 64}; - unsigned int m, s; - for (m = 0; m < sizeof(ms) / sizeof(ms[0]); m++) { - for (s = 0; s < sizeof(ss) / sizeof(ss[0]); s++) { - test_rand_int(ms[m] * ss[s], ss[s]); - } - } -} - /***** MODINV TESTS *****/ /* Compute the modular inverse of (odd) x mod 2^64. */ @@ -2221,7 +2198,7 @@ static void scalar_test(void) { for (i = 0; i < 100; ++i) { int low; int shift = 1 + secp256k1_testrand_int(15); - int expected = r.d[0] % (1 << shift); + int expected = r.d[0] % (1ULL << shift); low = secp256k1_scalar_shr_int(&r, shift); CHECK(expected == low); } @@ -2299,26 +2276,23 @@ static void scalar_test(void) { { /* Test multiplicative identity. */ - secp256k1_scalar r1, v1; - secp256k1_scalar_set_int(&v1,1); - secp256k1_scalar_mul(&r1, &s1, &v1); + secp256k1_scalar r1; + secp256k1_scalar_mul(&r1, &s1, &secp256k1_scalar_one); CHECK(secp256k1_scalar_eq(&r1, &s1)); } { /* Test additive identity. */ - secp256k1_scalar r1, v0; - secp256k1_scalar_set_int(&v0,0); - secp256k1_scalar_add(&r1, &s1, &v0); + secp256k1_scalar r1; + secp256k1_scalar_add(&r1, &s1, &secp256k1_scalar_zero); CHECK(secp256k1_scalar_eq(&r1, &s1)); } { /* Test zero product property. */ - secp256k1_scalar r1, v0; - secp256k1_scalar_set_int(&v0,0); - secp256k1_scalar_mul(&r1, &s1, &v0); - CHECK(secp256k1_scalar_eq(&r1, &v0)); + secp256k1_scalar r1; + secp256k1_scalar_mul(&r1, &s1, &secp256k1_scalar_zero); + CHECK(secp256k1_scalar_eq(&r1, &secp256k1_scalar_zero)); } } @@ -2350,12 +2324,24 @@ static void run_scalar_tests(void) { } { + /* Check that the scalar constants secp256k1_scalar_zero and + secp256k1_scalar_one contain the expected values. */ + secp256k1_scalar zero, one; + + CHECK(secp256k1_scalar_is_zero(&secp256k1_scalar_zero)); + secp256k1_scalar_set_int(&zero, 0); + CHECK(secp256k1_scalar_eq(&zero, &secp256k1_scalar_zero)); + + CHECK(secp256k1_scalar_is_one(&secp256k1_scalar_one)); + secp256k1_scalar_set_int(&one, 1); + CHECK(secp256k1_scalar_eq(&one, &secp256k1_scalar_one)); + } + + { /* (-1)+1 should be zero. */ - secp256k1_scalar s, o; - secp256k1_scalar_set_int(&s, 1); - CHECK(secp256k1_scalar_is_one(&s)); - secp256k1_scalar_negate(&o, &s); - secp256k1_scalar_add(&o, &o, &s); + secp256k1_scalar o; + secp256k1_scalar_negate(&o, &secp256k1_scalar_one); + secp256k1_scalar_add(&o, &o, &secp256k1_scalar_one); CHECK(secp256k1_scalar_is_zero(&o)); secp256k1_scalar_negate(&o, &o); CHECK(secp256k1_scalar_is_zero(&o)); @@ -2380,7 +2366,6 @@ static void run_scalar_tests(void) { secp256k1_scalar y; secp256k1_scalar z; secp256k1_scalar zz; - secp256k1_scalar one; secp256k1_scalar r1; secp256k1_scalar r2; secp256k1_scalar zzv; @@ -2917,7 +2902,6 @@ static void run_scalar_tests(void) { 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}} }; - secp256k1_scalar_set_int(&one, 1); for (i = 0; i < 33; i++) { secp256k1_scalar_set_b32(&x, chal[i][0], &overflow); CHECK(!overflow); @@ -2940,7 +2924,7 @@ static void run_scalar_tests(void) { CHECK(secp256k1_scalar_eq(&x, &z)); secp256k1_scalar_mul(&zz, &zz, &y); CHECK(!secp256k1_scalar_check_overflow(&zz)); - CHECK(secp256k1_scalar_eq(&one, &zz)); + CHECK(secp256k1_scalar_eq(&secp256k1_scalar_one, &zz)); } } } @@ -2952,17 +2936,7 @@ static void random_fe(secp256k1_fe *x) { unsigned char bin[32]; do { secp256k1_testrand256(bin); - if (secp256k1_fe_set_b32(x, bin)) { - return; - } - } while(1); -} - -static void random_fe_test(secp256k1_fe *x) { - unsigned char bin[32]; - do { - secp256k1_testrand256_test(bin); - if (secp256k1_fe_set_b32(x, bin)) { + if (secp256k1_fe_set_b32_limit(x, bin)) { return; } } while(1); @@ -3016,7 +2990,7 @@ static void run_field_convert(void) { unsigned char b322[32]; secp256k1_fe_storage fes2; /* Check conversions to fe. */ - CHECK(secp256k1_fe_set_b32(&fe2, b32)); + CHECK(secp256k1_fe_set_b32_limit(&fe2, b32)); CHECK(secp256k1_fe_equal_var(&fe, &fe2)); secp256k1_fe_from_storage(&fe2, &fes); CHECK(secp256k1_fe_equal_var(&fe, &fe2)); @@ -3027,13 +3001,75 @@ static void run_field_convert(void) { CHECK(secp256k1_memcmp_var(&fes2, &fes, sizeof(fes)) == 0); } +static void run_field_be32_overflow(void) { + { + static const unsigned char zero_overflow[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, + }; + static const unsigned char zero[32] = { 0x00 }; + unsigned char out[32]; + secp256k1_fe fe; + CHECK(secp256k1_fe_set_b32_limit(&fe, zero_overflow) == 0); + secp256k1_fe_set_b32_mod(&fe, zero_overflow); + CHECK(secp256k1_fe_normalizes_to_zero(&fe) == 1); + secp256k1_fe_normalize(&fe); + CHECK(secp256k1_fe_is_zero(&fe) == 1); + secp256k1_fe_get_b32(out, &fe); + CHECK(secp256k1_memcmp_var(out, zero, 32) == 0); + } + { + static const unsigned char one_overflow[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30, + }; + static const unsigned char one[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + unsigned char out[32]; + secp256k1_fe fe; + CHECK(secp256k1_fe_set_b32_limit(&fe, one_overflow) == 0); + secp256k1_fe_set_b32_mod(&fe, one_overflow); + secp256k1_fe_normalize(&fe); + CHECK(secp256k1_fe_cmp_var(&fe, &secp256k1_fe_one) == 0); + secp256k1_fe_get_b32(out, &fe); + CHECK(secp256k1_memcmp_var(out, one, 32) == 0); + } + { + static const unsigned char ff_overflow[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + static const unsigned char ff[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xD0, + }; + unsigned char out[32]; + secp256k1_fe fe; + const secp256k1_fe fe_ff = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0x01, 0x000003d0); + CHECK(secp256k1_fe_set_b32_limit(&fe, ff_overflow) == 0); + secp256k1_fe_set_b32_mod(&fe, ff_overflow); + secp256k1_fe_normalize(&fe); + CHECK(secp256k1_fe_cmp_var(&fe, &fe_ff) == 0); + secp256k1_fe_get_b32(out, &fe); + CHECK(secp256k1_memcmp_var(out, ff, 32) == 0); + } +} + /* Returns true if two field elements have the same representation. */ static int fe_identical(const secp256k1_fe *a, const secp256k1_fe *b) { int ret = 1; -#ifdef VERIFY - ret &= (a->magnitude == b->magnitude); - ret &= (a->normalized == b->normalized); -#endif /* Compare the struct member that holds the limbs. */ ret &= (secp256k1_memcmp_var(a->n, b->n, sizeof(a->n)) == 0); return ret; @@ -3121,16 +3157,22 @@ static void run_field_misc(void) { q = x; secp256k1_fe_cmov(&x, &z, 0); #ifdef VERIFY - CHECK(x.normalized && x.magnitude == 1); + CHECK(!x.normalized); + CHECK((x.magnitude == q.magnitude) || (x.magnitude == z.magnitude)); + CHECK((x.magnitude >= q.magnitude) && (x.magnitude >= z.magnitude)); #endif + x = q; secp256k1_fe_cmov(&x, &x, 1); CHECK(!fe_identical(&x, &z)); CHECK(fe_identical(&x, &q)); secp256k1_fe_cmov(&q, &z, 1); #ifdef VERIFY - CHECK(!q.normalized && q.magnitude == z.magnitude); + CHECK(!q.normalized); + CHECK((q.magnitude == x.magnitude) || (q.magnitude == z.magnitude)); + CHECK((q.magnitude >= x.magnitude) && (q.magnitude >= z.magnitude)); #endif CHECK(fe_identical(&q, &z)); + q = z; secp256k1_fe_normalize_var(&x); secp256k1_fe_normalize_var(&z); CHECK(!secp256k1_fe_equal_var(&x, &z)); @@ -3140,11 +3182,11 @@ static void run_field_misc(void) { CHECK(q.normalized && q.magnitude == 1); #endif for (j = 0; j < 6; j++) { - secp256k1_fe_negate(&z, &z, j+1); + secp256k1_fe_negate_unchecked(&z, &z, j+1); secp256k1_fe_normalize_var(&q); secp256k1_fe_cmov(&q, &z, (j&1)); #ifdef VERIFY - CHECK((q.normalized != (j&1)) && q.magnitude == ((j&1) ? z.magnitude : 1)); + CHECK(!q.normalized && q.magnitude == z.magnitude); #endif } secp256k1_fe_normalize_var(&z); @@ -3605,7 +3647,7 @@ static void run_inverse_tests(void) b32[31] = i & 0xff; b32[30] = (i >> 8) & 0xff; secp256k1_scalar_set_b32(&x_scalar, b32, NULL); - secp256k1_fe_set_b32(&x_fe, b32); + secp256k1_fe_set_b32_mod(&x_fe, b32); for (var = 0; var <= 1; ++var) { test_inverse_scalar(NULL, &x_scalar, var); test_inverse_field(NULL, &x_fe, var); @@ -3622,7 +3664,7 @@ static void run_inverse_tests(void) for (i = 0; i < 64 * COUNT; ++i) { (testrand ? secp256k1_testrand256_test : secp256k1_testrand256)(b32); secp256k1_scalar_set_b32(&x_scalar, b32, NULL); - secp256k1_fe_set_b32(&x_fe, b32); + secp256k1_fe_set_b32_mod(&x_fe, b32); for (var = 0; var <= 1; ++var) { test_inverse_scalar(NULL, &x_scalar, var); test_inverse_field(NULL, &x_fe, var); @@ -3692,7 +3734,7 @@ static void test_ge(void) { */ secp256k1_ge *ge = (secp256k1_ge *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_ge) * (1 + 4 * runs)); secp256k1_gej *gej = (secp256k1_gej *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_gej) * (1 + 4 * runs)); - secp256k1_fe zf; + secp256k1_fe zf, r; secp256k1_fe zfi2, zfi3; secp256k1_gej_set_infinity(&gej[0]); @@ -3726,14 +3768,15 @@ static void test_ge(void) { } /* Generate random zf, and zfi2 = 1/zf^2, zfi3 = 1/zf^3 */ - do { - random_field_element_test(&zf); - } while(secp256k1_fe_is_zero(&zf)); + random_fe_non_zero_test(&zf); random_field_element_magnitude(&zf); secp256k1_fe_inv_var(&zfi3, &zf); secp256k1_fe_sqr(&zfi2, &zfi3); secp256k1_fe_mul(&zfi3, &zfi3, &zfi2); + /* Generate random r */ + random_fe_non_zero_test(&r); + for (i1 = 0; i1 < 1 + 4 * runs; i1++) { int i2; for (i2 = 0; i2 < 1 + 4 * runs; i2++) { @@ -3846,6 +3889,29 @@ static void test_ge(void) { free(ge_set_all); } + /* Test that all elements have X coordinates on the curve. */ + for (i = 1; i < 4 * runs + 1; i++) { + secp256k1_fe n; + CHECK(secp256k1_ge_x_on_curve_var(&ge[i].x)); + /* And the same holds after random rescaling. */ + secp256k1_fe_mul(&n, &zf, &ge[i].x); + CHECK(secp256k1_ge_x_frac_on_curve_var(&n, &zf)); + } + + /* Test correspondence of secp256k1_ge_x{,_frac}_on_curve_var with ge_set_xo. */ + { + secp256k1_fe n; + secp256k1_ge q; + int ret_on_curve, ret_frac_on_curve, ret_set_xo; + secp256k1_fe_mul(&n, &zf, &r); + ret_on_curve = secp256k1_ge_x_on_curve_var(&r); + ret_frac_on_curve = secp256k1_ge_x_frac_on_curve_var(&n, &zf); + ret_set_xo = secp256k1_ge_set_xo_var(&q, &r, 0); + CHECK(ret_on_curve == ret_frac_on_curve); + CHECK(ret_on_curve == ret_set_xo); + if (ret_set_xo) CHECK(secp256k1_fe_equal_var(&r, &q.x)); + } + /* Test batch gej -> ge conversion with many infinities. */ for (i = 0; i < 4 * runs + 1; i++) { int odd; @@ -3926,22 +3992,15 @@ static void test_add_neg_y_diff_x(void) { * which this test is a regression test for. * * These points were generated in sage as - * # secp256k1 params - * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) - * C = EllipticCurve ([F (0), F (7)]) - * G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798) - * N = FiniteField(G.order()) * - * # endomorphism values (lambda is 1^{1/3} in N, beta is 1^{1/3} in F) - * x = polygen(N) - * lam = (1 - x^3).roots()[1][0] + * load("secp256k1_params.sage") * * # random "bad pair" * P = C.random_element() - * Q = -int(lam) * P - * print " P: %x %x" % P.xy() - * print " Q: %x %x" % Q.xy() - * print "P + Q: %x %x" % (P + Q).xy() + * Q = -int(LAMBDA) * P + * print(" P: %x %x" % P.xy()) + * print(" Q: %x %x" % Q.xy()) + * print("P + Q: %x %x" % (P + Q).xy()) */ secp256k1_gej aj = SECP256K1_GEJ_CONST( 0x8d24cd95, 0x0a355af1, 0x3c543505, 0x44238d30, @@ -4026,10 +4085,7 @@ static void run_gej(void) { CHECK(!secp256k1_gej_eq_var(&a, &b)); b = a; - random_field_element_test(&fe); - if (secp256k1_fe_is_zero(&fe)) { - continue; - } + random_fe_non_zero_test(&fe); secp256k1_gej_rescale(&a, &fe); CHECK(secp256k1_gej_eq_var(&a, &b)); } @@ -4338,9 +4394,9 @@ static void test_ecmult_target(const secp256k1_scalar* target, int mode) { secp256k1_ecmult(&p2j, &pj, &n2, &zero); secp256k1_ecmult(&ptj, &pj, target, &zero); } else { - secp256k1_ecmult_const(&p1j, &p, &n1, 256); - secp256k1_ecmult_const(&p2j, &p, &n2, 256); - secp256k1_ecmult_const(&ptj, &p, target, 256); + secp256k1_ecmult_const(&p1j, &p, &n1); + secp256k1_ecmult_const(&p2j, &p, &n2); + secp256k1_ecmult_const(&ptj, &p, target); } /* Add them all up: n1*P + n2*P + target*P = (n1+n2+target)*P = (n1+n1-n1-n2)*P = 0. */ @@ -4403,7 +4459,7 @@ static void ecmult_const_random_mult(void) { 0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956 ); secp256k1_gej b; - secp256k1_ecmult_const(&b, &a, &xn, 256); + secp256k1_ecmult_const(&b, &a, &xn); CHECK(secp256k1_ge_is_valid_var(&a)); ge_equals_gej(&expected_b, &b); @@ -4419,12 +4475,12 @@ static void ecmult_const_commutativity(void) { random_scalar_order_test(&a); random_scalar_order_test(&b); - secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a, 256); - secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b, 256); + secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a); + secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b); secp256k1_ge_set_gej(&mid1, &res1); secp256k1_ge_set_gej(&mid2, &res2); - secp256k1_ecmult_const(&res1, &mid1, &b, 256); - secp256k1_ecmult_const(&res2, &mid2, &a, 256); + secp256k1_ecmult_const(&res1, &mid1, &b); + secp256k1_ecmult_const(&res2, &mid2, &a); secp256k1_ge_set_gej(&mid1, &res1); secp256k1_ge_set_gej(&mid2, &res2); ge_equals_ge(&mid1, &mid2); @@ -4440,13 +4496,13 @@ static void ecmult_const_mult_zero_one(void) { secp256k1_scalar_negate(&negone, &one); random_group_element_test(&point); - secp256k1_ecmult_const(&res1, &point, &zero, 3); + secp256k1_ecmult_const(&res1, &point, &zero); secp256k1_ge_set_gej(&res2, &res1); CHECK(secp256k1_ge_is_infinity(&res2)); - secp256k1_ecmult_const(&res1, &point, &one, 2); + secp256k1_ecmult_const(&res1, &point, &one); secp256k1_ge_set_gej(&res2, &res1); ge_equals_ge(&res2, &point); - secp256k1_ecmult_const(&res1, &point, &negone, 256); + secp256k1_ecmult_const(&res1, &point, &negone); secp256k1_gej_neg(&res1, &res1); secp256k1_ge_set_gej(&res2, &res1); ge_equals_ge(&res2, &point); @@ -4468,15 +4524,13 @@ static void ecmult_const_mult_xonly(void) { random_scalar_order_test(&q); /* If i is odd, n=d*base.x for random non-zero d */ if (i & 1) { - do { - random_field_element_test(&d); - } while (secp256k1_fe_normalizes_to_zero_var(&d)); + random_fe_non_zero_test(&d); secp256k1_fe_mul(&n, &base.x, &d); } else { n = base.x; } /* Perform x-only multiplication. */ - res = secp256k1_ecmult_const_xonly(&resx, &n, (i & 1) ? &d : NULL, &q, 256, i & 2); + res = secp256k1_ecmult_const_xonly(&resx, &n, (i & 1) ? &d : NULL, &q, i & 2); CHECK(res); /* Perform normal multiplication. */ secp256k1_gej_set_ge(&basej, &base); @@ -4489,27 +4543,22 @@ static void ecmult_const_mult_xonly(void) { /* Test that secp256k1_ecmult_const_xonly correctly rejects X coordinates not on curve. */ for (i = 0; i < 2*COUNT; ++i) { - secp256k1_fe x, n, d, c, r; + secp256k1_fe x, n, d, r; int res; secp256k1_scalar q; random_scalar_order_test(&q); /* Generate random X coordinate not on the curve. */ do { - random_field_element_test(&x); - secp256k1_fe_sqr(&c, &x); - secp256k1_fe_mul(&c, &c, &x); - secp256k1_fe_add(&c, &secp256k1_fe_const_b); - } while (secp256k1_fe_is_square_var(&c)); + random_fe_test(&x); + } while (secp256k1_ge_x_on_curve_var(&x)); /* If i is odd, n=d*x for random non-zero d. */ if (i & 1) { - do { - random_field_element_test(&d); - } while (secp256k1_fe_normalizes_to_zero_var(&d)); + random_fe_non_zero_test(&d); secp256k1_fe_mul(&n, &x, &d); } else { n = x; } - res = secp256k1_ecmult_const_xonly(&r, &n, (i & 1) ? &d : NULL, &q, 256, 0); + res = secp256k1_ecmult_const_xonly(&r, &n, (i & 1) ? &d : NULL, &q, 0); CHECK(res == 0); } } @@ -4534,7 +4583,7 @@ static void ecmult_const_chain_multiply(void) { for (i = 0; i < 100; ++i) { secp256k1_ge tmp; secp256k1_ge_set_gej(&tmp, &point); - secp256k1_ecmult_const(&point, &tmp, &scalar, 256); + secp256k1_ecmult_const(&point, &tmp, &scalar); } secp256k1_ge_set_gej(&res, &point); ge_equals_gej(&res, &expected_point); @@ -4570,7 +4619,6 @@ static int ecmult_multi_false_callback(secp256k1_scalar *sc, secp256k1_ge *pt, s static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func ecmult_multi) { int ncount; - secp256k1_scalar szero; secp256k1_scalar sc[32]; secp256k1_ge pt[32]; secp256k1_gej r; @@ -4579,7 +4627,6 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi data.sc = sc; data.pt = pt; - secp256k1_scalar_set_int(&szero, 0); /* No points to multiply */ CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, NULL, ecmult_multi_callback, &data, 0)); @@ -4597,21 +4644,21 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi pt[1] = secp256k1_ge_const_g; /* only G scalar */ - secp256k1_ecmult(&r2, &ptgj, &szero, &sc[0]); + secp256k1_ecmult(&r2, &ptgj, &secp256k1_scalar_zero, &sc[0]); CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0)); CHECK(secp256k1_gej_eq_var(&r, &r2)); /* 1-point */ - secp256k1_ecmult(&r2, &ptgj, &sc[0], &szero); - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 1)); + secp256k1_ecmult(&r2, &ptgj, &sc[0], &secp256k1_scalar_zero); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 1)); CHECK(secp256k1_gej_eq_var(&r, &r2)); /* Try to multiply 1 point, but callback returns false */ - CHECK(!ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1)); + CHECK(!ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_false_callback, &data, 1)); /* 2-point */ secp256k1_ecmult(&r2, &ptgj, &sc[0], &sc[1]); - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 2)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 2)); CHECK(secp256k1_gej_eq_var(&r, &r2)); /* 2-point with G scalar */ @@ -4631,7 +4678,7 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi random_scalar_order(&sc[i]); secp256k1_ge_set_infinity(&pt[i]); } - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -4641,7 +4688,7 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi pt[i] = ptg; secp256k1_scalar_set_int(&sc[i], 0); } - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -4654,7 +4701,7 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi pt[2 * i + 1] = ptg; } - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); random_scalar_order(&sc[0]); @@ -4667,7 +4714,7 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]); } - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -4682,7 +4729,7 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi secp256k1_scalar_negate(&sc[i], &sc[i]); } - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 32)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 32)); CHECK(secp256k1_gej_is_infinity(&r)); } @@ -4700,8 +4747,8 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi secp256k1_gej_add_ge_var(&r, &r, &pt[i], NULL); } - secp256k1_ecmult(&r2, &r, &sc[0], &szero); - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + secp256k1_ecmult(&r2, &r, &sc[0], &secp256k1_scalar_zero); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 20)); CHECK(secp256k1_gej_eq_var(&r, &r2)); } @@ -4721,8 +4768,8 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi } secp256k1_gej_set_ge(&p0j, &pt[0]); - secp256k1_ecmult(&r2, &p0j, &rs, &szero); - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + secp256k1_ecmult(&r2, &p0j, &rs, &secp256k1_scalar_zero); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 20)); CHECK(secp256k1_gej_eq_var(&r, &r2)); } @@ -4733,13 +4780,13 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi } secp256k1_scalar_clear(&sc[0]); - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 20)); secp256k1_scalar_clear(&sc[1]); secp256k1_scalar_clear(&sc[2]); secp256k1_scalar_clear(&sc[3]); secp256k1_scalar_clear(&sc[4]); - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 6)); - CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &szero, ecmult_multi_callback, &data, 5)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 6)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 5)); CHECK(secp256k1_gej_is_infinity(&r)); /* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */ @@ -4763,8 +4810,8 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi secp256k1_scalar_set_int(&t1, (t1i + 1) / 2); secp256k1_scalar_cond_negate(&t1, t1i & 1); - secp256k1_ecmult(&t0p, &ptgj, &t0, &szero); - secp256k1_ecmult(&t1p, &ptgj, &t1, &szero); + secp256k1_ecmult(&t0p, &ptgj, &t0, &secp256k1_scalar_zero); + secp256k1_ecmult(&t1p, &ptgj, &t1, &secp256k1_scalar_zero); for(s0i = 0; s0i < TOP; s0i++) { for(s1i = 0; s1i < TOP; s1i++) { @@ -4783,8 +4830,8 @@ static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi secp256k1_scalar_mul(&tmp2, &t1, &sc[1]); secp256k1_scalar_add(&tmp1, &tmp1, &tmp2); - secp256k1_ecmult(&expected, &ptgj, &tmp1, &szero); - CHECK(ecmult_multi(&CTX->error_callback, scratch, &actual, &szero, ecmult_multi_callback, &data, 2)); + secp256k1_ecmult(&expected, &ptgj, &tmp1, &secp256k1_scalar_zero); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &actual, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 2)); CHECK(secp256k1_gej_eq_var(&actual, &expected)); } } @@ -4960,7 +5007,6 @@ static int test_ecmult_multi_random(secp256k1_scratch *scratch) { } static void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) { - secp256k1_scalar szero; secp256k1_scalar sc; secp256k1_ge pt; secp256k1_gej r; @@ -4971,11 +5017,10 @@ static void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_mu random_scalar_order(&sc); data.sc = ≻ data.pt = &pt; - secp256k1_scalar_set_int(&szero, 0); /* Try to multiply 1 point, but scratch space is empty.*/ scratch_empty = secp256k1_scratch_create(&CTX->error_callback, 0); - CHECK(!ecmult_multi(&CTX->error_callback, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1)); + CHECK(!ecmult_multi(&CTX->error_callback, scratch_empty, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 1)); secp256k1_scratch_destroy(&CTX->error_callback, scratch_empty); } @@ -5083,7 +5128,6 @@ static void test_ecmult_multi_batch_size_helper(void) { static void test_ecmult_multi_batching(void) { static const int n_points = 2*ECMULT_PIPPENGER_THRESHOLD; secp256k1_scalar scG; - secp256k1_scalar szero; secp256k1_scalar *sc = (secp256k1_scalar *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_scalar) * n_points); secp256k1_ge *pt = (secp256k1_ge *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_ge) * n_points); secp256k1_gej r; @@ -5093,11 +5137,10 @@ static void test_ecmult_multi_batching(void) { secp256k1_scratch *scratch; secp256k1_gej_set_infinity(&r2); - secp256k1_scalar_set_int(&szero, 0); /* Get random scalars and group elements and compute result */ random_scalar_order(&scG); - secp256k1_ecmult(&r2, &r2, &szero, &scG); + secp256k1_ecmult(&r2, &r2, &secp256k1_scalar_zero, &scG); for(i = 0; i < n_points; i++) { secp256k1_ge ptg; secp256k1_gej ptgj; @@ -5432,7 +5475,7 @@ static void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar secp256k1_ecmult(&rj3, &infj, &zero, x); secp256k1_ecmult_multi_var(NULL, scratch, &rj4, x, NULL, NULL, 0); secp256k1_ecmult_multi_var(NULL, scratch, &rj5, &zero, test_ecmult_accumulate_cb, (void*)x, 1); - secp256k1_ecmult_const(&rj6, &secp256k1_ge_const_g, x, 256); + secp256k1_ecmult_const(&rj6, &secp256k1_ge_const_g, x); secp256k1_ge_set_gej_var(&r, &rj1); ge_equals_gej(&r, &rj2); ge_equals_gej(&r, &rj3); @@ -7423,6 +7466,10 @@ static void run_ecdsa_wycheproof(void) { # include "modules/schnorrsig/tests_impl.h" #endif +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/tests_impl.h" +#endif + static void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -7439,16 +7486,31 @@ static void run_secp256k1_memczero_test(void) { } static void run_secp256k1_byteorder_tests(void) { - const uint32_t x = 0xFF03AB45; - const unsigned char x_be[4] = {0xFF, 0x03, 0xAB, 0x45}; - unsigned char buf[4]; - uint32_t x_; + { + const uint32_t x = 0xFF03AB45; + const unsigned char x_be[4] = {0xFF, 0x03, 0xAB, 0x45}; + unsigned char buf[4]; + uint32_t x_; - secp256k1_write_be32(buf, x); - CHECK(secp256k1_memcmp_var(buf, x_be, sizeof(buf)) == 0); + secp256k1_write_be32(buf, x); + CHECK(secp256k1_memcmp_var(buf, x_be, sizeof(buf)) == 0); - x_ = secp256k1_read_be32(buf); - CHECK(x == x_); + x_ = secp256k1_read_be32(buf); + CHECK(x == x_); + } + + { + const uint64_t x = 0xCAFE0123BEEF4567; + const unsigned char x_be[8] = {0xCA, 0xFE, 0x01, 0x23, 0xBE, 0xEF, 0x45, 0x67}; + unsigned char buf[8]; + uint64_t x_; + + secp256k1_write_be64(buf, x); + CHECK(secp256k1_memcmp_var(buf, x_be, sizeof(buf)) == 0); + + x_ = secp256k1_read_be64(buf); + CHECK(x == x_); + } } static void int_cmov_test(void) { @@ -7487,23 +7549,23 @@ static void fe_cmov_test(void) { secp256k1_fe a = zero; secp256k1_fe_cmov(&r, &a, 0); - CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0); + CHECK(fe_identical(&r, &max)); r = zero; a = max; secp256k1_fe_cmov(&r, &a, 1); - CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0); + CHECK(fe_identical(&r, &max)); a = zero; secp256k1_fe_cmov(&r, &a, 1); - CHECK(secp256k1_memcmp_var(&r, &zero, sizeof(r)) == 0); + CHECK(fe_identical(&r, &zero)); a = one; secp256k1_fe_cmov(&r, &a, 1); - CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); + CHECK(fe_identical(&r, &one)); r = one; a = zero; secp256k1_fe_cmov(&r, &a, 0); - CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); + CHECK(fe_identical(&r, &one)); } static void fe_storage_cmov_test(void) { @@ -7630,6 +7692,9 @@ int main(int argc, char **argv) { } printf("test count = %i\n", COUNT); + /* run test RNG tests (must run before we really initialize the test RNG) */ + run_xoshiro256pp_tests(); + /* find random seed */ secp256k1_testrand_init(argc > 2 ? argv[2] : NULL); @@ -7667,10 +7732,6 @@ int main(int argc, char **argv) { /* scratch tests */ run_scratch_tests(); - /* randomness tests */ - run_rand_bits(); - run_rand_int(); - /* integer arithmetic tests */ #ifdef SECP256K1_WIDEMUL_INT128 run_int128_tests(); @@ -7693,6 +7754,7 @@ int main(int argc, char **argv) { run_field_half(); run_field_misc(); run_field_convert(); + run_field_be32_overflow(); run_fe_mul(); run_sqr(); run_sqrt(); @@ -7754,6 +7816,10 @@ int main(int argc, char **argv) { run_schnorrsig_tests(); #endif +#ifdef ENABLE_MODULE_ELLSWIFT + run_ellswift_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); run_secp256k1_byteorder_tests(); diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c index 63b6ef03af..dbb6b7eb46 100644 --- a/src/secp256k1/src/tests_exhaustive.c +++ b/src/secp256k1/src/tests_exhaustive.c @@ -13,13 +13,22 @@ #define EXHAUSTIVE_TEST_ORDER 13 #endif +/* These values of B are all values in [1, 8] that result in a curve with even order. */ +#define EXHAUSTIVE_TEST_CURVE_HAS_EVEN_ORDER (SECP256K1_B == 1 || SECP256K1_B == 6 || SECP256K1_B == 8) + +#ifdef USE_EXTERNAL_DEFAULT_CALLBACKS + #pragma message("Ignoring USE_EXTERNAL_CALLBACKS in exhaustive_tests.") + #undef USE_EXTERNAL_DEFAULT_CALLBACKS +#endif #include "secp256k1.c" + #include "../include/secp256k1.h" #include "assumptions.h" #include "group.h" #include "testrand_impl.h" #include "ecmult_compute_table_impl.h" #include "ecmult_gen_compute_table_impl.h" +#include "util.h" static int count = 2; @@ -54,7 +63,7 @@ static void random_fe(secp256k1_fe *x) { unsigned char bin[32]; do { secp256k1_testrand256(bin); - if (secp256k1_fe_set_b32(x, bin)) { + if (secp256k1_fe_set_b32_limit(x, bin)) { return; } } while(1); @@ -192,7 +201,7 @@ static void test_exhaustive_ecmult(const secp256k1_ge *group, const secp256k1_ge } for (j = 0; j < EXHAUSTIVE_TEST_ORDER; j++) { - for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) { int ret; secp256k1_gej tmp; secp256k1_fe xn, xd, tmpf; @@ -203,19 +212,19 @@ static void test_exhaustive_ecmult(const secp256k1_ge *group, const secp256k1_ge secp256k1_scalar_set_int(&ng, j); /* Test secp256k1_ecmult_const. */ - secp256k1_ecmult_const(&tmp, &group[i], &ng, 256); + secp256k1_ecmult_const(&tmp, &group[i], &ng); ge_equals_gej(&group[(i * j) % EXHAUSTIVE_TEST_ORDER], &tmp); - if (j != 0) { + if (i != 0 && j != 0) { /* Test secp256k1_ecmult_const_xonly with all curve X coordinates, and xd=NULL. */ - ret = secp256k1_ecmult_const_xonly(&tmpf, &group[i].x, NULL, &ng, 256, 0); + ret = secp256k1_ecmult_const_xonly(&tmpf, &group[i].x, NULL, &ng, 0); CHECK(ret); CHECK(secp256k1_fe_equal_var(&tmpf, &group[(i * j) % EXHAUSTIVE_TEST_ORDER].x)); /* Test secp256k1_ecmult_const_xonly with all curve X coordinates, with random xd. */ random_fe_non_zero(&xd); secp256k1_fe_mul(&xn, &xd, &group[i].x); - ret = secp256k1_ecmult_const_xonly(&tmpf, &xn, &xd, &ng, 256, 0); + ret = secp256k1_ecmult_const_xonly(&tmpf, &xn, &xd, &ng, 0); CHECK(ret); CHECK(secp256k1_fe_equal_var(&tmpf, &group[(i * j) % EXHAUSTIVE_TEST_ORDER].x)); } @@ -389,6 +398,10 @@ static void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_g #include "modules/schnorrsig/tests_exhaustive_impl.h" #endif +#ifdef ENABLE_MODULE_ELLSWIFT +#include "modules/ellswift/tests_exhaustive_impl.h" +#endif + int main(int argc, char** argv) { int i; secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; @@ -484,6 +497,15 @@ int main(int argc, char** argv) { #ifdef ENABLE_MODULE_SCHNORRSIG test_exhaustive_schnorrsig(ctx); #endif +#ifdef ENABLE_MODULE_ELLSWIFT + /* The ellswift algorithm does have additional edge cases when operating on + * curves of even order, which are not included in the code as secp256k1 is + * of odd order. Skip the ellswift tests if the used exhaustive tests curve + * is even-ordered accordingly. */ + #if !EXHAUSTIVE_TEST_CURVE_HAS_EVEN_ORDER + test_exhaustive_ellswift(ctx, group); + #endif +#endif secp256k1_context_destroy(ctx); } diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h index e75c5ad552..801ea0c885 100644 --- a/src/secp256k1/src/util.h +++ b/src/secp256k1/src/util.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_UTIL_H #define SECP256K1_UTIL_H +#include "../include/secp256k1.h" + #include <stdlib.h> #include <stdint.h> #include <stdio.h> @@ -17,6 +19,51 @@ #define DEBUG_CONFIG_MSG(x) "DEBUG_CONFIG: " x #define DEBUG_CONFIG_DEF(x) DEBUG_CONFIG_MSG(#x "=" STR(x)) +/* Debug helper for printing arrays of unsigned char. */ +#define PRINT_BUF(buf, len) do { \ + printf("%s[%lu] = ", #buf, (unsigned long)len); \ + print_buf_plain(buf, len); \ +} while(0) + +static void print_buf_plain(const unsigned char *buf, size_t len) { + size_t i; + printf("{"); + for (i = 0; i < len; i++) { + if (i % 8 == 0) { + printf("\n "); + } else { + printf(" "); + } + printf("0x%02X,", buf[i]); + } + printf("\n}\n"); +} + +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(2,7) +# define SECP256K1_INLINE __inline__ +# elif (defined(_MSC_VER)) +# define SECP256K1_INLINE __inline +# else +# define SECP256K1_INLINE +# endif +# else +# define SECP256K1_INLINE inline +# endif + +/** Assert statically that expr is an integer constant expression, and run stmt. + * + * Useful for example to enforce that magnitude arguments are constant. + */ +#define ASSERT_INT_CONST_AND_DO(expr, stmt) do { \ + switch(42) { \ + case /* ERROR: integer argument is not constant */ expr: \ + break; \ + default: ; \ + } \ + stmt; \ +} while(0) + typedef struct { void (*fn)(const char *text, void* data); const void* data; @@ -319,4 +366,28 @@ SECP256K1_INLINE static void secp256k1_write_be32(unsigned char* p, uint32_t x) p[0] = x >> 24; } +/* Read a uint64_t in big endian */ +SECP256K1_INLINE static uint64_t secp256k1_read_be64(const unsigned char* p) { + return (uint64_t)p[0] << 56 | + (uint64_t)p[1] << 48 | + (uint64_t)p[2] << 40 | + (uint64_t)p[3] << 32 | + (uint64_t)p[4] << 24 | + (uint64_t)p[5] << 16 | + (uint64_t)p[6] << 8 | + (uint64_t)p[7]; +} + +/* Write a uint64_t in big endian */ +SECP256K1_INLINE static void secp256k1_write_be64(unsigned char* p, uint64_t x) { + p[7] = x; + p[6] = x >> 8; + p[5] = x >> 16; + p[4] = x >> 24; + p[3] = x >> 32; + p[2] = x >> 40; + p[1] = x >> 48; + p[0] = x >> 56; +} + #endif /* SECP256K1_UTIL_H */ diff --git a/src/secp256k1/tools/tests_wycheproof_generate.py b/src/secp256k1/tools/tests_wycheproof_generate.py index 333f6fbef0..b26dfa89d6 100755 --- a/src/secp256k1/tools/tests_wycheproof_generate.py +++ b/src/secp256k1/tools/tests_wycheproof_generate.py @@ -7,8 +7,6 @@ Generate a C file with ECDSA testvectors from the Wycheproof project. ''' import json -import hashlib -import urllib.request import sys filename_input = sys.argv[1] @@ -19,7 +17,8 @@ with open(filename_input) as f: num_groups = len(doc['testGroups']) def to_c_array(x): - if x == "": return "" + if x == "": + return "" s = ',0x'.join(a+b for a,b in zip(x[::2], x[1::2])) return "0x" + s @@ -43,18 +42,23 @@ for i in range(num_groups): sig_size = len(test_vector['sig']) // 2 msg_size = len(test_vector['msg']) // 2 - if test_vector['result'] == "invalid": expected_verify = 0 - elif test_vector['result'] == "valid": expected_verify = 1 - else: raise ValueError("invalid result field") + if test_vector['result'] == "invalid": + expected_verify = 0 + elif test_vector['result'] == "valid": + expected_verify = 1 + else: + raise ValueError("invalid result field") - if num_vectors != 0 and sig_size != 0: signatures += ",\n " + if num_vectors != 0 and sig_size != 0: + signatures += ",\n " new_msg = False msg = to_c_array(test_vector['msg']) msg_offset = offset_msg_running # check for repeated msg - if msg not in cache_msgs.keys(): - if num_vectors != 0 and msg_size != 0: messages += ",\n " + if msg not in cache_msgs: + if num_vectors != 0 and msg_size != 0: + messages += ",\n " cache_msgs[msg] = offset_msg_running messages += msg new_msg = True @@ -65,8 +69,9 @@ for i in range(num_groups): pk = to_c_array(public_key['uncompressed']) pk_offset = offset_pk_running # check for repeated pk - if pk not in cache_public_keys.keys(): - if num_vectors != 0: public_keys += ",\n " + if pk not in cache_public_keys: + if num_vectors != 0: + public_keys += ",\n " cache_public_keys[pk] = offset_pk_running public_keys += pk new_pk = True @@ -76,15 +81,11 @@ for i in range(num_groups): signatures += to_c_array(test_vector['sig']) out += " /" + "* tcId: " + str(test_vector['tcId']) + ". " + test_vector['comment'] + " *" + "/\n" - out += " {" + "{0}, {1}, {2}, {3}, {4}, {5}".format( - pk_offset, - msg_offset, - msg_size, - offset_sig, - sig_size, - expected_verify) + " },\n" - if new_msg: offset_msg_running += msg_size - if new_pk: offset_pk_running += 65 + out += f" {{{pk_offset}, {msg_offset}, {msg_size}, {offset_sig}, {sig_size}, {expected_verify} }},\n" + if new_msg: + offset_msg_running += msg_size + if new_pk: + offset_pk_running += 65 offset_sig += sig_size num_vectors += 1 @@ -101,7 +102,7 @@ typedef struct { print("/* Note: this file was autogenerated using tests_wycheproof_generate.py. Do not edit. */") -print("#define SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS ({})".format(num_vectors)) +print(f"#define SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS ({num_vectors})") print(struct_definition) diff --git a/src/serialize.h b/src/serialize.h index 7bc7b10779..0cda0ac7d5 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -188,9 +188,11 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; } } \ FORMATTER_METHODS(cls, obj) +// clang-format off #ifndef CHAR_EQUALS_INT8 template <typename Stream> void Serialize(Stream&, char) = delete; // char serialization forbidden. Use uint8_t or int8_t #endif +template <typename Stream> void Serialize(Stream& s, std::byte a) { ser_writedata8(s, uint8_t(a)); } template<typename Stream> inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); } template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); } template<typename Stream> inline void Serialize(Stream& s, int16_t a ) { ser_writedata16(s, a); } @@ -201,12 +203,12 @@ template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_wri template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); } template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(MakeByteSpan(a)); } template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(MakeByteSpan(a)); } -template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(AsBytes(span)); } -template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(AsBytes(span)); } +template <typename Stream, typename B> void Serialize(Stream& s, Span<B> span) { (void)/* force byte-type */UCharCast(span.data()); s.write(AsBytes(span)); } #ifndef CHAR_EQUALS_INT8 template <typename Stream> void Unserialize(Stream&, char) = delete; // char serialization forbidden. Use uint8_t or int8_t #endif +template <typename Stream> void Unserialize(Stream& s, std::byte& a) { a = std::byte{ser_readdata8(s)}; } template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); } template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); } template<typename Stream> inline void Unserialize(Stream& s, int16_t& a ) { a = ser_readdata16(s); } @@ -217,10 +219,11 @@ template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); } template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(MakeWritableByteSpan(a)); } template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(MakeWritableByteSpan(a)); } -template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(AsWritableBytes(span)); } +template <typename Stream, typename B> void Unserialize(Stream& s, Span<B> span) { (void)/* force byte-type */UCharCast(span.data()); s.read(AsWritableBytes(span)); } template <typename Stream> inline void Serialize(Stream& s, bool a) { uint8_t f = a; ser_writedata8(s, f); } template <typename Stream> inline void Unserialize(Stream& s, bool& a) { uint8_t f = ser_readdata8(s); a = f; } +// clang-format on /** @@ -472,10 +475,10 @@ struct CustomUintFormatter if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range"); if (BigEndian) { uint64_t raw = htobe64(v); - s.write({AsBytePtr(&raw) + 8 - Bytes, Bytes}); + s.write(AsBytes(Span{&raw, 1}).last(Bytes)); } else { uint64_t raw = htole64(v); - s.write({AsBytePtr(&raw), Bytes}); + s.write(AsBytes(Span{&raw, 1}).first(Bytes)); } } @@ -485,10 +488,10 @@ struct CustomUintFormatter static_assert(std::numeric_limits<U>::max() >= MAX && std::numeric_limits<U>::min() <= 0, "Assigned type too small"); uint64_t raw = 0; if (BigEndian) { - s.read({AsBytePtr(&raw) + 8 - Bytes, Bytes}); + s.read(AsWritableBytes(Span{&raw, 1}).last(Bytes)); v = static_cast<I>(be64toh(raw)); } else { - s.read({AsBytePtr(&raw), Bytes}); + s.read(AsWritableBytes(Span{&raw, 1}).first(Bytes)); v = static_cast<I>(le64toh(raw)); } } diff --git a/src/shutdown.cpp b/src/shutdown.cpp index d70017d734..fc18ccd207 100644 --- a/src/shutdown.cpp +++ b/src/shutdown.cpp @@ -5,107 +5,40 @@ #include <shutdown.h> -#if defined(HAVE_CONFIG_H) -#include <config/bitcoin-config.h> -#endif - +#include <kernel/context.h> #include <logging.h> -#include <node/interface_ui.h> #include <util/check.h> -#include <util/tokenpipe.h> -#include <warnings.h> +#include <util/signalinterrupt.h> #include <assert.h> -#include <atomic> -#ifdef WIN32 -#include <condition_variable> -#endif - -static std::atomic<int>* g_exit_status{nullptr}; - -bool AbortNode(const std::string& strMessage, bilingual_str user_message) -{ - SetMiscWarning(Untranslated(strMessage)); - LogPrintf("*** %s\n", strMessage); - if (user_message.empty()) { - user_message = _("A fatal internal error occurred, see debug.log for details"); - } - InitError(user_message); - Assert(g_exit_status)->store(EXIT_FAILURE); - StartShutdown(); - return false; -} - -static std::atomic<bool> fRequestShutdown(false); -#ifdef WIN32 -/** On windows it is possible to simply use a condition variable. */ -std::mutex g_shutdown_mutex; -std::condition_variable g_shutdown_cv; -#else -/** On UNIX-like operating systems use the self-pipe trick. - */ -static TokenPipeEnd g_shutdown_r; -static TokenPipeEnd g_shutdown_w; -#endif - -bool InitShutdownState(std::atomic<int>& exit_status) -{ - g_exit_status = &exit_status; -#ifndef WIN32 - std::optional<TokenPipe> pipe = TokenPipe::Make(); - if (!pipe) return false; - g_shutdown_r = pipe->TakeReadEnd(); - g_shutdown_w = pipe->TakeWriteEnd(); -#endif - return true; -} +#include <system_error> void StartShutdown() { -#ifdef WIN32 - std::unique_lock<std::mutex> lk(g_shutdown_mutex); - fRequestShutdown = true; - g_shutdown_cv.notify_one(); -#else - // This must be reentrant and safe for calling in a signal handler, so using a condition variable is not safe. - // Make sure that the token is only written once even if multiple threads call this concurrently or in - // case of a reentrant signal. - if (!fRequestShutdown.exchange(true)) { - // Write an arbitrary byte to the write end of the shutdown pipe. - int res = g_shutdown_w.TokenWrite('x'); - if (res != 0) { - LogPrintf("Sending shutdown token failed\n"); - assert(0); - } + try { + Assert(kernel::g_context)->interrupt(); + } catch (const std::system_error&) { + LogPrintf("Sending shutdown token failed\n"); + assert(0); } -#endif } void AbortShutdown() { - if (fRequestShutdown) { - // Cancel existing shutdown by waiting for it, this will reset condition flags and remove - // the shutdown token from the pipe. - WaitForShutdown(); - } - fRequestShutdown = false; + Assert(kernel::g_context)->interrupt.reset(); } bool ShutdownRequested() { - return fRequestShutdown; + return bool{Assert(kernel::g_context)->interrupt}; } void WaitForShutdown() { -#ifdef WIN32 - std::unique_lock<std::mutex> lk(g_shutdown_mutex); - g_shutdown_cv.wait(lk, [] { return fRequestShutdown.load(); }); -#else - int res = g_shutdown_r.TokenRead(); - if (res != 'x') { + try { + Assert(kernel::g_context)->interrupt.wait(); + } catch (const std::system_error&) { LogPrintf("Reading shutdown token failed\n"); assert(0); } -#endif } diff --git a/src/shutdown.h b/src/shutdown.h index c119bee96f..2d6ace8d93 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -6,18 +6,6 @@ #ifndef BITCOIN_SHUTDOWN_H #define BITCOIN_SHUTDOWN_H -#include <util/translation.h> // For bilingual_str - -#include <atomic> - -/** Abort with a message */ -bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str{}); - -/** Initialize shutdown state. This must be called before using either StartShutdown(), - * AbortShutdown() or WaitForShutdown(). Calling ShutdownRequested() is always safe. - */ -bool InitShutdownState(std::atomic<int>& exit_status); - /** Request shutdown of the application. */ void StartShutdown(); diff --git a/src/span.h b/src/span.h index 4692eca7fb..0563556dc2 100644 --- a/src/span.h +++ b/src/span.h @@ -243,21 +243,16 @@ T& SpanPopBack(Span<T>& span) return back; } -//! Convert a data pointer to a std::byte data pointer. -//! Where possible, please use the safer AsBytes helpers. -inline const std::byte* AsBytePtr(const void* data) { return reinterpret_cast<const std::byte*>(data); } -inline std::byte* AsBytePtr(void* data) { return reinterpret_cast<std::byte*>(data); } - // From C++20 as_bytes and as_writeable_bytes template <typename T> Span<const std::byte> AsBytes(Span<T> s) noexcept { - return {AsBytePtr(s.data()), s.size_bytes()}; + return {reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()}; } template <typename T> Span<std::byte> AsWritableBytes(Span<T> s) noexcept { - return {AsBytePtr(s.data()), s.size_bytes()}; + return {reinterpret_cast<std::byte*>(s.data()), s.size_bytes()}; } template <typename V> @@ -274,6 +269,7 @@ Span<std::byte> MakeWritableByteSpan(V&& v) noexcept // Helper functions to safely cast to unsigned char pointers. inline unsigned char* UCharCast(char* c) { return (unsigned char*)c; } inline unsigned char* UCharCast(unsigned char* c) { return c; } +inline unsigned char* UCharCast(std::byte* c) { return (unsigned char*)c; } inline const unsigned char* UCharCast(const char* c) { return (unsigned char*)c; } inline const unsigned char* UCharCast(const unsigned char* c) { return c; } inline const unsigned char* UCharCast(const std::byte* c) { return reinterpret_cast<const unsigned char*>(c); } diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 328e7f81a0..c02322ec7d 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -239,8 +239,9 @@ BOOST_AUTO_TEST_CASE(addrman_select_by_network) // ensure that both new and tried table are selected from bool new_selected{false}; bool tried_selected{false}; + int counter = 256; - while (!new_selected || !tried_selected) { + while (--counter > 0 && (!new_selected || !tried_selected)) { const CAddress selected{addrman->Select(/*new_only=*/false, NET_I2P).first}; BOOST_REQUIRE(selected == i2p_addr || selected == i2p_addr2); if (selected == i2p_addr) { @@ -249,6 +250,9 @@ BOOST_AUTO_TEST_CASE(addrman_select_by_network) new_selected = true; } } + + BOOST_CHECK(new_selected); + BOOST_CHECK(tried_selected); } BOOST_AUTO_TEST_CASE(addrman_select_special) diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index 1ff5d6cf59..9bd5c7c2b6 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -12,8 +12,8 @@ #include <pow.h> #include <script/standard.h> #include <test/util/blockfilter.h> +#include <test/util/index.h> #include <test/util/setup_common.h> -#include <util/time.h> #include <validation.h> #include <boost/test/unit_test.hpp> @@ -113,6 +113,7 @@ bool BuildChainTestingSetup::BuildChain(const CBlockIndex* pindex, BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) { BlockFilterIndex filter_index(interfaces::MakeChain(m_node), BlockFilterType::BASIC, 1 << 20, true); + BOOST_REQUIRE(filter_index.Init()); uint256 last_header; @@ -139,15 +140,10 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) // BlockUntilSyncedToCurrentChain should return false before index is started. BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain()); - BOOST_REQUIRE(filter_index.Start()); + BOOST_REQUIRE(filter_index.StartBackgroundSync()); // Allow filter index to catch up with the block index. - constexpr auto timeout{10s}; - const auto time_start{SteadyClock::now()}; - while (!filter_index.BlockUntilSyncedToCurrentChain()) { - BOOST_REQUIRE(time_start + timeout > SteadyClock::now()); - UninterruptibleSleep(std::chrono::milliseconds{100}); - } + IndexWaitSynced(filter_index); // Check that filter index has all blocks that were in the chain before it started. { diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp index f094766886..e4ed861b12 100644 --- a/src/test/blockmanager_tests.cpp +++ b/src/test/blockmanager_tests.cpp @@ -5,14 +5,16 @@ #include <chainparams.h> #include <node/blockstorage.h> #include <node/context.h> +#include <node/kernel_notifications.h> #include <util/chaintype.h> #include <validation.h> #include <boost/test/unit_test.hpp> #include <test/util/setup_common.h> -using node::BlockManager; using node::BLOCK_SERIALIZATION_HEADER_SIZE; +using node::BlockManager; +using node::KernelNotifications; using node::MAX_BLOCKFILE_SIZE; // use BasicTestingSetup here for the data directory configuration, setup, and cleanup @@ -21,11 +23,13 @@ BOOST_FIXTURE_TEST_SUITE(blockmanager_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos) { const auto params {CreateChainParams(ArgsManager{}, ChainType::MAIN)}; + KernelNotifications notifications{m_node.exit_status}; const BlockManager::Options blockman_opts{ .chainparams = *params, .blocks_dir = m_args.GetBlocksDirPath(), + .notifications = notifications, }; - BlockManager blockman{blockman_opts}; + BlockManager blockman{m_node.kernel->interrupt, blockman_opts}; CChain chain {}; // simulate adding a genesis block normally BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0, chain, nullptr).nPos, BLOCK_SERIALIZATION_HEADER_SIZE); @@ -86,4 +90,43 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_scan_unlink_already_pruned_files, TestChain BOOST_CHECK(!AutoFile(blockman.OpenBlockFile(new_pos, true)).IsNull()); } +BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup) +{ + // The goal of the function is to return the first not pruned block in the range [upper_block, lower_block]. + LOCK(::cs_main); + auto& chainman = m_node.chainman; + auto& blockman = chainman->m_blockman; + const CBlockIndex& tip = *chainman->ActiveTip(); + + // Function to prune all blocks from 'last_pruned_block' down to the genesis block + const auto& func_prune_blocks = [&](CBlockIndex* last_pruned_block) + { + LOCK(::cs_main); + CBlockIndex* it = last_pruned_block; + while (it != nullptr && it->nStatus & BLOCK_HAVE_DATA) { + it->nStatus &= ~BLOCK_HAVE_DATA; + it = it->pprev; + } + }; + + // 1) Return genesis block when all blocks are available + BOOST_CHECK_EQUAL(blockman.GetFirstStoredBlock(tip), chainman->ActiveChain()[0]); + BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *chainman->ActiveChain()[0])); + + // 2) Check lower_block when all blocks are available + CBlockIndex* lower_block = chainman->ActiveChain()[tip.nHeight / 2]; + BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *lower_block)); + + // Prune half of the blocks + int height_to_prune = tip.nHeight / 2; + CBlockIndex* first_available_block = chainman->ActiveChain()[height_to_prune + 1]; + CBlockIndex* last_pruned_block = first_available_block->pprev; + func_prune_blocks(last_pruned_block); + + // 3) The last block not pruned is in-between upper-block and the genesis block + BOOST_CHECK_EQUAL(blockman.GetFirstStoredBlock(tip), first_available_block); + BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *first_available_block)); + BOOST_CHECK(!blockman.CheckBlockDataAvailability(tip, *last_pruned_block)); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp index 503a58076b..74d6d7231a 100644 --- a/src/test/coinstatsindex_tests.cpp +++ b/src/test/coinstatsindex_tests.cpp @@ -6,31 +6,19 @@ #include <index/coinstatsindex.h> #include <interfaces/chain.h> #include <kernel/coinstats.h> +#include <test/util/index.h> #include <test/util/setup_common.h> #include <test/util/validation.h> -#include <util/time.h> #include <validation.h> #include <boost/test/unit_test.hpp> -#include <chrono> - BOOST_AUTO_TEST_SUITE(coinstatsindex_tests) -static void IndexWaitSynced(BaseIndex& index) -{ - // Allow the CoinStatsIndex to catch up with the block index that is syncing - // in a background thread. - const auto timeout = GetTime<std::chrono::seconds>() + 120s; - while (!index.BlockUntilSyncedToCurrentChain()) { - BOOST_REQUIRE(timeout > GetTime<std::chrono::milliseconds>()); - UninterruptibleSleep(100ms); - } -} - BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup) { CoinStatsIndex coin_stats_index{interfaces::MakeChain(m_node), 1 << 20, true}; + BOOST_REQUIRE(coin_stats_index.Init()); const CBlockIndex* block_index; { @@ -45,7 +33,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup) // is started. BOOST_CHECK(!coin_stats_index.BlockUntilSyncedToCurrentChain()); - BOOST_REQUIRE(coin_stats_index.Start()); + BOOST_REQUIRE(coin_stats_index.StartBackgroundSync()); IndexWaitSynced(coin_stats_index); @@ -96,7 +84,8 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup) const CChainParams& params = Params(); { CoinStatsIndex index{interfaces::MakeChain(m_node), 1 << 20}; - BOOST_REQUIRE(index.Start()); + BOOST_REQUIRE(index.Init()); + BOOST_REQUIRE(index.StartBackgroundSync()); IndexWaitSynced(index); std::shared_ptr<const CBlock> new_block; CBlockIndex* new_block_index = nullptr; @@ -122,8 +111,9 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup) { CoinStatsIndex index{interfaces::MakeChain(m_node), 1 << 20}; + BOOST_REQUIRE(index.Init()); // Make sure the index can be loaded. - BOOST_REQUIRE(index.Start()); + BOOST_REQUIRE(index.StartBackgroundSync()); index.Stop(); } } diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index e4e8596a5d..8332f54591 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -131,14 +131,13 @@ static void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, b } } -static void TestChaCha20(const std::string &hex_message, const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout) +static void TestChaCha20(const std::string &hex_message, const std::string &hexkey, ChaCha20::Nonce96 nonce, uint32_t seek, const std::string& hexout) { std::vector<unsigned char> key = ParseHex(hexkey); assert(key.size() == 32); std::vector<unsigned char> m = ParseHex(hex_message); ChaCha20 rng(key.data()); - rng.SetIV(nonce); - rng.Seek64(seek); + rng.Seek64(nonce, seek); std::vector<unsigned char> outres; outres.resize(hexout.size() / 2); assert(hex_message.empty() || m.size() * 2 == hexout.size()); @@ -152,8 +151,7 @@ static void TestChaCha20(const std::string &hex_message, const std::string &hexk BOOST_CHECK_EQUAL(hexout, HexStr(outres)); if (!hex_message.empty()) { // Manually XOR with the keystream and compare the output - rng.SetIV(nonce); - rng.Seek64(seek); + rng.Seek64(nonce, seek); std::vector<unsigned char> only_keystream(outres.size()); rng.Keystream(only_keystream.data(), only_keystream.size()); for (size_t i = 0; i != m.size(); i++) { @@ -169,7 +167,7 @@ static void TestChaCha20(const std::string &hex_message, const std::string &hexk lens[1] = InsecureRandRange(hexout.size() / 2U + 1U - lens[0]); lens[2] = hexout.size() / 2U - lens[0] - lens[1]; - rng.Seek64(seek); + rng.Seek64(nonce, seek); outres.assign(hexout.size() / 2U, 0); size_t pos = 0; for (int j = 0; j < 3; ++j) { @@ -186,13 +184,28 @@ static void TestChaCha20(const std::string &hex_message, const std::string &hexk static void TestPoly1305(const std::string &hexmessage, const std::string &hexkey, const std::string& hextag) { - std::vector<unsigned char> key = ParseHex(hexkey); - std::vector<unsigned char> m = ParseHex(hexmessage); - std::vector<unsigned char> tag = ParseHex(hextag); - std::vector<unsigned char> tagres; - tagres.resize(POLY1305_TAGLEN); - poly1305_auth(tagres.data(), m.data(), m.size(), key.data()); + auto key = ParseHex<std::byte>(hexkey); + auto m = ParseHex<std::byte>(hexmessage); + auto tag = ParseHex<std::byte>(hextag); + std::vector<std::byte> tagres(Poly1305::TAGLEN); + Poly1305{key}.Update(m).Finalize(tagres); BOOST_CHECK(tag == tagres); + + // Test incremental interface + for (int splits = 0; splits < 10; ++splits) { + for (int iter = 0; iter < 10; ++iter) { + auto data = Span{m}; + Poly1305 poly1305{key}; + for (int chunk = 0; chunk < splits; ++chunk) { + size_t now = InsecureRandRange(data.size() + 1); + poly1305.Update(data.first(now)); + data = data.subspan(now); + } + tagres.assign(Poly1305::TAGLEN, std::byte{}); + poly1305.Update(data).Finalize(tagres); + BOOST_CHECK(tag == tagres); + } + } } static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &salt_hex, const std::string &info_hex, const std::string &okm_check_hex) { @@ -482,38 +495,57 @@ BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) { BOOST_AUTO_TEST_CASE(chacha20_testvector) { + /* Example from RFC8439 section 2.3.2. */ + TestChaCha20("", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + {0x09000000, 0x4a000000}, 1, + "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4e" + "d2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"); + + /* Example from RFC8439 section 2.4.2. */ + TestChaCha20("4c616469657320616e642047656e746c656d656e206f662074686520636c6173" + "73206f66202739393a204966204920636f756c64206f6666657220796f75206f" + "6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73" + "637265656e20776f756c642062652069742e", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + {0, 0x4a000000}, 1, + "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0b" + "f91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d8" + "07ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab7793736" + "5af90bbf74a35be6b40b8eedf2785e42874d"); + // RFC 7539/8439 A.1 Test Vector #1: TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", - 0, 0, + {0, 0}, 0, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7" "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); // RFC 7539/8439 A.1 Test Vector #2: TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", - 0, 1, + {0, 0}, 1, "9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed" "29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f"); // RFC 7539/8439 A.1 Test Vector #3: TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000001", - 0, 1, + {0, 0}, 1, "3aeb5224ecf849929b9d828db1ced4dd832025e8018b8160b82284f3c949aa5a" "8eca00bbb4a73bdad192b5c42f73f2fd4e273644c8b36125a64addeb006c13a0"); // RFC 7539/8439 A.1 Test Vector #4: TestChaCha20("", "00ff000000000000000000000000000000000000000000000000000000000000", - 0, 2, + {0, 0}, 2, "72d54dfbf12ec44b362692df94137f328fea8da73990265ec1bbbea1ae9af0ca" "13b25aa26cb4a648cb9b9d1be65b2c0924a66c54d545ec1b7374f4872e99f096"); // RFC 7539/8439 A.1 Test Vector #5: TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", - 0x200000000000000, 0, + {0, 0x200000000000000}, 0, "c2c64d378cd536374ae204b9ef933fcd1a8b2288b3dfa49672ab765b54ee27c7" "8a970e0e955c14f3a88e741b97c286f75f8fc299e8148362fa198a39531bed6d"); @@ -521,7 +553,7 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector) TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", - 0, 0, + {0, 0}, 0, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7" "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); @@ -539,7 +571,7 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector) "74696f6e73206d61646520617420616e792074696d65206f7220706c6163652c" "207768696368206172652061646472657373656420746f", "0000000000000000000000000000000000000000000000000000000000000001", - 0x200000000000000, 1, + {0, 0x200000000000000}, 1, "a3fbf07df3fa2fde4f376ca23e82737041605d9f4f4f57bd8cff2c1d4b7955ec" "2a97948bd3722915c8f3d337f7d370050e9e96d647b7c39f56e031ca5eb6250d" "4042e02785ececfa4b4bb5e8ead0440e20b6e8db09d881a7c6132f420e527950" @@ -559,27 +591,93 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector) "6162653a0a416c6c206d696d737920776572652074686520626f726f676f7665" "732c0a416e6420746865206d6f6d65207261746873206f757467726162652e", "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", - 0x200000000000000, 42, + {0, 0x200000000000000}, 42, "62e6347f95ed87a45ffae7426f27a1df5fb69110044c0d73118effa95b01e5cf" "166d3df2d721caf9b21e5fb14c616871fd84c54f9d65b283196c7fe4f60553eb" "f39c6402c42234e32a356b3e764312a61a5532055716ead6962568f87d3f3f77" "04c6a8d1bcd1bf4d50d6154b6da731b187b58dfd728afa36757a797ac188d1"); + // RFC 7539/8439 A.4 Test Vector #1: + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000000", + {0, 0}, 0, + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7"); + + // RFC 7539/8439 A.4 Test Vector #2: + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000001", + {0, 0x200000000000000}, 0, + "ecfa254f845f647473d3cb140da9e87606cb33066c447b87bc2666dde3fbb739"); + + // RFC 7539/8439 A.4 Test Vector #3: + TestChaCha20("", + "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", + {0, 0x200000000000000}, 0, + "965e3bc6f9ec7ed9560808f4d229f94b137ff275ca9b3fcbdd59deaad23310ae"); + // test encryption TestChaCha20("4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756" "c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e" "20776f756c642062652069742e", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", {0, 0x4a000000UL}, 1, "6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d" "624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74" "a35be6b40b8eedf2785e42874d" ); // test keystream output - TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1, + TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", {0, 0x4a000000UL}, 1, "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb" "a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a" "832c89c167eacd901d7e2bf363"); + + // Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 + // The first one is identical to the above one from the RFC8439 A.1 vectors, but repeated here + // for completeness. + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000000", + {0, 0}, 0, + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7" + "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000001", + {0, 0}, 0, + "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41" + "bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963"); + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000000", + {0, 0x0100000000000000ULL}, 0, + "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031" + "e85a050278a7084527214f73efc7fa5b5277062eb7a0433e445f41e3"); + TestChaCha20("", + "0000000000000000000000000000000000000000000000000000000000000000", + {0, 1}, 0, + "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32" + "111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"); + TestChaCha20("", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + {0, 0x0706050403020100ULL}, 0, + "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c1" + "34a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a" + "38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd" + "9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c7" + "9db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025" + "d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d7" + "0eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2" + "ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9"); + + // Test overflow of 32-bit block counter, should increment the first 32-bit + // part of the nonce to retain compatibility with >256 GiB output. + // The test data was generated with an implementation that uses a 64-bit + // counter and a 64-bit initialization vector (PyCryptodome's ChaCha20 class + // with 8 bytes nonce length). + TestChaCha20("", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + {0, 0xdeadbeef12345678}, 0xffffffff, + "2d292c880513397b91221c3a647cfb0765a4815894715f411e3df5e0dd0ba9df" + "fd565dea5addbdb914208fde7950f23e0385f9a727143f6a6ac51d84b1c0fb3e" + "2e3b00b63d6841a1cc6d1538b1d3a74bef1eb2f54c7b7281e36e484dba89b351" + "c8f572617e61e342879f211b0e4c515df50ea9d0771518fad96cd0baee62deb6"); } BOOST_AUTO_TEST_CASE(chacha20_midblock) @@ -668,6 +766,57 @@ BOOST_AUTO_TEST_CASE(poly1305_testvector) TestPoly1305("e33594d7505e43b900000000000000003394d7505e4379cd010000000000000000000000000000000000000000000000", "0100000000000000040000000000000000000000000000000000000000000000", "13000000000000000000000000000000"); + + // Tests from https://github.com/floodyberry/poly1305-donna/blob/master/poly1305-donna.c + TestPoly1305("8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a" + "c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738" + "b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da" + "99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74" + "e355a5", + "eea6a7251c1e72916d11c2cb214d3c252539121d8e234e652d651fa4c8cff880", + "f3ffc7703f9400e52a7dfb4b3d3305d9"); + { + // mac of the macs of messages of length 0 to 256, where the key and messages have all + // their values set to the length. + auto total_key = ParseHex<std::byte>("01020304050607fffefdfcfbfaf9ffffffffffffffffffffffffffff00000000"); + Poly1305 total_ctx(total_key); + for (unsigned i = 0; i < 256; ++i) { + std::vector<std::byte> key(32, std::byte{(uint8_t)i}); + std::vector<std::byte> msg(i, std::byte{(uint8_t)i}); + std::array<std::byte, Poly1305::TAGLEN> tag; + Poly1305{key}.Update(msg).Finalize(tag); + total_ctx.Update(tag); + } + std::vector<std::byte> total_tag(Poly1305::TAGLEN); + total_ctx.Finalize(total_tag); + BOOST_CHECK(total_tag == ParseHex<std::byte>("64afe2e8d6ad7bbdd287f97c44623d39")); + } + + // Tests with sparse messages and random keys. + TestPoly1305("000000000000000000000094000000000000b07c4300000000002c002600d500" + "00000000000000000000000000bc58000000000000000000c9000000dd000000" + "00000000000000d34c000000000000000000000000f9009100000000000000c2" + "4b0000e900000000000000000000000000000000000e00000027000074000000" + "0000000003000000000000f1000000000000dce2000000000000003900000000" + "0000000000000000000000000000000000000000000000520000000000000000" + "000000000000000000000000009500000000000000000000000000cf00826700" + "000000a900000000000000000000000000000000000000000079000000000000" + "0000de0000004c000000000033000000000000000000000000002800aa000000" + "00003300860000e000000000", + "6e543496db3cf677592989891ab021f58390feb84fb419fbc7bb516a60bfa302", + "7ea80968354d40d9d790b45310caf7f3"); + TestPoly1305("0000005900000000c40000002f00000000000000000000000000000029690000" + "0000e8000037000000000000000000000000000b000000000000000000000000" + "000000000000000000000000001800006e0000000000a4000000000000000000" + "00000000000000004d00000000000000b0000000000000000000005a00000000" + "0000000000b7c300000000000000540000000000000000000000000a00000000" + "00005b0000000000000000000000000000000000002d00e70000000000000000" + "000000000000003400006800d700000000000000000000360000000000000000" + "00eb000000000000000000000000000000000000000000000000000028000000" + "37000000000000000000000000000000000000000000000000000000008f0000" + "000000000000000000000000", + "f0b659a4f3143d8a1e1dacb9a409fe7e7cd501dfb58b16a2623046c5d337922a", + "0e410fa9d7a40ac582e77546be9a72bb"); } BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests) @@ -708,7 +857,7 @@ static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aa std::vector<unsigned char> expected_ciphertext_and_mac = ParseHex(hex_encrypted_message); std::vector<unsigned char> expected_ciphertext_and_mac_sequence999 = ParseHex(hex_encrypted_message_seq_999); - std::vector<unsigned char> ciphertext_buf(plaintext_buf.size() + POLY1305_TAGLEN, 0); + std::vector<unsigned char> ciphertext_buf(plaintext_buf.size() + Poly1305::TAGLEN, 0); std::vector<unsigned char> plaintext_buf_new(plaintext_buf.size(), 0); std::vector<unsigned char> cmp_ctx_buffer(64); uint32_t out_len = 0; @@ -730,8 +879,7 @@ static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aa BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac.data(), ciphertext_buf.size()) == 0); // manually construct the AAD keystream - cmp_ctx.SetIV(seqnr_aad); - cmp_ctx.Seek64(0); + cmp_ctx.Seek64({0, seqnr_aad}, 0); cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64); BOOST_CHECK(memcmp(expected_aad_keystream.data(), cmp_ctx_buffer.data(), expected_aad_keystream.size()) == 0); // crypt the 3 length bytes and compare the length @@ -758,8 +906,7 @@ static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aa BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac_sequence999.data(), expected_ciphertext_and_mac_sequence999.size()) == 0); } // set nonce and block counter, output the keystream - cmp_ctx.SetIV(seqnr_aad); - cmp_ctx.Seek64(0); + cmp_ctx.Seek64({0, seqnr_aad}, 0); cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64); // crypt the 3 length bytes and compare the length diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index 1a06f16155..9193d9a8b3 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -15,6 +15,7 @@ #include <script/standard.h> #include <serialize.h> #include <test/util/net.h> +#include <test/util/random.h> #include <test/util/setup_common.h> #include <timedata.h> #include <util/string.h> @@ -131,8 +132,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management) { NodeId id{0}; auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); - auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr, - *m_node.chainman, *m_node.mempool, false); + auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr, *m_node.chainman, *m_node.mempool, {}); constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS; CConnman::Options options; @@ -208,8 +208,7 @@ BOOST_AUTO_TEST_CASE(block_relay_only_eviction) { NodeId id{0}; auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); - auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr, - *m_node.chainman, *m_node.mempool, false); + auto peerLogic = PeerManager::make(*connman, *m_node.addrman, nullptr, *m_node.chainman, *m_node.mempool, {}); constexpr int max_outbound_block_relay{MAX_BLOCK_RELAY_ONLY_CONNECTIONS}; constexpr int64_t MINIMUM_CONNECT_TIME{30}; @@ -272,8 +271,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement) auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); - auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(), - *m_node.chainman, *m_node.mempool, false); + auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(), *m_node.chainman, *m_node.mempool, {}); CNetAddr tor_netaddr; BOOST_REQUIRE( @@ -375,8 +373,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); - auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(), - *m_node.chainman, *m_node.mempool, false); + auto peerLogic = PeerManager::make(*connman, *m_node.addrman, banman.get(), *m_node.chainman, *m_node.mempool, {}); banman->ClearBanned(); int64_t nStartTime = GetTime(); diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index 7a7790e2ae..5cec0b07c9 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -128,7 +128,7 @@ std::set<std::pair<CPubKey, KeyOriginInfo>> GetKeyOriginData(const FlatSigningPr } void DoCheck(std::string prv, std::string pub, const std::string& norm_pub, int flags, - const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, + const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, std::optional<uint256> op_desc_id = std::nullopt, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY, bool replace_apostrophe_with_h_in_prv=false, bool replace_apostrophe_with_h_in_pub=false, uint32_t spender_nlocktime=0, uint32_t spender_nsequence=CTxIn::SEQUENCE_FINAL, std::map<std::vector<uint8_t>, std::vector<uint8_t>> preimages={}) @@ -165,6 +165,11 @@ void DoCheck(std::string prv, std::string pub, const std::string& norm_pub, int BOOST_CHECK_MESSAGE(EqualDescriptor(pub, pub1), "Private ser: " + pub1 + " Public desc: " + pub); BOOST_CHECK_MESSAGE(EqualDescriptor(pub, pub2), "Public ser: " + pub2 + " Public desc: " + pub); + // Check that the COMPAT identifier did not change + if (op_desc_id) { + BOOST_CHECK_MESSAGE(DescriptorID(*parse_priv) == *op_desc_id, "DescriptorID() " + DescriptorID(*parse_priv).ToString() + " does not match for priv " + prv); + } + // Check that both can be serialized with private key back to the private version, but not without private key. if (!(flags & MISSING_PRIVKEYS)) { std::string prv1; @@ -351,18 +356,18 @@ void DoCheck(std::string prv, std::string pub, const std::string& norm_pub, int } void Check(const std::string& prv, const std::string& pub, const std::string& norm_pub, int flags, - const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, + const std::vector<std::vector<std::string>>& scripts, const std::optional<OutputType>& type, std::optional<uint256> op_desc_id = std::nullopt, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY, uint32_t spender_nlocktime=0, uint32_t spender_nsequence=CTxIn::SEQUENCE_FINAL, std::map<std::vector<uint8_t>, std::vector<uint8_t>> preimages={}) { // Do not replace apostrophes with 'h' in prv and pub - DoCheck(prv, pub, norm_pub, flags, scripts, type, paths, /*replace_apostrophe_with_h_in_prv=*/false, + DoCheck(prv, pub, norm_pub, flags, scripts, type, op_desc_id, paths, /*replace_apostrophe_with_h_in_prv=*/false, /*replace_apostrophe_with_h_in_pub=*/false, /*spender_nlocktime=*/spender_nlocktime, /*spender_nsequence=*/spender_nsequence, /*preimages=*/preimages); // Replace apostrophes with 'h' both in prv and in pub, if apostrophes are found in both if (prv.find('\'') != std::string::npos && pub.find('\'') != std::string::npos) { - DoCheck(prv, pub, norm_pub, flags, scripts, type, paths, /*replace_apostrophe_with_h_in_prv=*/true, + DoCheck(prv, pub, norm_pub, flags, scripts, type, op_desc_id, paths, /*replace_apostrophe_with_h_in_prv=*/true, /*replace_apostrophe_with_h_in_pub=*/true, /*spender_nlocktime=*/spender_nlocktime, /*spender_nsequence=*/spender_nsequence, /*preimages=*/preimages); } @@ -375,20 +380,20 @@ BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(descriptor_test) { // Basic single-key compressed - Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, std::nullopt); - Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, std::nullopt); - Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2h/3/4h]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, {{1,0x80000002UL,3,0x80000004UL}}); - Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32); - Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT); - Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE | XONLY_KEYS, {{"512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11"}}, OutputType::BECH32M); + Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, std::nullopt, /*op_desc_id=*/uint256S("8ef71f7b6ac0918663f6706be469d6109f6922e21f484009d7ab49d77da36e8b")); + Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}}, std::nullopt, /*op_desc_id=*/uint256S("5fe175b43c58ac2cdde40521dc7d1dbc607f3dd795d00770206f4fdefb42229e")); + Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh([deadbeef/1/2h/3/4h]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("628130ae0530f2b24faf1ad2744a83568ac0ffac43e703e30c00d5f137869b84"), {{1,0x80000002UL,3,0x80000004UL}}); + Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("4a47b7f497721bf3fc48c69a5d22bc1f3617238649a8ba7cb96fbd92fec84a7e")); + Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("a13112753066b5c59473a87c5771b1694a10531944a60e0ab2d7ad66ecb65bcd")); + Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE | XONLY_KEYS, {{"512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11"}}, OutputType::BECH32M, /*op_desc_id=*/uint256S("4290f3d017b270be53b91abc56d9d2f23a3ff361d5b1d39550ba011e6cae0da5")); CheckUnparsable("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY2))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5))", "wpkh(): Pubkey '03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5' is invalid"); // Invalid pubkey CheckUnparsable("pkh(deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(deadbeef/1/2h/3/4h]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh(): Key origin start '[ character expected but not found, got 'd' instead"); // Missing start bracket in key origin CheckUnparsable("pkh([deadbeef]/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef]/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "pkh(): Multiple ']' characters found for a single pubkey"); // Multiple end brackets in key origin // Basic single-key uncompressed - Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)",SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, std::nullopt); - Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, std::nullopt); - Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY); + Check("combo(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)",SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac","76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, std::nullopt, /*op_desc_id=*/uint256S("33f6bb5d32c04e9d9e5466a8212836743bd5466aa0b8d5331ce8aa0812371ffd")); + Check("pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac"}}, std::nullopt, /*op_desc_id=*/uint256S("52306fc1f5d0cb78aacea9d3933092be9252adc27b146f97c16a94d6fcdb652e")); + Check("pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("36657e8690d4015032da1a8c1e37b315c3f7ccb010e6ada12967878711962991")); CheckUnparsable("wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "wpkh(): Uncompressed keys are not allowed"); // No uncompressed keys in witness CheckUnparsable("wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "pk(): Uncompressed keys are not allowed"); // No uncompressed keys in witness CheckUnparsable("sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))", "sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))", "wpkh(): Uncompressed keys are not allowed"); // No uncompressed keys in witness @@ -403,33 +408,33 @@ BOOST_AUTO_TEST_CASE(descriptor_test) Check("tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", "tr(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5,{pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5),{pk(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd),pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5)}})", XONLY_KEYS | SIGNABLE | MISSING_PRIVKEYS, {{"51201497ae16f30dacb88523ed9301bff17773b609e8a90518a3f96ea328a47d1500"}}, OutputType::BECH32M); // Versions with BIP32 derivations - Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt); - Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, std::nullopt, {{0}}); - Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{0xFFFFFFFFUL,0}}); - - Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13h]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}}); - Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); - Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, {{0}, {1}}); - Check("tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", XONLY_KEYS | RANGE, {{"512078bc707124daa551b65af74de2ec128b7525e10f374dc67b64e00ce0ab8b3e12"}, {"512001f0a02a17808c20134b78faab80ef93ffba82261ccef0a2314f5d62b6438f11"}, {"512021024954fcec88237a9386fce80ef2ced5f1e91b422b26c59ccfc174c8d1ad25"}}, OutputType::BECH32M, {{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}}); + Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}}, std::nullopt, /*op_desc_id=*/uint256S("7f127f7861594e3ede449eb47a7cc623c753acc0b0f0fc03fbb2dac636c20d6f")); + Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, std::nullopt, /*op_desc_id=*/uint256S("0e54cf04f2bb8d607e2241d611d169c6f7d78f0ab1f15a80642192a19fbdb7cc"), {{0}}); + Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("35a5cf511e941a35b9cb0cf515d3ef887aa4246db87d6af463265a386ad856fe"), {{0xFFFFFFFFUL,0}}); + + Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", "wpkh([ffffffff/13h]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, OutputType::BECH32, /*op_desc_id=*/std::nullopt, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}}); + Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", RANGE | HARDENED | DERIVE_HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/std::nullopt, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); + Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, std::nullopt, /*op_desc_id=*/std::nullopt, {{0}, {1}}); + Check("tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", "tr(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*))", XONLY_KEYS | RANGE, {{"512078bc707124daa551b65af74de2ec128b7525e10f374dc67b64e00ce0ab8b3e12"}, {"512001f0a02a17808c20134b78faab80ef93ffba82261ccef0a2314f5d62b6438f11"}, {"512021024954fcec88237a9386fce80ef2ced5f1e91b422b26c59ccfc174c8d1ad25"}}, OutputType::BECH32M, /*op_desc_id=*/std::nullopt, {{0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}}); // Mixed xpubs and const pubkeys - Check("wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", MIXED_PUBKEYS, {{"0020cb155486048b23a6da976d4c6fe071a2dbc8a7b57aaf225b8955f2e2a27b5f00"}},OutputType::BECH32,{{0},{}}); + Check("wsh(multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))","wsh(multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", MIXED_PUBKEYS, {{"0020cb155486048b23a6da976d4c6fe071a2dbc8a7b57aaf225b8955f2e2a27b5f00"}},OutputType::BECH32, /*op_desc_id=*/uint256S("88af8e5951779aa054dfe1071ef0f7266ba1c5558487bfd8525c95010fc0aba2"),{{0},{}}); // Mixed range xpubs and const pubkeys - Check("multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", RANGE | MIXED_PUBKEYS, {{"512102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e0762103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ec2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121035d30b6c66dc1e036c45369da8287518cf7e0d6ed1e2b905171c605708f14ca032103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"}}, std::nullopt,{{2},{1},{0},{}}); + Check("multi(1,xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)","multi(1,xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", RANGE | MIXED_PUBKEYS, {{"512102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e0762103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ec2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"},{"5121035d30b6c66dc1e036c45369da8287518cf7e0d6ed1e2b905171c605708f14ca032103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd52ae"}}, std::nullopt, /*op_desc_id=*/std::nullopt,{{2},{1},{0},{}}); CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo(): Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "pkh(): Key path value 2147483648 is out of range"); // BIP 32 path element overflow CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "pkh(): Key path value '1aa' is not a valid uint32"); // Path is not valid uint - Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, {{10, 20, 0xFFFFFFFFUL, 0}}); + Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, /*op_desc_id=*/std::nullopt, {{10, 20, 0xFFFFFFFFUL, 0}}); // Multisig constructions - Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt); - Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt); + Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt, /*op_desc_id=*/uint256S("b147e25eb4a9d3da4e86ed8e970d817563ae2cb9c71a756b11cfdeb4dc11b70c")); + Check("sortedmulti(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "sortedmulti(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt, /*op_desc_id=*/uint256S("62b59d1e32a62176ef7a17538f3b80c7d1afc53e5644eb753525bdb5d556486c")); Check("sortedmulti(1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}}, std::nullopt); - Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); - Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, std::nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}}); - Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); - Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", "sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT); - Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754"}}, OutputType::BECH32M); + Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, /*op_desc_id=*/std::nullopt, {{0x8000006FUL,222},{0}}); + Check("sortedmulti(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", "sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)", RANGE, {{"5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae"}, {"52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae"}, {"5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae"}}, std::nullopt, /*op_desc_id=*/std::nullopt, {{0}, {1}, {2}, {0, 0, 0}, {0, 0, 1}, {0, 0, 2}}); + Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", "wsh(multi(2,[bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", HARDENED | RANGE | DERIVE_HARDENED, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, OutputType::BECH32, /*op_desc_id=*/std::nullopt, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}}); + Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", "sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/std::nullopt); + Check("tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,pk(KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))", SIGNABLE | XONLY_KEYS, {{"512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754"}}, OutputType::BECH32M, /*op_desc_id=*/uint256S("af482b44c10b737b678e1091584818372e169e2dc5219e2877fabe1b83ae467b")); CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))", "P2SH script is too large, 547 bytes is larger than 520 bytes"); // P2SH does not fit 16 compressed pubkeys in a redeemscript CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Multiple ']' characters found for a single pubkey"); // Double key origin descriptor CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Fingerprint 'aaagaaaa' is not hex"); // Non hex fingerprint @@ -441,8 +446,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test) CheckUnparsable("multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(3,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be larger than the number of keys; threshold is 3 but only 2 keys specified"); // Threshold larger than number of keys CheckUnparsable("multi(3,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f)", "multi(3,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8)", "Cannot have 4 pubkeys in bare multisig; only at most 3 pubkeys"); // Threshold larger than number of keys CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "P2SH script is too large, 581 bytes is larger than 520 bytes"); // Cannot have more than 15 keys in a P2SH multisig, or we exceed maximum push size - Check("wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))","wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", "wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", SIGNABLE, {{"0020376bd8344b8b6ebe504ff85ef743eaa1aa9272178223bcb6887e9378efb341ac"}}, OutputType::BECH32); // In P2WSH we can have up to 20 keys - Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))","sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", "sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", SIGNABLE, {{"a914c2c9c510e9d7f92fd6131e94803a8d34a8ef675e87"}}, OutputType::P2SH_SEGWIT); // Even if it's wrapped into P2SH + Check("wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))","wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", "wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd))", SIGNABLE, {{"0020376bd8344b8b6ebe504ff85ef743eaa1aa9272178223bcb6887e9378efb341ac"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("2bb9d418ebdc3a75c465383985881527f3e5d6e520fb3efb152d4191b80e8412")); // In P2WSH we can have up to 20 keys + Check("sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))","sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", "sh(wsh(multi(20,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232,02bc2feaa536991d269aae46abb8f3772a5b3ad592314945e51543e7da84c4af6e,0318bf32e5217c1eb771a6d5ce1cd39395dff7ff665704f175c9a5451d95a2f2ca,02c681a6243f16208c2004bb81f5a8a67edfdd3e3711534eadeec3dcf0b010c759,0249fdd6b69768b8d84b4893f8ff84b36835c50183de20fcae8f366a45290d01fd)))", SIGNABLE, {{"a914c2c9c510e9d7f92fd6131e94803a8d34a8ef675e87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("69c3f3153ed2527d12cf78e53e719233fdb7fa6ca9f8a10059ce47d34b49c4cb")); // Even if it's wrapped into P2SH // Check for invalid nesting of structures CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", "A function is needed within P2SH"); // P2SH needs a script, not a key CheckUnparsable("sh(combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", "Can only have combo() at top level"); // Old must be top level @@ -453,8 +458,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test) CheckUnparsable("wsh(wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "wsh(wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", "Can only have wsh() at top level or inside sh()"); // Cannot embed P2WSH inside P2WSH // Checksums - Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#hgmsckna", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); - Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, {{0x8000006FUL,222},{0}}); + Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5t", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#hgmsckna", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("9339b7bfbe8cfd9d0d55819778ef77f52e5786e85b4c83be8a0d5b976e033f4c"), {{0x8000006FUL,222},{0}}); + Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", "sh(multi(2,[00000000/111h/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, OutputType::LEGACY, /*op_desc_id=*/uint256S("9339b7bfbe8cfd9d0d55819778ef77f52e5786e85b4c83be8a0d5b976e033f4c"), {{0x8000006FUL,222},{0}}); CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#", "Expected 8 character checksum, not 0 characters"); // Empty checksum CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfyq", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5tq", "Expected 8 character checksum, not 9 characters"); // Too long checksum CheckUnparsable("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxf", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))#tjg09x5", "Expected 8 character checksum, not 7 characters"); // Too short checksum @@ -474,6 +479,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test) RANGE | HARDENED | XONLY_KEYS, {{"51205172af752f057d543ce8e4a6f8dcf15548ec6be44041bfa93b72e191cfc8c1ee"}, {"51201b66f20b86f700c945ecb9ad9b0ad1662b73084e2bfea48bee02126350b8a5b1"}, {"512063e70f66d815218abcc2306aa930aaca07c5cde73b75127eb27b5e8c16b58a25"}}, OutputType::BECH32M, + /*op_desc_id=*/uint256S("458f0e7f4075a81c990c6be6d5b985027ac8b7f7cef8311696d95b7b49658c7d"), {{0x80000056, 0x80000001, 0x80000000, 1, 0}, {0x80000056, 0x80000001, 0x80000000, 1, 1}, {0x80000056, 0x80000001, 0x80000000, 1, 2}}); Check( @@ -482,7 +488,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test) "rawtr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE | XONLY_KEYS, {{"5120a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd"}}, - OutputType::BECH32M); + OutputType::BECH32M, + /*op_desc_id=*/uint256S("5ba3f7d83cee4795df00e0eaa5070a3e164283c5fc6e8586fd710eaa7a4168ec")); CheckUnparsable( "", @@ -529,31 +536,31 @@ BOOST_AUTO_TEST_CASE(descriptor_test) CheckUnparsable("wsh(and_b(and_b(older(1),a:older(100000000)),s:pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd)))", "wsh(and_b(and_b(older(1),a:older(100000000)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)))", "and_b(older(1),a:older(100000000)) is not sane: contains mixes of timelocks expressed in blocks and seconds"); CheckUnparsable("wsh(and_b(or_b(pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),s:pk(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn)),s:pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd)))", "wsh(and_b(or_b(pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),s:pk(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)))", "and_b(or_b(pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),s:pk(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0)),s:pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)) is not sane: contains duplicate public keys"); // Valid with extended keys. - Check("wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", DEFAULT, {{"0020acf425291b98a1d7e0d4690139442abc289175be32ef1f75945e339924246d73"}}, OutputType::BECH32, {{},{0}}); + Check("wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(and_v(v:ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e),multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", DEFAULT, {{"0020acf425291b98a1d7e0d4690139442abc289175be32ef1f75945e339924246d73"}}, OutputType::BECH32, /*op_desc_id=*/uint256S("0634b326edc66f9e2660562564d7a8fcca55f91dc4555ce0a51883cc72e0fa41"), {{},{0}}); // Valid under sh(wsh()) and with a mix of xpubs and raw keys. - Check("sh(wsh(thresh(1,pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE | MIXED_PUBKEYS, {{"a914767e9119ff3b3ac0cb6dcfe21de1842ccf85f1c487"}}, OutputType::P2SH_SEGWIT, {{},{0}}); + Check("sh(wsh(thresh(1,pkh(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(1,pkh(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE | MIXED_PUBKEYS, {{"a914767e9119ff3b3ac0cb6dcfe21de1842ccf85f1c487"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("3cfcad33bc25579d70b23ce634d317be00a4e5400e758e37c215bdc17b31bfb8"), {{},{0}}); // An exotic multisig, we can sign for both branches - Check("wsh(thresh(1,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc),a:pkh(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(thresh(1,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL),a:pkh(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(thresh(1,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL),a:pkh(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", SIGNABLE, {{"00204a4528fbc0947e02e921b54bd476fc8cc2ebb5c6ae2ccf10ed29fe2937fb6892"}}, OutputType::BECH32, {{},{0}}); + Check("wsh(thresh(1,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc),a:pkh(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)))", "wsh(thresh(1,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL),a:pkh(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", "wsh(thresh(1,pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL),a:pkh(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)))", SIGNABLE, {{"00204a4528fbc0947e02e921b54bd476fc8cc2ebb5c6ae2ccf10ed29fe2937fb6892"}}, OutputType::BECH32, /*op_desc_id=*/std::nullopt, {{},{0}}); // We can sign for a script requiring the two kinds of timelock. // But if we don't set a sequence high enough, we'll fail. - Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/1); + Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("f5c14a15b45d2af1b8ec69acfd3cf4790f069705d1b079efb0b8193fed181f64"), {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/1); // And same for the nLockTime. - Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, {{},{0}}, /*spender_nlocktime=*/999, /*spender_nsequence=*/2); + Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE_FAILS, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("f5c14a15b45d2af1b8ec69acfd3cf4790f069705d1b079efb0b8193fed181f64"), {{},{0}}, /*spender_nlocktime=*/999, /*spender_nsequence=*/2); // But if both are set to (at least) the required value, we'll succeed. - Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/2); + Check("sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", "sh(wsh(thresh(2,ndv:after(1000),a:and_n(multi(1,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0),n:older(2)))))", SIGNABLE, {{"a914099f400961f930d4c16c3b33c0e2a58ef53ac38f87"}}, OutputType::P2SH_SEGWIT, /*op_desc_id=*/uint256S("f5c14a15b45d2af1b8ec69acfd3cf4790f069705d1b079efb0b8193fed181f64"), {{},{0}}, /*spender_nlocktime=*/1000, /*spender_nsequence=*/2); // 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, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {}); + 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, {{}}, /*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"), ParseHex("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")}}); // 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, {{}}, /*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, {{}}, /*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_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")}}); // 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, {{}}, /*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, {{}}, /*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_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")}}); // 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, {{}}, /*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, {{}}, /*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_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")}}); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/FuzzedDataProvider.h b/src/test/fuzz/FuzzedDataProvider.h index 6cbfc39bc2..8a8214bd99 100644 --- a/src/test/fuzz/FuzzedDataProvider.h +++ b/src/test/fuzz/FuzzedDataProvider.h @@ -209,7 +209,7 @@ T FuzzedDataProvider::ConsumeIntegralInRange(T min, T max) { abort(); // Use the biggest type possible to hold the range and the result. - uint64_t range = static_cast<uint64_t>(max) - min; + uint64_t range = static_cast<uint64_t>(max) - static_cast<uint64_t>(min); uint64_t result = 0; size_t offset = 0; @@ -230,7 +230,7 @@ T FuzzedDataProvider::ConsumeIntegralInRange(T min, T max) { if (range != std::numeric_limits<decltype(range)>::max()) result = result % (range + 1); - return static_cast<T>(min + result); + return static_cast<T>(static_cast<uint64_t>(min) + result); } // Returns a floating point value in the range [Type's lowest, Type's max] by @@ -390,7 +390,7 @@ TS FuzzedDataProvider::ConvertUnsignedToSigned(TU value) { return static_cast<TS>(value); } else { constexpr auto TS_min = std::numeric_limits<TS>::min(); - return TS_min + static_cast<char>(value - TS_min); + return TS_min + static_cast<TS>(value - TS_min); } } diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index 7218bb82d1..8ac1330dcb 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -46,7 +46,7 @@ void initialize_addrman() return NetGroupManager(asmap); } -FUZZ_TARGET_INIT(data_stream_addr_man, initialize_addrman) +FUZZ_TARGET(data_stream_addr_man, .init = initialize_addrman) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider); @@ -171,8 +171,8 @@ public: hasher.Write(a.source.GetNetwork()); hasher.Write(addr_key.size()); hasher.Write(source_key.size()); - hasher.Write(addr_key.data(), addr_key.size()); - hasher.Write(source_key.data(), source_key.size()); + hasher.Write(addr_key); + hasher.Write(source_key); return (size_t)hasher.Finalize(); }; @@ -233,7 +233,7 @@ public: } }; -FUZZ_TARGET_INIT(addrman, initialize_addrman) +FUZZ_TARGET(addrman, .init = initialize_addrman) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); SetMockTime(ConsumeTime(fuzzed_data_provider)); @@ -300,18 +300,26 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman) }); } const AddrMan& const_addr_man{addr_man}; + std::optional<Network> network; + if (fuzzed_data_provider.ConsumeBool()) { + network = fuzzed_data_provider.PickValueInArray(ALL_NETWORKS); + } (void)const_addr_man.GetAddr( /*max_addresses=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), /*max_pct=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), - /*network=*/std::nullopt); - (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool()); - (void)const_addr_man.Size(); + network); + (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network); + std::optional<bool> in_new; + if (fuzzed_data_provider.ConsumeBool()) { + in_new = fuzzed_data_provider.ConsumeBool(); + } + (void)const_addr_man.Size(network, in_new); CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION); data_stream << const_addr_man; } // Check that serialize followed by unserialize produces the same addrman. -FUZZ_TARGET_INIT(addrman_serdeser, initialize_addrman) +FUZZ_TARGET(addrman_serdeser, .init = initialize_addrman) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); SetMockTime(ConsumeTime(fuzzed_data_provider)); diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp index cb5d29d9b8..3882e0e547 100644 --- a/src/test/fuzz/banman.cpp +++ b/src/test/fuzz/banman.cpp @@ -40,7 +40,7 @@ static bool operator==(const CBanEntry& lhs, const CBanEntry& rhs) lhs.nBanUntil == rhs.nBanUntil; } -FUZZ_TARGET_INIT(banman, initialize_banman) +FUZZ_TARGET(banman, .init = initialize_banman) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; SetMockTime(ConsumeTime(fuzzed_data_provider)); diff --git a/src/test/fuzz/bitdeque.cpp b/src/test/fuzz/bitdeque.cpp index 634a3de346..65f5cb3fd0 100644 --- a/src/test/fuzz/bitdeque.cpp +++ b/src/test/fuzz/bitdeque.cpp @@ -31,7 +31,7 @@ void InitRandData() } // namespace -FUZZ_TARGET_INIT(bitdeque, InitRandData) +FUZZ_TARGET(bitdeque, .init = InitRandData) { FuzzedDataProvider provider(buffer.data(), buffer.size()); FastRandomContext ctx(true); diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp index e90dcc189a..8c97fba323 100644 --- a/src/test/fuzz/block.cpp +++ b/src/test/fuzz/block.cpp @@ -23,7 +23,7 @@ void initialize_block() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(block, initialize_block) +FUZZ_TARGET(block, .init = initialize_block) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); CBlock block; diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index fc7e000dc7..723dc6420f 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -41,7 +41,7 @@ void initialize_coins_view() g_setup = testing_setup.get(); } -FUZZ_TARGET_INIT(coins_view, initialize_coins_view) +FUZZ_TARGET(coins_view, .init = initialize_coins_view) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; CCoinsView backend_coins_view; diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index f81658b832..23fadd8984 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -28,7 +28,7 @@ void initialize_connman() g_setup = testing_setup.get(); } -FUZZ_TARGET_INIT(connman, initialize_connman) +FUZZ_TARGET(connman, .init = initialize_connman) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; SetMockTime(ConsumeTime(fuzzed_data_provider)); diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp index 84b95117e2..ca8c1cd033 100644 --- a/src/test/fuzz/crypto.cpp +++ b/src/test/fuzz/crypto.cpp @@ -57,7 +57,7 @@ FUZZ_TARGET(crypto) (void)sha256.Write(data.data(), data.size()); (void)sha3.Write(data); (void)sha512.Write(data.data(), data.size()); - (void)sip_hasher.Write(data.data(), data.size()); + (void)sip_hasher.Write(data); (void)Hash(data); (void)Hash160(data); diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp index 3fa445096a..63c7bf3b45 100644 --- a/src/test/fuzz/crypto_chacha20.cpp +++ b/src/test/fuzz/crypto_chacha20.cpp @@ -28,10 +28,11 @@ FUZZ_TARGET(crypto_chacha20) chacha20.SetKey32(key.data()); }, [&] { - chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); - }, - [&] { - chacha20.Seek64(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); + chacha20.Seek64( + { + fuzzed_data_provider.ConsumeIntegral<uint32_t>(), + fuzzed_data_provider.ConsumeIntegral<uint64_t>() + }, fuzzed_data_provider.ConsumeIntegral<uint32_t>()); }, [&] { std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); @@ -63,17 +64,16 @@ void ChaCha20SplitFuzz(FuzzedDataProvider& provider) auto key_bytes = provider.ConsumeBytes<unsigned char>(32); std::copy(key_bytes.begin(), key_bytes.end(), key); uint64_t iv = provider.ConsumeIntegral<uint64_t>(); + uint32_t iv_prefix = provider.ConsumeIntegral<uint32_t>(); uint64_t total_bytes = provider.ConsumeIntegralInRange<uint64_t>(0, 1000000); - /* ~x = 2^64 - 1 - x, so ~(total_bytes >> 6) is the maximal seek position. */ - uint64_t seek = provider.ConsumeIntegralInRange<uint64_t>(0, ~(total_bytes >> 6)); + /* ~x = 2^BITS - 1 - x, so ~(total_bytes >> 6) is the maximal seek position. */ + uint32_t seek = provider.ConsumeIntegralInRange<uint32_t>(0, ~(uint32_t)(total_bytes >> 6)); // Initialize two ChaCha20 ciphers, with the same key/iv/position. ChaCha20 crypt1(key); ChaCha20 crypt2(key); - crypt1.SetIV(iv); - crypt1.Seek64(seek); - crypt2.SetIV(iv); - crypt2.Seek64(seek); + crypt1.Seek64({iv_prefix, iv}, seek); + crypt2.Seek64({iv_prefix, iv}, seek); // Construct vectors with data. std::vector<unsigned char> data1, data2; diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp index 596614a71b..84ac65dd88 100644 --- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp +++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp @@ -26,16 +26,16 @@ FUZZ_TARGET(crypto_chacha20_poly1305_aead) uint64_t seqnr_aad = 0; int aad_pos = 0; size_t buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096); - std::vector<uint8_t> in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - std::vector<uint8_t> out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + std::vector<uint8_t> in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); + std::vector<uint8_t> out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); bool is_encrypt = fuzzed_data_provider.ConsumeBool(); LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { CallOneOf( fuzzed_data_provider, [&] { buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(64, 4096); - in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); + out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); }, [&] { (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt); diff --git a/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp b/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp index 78fee48de6..285ea2dfe0 100644 --- a/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp +++ b/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp @@ -284,6 +284,8 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20) // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey32() does static const uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + ChaCha20::Nonce96 nonce{0, 0}; + uint32_t counter{0}; ECRYPT_ivsetup(&ctx, iv); LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 3000) { @@ -292,45 +294,56 @@ FUZZ_TARGET(crypto_diff_fuzz_chacha20) [&] { const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32); chacha20.SetKey32(key.data()); + nonce = {0, 0}; + counter = 0; ECRYPT_keysetup(&ctx, key.data(), key.size() * 8, 0); // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey32() does uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0}; ECRYPT_ivsetup(&ctx, iv); }, [&] { + uint32_t iv_prefix = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); uint64_t iv = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); - chacha20.SetIV(iv); + nonce = {iv_prefix, iv}; + counter = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); + chacha20.Seek64(nonce, counter); + ctx.input[12] = counter; + ctx.input[13] = iv_prefix; ctx.input[14] = iv; ctx.input[15] = iv >> 32; }, [&] { - uint64_t counter = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); - chacha20.Seek64(counter); - ctx.input[12] = counter; - ctx.input[13] = counter >> 32; - }, - [&] { uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096); - // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that. - uint64_t pos = ctx.input[12] + (((uint64_t)ctx.input[13]) << 32) + ((integralInRange + 63) >> 6); std::vector<uint8_t> output(integralInRange); chacha20.Keystream(output.data(), output.size()); std::vector<uint8_t> djb_output(integralInRange); ECRYPT_keystream_bytes(&ctx, djb_output.data(), djb_output.size()); assert(output == djb_output); - chacha20.Seek64(pos); + // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that. + uint32_t old_counter = counter; + counter += (integralInRange + 63) >> 6; + if (counter < old_counter) ++nonce.first; + if (integralInRange & 63) { + chacha20.Seek64(nonce, counter); + } + assert(counter == ctx.input[12]); }, [&] { uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096); - // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that. - uint64_t pos = ctx.input[12] + (((uint64_t)ctx.input[13]) << 32) + ((integralInRange + 63) >> 6); std::vector<uint8_t> output(integralInRange); const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size()); chacha20.Crypt(input.data(), output.data(), input.size()); std::vector<uint8_t> djb_output(integralInRange); ECRYPT_encrypt_bytes(&ctx, input.data(), djb_output.data(), input.size()); assert(output == djb_output); - chacha20.Seek64(pos); + // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that. + uint32_t old_counter = counter; + counter += (integralInRange + 63) >> 6; + if (counter < old_counter) ++nonce.first; + if (integralInRange & 63) { + chacha20.Seek64(nonce, counter); + } + assert(counter == ctx.input[12]); }); } } diff --git a/src/test/fuzz/crypto_poly1305.cpp b/src/test/fuzz/crypto_poly1305.cpp index ac555ed68c..f49729a34b 100644 --- a/src/test/fuzz/crypto_poly1305.cpp +++ b/src/test/fuzz/crypto_poly1305.cpp @@ -14,9 +14,40 @@ FUZZ_TARGET(crypto_poly1305) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; - const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, POLY1305_KEYLEN); + const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, Poly1305::KEYLEN); const std::vector<uint8_t> in = ConsumeRandomLengthByteVector(fuzzed_data_provider); - std::vector<uint8_t> tag_out(POLY1305_TAGLEN); - poly1305_auth(tag_out.data(), in.data(), in.size(), key.data()); + std::vector<std::byte> tag_out(Poly1305::TAGLEN); + Poly1305{MakeByteSpan(key)}.Update(MakeByteSpan(in)).Finalize(tag_out); +} + + +FUZZ_TARGET(crypto_poly1305_split) +{ + FuzzedDataProvider provider{buffer.data(), buffer.size()}; + + // Read key and instantiate two Poly1305 objects with it. + auto key = provider.ConsumeBytes<std::byte>(Poly1305::KEYLEN); + key.resize(Poly1305::KEYLEN); + Poly1305 poly_full{key}, poly_split{key}; + + // Vector that holds all bytes processed so far. + std::vector<std::byte> total_input; + + // Process input in pieces. + LIMITED_WHILE(provider.remaining_bytes(), 100) { + auto in = provider.ConsumeRandomLengthString(); + poly_split.Update(MakeByteSpan(in)); + // Update total_input to match what was processed. + total_input.insert(total_input.end(), MakeByteSpan(in).begin(), MakeByteSpan(in).end()); + } + + // Process entire input at once. + poly_full.Update(total_input); + + // Verify both agree. + std::array<std::byte, Poly1305::TAGLEN> tag_split, tag_full; + poly_split.Finalize(tag_split); + poly_full.Finalize(tag_full); + assert(tag_full == tag_split); } diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index 12c22ef2ed..579942c3b5 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -14,7 +14,7 @@ void initialize_descriptor_parse() SelectParams(ChainType::MAIN); } -FUZZ_TARGET_INIT(descriptor_parse, initialize_descriptor_parse) +FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse) { const std::string descriptor(buffer.begin(), buffer.end()); FlatSigningProvider signing_provider; diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp index 177711c6e4..09402233bd 100644 --- a/src/test/fuzz/deserialize.cpp +++ b/src/test/fuzz/deserialize.cpp @@ -49,7 +49,7 @@ void initialize_deserialize() } #define FUZZ_TARGET_DESERIALIZE(name, code) \ - FUZZ_TARGET_INIT(name, initialize_deserialize) \ + FUZZ_TARGET(name, .init = initialize_deserialize) \ { \ try { \ code \ diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp index 44ba8bc254..c20cbde05f 100644 --- a/src/test/fuzz/fuzz.cpp +++ b/src/test/fuzz/fuzz.cpp @@ -54,20 +54,25 @@ const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS = return g_args; }; -std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>>& FuzzTargets() +struct FuzzTarget { + const TypeTestOneInput test_one_input; + const FuzzTargetOptions opts; +}; + +auto& FuzzTargets() { - static std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>> g_fuzz_targets; + static std::map<std::string_view, FuzzTarget> g_fuzz_targets; return g_fuzz_targets; } -void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init, TypeHidden hidden) +void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts) { - const auto it_ins = FuzzTargets().try_emplace(name, std::move(target), std::move(init), hidden); + const auto it_ins{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped in C++20 */ {std::move(target), std::move(opts)})}; Assert(it_ins.second); } static std::string_view g_fuzz_target; -static TypeTestOneInput* g_test_one_input{nullptr}; +static const TypeTestOneInput* g_test_one_input{nullptr}; void initialize() { @@ -84,22 +89,22 @@ void initialize() bool should_exit{false}; if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) { - for (const auto& t : FuzzTargets()) { - if (std::get<2>(t.second)) continue; - std::cout << t.first << std::endl; + for (const auto& [name, t] : FuzzTargets()) { + if (t.opts.hidden) continue; + std::cout << name << std::endl; } should_exit = true; } if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) { std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl; std::ofstream out_stream{out_path, std::ios::binary}; - for (const auto& t : FuzzTargets()) { - if (std::get<2>(t.second)) continue; - out_stream << t.first << std::endl; + for (const auto& [name, t] : FuzzTargets()) { + if (t.opts.hidden) continue; + out_stream << name << std::endl; } - should_exit= true; + should_exit = true; } - if (should_exit){ + if (should_exit) { std::exit(EXIT_SUCCESS); } if (const auto* env_fuzz{std::getenv("FUZZ")}) { @@ -117,8 +122,8 @@ void initialize() std::exit(EXIT_FAILURE); } Assert(!g_test_one_input); - g_test_one_input = &std::get<0>(it->second); - std::get<1>(it->second)(); + g_test_one_input = &it->second.test_one_input; + it->second.opts.init(); } #if defined(PROVIDE_FUZZ_MAIN_FUNCTION) diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h index c91c33da67..0534f9bcf1 100644 --- a/src/test/fuzz/fuzz.h +++ b/src/test/fuzz/fuzz.h @@ -21,25 +21,25 @@ using FuzzBufferType = Span<const uint8_t>; using TypeTestOneInput = std::function<void(FuzzBufferType)>; -using TypeInitialize = std::function<void()>; -using TypeHidden = bool; +struct FuzzTargetOptions { + std::function<void()> init{[] {}}; + bool hidden{false}; +}; -void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init, TypeHidden hidden); +void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts); -inline void FuzzFrameworkEmptyInitFun() {} +#if defined(__clang__) +#define FUZZ_TARGET(...) _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"") DETAIL_FUZZ(__VA_ARGS__) _Pragma("clang diagnostic pop") +#else +#define FUZZ_TARGET(...) DETAIL_FUZZ(__VA_ARGS__) +#endif -#define FUZZ_TARGET(name) \ - FUZZ_TARGET_INIT(name, FuzzFrameworkEmptyInitFun) - -#define FUZZ_TARGET_INIT(name, init_fun) \ - FUZZ_TARGET_INIT_HIDDEN(name, init_fun, false) - -#define FUZZ_TARGET_INIT_HIDDEN(name, init_fun, hidden) \ +#define DETAIL_FUZZ(name, ...) \ void name##_fuzz_target(FuzzBufferType); \ struct name##_Before_Main { \ name##_Before_Main() \ { \ - FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, init_fun, hidden); \ + FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, {__VA_ARGS__}); \ } \ } const static g_##name##_before_main; \ void name##_fuzz_target(FuzzBufferType buffer) diff --git a/src/test/fuzz/golomb_rice.cpp b/src/test/fuzz/golomb_rice.cpp index 1a1225b635..e006653ca9 100644 --- a/src/test/fuzz/golomb_rice.cpp +++ b/src/test/fuzz/golomb_rice.cpp @@ -23,7 +23,7 @@ namespace { uint64_t HashToRange(const std::vector<uint8_t>& element, const uint64_t f) { const uint64_t hash = CSipHasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL) - .Write(element.data(), element.size()) + .Write(element) .Finalize(); return FastRange64(hash, f); } diff --git a/src/test/fuzz/headerssync.cpp b/src/test/fuzz/headerssync.cpp index c1a187038b..62f6bbaffe 100644 --- a/src/test/fuzz/headerssync.cpp +++ b/src/test/fuzz/headerssync.cpp @@ -46,7 +46,7 @@ public: } }; -FUZZ_TARGET_INIT(headers_sync_state, initialize_headers_sync_state_fuzz) +FUZZ_TARGET(headers_sync_state, .init = initialize_headers_sync_state_fuzz) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); auto mock_time{ConsumeTime(fuzzed_data_provider)}; diff --git a/src/test/fuzz/i2p.cpp b/src/test/fuzz/i2p.cpp index 3c6db96446..943595f8a4 100644 --- a/src/test/fuzz/i2p.cpp +++ b/src/test/fuzz/i2p.cpp @@ -18,7 +18,7 @@ void initialize_i2p() static const auto testing_setup = MakeNoLogFileContext<>(); } -FUZZ_TARGET_INIT(i2p, initialize_i2p) +FUZZ_TARGET(i2p, .init = initialize_i2p) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index edb1dca457..91521bc7f4 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -46,7 +46,7 @@ void initialize_integer() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(integer, initialize_integer) +FUZZ_TARGET(integer, .init = initialize_integer) { if (buffer.size() < sizeof(uint256) + sizeof(uint160)) { return; diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp index 3eab2e20c0..8faeb9e04f 100644 --- a/src/test/fuzz/key.cpp +++ b/src/test/fuzz/key.cpp @@ -15,13 +15,17 @@ #include <script/signingprovider.h> #include <script/standard.h> #include <streams.h> +#include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <util/chaintype.h> #include <util/strencodings.h> +#include <array> #include <cassert> +#include <cstddef> #include <cstdint> #include <numeric> +#include <optional> #include <string> #include <vector> @@ -31,7 +35,7 @@ void initialize_key() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(key, initialize_key) +FUZZ_TARGET(key, .init = initialize_key) { const CKey key = [&] { CKey k; @@ -303,3 +307,79 @@ FUZZ_TARGET_INIT(key, initialize_key) } } } + +FUZZ_TARGET(ellswift_roundtrip, .init = initialize_key) +{ + FuzzedDataProvider fdp{buffer.data(), buffer.size()}; + + auto key_bytes = fdp.ConsumeBytes<uint8_t>(32); + key_bytes.resize(32); + CKey key; + key.Set(key_bytes.begin(), key_bytes.end(), true); + if (!key.IsValid()) return; + + auto ent32 = fdp.ConsumeBytes<std::byte>(32); + ent32.resize(32); + + auto encoded_ellswift = key.EllSwiftCreate(ent32); + auto decoded_pubkey = encoded_ellswift.Decode(); + + assert(key.VerifyPubKey(decoded_pubkey)); +} + +FUZZ_TARGET(bip324_ecdh, .init = initialize_key) +{ + FuzzedDataProvider fdp{buffer.data(), buffer.size()}; + + // We generate private key, k1. + auto rnd32 = fdp.ConsumeBytes<uint8_t>(32); + rnd32.resize(32); + CKey k1; + k1.Set(rnd32.begin(), rnd32.end(), true); + if (!k1.IsValid()) return; + + // They generate private key, k2. + rnd32 = fdp.ConsumeBytes<uint8_t>(32); + rnd32.resize(32); + CKey k2; + k2.Set(rnd32.begin(), rnd32.end(), true); + if (!k2.IsValid()) return; + + // We construct an ellswift encoding for our key, k1_ellswift. + auto ent32_1 = fdp.ConsumeBytes<std::byte>(32); + ent32_1.resize(32); + auto k1_ellswift = k1.EllSwiftCreate(ent32_1); + + // They construct an ellswift encoding for their key, k2_ellswift. + auto ent32_2 = fdp.ConsumeBytes<std::byte>(32); + ent32_2.resize(32); + auto k2_ellswift = k2.EllSwiftCreate(ent32_2); + + // They construct another (possibly distinct) ellswift encoding for their key, k2_ellswift_bad. + auto ent32_2_bad = fdp.ConsumeBytes<std::byte>(32); + ent32_2_bad.resize(32); + auto k2_ellswift_bad = k2.EllSwiftCreate(ent32_2_bad); + assert((ent32_2_bad == ent32_2) == (k2_ellswift_bad == k2_ellswift)); + + // Determine who is who. + bool initiating = fdp.ConsumeBool(); + + // We compute our shared secret using our key and their public key. + auto ecdh_secret_1 = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, initiating); + // They compute their shared secret using their key and our public key. + auto ecdh_secret_2 = k2.ComputeBIP324ECDHSecret(k1_ellswift, k2_ellswift, !initiating); + // Those must match, as everyone is behaving correctly. + assert(ecdh_secret_1 == ecdh_secret_2); + + if (k1_ellswift != k2_ellswift) { + // Unless the two keys are exactly identical, acting as the wrong party breaks things. + auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, !initiating); + assert(ecdh_secret_bad != ecdh_secret_1); + } + + if (k2_ellswift_bad != k2_ellswift) { + // Unless both encodings created by them are identical, using the second one breaks things. + auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift_bad, k1_ellswift, initiating); + assert(ecdh_secret_bad != ecdh_secret_1); + } +} diff --git a/src/test/fuzz/key_io.cpp b/src/test/fuzz/key_io.cpp index a1c587a75b..5f98f2b7f1 100644 --- a/src/test/fuzz/key_io.cpp +++ b/src/test/fuzz/key_io.cpp @@ -18,7 +18,7 @@ void initialize_key_io() SelectParams(ChainType::MAIN); } -FUZZ_TARGET_INIT(key_io, initialize_key_io) +FUZZ_TARGET(key_io, .init = initialize_key_io) { const std::string random_string(buffer.begin(), buffer.end()); diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp index f4b7dc08fd..502f7b897c 100644 --- a/src/test/fuzz/load_external_block_file.cpp +++ b/src/test/fuzz/load_external_block_file.cpp @@ -23,7 +23,7 @@ void initialize_load_external_block_file() g_setup = testing_setup.get(); } -FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file) +FUZZ_TARGET(load_external_block_file, .init = initialize_load_external_block_file) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedFileProvider fuzzed_file_provider = ConsumeFile(fuzzed_data_provider); diff --git a/src/test/fuzz/message.cpp b/src/test/fuzz/message.cpp index 8b7e3f11cc..f839f9e326 100644 --- a/src/test/fuzz/message.cpp +++ b/src/test/fuzz/message.cpp @@ -23,7 +23,7 @@ void initialize_message() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(message, initialize_message) +FUZZ_TARGET(message, .init = initialize_message) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::string random_message = fuzzed_data_provider.ConsumeRandomLengthString(1024); diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp index 2b371f6d5f..e17e2bad60 100644 --- a/src/test/fuzz/mini_miner.cpp +++ b/src/test/fuzz/mini_miner.cpp @@ -30,7 +30,7 @@ void initialize_miner() } // Test that the MiniMiner can run with various outpoints and feerates. -FUZZ_TARGET_INIT(mini_miner, initialize_miner) +FUZZ_TARGET(mini_miner, .init = initialize_miner) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; CTxMemPool pool{CTxMemPool::Options{}}; @@ -106,7 +106,7 @@ FUZZ_TARGET_INIT(mini_miner, initialize_miner) } // Test that MiniMiner and BlockAssembler build the same block given the same transactions and constraints. -FUZZ_TARGET_INIT(mini_miner_selection, initialize_miner) +FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; CTxMemPool pool{CTxMemPool::Options{}}; diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp index 81c6f076b2..56327b9665 100644 --- a/src/test/fuzz/miniscript.cpp +++ b/src/test/fuzz/miniscript.cpp @@ -943,7 +943,8 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider) assert(decoded->ToScript(PARSER_CTX) == script); assert(decoded->GetType() == node->GetType()); - if (provider.ConsumeBool() && node->GetOps() < MAX_OPS_PER_SCRIPT && node->ScriptSize() < MAX_STANDARD_P2WSH_SCRIPT_SIZE) { + const auto node_ops{node->GetOps()}; + if (provider.ConsumeBool() && node_ops && *node_ops < MAX_OPS_PER_SCRIPT && node->ScriptSize() < MAX_STANDARD_P2WSH_SCRIPT_SIZE) { // Optionally pad the script with OP_NOPs to max op the ops limit of the constructed script. // This makes the script obviously not actually miniscript-compatible anymore, but the // signatures constructed in this test don't commit to the script anyway, so the same @@ -954,7 +955,7 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider) // Do not pad more than what would cause MAX_STANDARD_P2WSH_SCRIPT_SIZE to be reached, however, // as that also invalidates scripts. int add = std::min<int>( - MAX_OPS_PER_SCRIPT - node->GetOps(), + MAX_OPS_PER_SCRIPT - *node_ops, MAX_STANDARD_P2WSH_SCRIPT_SIZE - node->ScriptSize()); for (int i = 0; i < add; ++i) script.push_back(OP_NOP); } @@ -972,7 +973,7 @@ void TestNode(const NodeRef& node, FuzzedDataProvider& provider) if (nonmal_success) { // Non-malleable satisfactions are bounded by GetStackSize(). - assert(witness_nonmal.stack.size() <= node->GetStackSize()); + assert(witness_nonmal.stack.size() <= *node->GetStackSize()); // If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it. assert(mal_success); assert(witness_nonmal.stack == witness_mal.stack); @@ -1058,7 +1059,7 @@ void FuzzInitSmart() } /** Fuzz target that runs TestNode on nodes generated using ConsumeNodeStable. */ -FUZZ_TARGET_INIT(miniscript_stable, FuzzInit) +FUZZ_TARGET(miniscript_stable, .init = FuzzInit) { FuzzedDataProvider provider(buffer.data(), buffer.size()); TestNode(GenNode([&](Type needed_type) { @@ -1067,7 +1068,7 @@ FUZZ_TARGET_INIT(miniscript_stable, FuzzInit) } /** Fuzz target that runs TestNode on nodes generated using ConsumeNodeSmart. */ -FUZZ_TARGET_INIT(miniscript_smart, FuzzInitSmart) +FUZZ_TARGET(miniscript_smart, .init = FuzzInitSmart) { /** The set of types we aim to construct nodes for. Together they cover all. */ static constexpr std::array<Type, 4> BASE_TYPES{"B"_mst, "V"_mst, "K"_mst, "W"_mst}; @@ -1079,7 +1080,7 @@ FUZZ_TARGET_INIT(miniscript_smart, FuzzInitSmart) } /* Fuzz tests that test parsing from a string, and roundtripping via string. */ -FUZZ_TARGET_INIT(miniscript_string, FuzzInit) +FUZZ_TARGET(miniscript_string, .init = FuzzInit) { FuzzedDataProvider provider(buffer.data(), buffer.size()); auto str = provider.ConsumeRemainingBytesAsString(); diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp index e090f13061..ddf919f2e6 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -27,7 +27,7 @@ void initialize_net() static const auto testing_setup = MakeNoLogFileContext<>(ChainType::MAIN); } -FUZZ_TARGET_INIT(net, initialize_net) +FUZZ_TARGET(net, .init = initialize_net) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); SetMockTime(ConsumeTime(fuzzed_data_provider)); diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp index 049ae02f4d..5141d3362d 100644 --- a/src/test/fuzz/netaddress.cpp +++ b/src/test/fuzz/netaddress.cpp @@ -84,7 +84,7 @@ FUZZ_TARGET(netaddress) (void)CServiceHash(0, 0)(service); const CNetAddr other_net_addr = ConsumeNetAddr(fuzzed_data_provider); - (void)net_addr.GetReachabilityFrom(&other_net_addr); + (void)net_addr.GetReachabilityFrom(other_net_addr); (void)sub_net.Match(other_net_addr); const CService other_service{net_addr, fuzzed_data_provider.ConsumeIntegral<uint16_t>()}; diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp index ec3cdbff5a..78350a600e 100644 --- a/src/test/fuzz/p2p_transport_serialization.cpp +++ b/src/test/fuzz/p2p_transport_serialization.cpp @@ -22,7 +22,7 @@ void initialize_p2p_transport_serialization() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serialization) +FUZZ_TARGET(p2p_transport_serialization, .init = initialize_p2p_transport_serialization) { // Construct deserializer, with a dummy NodeId V1TransportDeserializer deserializer{Params(), NodeId{0}, SER_NETWORK, INIT_PROTO_VERSION}; @@ -77,7 +77,7 @@ FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serializa assert(msg.m_time == m_time); std::vector<unsigned char> header; - auto msg2 = CNetMsgMaker{msg.m_recv.GetVersion()}.Make(msg.m_type, MakeUCharSpan(msg.m_recv)); + auto msg2 = CNetMsgMaker{msg.m_recv.GetVersion()}.Make(msg.m_type, Span{msg.m_recv}); serializer.prepareForTransport(msg2, header); } } diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp index 6d33c1a8cc..c9096d0386 100644 --- a/src/test/fuzz/parse_univalue.cpp +++ b/src/test/fuzz/parse_univalue.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> -#include <core_io.h> #include <rpc/client.h> #include <rpc/util.h> #include <test/fuzz/fuzz.h> @@ -17,7 +16,7 @@ void initialize_parse_univalue() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(parse_univalue, initialize_parse_univalue) +FUZZ_TARGET(parse_univalue, .init = initialize_parse_univalue) { const std::string random_string(buffer.begin(), buffer.end()); bool valid = true; @@ -58,12 +57,6 @@ FUZZ_TARGET_INIT(parse_univalue, initialize_parse_univalue) } catch (const UniValue&) { } try { - (void)ParseHexUV(univalue, "A"); - (void)ParseHexUV(univalue, random_string); - } catch (const UniValue&) { - } catch (const std::runtime_error&) { - } - try { (void)ParseHexV(univalue, "A"); } catch (const UniValue&) { } catch (const std::runtime_error&) { @@ -75,7 +68,7 @@ FUZZ_TARGET_INIT(parse_univalue, initialize_parse_univalue) } try { (void)ParseSighashString(univalue); - } catch (const std::runtime_error&) { + } catch (const UniValue&) { } try { (void)AmountFromValue(univalue); diff --git a/src/test/fuzz/partially_downloaded_block.cpp b/src/test/fuzz/partially_downloaded_block.cpp index f8ba4f08d9..ae7a68762e 100644 --- a/src/test/fuzz/partially_downloaded_block.cpp +++ b/src/test/fuzz/partially_downloaded_block.cpp @@ -40,7 +40,7 @@ PartiallyDownloadedBlock::CheckBlockFn FuzzedCheckBlock(std::optional<BlockValid }; } -FUZZ_TARGET_INIT(partially_downloaded_block, initialize_pdb) +FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp index aa3cfe81df..227ee9d2c4 100644 --- a/src/test/fuzz/policy_estimator.cpp +++ b/src/test/fuzz/policy_estimator.cpp @@ -28,7 +28,7 @@ void initialize_policy_estimator() g_setup = testing_setup.get(); } -FUZZ_TARGET_INIT(policy_estimator, initialize_policy_estimator) +FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CBlockPolicyEstimator block_policy_estimator{FeeestPath(*g_setup->m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES}; diff --git a/src/test/fuzz/policy_estimator_io.cpp b/src/test/fuzz/policy_estimator_io.cpp index 3df40197d8..c04ef8f5b0 100644 --- a/src/test/fuzz/policy_estimator_io.cpp +++ b/src/test/fuzz/policy_estimator_io.cpp @@ -22,7 +22,7 @@ void initialize_policy_estimator_io() g_setup = testing_setup.get(); } -FUZZ_TARGET_INIT(policy_estimator_io, initialize_policy_estimator_io) +FUZZ_TARGET(policy_estimator_io, .init = initialize_policy_estimator_io) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider); diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp index 6d584c9f10..05cdb740e4 100644 --- a/src/test/fuzz/pow.cpp +++ b/src/test/fuzz/pow.cpp @@ -23,7 +23,7 @@ void initialize_pow() SelectParams(ChainType::MAIN); } -FUZZ_TARGET_INIT(pow, initialize_pow) +FUZZ_TARGET(pow, .init = initialize_pow) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const Consensus::Params& consensus_params = Params().GetConsensus(); @@ -87,7 +87,7 @@ FUZZ_TARGET_INIT(pow, initialize_pow) } -FUZZ_TARGET_INIT(pow_transition, initialize_pow) +FUZZ_TARGET(pow_transition, .init = initialize_pow) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const Consensus::Params& consensus_params{Params().GetConsensus()}; diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index 744ff4701d..6ed83feddf 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -58,7 +58,7 @@ void initialize_process_message() SyncWithValidationInterfaceQueue(); } -FUZZ_TARGET_INIT(process_message, initialize_process_message) +FUZZ_TARGET(process_message, .init = initialize_process_message) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp index 68d4e02a26..58e023956c 100644 --- a/src/test/fuzz/process_messages.cpp +++ b/src/test/fuzz/process_messages.cpp @@ -33,7 +33,7 @@ void initialize_process_messages() SyncWithValidationInterfaceQueue(); } -FUZZ_TARGET_INIT(process_messages, initialize_process_messages) +FUZZ_TARGET(process_messages, .init = initialize_process_messages) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); diff --git a/src/test/fuzz/rbf.cpp b/src/test/fuzz/rbf.cpp index 57a9a15a85..dbe99029c3 100644 --- a/src/test/fuzz/rbf.cpp +++ b/src/test/fuzz/rbf.cpp @@ -29,7 +29,7 @@ void initialize_rbf() g_setup = testing_setup.get(); } -FUZZ_TARGET_INIT(rbf, initialize_rbf) +FUZZ_TARGET(rbf, .init = initialize_rbf) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); SetMockTime(ConsumeTime(fuzzed_data_provider)); diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp index b1858a1800..2782888dc3 100644 --- a/src/test/fuzz/rpc.cpp +++ b/src/test/fuzz/rpc.cpp @@ -73,6 +73,7 @@ const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{ "addpeeraddress", // avoid DNS lookups "dumptxoutset", // avoid writing to disk "dumpwallet", // avoid writing to disk + "enumeratesigners", "echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.) "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large) "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large) @@ -347,7 +348,7 @@ void initialize_rpc() } } -FUZZ_TARGET_INIT(rpc, initialize_rpc) +FUZZ_TARGET(rpc, .init = initialize_rpc) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; SetMockTime(ConsumeTime(fuzzed_data_provider)); diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp index 8a88c1107a..1cb7d01906 100644 --- a/src/test/fuzz/script.cpp +++ b/src/test/fuzz/script.cpp @@ -36,7 +36,7 @@ void initialize_script() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(script, initialize_script) +FUZZ_TARGET(script, .init = initialize_script) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const CScript script{ConsumeScript(fuzzed_data_provider)}; diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp index 206d219afe..7862be2f21 100644 --- a/src/test/fuzz/script_assets_test_minimizer.cpp +++ b/src/test/fuzz/script_assets_test_minimizer.cpp @@ -186,7 +186,7 @@ void Test(const std::string& str) void test_init() {} -FUZZ_TARGET_INIT_HIDDEN(script_assets_test_minimizer, test_init, /*hidden=*/true) +FUZZ_TARGET(script_assets_test_minimizer, .init = test_init, .hidden = true) { if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return; const std::string str((const char*)buffer.data(), buffer.size() - 2); diff --git a/src/test/fuzz/script_format.cpp b/src/test/fuzz/script_format.cpp index 5aa0ea58ff..10150dcd7f 100644 --- a/src/test/fuzz/script_format.cpp +++ b/src/test/fuzz/script_format.cpp @@ -18,7 +18,7 @@ void initialize_script_format() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(script_format, initialize_script_format) +FUZZ_TARGET(script_format, .init = initialize_script_format) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const CScript script{ConsumeScript(fuzzed_data_provider)}; diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp index f332987987..486b1e5197 100644 --- a/src/test/fuzz/script_sigcache.cpp +++ b/src/test/fuzz/script_sigcache.cpp @@ -26,7 +26,7 @@ void initialize_script_sigcache() g_setup = testing_setup.get(); } -FUZZ_TARGET_INIT(script_sigcache, initialize_script_sigcache) +FUZZ_TARGET(script_sigcache, .init = initialize_script_sigcache) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp index 8b62daf162..cec98432e1 100644 --- a/src/test/fuzz/script_sign.cpp +++ b/src/test/fuzz/script_sign.cpp @@ -30,7 +30,7 @@ void initialize_script_sign() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(script_sign, initialize_script_sign) +FUZZ_TARGET(script_sign, .init = initialize_script_sign) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::vector<uint8_t> key = ConsumeRandomLengthByteVector(fuzzed_data_provider, 128); diff --git a/src/test/fuzz/signet.cpp b/src/test/fuzz/signet.cpp index e9af93c639..3ccf5eb6f0 100644 --- a/src/test/fuzz/signet.cpp +++ b/src/test/fuzz/signet.cpp @@ -22,7 +22,7 @@ void initialize_signet() static const auto testing_setup = MakeNoLogFileContext<>(ChainType::SIGNET); } -FUZZ_TARGET_INIT(signet, initialize_signet) +FUZZ_TARGET(signet, .init = initialize_signet) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; const std::optional<CBlock> block = ConsumeDeserializable<CBlock>(fuzzed_data_provider); diff --git a/src/test/fuzz/socks5.cpp b/src/test/fuzz/socks5.cpp index 73235b7ced..05b8312ab2 100644 --- a/src/test/fuzz/socks5.cpp +++ b/src/test/fuzz/socks5.cpp @@ -26,7 +26,7 @@ void initialize_socks5() default_socks5_recv_timeout = g_socks5_recv_timeout; } -FUZZ_TARGET_INIT(socks5, initialize_socks5) +FUZZ_TARGET(socks5, .init = initialize_socks5) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; ProxyCredentials proxy_credentials; diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp index 73c01d9297..73ae89b52a 100644 --- a/src/test/fuzz/system.cpp +++ b/src/test/fuzz/system.cpp @@ -27,7 +27,7 @@ std::string GetArgumentName(const std::string& name) return name.substr(0, idx); } -FUZZ_TARGET_INIT(system, initialize_system) +FUZZ_TARGET(system, .init = initialize_system) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); ArgsManager args_manager{}; diff --git a/src/test/fuzz/torcontrol.cpp b/src/test/fuzz/torcontrol.cpp index a78715f769..6189b3c08d 100644 --- a/src/test/fuzz/torcontrol.cpp +++ b/src/test/fuzz/torcontrol.cpp @@ -39,7 +39,7 @@ void initialize_torcontrol() static const auto testing_setup = MakeNoLogFileContext<>(); } -FUZZ_TARGET_INIT(torcontrol, initialize_torcontrol) +FUZZ_TARGET(torcontrol, .init = initialize_torcontrol) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp index c561675d1a..88c2a334c7 100644 --- a/src/test/fuzz/transaction.cpp +++ b/src/test/fuzz/transaction.cpp @@ -27,7 +27,7 @@ void initialize_transaction() SelectParams(ChainType::REGTEST); } -FUZZ_TARGET_INIT(transaction, initialize_transaction) +FUZZ_TARGET(transaction, .init = initialize_transaction) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); try { diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp index b758c715ef..ee73f67f66 100644 --- a/src/test/fuzz/tx_pool.cpp +++ b/src/test/fuzz/tx_pool.cpp @@ -131,7 +131,7 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte return CTxMemPool{mempool_opts}; } -FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool) +FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const auto& node = g_setup->m_node; @@ -307,7 +307,7 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool) Finish(fuzzed_data_provider, tx_pool, chainstate); } -FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool) +FUZZ_TARGET(tx_pool, .init = initialize_tx_pool) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const auto& node = g_setup->m_node; diff --git a/src/test/fuzz/txorphan.cpp b/src/test/fuzz/txorphan.cpp index ed55e3fad5..a84dc951fc 100644 --- a/src/test/fuzz/txorphan.cpp +++ b/src/test/fuzz/txorphan.cpp @@ -30,7 +30,7 @@ void initialize_orphanage() static const auto testing_setup = MakeNoLogFileContext(); } -FUZZ_TARGET_INIT(txorphan, initialize_orphanage) +FUZZ_TARGET(txorphan, .init = initialize_orphanage) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); SetMockTime(ConsumeTime(fuzzed_data_provider)); diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp index b4ef0c7a3e..dce728d96b 100644 --- a/src/test/fuzz/utxo_snapshot.cpp +++ b/src/test/fuzz/utxo_snapshot.cpp @@ -28,7 +28,7 @@ void initialize_chain() g_chain = &chain; } -FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain) +FUZZ_TARGET(utxo_snapshot, .init = initialize_chain) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); std::unique_ptr<const TestingSetup> setup{MakeNoLogFileContext<const TestingSetup>()}; diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp index 4e2ca6d903..c203dd4e39 100644 --- a/src/test/fuzz/validation_load_mempool.cpp +++ b/src/test/fuzz/validation_load_mempool.cpp @@ -33,7 +33,7 @@ void initialize_validation_load_mempool() g_setup = testing_setup.get(); } -FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool) +FUZZ_TARGET(validation_load_mempool, .init = initialize_validation_load_mempool) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; SetMockTime(ConsumeTime(fuzzed_data_provider)); diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp index b3df4dadd2..c1b0f552ea 100644 --- a/src/test/fuzz/versionbits.cpp +++ b/src/test/fuzz/versionbits.cpp @@ -111,7 +111,7 @@ void initialize() constexpr uint32_t MAX_START_TIME = 4102444800; // 2100-01-01 -FUZZ_TARGET_INIT(versionbits, initialize) +FUZZ_TARGET(versionbits, .init = initialize) { const CChainParams& params = *g_params; const int64_t interval = params.GetConsensus().nPowTargetSpacing; diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp index f1f435591b..a990797ca7 100644 --- a/src/test/hash_tests.cpp +++ b/src/test/hash_tests.cpp @@ -83,21 +83,21 @@ BOOST_AUTO_TEST_CASE(siphash) CSipHasher hasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x726fdb47dd0e0e31ull); static const unsigned char t0[1] = {0}; - hasher.Write(t0, 1); + hasher.Write(t0); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x74f839c593dc67fdull); static const unsigned char t1[7] = {1,2,3,4,5,6,7}; - hasher.Write(t1, 7); + hasher.Write(t1); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x93f5f5799a932462ull); hasher.Write(0x0F0E0D0C0B0A0908ULL); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x3f2acc7f57c29bdbull); static const unsigned char t2[2] = {16,17}; - hasher.Write(t2, 2); + hasher.Write(t2); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x4bc1b3f0968dd39cull); static const unsigned char t3[9] = {18,19,20,21,22,23,24,25,26}; - hasher.Write(t3, 9); + hasher.Write(t3); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x2f2e6163076bcfadull); static const unsigned char t4[5] = {27,28,29,30,31}; - hasher.Write(t4, 5); + hasher.Write(t4); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x7127512f72f27cceull); hasher.Write(0x2726252423222120ULL); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x0e3ea96b5304a7d0ull); @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(siphash) for (uint8_t x=0; x<std::size(siphash_4_2_testvec); ++x) { BOOST_CHECK_EQUAL(hasher2.Finalize(), siphash_4_2_testvec[x]); - hasher2.Write(&x, 1); + hasher2.Write(Span{&x, 1}); } // Check test vectors from spec, eight bytes at a time CSipHasher hasher3(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); @@ -140,9 +140,9 @@ BOOST_AUTO_TEST_CASE(siphash) uint8_t nb[4]; WriteLE32(nb, n); CSipHasher sip256(k1, k2); - sip256.Write(x.begin(), 32); + sip256.Write(x); CSipHasher sip288 = sip256; - sip288.Write(nb, 4); + sip288.Write(nb); BOOST_CHECK_EQUAL(SipHashUint256(k1, k2, x), sip256.Finalize()); BOOST_CHECK_EQUAL(SipHashUint256Extra(k1, k2, x, n), sip288.Finalize()); } diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 8f11bf5db2..86a8d17a76 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -344,4 +344,24 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors) } } +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_SUITE_END() diff --git a/src/test/miniminer_tests.cpp b/src/test/miniminer_tests.cpp index b26f7dafe3..1ee9e0c066 100644 --- a/src/test/miniminer_tests.cpp +++ b/src/test/miniminer_tests.cpp @@ -463,8 +463,8 @@ BOOST_FIXTURE_TEST_CASE(calculate_cluster, TestChain100Setup) } const auto vec_iters_zigzag = pool.GetIterVec(zigzag_txids); // It doesn't matter which tx we calculate cluster for, everybody is in it. - const std::vector<size_t> indeces{0, 22, 72, zigzag_txids.size() - 1}; - for (const auto index : indeces) { + const std::vector<size_t> indices{0, 22, 72, zigzag_txids.size() - 1}; + for (const auto index : indices) { const auto cluster = pool.GatherClusters({zigzag_txids[index]}); BOOST_CHECK_EQUAL(cluster.size(), zigzag_txids.size()); CTxMemPool::setEntries clusterset{cluster.begin(), cluster.end()}; diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp index 42e441c41a..9c811db3e9 100644 --- a/src/test/miniscript_tests.cpp +++ b/src/test/miniscript_tests.cpp @@ -6,6 +6,7 @@ #include <string> #include <vector> +#include <test/util/random.h> #include <test/util/setup_common.h> #include <boost/test/unit_test.hpp> @@ -297,7 +298,7 @@ void TestSatisfy(const std::string& testcase, const NodeRef& node) { if (nonmal_success) { // Non-malleable satisfactions are bounded by GetStackSize(). - BOOST_CHECK(witness_nonmal.stack.size() <= node->GetStackSize()); + BOOST_CHECK(witness_nonmal.stack.size() <= *node->GetStackSize()); // If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it. BOOST_CHECK(mal_success); BOOST_CHECK(witness_nonmal.stack == witness_mal.stack); @@ -375,8 +376,8 @@ void Test(const std::string& ms, const std::string& hexscript, int mode, int ops auto inferred_miniscript = miniscript::FromScript(computed_script, CONVERTER); BOOST_CHECK_MESSAGE(inferred_miniscript, "Cannot infer miniscript from script: " + ms); BOOST_CHECK_MESSAGE(inferred_miniscript->ToScript(CONVERTER) == computed_script, "Roundtrip failure: miniscript->script != miniscript->script->miniscript->script: " + ms); - if (opslimit != -1) BOOST_CHECK_MESSAGE((int)node->GetOps() == opslimit, "Ops limit mismatch: " << ms << " (" << node->GetOps() << " vs " << opslimit << ")"); - if (stacklimit != -1) BOOST_CHECK_MESSAGE((int)node->GetStackSize() == stacklimit, "Stack limit mismatch: " << ms << " (" << node->GetStackSize() << " vs " << stacklimit << ")"); + if (opslimit != -1) BOOST_CHECK_MESSAGE((int)*node->GetOps() == opslimit, "Ops limit mismatch: " << ms << " (" << *node->GetOps() << " vs " << opslimit << ")"); + if (stacklimit != -1) BOOST_CHECK_MESSAGE((int)*node->GetStackSize() == stacklimit, "Stack limit mismatch: " << ms << " (" << *node->GetStackSize() << " vs " << stacklimit << ")"); TestSatisfy(ms, node); } } @@ -498,8 +499,8 @@ BOOST_AUTO_TEST_CASE(fixed_tests) // For CHECKMULTISIG the OP cost is the number of keys, but the stack size is the number of sigs (+1) const auto ms_multi = miniscript::FromString("multi(1,03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", CONVERTER); BOOST_CHECK(ms_multi); - BOOST_CHECK_EQUAL(ms_multi->GetOps(), 4); // 3 pubkeys + CMS - BOOST_CHECK_EQUAL(ms_multi->GetStackSize(), 3); // 1 sig + dummy elem + script push + BOOST_CHECK_EQUAL(*ms_multi->GetOps(), 4); // 3 pubkeys + CMS + BOOST_CHECK_EQUAL(*ms_multi->GetStackSize(), 3); // 1 sig + dummy elem + script push // The 'd:' wrapper leaves on the stack what was DUP'ed at the beginning of its execution. // Since it contains an OP_IF just after on the same element, we can make sure that the element // in question must be OP_1 if OP_IF enforces that its argument must only be OP_1 or the empty diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index aa577f7b27..ead604598e 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -904,4 +904,109 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message) TestOnlyResetTimeData(); } + +BOOST_AUTO_TEST_CASE(advertise_local_address) +{ + auto CreatePeer = [](const CAddress& addr) { + return std::make_unique<CNode>(/*id=*/0, + /*sock=*/nullptr, + addr, + /*nKeyedNetGroupIn=*/0, + /*nLocalHostNonceIn=*/0, + CAddress{}, + /*pszDest=*/std::string{}, + ConnectionType::OUTBOUND_FULL_RELAY, + /*inbound_onion=*/false); + }; + SetReachable(NET_CJDNS, true); + + CAddress addr_ipv4{Lookup("1.2.3.4", 8333, false).value(), NODE_NONE}; + BOOST_REQUIRE(addr_ipv4.IsValid()); + BOOST_REQUIRE(addr_ipv4.IsIPv4()); + + CAddress addr_ipv6{Lookup("1122:3344:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE}; + BOOST_REQUIRE(addr_ipv6.IsValid()); + BOOST_REQUIRE(addr_ipv6.IsIPv6()); + + CAddress addr_ipv6_tunnel{Lookup("2002:3344:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE}; + BOOST_REQUIRE(addr_ipv6_tunnel.IsValid()); + BOOST_REQUIRE(addr_ipv6_tunnel.IsIPv6()); + BOOST_REQUIRE(addr_ipv6_tunnel.IsRFC3964()); + + CAddress addr_teredo{Lookup("2001:0000:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE}; + BOOST_REQUIRE(addr_teredo.IsValid()); + BOOST_REQUIRE(addr_teredo.IsIPv6()); + BOOST_REQUIRE(addr_teredo.IsRFC4380()); + + CAddress addr_onion; + BOOST_REQUIRE(addr_onion.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")); + BOOST_REQUIRE(addr_onion.IsValid()); + BOOST_REQUIRE(addr_onion.IsTor()); + + CAddress addr_i2p; + BOOST_REQUIRE(addr_i2p.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p")); + BOOST_REQUIRE(addr_i2p.IsValid()); + BOOST_REQUIRE(addr_i2p.IsI2P()); + + CService service_cjdns{Lookup("fc00:3344:5566:7788:9900:aabb:ccdd:eeff", 8333, false).value(), NODE_NONE}; + CAddress addr_cjdns{MaybeFlipIPv6toCJDNS(service_cjdns), NODE_NONE}; + BOOST_REQUIRE(addr_cjdns.IsValid()); + BOOST_REQUIRE(addr_cjdns.IsCJDNS()); + + const auto peer_ipv4{CreatePeer(addr_ipv4)}; + const auto peer_ipv6{CreatePeer(addr_ipv6)}; + const auto peer_ipv6_tunnel{CreatePeer(addr_ipv6_tunnel)}; + const auto peer_teredo{CreatePeer(addr_teredo)}; + const auto peer_onion{CreatePeer(addr_onion)}; + const auto peer_i2p{CreatePeer(addr_i2p)}; + const auto peer_cjdns{CreatePeer(addr_cjdns)}; + + // one local clearnet address - advertise to all but privacy peers + AddLocal(addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_ipv4) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_ipv6) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_ipv6_tunnel) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_teredo) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_cjdns) == addr_ipv4); + BOOST_CHECK(!GetLocalAddress(*peer_onion).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_i2p).IsValid()); + RemoveLocal(addr_ipv4); + + // local privacy addresses - don't advertise to clearnet peers + AddLocal(addr_onion); + AddLocal(addr_i2p); + BOOST_CHECK(!GetLocalAddress(*peer_ipv4).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_ipv6).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_ipv6_tunnel).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_teredo).IsValid()); + BOOST_CHECK(!GetLocalAddress(*peer_cjdns).IsValid()); + BOOST_CHECK(GetLocalAddress(*peer_onion) == addr_onion); + BOOST_CHECK(GetLocalAddress(*peer_i2p) == addr_i2p); + RemoveLocal(addr_onion); + RemoveLocal(addr_i2p); + + // local addresses from all networks + AddLocal(addr_ipv4); + AddLocal(addr_ipv6); + AddLocal(addr_ipv6_tunnel); + AddLocal(addr_teredo); + AddLocal(addr_onion); + AddLocal(addr_i2p); + AddLocal(addr_cjdns); + BOOST_CHECK(GetLocalAddress(*peer_ipv4) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_ipv6) == addr_ipv6); + BOOST_CHECK(GetLocalAddress(*peer_ipv6_tunnel) == addr_ipv6); + BOOST_CHECK(GetLocalAddress(*peer_teredo) == addr_ipv4); + BOOST_CHECK(GetLocalAddress(*peer_onion) == addr_onion); + BOOST_CHECK(GetLocalAddress(*peer_i2p) == addr_i2p); + BOOST_CHECK(GetLocalAddress(*peer_cjdns) == addr_cjdns); + RemoveLocal(addr_ipv4); + RemoveLocal(addr_ipv6); + RemoveLocal(addr_ipv6_tunnel); + RemoveLocal(addr_teredo); + RemoveLocal(addr_onion); + RemoveLocal(addr_i2p); + RemoveLocal(addr_cjdns); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 414e4509f5..43d887b5c9 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -4,6 +4,7 @@ #include <random.h> +#include <test/util/random.h> #include <test/util/setup_common.h> #include <util/time.h> diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 09f77d2b61..2e862621bc 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -186,32 +186,32 @@ BOOST_AUTO_TEST_CASE(noncanonical) std::vector<char>::size_type n; // zero encoded with three bytes: - ss.write(MakeByteSpan("\xfd\x00\x00").first(3)); + ss << Span{"\xfd\x00\x00"}.first(3); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0xfc encoded with three bytes: - ss.write(MakeByteSpan("\xfd\xfc\x00").first(3)); + ss << Span{"\xfd\xfc\x00"}.first(3); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0xfd encoded with three bytes is OK: - ss.write(MakeByteSpan("\xfd\xfd\x00").first(3)); + ss << Span{"\xfd\xfd\x00"}.first(3); n = ReadCompactSize(ss); BOOST_CHECK(n == 0xfd); // zero encoded with five bytes: - ss.write(MakeByteSpan("\xfe\x00\x00\x00\x00").first(5)); + ss << Span{"\xfe\x00\x00\x00\x00"}.first(5); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0xffff encoded with five bytes: - ss.write(MakeByteSpan("\xfe\xff\xff\x00\x00").first(5)); + ss << Span{"\xfe\xff\xff\x00\x00"}.first(5); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // zero encoded with nine bytes: - ss.write(MakeByteSpan("\xff\x00\x00\x00\x00\x00\x00\x00\x00").first(9)); + ss << Span{"\xff\x00\x00\x00\x00\x00\x00\x00\x00"}.first(9); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0x01ffffff encoded with nine bytes: - ss.write(MakeByteSpan("\xff\xff\xff\xff\x01\x00\x00\x00\x00").first(9)); + ss << Span{"\xff\xff\xff\xff\x01\x00\x00\x00\x00"}.first(9); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); } @@ -241,6 +241,17 @@ BOOST_AUTO_TEST_CASE(class_methods) ss2 << intval << boolval << stringval << charstrval << txval; ss2 >> methodtest3; BOOST_CHECK(methodtest3 == methodtest4); + { + DataStream ds; + const std::string in{"ab"}; + ds << Span{in} << std::byte{'c'}; + std::array<std::byte, 2> out; + std::byte out_3; + ds >> Span{out} >> out_3; + BOOST_CHECK_EQUAL(out.at(0), std::byte{'a'}); + BOOST_CHECK_EQUAL(out.at(1), std::byte{'b'}); + BOOST_CHECK_EQUAL(out_3, std::byte{'c'}); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/system_tests.cpp b/src/test/system_tests.cpp index 7ce350b84b..740f461548 100644 --- a/src/test/system_tests.cpp +++ b/src/test/system_tests.cpp @@ -7,16 +7,7 @@ #include <univalue.h> #ifdef ENABLE_EXTERNAL_SIGNER -#if defined(__GNUC__) -// Boost 1.78 requires the following workaround. -// See: https://github.com/boostorg/process/issues/235 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnarrowing" -#endif #include <boost/process.hpp> -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif #endif // ENABLE_EXTERNAL_SIGNER #include <boost/test/unit_test.hpp> diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp index b666517ae2..cb80dbed69 100644 --- a/src/test/txindex_tests.cpp +++ b/src/test/txindex_tests.cpp @@ -6,8 +6,8 @@ #include <index/txindex.h> #include <interfaces/chain.h> #include <script/standard.h> +#include <test/util/index.h> #include <test/util/setup_common.h> -#include <util/time.h> #include <validation.h> #include <boost/test/unit_test.hpp> @@ -17,6 +17,7 @@ BOOST_AUTO_TEST_SUITE(txindex_tests) BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) { TxIndex txindex(interfaces::MakeChain(m_node), 1 << 20, true); + BOOST_REQUIRE(txindex.Init()); CTransactionRef tx_disk; uint256 block_hash; @@ -29,15 +30,10 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) // BlockUntilSyncedToCurrentChain should return false before txindex is started. BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain()); - BOOST_REQUIRE(txindex.Start()); + BOOST_REQUIRE(txindex.StartBackgroundSync()); // Allow tx index to catch up with the block index. - constexpr auto timeout{10s}; - const auto time_start{SteadyClock::now()}; - while (!txindex.BlockUntilSyncedToCurrentChain()) { - BOOST_REQUIRE(time_start + timeout > SteadyClock::now()); - UninterruptibleSleep(std::chrono::milliseconds{100}); - } + IndexWaitSynced(txindex); // Check that txindex excludes genesis block transactions. const CBlock& genesis_block = Params().GenesisBlock(); diff --git a/src/test/util/index.cpp b/src/test/util/index.cpp new file mode 100644 index 0000000000..e653d5dbf0 --- /dev/null +++ b/src/test/util/index.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2020-2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <test/util/index.h> + +#include <index/base.h> +#include <shutdown.h> +#include <util/check.h> +#include <util/time.h> + +void IndexWaitSynced(const BaseIndex& index) +{ + while (!index.BlockUntilSyncedToCurrentChain()) { + // Assert shutdown was not requested to abort the test, instead of looping forever, in case + // there was an unexpected error in the index that caused it to stop syncing and request a shutdown. + Assert(!ShutdownRequested()); + + UninterruptibleSleep(100ms); + } + assert(index.GetSummary().synced); +} diff --git a/src/test/util/index.h b/src/test/util/index.h new file mode 100644 index 0000000000..95309f6273 --- /dev/null +++ b/src/test/util/index.h @@ -0,0 +1,13 @@ +// Copyright (c) 2020-2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TEST_UTIL_INDEX_H +#define BITCOIN_TEST_UTIL_INDEX_H + +class BaseIndex; + +/** Block until the index is synced to the current chain */ +void IndexWaitSynced(const BaseIndex& index); + +#endif // BITCOIN_TEST_UTIL_INDEX_H diff --git a/src/test/util/random.cpp b/src/test/util/random.cpp new file mode 100644 index 0000000000..4c87ab8df8 --- /dev/null +++ b/src/test/util/random.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <test/util/random.h> + +#include <logging.h> +#include <random.h> +#include <uint256.h> + +#include <cstdlib> +#include <string> + +FastRandomContext g_insecure_rand_ctx; + +/** Return the unsigned from the environment var if available, otherwise 0 */ +static uint256 GetUintFromEnv(const std::string& env_name) +{ + const char* num = std::getenv(env_name.c_str()); + if (!num) return {}; + return uint256S(num); +} + +void Seed(FastRandomContext& ctx) +{ + // Should be enough to get the seed once for the process + static uint256 seed{}; + static const std::string RANDOM_CTX_SEED{"RANDOM_CTX_SEED"}; + if (seed.IsNull()) seed = GetUintFromEnv(RANDOM_CTX_SEED); + if (seed.IsNull()) seed = GetRandHash(); + LogPrintf("%s: Setting random seed for current tests to %s=%s\n", __func__, RANDOM_CTX_SEED, seed.GetHex()); + ctx = FastRandomContext(seed); +} diff --git a/src/test/util/random.h b/src/test/util/random.h index 7997e8a346..c910bd6a3a 100644 --- a/src/test/util/random.h +++ b/src/test/util/random.h @@ -7,11 +7,41 @@ #include <consensus/amount.h> #include <random.h> -#include <test/util/setup_common.h> #include <uint256.h> #include <cstdint> +/** + * This global and the helpers that use it are not thread-safe. + * + * If thread-safety is needed, the global could be made thread_local (given + * that thread_local is supported on all architectures we support) or a + * per-thread instance could be used in the multi-threaded test. + */ +extern FastRandomContext g_insecure_rand_ctx; + +/** + * Flag to make GetRand in random.h return the same number + */ +extern bool g_mock_deterministic_tests; + +enum class SeedRand { + ZEROS, //!< Seed with a compile time constant of zeros + SEED, //!< Call the Seed() helper +}; + +/** Seed the given random ctx or use the seed passed in via an environment var */ +void Seed(FastRandomContext& ctx); + +static inline void SeedInsecureRand(SeedRand seed = SeedRand::SEED) +{ + if (seed == SeedRand::ZEROS) { + g_insecure_rand_ctx = FastRandomContext(/*fDeterministic=*/true); + } else { + Seed(g_insecure_rand_ctx); + } +} + static inline uint32_t InsecureRand32() { return g_insecure_rand_ctx.rand32(); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 93a60db832..65c657da96 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -27,11 +27,13 @@ #include <node/kernel_notifications.h> #include <node/mempool_args.h> #include <node/miner.h> +#include <node/peerman_args.h> #include <node/validation_cache_args.h> #include <noui.h> #include <policy/fees.h> #include <policy/fees_args.h> #include <pow.h> +#include <random.h> #include <rpc/blockchain.h> #include <rpc/register.h> #include <rpc/server.h> @@ -40,6 +42,7 @@ #include <shutdown.h> #include <streams.h> #include <test/util/net.h> +#include <test/util/random.h> #include <test/util/txmempool.h> #include <timedata.h> #include <txdb.h> @@ -73,29 +76,9 @@ using node::VerifyLoadedChainstate; const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; UrlDecodeFn* const URL_DECODE = nullptr; -FastRandomContext g_insecure_rand_ctx; /** Random context to get unique temp data dirs. Separate from g_insecure_rand_ctx, which can be seeded from a const env var */ static FastRandomContext g_insecure_rand_ctx_temp_path; -/** Return the unsigned from the environment var if available, otherwise 0 */ -static uint256 GetUintFromEnv(const std::string& env_name) -{ - const char* num = std::getenv(env_name.c_str()); - if (!num) return {}; - return uint256S(num); -} - -void Seed(FastRandomContext& ctx) -{ - // Should be enough to get the seed once for the process - static uint256 seed{}; - static const std::string RANDOM_CTX_SEED{"RANDOM_CTX_SEED"}; - if (seed.IsNull()) seed = GetUintFromEnv(RANDOM_CTX_SEED); - if (seed.IsNull()) seed = GetRandHash(); - LogPrintf("%s: Setting random seed for current tests to %s=%s\n", __func__, RANDOM_CTX_SEED, seed.GetHex()); - ctx = FastRandomContext(seed); -} - std::ostream& operator<<(std::ostream& os, const uint256& num) { os << num.ToString(); @@ -157,7 +140,6 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto noui_connect(); noui_connected = true; } - node::g_indexes_ready_to_sync = true; } BasicTestingSetup::~BasicTestingSetup() @@ -184,7 +166,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto m_cache_sizes = CalculateCacheSizes(m_args); - m_node.notifications = std::make_unique<KernelNotifications>(); + m_node.notifications = std::make_unique<KernelNotifications>(m_node.exit_status); const ChainstateManager::Options chainman_opts{ .chainparams = chainparams, @@ -196,8 +178,9 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto const BlockManager::Options blockman_opts{ .chainparams = chainman_opts.chainparams, .blocks_dir = m_args.GetBlocksDirPath(), + .notifications = chainman_opts.notifications, }; - m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts); + m_node.chainman = std::make_unique<ChainstateManager>(m_node.kernel->interrupt, chainman_opts, blockman_opts); m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(DBParams{ .path = m_args.GetDataDirNet() / "blocks" / "index", .cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db), @@ -269,9 +252,12 @@ TestingSetup::TestingSetup( m_node.args->GetIntArg("-checkaddrman", 0)); m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME); m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); // Deterministic randomness for tests. + PeerManager::Options peerman_opts; + ApplyArgsManOptions(*m_node.args, peerman_opts); m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman, m_node.banman.get(), *m_node.chainman, - *m_node.mempool, false); + *m_node.mempool, peerman_opts); + { CConnman::Options options; options.m_msgproc = m_node.peerman.get(); diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index b7429df02c..7cd4fdb417 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -11,7 +11,6 @@ #include <node/context.h> // IWYU pragma: export #include <primitives/transaction.h> #include <pubkey.h> -#include <random.h> #include <stdexcept> #include <util/chaintype.h> #include <util/check.h> @@ -25,6 +24,7 @@ class CFeeRate; class Chainstate; +class FastRandomContext; /** This is connected to the logger. Can be used to redirect logs to any other log */ extern const std::function<void(const std::string&)> G_TEST_LOG_FUN; @@ -41,37 +41,6 @@ std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::os } } // namespace std -/** - * This global and the helpers that use it are not thread-safe. - * - * If thread-safety is needed, the global could be made thread_local (given - * that thread_local is supported on all architectures we support) or a - * per-thread instance could be used in the multi-threaded test. - */ -extern FastRandomContext g_insecure_rand_ctx; - -/** - * Flag to make GetRand in random.h return the same number - */ -extern bool g_mock_deterministic_tests; - -enum class SeedRand { - ZEROS, //!< Seed with a compile time constant of zeros - SEED, //!< Call the Seed() helper -}; - -/** Seed the given random ctx or use the seed passed in via an environment var */ -void Seed(FastRandomContext& ctx); - -static inline void SeedInsecureRand(SeedRand seed = SeedRand::SEED) -{ - if (seed == SeedRand::ZEROS) { - g_insecure_rand_ctx = FastRandomContext(/*fDeterministic=*/true); - } else { - Seed(g_insecure_rand_ctx); - } -} - static constexpr CAmount CENT{1000000}; /** Basic testing setup. diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index b797de46af..99860961a2 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -10,6 +10,7 @@ #include <rpc/blockchain.h> #include <sync.h> #include <test/util/chainstate.h> +#include <test/util/logging.h> #include <test/util/random.h> #include <test/util/setup_common.h> #include <timedata.h> @@ -379,7 +380,7 @@ struct SnapshotTestSetup : TestChain100Setup { LOCK(::cs_main); chainman.ResetChainstates(); BOOST_CHECK_EQUAL(chainman.GetAll().size(), 0); - m_node.notifications = std::make_unique<KernelNotifications>(); + m_node.notifications = std::make_unique<KernelNotifications>(m_node.exit_status); const ChainstateManager::Options chainman_opts{ .chainparams = ::Params(), .datadir = chainman.m_options.datadir, @@ -389,11 +390,12 @@ struct SnapshotTestSetup : TestChain100Setup { const BlockManager::Options blockman_opts{ .chainparams = chainman_opts.chainparams, .blocks_dir = m_args.GetBlocksDirPath(), + .notifications = chainman_opts.notifications, }; // For robustness, ensure the old manager is destroyed before creating a // new one. m_node.chainman.reset(); - m_node.chainman = std::make_unique<ChainstateManager>(chainman_opts, blockman_opts); + m_node.chainman = std::make_unique<ChainstateManager>(m_node.kernel->interrupt, chainman_opts, blockman_opts); } return *Assert(m_node.chainman); } @@ -563,7 +565,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup auto db_cache_before_complete = active_cs.m_coinsdb_cache_size_bytes; SnapshotCompletionResult res; - auto mock_shutdown = [](bilingual_str msg) {}; + m_node.notifications->m_shutdown_on_fatal_error = false; fs::path snapshot_chainstate_dir = *node::FindSnapshotChainstateDir(chainman.m_options.datadir); BOOST_CHECK(fs::exists(snapshot_chainstate_dir)); @@ -573,8 +575,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()->GetBlockHash()); - res = WITH_LOCK(::cs_main, - return chainman.MaybeCompleteSnapshotValidation(mock_shutdown)); + res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation()); BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SUCCESS); WITH_LOCK(::cs_main, BOOST_CHECK(chainman.IsSnapshotValidated())); @@ -590,8 +591,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup BOOST_CHECK_EQUAL(all_chainstates[0], &active_cs); // Trying completion again should return false. - res = WITH_LOCK(::cs_main, - return chainman.MaybeCompleteSnapshotValidation(mock_shutdown)); + res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation()); BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SKIPPED); // The invalid snapshot path should not have been used. @@ -644,7 +644,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna Chainstate& validation_chainstate = *std::get<0>(chainstates); ChainstateManager& chainman = *Assert(m_node.chainman); SnapshotCompletionResult res; - auto mock_shutdown = [](bilingual_str msg) {}; + m_node.notifications->m_shutdown_on_fatal_error = false; // Test tampering with the IBD UTXO set with an extra coin to ensure it causes // snapshot completion to fail. @@ -660,9 +660,11 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna fs::path snapshot_chainstate_dir = gArgs.GetDataDirNet() / "chainstate_snapshot"; BOOST_CHECK(fs::exists(snapshot_chainstate_dir)); - res = WITH_LOCK(::cs_main, - return chainman.MaybeCompleteSnapshotValidation(mock_shutdown)); - BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::HASH_MISMATCH); + { + ASSERT_DEBUG_LOG("failed to validate the -assumeutxo snapshot state"); + res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation()); + BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::HASH_MISMATCH); + } auto all_chainstates = chainman.GetAll(); BOOST_CHECK_EQUAL(all_chainstates.size(), 1); diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 98d68f93e9..a8d6fb4b3f 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -16,7 +16,6 @@ #include <netbase.h> #include <util/readwritefile.h> #include <util/strencodings.h> -#include <util/syscall_sandbox.h> #include <util/thread.h> #include <util/time.h> @@ -653,7 +652,6 @@ static std::thread torControlThread; static void TorControlThread(CService onion_service_target) { - SetSyscallSandboxPolicy(SyscallSandboxPolicy::TOR_CONTROL); TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL), onion_service_target); event_base_dispatch(gBase); diff --git a/src/txdb.cpp b/src/txdb.cpp index b2095bd4a3..e5b5e0b8a1 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -9,8 +9,8 @@ #include <logging.h> #include <pow.h> #include <random.h> -#include <shutdown.h> #include <uint256.h> +#include <util/signalinterrupt.h> #include <util/translation.h> #include <util/vector.h> @@ -291,7 +291,7 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { return true; } -bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex) +bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt) { AssertLockHeld(::cs_main); std::unique_ptr<CDBIterator> pcursor(NewIterator()); @@ -299,7 +299,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, // Load m_block_index while (pcursor->Valid()) { - if (ShutdownRequested()) return false; + if (interrupt) return false; std::pair<uint8_t, uint256> key; if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) { CDiskBlockIndex diskindex; diff --git a/src/txdb.h b/src/txdb.h index 04d0ecb39f..6405437be9 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -29,6 +29,9 @@ class uint256; namespace Consensus { struct Params; }; +namespace util { +class SignalInterrupt; +} // namespace util //! -dbcache default (MiB) static const int64_t nDefaultDbCache = 450; @@ -98,7 +101,7 @@ public: void ReadReindexing(bool &fReindexing); bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); - bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex) + bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); }; diff --git a/src/txrequest.cpp b/src/txrequest.cpp index dd042103bd..4c94d4562d 100644 --- a/src/txrequest.cpp +++ b/src/txrequest.cpp @@ -124,7 +124,7 @@ public: Priority operator()(const uint256& txhash, NodeId peer, bool preferred) const { - uint64_t low_bits = CSipHasher(m_k0, m_k1).Write(txhash.begin(), txhash.size()).Write(peer).Finalize() >> 1; + uint64_t low_bits = CSipHasher(m_k0, m_k1).Write(txhash).Write(peer).Finalize() >> 1; return low_bits | uint64_t{preferred} << 63; } diff --git a/src/uint256.h b/src/uint256.h index 1cc3721487..d35b3a66fa 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -22,6 +22,7 @@ class base_blob { protected: static constexpr int WIDTH = BITS / 8; + static_assert(BITS % 8 == 0, "base_blob currently only supports whole bytes."); std::array<uint8_t, WIDTH> m_data; static_assert(WIDTH == sizeof(m_data), "Sanity check"); @@ -77,7 +78,7 @@ public: template<typename Stream> void Serialize(Stream& s) const { - s.write(MakeByteSpan(m_data)); + s << Span(m_data); } template<typename Stream> diff --git a/src/util/bytevectorhash.cpp b/src/util/bytevectorhash.cpp index 29be138eeb..92f1dbd5d8 100644 --- a/src/util/bytevectorhash.cpp +++ b/src/util/bytevectorhash.cpp @@ -16,5 +16,5 @@ ByteVectorHash::ByteVectorHash() : size_t ByteVectorHash::operator()(const std::vector<unsigned char>& input) const { - return CSipHasher(m_k0, m_k1).Write(input.data(), input.size()).Finalize(); + return CSipHasher(m_k0, m_k1).Write(input).Finalize(); } diff --git a/src/util/fs.cpp b/src/util/fs.cpp index e8fb72670f..14f7a44661 100644 --- a/src/util/fs.cpp +++ b/src/util/fs.cpp @@ -81,12 +81,7 @@ bool FileLock::TryLock() #else static std::string GetErrorReason() { - wchar_t* err; - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<WCHAR*>(&err), 0, nullptr); - std::wstring err_str(err); - LocalFree(err); - return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(err_str); + return Win32ErrorString(GetLastError()); } FileLock::FileLock(const fs::path& file) diff --git a/src/util/fs_helpers.cpp b/src/util/fs_helpers.cpp index d05cb8a63d..2a9eb3502e 100644 --- a/src/util/fs_helpers.cpp +++ b/src/util/fs_helpers.cpp @@ -14,6 +14,7 @@ #include <tinyformat.h> #include <util/fs.h> #include <util/getuniquepath.h> +#include <util/syserror.h> #include <cerrno> #include <filesystem> @@ -120,28 +121,28 @@ std::streampos GetFileSize(const char* path, std::streamsize max) bool FileCommit(FILE* file) { if (fflush(file) != 0) { // harmless if redundantly called - LogPrintf("%s: fflush failed: %d\n", __func__, errno); + LogPrintf("fflush failed: %s\n", SysErrorString(errno)); return false; } #ifdef WIN32 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); if (FlushFileBuffers(hFile) == 0) { - LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError()); + LogPrintf("FlushFileBuffers failed: %s\n", Win32ErrorString(GetLastError())); return false; } #elif defined(MAC_OSX) && defined(F_FULLFSYNC) if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success - LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno); + LogPrintf("fcntl F_FULLFSYNC failed: %s\n", SysErrorString(errno)); return false; } #elif HAVE_FDATASYNC if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync - LogPrintf("%s: fdatasync failed: %d\n", __func__, errno); + LogPrintf("fdatasync failed: %s\n", SysErrorString(errno)); return false; } #else if (fsync(fileno(file)) != 0 && errno != EINVAL) { - LogPrintf("%s: fsync failed: %d\n", __func__, errno); + LogPrintf("fsync failed: %s\n", SysErrorString(errno)); return false; } #endif diff --git a/src/util/hasher.cpp b/src/util/hasher.cpp index 81e9b990e1..f571725786 100644 --- a/src/util/hasher.cpp +++ b/src/util/hasher.cpp @@ -18,5 +18,5 @@ SaltedSipHasher::SaltedSipHasher() : m_k0(GetRand<uint64_t>()), m_k1(GetRand<uin size_t SaltedSipHasher::operator()(const Span<const unsigned char>& script) const { - return CSipHasher(m_k0, m_k1).Write(script.data(), script.size()).Finalize(); + return CSipHasher(m_k0, m_k1).Write(script).Finalize(); } diff --git a/src/util/signalinterrupt.cpp b/src/util/signalinterrupt.cpp new file mode 100644 index 0000000000..c551ba8044 --- /dev/null +++ b/src/util/signalinterrupt.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <util/signalinterrupt.h> + +#ifdef WIN32 +#include <mutex> +#else +#include <util/tokenpipe.h> +#endif + +#include <ios> +#include <optional> + +namespace util { + +SignalInterrupt::SignalInterrupt() : m_flag{false} +{ +#ifndef WIN32 + std::optional<TokenPipe> pipe = TokenPipe::Make(); + if (!pipe) throw std::ios_base::failure("Could not create TokenPipe"); + m_pipe_r = pipe->TakeReadEnd(); + m_pipe_w = pipe->TakeWriteEnd(); +#endif +} + +SignalInterrupt::operator bool() const +{ + return m_flag; +} + +void SignalInterrupt::reset() +{ + // Cancel existing interrupt by waiting for it, this will reset condition flags and remove + // the token from the pipe. + if (*this) wait(); + m_flag = false; +} + +void SignalInterrupt::operator()() +{ +#ifdef WIN32 + std::unique_lock<std::mutex> lk(m_mutex); + m_flag = true; + m_cv.notify_one(); +#else + // This must be reentrant and safe for calling in a signal handler, so using a condition variable is not safe. + // Make sure that the token is only written once even if multiple threads call this concurrently or in + // case of a reentrant signal. + if (!m_flag.exchange(true)) { + // Write an arbitrary byte to the write end of the pipe. + int res = m_pipe_w.TokenWrite('x'); + if (res != 0) { + throw std::ios_base::failure("Could not write interrupt token"); + } + } +#endif +} + +void SignalInterrupt::wait() +{ +#ifdef WIN32 + std::unique_lock<std::mutex> lk(m_mutex); + m_cv.wait(lk, [this] { return m_flag.load(); }); +#else + int res = m_pipe_r.TokenRead(); + if (res != 'x') { + throw std::ios_base::failure("Did not read expected interrupt token"); + } +#endif +} + +} // namespace util diff --git a/src/util/signalinterrupt.h b/src/util/signalinterrupt.h new file mode 100644 index 0000000000..ca02feda91 --- /dev/null +++ b/src/util/signalinterrupt.h @@ -0,0 +1,52 @@ +// Copyright (c) 2023 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_SIGNALINTERRUPT_H +#define BITCOIN_UTIL_SIGNALINTERRUPT_H + +#ifdef WIN32 +#include <condition_variable> +#include <mutex> +#else +#include <util/tokenpipe.h> +#endif + +#include <atomic> +#include <cstdlib> + +namespace util { +/** + * Helper class that manages an interrupt flag, and allows a thread or + * signal to interrupt another thread. + * + * This class is safe to be used in a signal handler. If sending an interrupt + * from a signal handler is not necessary, the more lightweight \ref + * CThreadInterrupt class can be used instead. + */ + +class SignalInterrupt +{ +public: + SignalInterrupt(); + explicit operator bool() const; + void operator()(); + void reset(); + void wait(); + +private: + std::atomic<bool> m_flag; + +#ifndef WIN32 + // On UNIX-like operating systems use the self-pipe trick. + TokenPipeEnd m_pipe_r; + TokenPipeEnd m_pipe_w; +#else + // On windows use a condition variable, since we don't have any signals there + std::mutex m_mutex; + std::condition_variable m_cv; +#endif +}; +} // namespace util + +#endif // BITCOIN_UTIL_SIGNALINTERRUPT_H diff --git a/src/util/sock.cpp b/src/util/sock.cpp index c83869bc77..fd64cae404 100644 --- a/src/util/sock.cpp +++ b/src/util/sock.cpp @@ -15,11 +15,6 @@ #include <stdexcept> #include <string> -#ifdef WIN32 -#include <codecvt> -#include <locale> -#endif - #ifdef USE_POLL #include <poll.h> #endif @@ -416,26 +411,12 @@ void Sock::Close() m_socket = INVALID_SOCKET; } -#ifdef WIN32 std::string NetworkErrorString(int err) { - wchar_t buf[256]; - buf[0] = 0; - if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, - nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - buf, ARRAYSIZE(buf), nullptr)) - { - return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err); - } - else - { - return strprintf("Unknown error (%d)", err); - } -} +#if defined(WIN32) + return Win32ErrorString(err); #else -std::string NetworkErrorString(int err) -{ // On BSD sockets implementations, NetworkErrorString is the same as SysErrorString. return SysErrorString(err); -} #endif +} diff --git a/src/util/syscall_sandbox.cpp b/src/util/syscall_sandbox.cpp deleted file mode 100644 index b1579bdb9c..0000000000 --- a/src/util/syscall_sandbox.cpp +++ /dev/null @@ -1,927 +0,0 @@ -// Copyright (c) 2020-2022 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#if defined(HAVE_CONFIG_H) -#include <config/bitcoin-config.h> -#endif // defined(HAVE_CONFIG_H) - -#include <util/syscall_sandbox.h> - -#if defined(USE_SYSCALL_SANDBOX) -#include <array> -#include <cassert> -#include <cstdint> -#include <exception> -#include <map> -#include <new> -#include <set> -#include <string> -#include <vector> - -#include <logging.h> -#include <tinyformat.h> -#include <util/threadnames.h> - -#include <linux/audit.h> -#include <linux/filter.h> -#include <linux/seccomp.h> -#include <linux/unistd.h> -#include <signal.h> -#include <sys/prctl.h> -#include <sys/types.h> -#include <unistd.h> - -namespace { -bool g_syscall_sandbox_enabled{false}; -bool g_syscall_sandbox_log_violation_before_terminating{false}; - -#if !defined(__x86_64__) -#error Syscall sandbox is an experimental feature currently available only under Linux x86-64. -#endif // defined(__x86_64__) - -#ifndef SECCOMP_RET_KILL_PROCESS -#define SECCOMP_RET_KILL_PROCESS 0x80000000U -#endif - -// Define system call numbers for x86_64 that are referenced in the system call profile -// but not provided by the kernel headers used in the GUIX build. -// Usually, they can be found via "grep name /usr/include/x86_64-linux-gnu/asm/unistd_64.h" - -#ifndef __NR_clone3 -#define __NR_clone3 435 -#endif - -#ifndef __NR_statx -#define __NR_statx 332 -#endif - -#ifndef __NR_getrandom -#define __NR_getrandom 318 -#endif - -#ifndef __NR_membarrier -#define __NR_membarrier 324 -#endif - -#ifndef __NR_copy_file_range -#define __NR_copy_file_range 326 -#endif - -#ifndef __NR_rseq -#define __NR_rseq 334 -#endif - -// This list of syscalls in LINUX_SYSCALLS is only used to map syscall numbers to syscall names in -// order to be able to print user friendly error messages which include the syscall name in addition -// to the syscall number. -// -// Example output in case of a syscall violation where the syscall is present in LINUX_SYSCALLS: -// -// ``` -// 2021-06-09T12:34:56Z ERROR: The syscall "execve" (syscall number 59) is not allowed by the syscall sandbox in thread "msghand". Please report. -// ``` -// -// Example output in case of a syscall violation where the syscall is not present in LINUX_SYSCALLS: -// -// ``` -// 2021-06-09T12:34:56Z ERROR: The syscall "*unknown*" (syscall number 314) is not allowed by the syscall sandbox in thread "msghand". Please report. -// `` -// -// LINUX_SYSCALLS contains two types of syscalls: -// 1.) Syscalls that are present under all architectures or relevant Linux kernel versions for which -// we support the syscall sandbox feature (currently only Linux x86-64). Examples include read, -// write, open, close, etc. -// 2.) Syscalls that are present under a subset of architectures or relevant Linux kernel versions -// for which we support the syscall sandbox feature. This type of syscalls should be added to -// LINUX_SYSCALLS conditional on availability like in the following example: -// ... -// #if defined(__NR_arch_dependent_syscall) -// {__NR_arch_dependent_syscall, "arch_dependent_syscall"}, -// #endif // defined(__NR_arch_dependent_syscall) -// ... -const std::map<uint32_t, std::string> LINUX_SYSCALLS{ - {__NR_accept, "accept"}, - {__NR_accept4, "accept4"}, - {__NR_access, "access"}, - {__NR_acct, "acct"}, - {__NR_add_key, "add_key"}, - {__NR_adjtimex, "adjtimex"}, - {__NR_afs_syscall, "afs_syscall"}, - {__NR_alarm, "alarm"}, - {__NR_arch_prctl, "arch_prctl"}, - {__NR_bind, "bind"}, - {__NR_bpf, "bpf"}, - {__NR_brk, "brk"}, - {__NR_capget, "capget"}, - {__NR_capset, "capset"}, - {__NR_chdir, "chdir"}, - {__NR_chmod, "chmod"}, - {__NR_chown, "chown"}, - {__NR_chroot, "chroot"}, - {__NR_clock_adjtime, "clock_adjtime"}, - {__NR_clock_getres, "clock_getres"}, - {__NR_clock_gettime, "clock_gettime"}, - {__NR_clock_nanosleep, "clock_nanosleep"}, - {__NR_clock_settime, "clock_settime"}, - {__NR_clone, "clone"}, - {__NR_clone3, "clone3"}, - {__NR_close, "close"}, - {__NR_connect, "connect"}, - {__NR_copy_file_range, "copy_file_range"}, - {__NR_creat, "creat"}, - {__NR_create_module, "create_module"}, - {__NR_delete_module, "delete_module"}, - {__NR_dup, "dup"}, - {__NR_dup2, "dup2"}, - {__NR_dup3, "dup3"}, - {__NR_epoll_create, "epoll_create"}, - {__NR_epoll_create1, "epoll_create1"}, - {__NR_epoll_ctl, "epoll_ctl"}, - {__NR_epoll_ctl_old, "epoll_ctl_old"}, - {__NR_epoll_pwait, "epoll_pwait"}, - {__NR_epoll_wait, "epoll_wait"}, - {__NR_epoll_wait_old, "epoll_wait_old"}, - {__NR_eventfd, "eventfd"}, - {__NR_eventfd2, "eventfd2"}, - {__NR_execve, "execve"}, - {__NR_execveat, "execveat"}, - {__NR_exit, "exit"}, - {__NR_exit_group, "exit_group"}, - {__NR_faccessat, "faccessat"}, - {__NR_fadvise64, "fadvise64"}, - {__NR_fallocate, "fallocate"}, - {__NR_fanotify_init, "fanotify_init"}, - {__NR_fanotify_mark, "fanotify_mark"}, - {__NR_fchdir, "fchdir"}, - {__NR_fchmod, "fchmod"}, - {__NR_fchmodat, "fchmodat"}, - {__NR_fchown, "fchown"}, - {__NR_fchownat, "fchownat"}, - {__NR_fcntl, "fcntl"}, - {__NR_fdatasync, "fdatasync"}, - {__NR_fgetxattr, "fgetxattr"}, - {__NR_finit_module, "finit_module"}, - {__NR_flistxattr, "flistxattr"}, - {__NR_flock, "flock"}, - {__NR_fork, "fork"}, - {__NR_fremovexattr, "fremovexattr"}, - {__NR_fsetxattr, "fsetxattr"}, - {__NR_fstat, "fstat"}, - {__NR_fstatfs, "fstatfs"}, - {__NR_fsync, "fsync"}, - {__NR_ftruncate, "ftruncate"}, - {__NR_futex, "futex"}, - {__NR_futimesat, "futimesat"}, - {__NR_get_kernel_syms, "get_kernel_syms"}, - {__NR_get_mempolicy, "get_mempolicy"}, - {__NR_get_robust_list, "get_robust_list"}, - {__NR_get_thread_area, "get_thread_area"}, - {__NR_getcpu, "getcpu"}, - {__NR_getcwd, "getcwd"}, - {__NR_getdents, "getdents"}, - {__NR_getdents64, "getdents64"}, - {__NR_getegid, "getegid"}, - {__NR_geteuid, "geteuid"}, - {__NR_getgid, "getgid"}, - {__NR_getgroups, "getgroups"}, - {__NR_getitimer, "getitimer"}, - {__NR_getpeername, "getpeername"}, - {__NR_getpgid, "getpgid"}, - {__NR_getpgrp, "getpgrp"}, - {__NR_getpid, "getpid"}, - {__NR_getpmsg, "getpmsg"}, - {__NR_getppid, "getppid"}, - {__NR_getpriority, "getpriority"}, - {__NR_getrandom, "getrandom"}, - {__NR_getresgid, "getresgid"}, - {__NR_getresuid, "getresuid"}, - {__NR_getrlimit, "getrlimit"}, - {__NR_getrusage, "getrusage"}, - {__NR_getsid, "getsid"}, - {__NR_getsockname, "getsockname"}, - {__NR_getsockopt, "getsockopt"}, - {__NR_gettid, "gettid"}, - {__NR_gettimeofday, "gettimeofday"}, - {__NR_getuid, "getuid"}, - {__NR_getxattr, "getxattr"}, - {__NR_init_module, "init_module"}, - {__NR_inotify_add_watch, "inotify_add_watch"}, - {__NR_inotify_init, "inotify_init"}, - {__NR_inotify_init1, "inotify_init1"}, - {__NR_inotify_rm_watch, "inotify_rm_watch"}, - {__NR_io_cancel, "io_cancel"}, - {__NR_io_destroy, "io_destroy"}, - {__NR_io_getevents, "io_getevents"}, - {__NR_io_setup, "io_setup"}, - {__NR_io_submit, "io_submit"}, - {__NR_ioctl, "ioctl"}, - {__NR_ioperm, "ioperm"}, - {__NR_iopl, "iopl"}, - {__NR_ioprio_get, "ioprio_get"}, - {__NR_ioprio_set, "ioprio_set"}, - {__NR_kcmp, "kcmp"}, - {__NR_kexec_file_load, "kexec_file_load"}, - {__NR_kexec_load, "kexec_load"}, - {__NR_keyctl, "keyctl"}, - {__NR_kill, "kill"}, - {__NR_lchown, "lchown"}, - {__NR_lgetxattr, "lgetxattr"}, - {__NR_link, "link"}, - {__NR_linkat, "linkat"}, - {__NR_listen, "listen"}, - {__NR_listxattr, "listxattr"}, - {__NR_llistxattr, "llistxattr"}, - {__NR_lookup_dcookie, "lookup_dcookie"}, - {__NR_lremovexattr, "lremovexattr"}, - {__NR_lseek, "lseek"}, - {__NR_lsetxattr, "lsetxattr"}, - {__NR_lstat, "lstat"}, - {__NR_madvise, "madvise"}, - {__NR_mbind, "mbind"}, - {__NR_membarrier, "membarrier"}, - {__NR_memfd_create, "memfd_create"}, - {__NR_migrate_pages, "migrate_pages"}, - {__NR_mincore, "mincore"}, - {__NR_mkdir, "mkdir"}, - {__NR_mkdirat, "mkdirat"}, - {__NR_mknod, "mknod"}, - {__NR_mknodat, "mknodat"}, - {__NR_mlock, "mlock"}, - {__NR_mlock2, "mlock2"}, - {__NR_mlockall, "mlockall"}, - {__NR_mmap, "mmap"}, - {__NR_modify_ldt, "modify_ldt"}, - {__NR_mount, "mount"}, - {__NR_move_pages, "move_pages"}, - {__NR_mprotect, "mprotect"}, - {__NR_mq_getsetattr, "mq_getsetattr"}, - {__NR_mq_notify, "mq_notify"}, - {__NR_mq_open, "mq_open"}, - {__NR_mq_timedreceive, "mq_timedreceive"}, - {__NR_mq_timedsend, "mq_timedsend"}, - {__NR_mq_unlink, "mq_unlink"}, - {__NR_mremap, "mremap"}, - {__NR_msgctl, "msgctl"}, - {__NR_msgget, "msgget"}, - {__NR_msgrcv, "msgrcv"}, - {__NR_msgsnd, "msgsnd"}, - {__NR_msync, "msync"}, - {__NR_munlock, "munlock"}, - {__NR_munlockall, "munlockall"}, - {__NR_munmap, "munmap"}, - {__NR_name_to_handle_at, "name_to_handle_at"}, - {__NR_nanosleep, "nanosleep"}, - {__NR_newfstatat, "newfstatat"}, - {__NR_nfsservctl, "nfsservctl"}, - {__NR_open, "open"}, - {__NR_open_by_handle_at, "open_by_handle_at"}, - {__NR_openat, "openat"}, - {__NR_pause, "pause"}, - {__NR_perf_event_open, "perf_event_open"}, - {__NR_personality, "personality"}, - {__NR_pipe, "pipe"}, - {__NR_pipe2, "pipe2"}, - {__NR_pivot_root, "pivot_root"}, -#ifdef __NR_pkey_alloc - {__NR_pkey_alloc, "pkey_alloc"}, -#endif -#ifdef __NR_pkey_free - {__NR_pkey_free, "pkey_free"}, -#endif -#ifdef __NR_pkey_mprotect - {__NR_pkey_mprotect, "pkey_mprotect"}, -#endif - {__NR_poll, "poll"}, - {__NR_ppoll, "ppoll"}, - {__NR_prctl, "prctl"}, - {__NR_pread64, "pread64"}, - {__NR_preadv, "preadv"}, -#ifdef __NR_preadv2 - {__NR_preadv2, "preadv2"}, -#endif - {__NR_prlimit64, "prlimit64"}, - {__NR_process_vm_readv, "process_vm_readv"}, - {__NR_process_vm_writev, "process_vm_writev"}, - {__NR_pselect6, "pselect6"}, - {__NR_ptrace, "ptrace"}, - {__NR_putpmsg, "putpmsg"}, - {__NR_pwrite64, "pwrite64"}, - {__NR_pwritev, "pwritev"}, -#ifdef __NR_pwritev2 - {__NR_pwritev2, "pwritev2"}, -#endif - {__NR__sysctl, "_sysctl"}, - {__NR_query_module, "query_module"}, - {__NR_quotactl, "quotactl"}, - {__NR_read, "read"}, - {__NR_readahead, "readahead"}, - {__NR_readlink, "readlink"}, - {__NR_readlinkat, "readlinkat"}, - {__NR_readv, "readv"}, - {__NR_reboot, "reboot"}, - {__NR_recvfrom, "recvfrom"}, - {__NR_recvmmsg, "recvmmsg"}, - {__NR_recvmsg, "recvmsg"}, - {__NR_remap_file_pages, "remap_file_pages"}, - {__NR_removexattr, "removexattr"}, - {__NR_rename, "rename"}, - {__NR_renameat, "renameat"}, - {__NR_renameat2, "renameat2"}, - {__NR_request_key, "request_key"}, - {__NR_restart_syscall, "restart_syscall"}, - {__NR_rmdir, "rmdir"}, - {__NR_rseq, "rseq"}, - {__NR_rt_sigaction, "rt_sigaction"}, - {__NR_rt_sigpending, "rt_sigpending"}, - {__NR_rt_sigprocmask, "rt_sigprocmask"}, - {__NR_rt_sigqueueinfo, "rt_sigqueueinfo"}, - {__NR_rt_sigreturn, "rt_sigreturn"}, - {__NR_rt_sigsuspend, "rt_sigsuspend"}, - {__NR_rt_sigtimedwait, "rt_sigtimedwait"}, - {__NR_rt_tgsigqueueinfo, "rt_tgsigqueueinfo"}, - {__NR_sched_get_priority_max, "sched_get_priority_max"}, - {__NR_sched_get_priority_min, "sched_get_priority_min"}, - {__NR_sched_getaffinity, "sched_getaffinity"}, - {__NR_sched_getattr, "sched_getattr"}, - {__NR_sched_getparam, "sched_getparam"}, - {__NR_sched_getscheduler, "sched_getscheduler"}, - {__NR_sched_rr_get_interval, "sched_rr_get_interval"}, - {__NR_sched_setaffinity, "sched_setaffinity"}, - {__NR_sched_setattr, "sched_setattr"}, - {__NR_sched_setparam, "sched_setparam"}, - {__NR_sched_setscheduler, "sched_setscheduler"}, - {__NR_sched_yield, "sched_yield"}, - {__NR_seccomp, "seccomp"}, - {__NR_security, "security"}, - {__NR_select, "select"}, - {__NR_semctl, "semctl"}, - {__NR_semget, "semget"}, - {__NR_semop, "semop"}, - {__NR_semtimedop, "semtimedop"}, - {__NR_sendfile, "sendfile"}, - {__NR_sendmmsg, "sendmmsg"}, - {__NR_sendmsg, "sendmsg"}, - {__NR_sendto, "sendto"}, - {__NR_set_mempolicy, "set_mempolicy"}, - {__NR_set_robust_list, "set_robust_list"}, - {__NR_set_thread_area, "set_thread_area"}, - {__NR_set_tid_address, "set_tid_address"}, - {__NR_setdomainname, "setdomainname"}, - {__NR_setfsgid, "setfsgid"}, - {__NR_setfsuid, "setfsuid"}, - {__NR_setgid, "setgid"}, - {__NR_setgroups, "setgroups"}, - {__NR_sethostname, "sethostname"}, - {__NR_setitimer, "setitimer"}, - {__NR_setns, "setns"}, - {__NR_setpgid, "setpgid"}, - {__NR_setpriority, "setpriority"}, - {__NR_setregid, "setregid"}, - {__NR_setresgid, "setresgid"}, - {__NR_setresuid, "setresuid"}, - {__NR_setreuid, "setreuid"}, - {__NR_setrlimit, "setrlimit"}, - {__NR_setsid, "setsid"}, - {__NR_setsockopt, "setsockopt"}, - {__NR_settimeofday, "settimeofday"}, - {__NR_setuid, "setuid"}, - {__NR_setxattr, "setxattr"}, - {__NR_shmat, "shmat"}, - {__NR_shmctl, "shmctl"}, - {__NR_shmdt, "shmdt"}, - {__NR_shmget, "shmget"}, - {__NR_shutdown, "shutdown"}, - {__NR_sigaltstack, "sigaltstack"}, - {__NR_signalfd, "signalfd"}, - {__NR_signalfd4, "signalfd4"}, - {__NR_socket, "socket"}, - {__NR_socketpair, "socketpair"}, - {__NR_splice, "splice"}, - {__NR_stat, "stat"}, - {__NR_statfs, "statfs"}, - {__NR_statx, "statx"}, - {__NR_swapoff, "swapoff"}, - {__NR_swapon, "swapon"}, - {__NR_symlink, "symlink"}, - {__NR_symlinkat, "symlinkat"}, - {__NR_sync, "sync"}, - {__NR_sync_file_range, "sync_file_range"}, - {__NR_syncfs, "syncfs"}, - {__NR_sysfs, "sysfs"}, - {__NR_sysinfo, "sysinfo"}, - {__NR_syslog, "syslog"}, - {__NR_tee, "tee"}, - {__NR_tgkill, "tgkill"}, - {__NR_time, "time"}, - {__NR_timer_create, "timer_create"}, - {__NR_timer_delete, "timer_delete"}, - {__NR_timer_getoverrun, "timer_getoverrun"}, - {__NR_timer_gettime, "timer_gettime"}, - {__NR_timer_settime, "timer_settime"}, - {__NR_timerfd_create, "timerfd_create"}, - {__NR_timerfd_gettime, "timerfd_gettime"}, - {__NR_timerfd_settime, "timerfd_settime"}, - {__NR_times, "times"}, - {__NR_tkill, "tkill"}, - {__NR_truncate, "truncate"}, - {__NR_tuxcall, "tuxcall"}, - {__NR_umask, "umask"}, - {__NR_umount2, "umount2"}, - {__NR_uname, "uname"}, - {__NR_unlink, "unlink"}, - {__NR_unlinkat, "unlinkat"}, - {__NR_unshare, "unshare"}, - {__NR_uselib, "uselib"}, - {__NR_userfaultfd, "userfaultfd"}, - {__NR_ustat, "ustat"}, - {__NR_utime, "utime"}, - {__NR_utimensat, "utimensat"}, - {__NR_utimes, "utimes"}, - {__NR_vfork, "vfork"}, - {__NR_vhangup, "vhangup"}, - {__NR_vmsplice, "vmsplice"}, - {__NR_vserver, "vserver"}, - {__NR_wait4, "wait4"}, - {__NR_waitid, "waitid"}, - {__NR_write, "write"}, - {__NR_writev, "writev"}, -}; - -std::string GetLinuxSyscallName(uint32_t syscall_number) -{ - const auto element = LINUX_SYSCALLS.find(syscall_number); - if (element != LINUX_SYSCALLS.end()) { - return element->second; - } - return "*unknown*"; -} - -// See Linux kernel developer Kees Cook's seccomp guide at <https://outflux.net/teach-seccomp/> for -// an accessible introduction to using seccomp. -// -// This function largely follows <https://outflux.net/teach-seccomp/step-3/syscall-reporter.c> and -// <https://outflux.net/teach-seccomp/step-3/seccomp-bpf.h>. -// -// Seccomp BPF resources: -// * Seccomp BPF documentation: <https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html> -// * seccomp(2) manual page: <https://www.kernel.org/doc/man-pages/online/pages/man2/seccomp.2.html> -// * Seccomp BPF demo code samples: <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/samples/seccomp> -void SyscallSandboxDebugSignalHandler(int, siginfo_t* signal_info, void* void_signal_context) -{ - // The si_code field inside the siginfo_t argument that is passed to a SA_SIGINFO signal handler - // is a value indicating why the signal was sent. - // - // The following value can be placed in si_code for a SIGSYS signal: - // * SYS_SECCOMP (since Linux 3.5): Triggered by a seccomp(2) filter rule. - constexpr int32_t SYS_SECCOMP_SI_CODE{1}; - assert(signal_info->si_code == SYS_SECCOMP_SI_CODE); - - // The ucontext_t structure contains signal context information that was saved on the user-space - // stack by the kernel. - const ucontext_t* signal_context = static_cast<ucontext_t*>(void_signal_context); - assert(signal_context != nullptr); - - std::set_new_handler(std::terminate); - // Portability note: REG_RAX is Linux x86_64 specific. - const uint32_t syscall_number = static_cast<uint32_t>(signal_context->uc_mcontext.gregs[REG_RAX]); - const std::string syscall_name = GetLinuxSyscallName(syscall_number); - const std::string thread_name = !util::ThreadGetInternalName().empty() ? util::ThreadGetInternalName() : "*unnamed*"; - const std::string error_message = strprintf("ERROR: The syscall \"%s\" (syscall number %d) is not allowed by the syscall sandbox in thread \"%s\". Please report.", syscall_name, syscall_number, thread_name); - tfm::format(std::cerr, "%s\n", error_message); - LogPrintf("%s\n", error_message); - std::terminate(); -} - -// This function largely follows install_syscall_reporter from Kees Cook's seccomp guide: -// <https://outflux.net/teach-seccomp/step-3/syscall-reporter.c> -bool SetupSyscallSandboxDebugHandler() -{ - struct sigaction action = {}; - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGSYS); - action.sa_sigaction = &SyscallSandboxDebugSignalHandler; - action.sa_flags = SA_SIGINFO; - if (sigaction(SIGSYS, &action, nullptr) < 0) { - return false; - } - if (sigprocmask(SIG_UNBLOCK, &mask, nullptr)) { - return false; - } - return true; -} - -enum class SyscallSandboxAction { - KILL_PROCESS, - INVOKE_SIGNAL_HANDLER, -}; - -class SeccompPolicyBuilder -{ - std::set<uint32_t> allowed_syscalls; - -public: - SeccompPolicyBuilder() - { - // Allowed by default. - AllowAddressSpaceAccess(); - AllowEpoll(); - AllowEventFd(); - AllowFutex(); - AllowGeneralIo(); - AllowGetRandom(); - AllowGetSimpleId(); - AllowGetTime(); - AllowGlobalProcessEnvironment(); - AllowGlobalSystemStatus(); - AllowKernelInternalApi(); - AllowNetworkSocketInformation(); - AllowOperationOnExistingFileDescriptor(); - AllowPipe(); - AllowPrctl(); - AllowProcessStartOrDeath(); - AllowScheduling(); - AllowSignalHandling(); - AllowSleep(); - AllowUmask(); - } - - void AllowAddressSpaceAccess() - { - allowed_syscalls.insert(__NR_brk); // change data segment size - allowed_syscalls.insert(__NR_madvise); // give advice about use of memory - allowed_syscalls.insert(__NR_membarrier); // issue memory barriers on a set of threads - allowed_syscalls.insert(__NR_mincore); // check if virtual memory is in RAM - allowed_syscalls.insert(__NR_mlock); // lock memory - allowed_syscalls.insert(__NR_mmap); // map files or devices into memory - allowed_syscalls.insert(__NR_mprotect); // set protection on a region of memory - allowed_syscalls.insert(__NR_mremap); // remap a file in memory - allowed_syscalls.insert(__NR_munlock); // unlock memory - allowed_syscalls.insert(__NR_munmap); // unmap files or devices into memory - } - - void AllowEpoll() - { - allowed_syscalls.insert(__NR_epoll_create1); // open an epoll file descriptor - allowed_syscalls.insert(__NR_epoll_ctl); // control interface for an epoll file descriptor - allowed_syscalls.insert(__NR_epoll_pwait); // wait for an I/O event on an epoll file descriptor - allowed_syscalls.insert(__NR_epoll_wait); // wait for an I/O event on an epoll file descriptor - } - - void AllowEventFd() - { - allowed_syscalls.insert(__NR_eventfd2); // create a file descriptor for event notification - } - - void AllowFileSystem() - { - allowed_syscalls.insert(__NR_access); // check user's permissions for a file - allowed_syscalls.insert(__NR_chdir); // change working directory - allowed_syscalls.insert(__NR_chmod); // change permissions of a file - allowed_syscalls.insert(__NR_copy_file_range); // copy a range of data from one file to another - allowed_syscalls.insert(__NR_fallocate); // manipulate file space - allowed_syscalls.insert(__NR_fchmod); // change permissions of a file - allowed_syscalls.insert(__NR_fchown); // change ownership of a file - allowed_syscalls.insert(__NR_fdatasync); // synchronize a file's in-core state with storage device - allowed_syscalls.insert(__NR_flock); // apply or remove an advisory lock on an open file - allowed_syscalls.insert(__NR_fstat); // get file status - allowed_syscalls.insert(__NR_fstatfs); // get file system status - allowed_syscalls.insert(__NR_fsync); // synchronize a file's in-core state with storage device - allowed_syscalls.insert(__NR_ftruncate); // truncate a file to a specified length - allowed_syscalls.insert(__NR_getcwd); // get current working directory - allowed_syscalls.insert(__NR_getdents); // get directory entries - allowed_syscalls.insert(__NR_getdents64); // get directory entries - allowed_syscalls.insert(__NR_lstat); // get file status - allowed_syscalls.insert(__NR_mkdir); // create a directory - allowed_syscalls.insert(__NR_newfstatat); // get file status - allowed_syscalls.insert(__NR_open); // open and possibly create a file - allowed_syscalls.insert(__NR_openat); // open and possibly create a file - allowed_syscalls.insert(__NR_readlink); // read value of a symbolic link - allowed_syscalls.insert(__NR_rename); // change the name or location of a file - allowed_syscalls.insert(__NR_rmdir); // delete a directory - allowed_syscalls.insert(__NR_sendfile); // transfer data between file descriptors - allowed_syscalls.insert(__NR_stat); // get file status - allowed_syscalls.insert(__NR_statfs); // get filesystem statistics - allowed_syscalls.insert(__NR_statx); // get file status (extended) - allowed_syscalls.insert(__NR_unlink); // delete a name and possibly the file it refers to - allowed_syscalls.insert(__NR_unlinkat); // delete relative to a directory file descriptor - } - - void AllowFutex() - { - allowed_syscalls.insert(__NR_futex); // fast user-space locking - allowed_syscalls.insert(__NR_set_robust_list); // set list of robust futexes - } - - void AllowGeneralIo() - { - allowed_syscalls.insert(__NR_ioctl); // control device - allowed_syscalls.insert(__NR_lseek); // reposition read/write file offset - allowed_syscalls.insert(__NR_poll); // wait for some event on a file descriptor - allowed_syscalls.insert(__NR_ppoll); // wait for some event on a file descriptor - allowed_syscalls.insert(__NR_pread64); // read from a file descriptor at a given offset - allowed_syscalls.insert(__NR_pwrite64); // write to a file descriptor at a given offset - allowed_syscalls.insert(__NR_read); // read from a file descriptor - allowed_syscalls.insert(__NR_readv); // read data into multiple buffers - allowed_syscalls.insert(__NR_recvfrom); // receive a message from a socket - allowed_syscalls.insert(__NR_recvmsg); // receive a message from a socket - allowed_syscalls.insert(__NR_select); // synchronous I/O multiplexing - allowed_syscalls.insert(__NR_sendmmsg); // send multiple messages on a socket - allowed_syscalls.insert(__NR_sendmsg); // send a message on a socket - allowed_syscalls.insert(__NR_sendto); // send a message on a socket - allowed_syscalls.insert(__NR_write); // write to a file descriptor - allowed_syscalls.insert(__NR_writev); // write data into multiple buffers - } - - void AllowGetRandom() - { - allowed_syscalls.insert(__NR_getrandom); // obtain a series of random bytes - } - - void AllowGetSimpleId() - { - allowed_syscalls.insert(__NR_getegid); // get group identity - allowed_syscalls.insert(__NR_geteuid); // get user identity - allowed_syscalls.insert(__NR_getgid); // get group identity - allowed_syscalls.insert(__NR_getpgid); // get process group - allowed_syscalls.insert(__NR_getpid); // get process identification - allowed_syscalls.insert(__NR_getppid); // get process identification - allowed_syscalls.insert(__NR_getresgid); // get real, effective and saved group IDs - allowed_syscalls.insert(__NR_getresuid); // get real, effective and saved user IDs - allowed_syscalls.insert(__NR_getsid); // get session ID - allowed_syscalls.insert(__NR_gettid); // get thread identification - allowed_syscalls.insert(__NR_getuid); // get user identity - } - - void AllowGetTime() - { - allowed_syscalls.insert(__NR_clock_getres); // find the resolution (precision) of the specified clock - allowed_syscalls.insert(__NR_clock_gettime); // retrieve the time of the specified clock - allowed_syscalls.insert(__NR_gettimeofday); // get timeval - } - - void AllowGlobalProcessEnvironment() - { - allowed_syscalls.insert(__NR_getrlimit); // get resource limits - allowed_syscalls.insert(__NR_getrusage); // get resource usage - allowed_syscalls.insert(__NR_prlimit64); // get/set resource limits - } - - void AllowGlobalSystemStatus() - { - allowed_syscalls.insert(__NR_sysinfo); // return system information - allowed_syscalls.insert(__NR_uname); // get name and information about current kernel - } - - void AllowKernelInternalApi() - { - allowed_syscalls.insert(__NR_restart_syscall); // restart a system call after interruption by a stop signal - } - - void AllowNetwork() - { - allowed_syscalls.insert(__NR_accept); // accept a connection on a socket - allowed_syscalls.insert(__NR_accept4); // accept a connection on a socket - allowed_syscalls.insert(__NR_bind); // bind a name to a socket - allowed_syscalls.insert(__NR_connect); // initiate a connection on a socket - allowed_syscalls.insert(__NR_listen); // listen for connections on a socket - allowed_syscalls.insert(__NR_setsockopt); // set options on sockets - allowed_syscalls.insert(__NR_socket); // create an endpoint for communication - allowed_syscalls.insert(__NR_socketpair); // create a pair of connected sockets - } - - void AllowNetworkSocketInformation() - { - allowed_syscalls.insert(__NR_getpeername); // get name of connected peer socket - allowed_syscalls.insert(__NR_getsockname); // get socket name - allowed_syscalls.insert(__NR_getsockopt); // get options on sockets - } - - void AllowOperationOnExistingFileDescriptor() - { - allowed_syscalls.insert(__NR_close); // close a file descriptor - allowed_syscalls.insert(__NR_dup); // duplicate a file descriptor - allowed_syscalls.insert(__NR_dup2); // duplicate a file descriptor - allowed_syscalls.insert(__NR_fcntl); // manipulate file descriptor - allowed_syscalls.insert(__NR_shutdown); // shut down part of a full-duplex connection - } - - void AllowPipe() - { - allowed_syscalls.insert(__NR_pipe); // create pipe - allowed_syscalls.insert(__NR_pipe2); // create pipe - } - - void AllowPrctl() - { - allowed_syscalls.insert(__NR_arch_prctl); // set architecture-specific thread state - allowed_syscalls.insert(__NR_prctl); // operations on a process - } - - void AllowProcessStartOrDeath() - { - allowed_syscalls.insert(__NR_clone); // create a child process - allowed_syscalls.insert(__NR_clone3); // create a child process - allowed_syscalls.insert(__NR_exit); // terminate the calling process - allowed_syscalls.insert(__NR_exit_group); // exit all threads in a process - allowed_syscalls.insert(__NR_fork); // create a child process - allowed_syscalls.insert(__NR_tgkill); // send a signal to a thread - allowed_syscalls.insert(__NR_wait4); // wait for process to change state, BSD style - allowed_syscalls.insert(__NR_rseq); // register restartable sequence for thread - } - - void AllowScheduling() - { - allowed_syscalls.insert(__NR_sched_getaffinity); // set a thread's CPU affinity mask - allowed_syscalls.insert(__NR_sched_getparam); // get scheduling parameters - allowed_syscalls.insert(__NR_sched_getscheduler); // get scheduling policy/parameters - allowed_syscalls.insert(__NR_sched_setscheduler); // set scheduling policy/parameters - allowed_syscalls.insert(__NR_sched_yield); // yield the processor - } - - void AllowSignalHandling() - { - allowed_syscalls.insert(__NR_rt_sigaction); // examine and change a signal action - allowed_syscalls.insert(__NR_rt_sigprocmask); // examine and change blocked signals - allowed_syscalls.insert(__NR_rt_sigreturn); // return from signal handler and cleanup stack frame - allowed_syscalls.insert(__NR_sigaltstack); // set and/or get signal stack context - } - - void AllowSleep() - { - allowed_syscalls.insert(__NR_clock_nanosleep); // high-resolution sleep with specifiable clock - allowed_syscalls.insert(__NR_nanosleep); // high-resolution sleep - } - - void AllowUmask() - { - allowed_syscalls.insert(__NR_umask); // set file mode creation mask - } - - // See Linux kernel developer Kees Cook's seccomp guide at <https://outflux.net/teach-seccomp/> - // for an accessible introduction to using seccomp. - // - // This function largely follows <https://outflux.net/teach-seccomp/step-3/seccomp-bpf.h>. - std::vector<sock_filter> BuildFilter(SyscallSandboxAction default_action) - { - std::vector<sock_filter> bpf_policy; - // See VALIDATE_ARCHITECTURE in seccomp-bpf.h referenced above. - bpf_policy.push_back(BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, arch))); - // Portability note: AUDIT_ARCH_X86_64 is Linux x86_64 specific. - bpf_policy.push_back(BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, AUDIT_ARCH_X86_64, 1, 0)); - bpf_policy.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS)); - // See EXAMINE_SYSCALL in seccomp-bpf.h referenced above. - bpf_policy.push_back(BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, nr))); - for (const uint32_t allowed_syscall : allowed_syscalls) { - // See ALLOW_SYSCALL in seccomp-bpf.h referenced above. - bpf_policy.push_back(BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, allowed_syscall, 0, 1)); - bpf_policy.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)); - } - switch (default_action) { - case SyscallSandboxAction::KILL_PROCESS: - // Disallow syscall and kill the process. - // - // See KILL_PROCESS in seccomp-bpf.h referenced above. - // - // Note that we're using SECCOMP_RET_KILL_PROCESS (kill the process) instead - // of SECCOMP_RET_KILL_THREAD (kill the thread). The SECCOMP_RET_KILL_PROCESS - // action was introduced in Linux 4.14. - // - // SECCOMP_RET_KILL_PROCESS: Results in the entire process exiting immediately without - // executing the system call. - // - // SECCOMP_RET_KILL_PROCESS documentation: - // <https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html> - bpf_policy.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS)); - break; - case SyscallSandboxAction::INVOKE_SIGNAL_HANDLER: - // Disallow syscall and force a SIGSYS to trigger syscall debug reporter. - // - // SECCOMP_RET_TRAP: Results in the kernel sending a SIGSYS signal to the triggering - // task without executing the system call. - // - // SECCOMP_RET_TRAP documentation: - // <https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html> - bpf_policy.push_back(BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRAP)); - break; - } - return bpf_policy; - } -}; -} // namespace - -bool SetupSyscallSandbox(bool log_syscall_violation_before_terminating) -{ - assert(!g_syscall_sandbox_enabled && "SetupSyscallSandbox(...) should only be called once."); - g_syscall_sandbox_enabled = true; - g_syscall_sandbox_log_violation_before_terminating = log_syscall_violation_before_terminating; - if (log_syscall_violation_before_terminating) { - if (!SetupSyscallSandboxDebugHandler()) { - return false; - } - } - return true; -} - -void TestDisallowedSandboxCall() -{ - // The getgroups syscall is assumed NOT to be allowed by the syscall sandbox policy. - std::array<gid_t, 1> groups; - [[maybe_unused]] int32_t ignored = getgroups(groups.size(), groups.data()); -} -#endif // defined(USE_SYSCALL_SANDBOX) - -void SetSyscallSandboxPolicy(SyscallSandboxPolicy syscall_policy) -{ -#if defined(USE_SYSCALL_SANDBOX) - if (!g_syscall_sandbox_enabled) { - return; - } - SeccompPolicyBuilder seccomp_policy_builder; - switch (syscall_policy) { - case SyscallSandboxPolicy::INITIALIZATION: // Thread: main thread (state: init) - // SyscallSandboxPolicy::INITIALIZATION is the first policy loaded. - // - // Subsequently loaded policies can reduce the abilities further, but - // abilities can never be regained. - // - // SyscallSandboxPolicy::INITIALIZATION must thus be a superset of all - // other policies. - seccomp_policy_builder.AllowFileSystem(); - seccomp_policy_builder.AllowNetwork(); - break; - case SyscallSandboxPolicy::INITIALIZATION_DNS_SEED: // Thread: dnsseed - seccomp_policy_builder.AllowFileSystem(); - seccomp_policy_builder.AllowNetwork(); - break; - case SyscallSandboxPolicy::INITIALIZATION_LOAD_BLOCKS: // Thread: loadblk - seccomp_policy_builder.AllowFileSystem(); - break; - case SyscallSandboxPolicy::INITIALIZATION_MAP_PORT: // Thread: mapport - seccomp_policy_builder.AllowFileSystem(); - seccomp_policy_builder.AllowNetwork(); - break; - case SyscallSandboxPolicy::MESSAGE_HANDLER: // Thread: msghand - seccomp_policy_builder.AllowFileSystem(); - break; - case SyscallSandboxPolicy::NET: // Thread: net - seccomp_policy_builder.AllowFileSystem(); - seccomp_policy_builder.AllowNetwork(); - break; - case SyscallSandboxPolicy::NET_ADD_CONNECTION: // Thread: addcon - seccomp_policy_builder.AllowFileSystem(); - seccomp_policy_builder.AllowNetwork(); - break; - case SyscallSandboxPolicy::NET_HTTP_SERVER: // Thread: http - seccomp_policy_builder.AllowFileSystem(); - seccomp_policy_builder.AllowNetwork(); - break; - case SyscallSandboxPolicy::NET_HTTP_SERVER_WORKER: // Thread: httpworker.<N> - seccomp_policy_builder.AllowFileSystem(); - seccomp_policy_builder.AllowNetwork(); - break; - case SyscallSandboxPolicy::NET_OPEN_CONNECTION: // Thread: opencon - seccomp_policy_builder.AllowFileSystem(); - seccomp_policy_builder.AllowNetwork(); - break; - case SyscallSandboxPolicy::SCHEDULER: // Thread: scheduler - seccomp_policy_builder.AllowFileSystem(); - break; - case SyscallSandboxPolicy::TOR_CONTROL: // Thread: torcontrol - seccomp_policy_builder.AllowFileSystem(); - seccomp_policy_builder.AllowNetwork(); - break; - case SyscallSandboxPolicy::TX_INDEX: // Thread: txindex - seccomp_policy_builder.AllowFileSystem(); - break; - case SyscallSandboxPolicy::VALIDATION_SCRIPT_CHECK: // Thread: scriptch.<N> - break; - case SyscallSandboxPolicy::SHUTOFF: // Thread: main thread (state: shutoff) - seccomp_policy_builder.AllowFileSystem(); - break; - } - - const SyscallSandboxAction default_action = g_syscall_sandbox_log_violation_before_terminating ? SyscallSandboxAction::INVOKE_SIGNAL_HANDLER : SyscallSandboxAction::KILL_PROCESS; - std::vector<sock_filter> filter = seccomp_policy_builder.BuildFilter(default_action); - const sock_fprog prog = { - .len = static_cast<uint16_t>(filter.size()), - .filter = filter.data(), - }; - // Do not allow abilities to be regained after being dropped. - // - // PR_SET_NO_NEW_PRIVS documentation: <https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html> - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) { - throw std::runtime_error("Syscall sandbox enforcement failed: prctl(PR_SET_NO_NEW_PRIVS)"); - } - // Install seccomp-bpf syscall filter. - // - // PR_SET_SECCOMP documentation: <https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html> - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) { - throw std::runtime_error("Syscall sandbox enforcement failed: prctl(PR_SET_SECCOMP)"); - } - - const std::string thread_name = !util::ThreadGetInternalName().empty() ? util::ThreadGetInternalName() : "*unnamed*"; - LogPrint(BCLog::UTIL, "Syscall filter installed for thread \"%s\"\n", thread_name); -#endif // defined(USE_SYSCALL_SANDBOX) -} diff --git a/src/util/syscall_sandbox.h b/src/util/syscall_sandbox.h deleted file mode 100644 index 3e56ebe937..0000000000 --- a/src/util/syscall_sandbox.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2020-2022 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_UTIL_SYSCALL_SANDBOX_H -#define BITCOIN_UTIL_SYSCALL_SANDBOX_H - -enum class SyscallSandboxPolicy { - // 1. Initialization - INITIALIZATION, - INITIALIZATION_DNS_SEED, - INITIALIZATION_LOAD_BLOCKS, - INITIALIZATION_MAP_PORT, - - // 2. Steady state (non-initialization, non-shutdown) - MESSAGE_HANDLER, - NET, - NET_ADD_CONNECTION, - NET_HTTP_SERVER, - NET_HTTP_SERVER_WORKER, - NET_OPEN_CONNECTION, - SCHEDULER, - TOR_CONTROL, - TX_INDEX, - VALIDATION_SCRIPT_CHECK, - - // 3. Shutdown - SHUTOFF, -}; - -//! Force the current thread (and threads created from the current thread) into a restricted-service -//! operating mode where only a subset of all syscalls are available. -//! -//! Subsequent calls to this function can reduce the abilities further, but abilities can never be -//! regained. -//! -//! This function is a no-op unless SetupSyscallSandbox(...) has been called. -//! -//! SetupSyscallSandbox(...) is called during bitcoind initialization if Bitcoin Core was compiled -//! with seccomp-bpf support (--with-seccomp) *and* the parameter -sandbox=<mode> was passed to -//! bitcoind. -//! -//! This experimental feature is available under Linux x86_64 only. -void SetSyscallSandboxPolicy(SyscallSandboxPolicy syscall_policy); - -#if defined(USE_SYSCALL_SANDBOX) -//! Setup and enable the experimental syscall sandbox for the running process. -[[nodiscard]] bool SetupSyscallSandbox(bool log_syscall_violation_before_terminating); - -//! Invoke a disallowed syscall. Use for testing purposes. -void TestDisallowedSandboxCall(); -#endif // defined(USE_SYSCALL_SANDBOX) - -#endif // BITCOIN_UTIL_SYSCALL_SANDBOX_H diff --git a/src/util/syserror.cpp b/src/util/syserror.cpp index 5270f55366..bac498a23d 100644 --- a/src/util/syserror.cpp +++ b/src/util/syserror.cpp @@ -12,6 +12,12 @@ #include <cstring> #include <string> +#if defined(WIN32) +#include <windows.h> +#include <locale> +#include <codecvt> +#endif + std::string SysErrorString(int err) { char buf[1024]; @@ -33,3 +39,21 @@ std::string SysErrorString(int err) return strprintf("Unknown error (%d)", err); } } + +#if defined(WIN32) +std::string Win32ErrorString(int err) +{ + wchar_t buf[256]; + buf[0] = 0; + if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, + nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buf, ARRAYSIZE(buf), nullptr)) + { + return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err); + } + else + { + return strprintf("Unknown error (%d)", err); + } +} +#endif diff --git a/src/util/syserror.h b/src/util/syserror.h index a54ba553ee..eb02141b9d 100644 --- a/src/util/syserror.h +++ b/src/util/syserror.h @@ -13,4 +13,8 @@ */ std::string SysErrorString(int err); +#if defined(WIN32) +std::string Win32ErrorString(int err); +#endif + #endif // BITCOIN_UTIL_SYSERROR_H diff --git a/src/util/threadinterrupt.h b/src/util/threadinterrupt.h index ccc053f576..0b79b38276 100644 --- a/src/util/threadinterrupt.h +++ b/src/util/threadinterrupt.h @@ -12,11 +12,17 @@ #include <chrono> #include <condition_variable> -/* - A helper class for interruptible sleeps. Calling operator() will interrupt - any current sleep, and after that point operator bool() will return true - until reset. -*/ +/** + * A helper class for interruptible sleeps. Calling operator() will interrupt + * any current sleep, and after that point operator bool() will return true + * until reset. + * + * This class should not be used in a signal handler. It uses thread + * synchronization primitives that are not safe to use with signals. If sending + * an interrupt from a signal handler is necessary, the \ref SignalInterrupt + * class can be used instead. + */ + class CThreadInterrupt { public: diff --git a/src/validation.cpp b/src/validation.cpp index 1fd20ca2c8..e6def01db5 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -37,7 +37,6 @@ #include <reverse_iterator.h> #include <script/script.h> #include <script/sigcache.h> -#include <shutdown.h> #include <signet.h> #include <tinyformat.h> #include <txdb.h> @@ -50,6 +49,7 @@ #include <util/hasher.h> #include <util/moneystr.h> #include <util/rbf.h> +#include <util/signalinterrupt.h> #include <util/strencodings.h> #include <util/time.h> #include <util/trace.h> @@ -1841,9 +1841,9 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, return true; } -bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage) +bool FatalError(Notifications& notifications, BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage) { - AbortNode(strMessage, userMessage); + notifications.fatalError(strMessage, userMessage); return state.Error(strMessage); } @@ -2078,7 +2078,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, // We don't write down blocks to disk if they may have been // corrupted, so this should be impossible unless we're having hardware // problems. - return AbortNode(state, "Corrupt block found indicating potential hardware failure; shutting down"); + return FatalError(m_chainman.GetNotifications(), state, "Corrupt block found indicating potential hardware failure; shutting down"); } return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString()); } @@ -2498,7 +2498,7 @@ bool Chainstate::FlushStateToDisk( if (fDoFullFlush || fPeriodicWrite) { // Ensure we can write block index if (!CheckDiskSpace(m_blockman.m_opts.blocks_dir)) { - return AbortNode(state, "Disk space is too low!", _("Disk space is too low!")); + return FatalError(m_chainman.GetNotifications(), state, "Disk space is too low!", _("Disk space is too low!")); } { LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH); @@ -2512,7 +2512,7 @@ bool Chainstate::FlushStateToDisk( LOG_TIME_MILLIS_WITH_CATEGORY("write block index to disk", BCLog::BENCH); if (!m_blockman.WriteBlockIndexDB()) { - return AbortNode(state, "Failed to write to block index database"); + return FatalError(m_chainman.GetNotifications(), state, "Failed to write to block index database"); } } // Finally remove any pruned files @@ -2534,11 +2534,11 @@ bool Chainstate::FlushStateToDisk( // an overestimation, as most will delete an existing entry or // overwrite one. Still, use a conservative safety factor of 2. if (!CheckDiskSpace(m_chainman.m_options.datadir, 48 * 2 * 2 * CoinsTip().GetCacheSize())) { - return AbortNode(state, "Disk space is too low!", _("Disk space is too low!")); + return FatalError(m_chainman.GetNotifications(), state, "Disk space is too low!", _("Disk space is too low!")); } // Flush the chainstate (which may refer to block index entries). if (!CoinsTip().Flush()) - return AbortNode(state, "Failed to write to coin database"); + return FatalError(m_chainman.GetNotifications(), state, "Failed to write to coin database"); m_last_flush = nNow; full_flush_completed = true; TRACE5(utxocache, flush, @@ -2554,7 +2554,7 @@ bool Chainstate::FlushStateToDisk( GetMainSignals().ChainStateFlushed(m_chain.GetLocator()); } } catch (const std::runtime_error& e) { - return AbortNode(state, std::string("System error while flushing: ") + e.what()); + return FatalError(m_chainman.GetNotifications(), state, std::string("System error while flushing: ") + e.what()); } return true; } @@ -2790,7 +2790,7 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, if (!pblock) { std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>(); if (!m_blockman.ReadBlockFromDisk(*pblockNew, *pindexNew)) { - return AbortNode(state, "Failed to read block"); + return FatalError(m_chainman.GetNotifications(), state, "Failed to read block"); } pthisBlock = pblockNew; } else { @@ -2974,7 +2974,7 @@ bool Chainstate::ActivateBestChainStep(BlockValidationState& state, CBlockIndex* // If we're unable to disconnect a block during normal operation, // then that is a failure of our local system -- we should abort // rather than stay on a less work chain. - AbortNode(state, "Failed to disconnect block; see debug.log for details"); + FatalError(m_chainman.GetNotifications(), state, "Failed to disconnect block; see debug.log for details"); return false; } fBlocksDisconnected = true; @@ -3171,23 +3171,27 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr< GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); // Always notify the UI if a new block tip was connected - m_chainman.GetNotifications().blockTip(GetSynchronizationState(fInitialDownload), *pindexNewTip); + if (kernel::IsInterrupted(m_chainman.GetNotifications().blockTip(GetSynchronizationState(fInitialDownload), *pindexNewTip))) { + // Just breaking and returning success for now. This could + // be changed to bubble up the kernel::Interrupted value to + // the caller so the caller could distinguish between + // completed and interrupted operations. + break; + } } } // When we reach this point, we switched to a new tip (stored in pindexNewTip). - if (m_chainman.StopAtHeight() && pindexNewTip && pindexNewTip->nHeight >= m_chainman.StopAtHeight()) StartShutdown(); - if (WITH_LOCK(::cs_main, return m_disabled)) { // Background chainstate has reached the snapshot base block, so exit. break; } - // We check shutdown only after giving ActivateBestChainStep a chance to run once so that we - // never shutdown before connecting the genesis block during LoadChainTip(). Previously this - // caused an assert() failure during shutdown in such cases as the UTXO DB flushing checks + // We check interrupt only after giving ActivateBestChainStep a chance to run once so that we + // never interrupt before connecting the genesis block during LoadChainTip(). Previously this + // caused an assert() failure during interrupt in such cases as the UTXO DB flushing checks // that the best block hash is non-null. - if (ShutdownRequested()) break; + if (m_chainman.m_interrupt) break; } while (pindexNewTip != pindexMostWork); CheckBlockIndex(); @@ -3277,7 +3281,7 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde // Disconnect (descendants of) pindex, and mark them invalid. while (true) { - if (ShutdownRequested()) break; + if (m_chainman.m_interrupt) break; // Make sure the queue of validation callbacks doesn't grow unboundedly. LimitValidationInterfaceQueue(); @@ -3368,7 +3372,14 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde // Only notify about a new block tip if the active chain was modified. if (pindex_was_in_chain) { - m_chainman.GetNotifications().blockTip(GetSynchronizationState(IsInitialBlockDownload()), *to_mark_failed->pprev); + // Ignoring return value for now, this could be changed to bubble up + // kernel::Interrupted value to the caller so the caller could + // distinguish between completed and interrupted operations. It might + // also make sense for the blockTip notification to have an enum + // parameter indicating the source of the tip change so hooks can + // distinguish user-initiated invalidateblock changes from other + // changes. + (void)m_chainman.GetNotifications().blockTip(GetSynchronizationState(IsInitialBlockDownload()), *to_mark_failed->pprev); } return true; } @@ -3969,7 +3980,7 @@ bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockV } ReceivedBlockTransactions(block, pindex, blockPos); } catch (const std::runtime_error& e) { - return AbortNode(state, std::string("System error: ") + e.what()); + return FatalError(m_chainman.GetNotifications(), state, std::string("System error: ") + e.what()); } FlushStateToDisk(state, FlushStateMode::NONE); @@ -4079,7 +4090,7 @@ void Chainstate::LoadMempool(const fs::path& load_path, FopenFn mockable_fopen_f { if (!m_mempool) return; ::LoadMempool(*m_mempool, load_path, *this, mockable_fopen_function); - m_mempool->SetLoadTried(!ShutdownRequested()); + m_mempool->SetLoadTried(!m_chainman.m_interrupt); } bool Chainstate::LoadChainTip() @@ -4212,7 +4223,7 @@ VerifyDBResult CVerifyDB::VerifyDB( skipped_l3_checks = true; } } - if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED; + if (chainstate.m_chainman.m_interrupt) return VerifyDBResult::INTERRUPTED; } if (pindexFailure) { LogPrintf("Verification error: coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions); @@ -4245,7 +4256,7 @@ VerifyDBResult CVerifyDB::VerifyDB( LogPrintf("Verification error: found unconnectable block at %d, hash=%s (%s)\n", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString()); return VerifyDBResult::CORRUPTED_BLOCK_DB; } - if (ShutdownRequested()) return VerifyDBResult::INTERRUPTED; + if (chainstate.m_chainman.m_interrupt) return VerifyDBResult::INTERRUPTED; } } @@ -4413,7 +4424,7 @@ bool ChainstateManager::LoadBlockIndex() } for (CBlockIndex* pindex : vSortedByHeight) { - if (ShutdownRequested()) return false; + if (m_interrupt) return false; if (pindex->IsAssumedValid() || (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) { @@ -4519,7 +4530,7 @@ void Chainstate::LoadExternalBlockFile( // such as a block fails to deserialize. uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { - if (ShutdownRequested()) return; + if (m_chainman.m_interrupt) return; blkdat.SetPos(nRewind); nRewind++; // start one byte further next time, in case of failure @@ -4660,7 +4671,7 @@ void Chainstate::LoadExternalBlockFile( } } } catch (const std::runtime_error& e) { - AbortNode(std::string("System error: ") + e.what()); + m_chainman.GetNotifications().fatalError(std::string("System error: ") + e.what()); } LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start)); } @@ -4999,15 +5010,15 @@ static bool DeleteCoinsDBFromDisk(const fs::path db_path, bool is_snapshot) if (is_snapshot) { fs::path base_blockhash_path = db_path / node::SNAPSHOT_BLOCKHASH_FILENAME; - if (fs::exists(base_blockhash_path)) { - bool removed = fs::remove(base_blockhash_path); - if (!removed) { - LogPrintf("[snapshot] failed to remove file %s\n", - fs::PathToString(base_blockhash_path)); + try { + bool existed = fs::remove(base_blockhash_path); + if (!existed) { + LogPrintf("[snapshot] snapshot chainstate dir being removed lacks %s file\n", + fs::PathToString(node::SNAPSHOT_BLOCKHASH_FILENAME)); } - } else { - LogPrintf("[snapshot] snapshot chainstate dir being removed lacks %s file\n", - fs::PathToString(node::SNAPSHOT_BLOCKHASH_FILENAME)); + } catch (const fs::filesystem_error& e) { + LogPrintf("[snapshot] failed to remove file %s: %s\n", + fs::PathToString(base_blockhash_path), fsbridge::get_filesystem_error_message(e)); } } @@ -5112,7 +5123,7 @@ bool ChainstateManager::ActivateSnapshot( snapshot_chainstate.reset(); bool removed = DeleteCoinsDBFromDisk(*snapshot_datadir, /*is_snapshot=*/true); if (!removed) { - AbortNode(strprintf("Failed to remove snapshot chainstate dir (%s). " + GetNotifications().fatalError(strprintf("Failed to remove snapshot chainstate dir (%s). " "Manually remove it before restarting.\n", fs::PathToString(*snapshot_datadir))); } } @@ -5150,15 +5161,15 @@ static void FlushSnapshotToDisk(CCoinsViewCache& coins_cache, bool snapshot_load struct StopHashingException : public std::exception { - const char* what() const throw() override + const char* what() const noexcept override { - return "ComputeUTXOStats interrupted by shutdown."; + return "ComputeUTXOStats interrupted."; } }; -static void SnapshotUTXOHashBreakpoint() +static void SnapshotUTXOHashBreakpoint(const util::SignalInterrupt& interrupt) { - if (ShutdownRequested()) throw StopHashingException(); + if (interrupt) throw StopHashingException(); } bool ChainstateManager::PopulateAndValidateSnapshot( @@ -5235,7 +5246,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot( // If our average Coin size is roughly 41 bytes, checking every 120,000 coins // means <5MB of memory imprecision. if (coins_processed % 120000 == 0) { - if (ShutdownRequested()) { + if (m_interrupt) { return false; } @@ -5292,7 +5303,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot( try { maybe_stats = ComputeUTXOStats( - CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, SnapshotUTXOHashBreakpoint); + CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, [&interrupt = m_interrupt] { SnapshotUTXOHashBreakpoint(interrupt); }); } catch (StopHashingException const&) { return false; } @@ -5377,8 +5388,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot( // through IsUsable() checks, or // // (ii) giving each chainstate its own lock instead of using cs_main for everything. -SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation( - std::function<void(bilingual_str)> shutdown_fnc) +SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation() { AssertLockHeld(cs_main); if (m_ibd_chainstate.get() == &this->ActiveChainstate() || @@ -5430,7 +5440,7 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation( user_error = strprintf(Untranslated("%s\n%s"), user_error, util::ErrorString(rename_result)); } - shutdown_fnc(user_error); + GetNotifications().fatalError(user_error.original, user_error); }; if (index_new.GetBlockHash() != snapshot_blockhash) { @@ -5470,7 +5480,7 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation( CoinStatsHashType::HASH_SERIALIZED, &ibd_coins_db, m_blockman, - SnapshotUTXOHashBreakpoint); + [&interrupt = m_interrupt] { SnapshotUTXOHashBreakpoint(interrupt); }); } catch (StopHashingException const&) { return SnapshotCompletionResult::STATS_FAILED; } @@ -5579,9 +5589,10 @@ static ChainstateManager::Options&& Flatten(ChainstateManager::Options&& opts) return std::move(opts); } -ChainstateManager::ChainstateManager(Options options, node::BlockManager::Options blockman_options) - : m_options{Flatten(std::move(options))}, - m_blockman{std::move(blockman_options)} {} +ChainstateManager::ChainstateManager(const util::SignalInterrupt& interrupt, Options options, node::BlockManager::Options blockman_options) + : m_interrupt{interrupt}, + m_options{Flatten(std::move(options))}, + m_blockman{interrupt, std::move(blockman_options)} {} ChainstateManager::~ChainstateManager() { @@ -5726,13 +5737,13 @@ bool ChainstateManager::ValidatedSnapshotCleanup() fs::path tmp_old{ibd_chainstate_path + "_todelete"}; - auto rename_failed_abort = []( + auto rename_failed_abort = [this]( fs::path p_old, fs::path p_new, const fs::filesystem_error& err) { LogPrintf("%s: error renaming file (%s): %s\n", __func__, fs::PathToString(p_old), err.what()); - AbortNode(strprintf( + GetNotifications().fatalError(strprintf( "Rename of '%s' -> '%s' failed. " "Cannot clean up the background chainstate leveldb directory.", fs::PathToString(p_old), fs::PathToString(p_new))); @@ -5757,7 +5768,7 @@ bool ChainstateManager::ValidatedSnapshotCleanup() } if (!DeleteCoinsDBFromDisk(tmp_old, /*is_snapshot=*/false)) { - // No need to AbortNode because once the unneeded bg chainstate data is + // No need to FatalError because once the unneeded bg chainstate data is // moved, it will not interfere with subsequent initialization. LogPrintf("Deletion of %s failed. Please remove it manually, as the " /* Continued */ "directory is now unnecessary.\n", diff --git a/src/validation.h b/src/validation.h index 7b215dea6b..af8ceb5dfa 100644 --- a/src/validation.h +++ b/src/validation.h @@ -23,7 +23,6 @@ #include <policy/packages.h> #include <policy/policy.h> #include <script/script_error.h> -#include <shutdown.h> #include <sync.h> #include <txdb.h> #include <txmempool.h> // For CTxMemPool::cs @@ -43,6 +42,7 @@ #include <stdint.h> #include <string> #include <thread> +#include <type_traits> #include <utility> #include <vector> @@ -61,6 +61,9 @@ class SnapshotMetadata; namespace Consensus { struct Params; } // namespace Consensus +namespace util { +class SignalInterrupt; +} // namespace util /** Maximum number of dedicated script-checking threads allowed */ static const int MAX_SCRIPTCHECK_THREADS = 15; @@ -102,7 +105,7 @@ void StopScriptCheckWorkerThreads(); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); -bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str{}); +bool FatalError(kernel::Notifications& notifications, BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = {}); /** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex); @@ -330,6 +333,11 @@ public: ScriptError GetScriptError() const { return error; } }; +// CScriptCheck is used a lot in std::vector, make sure that's efficient +static_assert(std::is_nothrow_move_assignable_v<CScriptCheck>); +static_assert(std::is_nothrow_move_constructible_v<CScriptCheck>); +static_assert(std::is_nothrow_destructible_v<CScriptCheck>); + /** Initializes the script-execution cache */ [[nodiscard]] bool InitScriptExecutionCache(size_t max_size_bytes); @@ -953,7 +961,7 @@ private: public: using Options = kernel::ChainstateManagerOpts; - explicit ChainstateManager(Options options, node::BlockManager::Options blockman_options); + explicit ChainstateManager(const util::SignalInterrupt& interrupt, Options options, node::BlockManager::Options blockman_options); const CChainParams& GetParams() const { return m_options.chainparams; } const Consensus::Params& GetConsensus() const { return m_options.chainparams.GetConsensus(); } @@ -961,7 +969,6 @@ public: const arith_uint256& MinimumChainWork() const { return *Assert(m_options.minimum_chain_work); } const uint256& AssumedValidBlock() const { return *Assert(m_options.assumed_valid_block); } kernel::Notifications& GetNotifications() const { return m_options.notifications; }; - int StopAtHeight() const { return m_options.stop_at_height; }; /** * Alias for ::cs_main. @@ -976,8 +983,9 @@ public: */ RecursiveMutex& GetMutex() const LOCK_RETURNED(::cs_main) { return ::cs_main; } + const util::SignalInterrupt& m_interrupt; const Options m_options; - std::thread m_load_block; + std::thread m_thread_load; //! A single BlockManager instance is shared across each constructed //! chainstate to avoid duplicating block metadata. node::BlockManager m_blockman; @@ -1046,10 +1054,7 @@ public: //! If the coins match (expected), then mark the validation chainstate for //! deletion and continue using the snapshot chainstate as active. //! Otherwise, revert to using the ibd chainstate and shutdown. - SnapshotCompletionResult MaybeCompleteSnapshotValidation( - std::function<void(bilingual_str)> shutdown_fnc = - [](bilingual_str msg) { AbortNode(msg.original, msg); }) - EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + SnapshotCompletionResult MaybeCompleteSnapshotValidation() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); //! The most-work chain. Chainstate& ActiveChainstate() const; diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp index 68abdcd81e..9ea43ca67c 100644 --- a/src/wallet/bdb.cpp +++ b/src/wallet/bdb.cpp @@ -18,6 +18,7 @@ #include <stdint.h> +#include <db_cxx.h> #include <sys/stat.h> // Windows may not define S_IRUSR or S_IWUSR. We define both @@ -29,6 +30,8 @@ #endif #endif +static_assert(BDB_DB_FILE_ID_LEN == DB_FILE_ID_LEN, "DB_FILE_ID_LEN should be 20."); + namespace wallet { namespace { @@ -232,6 +235,26 @@ BerkeleyEnvironment::BerkeleyEnvironment() : m_use_shared_memory(false) fMockDb = true; } +/** RAII class that automatically cleanses its data on destruction */ +class SafeDbt final +{ + Dbt m_dbt; + +public: + // construct Dbt with internally-managed data + SafeDbt(); + // construct Dbt with provided data + SafeDbt(void* data, size_t size); + ~SafeDbt(); + + // delegate to Dbt + const void* get_data() const; + uint32_t get_size() const; + + // conversion operator to access the underlying Dbt + operator Dbt*(); +}; + SafeDbt::SafeDbt() { m_dbt.set_flags(DB_DBT_MALLOC); @@ -271,6 +294,18 @@ SafeDbt::operator Dbt*() return &m_dbt; } +static Span<const std::byte> SpanFromDbt(const SafeDbt& dbt) +{ + return {reinterpret_cast<const std::byte*>(dbt.get_data()), dbt.get_size()}; +} + +BerkeleyDatabase::BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options) : + WalletDatabase(), env(std::move(env)), m_filename(std::move(filename)), m_max_log_mb(options.max_log_mb) +{ + auto inserted = this->env->m_databases.emplace(m_filename, std::ref(*this)); + assert(inserted.second); +} + bool BerkeleyDatabase::Verify(bilingual_str& errorStr) { fs::path walletDir = env->Directory(); @@ -458,6 +493,15 @@ void BerkeleyEnvironment::ReloadDbEnv() Open(open_err); } +DbTxn* BerkeleyEnvironment::TxnBegin(int flags) +{ + DbTxn* ptxn = nullptr; + int ret = dbenv->txn_begin(nullptr, &ptxn, flags); + if (!ptxn || ret != 0) + return nullptr; + return ptxn; +} + bool BerkeleyDatabase::Rewrite(const char* pszSkip) { while (true) { @@ -702,7 +746,7 @@ DatabaseCursor::Status BerkeleyCursor::Next(DataStream& ssKey, DataStream& ssVal return Status::FAIL; } - Span<const std::byte> raw_key = {AsBytePtr(datKey.get_data()), datKey.get_size()}; + Span<const std::byte> raw_key = SpanFromDbt(datKey); if (!m_key_prefix.empty() && std::mismatch(raw_key.begin(), raw_key.end(), m_key_prefix.begin(), m_key_prefix.end()).second != m_key_prefix.end()) { return Status::DONE; } @@ -711,7 +755,7 @@ DatabaseCursor::Status BerkeleyCursor::Next(DataStream& ssKey, DataStream& ssVal ssKey.clear(); ssKey.write(raw_key); ssValue.clear(); - ssValue.write({AsBytePtr(datValue.get_data()), datValue.get_size()}); + ssValue.write(SpanFromDbt(datValue)); return Status::MORE; } @@ -738,7 +782,7 @@ bool BerkeleyBatch::TxnBegin() { if (!pdb || activeTxn) return false; - DbTxn* ptxn = env->TxnBegin(); + DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC); if (!ptxn) return false; activeTxn = ptxn; @@ -796,7 +840,7 @@ bool BerkeleyBatch::ReadKey(DataStream&& key, DataStream& value) int ret = pdb->get(activeTxn, datKey, datValue, 0); if (ret == 0 && datValue.get_data() != nullptr) { value.clear(); - value.write({AsBytePtr(datValue.get_data()), datValue.get_size()}); + value.write(SpanFromDbt(datValue)); return true; } return false; diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h index 8cc03692d6..630630ebe0 100644 --- a/src/wallet/bdb.h +++ b/src/wallet/bdb.h @@ -21,21 +21,21 @@ #include <unordered_map> #include <vector> -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsuggest-override" -#endif -#include <db_cxx.h> -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC diagnostic pop -#endif - struct bilingual_str; +class DbEnv; +class DbTxn; +class Db; +class Dbc; + +// This constant was introduced in BDB 4.0.14 and has never changed, but there +// is a belt-and-suspenders check in the cpp file just in case. +#define BDB_DB_FILE_ID_LEN 20 /* Unique file ID length. */ + namespace wallet { struct WalletDatabaseFileId { - uint8_t value[DB_FILE_ID_LEN]; + uint8_t value[BDB_DB_FILE_ID_LEN]; bool operator==(const WalletDatabaseFileId& rhs) const; }; @@ -74,14 +74,7 @@ public: void CloseDb(const fs::path& filename); void ReloadDbEnv(); - DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) - { - DbTxn* ptxn = nullptr; - int ret = dbenv->txn_begin(nullptr, &ptxn, flags); - if (!ptxn || ret != 0) - return nullptr; - return ptxn; - } + DbTxn* TxnBegin(int flags); }; /** Get BerkeleyEnvironment given a directory path. */ @@ -98,12 +91,7 @@ public: BerkeleyDatabase() = delete; /** Create DB handle to real database */ - BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options) : - WalletDatabase(), env(std::move(env)), m_filename(std::move(filename)), m_max_log_mb(options.max_log_mb) - { - auto inserted = this->env->m_databases.emplace(m_filename, std::ref(*this)); - assert(inserted.second); - } + BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options); ~BerkeleyDatabase() override; @@ -166,26 +154,6 @@ public: std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override; }; -/** RAII class that automatically cleanses its data on destruction */ -class SafeDbt final -{ - Dbt m_dbt; - -public: - // construct Dbt with internally-managed data - SafeDbt(); - // construct Dbt with provided data - SafeDbt(void* data, size_t size); - ~SafeDbt(); - - // delegate to Dbt - const void* get_data() const; - uint32_t get_size() const; - - // conversion operator to access the underlying Dbt - operator Dbt*(); -}; - class BerkeleyCursor : public DatabaseCursor { private: diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index bd74025f09..d6b9b68e1f 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -191,7 +191,7 @@ public: } }; -util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng, +util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, CAmount change_fee, FastRandomContext& rng, int max_weight) { SelectionResult result(target_value, SelectionAlgorithm::SRD); @@ -201,7 +201,7 @@ util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utx // barely meets the target. Just use the lower bound change target instead of the randomly // generated one, since SRD will result in a random change amount anyway; avoid making the // target needlessly large. - target_value += CHANGE_LOWER; + target_value += CHANGE_LOWER + change_fee; std::vector<size_t> indexes; indexes.resize(utxo_pool.size()); diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h index 432d7d1431..afd868fc89 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -421,7 +421,7 @@ util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool * @param[in] max_weight The maximum allowed weight for a selection result to be valid * @returns If successful, a valid SelectionResult, otherwise, util::Error */ -util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng, +util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, CAmount change_fee, FastRandomContext& rng, int max_weight); // Original coin selection algorithm as a fallback diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp index 44c93eed7c..3ac5cf03b1 100644 --- a/src/wallet/dump.cpp +++ b/src/wallet/dump.cpp @@ -57,12 +57,12 @@ bool DumpWallet(const ArgsManager& args, CWallet& wallet, bilingual_str& error) // Write out a magic string with version std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION); dump_file.write(line.data(), line.size()); - hasher.write(MakeByteSpan(line)); + hasher << Span{line}; // Write out the file format line = strprintf("%s,%s\n", "format", db.Format()); dump_file.write(line.data(), line.size()); - hasher.write(MakeByteSpan(line)); + hasher << Span{line}; if (ret) { @@ -83,7 +83,7 @@ bool DumpWallet(const ArgsManager& args, CWallet& wallet, bilingual_str& error) std::string value_str = HexStr(ss_value); line = strprintf("%s,%s\n", key_str, value_str); dump_file.write(line.data(), line.size()); - hasher.write(MakeByteSpan(line)); + hasher << Span{line}; } } @@ -160,7 +160,7 @@ bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs:: return false; } std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value); - hasher.write(MakeByteSpan(magic_hasher_line)); + hasher << Span{magic_hasher_line}; // Get the stored file format std::string format_key; @@ -191,7 +191,7 @@ bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs:: warnings.push_back(strprintf(_("Warning: Dumpfile wallet format \"%s\" does not match command line specified format \"%s\"."), format_value, file_format)); } std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value); - hasher.write(MakeByteSpan(format_hasher_line)); + hasher << Span{format_hasher_line}; DatabaseOptions options; DatabaseStatus status; @@ -236,7 +236,7 @@ bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs:: } std::string line = strprintf("%s,%s\n", key, value); - hasher.write(MakeByteSpan(line)); + hasher << Span{line}; if (key.empty() || value.empty()) { continue; diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 0e1f1f9847..3720d144eb 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -152,8 +152,14 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid) } Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<bilingual_str>& errors, - CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine, const std::vector<CTxOut>& outputs) + CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine, const std::vector<CTxOut>& outputs, std::optional<uint32_t> reduce_output) { + // Cannot both specify new outputs and an output to reduce + if (!outputs.empty() && reduce_output.has_value()) { + errors.push_back(Untranslated("Cannot specify both new outputs to use and an output index to reduce")); + return Result::INVALID_PARAMETER; + } + // We are going to modify coin control later, copy to re-use CCoinControl new_coin_control(coin_control); @@ -166,6 +172,12 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo } const CWalletTx& wtx = it->second; + // Make sure that reduce_output is valid + if (reduce_output.has_value() && reduce_output.value() >= wtx.tx->vout.size()) { + errors.push_back(Untranslated("Change position is out of range")); + return Result::INVALID_PARAMETER; + } + // Retrieve all of the UTXOs and add them to coin control // While we're here, calculate the input amount std::map<COutPoint, Coin> coins; @@ -233,14 +245,15 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo std::vector<CRecipient> recipients; CAmount new_outputs_value = 0; const auto& txouts = outputs.empty() ? wtx.tx->vout : outputs; - for (const auto& output : txouts) { - if (!OutputIsChange(wallet, output)) { - CRecipient recipient = {output.scriptPubKey, output.nValue, false}; - recipients.push_back(recipient); - } else { + for (size_t i = 0; i < txouts.size(); ++i) { + const CTxOut& output = txouts.at(i); + if (reduce_output.has_value() ? reduce_output.value() == i : OutputIsChange(wallet, output)) { CTxDestination change_dest; ExtractDestination(output.scriptPubKey, change_dest); new_coin_control.destChange = change_dest; + } else { + CRecipient recipient = {output.scriptPubKey, output.nValue, false}; + recipients.push_back(recipient); } new_outputs_value += output.nValue; } diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h index 53cf16e0f1..f00bf15730 100644 --- a/src/wallet/feebumper.h +++ b/src/wallet/feebumper.h @@ -43,6 +43,8 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid); * @param[out] new_fee the fee that the bump transaction pays * @param[out] mtx The bump transaction itself * @param[in] require_mine Whether the original transaction must consist of inputs that can be spent by the wallet + * @param[in] outputs Vector of new outputs to replace the bumped transaction's outputs + * @param[in] reduce_output The position of the change output to deduct the fee from in the transaction being bumped */ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, @@ -52,7 +54,8 @@ Result CreateRateBumpTransaction(CWallet& wallet, CAmount& new_fee, CMutableTransaction& mtx, bool require_mine, - const std::vector<CTxOut>& outputs); + const std::vector<CTxOut>& outputs, + std::optional<uint32_t> reduce_output = std::nullopt); //! Sign the new transaction, //! @return false if the tx couldn't be found or if it was diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index feee643f26..b695d4bed3 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -1015,9 +1015,11 @@ static RPCHelpMan bumpfee_helper(std::string method_name) "\"" + FeeModes("\"\n\"") + "\""}, {"outputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "New outputs (key-value pairs) which will replace\n" "the original ones, if provided. Each address can only appear once and there can\n" - "only be one \"data\" object.\n", + "only be one \"data\" object.\n" + "Cannot be provided if 'reduce_output' is specified.", OutputsDoc(), RPCArgOptions{.skip_type_check = true}}, + {"reduce_output", RPCArg::Type::NUM, RPCArg::DefaultHint{"not set, detect change automatically"}, "The 0-based index of the output from which the additional fees will be deducted. In general, this should be the position of change output. Cannot be provided if 'outputs' is specified."}, }, RPCArgOptions{.oneline_description="options"}}, }, @@ -1056,6 +1058,8 @@ static RPCHelpMan bumpfee_helper(std::string method_name) coin_control.m_signal_bip125_rbf = true; std::vector<CTxOut> outputs; + std::optional<uint32_t> reduce_output; + if (!request.params[1].isNull()) { UniValue options = request.params[1]; RPCTypeCheckObj(options, @@ -1066,6 +1070,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name) {"replaceable", UniValueType(UniValue::VBOOL)}, {"estimate_mode", UniValueType(UniValue::VSTR)}, {"outputs", UniValueType()}, // will be checked by AddOutputs() + {"reduce_output", UniValueType(UniValue::VNUM)}, }, true, true); @@ -1089,6 +1094,10 @@ static RPCHelpMan bumpfee_helper(std::string method_name) AddOutputs(tempTx, options["outputs"]); outputs = tempTx.vout; } + + if (options.exists("reduce_output")) { + reduce_output = options["reduce_output"].getInt<uint32_t>(); + } } // Make sure the results are valid at least up to the most recent block @@ -1106,7 +1115,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name) CMutableTransaction mtx; feebumper::Result res; // Targeting feerate bump. - res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx, /*require_mine=*/ !want_psbt, outputs); + res = feebumper::CreateRateBumpTransaction(*pwallet, hash, coin_control, errors, old_fee, new_fee, mtx, /*require_mine=*/ !want_psbt, outputs, reduce_output); if (res != feebumper::Result::OK) { switch(res) { case feebumper::Result::INVALID_ADDRESS_OR_KEY: diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp index e303310273..0a0745b1c5 100644 --- a/src/wallet/salvage.cpp +++ b/src/wallet/salvage.cpp @@ -11,6 +11,8 @@ #include <wallet/wallet.h> #include <wallet/walletdb.h> +#include <db_cxx.h> + namespace wallet { /* End of headers, beginning of key/value data */ static const char *HEADER_END = "HEADER=END"; @@ -18,11 +20,6 @@ static const char *HEADER_END = "HEADER=END"; static const char *DATA_END = "DATA=END"; typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair; -static bool KeyFilter(const std::string& type) -{ - return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN; -} - class DummyCursor : public DatabaseCursor { Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; } @@ -180,23 +177,30 @@ bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bil return false; } - DbTxn* ptxn = env->TxnBegin(); + DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC); CWallet dummyWallet(nullptr, "", std::make_unique<DummyDatabase>()); for (KeyValPair& row : salvagedData) { /* Filter for only private key type KV pairs to be added to the salvaged wallet */ DataStream ssKey{row.first}; - CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); + DataStream ssValue(row.second); std::string strType, strErr; - bool fReadOK; - { - // Required in LoadKeyMetadata(): - LOCK(dummyWallet.cs_wallet); - fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType, strErr, KeyFilter); - } - if (!KeyFilter(strType)) { + + // We only care about KEY, MASTER_KEY, CRYPTED_KEY, and HDCHAIN types + ssKey >> strType; + bool fReadOK = false; + if (strType == DBKeys::KEY) { + fReadOK = LoadKey(&dummyWallet, ssKey, ssValue, strErr); + } else if (strType == DBKeys::CRYPTED_KEY) { + fReadOK = LoadCryptedKey(&dummyWallet, ssKey, ssValue, strErr); + } else if (strType == DBKeys::MASTER_KEY) { + fReadOK = LoadEncryptionKey(&dummyWallet, ssKey, ssValue, strErr); + } else if (strType == DBKeys::HDCHAIN) { + fReadOK = LoadHDChain(&dummyWallet, ssValue, strErr); + } else { continue; } + if (!fReadOK) { warnings.push_back(strprintf(Untranslated("WARNING: WalletBatch::Recover skipping %s: %s"), strType, strErr)); diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 796b7f11c5..5b110b4d14 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -2584,10 +2584,7 @@ std::unique_ptr<CKeyMetadata> DescriptorScriptPubKeyMan::GetMetadata(const CTxDe uint256 DescriptorScriptPubKeyMan::GetID() const { LOCK(cs_desc_man); - std::string desc_str = m_wallet_descriptor.descriptor->ToString(); - uint256 id; - CSHA256().Write((unsigned char*)desc_str.data(), desc_str.size()).Finalize(id.begin()); - return id; + return DescriptorID(*m_wallet_descriptor.descriptor); } void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache) diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index 99c6582f9c..a3728223fb 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -583,7 +583,7 @@ util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue, results.push_back(*knapsack_result); } else append_error(knapsack_result); - if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.rng_fast, max_inputs_weight)}) { + if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.m_change_fee, coin_selection_params.rng_fast, max_inputs_weight)}) { srd_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee); results.push_back(*srd_result); } else append_error(srd_result); diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index 8d7fe97bb1..ecd34bb96a 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -24,6 +24,12 @@ namespace wallet { static constexpr int32_t WALLET_SCHEMA_VERSION = 0; +static Span<const std::byte> SpanFromBlob(sqlite3_stmt* stmt, int col) +{ + return {reinterpret_cast<const std::byte*>(sqlite3_column_blob(stmt, col)), + static_cast<size_t>(sqlite3_column_bytes(stmt, col))}; +} + static void ErrorLogCallback(void* arg, int code, const char* msg) { // From sqlite3_config() documentation for the SQLITE_CONFIG_LOG option: @@ -434,10 +440,8 @@ bool SQLiteBatch::ReadKey(DataStream&& key, DataStream& value) return false; } // Leftmost column in result is index 0 - const std::byte* data{AsBytePtr(sqlite3_column_blob(m_read_stmt, 0))}; - size_t data_size(sqlite3_column_bytes(m_read_stmt, 0)); value.clear(); - value.write({data, data_size}); + value.write(SpanFromBlob(m_read_stmt, 0)); sqlite3_clear_bindings(m_read_stmt); sqlite3_reset(m_read_stmt); @@ -527,12 +531,8 @@ DatabaseCursor::Status SQLiteCursor::Next(DataStream& key, DataStream& value) value.clear(); // Leftmost column in result is index 0 - const std::byte* key_data{AsBytePtr(sqlite3_column_blob(m_cursor_stmt, 0))}; - size_t key_data_size(sqlite3_column_bytes(m_cursor_stmt, 0)); - key.write({key_data, key_data_size}); - const std::byte* value_data{AsBytePtr(sqlite3_column_blob(m_cursor_stmt, 1))}; - size_t value_data_size(sqlite3_column_bytes(m_cursor_stmt, 1)); - value.write({value_data, value_data_size}); + key.write(SpanFromBlob(m_cursor_stmt, 0)); + value.write(SpanFromBlob(m_cursor_stmt, 1)); return Status::MORE; } diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h index 0378bbb8d6..f1ce0567e1 100644 --- a/src/wallet/sqlite.h +++ b/src/wallet/sqlite.h @@ -8,10 +8,11 @@ #include <sync.h> #include <wallet/db.h> -#include <sqlite3.h> - struct bilingual_str; +struct sqlite3_stmt; +struct sqlite3; + namespace wallet { class SQLiteDatabase; diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index b1d67c1432..c8283f453a 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -968,7 +968,7 @@ static util::Result<SelectionResult> SelectCoinsSRD(const CAmount& target, std::unique_ptr<CWallet> wallet = NewWallet(m_node); CoinEligibilityFilter filter(0, 0, 0); // accept all coins without ancestors Groups group = GroupOutputs(*wallet, coin_setup(*wallet), cs_params, {{filter}})[filter].all_groups; - return SelectCoinsSRD(group.positive_group, target, cs_params.rng_fast, max_weight); + return SelectCoinsSRD(group.positive_group, target, cs_params.m_change_fee, cs_params.rng_fast, max_weight); } BOOST_AUTO_TEST_CASE(srd_tests) diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp index e0b4a1e74c..d341e84d9b 100644 --- a/src/wallet/test/db_tests.cpp +++ b/src/wallet/test/db_tests.cpp @@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test) for (const auto& database : TestDatabases(m_path_root)) { std::unique_ptr<DatabaseBatch> batch = database->MakeBatch(); for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) { - batch->Write(MakeUCharSpan(k), MakeUCharSpan(v)); + batch->Write(Span{k}, Span{v}); } CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs}); CheckPrefix(*batch, StringBytes("prefix"), {p, ps}); diff --git a/src/wallet/test/fuzz/coincontrol.cpp b/src/wallet/test/fuzz/coincontrol.cpp index 7dabdfb472..0f71f28df2 100644 --- a/src/wallet/test/fuzz/coincontrol.cpp +++ b/src/wallet/test/fuzz/coincontrol.cpp @@ -20,7 +20,7 @@ void initialize_coincontrol() g_setup = testing_setup.get(); } -FUZZ_TARGET_INIT(coincontrol, initialize_coincontrol) +FUZZ_TARGET(coincontrol, .init = initialize_coincontrol) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const auto& node = g_setup->m_node; diff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp index 9be8efab62..bc935157b1 100644 --- a/src/wallet/test/fuzz/coinselection.cpp +++ b/src/wallet/test/fuzz/coinselection.cpp @@ -90,8 +90,11 @@ FUZZ_TARGET(coinselection) // Run coinselection algorithms const auto result_bnb = SelectCoinsBnB(group_pos, target, cost_of_change, MAX_STANDARD_TX_WEIGHT); - auto result_srd = SelectCoinsSRD(group_pos, target, fast_random_context, MAX_STANDARD_TX_WEIGHT); - if (result_srd) result_srd->ComputeAndSetWaste(cost_of_change, cost_of_change, 0); + auto result_srd = SelectCoinsSRD(group_pos, target, coin_params.m_change_fee, fast_random_context, MAX_STANDARD_TX_WEIGHT); + if (result_srd) { + assert(result_srd->GetChange(CHANGE_LOWER, coin_params.m_change_fee) > 0); // Demonstrate that SRD creates change of at least CHANGE_LOWER + result_srd->ComputeAndSetWaste(cost_of_change, cost_of_change, 0); + } CAmount change_target{GenerateChangeTarget(target, coin_params.m_change_fee, fast_random_context)}; auto result_knapsack = KnapsackSolver(group_all, target, change_target, fast_random_context, MAX_STANDARD_TX_WEIGHT); diff --git a/src/wallet/test/fuzz/fees.cpp b/src/wallet/test/fuzz/fees.cpp index 24e1098941..2f7892dc0a 100644 --- a/src/wallet/test/fuzz/fees.cpp +++ b/src/wallet/test/fuzz/fees.cpp @@ -25,7 +25,7 @@ void initialize_setup() g_wallet_ptr = std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase()); } -FUZZ_TARGET_INIT(wallet_fees, initialize_setup) +FUZZ_TARGET(wallet_fees, .init = initialize_setup) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; const auto& node{g_setup->m_node}; diff --git a/src/wallet/test/fuzz/notifications.cpp b/src/wallet/test/fuzz/notifications.cpp index f4b69f7403..42accafe5b 100644 --- a/src/wallet/test/fuzz/notifications.cpp +++ b/src/wallet/test/fuzz/notifications.cpp @@ -79,7 +79,7 @@ struct FuzzedWallet { } }; -FUZZ_TARGET_INIT(wallet_notifications, initialize_setup) +FUZZ_TARGET(wallet_notifications, .init = initialize_setup) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; // The total amount, to be distributed to the wallets a and b in txs diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 65b02c267d..dd914f159c 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -15,6 +15,7 @@ #include <policy/policy.h> #include <rpc/server.h> #include <test/util/logging.h> +#include <test/util/random.h> #include <test/util/setup_common.h> #include <util/translation.h> #include <validation.h> diff --git a/src/wallet/test/walletload_tests.cpp b/src/wallet/test/walletload_tests.cpp index 73a4b77188..1bd2bf012f 100644 --- a/src/wallet/test/walletload_tests.cpp +++ b/src/wallet/test/walletload_tests.cpp @@ -4,6 +4,7 @@ #include <wallet/test/util.h> #include <wallet/wallet.h> +#include <test/util/logging.h> #include <test/util/setup_common.h> #include <boost/test/unit_test.hpp> @@ -19,7 +20,7 @@ public: explicit DummyDescriptor(const std::string& descriptor) : desc(descriptor) {}; ~DummyDescriptor() = default; - std::string ToString() const override { return desc; } + std::string ToString(bool compat_format) const override { return desc; } std::optional<OutputType> GetOutputType() const override { return OutputType::UNKNOWN; } bool IsRange() const override { return false; } @@ -32,7 +33,7 @@ public: void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const override {} }; -BOOST_FIXTURE_TEST_CASE(wallet_load_unknown_descriptor, TestingSetup) +BOOST_FIXTURE_TEST_CASE(wallet_load_descriptors, TestingSetup) { std::unique_ptr<WalletDatabase> database = CreateMockableWalletDatabase(); { @@ -49,6 +50,33 @@ BOOST_FIXTURE_TEST_CASE(wallet_load_unknown_descriptor, TestingSetup) const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(database))); BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::UNKNOWN_DESCRIPTOR); } + + // Test 2 + // Now write a valid descriptor with an invalid ID. + // As the software produces another ID for the descriptor, the loading process must be aborted. + database = CreateMockableWalletDatabase(); + + // Verify the error + bool found = false; + DebugLogHelper logHelper("The descriptor ID calculated by the wallet differs from the one in DB", [&](const std::string* s) { + found = true; + return false; + }); + + { + // Write valid descriptor with invalid ID + WalletBatch batch(*database, false); + std::string desc = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu"; + WalletDescriptor wallet_descriptor(std::make_shared<DummyDescriptor>(desc), 0, 0, 0, 0); + BOOST_CHECK(batch.WriteDescriptor(uint256::ONE, wallet_descriptor)); + } + + { + // Now try to load the wallet and verify the error. + const std::shared_ptr<CWallet> wallet(new CWallet(m_node.chain.get(), "", std::move(database))); + BOOST_CHECK_EQUAL(wallet->LoadWallet(), DBErrors::CORRUPT); + BOOST_CHECK(found); // The error must be logged + } } bool HasAnyRecordOfType(WalletDatabase& db, const std::string& key) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ba11933b91..8fa93b97d6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -245,6 +245,11 @@ std::shared_ptr<CWallet> LoadWalletInternal(WalletContext& context, const std::s return nullptr; } + // Legacy wallets are being deprecated, warn if the loaded wallet is legacy + if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { + warnings.push_back(_("Wallet loaded successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future. Legacy wallets can be migrated to a descriptor wallet with migratewallet.")); + } + NotifyWalletLoaded(context, wallet); AddWallet(context, wallet); wallet->postInitProcess(); @@ -2929,7 +2934,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri else if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) { warnings.push_back(strprintf(_("Error reading %s! All keys read correctly, but transaction data" - " or address book entries might be missing or incorrect."), + " or address metadata may be missing or incorrect."), walletFile)); } else if (nLoadWalletRet == DBErrors::TOO_NEW) { @@ -3878,7 +3883,7 @@ bool CWallet::MigrateToSQLite(bilingual_str& error) bool began = batch->TxnBegin(); assert(began); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution. for (const auto& [key, value] : records) { - if (!batch->Write(MakeUCharSpan(key), MakeUCharSpan(value))) { + if (!batch->Write(Span{key}, Span{value})) { batch->TxnAbort(); m_database->Close(); fs::remove(m_database->Filename()); @@ -4026,7 +4031,7 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error) return false; } } else { - // Labels for everything else (send) should be cloned to all + // Labels for everything else ("send") should be cloned to all if (data.watchonly_wallet) { LOCK(data.watchonly_wallet->cs_wallet); // Add to the watchonly. Preserve the labels, purpose, and change-ness @@ -4035,7 +4040,6 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error) if (!addr_pair.second.IsChange()) { data.watchonly_wallet->m_address_book[addr_pair.first].SetLabel(label); } - continue; } if (data.solvable_wallet) { LOCK(data.solvable_wallet->cs_wallet); @@ -4045,7 +4049,6 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error) if (!addr_pair.second.IsChange()) { data.solvable_wallet->m_address_book[addr_pair.first].SetLabel(label); } - continue; } } } @@ -4056,10 +4059,10 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error) WalletBatch batch{wallet.GetDatabase()}; for (const auto& [destination, addr_book_data] : wallet.m_address_book) { auto address{EncodeDestination(destination)}; - auto label{addr_book_data.GetLabel()}; - // don't bother writing default values (unknown purpose, empty label) + std::optional<std::string> label = addr_book_data.IsChange() ? std::nullopt : std::make_optional(addr_book_data.GetLabel()); + // don't bother writing default values (unknown purpose) if (addr_book_data.purpose) batch.WritePurpose(address, PurposeToString(*addr_book_data.purpose)); - if (!label.empty()) batch.WriteName(address, label); + if (label) batch.WriteName(address, *label); } }; if (data.watchonly_wallet) persist_address_book(*data.watchonly_wallet); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 34fe8ab17f..8212c04464 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -11,6 +11,7 @@ #include <serialize.h> #include <sync.h> #include <util/bip32.h> +#include <util/check.h> #include <util/fs.h> #include <util/time.h> #include <util/translation.h> @@ -297,426 +298,598 @@ bool WalletBatch::EraseLockedUTXO(const COutPoint& output) return EraseIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n))); } -class CWalletScanState { -public: - unsigned int nKeys{0}; - unsigned int nCKeys{0}; - unsigned int nWatchKeys{0}; - unsigned int nKeyMeta{0}; - unsigned int m_unknown_records{0}; - bool fIsEncrypted{false}; - bool fAnyUnordered{false}; - std::vector<uint256> vWalletUpgrade; - std::map<OutputType, uint256> m_active_external_spks; - std::map<OutputType, uint256> m_active_internal_spks; - std::map<uint256, DescriptorCache> m_descriptor_caches; - std::map<std::pair<uint256, CKeyID>, CKey> m_descriptor_keys; - std::map<std::pair<uint256, CKeyID>, std::pair<CPubKey, std::vector<unsigned char>>> m_descriptor_crypt_keys; - std::map<uint160, CHDChain> m_hd_chains; - bool tx_corrupt{false}; - bool descriptor_unknown{false}; - bool unexpected_legacy_entry{false}; - - CWalletScanState() = default; -}; - -static bool -ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, - CWalletScanState &wss, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr) { + LOCK(pwallet->cs_wallet); try { - // Unserialize - // Taking advantage of the fact that pair serialization - // is just the two items serialized one after the other - ssKey >> strType; - // If we have a filter, check if this matches the filter - if (filter_fn && !filter_fn(strType)) { - return true; - } - // Legacy entries in descriptor wallets are not allowed, abort immediately - if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) && DBKeys::LEGACY_TYPES.count(strType) > 0) { - wss.unexpected_legacy_entry = true; + CPubKey vchPubKey; + ssKey >> vchPubKey; + if (!vchPubKey.IsValid()) + { + strErr = "Error reading wallet database: CPubKey corrupt"; return false; } - if (strType == DBKeys::NAME) { - std::string strAddress; - ssKey >> strAddress; - std::string label; - ssValue >> label; - pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label); - } else if (strType == DBKeys::PURPOSE) { - std::string strAddress; - ssKey >> strAddress; - std::string purpose_str; - ssValue >> purpose_str; - std::optional<AddressPurpose> purpose{PurposeFromString(purpose_str)}; - if (!purpose) { - pwallet->WalletLogPrintf("Warning: nonstandard purpose string '%s' for address '%s'\n", purpose_str, strAddress); - } - pwallet->m_address_book[DecodeDestination(strAddress)].purpose = purpose; - } else if (strType == DBKeys::TX) { - uint256 hash; - ssKey >> hash; - // LoadToWallet call below creates a new CWalletTx that fill_wtx - // callback fills with transaction metadata. - auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) { - if(!new_tx) { - // There's some corruption here since the tx we just tried to load was already in the wallet. - // We don't consider this type of corruption critical, and can fix it by removing tx data and - // rescanning. - wss.tx_corrupt = true; - return false; - } - ssValue >> wtx; - if (wtx.GetHash() != hash) - return false; + CKey key; + CPrivKey pkey; + uint256 hash; + + ssValue >> pkey; + + // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey] + // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key + // using EC operations as a checksum. + // Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while + // remaining backwards-compatible. + try + { + ssValue >> hash; + } + catch (const std::ios_base::failure&) {} - // Undo serialize changes in 31600 - if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) - { - if (!ssValue.empty()) - { - uint8_t fTmp; - uint8_t fUnused; - std::string unused_string; - ssValue >> fTmp >> fUnused >> unused_string; - strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s", - wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString()); - wtx.fTimeReceivedIsTxTime = fTmp; - } - else - { - strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString()); - wtx.fTimeReceivedIsTxTime = 0; - } - wss.vWalletUpgrade.push_back(hash); - } + bool fSkipCheck = false; - if (wtx.nOrderPos == -1) - wss.fAnyUnordered = true; + if (!hash.IsNull()) + { + // hash pubkey/privkey to accelerate wallet load + std::vector<unsigned char> vchKey; + vchKey.reserve(vchPubKey.size() + pkey.size()); + vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); + vchKey.insert(vchKey.end(), pkey.begin(), pkey.end()); - return true; - }; - if (!pwallet->LoadToWallet(hash, fill_wtx)) { - return false; - } - } else if (strType == DBKeys::WATCHS) { - wss.nWatchKeys++; - CScript script; - ssKey >> script; - uint8_t fYes; - ssValue >> fYes; - if (fYes == '1') { - pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script); - } - } else if (strType == DBKeys::KEY) { - CPubKey vchPubKey; - ssKey >> vchPubKey; - if (!vchPubKey.IsValid()) + if (Hash(vchKey) != hash) { - strErr = "Error reading wallet database: CPubKey corrupt"; + strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; return false; } - CKey key; - CPrivKey pkey; - uint256 hash; - wss.nKeys++; - ssValue >> pkey; + fSkipCheck = true; + } - // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey] - // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key - // using EC operations as a checksum. - // Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while - // remaining backwards-compatible. - try - { - ssValue >> hash; + if (!key.Load(pkey, vchPubKey, fSkipCheck)) + { + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; + } + if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey)) + { + strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed"; + return false; + } + } catch (const std::exception& e) { + if (strErr.empty()) { + strErr = e.what(); + } + return false; + } + return true; +} + +bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr) +{ + LOCK(pwallet->cs_wallet); + try { + CPubKey vchPubKey; + ssKey >> vchPubKey; + if (!vchPubKey.IsValid()) + { + strErr = "Error reading wallet database: CPubKey corrupt"; + return false; + } + std::vector<unsigned char> vchPrivKey; + ssValue >> vchPrivKey; + + // Get the checksum and check it + bool checksum_valid = false; + if (!ssValue.eof()) { + uint256 checksum; + ssValue >> checksum; + if (!(checksum_valid = Hash(vchPrivKey) == checksum)) { + strErr = "Error reading wallet database: Encrypted key corrupt"; + return false; } - catch (const std::ios_base::failure&) {} + } - bool fSkipCheck = false; + if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid)) + { + strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed"; + return false; + } + } catch (const std::exception& e) { + if (strErr.empty()) { + strErr = e.what(); + } + return false; + } + return true; +} - if (!hash.IsNull()) - { - // hash pubkey/privkey to accelerate wallet load - std::vector<unsigned char> vchKey; - vchKey.reserve(vchPubKey.size() + pkey.size()); - vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); - vchKey.insert(vchKey.end(), pkey.begin(), pkey.end()); +bool LoadEncryptionKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr) +{ + LOCK(pwallet->cs_wallet); + try { + // Master encryption key is loaded into only the wallet and not any of the ScriptPubKeyMans. + unsigned int nID; + ssKey >> nID; + CMasterKey kMasterKey; + ssValue >> kMasterKey; + if(pwallet->mapMasterKeys.count(nID) != 0) + { + strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); + return false; + } + pwallet->mapMasterKeys[nID] = kMasterKey; + if (pwallet->nMasterKeyMaxID < nID) + pwallet->nMasterKeyMaxID = nID; - if (Hash(vchKey) != hash) - { - strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; - return false; - } + } catch (const std::exception& e) { + if (strErr.empty()) { + strErr = e.what(); + } + return false; + } + return true; +} - fSkipCheck = true; - } +bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr) +{ + LOCK(pwallet->cs_wallet); + try { + CHDChain chain; + ssValue >> chain; + pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain); + } catch (const std::exception& e) { + if (strErr.empty()) { + strErr = e.what(); + } + return false; + } + return true; +} - if (!key.Load(pkey, vchPubKey, fSkipCheck)) - { - strErr = "Error reading wallet database: CPrivKey corrupt"; - return false; - } - if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey)) - { - strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed"; - return false; - } - } else if (strType == DBKeys::MASTER_KEY) { - // Master encryption key is loaded into only the wallet and not any of the ScriptPubKeyMans. - unsigned int nID; - ssKey >> nID; - CMasterKey kMasterKey; - ssValue >> kMasterKey; - if(pwallet->mapMasterKeys.count(nID) != 0) - { - strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); - return false; - } - pwallet->mapMasterKeys[nID] = kMasterKey; - if (pwallet->nMasterKeyMaxID < nID) - pwallet->nMasterKeyMaxID = nID; - } else if (strType == DBKeys::CRYPTED_KEY) { - CPubKey vchPubKey; - ssKey >> vchPubKey; - if (!vchPubKey.IsValid()) - { - strErr = "Error reading wallet database: CPubKey corrupt"; - return false; +static DBErrors LoadMinVersion(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +{ + AssertLockHeld(pwallet->cs_wallet); + int nMinVersion = 0; + if (batch.Read(DBKeys::MINVERSION, nMinVersion)) { + if (nMinVersion > FEATURE_LATEST) + return DBErrors::TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + return DBErrors::LOAD_OK; +} + +static DBErrors LoadWalletFlags(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +{ + AssertLockHeld(pwallet->cs_wallet); + uint64_t flags; + if (batch.Read(DBKeys::FLAGS, flags)) { + if (!pwallet->LoadWalletFlags(flags)) { + pwallet->WalletLogPrintf("Error reading wallet database: Unknown non-tolerable wallet flags found\n"); + return DBErrors::TOO_NEW; + } + } + return DBErrors::LOAD_OK; +} + +struct LoadResult +{ + DBErrors m_result{DBErrors::LOAD_OK}; + int m_records{0}; +}; + +using LoadFunc = std::function<DBErrors(CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err)>; +static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std::string& key, DataStream& prefix, LoadFunc load_func) +{ + LoadResult result; + DataStream ssKey; + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + + Assume(!prefix.empty()); + std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix); + if (!cursor) { + pwallet->WalletLogPrintf("Error getting database cursor for '%s' records\n", key); + result.m_result = DBErrors::CORRUPT; + return result; + } + + while (true) { + DatabaseCursor::Status status = cursor->Next(ssKey, ssValue); + if (status == DatabaseCursor::Status::DONE) { + break; + } else if (status == DatabaseCursor::Status::FAIL) { + pwallet->WalletLogPrintf("Error reading next '%s' record for wallet database\n", key); + result.m_result = DBErrors::CORRUPT; + return result; + } + std::string type; + ssKey >> type; + assert(type == key); + std::string error; + DBErrors record_res = load_func(pwallet, ssKey, ssValue, error); + if (record_res != DBErrors::LOAD_OK) { + pwallet->WalletLogPrintf("%s\n", error); + } + result.m_result = std::max(result.m_result, record_res); + ++result.m_records; + } + return result; +} + +static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std::string& key, LoadFunc load_func) +{ + DataStream prefix; + prefix << key; + return LoadRecords(pwallet, batch, key, prefix, load_func); +} + +static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +{ + AssertLockHeld(pwallet->cs_wallet); + DBErrors result = DBErrors::LOAD_OK; + + // Make sure descriptor wallets don't have any legacy records + if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { + for (const auto& type : DBKeys::LEGACY_TYPES) { + DataStream key; + CDataStream value(SER_DISK, CLIENT_VERSION); + + DataStream prefix; + prefix << type; + std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix); + if (!cursor) { + pwallet->WalletLogPrintf("Error getting database cursor for '%s' records\n", type); + return DBErrors::CORRUPT; } - std::vector<unsigned char> vchPrivKey; - ssValue >> vchPrivKey; - - // Get the checksum and check it - bool checksum_valid = false; - if (!ssValue.eof()) { - uint256 checksum; - ssValue >> checksum; - if (!(checksum_valid = Hash(vchPrivKey) == checksum)) { - strErr = "Error reading wallet database: Encrypted key corrupt"; - return false; - } + + DatabaseCursor::Status status = cursor->Next(key, value); + if (status != DatabaseCursor::Status::DONE) { + pwallet->WalletLogPrintf("Error: Unexpected legacy entry found in descriptor wallet %s. The wallet might have been tampered with or created with malicious intent.\n", pwallet->GetName()); + return DBErrors::UNEXPECTED_LEGACY_ENTRY; } + } - wss.nCKeys++; + return DBErrors::LOAD_OK; + } - if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid)) - { - strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed"; - return false; - } - wss.fIsEncrypted = true; - } else if (strType == DBKeys::KEYMETA) { - CPubKey vchPubKey; - ssKey >> vchPubKey; - CKeyMetadata keyMeta; - ssValue >> keyMeta; - wss.nKeyMeta++; - pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta); - - // Extract some CHDChain info from this metadata if it has any - if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) { - // Get the path from the key origin or from the path string - // Not applicable when path is "s" or "m" as those indicate a seed - // See https://github.com/bitcoin/bitcoin/pull/12924 - bool internal = false; - uint32_t index = 0; - if (keyMeta.hdKeypath != "s" && keyMeta.hdKeypath != "m") { - std::vector<uint32_t> path; - if (keyMeta.has_key_origin) { - // We have a key origin, so pull it from its path vector - path = keyMeta.key_origin.path; - } else { - // No key origin, have to parse the string - if (!ParseHDKeypath(keyMeta.hdKeypath, path)) { - strErr = "Error reading wallet database: keymeta with invalid HD keypath"; - return false; - } - } + // Load HD Chain + // Note: There should only be one HDCHAIN record with no data following the type + LoadResult hd_chain_res = LoadRecords(pwallet, batch, DBKeys::HDCHAIN, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + return LoadHDChain(pwallet, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, hd_chain_res.m_result); + + // Load unencrypted keys + LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::KEY, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + return LoadKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, key_res.m_result); + + // Load encrypted keys + LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::CRYPTED_KEY, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + return LoadCryptedKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, ckey_res.m_result); + + // Load scripts + LoadResult script_res = LoadRecords(pwallet, batch, DBKeys::CSCRIPT, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) { + uint160 hash; + key >> hash; + CScript script; + value >> script; + if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script)) + { + strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed"; + return DBErrors::NONCRITICAL_ERROR; + } + return DBErrors::LOAD_OK; + }); + result = std::max(result, script_res.m_result); + + // Check whether rewrite is needed + if (ckey_res.m_records > 0) { + // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: + if (last_client == 40000 || last_client == 50000) result = std::max(result, DBErrors::NEED_REWRITE); + } - // Extract the index and internal from the path - // Path string is m/0'/k'/i' - // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit - // k == 0 for external, 1 for internal. i is the index - if (path.size() != 3) { - strErr = "Error reading wallet database: keymeta found with unexpected path"; - return false; - } - if (path[0] != 0x80000000) { - strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]); - return false; - } - if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) { - strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]); - return false; - } - if ((path[2] & 0x80000000) == 0) { - strErr = strprintf("Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]); - return false; + // Load keymeta + std::map<uint160, CHDChain> hd_chains; + LoadResult keymeta_res = LoadRecords(pwallet, batch, DBKeys::KEYMETA, + [&hd_chains] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) { + CPubKey vchPubKey; + key >> vchPubKey; + CKeyMetadata keyMeta; + value >> keyMeta; + pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta); + + // Extract some CHDChain info from this metadata if it has any + if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) { + // Get the path from the key origin or from the path string + // Not applicable when path is "s" or "m" as those indicate a seed + // See https://github.com/bitcoin/bitcoin/pull/12924 + bool internal = false; + uint32_t index = 0; + if (keyMeta.hdKeypath != "s" && keyMeta.hdKeypath != "m") { + std::vector<uint32_t> path; + if (keyMeta.has_key_origin) { + // We have a key origin, so pull it from its path vector + path = keyMeta.key_origin.path; + } else { + // No key origin, have to parse the string + if (!ParseHDKeypath(keyMeta.hdKeypath, path)) { + strErr = "Error reading wallet database: keymeta with invalid HD keypath"; + return DBErrors::NONCRITICAL_ERROR; } - internal = path[1] == (1 | 0x80000000); - index = path[2] & ~0x80000000; } - // Insert a new CHDChain, or get the one that already exists - auto ins = wss.m_hd_chains.emplace(keyMeta.hd_seed_id, CHDChain()); - CHDChain& chain = ins.first->second; - if (ins.second) { - // For new chains, we want to default to VERSION_HD_BASE until we see an internal - chain.nVersion = CHDChain::VERSION_HD_BASE; - chain.seed_id = keyMeta.hd_seed_id; + // Extract the index and internal from the path + // Path string is m/0'/k'/i' + // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit + // k == 0 for external, 1 for internal. i is the index + if (path.size() != 3) { + strErr = "Error reading wallet database: keymeta found with unexpected path"; + return DBErrors::NONCRITICAL_ERROR; } - if (internal) { - chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT; - chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index + 1); - } else { - chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index + 1); + if (path[0] != 0x80000000) { + strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]); + return DBErrors::NONCRITICAL_ERROR; } + if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) { + strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]); + return DBErrors::NONCRITICAL_ERROR; + } + if ((path[2] & 0x80000000) == 0) { + strErr = strprintf("Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]); + return DBErrors::NONCRITICAL_ERROR; + } + internal = path[1] == (1 | 0x80000000); + index = path[2] & ~0x80000000; } - } else if (strType == DBKeys::WATCHMETA) { - CScript script; - ssKey >> script; - CKeyMetadata keyMeta; - ssValue >> keyMeta; - wss.nKeyMeta++; - pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta); - } else if (strType == DBKeys::DEFAULTKEY) { - // We don't want or need the default key, but if there is one set, - // we want to make sure that it is valid so that we can detect corruption - CPubKey vchPubKey; - ssValue >> vchPubKey; - if (!vchPubKey.IsValid()) { - strErr = "Error reading wallet database: Default Key corrupt"; - return false; - } - } else if (strType == DBKeys::POOL) { - int64_t nIndex; - ssKey >> nIndex; - CKeyPool keypool; - ssValue >> keypool; - - pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool); - } else if (strType == DBKeys::CSCRIPT) { - uint160 hash; - ssKey >> hash; - CScript script; - ssValue >> script; - if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script)) - { - strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed"; - return false; + + // Insert a new CHDChain, or get the one that already exists + auto [ins, inserted] = hd_chains.emplace(keyMeta.hd_seed_id, CHDChain()); + CHDChain& chain = ins->second; + if (inserted) { + // For new chains, we want to default to VERSION_HD_BASE until we see an internal + chain.nVersion = CHDChain::VERSION_HD_BASE; + chain.seed_id = keyMeta.hd_seed_id; } - } else if (strType == DBKeys::ORDERPOSNEXT) { - ssValue >> pwallet->nOrderPosNext; - } else if (strType == DBKeys::DESTDATA) { - std::string strAddress, strKey, strValue; - ssKey >> strAddress; - ssKey >> strKey; - ssValue >> strValue; - const CTxDestination& dest{DecodeDestination(strAddress)}; - if (strKey.compare("used") == 0) { - // Load "used" key indicating if an IsMine address has - // previously been spent from with avoid_reuse option enabled. - // The strValue is not used for anything currently, but could - // hold more information in the future. Current values are just - // "1" or "p" for present (which was written prior to - // f5ba424cd44619d9b9be88b8593d69a7ba96db26). - pwallet->LoadAddressPreviouslySpent(dest); - } else if (strKey.compare(0, 2, "rr") == 0) { - // Load "rr##" keys where ## is a decimal number, and strValue - // is a serialized RecentRequestEntry object. - pwallet->LoadAddressReceiveRequest(dest, strKey.substr(2), strValue); + if (internal) { + chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT; + chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index + 1); + } else { + chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index + 1); } - } else if (strType == DBKeys::HDCHAIN) { - CHDChain chain; - ssValue >> chain; - pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain); - } else if (strType == DBKeys::OLD_KEY) { - strErr = "Found unsupported 'wkey' record, try loading with version 0.18"; - return false; - } else if (strType == DBKeys::ACTIVEEXTERNALSPK || strType == DBKeys::ACTIVEINTERNALSPK) { - uint8_t type; - ssKey >> type; - uint256 id; - ssValue >> id; + } + return DBErrors::LOAD_OK; + }); + result = std::max(result, keymeta_res.m_result); - bool internal = strType == DBKeys::ACTIVEINTERNALSPK; - auto& spk_mans = internal ? wss.m_active_internal_spks : wss.m_active_external_spks; - if (spk_mans.count(static_cast<OutputType>(type)) > 0) { - strErr = "Multiple ScriptPubKeyMans specified for a single type"; - return false; - } - spk_mans[static_cast<OutputType>(type)] = id; - } else if (strType == DBKeys::WALLETDESCRIPTOR) { - uint256 id; - ssKey >> id; - WalletDescriptor desc; - try { - ssValue >> desc; - } catch (const std::ios_base::failure& e) { - strErr = e.what(); - wss.descriptor_unknown = true; - return false; + // Set inactive chains + if (!hd_chains.empty()) { + LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan(); + if (legacy_spkm) { + for (const auto& [hd_seed_id, chain] : hd_chains) { + if (hd_seed_id != legacy_spkm->GetHDChain().seed_id) { + legacy_spkm->AddInactiveHDChain(chain); + } } - if (wss.m_descriptor_caches.count(id) == 0) { - wss.m_descriptor_caches[id] = DescriptorCache(); + } else { + pwallet->WalletLogPrintf("Inactive HD Chains found but no Legacy ScriptPubKeyMan\n"); + result = DBErrors::CORRUPT; + } + } + + // Load watchonly scripts + LoadResult watch_script_res = LoadRecords(pwallet, batch, DBKeys::WATCHS, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + CScript script; + key >> script; + uint8_t fYes; + value >> fYes; + if (fYes == '1') { + pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script); + } + return DBErrors::LOAD_OK; + }); + result = std::max(result, watch_script_res.m_result); + + // Load watchonly meta + LoadResult watch_meta_res = LoadRecords(pwallet, batch, DBKeys::WATCHMETA, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + CScript script; + key >> script; + CKeyMetadata keyMeta; + value >> keyMeta; + pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta); + return DBErrors::LOAD_OK; + }); + result = std::max(result, watch_meta_res.m_result); + + // Load keypool + LoadResult pool_res = LoadRecords(pwallet, batch, DBKeys::POOL, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + int64_t nIndex; + key >> nIndex; + CKeyPool keypool; + value >> keypool; + pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool); + return DBErrors::LOAD_OK; + }); + result = std::max(result, pool_res.m_result); + + // Deal with old "wkey" and "defaultkey" records. + // These are not actually loaded, but we need to check for them + + // We don't want or need the default key, but if there is one set, + // we want to make sure that it is valid so that we can detect corruption + // Note: There should only be one DEFAULTKEY with nothing trailing the type + LoadResult default_key_res = LoadRecords(pwallet, batch, DBKeys::DEFAULTKEY, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + CPubKey default_pubkey; + try { + value >> default_pubkey; + } catch (const std::exception& e) { + err = e.what(); + return DBErrors::CORRUPT; + } + if (!default_pubkey.IsValid()) { + err = "Error reading wallet database: Default Key corrupt"; + return DBErrors::CORRUPT; + } + return DBErrors::LOAD_OK; + }); + result = std::max(result, default_key_res.m_result); + + // "wkey" records are unsupported, if we see any, throw an error + LoadResult wkey_res = LoadRecords(pwallet, batch, DBKeys::OLD_KEY, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + err = "Found unsupported 'wkey' record, try loading with version 0.18"; + return DBErrors::LOAD_FAIL; + }); + result = std::max(result, wkey_res.m_result); + + if (result <= DBErrors::NONCRITICAL_ERROR) { + // Only do logging and time first key update if there were no critical errors + pwallet->WalletLogPrintf("Legacy Wallet Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total.\n", + key_res.m_records, ckey_res.m_records, keymeta_res.m_records, key_res.m_records + ckey_res.m_records); + + // nTimeFirstKey is only reliable if all keys have metadata + if (pwallet->IsLegacy() && (key_res.m_records + ckey_res.m_records + watch_script_res.m_records) != (keymeta_res.m_records + watch_meta_res.m_records)) { + auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan(); + if (spk_man) { + LOCK(spk_man->cs_KeyStore); + spk_man->UpdateTimeFirstKey(1); } - pwallet->LoadDescriptorScriptPubKeyMan(id, desc); - } else if (strType == DBKeys::WALLETDESCRIPTORCACHE) { + } + } + + return result; +} + +template<typename... Args> +static DataStream PrefixStream(const Args&... args) +{ + DataStream prefix; + SerializeMany(prefix, args...); + return prefix; +} + +static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +{ + AssertLockHeld(pwallet->cs_wallet); + + // Load descriptor record + int num_keys = 0; + int num_ckeys= 0; + LoadResult desc_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTOR, + [&batch, &num_keys, &num_ckeys, &last_client] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) { + DBErrors result = DBErrors::LOAD_OK; + + uint256 id; + key >> id; + WalletDescriptor desc; + try { + value >> desc; + } catch (const std::ios_base::failure& e) { + strErr = strprintf("Error: Unrecognized descriptor found in wallet %s. ", pwallet->GetName()); + strErr += (last_client > CLIENT_VERSION) ? "The wallet might had been created on a newer version. " : + "The database might be corrupted or the software version is not compatible with one of your wallet descriptors. "; + strErr += "Please try running the latest software version"; + // Also include error details + strErr = strprintf("%s\nDetails: %s", strErr, e.what()); + return DBErrors::UNKNOWN_DESCRIPTOR; + } + pwallet->LoadDescriptorScriptPubKeyMan(id, desc); + + // Prior to doing anything with this spkm, verify ID compatibility + if (id != pwallet->GetDescriptorScriptPubKeyMan(desc)->GetID()) { + strErr = "The descriptor ID calculated by the wallet differs from the one in DB"; + return DBErrors::CORRUPT; + } + + DescriptorCache cache; + + // Get key cache for this descriptor + DataStream prefix = PrefixStream(DBKeys::WALLETDESCRIPTORCACHE, id); + LoadResult key_cache_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORCACHE, prefix, + [&id, &cache] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { bool parent = true; uint256 desc_id; uint32_t key_exp_index; uint32_t der_index; - ssKey >> desc_id; - ssKey >> key_exp_index; + key >> desc_id; + assert(desc_id == id); + key >> key_exp_index; // if the der_index exists, it's a derived xpub try { - ssKey >> der_index; + key >> der_index; parent = false; } catch (...) {} std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE); - ssValue >> ser_xpub; + value >> ser_xpub; CExtPubKey xpub; xpub.Decode(ser_xpub.data()); if (parent) { - wss.m_descriptor_caches[desc_id].CacheParentExtPubKey(key_exp_index, xpub); + cache.CacheParentExtPubKey(key_exp_index, xpub); } else { - wss.m_descriptor_caches[desc_id].CacheDerivedExtPubKey(key_exp_index, der_index, xpub); + cache.CacheDerivedExtPubKey(key_exp_index, der_index, xpub); } - } else if (strType == DBKeys::WALLETDESCRIPTORLHCACHE) { + return DBErrors::LOAD_OK; + }); + result = std::max(result, key_cache_res.m_result); + + // Get last hardened cache for this descriptor + prefix = PrefixStream(DBKeys::WALLETDESCRIPTORLHCACHE, id); + LoadResult lh_cache_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORLHCACHE, prefix, + [&id, &cache] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { uint256 desc_id; uint32_t key_exp_index; - ssKey >> desc_id; - ssKey >> key_exp_index; + key >> desc_id; + assert(desc_id == id); + key >> key_exp_index; std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE); - ssValue >> ser_xpub; + value >> ser_xpub; CExtPubKey xpub; xpub.Decode(ser_xpub.data()); - wss.m_descriptor_caches[desc_id].CacheLastHardenedExtPubKey(key_exp_index, xpub); - } else if (strType == DBKeys::WALLETDESCRIPTORKEY) { + cache.CacheLastHardenedExtPubKey(key_exp_index, xpub); + return DBErrors::LOAD_OK; + }); + result = std::max(result, lh_cache_res.m_result); + + // Set the cache for this descriptor + auto spk_man = (DescriptorScriptPubKeyMan*)pwallet->GetScriptPubKeyMan(id); + assert(spk_man); + spk_man->SetCache(cache); + + // Get unencrypted keys + prefix = PrefixStream(DBKeys::WALLETDESCRIPTORKEY, id); + LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORKEY, prefix, + [&id, &spk_man] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) { uint256 desc_id; CPubKey pubkey; - ssKey >> desc_id; - ssKey >> pubkey; + key >> desc_id; + assert(desc_id == id); + key >> pubkey; if (!pubkey.IsValid()) { - strErr = "Error reading wallet database: CPubKey corrupt"; - return false; + strErr = "Error reading wallet database: descriptor unencrypted key CPubKey corrupt"; + return DBErrors::CORRUPT; } - CKey key; + CKey privkey; CPrivKey pkey; uint256 hash; - wss.nKeys++; - ssValue >> pkey; - ssValue >> hash; + value >> pkey; + value >> hash; // hash pubkey/privkey to accelerate wallet load std::vector<unsigned char> to_hash; @@ -726,77 +899,254 @@ ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, if (Hash(to_hash) != hash) { - strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; - return false; + strErr = "Error reading wallet database: descriptor unencrypted key CPubKey/CPrivKey corrupt"; + return DBErrors::CORRUPT; } - if (!key.Load(pkey, pubkey, true)) + if (!privkey.Load(pkey, pubkey, true)) { - strErr = "Error reading wallet database: CPrivKey corrupt"; - return false; + strErr = "Error reading wallet database: descriptor unencrypted key CPrivKey corrupt"; + return DBErrors::CORRUPT; } - wss.m_descriptor_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), key)); - } else if (strType == DBKeys::WALLETDESCRIPTORCKEY) { + spk_man->AddKey(pubkey.GetID(), privkey); + return DBErrors::LOAD_OK; + }); + result = std::max(result, key_res.m_result); + num_keys = key_res.m_records; + + // Get encrypted keys + prefix = PrefixStream(DBKeys::WALLETDESCRIPTORCKEY, id); + LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORCKEY, prefix, + [&id, &spk_man] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { uint256 desc_id; CPubKey pubkey; - ssKey >> desc_id; - ssKey >> pubkey; + key >> desc_id; + assert(desc_id == id); + key >> pubkey; if (!pubkey.IsValid()) { - strErr = "Error reading wallet database: CPubKey corrupt"; - return false; + err = "Error reading wallet database: descriptor encrypted key CPubKey corrupt"; + return DBErrors::CORRUPT; } std::vector<unsigned char> privkey; - ssValue >> privkey; - wss.nCKeys++; + value >> privkey; - wss.m_descriptor_crypt_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(pubkey, privkey))); - wss.fIsEncrypted = true; - } else if (strType == DBKeys::LOCKED_UTXO) { - uint256 hash; - uint32_t n; - ssKey >> hash; - ssKey >> n; - pwallet->LockCoin(COutPoint(hash, n)); - } else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE && - strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY && - strType != DBKeys::VERSION && strType != DBKeys::SETTINGS && - strType != DBKeys::FLAGS) { - wss.m_unknown_records++; + spk_man->AddCryptedKey(pubkey.GetID(), pubkey, privkey); + return DBErrors::LOAD_OK; + }); + result = std::max(result, ckey_res.m_result); + num_ckeys = ckey_res.m_records; + + return result; + }); + + if (desc_res.m_result <= DBErrors::NONCRITICAL_ERROR) { + // Only log if there are no critical errors + pwallet->WalletLogPrintf("Descriptors: %u, Descriptor Keys: %u plaintext, %u encrypted, %u total.\n", + desc_res.m_records, num_keys, num_ckeys, num_keys + num_ckeys); + } + + return desc_res.m_result; +} + +static DBErrors LoadAddressBookRecords(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +{ + AssertLockHeld(pwallet->cs_wallet); + DBErrors result = DBErrors::LOAD_OK; + + // Load name record + LoadResult name_res = LoadRecords(pwallet, batch, DBKeys::NAME, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { + std::string strAddress; + key >> strAddress; + std::string label; + value >> label; + pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label); + return DBErrors::LOAD_OK; + }); + result = std::max(result, name_res.m_result); + + // Load purpose record + LoadResult purpose_res = LoadRecords(pwallet, batch, DBKeys::PURPOSE, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { + std::string strAddress; + key >> strAddress; + std::string purpose_str; + value >> purpose_str; + std::optional<AddressPurpose> purpose{PurposeFromString(purpose_str)}; + if (!purpose) { + pwallet->WalletLogPrintf("Warning: nonstandard purpose string '%s' for address '%s'\n", purpose_str, strAddress); } - } catch (const std::exception& e) { - if (strErr.empty()) { - strErr = e.what(); + pwallet->m_address_book[DecodeDestination(strAddress)].purpose = purpose; + return DBErrors::LOAD_OK; + }); + result = std::max(result, purpose_res.m_result); + + // Load destination data record + LoadResult dest_res = LoadRecords(pwallet, batch, DBKeys::DESTDATA, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { + std::string strAddress, strKey, strValue; + key >> strAddress; + key >> strKey; + value >> strValue; + const CTxDestination& dest{DecodeDestination(strAddress)}; + if (strKey.compare("used") == 0) { + // Load "used" key indicating if an IsMine address has + // previously been spent from with avoid_reuse option enabled. + // The strValue is not used for anything currently, but could + // hold more information in the future. Current values are just + // "1" or "p" for present (which was written prior to + // f5ba424cd44619d9b9be88b8593d69a7ba96db26). + pwallet->LoadAddressPreviouslySpent(dest); + } else if (strKey.compare(0, 2, "rr") == 0) { + // Load "rr##" keys where ## is a decimal number, and strValue + // is a serialized RecentRequestEntry object. + pwallet->LoadAddressReceiveRequest(dest, strKey.substr(2), strValue); } - return false; - } catch (...) { - if (strErr.empty()) { - strErr = "Caught unknown exception in ReadKeyValue"; + return DBErrors::LOAD_OK; + }); + result = std::max(result, dest_res.m_result); + + return result; +} + +static DBErrors LoadTxRecords(CWallet* pwallet, DatabaseBatch& batch, std::vector<uint256>& upgraded_txs, bool& any_unordered) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +{ + AssertLockHeld(pwallet->cs_wallet); + DBErrors result = DBErrors::LOAD_OK; + + // Load tx record + any_unordered = false; + LoadResult tx_res = LoadRecords(pwallet, batch, DBKeys::TX, + [&any_unordered, &upgraded_txs] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { + DBErrors result = DBErrors::LOAD_OK; + uint256 hash; + key >> hash; + // LoadToWallet call below creates a new CWalletTx that fill_wtx + // callback fills with transaction metadata. + auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) { + if(!new_tx) { + // There's some corruption here since the tx we just tried to load was already in the wallet. + err = "Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning."; + result = DBErrors::CORRUPT; + return false; + } + value >> wtx; + if (wtx.GetHash() != hash) + return false; + + // Undo serialize changes in 31600 + if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) + { + if (!value.empty()) + { + uint8_t fTmp; + uint8_t fUnused; + std::string unused_string; + value >> fTmp >> fUnused >> unused_string; + pwallet->WalletLogPrintf("LoadWallet() upgrading tx ver=%d %d %s\n", + wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString()); + wtx.fTimeReceivedIsTxTime = fTmp; + } + else + { + pwallet->WalletLogPrintf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString()); + wtx.fTimeReceivedIsTxTime = 0; + } + upgraded_txs.push_back(hash); + } + + if (wtx.nOrderPos == -1) + any_unordered = true; + + return true; + }; + if (!pwallet->LoadToWallet(hash, fill_wtx)) { + // Use std::max as fill_wtx may have already set result to CORRUPT + result = std::max(result, DBErrors::NEED_RESCAN); } - return false; - } - return true; + return result; + }); + result = std::max(result, tx_res.m_result); + + // Load locked utxo record + LoadResult locked_utxo_res = LoadRecords(pwallet, batch, DBKeys::LOCKED_UTXO, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { + uint256 hash; + uint32_t n; + key >> hash; + key >> n; + pwallet->LockCoin(COutPoint(hash, n)); + return DBErrors::LOAD_OK; + }); + result = std::max(result, locked_utxo_res.m_result); + + // Load orderposnext record + // Note: There should only be one ORDERPOSNEXT record with nothing trailing the type + LoadResult order_pos_res = LoadRecords(pwallet, batch, DBKeys::ORDERPOSNEXT, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { + try { + value >> pwallet->nOrderPosNext; + } catch (const std::exception& e) { + err = e.what(); + return DBErrors::NONCRITICAL_ERROR; + } + return DBErrors::LOAD_OK; + }); + result = std::max(result, order_pos_res.m_result); + + return result; } -bool ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn) +static DBErrors LoadActiveSPKMs(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { - CWalletScanState dummy_wss; - LOCK(pwallet->cs_wallet); - return ReadKeyValue(pwallet, ssKey, ssValue, dummy_wss, strType, strErr, filter_fn); + AssertLockHeld(pwallet->cs_wallet); + DBErrors result = DBErrors::LOAD_OK; + + // Load spk records + std::set<std::pair<OutputType, bool>> seen_spks; + for (const auto& spk_key : {DBKeys::ACTIVEEXTERNALSPK, DBKeys::ACTIVEINTERNALSPK}) { + LoadResult spkm_res = LoadRecords(pwallet, batch, spk_key, + [&seen_spks, &spk_key] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) { + uint8_t output_type; + key >> output_type; + uint256 id; + value >> id; + + bool internal = spk_key == DBKeys::ACTIVEINTERNALSPK; + auto [it, insert] = seen_spks.emplace(static_cast<OutputType>(output_type), internal); + if (!insert) { + strErr = "Multiple ScriptpubKeyMans specified for a single type"; + return DBErrors::CORRUPT; + } + pwallet->LoadActiveScriptPubKeyMan(id, static_cast<OutputType>(output_type), /*internal=*/internal); + return DBErrors::LOAD_OK; + }); + result = std::max(result, spkm_res.m_result); + } + return result; } -bool WalletBatch::IsKeyType(const std::string& strType) +static DBErrors LoadDecryptionKeys(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { - return (strType == DBKeys::KEY || - strType == DBKeys::MASTER_KEY || strType == DBKeys::CRYPTED_KEY); + AssertLockHeld(pwallet->cs_wallet); + + // Load decryption key (mkey) records + LoadResult mkey_res = LoadRecords(pwallet, batch, DBKeys::MASTER_KEY, + [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) { + if (!LoadEncryptionKey(pwallet, key, value, err)) { + return DBErrors::CORRUPT; + } + return DBErrors::LOAD_OK; + }); + return mkey_res.m_result; } DBErrors WalletBatch::LoadWallet(CWallet* pwallet) { - CWalletScanState wss; - bool fNoncriticalErrors = false; - bool rescan_required = false; DBErrors result = DBErrors::LOAD_OK; + bool any_unordered = false; + std::vector<uint256> upgraded_txs; LOCK(pwallet->cs_wallet); @@ -806,22 +1156,11 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) pwallet->WalletLogPrintf("Wallet file version = %d, last client version = %d\n", pwallet->GetVersion(), last_client); try { - int nMinVersion = 0; - if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) { - if (nMinVersion > FEATURE_LATEST) - return DBErrors::TOO_NEW; - pwallet->LoadMinVersion(nMinVersion); - } + if ((result = LoadMinVersion(pwallet, *m_batch)) != DBErrors::LOAD_OK) return result; // Load wallet flags, so they are known when processing other records. // The FLAGS key is absent during wallet creation. - uint64_t flags; - if (m_batch->Read(DBKeys::FLAGS, flags)) { - if (!pwallet->LoadWalletFlags(flags)) { - pwallet->WalletLogPrintf("Error reading wallet database: Unknown non-tolerable wallet flags found\n"); - return DBErrors::CORRUPT; - } - } + if ((result = LoadWalletFlags(pwallet, *m_batch)) != DBErrors::LOAD_OK) return result; #ifndef ENABLE_EXTERNAL_SIGNER if (pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) { @@ -830,101 +1169,31 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) } #endif - // Get cursor - std::unique_ptr<DatabaseCursor> cursor = m_batch->GetNewCursor(); - if (!cursor) - { - pwallet->WalletLogPrintf("Error getting wallet database cursor\n"); - return DBErrors::CORRUPT; - } - - while (true) - { - // Read next record - DataStream ssKey{}; - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - DatabaseCursor::Status status = cursor->Next(ssKey, ssValue); - if (status == DatabaseCursor::Status::DONE) { - break; - } else if (status == DatabaseCursor::Status::FAIL) { - cursor.reset(); - pwallet->WalletLogPrintf("Error reading next record from wallet database\n"); - return DBErrors::CORRUPT; - } + // Load legacy wallet keys + result = std::max(LoadLegacyWalletRecords(pwallet, *m_batch, last_client), result); - // Try to be tolerant of single corrupt records: - std::string strType, strErr; - if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) - { - if (wss.unexpected_legacy_entry) { - strErr = strprintf("Error: Unexpected legacy entry found in descriptor wallet %s. ", pwallet->GetName()); - strErr += "The wallet might have been tampered with or created with malicious intent."; - pwallet->WalletLogPrintf("%s\n", strErr); - return DBErrors::UNEXPECTED_LEGACY_ENTRY; - } - // losing keys is considered a catastrophic error, anything else - // we assume the user can live with: - if (IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) { - result = DBErrors::CORRUPT; - } else if (strType == DBKeys::FLAGS) { - // reading the wallet flags can only fail if unknown flags are present - result = DBErrors::TOO_NEW; - } else if (wss.tx_corrupt) { - pwallet->WalletLogPrintf("Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.\n"); - // Set tx_corrupt back to false so that the error is only printed once (per corrupt tx) - wss.tx_corrupt = false; - result = DBErrors::CORRUPT; - } else if (wss.descriptor_unknown) { - strErr = strprintf("Error: Unrecognized descriptor found in wallet %s. ", pwallet->GetName()); - strErr += (last_client > CLIENT_VERSION) ? "The wallet might had been created on a newer version. " : - "The database might be corrupted or the software version is not compatible with one of your wallet descriptors. "; - strErr += "Please try running the latest software version"; - pwallet->WalletLogPrintf("%s\n", strErr); - return DBErrors::UNKNOWN_DESCRIPTOR; - } else { - // Leave other errors alone, if we try to fix them we might make things worse. - fNoncriticalErrors = true; // ... but do warn the user there is something wrong. - if (strType == DBKeys::TX) - // Rescan if there is a bad transaction record: - rescan_required = true; - } - } - if (!strErr.empty()) - pwallet->WalletLogPrintf("%s\n", strErr); - } - } catch (...) { - result = DBErrors::CORRUPT; - } + // Load descriptors + result = std::max(LoadDescriptorWalletRecords(pwallet, *m_batch, last_client), result); + // Early return if there are unknown descriptors. Later loading of ACTIVEINTERNALSPK and ACTIVEEXTERNALEXPK + // may reference the unknown descriptor's ID which can result in a misleading corruption error + // when in reality the wallet is simply too new. + if (result == DBErrors::UNKNOWN_DESCRIPTOR) return result; - // Set the active ScriptPubKeyMans - for (auto spk_man_pair : wss.m_active_external_spks) { - pwallet->LoadActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /*internal=*/false); - } - for (auto spk_man_pair : wss.m_active_internal_spks) { - pwallet->LoadActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /*internal=*/true); - } + // Load address book + result = std::max(LoadAddressBookRecords(pwallet, *m_batch), result); - // Set the descriptor caches - for (const auto& desc_cache_pair : wss.m_descriptor_caches) { - auto spk_man = pwallet->GetScriptPubKeyMan(desc_cache_pair.first); - assert(spk_man); - ((DescriptorScriptPubKeyMan*)spk_man)->SetCache(desc_cache_pair.second); - } + // Load tx records + result = std::max(LoadTxRecords(pwallet, *m_batch, upgraded_txs, any_unordered), result); - // Set the descriptor keys - for (const auto& desc_key_pair : wss.m_descriptor_keys) { - auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first); - ((DescriptorScriptPubKeyMan*)spk_man)->AddKey(desc_key_pair.first.second, desc_key_pair.second); - } - for (const auto& desc_key_pair : wss.m_descriptor_crypt_keys) { - auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first); - ((DescriptorScriptPubKeyMan*)spk_man)->AddCryptedKey(desc_key_pair.first.second, desc_key_pair.second.first, desc_key_pair.second.second); - } + // Load SPKMs + result = std::max(LoadActiveSPKMs(pwallet, *m_batch), result); - if (rescan_required && result == DBErrors::LOAD_OK) { - result = DBErrors::NEED_RESCAN; - } else if (fNoncriticalErrors && result == DBErrors::LOAD_OK) { - result = DBErrors::NONCRITICAL_ERROR; + // Load decryption keys + result = std::max(LoadDecryptionKeys(pwallet, *m_batch), result); + } catch (...) { + // Exceptions that can be ignored or treated as non-critical are handled by the individual loading functions. + // Any uncaught exceptions will be caught here and treated as critical. + result = DBErrors::CORRUPT; } // Any wallet corruption at all: skip any rewriting or @@ -932,29 +1201,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) if (result != DBErrors::LOAD_OK) return result; - pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n", - wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records); - - // nTimeFirstKey is only reliable if all keys have metadata - if (pwallet->IsLegacy() && (wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) { - auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan(); - if (spk_man) { - LOCK(spk_man->cs_KeyStore); - spk_man->UpdateTimeFirstKey(1); - } - } - - for (const uint256& hash : wss.vWalletUpgrade) + for (const uint256& hash : upgraded_txs) WriteTx(pwallet->mapWallet.at(hash)); - // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: - if (wss.fIsEncrypted && (last_client == 40000 || last_client == 50000)) - return DBErrors::NEED_REWRITE; - if (!has_last_client || last_client != CLIENT_VERSION) // Update m_batch->Write(DBKeys::VERSION, CLIENT_VERSION); - if (wss.fAnyUnordered) + if (any_unordered) result = pwallet->ReorderTransactions(); // Upgrade all of the wallet keymetadata to have the hd master key id @@ -973,20 +1226,6 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) result = DBErrors::CORRUPT; } - // Set the inactive chain - if (wss.m_hd_chains.size() > 0) { - LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan(); - if (!legacy_spkm) { - pwallet->WalletLogPrintf("Inactive HD Chains found but no Legacy ScriptPubKeyMan\n"); - return DBErrors::CORRUPT; - } - for (const auto& chain_pair : wss.m_hd_chains) { - if (chain_pair.first != pwallet->GetLegacyScriptPubKeyMan()->GetHDChain().seed_id) { - pwallet->GetLegacyScriptPubKeyMan()->AddInactiveHDChain(chain_pair.second); - } - } - } - return result; } @@ -1161,7 +1400,7 @@ bool WalletBatch::EraseRecords(const std::unordered_set<std::string>& types) } // Make a copy of key to avoid data being deleted by the following read of the type - Span<const unsigned char> key_data = MakeUCharSpan(key); + Span key_data{key}; std::string type; key >> type; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index f84a89b23f..8f7c2f030c 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -42,19 +42,21 @@ struct WalletContext; static const bool DEFAULT_FLUSHWALLET = true; -/** Error statuses for the wallet database */ -enum class DBErrors +/** Error statuses for the wallet database. + * Values are in order of severity. When multiple errors occur, the most severe (highest value) will be returned. + */ +enum class DBErrors : int { - LOAD_OK, - CORRUPT, - NONCRITICAL_ERROR, - TOO_NEW, - EXTERNAL_SIGNER_SUPPORT_REQUIRED, - LOAD_FAIL, - NEED_REWRITE, - NEED_RESCAN, - UNKNOWN_DESCRIPTOR, - UNEXPECTED_LEGACY_ENTRY + LOAD_OK = 0, + NEED_RESCAN = 1, + NEED_REWRITE = 2, + EXTERNAL_SIGNER_SUPPORT_REQUIRED = 3, + NONCRITICAL_ERROR = 4, + TOO_NEW = 5, + UNKNOWN_DESCRIPTOR = 6, + LOAD_FAIL = 7, + UNEXPECTED_LEGACY_ENTRY = 8, + CORRUPT = 9, }; namespace DBKeys { @@ -276,8 +278,6 @@ public: DBErrors LoadWallet(CWallet* pwallet); DBErrors FindWalletTxHashes(std::vector<uint256>& tx_hashes); DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut); - /* Function to determine if a certain KV/key-type is a key (cryptographical key) type */ - static bool IsKeyType(const std::string& strType); //! write the hdchain model (external chain child index counter) bool WriteHDChain(const CHDChain& chain); @@ -300,11 +300,10 @@ private: //! Compacts BDB state so that wallet.dat is self-contained (if there are changes) void MaybeCompactWalletDB(WalletContext& context); -//! Callback for filtering key types to deserialize in ReadKeyValue -using KeyFilterFn = std::function<bool(const std::string&)>; - -//! Unserialize a given Key-Value pair and load it into the wallet -bool ReadKeyValue(CWallet* pwallet, DataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr); +bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); +bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); +bool LoadEncryptionKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); +bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr); } // namespace wallet #endif // BITCOIN_WALLET_WALLETDB_H |