diff --git a/build_msvc/libsecp256k1/libsecp256k1.vcxproj b/build_msvc/libsecp256k1/libsecp256k1.vcxproj index 7ea4b965346..609f6d26350 100644 --- a/build_msvc/libsecp256k1/libsecp256k1.vcxproj +++ b/build_msvc/libsecp256k1/libsecp256k1.vcxproj @@ -14,7 +14,7 @@ - ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;ENABLE_MODULE_ELLSWIFT;%(PreprocessorDefinitions) + ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;ENABLE_MODULE_ELLSWIFT;ENABLE_MODULE_SILENTPAYMENTS;%(PreprocessorDefinitions) USE_ASM_X86_64;%(UndefinePreprocessorDefinitions) ..\..\src\secp256k1;%(AdditionalIncludeDirectories) 4146;4244;4267 diff --git a/configure.ac b/configure.ac index c7d124a1f17..b797f1570b3 100644 --- a/configure.ac +++ b/configure.ac @@ -1818,7 +1818,7 @@ CPPFLAGS="$CPPFLAGS_TEMP" if test -n "$use_sanitizers"; then export SECP_CFLAGS="$SECP_CFLAGS $SANITIZER_CFLAGS" fi -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --disable-module-ecdh" +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-ecdh --enable-module-silentpayments" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index c2e0c7b5b87..4d3d1cd4758 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -124,6 +124,7 @@ BITCOIN_CORE_H = \ base58.h \ bech32.h \ bip324.h \ + bip352.h \ blockencodings.h \ blockfilter.h \ chain.h \ @@ -384,6 +385,7 @@ libbitcoin_node_a_SOURCES = \ addrman.cpp \ banman.cpp \ bip324.cpp \ + bip352.cpp \ blockencodings.cpp \ blockfilter.cpp \ chain.cpp \ @@ -669,6 +671,7 @@ libbitcoin_common_a_SOURCES = \ addresstype.cpp \ base58.cpp \ bech32.cpp \ + bip352.cpp \ chainparams.cpp \ coins.cpp \ common/args.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 942e0bf69b3..a73a28ff142 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -17,6 +17,7 @@ FUZZ_BINARY=test/fuzz/fuzz$(EXEEXT) JSON_TEST_FILES = \ test/data/script_tests.json \ test/data/bip341_wallet_vectors.json \ + test/data/bip352_send_and_receive_vectors.json \ test/data/base58_encode_decode.json \ test/data/blockfilters.json \ test/data/key_io_valid.json \ @@ -75,6 +76,7 @@ BITCOIN_TESTS =\ test/bech32_tests.cpp \ test/bip32_tests.cpp \ test/bip324_tests.cpp \ + test/bip352_tests.cpp \ test/blockchain_tests.cpp \ test/blockencodings_tests.cpp \ test/blockfilter_index_tests.cpp \ diff --git a/src/addresstype.cpp b/src/addresstype.cpp index f199d1b4794..49592519439 100644 --- a/src/addresstype.cpp +++ b/src/addresstype.cpp @@ -104,6 +104,10 @@ namespace { class CScriptVisitor { public: + CScript operator()(const V0SilentPaymentDestination& dest) const + { + return CScript(); + } CScript operator()(const CNoDestination& dest) const { return dest.GetScript(); @@ -152,6 +156,9 @@ public: bool operator()(const PubKeyDestination& dest) const { return false; } bool operator()(const PKHash& dest) const { return true; } bool operator()(const ScriptHash& dest) const { return true; } + // silent payment addresses are not valid until sending support has been implemented + // TODO: set this to true once sending is implemented + bool operator()(const V0SilentPaymentDestination& dest) const { return false; } bool operator()(const WitnessV0KeyHash& dest) const { return true; } bool operator()(const WitnessV0ScriptHash& dest) const { return true; } bool operator()(const WitnessV1Taproot& dest) const { return true; } diff --git a/src/addresstype.h b/src/addresstype.h index 0152858bad2..446593b90a6 100644 --- a/src/addresstype.h +++ b/src/addresstype.h @@ -116,6 +116,25 @@ public: } }; +struct V0SilentPaymentDestination +{ + CPubKey m_scan_pubkey; + CPubKey m_spend_pubkey; + + friend bool operator==(const V0SilentPaymentDestination& a, const V0SilentPaymentDestination& b) { + if (a.m_scan_pubkey != b.m_scan_pubkey) return false; + if (a.m_spend_pubkey != b.m_spend_pubkey) return false; + return true; + } + + friend bool operator<(const V0SilentPaymentDestination& a, const V0SilentPaymentDestination& b) { + if (a.m_scan_pubkey < b.m_scan_pubkey) return true; + if (a.m_scan_pubkey > b.m_scan_pubkey) return false; + if (a.m_spend_pubkey < b.m_spend_pubkey) return true; + return false; + } +}; + /** * A txout script categorized into standard templates. * * CNoDestination: Optionally a script, no corresponding address. @@ -128,7 +147,7 @@ public: * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W??? address) * A CTxDestination is the internal data type encoded in a bitcoin address */ -using CTxDestination = std::variant; +using CTxDestination = std::variant; /** Check whether a CTxDestination corresponds to one with an address. */ bool IsValidDestination(const CTxDestination& dest); diff --git a/src/bech32.cpp b/src/bech32.cpp index ba3c419d8b6..c17b3fe7ef3 100644 --- a/src/bech32.cpp +++ b/src/bech32.cpp @@ -370,11 +370,11 @@ std::string Encode(Encoding encoding, const std::string& hrp, const data& values } /** Decode a Bech32 or Bech32m string. */ -DecodeResult Decode(const std::string& str) { +DecodeResult Decode(const std::string& str, CharLimit limit) { std::vector errors; if (!CheckCharacters(str, errors)) return {}; size_t pos = str.rfind('1'); - if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) { + if (str.size() > limit || pos == str.npos || pos == 0 || pos + 7 > str.size()) { return {}; } data values(str.size() - 1 - pos); @@ -397,12 +397,12 @@ DecodeResult Decode(const std::string& str) { } /** Find index of an incorrect character in a Bech32 string. */ -std::pair> LocateErrors(const std::string& str) { +std::pair> LocateErrors(const std::string& str, CharLimit limit) { std::vector error_locations{}; - if (str.size() > 90) { - error_locations.resize(str.size() - 90); - std::iota(error_locations.begin(), error_locations.end(), 90); + if (str.size() > limit) { + error_locations.resize(str.size() - limit); + std::iota(error_locations.begin(), error_locations.end(), static_cast(limit)); return std::make_pair("Bech32 string too long", std::move(error_locations)); } diff --git a/src/bech32.h b/src/bech32.h index 5e89e6efdaa..8f47fb07121 100644 --- a/src/bech32.h +++ b/src/bech32.h @@ -28,6 +28,17 @@ enum class Encoding { BECH32M, //!< Bech32m encoding as defined in BIP350 }; +/** Character limits for bech32(m) encoded strings. Character limits are how we provide error location guarantees. + * These values should never exceed 2^31 - 1 (max value for a 32-bit int), since there are places where we may need to + * convert the CharLimit::VALUE to an int. In practice, this should never happen since this CharLimit applies to an address encoding + * and we would never encode an address with such a massive value */ +enum CharLimit : size_t { + NO_LIMIT = 0, //!< Allows for Bech32(m) encoded strings of arbitrary size. No guarantees on the number of errors detected + + SEGWIT = 90, //!< BIP173/350 imposed 90 character limit on Bech32(m) encoded addresses. This guarantees finding up to 4 errors + SILENT_PAYMENTS = 1024, //!< BIP352 imposed 1024 character limit, which guarantees finding up to 3 errors +}; + /** Encode a Bech32 or Bech32m string. If hrp contains uppercase characters, this will cause an * assertion error. Encoding must be one of BECH32 or BECH32M. */ std::string Encode(Encoding encoding, const std::string& hrp, const std::vector& values); @@ -43,10 +54,10 @@ struct DecodeResult }; /** Decode a Bech32 or Bech32m string. */ -DecodeResult Decode(const std::string& str); +DecodeResult Decode(const std::string& str, CharLimit limit = CharLimit::SEGWIT); /** Return the positions of errors in a Bech32 string. */ -std::pair> LocateErrors(const std::string& str); +std::pair> LocateErrors(const std::string& str, CharLimit limit = CharLimit::SEGWIT); } // namespace bech32 diff --git a/src/bip352.cpp b/src/bip352.cpp new file mode 100644 index 00000000000..a8c48ba9a84 --- /dev/null +++ b/src/bip352.cpp @@ -0,0 +1,352 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include