aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build_msvc/common.init.vcxproj.in2
-rw-r--r--configure.ac2
-rwxr-xr-xcontrib/devtools/symbol-check.py10
-rw-r--r--depends/packages/xcb_proto.mk4
-rw-r--r--doc/build-unix.md2
-rw-r--r--src/Makefile.test_util.include1
-rw-r--r--src/blockfilter.cpp2
-rw-r--r--src/crypto/siphash.cpp7
-rw-r--r--src/crypto/siphash.h3
-rw-r--r--src/net.cpp4
-rw-r--r--src/netaddress.h2
-rw-r--r--src/protocol.cpp75
-rw-r--r--src/qt/test/uritests.cpp26
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/script/descriptor.cpp4
-rw-r--r--src/secp256k1/.cirrus.yml54
-rw-r--r--src/secp256k1/CHANGELOG.md10
-rw-r--r--src/secp256k1/Makefile.am6
-rwxr-xr-xsrc/secp256k1/ci/cirrus.sh19
-rw-r--r--src/secp256k1/ci/linux-debian.Dockerfile45
-rw-r--r--src/secp256k1/configure.ac6
-rw-r--r--src/secp256k1/doc/ellswift.md4
-rw-r--r--src/secp256k1/examples/CMakeLists.txt3
-rw-r--r--src/secp256k1/examples/examples_util.h2
-rw-r--r--src/secp256k1/include/secp256k1.h63
-rw-r--r--src/secp256k1/include/secp256k1_ecdh.h4
-rw-r--r--src/secp256k1/include/secp256k1_ellswift.h8
-rw-r--r--src/secp256k1/include/secp256k1_extrakeys.h16
-rw-r--r--src/secp256k1/include/secp256k1_schnorrsig.h2
-rw-r--r--src/secp256k1/sage/group_prover.sage2
-rw-r--r--src/secp256k1/src/CMakeLists.txt6
-rw-r--r--src/secp256k1/src/ecdsa_impl.h21
-rw-r--r--src/secp256k1/src/ecmult.h2
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h2
-rw-r--r--src/secp256k1/src/ecmult_gen_compute_table_impl.h3
-rw-r--r--src/secp256k1/src/ecmult_impl.h4
-rw-r--r--src/secp256k1/src/field.h34
-rw-r--r--src/secp256k1/src/field_10x26_impl.h4
-rw-r--r--src/secp256k1/src/field_5x52_impl.h4
-rw-r--r--src/secp256k1/src/field_impl.h14
-rw-r--r--src/secp256k1/src/group.h3
-rw-r--r--src/secp256k1/src/group_impl.h11
-rw-r--r--src/secp256k1/src/int128_struct_impl.h5
-rw-r--r--src/secp256k1/src/modules/ellswift/Makefile.am.include1
-rw-r--r--src/secp256k1/src/modules/ellswift/tests_exhaustive_impl.h39
-rw-r--r--src/secp256k1/src/modules/ellswift/tests_impl.h4
-rw-r--r--src/secp256k1/src/precompute_ecmult.c5
-rw-r--r--src/secp256k1/src/testrand_impl.h65
-rw-r--r--src/secp256k1/src/tests.c204
-rw-r--r--src/secp256k1/src/tests_exhaustive.c16
-rw-r--r--src/secp256k1/src/util.h13
-rw-r--r--src/test/denialofservice_tests.cpp1
-rw-r--r--src/test/fuzz/addrman.cpp4
-rw-r--r--src/test/fuzz/crypto.cpp2
-rw-r--r--src/test/fuzz/golomb_rice.cpp2
-rw-r--r--src/test/hash_tests.cpp16
-rw-r--r--src/test/miniscript_tests.cpp1
-rw-r--r--src/test/random_tests.cpp1
-rw-r--r--src/test/util/random.cpp33
-rw-r--r--src/test/util/random.h32
-rw-r--r--src/test/util/setup_common.cpp22
-rw-r--r--src/test/util/setup_common.h33
-rw-r--r--src/txrequest.cpp2
-rw-r--r--src/util/bytevectorhash.cpp2
-rw-r--r--src/util/fs.cpp7
-rw-r--r--src/util/fs_helpers.cpp11
-rw-r--r--src/util/hasher.cpp2
-rw-r--r--src/util/sock.cpp25
-rw-r--r--src/util/syserror.cpp24
-rw-r--r--src/util/syserror.h4
-rw-r--r--src/wallet/feebumper.cpp25
-rw-r--r--src/wallet/feebumper.h5
-rw-r--r--src/wallet/rpc/spend.cpp13
-rw-r--r--src/wallet/test/wallet_tests.cpp1
-rw-r--r--src/wallet/walletdb.cpp4
-rw-r--r--test/functional/data/rpc_decodescript.json2
-rwxr-xr-xtest/functional/mining_basic.py46
-rwxr-xr-xtest/functional/rpc_decodescript.py2
-rwxr-xr-xtest/functional/test_framework/test_node.py11
-rwxr-xr-xtest/functional/wallet_bumpfee.py77
-rwxr-xr-xtest/functional/wallet_migration.py50
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py8
-rwxr-xr-xtest/fuzz/test_runner.py49
84 files changed, 851 insertions, 518 deletions
diff --git a/build_msvc/common.init.vcxproj.in b/build_msvc/common.init.vcxproj.in
index 24d5922182..9cce546268 100644
--- a/build_msvc/common.init.vcxproj.in
+++ b/build_msvc/common.init.vcxproj.in
@@ -90,7 +90,7 @@
<AdditionalOptions>/utf-8 /Zc:__cplusplus /std:c++20 %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>4018;4244;4267;4715;4805</DisableSpecificWarnings>
<TreatWarningAsError>true</TreatWarningAsError>
- <PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SECP256K1_STATIC;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\src\minisketch\include;..\..\src\univalue\include;..\..\src\secp256k1\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
diff --git a/configure.ac b/configure.ac
index 9e3a7cca8a..a2faa23e2d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -714,6 +714,8 @@ case $host in
AC_MSG_ERROR([windres not found])
fi
+ CORE_CPPFLAGS="$CORE_CPPFLAGS -DSECP256K1_STATIC"
+
CORE_CPPFLAGS="$CORE_CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -D_WIN32_WINNT=0x0601 -D_WIN32_IE=0x0501 -DWIN32_LEAN_AND_MEAN"
dnl Prevent the definition of min/max macros.
dnl We always want to use the standard library.
diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py
index 8a2ea3e1fa..c5f0a761f1 100755
--- a/contrib/devtools/symbol-check.py
+++ b/contrib/devtools/symbol-check.py
@@ -157,21 +157,21 @@ PE_ALLOWED_LIBRARIES = {
'KERNEL32.dll', # win32 base APIs
'msvcrt.dll', # C standard library for MSVC
'SHELL32.dll', # shell API
-'USER32.dll', # user interface
'WS2_32.dll', # sockets
# bitcoin-qt only
'dwmapi.dll', # desktop window manager
'GDI32.dll', # graphics device interface
'IMM32.dll', # input method editor
-'NETAPI32.dll',
+'NETAPI32.dll', # network management
'ole32.dll', # component object model
'OLEAUT32.dll', # OLE Automation API
'SHLWAPI.dll', # light weight shell API
-'USERENV.dll',
-'UxTheme.dll',
+'USER32.dll', # user interface
+'USERENV.dll', # user management
+'UxTheme.dll', # visual style
'VERSION.dll', # version checking
'WINMM.dll', # WinMM audio API
-'WTSAPI32.dll',
+'WTSAPI32.dll', # Remote Desktop
}
def check_version(max_versions, version, arch) -> bool:
diff --git a/depends/packages/xcb_proto.mk b/depends/packages/xcb_proto.mk
index 9be822506d..6e1c5a10a8 100644
--- a/depends/packages/xcb_proto.mk
+++ b/depends/packages/xcb_proto.mk
@@ -1,8 +1,8 @@
package=xcb_proto
-$(package)_version=1.14.1
+$(package)_version=1.15.2
$(package)_download_path=https://xorg.freedesktop.org/archive/individual/proto
$(package)_file_name=xcb-proto-$($(package)_version).tar.xz
-$(package)_sha256_hash=f04add9a972ac334ea11d9d7eb4fc7f8883835da3e4859c9afa971efdf57fcc3
+$(package)_sha256_hash=7072beb1f680a2fe3f9e535b797c146d22528990c72f63ddb49d2f350a3653ed
define $(package)_config_cmds
$($(package)_autoconf)
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 3633d4f811..848dd5f71a 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -132,7 +132,7 @@ ZMQ dependencies (provides ZMQ API):
User-Space, Statically Defined Tracing (USDT) dependencies:
- sudo dnf install systemtap
+ sudo dnf install systemtap-sdt-devel
GUI dependencies:
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index 33c299f34c..6a1fd712bd 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -40,6 +40,7 @@ libtest_util_a_SOURCES = \
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/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/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/net.cpp b/src/net.cpp
index 1b1b540417..1eda51e9df 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -2529,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)
@@ -2912,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,
diff --git a/src/netaddress.h b/src/netaddress.h
index 36dc886406..7cba6c00d0 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -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/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/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/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/script/descriptor.cpp b/src/script/descriptor.cpp
index f57f167e5d..09ded5fc61 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -1723,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) {
diff --git a/src/secp256k1/.cirrus.yml b/src/secp256k1/.cirrus.yml
index 5a00b65a33..ae205c624c 100644
--- a/src/secp256k1/.cirrus.yml
+++ b/src/secp256k1/.cirrus.yml
@@ -54,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
@@ -68,12 +62,12 @@ 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, ELLSWIFT: yes}
- env: {WIDEMUL: int128}
@@ -86,6 +80,7 @@ task:
- 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:
@@ -93,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
@@ -111,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
@@ -125,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
@@ -157,7 +156,6 @@ task:
SCHNORRSIG: yes
ELLSWIFT: yes
CTIMETESTS: no
- << : *MERGE_BASE
test_script:
# https://sourceware.org/bugzilla/show_bug.cgi?id=27008
- rm /etc/ld.so.cache
@@ -180,7 +178,6 @@ task:
matrix:
- env: {}
- env: {EXPERIMENTAL: yes, ASM: arm32}
- << : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
@@ -198,7 +195,6 @@ task:
SCHNORRSIG: yes
ELLSWIFT: yes
CTIMETESTS: no
- << : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
@@ -216,7 +212,6 @@ task:
SCHNORRSIG: yes
ELLSWIFT: yes
CTIMETESTS: no
- << : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
@@ -237,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
@@ -280,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
@@ -325,7 +318,6 @@ task:
- env:
HOST: i686-linux-gnu
CC: i686-linux-gnu-gcc
- << : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
@@ -352,7 +344,6 @@ task:
ECMULTGENPRECISION: 2
ECMULTWINDOW: 2
CFLAGS: "-fsanitize=memory -g -O3"
- << : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
@@ -369,7 +360,6 @@ task:
RECOVERY: yes
SCHNORRSIG: yes
ELLSWIFT: yes
- << : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
@@ -401,11 +391,17 @@ 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 -E env CFLAGS="/WX" 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
diff --git a/src/secp256k1/CHANGELOG.md b/src/secp256k1/CHANGELOG.md
index 8e31edc6ee..dacb0a3f48 100644
--- a/src/secp256k1/CHANGELOG.md
+++ b/src/secp256k1/CHANGELOG.md
@@ -7,6 +7,16 @@ 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`.
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index ee14ac4509..32bc729a41 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -153,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
@@ -163,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
@@ -174,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
diff --git a/src/secp256k1/ci/cirrus.sh b/src/secp256k1/ci/cirrus.sh
index 8d82818611..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
@@ -53,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 \
diff --git a/src/secp256k1/ci/linux-debian.Dockerfile b/src/secp256k1/ci/linux-debian.Dockerfile
index 54eafcab25..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,9 +25,44 @@ 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 && \
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index 82cf95132d..a502d1304a 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -127,12 +127,6 @@ AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [
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"
- # 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"
fi
])
SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS)
diff --git a/src/secp256k1/doc/ellswift.md b/src/secp256k1/doc/ellswift.md
index 7fbb7c1787..9d60e6be0b 100644
--- a/src/secp256k1/doc/ellswift.md
+++ b/src/secp256k1/doc/ellswift.md
@@ -88,7 +88,7 @@ $$
\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
+ \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 \\
@@ -329,7 +329,7 @@ $t$ value for multiple $c$ inputs (thereby biasing that encoding):
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 $2w(u + 2v) = 2X_0(u)$ while also either $w \neq 2Y_0(u)$ or $h(u)=0$.
+ * 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.$
diff --git a/src/secp256k1/examples/CMakeLists.txt b/src/secp256k1/examples/CMakeLists.txt
index e095b7f84f..e2ea473008 100644
--- a/src/secp256k1/examples/CMakeLists.txt
+++ b/src/secp256k1/examples/CMakeLists.txt
@@ -6,9 +6,6 @@ target_link_libraries(example INTERFACE
secp256k1
$<$<PLATFORM_ID:Windows>:bcrypt>
)
-if(NOT BUILD_SHARED_LIBS AND MSVC)
- target_link_options(example INTERFACE /IGNORE:4217)
-endif()
add_executable(ecdsa_example ecdsa.c)
target_link_libraries(ecdsa_example example)
diff --git a/src/secp256k1/examples/examples_util.h b/src/secp256k1/examples/examples_util.h
index 8e3a8f00cf..3293b64032 100644
--- a/src/secp256k1/examples/examples_util.h
+++ b/src/secp256k1/examples/examples_util.h
@@ -95,7 +95,7 @@ static 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 a7a2be7a3a..936f0b42b7 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -133,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
@@ -226,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)
@@ -626,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.
*
@@ -733,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,
@@ -761,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
index 3851f93098..f79bd88396 100644
--- a/src/secp256k1/include/secp256k1_ellswift.h
+++ b/src/secp256k1/include/secp256k1_ellswift.h
@@ -41,6 +41,8 @@ extern "C" {
* - 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
@@ -70,7 +72,7 @@ typedef int (*secp256k1_ellswift_xdh_hash_function)(
/** 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_VAR const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_prefix;
+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
@@ -78,7 +80,7 @@ SECP256K1_API_VAR const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_
* 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_VAR const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_bip324;
+SECP256K1_API const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_bip324;
/** Construct a 64-byte ElligatorSwift encoding of a given pubkey.
*
@@ -159,7 +161,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_create(
/** 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 succesfully computed
+ * 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.
diff --git a/src/secp256k1/include/secp256k1_extrakeys.h b/src/secp256k1/include/secp256k1_extrakeys.h
index 673fca01f9..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,
@@ -229,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 1ee665fd19..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.
*
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 0bba19982a..b305751b08 100644
--- a/src/secp256k1/src/CMakeLists.txt
+++ b/src/secp256k1/src/CMakeLists.txt
@@ -20,10 +20,10 @@ if(SECP256K1_ASM STREQUAL "arm32")
target_link_libraries(secp256k1_asm INTERFACE secp256k1_asm_arm)
endif()
-# Define our export symbol only for Win32 and only for shared libs.
-# This matches libtool's usage of DLL_EXPORT
if(WIN32)
- set_target_properties(secp256k1 PROPERTIES DEFINE_SYMBOL "DLL_EXPORT")
+ # 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()
# Object libs don't know if they're being built for a shared or static lib.
diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h
index 48e30851b5..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
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_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 26b3e238d8..06f9e53ffd 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -276,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.
diff --git a/src/secp256k1/src/ecmult_gen_compute_table_impl.h b/src/secp256k1/src/ecmult_gen_compute_table_impl.h
index 7d672b9950..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);
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index f4624677d7..6d14c7ac4b 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -288,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++) {
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index e632f9e3e2..bb99f948ef 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -88,8 +88,8 @@ static const secp256k1_fe secp256k1_const_beta = SECP256K1_FE_CONST(
# 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 secp256k1_fe_impl_negate
-# define secp256k1_fe_mul_int secp256k1_fe_impl_mul_int
+# 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
@@ -192,14 +192,14 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b);
/** Set a field element equal to a provided 32-byte big endian value, reducing it.
*
- * On input, r does not need to be initalized. a must be a pointer to an initialized 32-byte array.
+ * 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);
/** Set a field element equal to a provided 32-byte big endian value, checking for overflow.
*
- * On input, r does not need to be initalized. a must be a pointer to an initialized 32-byte array.
+ * 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).
*/
@@ -214,11 +214,17 @@ static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a);
/** 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 in [0,31].
+ * 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.
*/
-static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m);
+#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);
/** Add a small integer to a field element.
*
@@ -229,12 +235,18 @@ static void secp256k1_fe_add_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 in [0,32].
+ * 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.
*/
-static void secp256k1_fe_mul_int(secp256k1_fe *r, int a);
+#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);
/** Increment a field element by another.
*
@@ -267,8 +279,10 @@ static void secp256k1_fe_sqr(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.
- * Performs {r = sqrt(a)} or {r = sqrt(-a)}, whichever exists. The resulting value
- * represented by r will be a square itself. Variables r and a must not point to the same object.
+ * 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);
diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h
index c1b32b80a8..8445db1639 100644
--- a/src/secp256k1/src/field_10x26_impl.h
+++ b/src/secp256k1/src/field_10x26_impl.h
@@ -344,7 +344,7 @@ static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) {
r[31] = a->n[0] & 0xff;
}
-SECP256K1_INLINE static void secp256k1_fe_impl_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) {
+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);
@@ -365,7 +365,7 @@ SECP256K1_INLINE static void secp256k1_fe_impl_negate(secp256k1_fe *r, const sec
r->n[9] = 0x03FFFFFUL * 2 * (m + 1) - a->n[9];
}
-SECP256K1_INLINE static void secp256k1_fe_impl_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;
diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h
index 0a4cc1a630..ecb70502c2 100644
--- a/src/secp256k1/src/field_5x52_impl.h
+++ b/src/secp256k1/src/field_5x52_impl.h
@@ -314,7 +314,7 @@ static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) {
r[31] = a->n[0] & 0xFF;
}
-SECP256K1_INLINE static void secp256k1_fe_impl_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) {
+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);
@@ -329,7 +329,7 @@ SECP256K1_INLINE static void secp256k1_fe_impl_negate(secp256k1_fe *r, const sec
r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * (m + 1) - a->n[4];
}
-SECP256K1_INLINE static void secp256k1_fe_impl_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;
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index f9769a4a39..7f18ebdc94 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -44,7 +44,7 @@ SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const
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.
*
@@ -289,23 +289,23 @@ SECP256K1_INLINE static void secp256k1_fe_get_b32(unsigned char *r, const secp25
secp256k1_fe_impl_get_b32(r, a);
}
-static void secp256k1_fe_impl_negate(secp256k1_fe *r, const secp256k1_fe *a, int m);
-SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) {
+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(r, a, 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(secp256k1_fe *r, int a);
-SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) {
+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(r, a);
+ secp256k1_fe_impl_mul_int_unchecked(r, a);
r->magnitude *= a;
r->normalized = 0;
secp256k1_fe_verify(r);
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index 877c3eaeed..1cc6137161 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -106,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) */
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index dcd171f574..ffdfeaa10a 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -314,13 +314,17 @@ static int secp256k1_gej_eq_var(const secp256k1_gej *a, const secp256k1_gej *b)
}
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) {
@@ -349,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);
}
diff --git a/src/secp256k1/src/int128_struct_impl.h b/src/secp256k1/src/int128_struct_impl.h
index 990982da84..962a71d13b 100644
--- a/src/secp256k1/src/int128_struct_impl.h
+++ b/src/secp256k1/src/int128_struct_impl.h
@@ -80,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/modules/ellswift/Makefile.am.include b/src/secp256k1/src/modules/ellswift/Makefile.am.include
index e7efea2981..8251231ea3 100644
--- a/src/secp256k1/src/modules/ellswift/Makefile.am.include
+++ b/src/secp256k1/src/modules/ellswift/Makefile.am.include
@@ -2,3 +2,4 @@ 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/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
index 86ca09862b..47f443d980 100644
--- a/src/secp256k1/src/modules/ellswift/tests_impl.h
+++ b/src/secp256k1/src/modules/ellswift/tests_impl.h
@@ -322,7 +322,9 @@ void run_ellswift_tests(void) {
secp256k1_testrand256_test(auxrnd32a);
secp256k1_testrand256_test(auxrnd32b);
random_scalar_order_test(&seca);
- random_scalar_order_test(&secb);
+ /* 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);
diff --git a/src/secp256k1/src/precompute_ecmult.c b/src/secp256k1/src/precompute_ecmult.c
index 742142cf58..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;
}
diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h
index 1b7481a53b..fe97620435 100644
--- a/src/secp256k1/src/testrand_impl.h
+++ b/src/secp256k1/src/testrand_impl.h
@@ -16,8 +16,6 @@
#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";
@@ -36,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) {
@@ -57,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 8ada3f869b..920d31b481 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -89,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_limit(fe, b32)) {
- break;
- }
- } while(1);
-}
-
static void random_field_element_magnitude(secp256k1_fe *fe) {
secp256k1_fe zero;
int n = secp256k1_testrand_int(9);
@@ -108,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;
@@ -129,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);
@@ -180,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();
@@ -823,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. */
@@ -2984,16 +2942,6 @@ static void random_fe(secp256k1_fe *x) {
} while(1);
}
-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(secp256k1_fe *nz) {
int tries = 10;
while (--tries >= 0) {
@@ -3234,7 +3182,7 @@ 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
@@ -3820,18 +3768,14 @@ 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 */
- do {
- random_field_element_test(&r);
- } while(secp256k1_fe_is_zero(&r));
+ random_fe_non_zero_test(&r);
for (i1 = 0; i1 < 1 + 4 * runs; i1++) {
int i2;
@@ -4048,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,
@@ -4148,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));
}
@@ -4590,9 +4524,7 @@ 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;
@@ -4611,22 +4543,17 @@ 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_int(&c, SECP256K1_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;
@@ -7765,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);
@@ -7802,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();
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index d35acdd58e..dbb6b7eb46 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -13,6 +13,9 @@
#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
@@ -395,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];
@@ -490,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 e2ee8a8f19..801ea0c885 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -51,6 +51,19 @@ static void print_buf_plain(const unsigned char *buf, size_t len) {
# 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;
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 1a06f16155..13349329ff 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>
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index f0035ddf21..8ac1330dcb 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -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();
};
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/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/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/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 2d183c8844..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>
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/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 d8f30bdc6e..6ae2187974 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -32,6 +32,7 @@
#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 +41,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 +75,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();
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/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/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/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/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/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/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/walletdb.cpp b/src/wallet/walletdb.cpp
index ffe5fd4a18..8212c04464 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -794,11 +794,13 @@ static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& bat
WalletDescriptor desc;
try {
value >> desc;
- } catch (const std::ios_base::failure&) {
+ } 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);
diff --git a/test/functional/data/rpc_decodescript.json b/test/functional/data/rpc_decodescript.json
index 5f3e725d4c..4a15ae8792 100644
--- a/test/functional/data/rpc_decodescript.json
+++ b/test/functional/data/rpc_decodescript.json
@@ -69,7 +69,7 @@
"p2sh": "2N34iiGoUUkVSPiaaTFpJjB1FR9TXQu3PGM",
"segwit": {
"asm": "0 96c2368fc30514a438a8bd909f93c49a1549d77198ccbdb792043b666cb24f42",
- "desc": "wsh(raw(02eeee))#gtay4y0z",
+ "desc": "addr(bcrt1qjmprdr7rq522gw9ghkgfly7yng25n4m3nrxtmdujqsakvm9jfapqk795l5)#5akkdska",
"hex": "002096c2368fc30514a438a8bd909f93c49a1549d77198ccbdb792043b666cb24f42",
"address": "bcrt1qjmprdr7rq522gw9ghkgfly7yng25n4m3nrxtmdujqsakvm9jfapqk795l5",
"type": "witness_v0_scripthash",
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index aabf06ee53..56cd615dac 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -18,9 +18,10 @@ from test_framework.blocktools import (
TIME_GENESIS_BLOCK,
)
from test_framework.messages import (
+ BLOCK_HEADER_SIZE,
CBlock,
CBlockHeader,
- BLOCK_HEADER_SIZE,
+ COIN,
ser_uint256,
)
from test_framework.p2p import P2PDataStore
@@ -28,12 +29,14 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
+ get_fee,
)
from test_framework.wallet import MiniWallet
VERSIONBITS_TOP_BITS = 0x20000000
VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
+DEFAULT_BLOCK_MIN_TX_FEE = 1000 # default `-blockmintxfee` setting [sat/kvB]
def assert_template(node, block, expect, rehash=True):
@@ -73,6 +76,45 @@ class MiningTest(BitcoinTestFramework):
self.restart_node(0)
self.connect_nodes(0, 1)
+ def test_blockmintxfee_parameter(self):
+ self.log.info("Test -blockmintxfee setting")
+ self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
+ node = self.nodes[0]
+
+ # test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB]
+ for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 50, 100, 500, 2500, 5000, 21000, 333333, 2500000):
+ blockmintxfee_btc_kvb = blockmintxfee_sat_kvb / Decimal(COIN)
+ if blockmintxfee_sat_kvb == DEFAULT_BLOCK_MIN_TX_FEE:
+ self.log.info(f"-> Default -blockmintxfee setting ({blockmintxfee_sat_kvb} sat/kvB)...")
+ else:
+ blockmintxfee_parameter = f"-blockmintxfee={blockmintxfee_btc_kvb:.8f}"
+ self.log.info(f"-> Test {blockmintxfee_parameter} ({blockmintxfee_sat_kvb} sat/kvB)...")
+ self.restart_node(0, extra_args=[blockmintxfee_parameter, '-minrelaytxfee=0', '-persistmempool=0'])
+ self.wallet.rescan_utxos() # to avoid spending outputs of txs that are not in mempool anymore after restart
+
+ # submit one tx with exactly the blockmintxfee rate, and one slightly below
+ tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb)
+ assert_equal(tx_with_min_feerate["fee"], get_fee(tx_with_min_feerate["tx"].get_vsize(), blockmintxfee_btc_kvb))
+ if blockmintxfee_btc_kvb > 0:
+ lowerfee_btc_kvb = blockmintxfee_btc_kvb - Decimal(10)/COIN # 0.01 sat/vbyte lower
+ tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb)
+ assert_equal(tx_below_min_feerate["fee"], get_fee(tx_below_min_feerate["tx"].get_vsize(), lowerfee_btc_kvb))
+ else: # go below zero fee by using modified fees
+ tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb)
+ node.prioritisetransaction(tx_below_min_feerate["txid"], 0, -1)
+
+ # check that tx below specified fee-rate is neither in template nor in the actual block
+ block_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
+ block_template_txids = [tx['txid'] for tx in block_template['transactions']]
+ self.generate(self.wallet, 1, sync_fun=self.no_op)
+ block = node.getblock(node.getbestblockhash(), verbosity=2)
+ block_txids = [tx['txid'] for tx in block['tx']]
+
+ assert tx_with_min_feerate['txid'] in block_template_txids
+ assert tx_with_min_feerate['txid'] in block_txids
+ assert tx_below_min_feerate['txid'] not in block_template_txids
+ assert tx_below_min_feerate['txid'] not in block_txids
+
def run_test(self):
node = self.nodes[0]
self.wallet = MiniWallet(node)
@@ -279,6 +321,8 @@ class MiningTest(BitcoinTestFramework):
node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
+ self.test_blockmintxfee_parameter()
+
if __name__ == '__main__':
MiningTest().main()
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index 673836bd04..f37e61ab50 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -271,7 +271,7 @@ class DecodeScriptTest(BitcoinTestFramework):
assert res["segwit"]["desc"] == "wsh(and_v(and_v(v:hash160(ffffffffffffffffffffffffffffffffffffffff),v:pk(0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)),older(1)))#gm8xz4fl"
# Miniscript-incompatible offered HTLC
res = self.nodes[0].decodescript("82012088a914ffffffffffffffffffffffffffffffffffffffff882102ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffacb2")
- assert res["segwit"]["desc"] == "wsh(raw(82012088a914ffffffffffffffffffffffffffffffffffffffff882102ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffacb2))#ra6w2xa7"
+ assert res["segwit"]["desc"] == "addr(bcrt1q73qyfypp47hvgnkjqnav0j3k2lq3v76wg22dk8tmwuz5sfgv66xsvxg6uu)#9p3q328s"
# Miniscript-compatible multisig bigger than 520 byte P2SH limit.
res = self.nodes[0].decodescript("5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68")
assert_equal(res["segwit"]["desc"], "wsh(or_d(multi(11,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb,03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a),and_v(v:older(4032),multi(2,03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb))))#7jwwklk4")
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 56087013fb..1fcef6ce1c 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -644,10 +644,13 @@ class TestNode():
# in comparison to the upside of making tests less fragile and unexpected intermittent errors less likely.
p2p_conn.sync_with_ping()
- # Consistency check that the Bitcoin Core has received our user agent string. This checks the
- # node's newest peer. It could be racy if another Bitcoin Core node has connected since we opened
- # our connection, but we don't expect that to happen.
- assert_equal(self.getpeerinfo()[-1]['subver'], P2P_SUBVERSION)
+ # Consistency check that the node received our user agent string.
+ # Find our connection in getpeerinfo by our address:port, as it is unique.
+ sockname = p2p_conn._transport.get_extra_info("socket").getsockname()
+ our_addr_and_port = f"{sockname[0]}:{sockname[1]}"
+ info = [peer for peer in self.getpeerinfo() if peer["addr"] == our_addr_and_port]
+ assert_equal(len(info), 1)
+ assert_equal(info[0]["subver"], P2P_SUBVERSION)
return p2p_conn
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index b9ebf64c22..4bc01f3035 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -24,9 +24,11 @@ from test_framework.messages import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_fee_amount,
assert_greater_than,
assert_raises_rpc_error,
get_fee,
+ find_vout_for_address,
)
from test_framework.wallet import MiniWallet
@@ -109,6 +111,8 @@ class BumpFeeTest(BitcoinTestFramework):
test_small_output_with_feerate_succeeds(self, rbf_node, dest_address)
test_no_more_inputs_fails(self, rbf_node, dest_address)
self.test_bump_back_to_yourself()
+ self.test_provided_change_pos(rbf_node)
+ self.test_single_output()
# Context independent tests
test_feerate_checks_replaced_outputs(self, rbf_node, peer_node)
@@ -174,6 +178,13 @@ class BumpFeeTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data",
rbf_node.bumpfee, rbfid, {"outputs": [{"data": "deadbeef"}, {"data": "deadbeef"}]})
+ self.log.info("Test reduce_output option")
+ assert_raises_rpc_error(-1, "JSON integer out of range", rbf_node.bumpfee, rbfid, {"reduce_output": -1})
+ assert_raises_rpc_error(-8, "Change position is out of range", rbf_node.bumpfee, rbfid, {"reduce_output": 2})
+
+ self.log.info("Test outputs and reduce_output cannot both be provided")
+ assert_raises_rpc_error(-8, "Cannot specify both new outputs to use and an output index to reduce", rbf_node.bumpfee, rbfid, {"reduce_output": 2, "outputs": [{dest_address: 0.1}]})
+
self.clear_mempool()
def test_bump_back_to_yourself(self):
@@ -225,6 +236,72 @@ class BumpFeeTest(BitcoinTestFramework):
node.unloadwallet("back_to_yourself")
+ def test_provided_change_pos(self, rbf_node):
+ self.log.info("Test the reduce_output option")
+
+ change_addr = rbf_node.getnewaddress()
+ dest_addr = rbf_node.getnewaddress()
+ assert_equal(rbf_node.getaddressinfo(change_addr)["ischange"], False)
+ assert_equal(rbf_node.getaddressinfo(dest_addr)["ischange"], False)
+
+ send_res = rbf_node.send(outputs=[{dest_addr: 1}], options={"change_address": change_addr})
+ assert send_res["complete"]
+ txid = send_res["txid"]
+
+ tx = rbf_node.gettransaction(txid=txid, verbose=True)
+ assert_equal(len(tx["decoded"]["vout"]), 2)
+
+ change_pos = find_vout_for_address(rbf_node, txid, change_addr)
+ change_value = tx["decoded"]["vout"][change_pos]["value"]
+
+ bumped = rbf_node.bumpfee(txid, {"reduce_output": change_pos})
+ new_txid = bumped["txid"]
+
+ new_tx = rbf_node.gettransaction(txid=new_txid, verbose=True)
+ assert_equal(len(new_tx["decoded"]["vout"]), 2)
+ new_change_pos = find_vout_for_address(rbf_node, new_txid, change_addr)
+ new_change_value = new_tx["decoded"]["vout"][new_change_pos]["value"]
+
+ assert_greater_than(change_value, new_change_value)
+
+
+ def test_single_output(self):
+ self.log.info("Test that single output txs can be bumped")
+ node = self.nodes[1]
+
+ node.createwallet("single_out_rbf")
+ wallet = node.get_wallet_rpc("single_out_rbf")
+
+ addr = wallet.getnewaddress()
+ amount = Decimal("0.001")
+ # Make 2 UTXOs
+ self.nodes[0].sendtoaddress(addr, amount)
+ self.nodes[0].sendtoaddress(addr, amount)
+ self.generate(self.nodes[0], 1)
+ utxos = wallet.listunspent()
+
+ tx = wallet.sendall(recipients=[wallet.getnewaddress()], fee_rate=2, options={"inputs": [utxos[0]]})
+
+ # Reduce the only output with a crazy high feerate, should fail as the output would be dust
+ assert_raises_rpc_error(-4, "The transaction amount is too small to pay the fee", wallet.bumpfee, txid=tx["txid"], options={"fee_rate": 1100, "reduce_output": 0})
+
+ # Reduce the only output successfully
+ bumped = wallet.bumpfee(txid=tx["txid"], options={"fee_rate": 10, "reduce_output": 0})
+ bumped_tx = wallet.gettransaction(txid=bumped["txid"], verbose=True)
+ assert_equal(len(bumped_tx["decoded"]["vout"]), 1)
+ assert_equal(len(bumped_tx["decoded"]["vin"]), 1)
+ assert_equal(bumped_tx["decoded"]["vout"][0]["value"] + bumped["fee"], amount)
+ assert_fee_amount(bumped["fee"], bumped_tx["decoded"]["vsize"], Decimal(10) / Decimal(1e8) * 1000)
+
+ # Bumping without reducing adds a new input and output
+ bumped = wallet.bumpfee(txid=bumped["txid"], options={"fee_rate": 20})
+ bumped_tx = wallet.gettransaction(txid=bumped["txid"], verbose=True)
+ assert_equal(len(bumped_tx["decoded"]["vout"]), 2)
+ assert_equal(len(bumped_tx["decoded"]["vin"]), 2)
+ assert_fee_amount(bumped["fee"], bumped_tx["decoded"]["vsize"], Decimal(20) / Decimal(1e8) * 1000)
+
+ wallet.unloadwallet()
+
def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
self.log.info('Test simple bumpfee: {}'.format(mode))
rbfid = spend_one_input(rbf_node, dest_address)
diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py
index 278ef1ec87..925376e8cd 100755
--- a/test/functional/wallet_migration.py
+++ b/test/functional/wallet_migration.py
@@ -8,6 +8,8 @@ import random
import shutil
from test_framework.descriptors import descsum_create
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.messages import COIN, CTransaction, CTxOut
+from test_framework.script_util import key_to_p2pkh_script, script_to_p2sh_script, script_to_p2wsh_script
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
@@ -639,6 +641,53 @@ class WalletMigrationTest(BitcoinTestFramework):
for addr_info in [addr_external, addr_external_with_label]:
check(addr_info, wallet_solvables)
+ def test_migrate_raw_p2sh(self):
+ self.log.info("Test migration of watch-only raw p2sh script")
+ df_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+ wallet = self.create_legacy_wallet("raw_p2sh")
+
+ def send_to_script(script, amount):
+ tx = CTransaction()
+ tx.vout.append(CTxOut(nValue=amount*COIN, scriptPubKey=script))
+
+ hex_tx = df_wallet.fundrawtransaction(tx.serialize().hex())['hex']
+ signed_tx = df_wallet.signrawtransactionwithwallet(hex_tx)
+ df_wallet.sendrawtransaction(signed_tx['hex'])
+ self.generate(self.nodes[0], 1)
+
+ # Craft sh(pkh(key)) script and send coins to it
+ pubkey = df_wallet.getaddressinfo(df_wallet.getnewaddress())["pubkey"]
+ script_pkh = key_to_p2pkh_script(pubkey)
+ script_sh_pkh = script_to_p2sh_script(script_pkh)
+ send_to_script(script=script_sh_pkh, amount=2)
+
+ # Import script and check balance
+ wallet.rpc.importaddress(address=script_pkh.hex(), label="raw_spk", rescan=True, p2sh=True)
+ assert_equal(wallet.getbalances()['watchonly']['trusted'], 2)
+
+ # Craft wsh(pkh(key)) and send coins to it
+ pubkey = df_wallet.getaddressinfo(df_wallet.getnewaddress())["pubkey"]
+ script_wsh_pkh = script_to_p2wsh_script(key_to_p2pkh_script(pubkey))
+ send_to_script(script=script_wsh_pkh, amount=3)
+
+ # Import script and check balance
+ wallet.rpc.importaddress(address=script_wsh_pkh.hex(), label="raw_spk2", rescan=True, p2sh=False)
+ assert_equal(wallet.getbalances()['watchonly']['trusted'], 5)
+
+ # Migrate wallet and re-check balance
+ info_migration = wallet.migratewallet()
+ wallet_wo = self.nodes[0].get_wallet_rpc(info_migration["watchonly_name"])
+
+ # Watch-only balance is under "mine".
+ assert_equal(wallet_wo.getbalances()['mine']['trusted'], 5)
+ # The watch-only scripts are no longer part of the main wallet
+ assert_equal(wallet.getbalances()['mine']['trusted'], 0)
+
+ # Just in case, also verify wallet restart
+ self.nodes[0].unloadwallet(info_migration["watchonly_name"])
+ self.nodes[0].loadwallet(info_migration["watchonly_name"])
+ assert_equal(wallet_wo.getbalances()['mine']['trusted'], 5)
+
def run_test(self):
self.generate(self.nodes[0], 101)
@@ -654,6 +703,7 @@ class WalletMigrationTest(BitcoinTestFramework):
self.test_default_wallet()
self.test_direct_file()
self.test_addressbook()
+ self.test_migrate_raw_p2sh()
if __name__ == '__main__':
WalletMigrationTest().main()
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 7bdb6f5e3a..f36d8efda7 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -108,9 +108,13 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
# Set correct m_best_block_time, which is used in ResubmitWalletTransactions
node.syncwithvalidationinterfacequeue()
- # Evict these txs from the mempool
evict_time = block_time + 60 * 60 * DEFAULT_MEMPOOL_EXPIRY_HOURS + 5
- node.setmocktime(evict_time)
+ # Flush out currently scheduled resubmit attempt now so that there can't be one right between eviction and check.
+ with node.assert_debug_log(['resubmit 2 unconfirmed transactions']):
+ node.setmocktime(evict_time)
+ node.mockscheduler(60)
+
+ # Evict these txs from the mempool
indep_send = node.send(outputs=[{node.getnewaddress(): 1}], inputs=[indep_utxo])
node.getmempoolentry(indep_send["txid"])
assert_raises_rpc_error(-5, "Transaction not in mempool", node.getmempoolentry, txid)
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index ef1583d446..1ce7c71360 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -193,27 +193,52 @@ def main():
)
-def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
- """Generates new corpus.
+def transform_process_message_target(targets, src_dir):
+ """Add a target per process message, and also keep ("process_message", {}) to allow for
+ cross-pollination, or unlimited search"""
+
+ p2p_msg_target = "process_message"
+ if (p2p_msg_target, {}) in targets:
+ lines = subprocess.run(
+ ["git", "grep", "--function-context", "g_all_net_message_types{", src_dir / "src" / "protocol.cpp"],
+ check=True,
+ stdout=subprocess.PIPE,
+ text=True,
+ ).stdout.splitlines()
+ lines = [l.split("::", 1)[1].split(",")[0].lower() for l in lines if l.startswith("src/protocol.cpp- NetMsgType::")]
+ assert len(lines)
+ targets += [(p2p_msg_target, {"LIMIT_TO_MESSAGE_TYPE": m}) for m in lines]
+ return targets
+
+
+def transform_rpc_target(targets, src_dir):
+ """Add a target per RPC command, and also keep ("rpc", {}) to allow for cross-pollination,
+ or unlimited search"""
- Run {targets} without input, and outputs the generated corpus to
- {corpus_dir}.
- """
- logging.info("Generating corpus to {}".format(corpus_dir))
rpc_target = "rpc"
- has_rpc = rpc_target in targets
- if has_rpc:
- targets.remove(rpc_target)
- targets = [(t, {}) for t in targets]
- if has_rpc:
+ if (rpc_target, {}) in targets:
lines = subprocess.run(
- ["git", "grep", "--function-context", "RPC_COMMANDS_SAFE_FOR_FUZZING{", os.path.join(src_dir, "src", "test", "fuzz", "rpc.cpp")],
+ ["git", "grep", "--function-context", "RPC_COMMANDS_SAFE_FOR_FUZZING{", src_dir / "src" / "test" / "fuzz" / "rpc.cpp"],
check=True,
stdout=subprocess.PIPE,
text=True,
).stdout.splitlines()
lines = [l.split("\"", 1)[1].split("\"")[0] for l in lines if l.startswith("src/test/fuzz/rpc.cpp- \"")]
+ assert len(lines)
targets += [(rpc_target, {"LIMIT_TO_RPC_COMMAND": r}) for r in lines]
+ return targets
+
+
+def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
+ """Generates new corpus.
+
+ Run {targets} without input, and outputs the generated corpus to
+ {corpus_dir}.
+ """
+ logging.info("Generating corpus to {}".format(corpus_dir))
+ targets = [(t, {}) for t in targets] # expand to add dictionary for target-specific env variables
+ targets = transform_process_message_target(targets, Path(src_dir))
+ targets = transform_rpc_target(targets, Path(src_dir))
def job(command, t, t_env):
logging.debug(f"Running '{command}'")