Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Silent Payments: Implement BIP352 #28122

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion build_msvc/libsecp256k1/libsecp256k1.vcxproj
Expand Up @@ -14,7 +14,7 @@
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;ENABLE_MODULE_ELLSWIFT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;ENABLE_MODULE_ELLSWIFT;ENABLE_MODULE_SILENTPAYMENTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<UndefinePreprocessorDefinitions>USE_ASM_X86_64;%(UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src\secp256k1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<DisableSpecificWarnings>4146;4244;4267</DisableSpecificWarnings>
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.am
Expand Up @@ -134,6 +134,7 @@ BITCOIN_CORE_H = \
clientversion.h \
coins.h \
common/args.h \
common/bip352.h \
common/bloom.h \
common/init.h \
common/run_command.h \
Expand Down Expand Up @@ -672,6 +673,7 @@ libbitcoin_common_a_SOURCES = \
chainparams.cpp \
coins.cpp \
common/args.cpp \
common/bip352.cpp \
common/bloom.cpp \
common/config.cpp \
common/init.cpp \
Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.test.include
Expand Up @@ -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 \
Expand Down Expand Up @@ -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 \
Expand Down
7 changes: 7 additions & 0 deletions src/addresstype.cpp
Expand Up @@ -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();
Expand Down Expand Up @@ -152,6 +156,9 @@ class ValidDestinationVisitor
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; }
Expand Down
21 changes: 20 additions & 1 deletion src/addresstype.h
Expand Up @@ -116,6 +116,25 @@ struct WitnessUnknown
}
};

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.
Expand All @@ -128,7 +147,7 @@ struct WitnessUnknown
* * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W??? address)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
using CTxDestination = std::variant<CNoDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown>;
using CTxDestination = std::variant<CNoDestination, V0SilentPaymentDestination, PubKeyDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown>;

/** Check whether a CTxDestination corresponds to one with an address. */
bool IsValidDestination(const CTxDestination& dest);
Expand Down
12 changes: 6 additions & 6 deletions src/bech32.cpp
Expand Up @@ -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<int> 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);
Expand All @@ -397,12 +397,12 @@ DecodeResult Decode(const std::string& str) {
}

/** Find index of an incorrect character in a Bech32 string. */
std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str) {
std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str, CharLimit limit) {
std::vector<int> 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<int>(limit));
return std::make_pair("Bech32 string too long", std::move(error_locations));
}

Expand Down
15 changes: 13 additions & 2 deletions src/bech32.h
Expand Up @@ -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 on Bech32m encoded silent payment addresses. This 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<uint8_t>& values);
Expand All @@ -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<std::string, std::vector<int>> LocateErrors(const std::string& str);
std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str, CharLimit limit = CharLimit::SEGWIT);

} // namespace bech32

Expand Down