aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.qt.include4
-rw-r--r--src/addrman.cpp44
-rw-r--r--src/addrman.h96
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/bench/duplicate_inputs.cpp1
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/chain.h2
-rw-r--r--src/chainparams.cpp2
-rw-r--r--src/chainparamsseeds.h512
-rw-r--r--src/consensus/tx_verify.h2
-rw-r--r--src/index/base.cpp1
-rw-r--r--src/init.cpp24
-rw-r--r--src/init.h2
-rw-r--r--src/init/bitcoin-node.cpp2
-rw-r--r--src/init/bitcoind.cpp2
-rw-r--r--src/interfaces/chain.h3
-rw-r--r--src/interfaces/node.h6
-rw-r--r--src/interfaces/wallet.h19
-rw-r--r--src/key_io.cpp15
-rw-r--r--src/miner.cpp12
-rw-r--r--src/net.cpp122
-rw-r--r--src/net.h26
-rw-r--r--src/net_processing.cpp127
-rw-r--r--src/netaddress.cpp70
-rw-r--r--src/netaddress.h40
-rw-r--r--src/node/blockstorage.cpp1
-rw-r--r--src/node/coin.cpp1
-rw-r--r--src/node/coinstats.cpp1
-rw-r--r--src/node/context.cpp1
-rw-r--r--src/node/context.h2
-rw-r--r--src/node/interfaces.cpp62
-rw-r--r--src/node/transaction.cpp1
-rw-r--r--src/policy/packages.cpp62
-rw-r--r--src/policy/packages.h10
-rw-r--r--src/pubkey.cpp44
-rw-r--r--src/pubkey.h42
-rw-r--r--src/qt/addressbookpage.cpp8
-rw-r--r--src/qt/bitcoin.cpp4
-rw-r--r--src/qt/bitcoingui.cpp27
-rw-r--r--src/qt/coincontroldialog.cpp12
-rw-r--r--src/qt/createwalletdialog.cpp66
-rw-r--r--src/qt/createwalletdialog.h9
-rw-r--r--src/qt/forms/createwalletdialog.ui11
-rw-r--r--src/qt/forms/debugwindow.ui56
-rw-r--r--src/qt/forms/optionsdialog.ui30
-rw-r--r--src/qt/forms/receiverequestdialog.ui13
-rw-r--r--src/qt/optionsdialog.cpp2
-rw-r--r--src/qt/optionsmodel.cpp15
-rw-r--r--src/qt/optionsmodel.h1
-rw-r--r--src/qt/overviewpage.cpp9
-rw-r--r--src/qt/overviewpage.h1
-rw-r--r--src/qt/peertablemodel.cpp97
-rw-r--r--src/qt/peertablemodel.h21
-rw-r--r--src/qt/psbtoperationsdialog.cpp6
-rw-r--r--src/qt/qrimagewidget.cpp4
-rw-r--r--src/qt/receivecoinsdialog.cpp10
-rw-r--r--src/qt/receiverequestdialog.cpp6
-rw-r--r--src/qt/recentrequeststablemodel.cpp12
-rw-r--r--src/qt/rpcconsole.cpp68
-rw-r--r--src/qt/rpcconsole.h5
-rw-r--r--src/qt/sendcoinsdialog.cpp80
-rw-r--r--src/qt/test/apptests.cpp5
-rw-r--r--src/qt/test/wallettests.cpp26
-rw-r--r--src/qt/transactionview.cpp20
-rw-r--r--src/qt/walletcontroller.cpp14
-rw-r--r--src/qt/walletframe.cpp36
-rw-r--r--src/qt/walletframe.h11
-rw-r--r--src/qt/walletmodel.cpp33
-rw-r--r--src/qt/walletmodel.h4
-rw-r--r--src/qt/walletmodeltransaction.cpp5
-rw-r--r--src/qt/walletmodeltransaction.h2
-rw-r--r--src/qt/walletview.cpp7
-rw-r--r--src/qt/walletview.h3
-rw-r--r--src/rest.cpp2
-rw-r--r--src/rpc/blockchain.cpp1
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp10
-rw-r--r--src/rpc/util.cpp10
-rw-r--r--src/script/descriptor.cpp185
-rw-r--r--src/script/interpreter.cpp9
-rw-r--r--src/script/interpreter.h4
-rw-r--r--src/script/standard.cpp116
-rw-r--r--src/script/standard.h91
-rw-r--r--src/secp256k1/.cirrus.yml198
-rw-r--r--src/secp256k1/.travis.yml108
-rw-r--r--src/secp256k1/Makefile.am8
-rw-r--r--src/secp256k1/README.md4
-rw-r--r--src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m42
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m413
-rwxr-xr-xsrc/secp256k1/ci/cirrus.sh (renamed from src/secp256k1/contrib/travis.sh)52
-rw-r--r--src/secp256k1/ci/linux-debian.Dockerfile13
-rw-r--r--src/secp256k1/configure.ac279
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.c10
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.h10
-rw-r--r--src/secp256k1/contrib/lax_der_privatekey_parsing.c10
-rw-r--r--src/secp256k1/contrib/lax_der_privatekey_parsing.h10
-rw-r--r--src/secp256k1/doc/safegcd_implementation.md765
-rw-r--r--src/secp256k1/include/secp256k1.h47
-rw-r--r--src/secp256k1/include/secp256k1_extrakeys.h13
-rw-r--r--src/secp256k1/include/secp256k1_recovery.h24
-rw-r--r--src/secp256k1/sage/gen_exhaustive_groups.sage7
-rw-r--r--src/secp256k1/sage/gen_split_lambda_constants.sage114
-rw-r--r--src/secp256k1/sage/group_prover.sage23
-rw-r--r--src/secp256k1/sage/prove_group_implementations.sage (renamed from src/secp256k1/sage/secp256k1.sage)0
-rw-r--r--src/secp256k1/sage/secp256k1_params.sage36
-rw-r--r--src/secp256k1/sage/weierstrass_prover.sage32
-rw-r--r--src/secp256k1/src/asm/field_10x26_arm.s10
-rw-r--r--src/secp256k1/src/assumptions.h10
-rw-r--r--src/secp256k1/src/basic-config.h29
-rw-r--r--src/secp256k1/src/bench.h10
-rw-r--r--src/secp256k1/src/bench_ecdh.c10
-rw-r--r--src/secp256k1/src/bench_ecmult.c11
-rw-r--r--src/secp256k1/src/bench_internal.c73
-rw-r--r--src/secp256k1/src/bench_recover.c10
-rw-r--r--src/secp256k1/src/bench_schnorrsig.c10
-rw-r--r--src/secp256k1/src/bench_sign.c18
-rw-r--r--src/secp256k1/src/bench_verify.c26
-rw-r--r--src/secp256k1/src/ecdsa.h10
-rw-r--r--src/secp256k1/src/ecdsa_impl.h10
-rw-r--r--src/secp256k1/src/eckey.h10
-rw-r--r--src/secp256k1/src/eckey_impl.h10
-rw-r--r--src/secp256k1/src/ecmult.h11
-rw-r--r--src/secp256k1/src/ecmult_const.h10
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h10
-rw-r--r--src/secp256k1/src/ecmult_gen.h10
-rw-r--r--src/secp256k1/src/ecmult_gen_impl.h12
-rw-r--r--src/secp256k1/src/ecmult_impl.h16
-rw-r--r--src/secp256k1/src/field.h29
-rw-r--r--src/secp256k1/src/field_10x26.h10
-rw-r--r--src/secp256k1/src/field_10x26_impl.h103
-rw-r--r--src/secp256k1/src/field_5x52.h10
-rw-r--r--src/secp256k1/src/field_5x52_asm_impl.h10
-rw-r--r--src/secp256k1/src/field_5x52_impl.h91
-rw-r--r--src/secp256k1/src/field_5x52_int128_impl.h10
-rw-r--r--src/secp256k1/src/field_impl.h190
-rw-r--r--src/secp256k1/src/gen_context.c23
-rw-r--r--src/secp256k1/src/group.h25
-rw-r--r--src/secp256k1/src/group_impl.h37
-rw-r--r--src/secp256k1/src/hash.h10
-rw-r--r--src/secp256k1/src/hash_impl.h10
-rw-r--r--src/secp256k1/src/modinv32.h42
-rw-r--r--src/secp256k1/src/modinv32_impl.h587
-rw-r--r--src/secp256k1/src/modinv64.h46
-rw-r--r--src/secp256k1/src/modinv64_impl.h593
-rw-r--r--src/secp256k1/src/modules/ecdh/main_impl.h10
-rw-r--r--src/secp256k1/src/modules/ecdh/tests_impl.h10
-rw-r--r--src/secp256k1/src/modules/extrakeys/main_impl.h26
-rw-r--r--src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h14
-rw-r--r--src/secp256k1/src/modules/extrakeys/tests_impl.h41
-rw-r--r--src/secp256k1/src/modules/recovery/main_impl.h22
-rw-r--r--src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h10
-rw-r--r--src/secp256k1/src/modules/recovery/tests_impl.h10
-rw-r--r--src/secp256k1/src/modules/schnorrsig/main_impl.h16
-rw-r--r--src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h14
-rw-r--r--src/secp256k1/src/modules/schnorrsig/tests_impl.h16
-rw-r--r--src/secp256k1/src/num.h74
-rw-r--r--src/secp256k1/src/num_gmp.h20
-rw-r--r--src/secp256k1/src/num_gmp_impl.h288
-rw-r--r--src/secp256k1/src/num_impl.h24
-rw-r--r--src/secp256k1/src/scalar.h22
-rw-r--r--src/secp256k1/src/scalar_4x64.h10
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h252
-rw-r--r--src/secp256k1/src/scalar_8x32.h10
-rw-r--r--src/secp256k1/src/scalar_8x32_impl.h189
-rw-r--r--src/secp256k1/src/scalar_impl.h229
-rw-r--r--src/secp256k1/src/scalar_low.h10
-rw-r--r--src/secp256k1/src/scalar_low_impl.h29
-rw-r--r--src/secp256k1/src/scratch.h16
-rw-r--r--src/secp256k1/src/scratch_impl.h14
-rw-r--r--src/secp256k1/src/secp256k1.c81
-rw-r--r--src/secp256k1/src/selftest.h10
-rw-r--r--src/secp256k1/src/testrand.h10
-rw-r--r--src/secp256k1/src/testrand_impl.h10
-rw-r--r--src/secp256k1/src/tests.c1729
-rw-r--r--src/secp256k1/src/tests_exhaustive.c8
-rw-r--r--src/secp256k1/src/util.h89
-rw-r--r--src/secp256k1/src/valgrind_ctime_test.c78
-rw-r--r--src/test/addrman_tests.cpp4
-rw-r--r--src/test/blockfilter_index_tests.cpp26
-rw-r--r--src/test/coinstatsindex_tests.cpp8
-rw-r--r--src/test/denialofservice_tests.cpp49
-rw-r--r--src/test/fuzz/addrman.cpp22
-rw-r--r--src/test/fuzz/coins_view.cpp9
-rw-r--r--src/test/fuzz/float.cpp29
-rw-r--r--src/test/fuzz/fuzz.cpp9
-rw-r--r--src/test/fuzz/load_external_block_file.cpp7
-rw-r--r--src/test/fuzz/node_eviction.cpp2
-rw-r--r--src/test/fuzz/util.h2
-rw-r--r--src/test/fuzz/validation_load_mempool.cpp7
-rw-r--r--src/test/interfaces_tests.cpp2
-rw-r--r--src/test/miner_tests.cpp93
-rw-r--r--src/test/net_peer_eviction_tests.cpp504
-rw-r--r--src/test/net_tests.cpp39
-rw-r--r--src/test/netbase_tests.cpp36
-rw-r--r--src/test/script_standard_tests.cpp73
-rw-r--r--src/test/txindex_tests.cpp2
-rw-r--r--src/test/txvalidation_tests.cpp2
-rw-r--r--src/test/txvalidationcache_tests.cpp48
-rw-r--r--src/test/util/mining.cpp4
-rw-r--r--src/test/util/net.h12
-rw-r--r--src/test/util/setup_common.cpp23
-rw-r--r--src/test/util_tests.cpp19
-rw-r--r--src/test/validation_block_tests.cpp16
-rw-r--r--src/txmempool.cpp7
-rw-r--r--src/txmempool.h3
-rw-r--r--src/uint256.h2
-rw-r--r--src/util/spanparsing.cpp6
-rw-r--r--src/util/system.cpp11
-rw-r--r--src/validation.cpp195
-rw-r--r--src/validation.h41
-rw-r--r--src/wallet/db.cpp2
-rw-r--r--src/wallet/interfaces.cpp20
-rw-r--r--src/wallet/rpcdump.cpp12
-rw-r--r--src/wallet/rpcwallet.cpp1
-rw-r--r--src/wallet/spend.cpp499
-rw-r--r--src/wallet/test/coinselector_tests.cpp68
-rw-r--r--src/wallet/test/wallet_tests.cpp46
-rw-r--r--src/wallet/wallet.cpp53
-rw-r--r--src/wallet/wallet.h21
-rw-r--r--src/wallet/walletdb.cpp7
-rw-r--r--src/zmq/zmqutil.cpp8
-rw-r--r--src/zmq/zmqutil.h4
224 files changed, 7405 insertions, 4506 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 66cb7cec2a..e2ed70556d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -351,6 +351,7 @@ libbitcoin_server_a_SOURCES = \
node/ui_interface.cpp \
noui.cpp \
policy/fees.cpp \
+ policy/packages.cpp \
policy/rbf.cpp \
policy/settings.cpp \
pow.cpp \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 9ad66bc85b..a1821cafe3 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -365,12 +365,12 @@ translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCO
$(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM)
@test -f $(RCC)
@cp -f $< $(@D)/temp_$(<F)
- $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin_locale $(@D)/temp_$(<F) > $@
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin_locale --format-version 1 $(@D)/temp_$(<F) > $@
@rm $(@D)/temp_$(<F)
$(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_FONTS) $(RES_ICONS) $(RES_ANIMATION)
@test -f $(RCC)
- $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin $< > $@
+ $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin --format-version 1 $< > $@
CLEAN_QT = $(nodist_qt_libbitcoinqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno qt/temp_bitcoin_locale.qrc
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 14b412a038..8f702b5a8c 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -12,6 +12,8 @@
#include <cmath>
#include <optional>
+#include <unordered_map>
+#include <unordered_set>
int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
{
@@ -77,12 +79,14 @@ double CAddrInfo::GetChance(int64_t nNow) const
CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
{
- std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
+ AssertLockHeld(cs);
+
+ const auto it = mapAddr.find(addr);
if (it == mapAddr.end())
return nullptr;
if (pnId)
*pnId = (*it).second;
- std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
+ const auto it2 = mapInfo.find((*it).second);
if (it2 != mapInfo.end())
return &(*it2).second;
return nullptr;
@@ -90,6 +94,8 @@ CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
{
+ AssertLockHeld(cs);
+
int nId = nIdCount++;
mapInfo[nId] = CAddrInfo(addr, addrSource);
mapAddr[addr] = nId;
@@ -102,6 +108,8 @@ CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, in
void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
{
+ AssertLockHeld(cs);
+
if (nRndPos1 == nRndPos2)
return;
@@ -122,6 +130,8 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
void CAddrMan::Delete(int nId)
{
+ AssertLockHeld(cs);
+
assert(mapInfo.count(nId) != 0);
CAddrInfo& info = mapInfo[nId];
assert(!info.fInTried);
@@ -136,6 +146,8 @@ void CAddrMan::Delete(int nId)
void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
{
+ AssertLockHeld(cs);
+
// if there is an entry in the specified bucket, delete it.
if (vvNew[nUBucket][nUBucketPos] != -1) {
int nIdDelete = vvNew[nUBucket][nUBucketPos];
@@ -151,6 +163,8 @@ void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
void CAddrMan::MakeTried(CAddrInfo& info, int nId)
{
+ AssertLockHeld(cs);
+
// remove the entry from all new buckets
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
int pos = info.GetBucketPosition(nKey, true, bucket);
@@ -199,6 +213,8 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
{
+ AssertLockHeld(cs);
+
int nId;
nLastGood = nTime;
@@ -265,6 +281,8 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
{
+ AssertLockHeld(cs);
+
if (!addr.IsRoutable())
return false;
@@ -338,6 +356,8 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -360,7 +380,9 @@ void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
CAddrInfo CAddrMan::Select_(bool newOnly)
{
- if (size() == 0)
+ AssertLockHeld(cs);
+
+ if (vRandom.empty())
return CAddrInfo();
if (newOnly && nNew == 0)
@@ -408,8 +430,10 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
#ifdef DEBUG_ADDRMAN
int CAddrMan::Check_()
{
- std::set<int> setTried;
- std::map<int, int> mapNew;
+ AssertLockHeld(cs);
+
+ std::unordered_set<int> setTried;
+ std::unordered_map<int, int> mapNew;
if (vRandom.size() != (size_t)(nTried + nNew))
return -7;
@@ -485,6 +509,8 @@ int CAddrMan::Check_()
void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network)
{
+ AssertLockHeld(cs);
+
size_t nNodes = vRandom.size();
if (max_pct != 0) {
nNodes = max_pct * nNodes / 100;
@@ -517,6 +543,8 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
void CAddrMan::Connected_(const CService& addr, int64_t nTime)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -537,6 +565,8 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime)
void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
{
+ AssertLockHeld(cs);
+
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
@@ -555,6 +585,8 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
void CAddrMan::ResolveCollisions_()
{
+ AssertLockHeld(cs);
+
for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
int id_new = *it;
@@ -614,6 +646,8 @@ void CAddrMan::ResolveCollisions_()
CAddrInfo CAddrMan::SelectTriedCollision_()
{
+ AssertLockHeld(cs);
+
if (m_tried_collisions.size() == 0) return CAddrInfo();
std::set<int>::iterator it = m_tried_collisions.begin();
diff --git a/src/addrman.h b/src/addrman.h
index 41994288db..665e253192 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -8,22 +8,22 @@
#include <clientversion.h>
#include <config/bitcoin-config.h>
+#include <fs.h>
+#include <hash.h>
#include <netaddress.h>
#include <protocol.h>
#include <random.h>
+#include <streams.h>
#include <sync.h>
#include <timedata.h>
#include <tinyformat.h>
#include <util/system.h>
-#include <fs.h>
-#include <hash.h>
#include <iostream>
-#include <map>
#include <optional>
#include <set>
#include <stdint.h>
-#include <streams.h>
+#include <unordered_map>
#include <vector>
/**
@@ -231,6 +231,7 @@ public:
*/
template <typename Stream>
void Serialize(Stream& s_) const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
@@ -251,7 +252,7 @@ public:
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
s << nUBuckets;
- std::map<int, int> mapUnkIds;
+ std::unordered_map<int, int> mapUnkIds;
int nIds = 0;
for (const auto& entry : mapInfo) {
mapUnkIds[entry.first] = nIds;
@@ -296,10 +297,11 @@ public:
template <typename Stream>
void Unserialize(Stream& s_)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
- Clear();
+ assert(vRandom.empty());
Format format;
s_ >> Using<CustomUintFormatter<1>>(format);
@@ -435,13 +437,13 @@ public:
// Prune new entries with refcount 0 (as a result of collisions).
int nLostUnk = 0;
- for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
+ for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
if (it->second.fInTried == false && it->second.nRefCount == 0) {
- std::map<int, CAddrInfo>::const_iterator itCopy = it++;
+ const auto itCopy = it++;
Delete(itCopy->first);
- nLostUnk++;
+ ++nLostUnk;
} else {
- it++;
+ ++it;
}
}
if (nLost + nLostUnk > 0) {
@@ -452,6 +454,7 @@ public:
}
void Clear()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
std::vector<int>().swap(vRandom);
@@ -487,26 +490,15 @@ public:
//! Return the number of (unique) addresses in all tables.
size_t size() const
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead
return vRandom.size();
}
- //! Consistency check
- void Check()
- {
-#ifdef DEBUG_ADDRMAN
- {
- LOCK(cs);
- int err;
- if ((err=Check_()))
- LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
- }
-#endif
- }
-
//! Add a single address.
bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
bool fRet = false;
@@ -521,6 +513,7 @@ public:
//! Add multiple addresses.
bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
int nAdd = 0;
@@ -536,6 +529,7 @@ public:
//! Mark an entry as accessible.
void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -545,6 +539,7 @@ public:
//! Mark an entry as connection attempted to.
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -554,6 +549,7 @@ public:
//! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
void ResolveCollisions()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -563,14 +559,12 @@ public:
//! Randomly select an address in tried that another address is attempting to evict.
CAddrInfo SelectTriedCollision()
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
- CAddrInfo ret;
- {
- LOCK(cs);
- Check();
- ret = SelectTriedCollision_();
- Check();
- }
+ LOCK(cs);
+ Check();
+ const CAddrInfo ret = SelectTriedCollision_();
+ Check();
return ret;
}
@@ -578,14 +572,12 @@ public:
* Choose an address to connect to.
*/
CAddrInfo Select(bool newOnly = false)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
- CAddrInfo addrRet;
- {
- LOCK(cs);
- Check();
- addrRet = Select_(newOnly);
- Check();
- }
+ LOCK(cs);
+ Check();
+ const CAddrInfo addrRet = Select_(newOnly);
+ Check();
return addrRet;
}
@@ -597,19 +589,19 @@ public:
* @param[in] network Select only addresses of this network (nullopt = all).
*/
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
+ LOCK(cs);
Check();
std::vector<CAddress> vAddr;
- {
- LOCK(cs);
- GetAddr_(vAddr, max_addresses, max_pct, network);
- }
+ GetAddr_(vAddr, max_addresses, max_pct, network);
Check();
return vAddr;
}
//! Outer function for Connected_()
void Connected(const CService &addr, int64_t nTime = GetAdjustedTime())
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -618,6 +610,7 @@ public:
}
void SetServices(const CService &addr, ServiceFlags nServices)
+ EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
Check();
@@ -633,8 +626,8 @@ protected:
FastRandomContext insecure_rand;
private:
- //! critical section to protect the inner data structures
- mutable RecursiveMutex cs;
+ //! A mutex to protect the inner data structures.
+ mutable Mutex cs;
//! Serialization versions.
enum Format : uint8_t {
@@ -662,10 +655,10 @@ private:
int nIdCount GUARDED_BY(cs);
//! table with information about all nIds
- std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
+ std::unordered_map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
//! find an nId based on its network address
- std::map<CNetAddr, int> mapAddr GUARDED_BY(cs);
+ std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
std::vector<int> vRandom GUARDED_BY(cs);
@@ -725,6 +718,19 @@ private:
//! Return a random to-be-evicted tried table address.
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+ //! Consistency check
+ void Check()
+ EXCLUSIVE_LOCKS_REQUIRED(cs)
+ {
+#ifdef DEBUG_ADDRMAN
+ AssertLockHeld(cs);
+ const int err = Check_();
+ if (err) {
+ LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
+ }
+#endif
+ }
+
#ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index c279a9af2f..5beb833b48 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -56,7 +56,7 @@ static void CoinSelection(benchmark::Bench& bench)
bench.run([&] {
std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
- bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
+ bool success = wallet.AttemptSelection(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
assert(success);
assert(nValueRet == 1003 * COIN);
assert(setCoinsRet.size() == 2);
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 4f6e1122b8..8703a1cf94 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -25,7 +25,6 @@ static void DuplicateInputs(benchmark::Bench& bench)
CMutableTransaction naughtyTx{};
LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(testing_setup->m_node.chainman->ActiveChain()));
CBlockIndex* pindexPrev = testing_setup->m_node.chainman->ActiveChain().Tip();
assert(pindexPrev != nullptr);
block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index cf9e4fad44..654679af27 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -112,8 +112,8 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
util::ThreadSetInternalName("init");
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
- SetupServerArgs(node);
ArgsManager& args = *Assert(node.args);
+ SetupServerArgs(args);
std::string error;
if (!args.ParseParameters(argc, argv, error)) {
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error)));
diff --git a/src/chain.h b/src/chain.h
index 04a5db5a17..84a3a4e1e7 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -182,7 +182,7 @@ public:
//!
//! Note: this value is modified to show BLOCK_OPT_WITNESS during UTXO snapshot
//! load to avoid the block index being spuriously rewound.
- //! @sa RewindBlockIndex
+ //! @sa NeedsRedownload
//! @sa ActivateSnapshot
uint32_t nStatus{0};
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 1b71c4db43..fdaadeed4a 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -423,7 +423,7 @@ public:
pchMessageStart[2] = 0xb5;
pchMessageStart[3] = 0xda;
nDefaultPort = 18444;
- nPruneAfterHeight = gArgs.GetBoolArg("-fastprune", false) ? 100 : 1000;
+ nPruneAfterHeight = args.GetBoolArg("-fastprune", false) ? 100 : 1000;
m_assumed_blockchain_size = 0;
m_assumed_chain_state_size = 0;
diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h
index 994d420885..08587e2b37 100644
--- a/src/chainparamsseeds.h
+++ b/src/chainparamsseeds.h
@@ -659,518 +659,6 @@ static const uint8_t chainparams_seed_main[] = {
0x02,0x10,0x2a,0x0f,0xdf,0x00,0x00,0x00,0x02,0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x20,0x8d,
0x02,0x10,0x2c,0x0f,0xf5,0x98,0x00,0x05,0x00,0x01,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
0x02,0x10,0x2c,0x0f,0xfc,0xe8,0x00,0x00,0x04,0x00,0x0b,0x7c,0x00,0x00,0x00,0x00,0x00,0x01,0x20,0x8d,
- 0x03,0x0a,0xd6,0xbc,0x4a,0x3c,0x6d,0x03,0xa9,0x4e,0x1f,0x55,0x20,0x8d,
- 0x03,0x0a,0xd6,0x8f,0xf0,0xf8,0xbb,0x10,0x00,0x18,0x42,0x54,0x20,0x8d,
- 0x03,0x0a,0xd6,0xec,0x32,0xc1,0x59,0x9c,0xd8,0x46,0xd5,0x48,0x20,0x8d,
- 0x03,0x0a,0xd6,0xf0,0x8d,0x96,0x37,0xc3,0x27,0x61,0x9a,0x24,0x20,0x8d,
- 0x03,0x0a,0xd0,0x12,0xe6,0xed,0x8e,0xc1,0x78,0x8d,0x1c,0x21,0x20,0x8d,
- 0x03,0x0a,0xd0,0x4a,0xc5,0xbd,0x5d,0xe9,0xca,0x57,0xaf,0xc4,0x20,0x8d,
- 0x03,0x0a,0xd0,0x94,0xc0,0x97,0xd2,0x32,0xed,0x81,0x92,0x67,0x20,0x8d,
- 0x03,0x0a,0xd1,0xd5,0x49,0x23,0xa6,0x10,0x01,0x49,0xda,0x05,0x20,0x8d,
- 0x03,0x0a,0xd2,0x2a,0x76,0x2c,0x37,0x09,0x9a,0xa1,0x61,0x4b,0x20,0x8d,
- 0x03,0x0a,0xd3,0x19,0x77,0x50,0xf5,0xf3,0x48,0x17,0x59,0x50,0x20,0x8d,
- 0x03,0x0a,0xd4,0x24,0xda,0xf8,0x97,0x6d,0x28,0x80,0x47,0xf9,0x20,0x8d,
- 0x03,0x0a,0xd4,0x28,0x30,0x9d,0x6d,0xac,0x1e,0xb4,0x6e,0x59,0x20,0x8d,
- 0x03,0x0a,0xd5,0x13,0x71,0x95,0xd5,0x2e,0x12,0xf6,0x0e,0x6e,0x20,0x8d,
- 0x03,0x0a,0xd5,0xc6,0x62,0x50,0xb1,0x22,0xb6,0x4a,0x31,0x56,0x20,0x8d,
- 0x03,0x0a,0xd5,0xc7,0x99,0x46,0x87,0x91,0x13,0xc9,0xc9,0x16,0x20,0x8d,
- 0x03,0x0a,0xdf,0x22,0x06,0xea,0xce,0x87,0x08,0x09,0x32,0x52,0x20,0x8d,
- 0x03,0x0a,0xdf,0xa1,0xf5,0x1c,0xe4,0x4e,0x97,0x71,0xee,0xc5,0x20,0x8d,
- 0x03,0x0a,0xdf,0xf7,0x64,0x8e,0x4f,0xa3,0xbb,0xaa,0x4f,0x30,0x20,0x8d,
- 0x03,0x0a,0xdf,0xfc,0xa5,0x92,0x7d,0xbc,0x03,0x13,0x69,0x35,0x20,0x8d,
- 0x03,0x0a,0xdf,0xd5,0x61,0xfc,0xb7,0x73,0xff,0xef,0x2f,0xaa,0x20,0x8d,
- 0x03,0x0a,0xd9,0x3b,0x3f,0x9e,0x1c,0x02,0xe7,0xd9,0xba,0xb7,0x20,0x8d,
- 0x03,0x0a,0xd9,0x83,0x73,0x90,0x25,0x3b,0xa9,0x4b,0x18,0x5b,0x20,0x8d,
- 0x03,0x0a,0xd9,0xcc,0x14,0xe4,0x9a,0x68,0x6d,0x8a,0x12,0xc5,0x20,0x8d,
- 0x03,0x0a,0xda,0x29,0x4a,0xc4,0x7a,0xb0,0x0e,0x0d,0x0a,0xee,0x20,0x8d,
- 0x03,0x0a,0xda,0x67,0x7a,0x24,0x60,0x45,0x8f,0xe4,0x2e,0x74,0x20,0x8d,
- 0x03,0x0a,0xda,0xfa,0x48,0x68,0x74,0xfb,0x2b,0x21,0x27,0x80,0x20,0x8d,
- 0x03,0x0a,0xdb,0x5c,0x56,0x99,0xb0,0x5c,0x08,0x43,0xb7,0xee,0x20,0x8d,
- 0x03,0x0a,0xdc,0x79,0xc1,0x8f,0x29,0x44,0xf2,0xdc,0x00,0xf6,0x20,0x8d,
- 0x03,0x0a,0xdd,0x66,0x1a,0x59,0x93,0x73,0x7f,0x58,0x76,0x19,0x20,0x8d,
- 0x03,0x0a,0xe7,0x9c,0x7c,0xce,0x79,0xe3,0xc8,0xa4,0x73,0x66,0x20,0x8d,
- 0x03,0x0a,0xe7,0xca,0xbd,0xa2,0xab,0xe5,0x7b,0xe4,0xca,0x71,0x20,0x8d,
- 0x03,0x0a,0xe7,0xd1,0xe8,0x45,0x7a,0x42,0x60,0x2b,0x2c,0xde,0x20,0x8d,
- 0x03,0x0a,0xe7,0xe9,0x47,0x9b,0x22,0x6c,0x6c,0x03,0xba,0x6e,0x20,0x8d,
- 0x03,0x0a,0xe0,0xba,0x25,0x23,0x7f,0x25,0x5c,0x51,0xcb,0xc3,0x20,0x8d,
- 0x03,0x0a,0xe1,0x21,0xbf,0x26,0x37,0xfd,0xe9,0x89,0x95,0xe2,0x20,0x8d,
- 0x03,0x0a,0xe1,0x2c,0xa1,0xde,0xa2,0x37,0x7e,0x01,0xc5,0xa8,0x20,0x8d,
- 0x03,0x0a,0xe1,0x57,0x53,0x20,0x2d,0x66,0x9a,0xb1,0xed,0xa0,0x20,0x8d,
- 0x03,0x0a,0xe2,0x00,0xe6,0xcf,0x0c,0xe7,0xd0,0xc0,0x58,0x9c,0x20,0x8d,
- 0x03,0x0a,0xe2,0x6f,0x9d,0xfd,0xce,0xa7,0x40,0x6f,0xfb,0x62,0x20,0x8d,
- 0x03,0x0a,0xe3,0x1a,0xaa,0xa7,0xc7,0x07,0xf6,0x48,0x34,0x2a,0x20,0x8d,
- 0x03,0x0a,0xe3,0x5b,0x4c,0x5d,0x9d,0x57,0x66,0xbc,0x26,0x1b,0x20,0x8d,
- 0x03,0x0a,0xe3,0x73,0xac,0x1b,0x82,0x6b,0xa6,0x4d,0x91,0x3f,0x20,0x8d,
- 0x03,0x0a,0xe3,0xdd,0x9b,0x1f,0xdd,0xf7,0x30,0x6c,0x8c,0x6a,0x20,0x8d,
- 0x03,0x0a,0xe4,0x0c,0x50,0xf7,0xd1,0xab,0xc2,0xc2,0x4a,0xff,0x20,0x8d,
- 0x03,0x0a,0xe4,0x64,0x0b,0xeb,0x73,0x04,0x33,0x66,0x21,0x89,0x20,0x8d,
- 0x03,0x0a,0xe5,0x3a,0x9e,0x83,0x1e,0x88,0x24,0xeb,0x4f,0x8c,0x20,0x8d,
- 0x03,0x0a,0xe5,0x5d,0x1a,0xcd,0xd8,0x21,0x8f,0xcc,0x86,0xb1,0x20,0x8d,
- 0x03,0x0a,0xee,0xa5,0xc4,0xf5,0xeb,0x1d,0x96,0xfc,0x9e,0x76,0x20,0x8d,
- 0x03,0x0a,0xe8,0x02,0xf4,0x22,0x05,0xa9,0x14,0xe2,0x26,0x2e,0x20,0x8d,
- 0x03,0x0a,0xe8,0x30,0x3c,0xde,0xfe,0x4e,0x1d,0x9d,0xb4,0x99,0x20,0x8d,
- 0x03,0x0a,0xe8,0xaf,0x91,0xca,0x33,0x72,0xba,0x33,0x3b,0x80,0x20,0x8d,
- 0x03,0x0a,0xe9,0x07,0x44,0x29,0xf4,0x1a,0x09,0xb4,0xe2,0x25,0x20,0x8d,
- 0x03,0x0a,0xe9,0x1e,0x40,0x15,0x4c,0xc0,0x38,0x5a,0xf4,0x7d,0x20,0x8d,
- 0x03,0x0a,0xe9,0x71,0x75,0xe6,0x68,0x16,0xe7,0xe6,0xba,0x79,0x20,0x8d,
- 0x03,0x0a,0xe9,0xc7,0xe2,0x60,0x96,0xee,0x02,0xd8,0x78,0xc1,0x20,0x8d,
- 0x03,0x0a,0xea,0x70,0x5c,0x9e,0xca,0x90,0x7d,0x48,0xc5,0xfa,0x20,0x8d,
- 0x03,0x0a,0xeb,0x5c,0xe8,0x18,0x53,0xef,0xbe,0x83,0x77,0xf5,0x20,0x8d,
- 0x03,0x0a,0xeb,0x64,0x56,0x71,0xb0,0x86,0x72,0xf8,0xa6,0x2f,0x20,0x8d,
- 0x03,0x0a,0xeb,0xa1,0x29,0xde,0x4f,0xc9,0xd6,0x64,0x90,0xbe,0x20,0x8d,
- 0x03,0x0a,0xeb,0xf3,0x96,0x0f,0x93,0x2b,0x9b,0x18,0x64,0x3a,0x20,0x8d,
- 0x03,0x0a,0xec,0x84,0xa6,0x5f,0x98,0xa0,0x82,0xd7,0xf1,0x0e,0x20,0x8d,
- 0x03,0x0a,0xed,0x09,0xfb,0x3a,0x39,0x0b,0x7c,0x77,0x37,0x64,0x20,0x8d,
- 0x03,0x0a,0xed,0xae,0x7b,0xea,0x6e,0xcb,0xdd,0x52,0xfb,0x3b,0x20,0x8d,
- 0x03,0x0a,0xed,0xd5,0xbc,0x51,0xbb,0xf1,0x37,0xa2,0x6f,0x88,0x20,0x8d,
- 0x03,0x0a,0xee,0x4c,0x79,0xe8,0xdf,0xa8,0xa4,0x07,0xa3,0xdd,0x20,0x8d,
- 0x03,0x0a,0xf6,0x86,0x21,0xbe,0xa3,0x72,0xcb,0x95,0x0f,0x2b,0x20,0x8d,
- 0x03,0x0a,0xf6,0xc2,0xa7,0x69,0x87,0x45,0xda,0xdd,0x07,0xe3,0x20,0x8d,
- 0x03,0x0a,0xf7,0xce,0x9a,0x96,0xbe,0xb2,0x05,0x30,0x2d,0x9d,0x20,0x8d,
- 0x03,0x0a,0xf1,0xbc,0xa7,0x71,0x4b,0x51,0x7a,0x09,0xac,0x68,0x20,0x8d,
- 0x03,0x0a,0xf2,0xbb,0x98,0x90,0x97,0xb7,0x04,0x01,0xdd,0x1d,0x20,0x8d,
- 0x03,0x0a,0xf2,0x8b,0xd0,0x60,0xeb,0x79,0x1b,0x8b,0x18,0x12,0x20,0x8d,
- 0x03,0x0a,0xf3,0x00,0x83,0x5d,0x35,0x11,0x27,0xc7,0xa2,0x64,0x20,0x8d,
- 0x03,0x0a,0xf4,0x49,0x29,0x57,0x83,0xab,0xd6,0x1e,0xa0,0xe7,0x20,0x8d,
- 0x03,0x0a,0xf4,0x52,0x4b,0xf8,0xd8,0xa0,0x28,0x8d,0x8b,0xa4,0x20,0x8d,
- 0x03,0x0a,0xf4,0x94,0x66,0x97,0x9b,0x7b,0xce,0x3a,0xa6,0x80,0x20,0x8d,
- 0x03,0x0a,0xf4,0xa7,0x70,0x38,0x74,0xb2,0x24,0x6e,0xca,0x07,0x20,0x8d,
- 0x03,0x0a,0xf5,0xe1,0x8e,0x4e,0x5e,0x0b,0xbd,0x4e,0x8c,0xcc,0x20,0x8d,
- 0x03,0x0a,0xf8,0x2a,0xd5,0xec,0x70,0x79,0xa9,0xad,0xa6,0xa0,0x20,0x8d,
- 0x03,0x0a,0xf9,0x4b,0xcb,0x2b,0x5e,0xf3,0x5d,0xad,0xce,0xed,0x20,0x8d,
- 0x03,0x0a,0xf9,0xd0,0xf0,0xd3,0x25,0x18,0xb1,0x98,0x29,0x46,0x20,0x8d,
- 0x03,0x0a,0xfc,0x92,0xc5,0xe6,0x33,0x3a,0x56,0xf2,0xe0,0x6a,0x20,0x8d,
- 0x03,0x0a,0xfc,0xe9,0x3d,0xe6,0x7a,0x02,0xad,0x16,0x5b,0xd7,0x20,0x8d,
- 0x03,0x0a,0xfd,0x0f,0x24,0xe5,0x3e,0x6d,0xf6,0x32,0xb6,0xf3,0x20,0x8d,
- 0x03,0x0a,0xfd,0x99,0xcb,0x49,0xdb,0xb5,0x41,0x3b,0xb4,0x33,0x20,0x8d,
- 0x03,0x0a,0xfe,0x14,0xcc,0xd3,0x01,0xb0,0xf4,0xf9,0xe4,0xdc,0x20,0x8d,
- 0x03,0x0a,0x06,0xbe,0x1a,0x9d,0x0d,0x07,0x31,0xad,0xa6,0xee,0x20,0x8d,
- 0x03,0x0a,0x07,0x77,0x59,0x8d,0x9f,0xa2,0x09,0x3e,0xd4,0x6b,0x20,0x8d,
- 0x03,0x0a,0x07,0x7d,0xdf,0xea,0xe9,0xa3,0x8a,0xd9,0xe8,0x6f,0x20,0x8d,
- 0x03,0x0a,0x00,0x5f,0xae,0xa9,0xa8,0x28,0xe4,0xd1,0x6a,0x35,0x20,0x8d,
- 0x03,0x0a,0x01,0x0b,0x7f,0xd0,0x39,0x78,0x17,0xf1,0x2c,0x0a,0x20,0x8d,
- 0x03,0x0a,0x02,0x3b,0x1d,0x0d,0x0e,0xcb,0x89,0xf8,0xc4,0x79,0x20,0x8d,
- 0x03,0x0a,0x02,0x1f,0x47,0xbc,0xe8,0x9e,0xc8,0xd3,0x19,0xe9,0x20,0x8d,
- 0x03,0x0a,0x02,0xcc,0xf4,0xa7,0x06,0x1e,0xcd,0x36,0xb1,0xef,0x20,0x8d,
- 0x03,0x0a,0x02,0xd0,0x7a,0x03,0xf1,0x3e,0x05,0xce,0xe8,0xf1,0x20,0x8d,
- 0x03,0x0a,0x03,0x36,0x6c,0x60,0xb8,0x6d,0xf3,0x6c,0x5c,0xf7,0x20,0x8d,
- 0x03,0x0a,0x03,0x54,0xec,0xe4,0xa7,0x5e,0xa3,0xba,0x0b,0xd4,0x20,0x8d,
- 0x03,0x0a,0x04,0xf7,0x3b,0x25,0x61,0x98,0xb4,0xb8,0x36,0x1d,0x20,0x8d,
- 0x03,0x0a,0x05,0x60,0xe0,0xaf,0xfa,0x7b,0x05,0xee,0x0f,0x08,0x20,0x8d,
- 0x03,0x0a,0x05,0x98,0x3c,0xe8,0xb2,0xd8,0x7a,0x7e,0xd2,0x7d,0x20,0x8d,
- 0x03,0x0a,0x06,0x31,0x67,0xa3,0x1f,0xf8,0x69,0x31,0xa6,0x29,0x20,0x8d,
- 0x03,0x0a,0x0e,0x91,0xb7,0xa7,0xe2,0xd7,0x05,0x57,0xc6,0x5f,0x20,0x8d,
- 0x03,0x0a,0x0f,0x10,0xb2,0x07,0x17,0x15,0x3c,0xd9,0xcd,0x0e,0x20,0x8d,
- 0x03,0x0a,0x0f,0x2b,0x55,0x06,0x08,0x78,0x98,0xab,0x3f,0x95,0x20,0x8d,
- 0x03,0x0a,0x08,0xc6,0x58,0x5d,0xf2,0xea,0x02,0x3d,0x96,0x76,0x20,0x8d,
- 0x03,0x0a,0x09,0x3a,0x13,0x09,0xee,0xe3,0x9d,0x4b,0xf6,0x18,0x20,0x8d,
- 0x03,0x0a,0x09,0x96,0x0e,0x33,0xd9,0x24,0xeb,0x3a,0xfd,0x72,0x20,0x8d,
- 0x03,0x0a,0x09,0xf7,0xa3,0x66,0xdb,0x6e,0x04,0xac,0xc2,0x93,0x20,0x8d,
- 0x03,0x0a,0x09,0xdd,0xc5,0x38,0x6f,0x21,0xdb,0xfb,0xc7,0x77,0x20,0x8d,
- 0x03,0x0a,0x0a,0x26,0x27,0x21,0xbc,0x8a,0xca,0x0e,0x5a,0x17,0x20,0x8d,
- 0x03,0x0a,0x0a,0x2d,0xf9,0x79,0x25,0xf4,0x74,0xc2,0xec,0x54,0x20,0x8d,
- 0x03,0x0a,0x0a,0xbf,0x87,0xf8,0x8f,0x6b,0x04,0xb5,0xc3,0xfa,0x20,0x8d,
- 0x03,0x0a,0x0a,0xc4,0xa9,0xc4,0xd5,0x27,0x6a,0x49,0xa6,0x4a,0x20,0x8d,
- 0x03,0x0a,0x0a,0xec,0x17,0xfc,0xc5,0x19,0x4a,0x39,0x5f,0x86,0x20,0x8d,
- 0x03,0x0a,0x0b,0x6e,0xdf,0x42,0x02,0xef,0x4d,0x56,0xf5,0xcf,0x20,0x8d,
- 0x03,0x0a,0x0b,0xfe,0xed,0x69,0x75,0x12,0x41,0x62,0x2e,0xb5,0x20,0x8d,
- 0x03,0x0a,0x0c,0x21,0x88,0x50,0x46,0x4f,0x26,0x23,0xb7,0xdc,0x20,0x8d,
- 0x03,0x0a,0x0d,0x47,0x96,0x52,0x62,0x81,0x7e,0x6c,0xe5,0xbd,0x20,0x8d,
- 0x03,0x0a,0x16,0xfd,0x96,0x10,0xc9,0x52,0x1a,0x59,0xb2,0x65,0x20,0x8d,
- 0x03,0x0a,0x17,0x0a,0xdf,0x68,0xcd,0x5c,0xd6,0x68,0xbe,0x75,0x20,0x8d,
- 0x03,0x0a,0x10,0x00,0x45,0xf7,0x04,0x1d,0x50,0xe7,0x43,0x2a,0x20,0x8d,
- 0x03,0x0a,0x10,0x21,0xde,0x00,0x2b,0x28,0x62,0xda,0x30,0x63,0x20,0x8d,
- 0x03,0x0a,0x11,0x22,0xd8,0xb2,0x2a,0xee,0x5c,0xcc,0xbb,0x2d,0x20,0x8d,
- 0x03,0x0a,0x11,0xe2,0x8f,0x22,0x66,0x48,0x00,0x67,0x17,0x93,0x20,0x8d,
- 0x03,0x0a,0x13,0x45,0x64,0x2b,0x73,0x68,0xf4,0x44,0xb3,0xb9,0x20,0x8d,
- 0x03,0x0a,0x15,0x30,0x98,0x3b,0x28,0x23,0x04,0xcb,0x02,0xeb,0x20,0x8d,
- 0x03,0x0a,0x15,0xff,0x00,0x68,0xcf,0x86,0x1f,0xf7,0xac,0x7d,0x20,0x8d,
- 0x03,0x0a,0x16,0x5f,0xfb,0x18,0x14,0x97,0x0d,0x54,0x3b,0xfa,0x20,0x8d,
- 0x03,0x0a,0x1e,0x8a,0xde,0xf2,0x25,0xc2,0x46,0x06,0x99,0x1c,0x20,0x8d,
- 0x03,0x0a,0x1e,0xa4,0xae,0x76,0x9e,0x10,0x3d,0xcc,0x12,0x07,0x20,0x8d,
- 0x03,0x0a,0x1e,0xc0,0xeb,0x31,0xa6,0xaa,0xa7,0x2c,0xa0,0x04,0x20,0x8d,
- 0x03,0x0a,0x1f,0x51,0x4e,0x01,0x19,0xde,0x34,0xa3,0x08,0xc9,0x20,0x8d,
- 0x03,0x0a,0x1f,0xb2,0x1b,0x6a,0x57,0x6d,0xcc,0x9e,0xca,0xbb,0x20,0x8d,
- 0x03,0x0a,0x18,0x7b,0x11,0xf4,0x9c,0xf4,0xfe,0xc3,0x21,0xa8,0x20,0x8d,
- 0x03,0x0a,0x18,0x91,0xa3,0x51,0x6e,0x8a,0xf9,0xcc,0x27,0xbd,0x20,0x8d,
- 0x03,0x0a,0x18,0xdf,0x33,0xe9,0x96,0x9e,0xe3,0x2a,0xb9,0xc6,0x20,0x8d,
- 0x03,0x0a,0x19,0x63,0x6c,0x83,0xe5,0x11,0x04,0xa6,0xb5,0x92,0x20,0x8d,
- 0x03,0x0a,0x1a,0x6c,0x74,0x95,0x3c,0x89,0xf6,0xec,0xef,0x09,0x20,0x8d,
- 0x03,0x0a,0x1a,0x95,0xd6,0x31,0xe4,0xea,0x66,0x97,0x0d,0x5d,0x20,0x8d,
- 0x03,0x0a,0x1b,0x93,0xbc,0x99,0x92,0x0e,0x69,0x16,0x40,0xcf,0x20,0x8d,
- 0x03,0x0a,0x1b,0xc4,0x4e,0x17,0x71,0x14,0x06,0x3c,0x86,0xfd,0x20,0x8d,
- 0x03,0x0a,0x1c,0x6c,0xed,0xd5,0xb7,0x11,0xfa,0xec,0x94,0x2e,0x20,0x8d,
- 0x03,0x0a,0x1d,0x10,0xa5,0x20,0x77,0x43,0xf6,0xbc,0x12,0xed,0x20,0x8d,
- 0x03,0x0a,0x1d,0x20,0x35,0xa1,0xf3,0x16,0xb4,0x8f,0x1c,0xbd,0x20,0x8d,
- 0x03,0x0a,0x1d,0x30,0xfe,0x09,0xc7,0xe8,0xfe,0xd3,0xee,0x83,0x20,0x8d,
- 0x03,0x0a,0x1d,0x33,0xd9,0xd9,0xdb,0xcf,0xc5,0xde,0xae,0xe9,0x20,0x8d,
- 0x03,0x0a,0x1d,0x69,0xe1,0xac,0x11,0xf1,0x32,0x2f,0x5c,0x8d,0x20,0x8d,
- 0x03,0x0a,0x1e,0x3d,0x98,0x4b,0x9e,0xc0,0x96,0x40,0x63,0x0f,0x20,0x8d,
- 0x03,0x0a,0x1e,0x75,0x81,0xb1,0x3b,0xc4,0x22,0x26,0x72,0x3f,0x20,0x8d,
- 0x03,0x0a,0x26,0x83,0xa0,0x76,0x54,0xa8,0xc1,0x6c,0xde,0x83,0x20,0x8d,
- 0x03,0x0a,0x26,0xf6,0x7e,0xfd,0x3a,0x25,0x94,0xa8,0x49,0xbd,0x20,0x8d,
- 0x03,0x0a,0x27,0x54,0x94,0x03,0x1f,0x7e,0x53,0xd8,0x3f,0x35,0x20,0x8d,
- 0x03,0x0a,0x27,0xd0,0xa7,0x73,0x43,0xd5,0xb2,0x26,0x57,0x1c,0x20,0x8d,
- 0x03,0x0a,0x20,0x3c,0x17,0x1f,0x8a,0x74,0xe1,0xdf,0x5a,0x5d,0x20,0x8d,
- 0x03,0x0a,0x21,0x47,0x7f,0x18,0x5c,0x97,0x49,0x9c,0x40,0x86,0x20,0x8d,
- 0x03,0x0a,0x21,0x62,0xfa,0x51,0x02,0xf5,0x14,0x4c,0x40,0x52,0x20,0x8d,
- 0x03,0x0a,0x21,0xa3,0x41,0x6c,0x28,0xda,0x27,0x1a,0x78,0xd0,0x20,0x8d,
- 0x03,0x0a,0x24,0x45,0xe9,0xa6,0x5a,0xa0,0xb0,0x01,0xaf,0x5b,0x20,0x8d,
- 0x03,0x0a,0x25,0x09,0xa6,0xf6,0x4a,0xec,0xd5,0x33,0x74,0x35,0x20,0x8d,
- 0x03,0x0a,0x26,0x55,0x1f,0xca,0x70,0xe5,0xbe,0xe3,0xa6,0x33,0x20,0x8d,
- 0x03,0x0a,0x2e,0xdb,0x8c,0x24,0x20,0xf2,0x9f,0x7c,0xb4,0xea,0x20,0x8d,
- 0x03,0x0a,0x28,0x21,0xfd,0xd5,0x3c,0x78,0xa5,0xfd,0xcc,0xf4,0x20,0x8d,
- 0x03,0x0a,0x28,0xeb,0x35,0xa7,0x6f,0x90,0x83,0x7a,0x1f,0xfd,0x20,0x8d,
- 0x03,0x0a,0x29,0x86,0xfb,0xba,0xbc,0x6e,0x6f,0x53,0x89,0xf5,0x20,0x8d,
- 0x03,0x0a,0x2a,0x25,0x08,0x7a,0xb9,0x56,0xd9,0xe9,0xeb,0x5d,0x20,0x8d,
- 0x03,0x0a,0x2a,0x8c,0xfd,0xc2,0xc4,0x30,0x05,0x11,0xe8,0x29,0x20,0x8d,
- 0x03,0x0a,0x2b,0xb7,0x31,0x96,0xd7,0xd7,0xe6,0x05,0x42,0x1d,0x20,0x8d,
- 0x03,0x0a,0x2c,0x15,0x79,0x88,0xf6,0xc3,0xd1,0x27,0xa9,0xf5,0x20,0x8d,
- 0x03,0x0a,0x2c,0x28,0xda,0x1d,0x76,0xa8,0xff,0x18,0x78,0x7d,0x20,0x8d,
- 0x03,0x0a,0x2c,0x6d,0x3e,0xb2,0x42,0x7e,0x0e,0x8a,0x59,0xe4,0x20,0x8d,
- 0x03,0x0a,0x2c,0xc1,0xc3,0x15,0x28,0xa5,0x7c,0x5d,0x2c,0x9a,0x20,0x8d,
- 0x03,0x0a,0x2d,0x1d,0x8d,0x21,0xf4,0x84,0x61,0x62,0x74,0x45,0x20,0x8d,
- 0x03,0x0a,0x2e,0x7c,0xd9,0x21,0x3e,0x4a,0x31,0x4b,0x2e,0x42,0x20,0x8d,
- 0x03,0x0a,0x36,0xea,0xb6,0x80,0x00,0x71,0xbb,0x23,0x51,0x1d,0x20,0x8d,
- 0x03,0x0a,0x37,0x38,0x8f,0x26,0xd2,0xa4,0xd5,0x66,0x49,0xf9,0x20,0x8d,
- 0x03,0x0a,0x37,0x7b,0x3f,0x74,0x7d,0x12,0x92,0x8b,0x89,0xb6,0x20,0x8d,
- 0x03,0x0a,0x30,0x12,0x3f,0x13,0x11,0x5e,0xa1,0x65,0x15,0x86,0x20,0x8d,
- 0x03,0x0a,0x30,0x57,0x42,0x6c,0xf1,0xee,0xdf,0xc3,0x46,0xff,0x20,0x8d,
- 0x03,0x0a,0x30,0x5f,0x17,0x76,0x79,0x1d,0x11,0x42,0x97,0x95,0x20,0x8d,
- 0x03,0x0a,0x30,0xb8,0xbd,0xce,0x0b,0xde,0xa0,0x72,0x99,0x88,0x20,0x8d,
- 0x03,0x0a,0x30,0x9a,0xb7,0x46,0xb3,0x7e,0x05,0x40,0x24,0x5e,0x20,0x8d,
- 0x03,0x0a,0x30,0xe4,0x80,0xe9,0xaa,0xd1,0x08,0xe4,0x0c,0xc2,0x20,0x8d,
- 0x03,0x0a,0x31,0x3a,0x66,0x7c,0x5e,0xb7,0xf0,0x03,0xbf,0x3f,0x20,0x8d,
- 0x03,0x0a,0x31,0x0c,0x29,0x90,0x84,0x7f,0x05,0x62,0xcd,0x7d,0x20,0x8d,
- 0x03,0x0a,0x31,0x5d,0x88,0x82,0x83,0x35,0x7b,0x04,0x8d,0x54,0x20,0x8d,
- 0x03,0x0a,0x31,0x9e,0x1a,0x61,0xec,0xb9,0x91,0xaf,0x2c,0x5e,0x20,0x8d,
- 0x03,0x0a,0x31,0xe0,0x8a,0xe0,0x9f,0x11,0x44,0xa4,0x49,0xb3,0x20,0x8d,
- 0x03,0x0a,0x32,0x22,0x05,0x5d,0xcc,0x69,0x3a,0x50,0xe3,0xdc,0x20,0x8d,
- 0x03,0x0a,0x32,0xf3,0xd3,0x15,0x5b,0xdc,0xe9,0x43,0x75,0xa4,0x20,0x8d,
- 0x03,0x0a,0x33,0xd6,0x09,0xdd,0xd8,0x37,0x5b,0x75,0xf6,0x29,0x20,0x8d,
- 0x03,0x0a,0x34,0x50,0xf5,0xf6,0xe9,0xb6,0x34,0x31,0x47,0xc2,0x20,0x8d,
- 0x03,0x0a,0x34,0xce,0x7c,0xad,0x90,0x12,0x35,0xa6,0xde,0x34,0x20,0x8d,
- 0x03,0x0a,0x34,0xdd,0xa1,0xfb,0x92,0xb3,0xa4,0x56,0x2b,0xc2,0x20,0x8d,
- 0x03,0x0a,0x35,0x00,0x24,0x34,0x98,0xee,0x98,0x61,0x05,0xfa,0x20,0x8d,
- 0x03,0x0a,0x35,0x95,0x33,0x45,0x93,0xb2,0xbc,0xda,0xf6,0x42,0x20,0x8d,
- 0x03,0x0a,0x35,0x9d,0x76,0xb9,0x43,0x15,0x85,0xf3,0xe3,0x8f,0x20,0x8d,
- 0x03,0x0a,0x3e,0xf7,0xb2,0xf2,0x0d,0xb1,0x3e,0xc8,0xe1,0x8d,0x20,0x8d,
- 0x03,0x0a,0x3f,0x81,0xbd,0x37,0x81,0x58,0x6d,0x6c,0x37,0x83,0x20,0x8d,
- 0x03,0x0a,0x38,0x0b,0x69,0xc4,0x2e,0x74,0xb2,0xe2,0x30,0x2c,0x20,0x8d,
- 0x03,0x0a,0x38,0x6c,0x73,0x48,0x3b,0x21,0x10,0xd6,0xc7,0xd3,0x20,0x8d,
- 0x03,0x0a,0x38,0xab,0xe2,0xba,0xe7,0xeb,0x15,0xf2,0x9c,0x3d,0x20,0x8d,
- 0x03,0x0a,0x38,0xfc,0x75,0x4c,0x4b,0xf5,0x80,0xcc,0xaf,0x2c,0x20,0x8d,
- 0x03,0x0a,0x38,0xc1,0xe6,0x48,0x1c,0xaf,0x23,0x3f,0xfc,0xd7,0x20,0x8d,
- 0x03,0x0a,0x38,0xcc,0xdb,0xaa,0x90,0x90,0xfd,0x64,0xda,0xd7,0x20,0x8d,
- 0x03,0x0a,0x39,0x8f,0xb0,0x65,0xbb,0x21,0x24,0x31,0xd4,0x46,0x20,0x8d,
- 0x03,0x0a,0x39,0xf1,0x7a,0x78,0x36,0x52,0x48,0x52,0x25,0xd9,0x20,0x8d,
- 0x03,0x0a,0x3a,0x32,0xdf,0x45,0x8e,0x2c,0x8d,0xba,0x3d,0x8d,0x20,0x8d,
- 0x03,0x0a,0x3a,0x61,0x7b,0xcb,0x1a,0x74,0x88,0xc2,0xd4,0x95,0x20,0x8d,
- 0x03,0x0a,0x3a,0x82,0xff,0xb0,0x26,0xb7,0x94,0xb5,0xcb,0x92,0x20,0x8d,
- 0x03,0x0a,0x3a,0xdc,0x9a,0x59,0x16,0x0a,0x9c,0x9e,0x28,0x79,0x20,0x8d,
- 0x03,0x0a,0x3a,0xf3,0x79,0x26,0x3f,0x70,0x77,0x0c,0xe6,0x10,0x20,0x8d,
- 0x03,0x0a,0x3b,0x51,0x4c,0xbd,0x64,0xc9,0x03,0x83,0xd7,0xe0,0x20,0x8d,
- 0x03,0x0a,0x3b,0x9a,0x32,0x59,0x49,0xe4,0xb9,0x11,0x8a,0xc5,0x20,0x8d,
- 0x03,0x0a,0x3c,0x17,0xd6,0xd7,0xd5,0x38,0x88,0x81,0xec,0x2d,0x20,0x8d,
- 0x03,0x0a,0x3c,0x9e,0x97,0x7d,0x90,0x8c,0x49,0xd3,0x62,0xf1,0x20,0x8d,
- 0x03,0x0a,0x3d,0x3d,0xc9,0x69,0x83,0x8e,0xef,0xfc,0x5d,0x40,0x20,0x8d,
- 0x03,0x0a,0x3d,0x6d,0x58,0x6a,0x56,0x54,0x2d,0xb8,0x57,0x0e,0x20,0x8d,
- 0x03,0x0a,0x3d,0x9d,0xa0,0xa3,0x0d,0x1c,0x63,0x57,0xaf,0xc5,0x20,0x8d,
- 0x03,0x0a,0x3e,0x6e,0x9d,0x8e,0x67,0xde,0x35,0x79,0xf3,0xae,0x20,0x8d,
- 0x03,0x0a,0x46,0xe8,0x5b,0xd2,0xdb,0x9f,0xc5,0x72,0x8d,0xf0,0x20,0x8d,
- 0x03,0x0a,0x40,0x36,0xdd,0xc3,0xb4,0xe7,0x4d,0x57,0xdf,0xe0,0x20,0x8d,
- 0x03,0x0a,0x40,0x8a,0x69,0x6c,0xa2,0x98,0x94,0x3e,0x60,0x8e,0x20,0x8d,
- 0x03,0x0a,0x40,0x9f,0x9f,0x4c,0xf0,0xa8,0xd2,0x2b,0x2e,0xa1,0x20,0x8d,
- 0x03,0x0a,0x41,0x77,0xac,0xbb,0xb4,0xe3,0x0e,0x3a,0x34,0xa3,0x20,0x8d,
- 0x03,0x0a,0x41,0xb8,0xb3,0x52,0x0b,0xf5,0x6e,0xa0,0xb1,0x91,0x20,0x8d,
- 0x03,0x0a,0x41,0xce,0x28,0xfc,0xa7,0x16,0x60,0x30,0x0b,0x98,0x20,0x8d,
- 0x03,0x0a,0x42,0x59,0xa9,0xe2,0xee,0x0f,0xea,0xaa,0x83,0x39,0x20,0x8d,
- 0x03,0x0a,0x43,0xf6,0xfa,0x52,0x06,0x3d,0x18,0x5c,0xf6,0xd6,0x20,0x8d,
- 0x03,0x0a,0x44,0x36,0xa2,0x4f,0xfa,0x2e,0xf1,0xa2,0xc5,0xe6,0x20,0x8d,
- 0x03,0x0a,0x44,0x00,0x69,0xf4,0x4e,0xe0,0xe7,0xf3,0xf8,0xe5,0x20,0x8d,
- 0x03,0x0a,0x45,0x0d,0x6e,0x69,0x07,0xf1,0xdf,0x18,0x47,0x5e,0x20,0x8d,
- 0x03,0x0a,0x45,0x4b,0xff,0xf2,0xbc,0x9f,0xd5,0xed,0xa3,0xc3,0x20,0x8d,
- 0x03,0x0a,0x45,0x4a,0x01,0x0c,0xbf,0x12,0x0d,0xac,0xeb,0x1a,0x20,0x8d,
- 0x03,0x0a,0x45,0x65,0x71,0xd9,0x54,0xeb,0x8d,0xac,0xa7,0x8b,0x20,0x8d,
- 0x03,0x0a,0x45,0xec,0x68,0x9c,0x0a,0x5d,0x69,0xc3,0x79,0xdf,0x20,0x8d,
- 0x03,0x0a,0x46,0x7b,0xe6,0x39,0xde,0x62,0x9f,0xb3,0x7e,0xee,0x20,0x8d,
- 0x03,0x0a,0x4e,0x84,0xfe,0xb2,0x96,0xea,0x76,0xba,0x30,0x57,0x20,0x8d,
- 0x03,0x0a,0x4e,0x97,0x15,0x46,0xd4,0x32,0xc7,0x62,0x5a,0xd2,0x20,0x8d,
- 0x03,0x0a,0x4e,0xa1,0x36,0x28,0x7a,0x18,0x02,0xb9,0x4b,0x3c,0x20,0x8d,
- 0x03,0x0a,0x4f,0x49,0xac,0x50,0x0d,0xef,0xeb,0xa3,0xf4,0x8b,0x20,0x8d,
- 0x03,0x0a,0x4f,0x52,0x58,0x8e,0x67,0x84,0xfa,0x6d,0x76,0xf9,0x20,0x8d,
- 0x03,0x0a,0x4f,0x56,0xad,0x52,0xba,0x0c,0x9e,0x58,0x5c,0xaa,0x20,0x8d,
- 0x03,0x0a,0x48,0x1b,0x5a,0xe6,0x4c,0xc8,0xa4,0x9d,0x95,0x0b,0x20,0x8d,
- 0x03,0x0a,0x49,0x1a,0xdd,0x4d,0x98,0x5e,0xef,0x70,0x45,0x90,0x20,0x8d,
- 0x03,0x0a,0x49,0x5c,0x4e,0x97,0x52,0x16,0x5c,0x92,0xbf,0x7a,0x20,0x8d,
- 0x03,0x0a,0x49,0x84,0x64,0x79,0x5a,0x7d,0xdc,0xe4,0x76,0x1b,0x20,0x8d,
- 0x03,0x0a,0x49,0xc0,0xd0,0x6b,0x92,0xd8,0xf2,0xa4,0x4f,0x2f,0x20,0x8d,
- 0x03,0x0a,0x4a,0x26,0x6a,0x2c,0x3a,0xe3,0x2a,0x58,0x44,0x66,0x20,0x8d,
- 0x03,0x0a,0x4a,0x69,0x5b,0x05,0x25,0xca,0xd2,0xc6,0xfe,0x7b,0x20,0x8d,
- 0x03,0x0a,0x4a,0xc4,0x57,0x30,0xd1,0xed,0xca,0x4b,0x81,0x05,0x20,0x8d,
- 0x03,0x0a,0x4a,0xc8,0x79,0x7b,0x01,0x0e,0xbd,0x05,0xb5,0xa0,0x20,0x8d,
- 0x03,0x0a,0x4a,0xd3,0x9c,0xf2,0x6c,0x0c,0x23,0x78,0x6e,0x1d,0x20,0x8d,
- 0x03,0x0a,0x4b,0x85,0xc7,0x40,0x44,0x20,0xd4,0x6f,0xfe,0xa5,0x20,0x8d,
- 0x03,0x0a,0x4c,0x7b,0x8f,0x35,0x34,0x08,0x83,0x5f,0x1b,0x7f,0x20,0x8d,
- 0x03,0x0a,0x4c,0x5c,0x07,0x4b,0xcb,0x07,0x2a,0x82,0x1d,0xdc,0x20,0x8d,
- 0x03,0x0a,0x4c,0x98,0xf3,0x99,0x40,0xc7,0xd0,0x83,0x85,0x51,0x20,0x8d,
- 0x03,0x0a,0x4c,0xd5,0x26,0xb9,0x54,0x90,0x72,0xc9,0x7e,0xcb,0x20,0x8d,
- 0x03,0x0a,0x4d,0x3a,0x3a,0x3b,0x71,0xf3,0xfc,0x34,0x65,0xa2,0x20,0x8d,
- 0x03,0x0a,0x4d,0xbd,0x9c,0x32,0xe2,0x69,0x02,0x03,0xd2,0x89,0x20,0x8d,
- 0x03,0x0a,0x4d,0xc0,0xba,0x9c,0xbf,0xb7,0xec,0x4a,0xc3,0x36,0x20,0x8d,
- 0x03,0x0a,0x4e,0x32,0x72,0x6d,0x06,0xe7,0x10,0x25,0x62,0x41,0x20,0x8d,
- 0x03,0x0a,0x4e,0x49,0xea,0x29,0xbc,0x40,0xe2,0x7e,0x70,0x8e,0x20,0x8d,
- 0x03,0x0a,0x57,0x0c,0xb7,0x4d,0x77,0x6b,0x27,0x30,0xf8,0x53,0x20,0x8d,
- 0x03,0x0a,0x57,0xe9,0x8d,0xa2,0xcc,0xa9,0xa9,0x9c,0x18,0x7a,0x20,0x8d,
- 0x03,0x0a,0x52,0xf5,0xb7,0x14,0x06,0xdd,0x14,0x1f,0x1e,0xeb,0x20,0x8d,
- 0x03,0x0a,0x53,0x95,0x3d,0x42,0x3e,0x1f,0x1e,0xcc,0x07,0x43,0x20,0x8d,
- 0x03,0x0a,0x53,0xc0,0xba,0x6c,0xfd,0xc0,0xd4,0xe0,0x22,0xb2,0x20,0x8d,
- 0x03,0x0a,0x54,0x46,0xf0,0x8e,0xb3,0x85,0xba,0x2e,0xac,0x84,0x20,0x8d,
- 0x03,0x0a,0x54,0x51,0x6f,0x2b,0x29,0xc8,0x23,0x93,0x07,0x66,0x20,0x8d,
- 0x03,0x0a,0x54,0x5f,0xa9,0x9c,0x4c,0xb4,0x5f,0x27,0x50,0x9e,0x20,0x8d,
- 0x03,0x0a,0x55,0x71,0x51,0xd9,0x36,0x98,0x09,0xd6,0x3b,0xff,0x20,0x8d,
- 0x03,0x0a,0x56,0x23,0x78,0xa3,0xb1,0x0c,0x7c,0x87,0xd2,0x32,0x20,0x8d,
- 0x03,0x0a,0x56,0x76,0xeb,0x9b,0xff,0xe7,0x47,0x79,0xfb,0x50,0x20,0x8d,
- 0x03,0x0a,0x5e,0xed,0xd2,0x89,0x48,0xd5,0x83,0x17,0x6a,0x01,0x20,0x8d,
- 0x03,0x0a,0x5f,0x38,0x14,0x4a,0x97,0x39,0xff,0x12,0x07,0xb0,0x20,0x8d,
- 0x03,0x0a,0x5f,0x7d,0xd3,0x77,0x5b,0x23,0x12,0x40,0xd2,0x49,0x20,0x8d,
- 0x03,0x0a,0x5f,0xad,0xd5,0x0c,0x88,0x35,0xa4,0x66,0x97,0xb3,0x20,0x8d,
- 0x03,0x0a,0x5f,0xc1,0xc2,0x32,0x38,0x2d,0xd4,0x93,0x31,0x81,0x20,0x8d,
- 0x03,0x0a,0x5f,0xe4,0xb7,0x48,0x49,0x84,0x02,0x82,0x8a,0x56,0x20,0x8d,
- 0x03,0x0a,0x58,0x00,0x54,0xc2,0xb3,0x71,0xbe,0x34,0x95,0x7a,0x20,0x8d,
- 0x03,0x0a,0x58,0x0f,0xef,0xf9,0x57,0x09,0x82,0x6b,0x6e,0x9a,0x20,0x8d,
- 0x03,0x0a,0x58,0x61,0xa0,0x7d,0xed,0x7b,0x2a,0x8b,0x6a,0x0e,0x20,0x8d,
- 0x03,0x0a,0x58,0xb9,0x66,0xbe,0x0b,0xd7,0xeb,0x86,0x23,0x7d,0x20,0x8d,
- 0x03,0x0a,0x58,0xdc,0x52,0x84,0xaf,0x56,0xd3,0xe1,0x7f,0x1f,0x20,0x8d,
- 0x03,0x0a,0x59,0x0e,0xf6,0x19,0x6a,0x45,0x5c,0x18,0x6a,0x0e,0x20,0x8d,
- 0x03,0x0a,0x59,0x89,0x67,0xa7,0x3f,0x41,0x3e,0x30,0x42,0x11,0x20,0x8d,
- 0x03,0x0a,0x59,0x95,0x50,0xd6,0x2e,0xf7,0xd2,0xe6,0x3a,0x56,0x20,0x8d,
- 0x03,0x0a,0x5a,0x2d,0xdc,0xf1,0xa6,0x40,0xbc,0x1f,0xd5,0xb5,0x20,0x8d,
- 0x03,0x0a,0x5a,0x65,0xf3,0x5a,0x2c,0x66,0x41,0xe8,0x78,0xc0,0x20,0x8d,
- 0x03,0x0a,0x5b,0x2a,0x0b,0xec,0x9e,0x05,0x81,0x7a,0x9e,0x08,0x20,0x8d,
- 0x03,0x0a,0x5c,0x73,0xff,0x8e,0xc5,0xfe,0x21,0xc1,0x19,0xb3,0x20,0x8d,
- 0x03,0x0a,0x5d,0x41,0xde,0x3d,0xa1,0x86,0x9b,0x26,0x27,0x11,0x20,0x8d,
- 0x03,0x0a,0x5d,0xc5,0xaa,0x3c,0xf7,0xc6,0x2e,0x55,0x9d,0xa5,0x20,0x8d,
- 0x03,0x0a,0x5e,0x13,0x80,0x8e,0x3c,0x3b,0x13,0xb0,0xc0,0x01,0x20,0x8d,
- 0x03,0x0a,0x5e,0x75,0x95,0xb5,0x98,0xc3,0x6d,0x33,0x58,0xba,0x20,0x8d,
- 0x03,0x0a,0x67,0x8e,0x26,0xbd,0x0a,0x43,0x30,0x7d,0xff,0x0f,0x20,0x8d,
- 0x03,0x0a,0x60,0xfd,0xbe,0xb9,0x89,0x6c,0x4c,0x72,0x10,0x7b,0x20,0x8d,
- 0x03,0x0a,0x60,0xc3,0xb7,0x51,0xf6,0x2f,0x0b,0xa8,0x61,0x21,0x20,0x8d,
- 0x03,0x0a,0x61,0x3c,0x3e,0x12,0x57,0xfb,0x8e,0x36,0xdd,0xa4,0x20,0x8d,
- 0x03,0x0a,0x61,0x04,0x55,0x21,0x5d,0x12,0x39,0xfb,0x09,0x49,0x20,0x8d,
- 0x03,0x0a,0x61,0x63,0x52,0x55,0xbf,0xb7,0xa3,0x69,0x3f,0x91,0x20,0x8d,
- 0x03,0x0a,0x62,0x19,0x4a,0x4d,0x64,0xb7,0x65,0x19,0x8e,0x8a,0x20,0x8d,
- 0x03,0x0a,0x63,0x71,0x25,0x6d,0x19,0xbd,0x62,0x0d,0x9e,0x95,0x20,0x8d,
- 0x03,0x0a,0x64,0x29,0xe3,0x42,0x71,0x3b,0x3d,0x7c,0xda,0xc7,0x20,0x8d,
- 0x03,0x0a,0x65,0xa8,0x2f,0x55,0xcc,0xe3,0x4c,0x84,0xcc,0x3b,0x20,0x8d,
- 0x03,0x0a,0x65,0xc7,0x38,0xa4,0xe4,0xd6,0x0b,0x2b,0xed,0xe6,0x20,0x8d,
- 0x03,0x0a,0x6f,0x10,0x12,0x4f,0x8f,0x44,0x85,0x5d,0x69,0xa9,0x20,0x8d,
- 0x03,0x0a,0x6f,0x87,0xcf,0x54,0x39,0xbf,0x36,0x12,0x55,0x61,0x20,0x8d,
- 0x03,0x0a,0x6f,0xa7,0xe5,0x14,0xd9,0x5d,0x5d,0x9b,0x9c,0xac,0x20,0x8d,
- 0x03,0x0a,0x6f,0xe3,0x17,0x08,0xf6,0x24,0x4b,0xa8,0x5f,0x24,0x20,0x8d,
- 0x03,0x0a,0x68,0xa4,0x34,0x41,0x8d,0xb9,0xda,0xd4,0x86,0x59,0x20,0x8d,
- 0x03,0x0a,0x6a,0x27,0x7b,0x6d,0x0b,0x29,0x5a,0x67,0xd1,0x95,0x20,0x8d,
- 0x03,0x0a,0x6a,0x57,0x2a,0xd0,0x28,0x58,0xc8,0x75,0xd2,0xd1,0x20,0x8d,
- 0x03,0x0a,0x6a,0x64,0xb2,0xc9,0x15,0xc6,0x0e,0x8b,0x86,0x4f,0x20,0x8d,
- 0x03,0x0a,0x6a,0x8b,0xd2,0x78,0x3f,0x7a,0xf8,0x92,0x8f,0x80,0x20,0x8d,
- 0x03,0x0a,0x6a,0x9e,0xf9,0x07,0x73,0xd8,0xe8,0x24,0x93,0xcc,0x20,0x8d,
- 0x03,0x0a,0x6a,0xcb,0x6c,0x41,0x52,0x61,0x20,0x4e,0x77,0x39,0x20,0x8d,
- 0x03,0x0a,0x6a,0xf0,0x96,0x3c,0x4c,0x78,0x33,0xd0,0xf0,0x00,0x20,0x8d,
- 0x03,0x0a,0x6b,0x59,0x5f,0xe7,0xdd,0x57,0xba,0xc1,0x12,0x51,0x20,0x8d,
- 0x03,0x0a,0x6c,0x62,0x5b,0x0d,0x91,0x66,0xd0,0xca,0x10,0x2d,0x20,0x8d,
- 0x03,0x0a,0x6c,0x62,0xc5,0x19,0x94,0x5b,0xcd,0x20,0xd9,0x73,0x20,0x8d,
- 0x03,0x0a,0x6d,0xb8,0x7f,0xac,0x82,0x55,0x27,0xf2,0x01,0xf5,0x20,0x8d,
- 0x03,0x0a,0x6d,0x95,0x8d,0xd8,0x7b,0x41,0xdc,0x81,0xd4,0x3d,0x20,0x8d,
- 0x03,0x0a,0x6e,0x38,0xa5,0x11,0x8c,0x64,0x2b,0xc5,0xbe,0x6c,0x20,0x8d,
- 0x03,0x0a,0x76,0xbb,0x65,0x0a,0xdf,0x23,0xa2,0x6d,0x4d,0xc8,0x20,0x8d,
- 0x03,0x0a,0x76,0x8d,0x46,0x54,0x2a,0xb7,0x9e,0xce,0x74,0x45,0x20,0x8d,
- 0x03,0x0a,0x77,0x30,0x99,0x1c,0x76,0x58,0x64,0x7c,0x2e,0x16,0x20,0x8d,
- 0x03,0x0a,0x71,0x6f,0xc8,0x1a,0xde,0x5b,0xde,0xda,0xcc,0xd5,0x20,0x8d,
- 0x03,0x0a,0x72,0x89,0x34,0x3d,0x7c,0x33,0x47,0x01,0x02,0x92,0x20,0x8d,
- 0x03,0x0a,0x74,0x3b,0x0e,0x42,0x30,0x42,0x63,0xa5,0x3e,0x8d,0x20,0x8d,
- 0x03,0x0a,0x74,0x2d,0xb6,0x15,0xc8,0x70,0x60,0x25,0x2e,0xe7,0x20,0x8d,
- 0x03,0x0a,0x74,0x65,0x8d,0x57,0xdb,0x20,0xa2,0xc1,0xa7,0xbd,0x20,0x8d,
- 0x03,0x0a,0x74,0xf9,0x3c,0xb3,0x2d,0xc2,0x18,0xc5,0xcb,0x2a,0x20,0x8d,
- 0x03,0x0a,0x75,0x95,0xe1,0x69,0x25,0x99,0xec,0xac,0x00,0xe4,0x20,0x8d,
- 0x03,0x0a,0x76,0x3f,0x29,0x6c,0xec,0xd3,0x95,0x7e,0x4e,0x8d,0x20,0x8d,
- 0x03,0x0a,0x7e,0x9e,0x2f,0x58,0x20,0x23,0xea,0x34,0x78,0x44,0x20,0x8d,
- 0x03,0x0a,0x7e,0xaf,0xae,0x18,0x67,0x04,0x98,0x61,0x2f,0xa9,0x20,0x8d,
- 0x03,0x0a,0x7f,0x84,0xea,0x51,0x31,0xd3,0x46,0x75,0xae,0xbb,0x20,0x8d,
- 0x03,0x0a,0x78,0x3e,0x3b,0x74,0x2b,0x6f,0x57,0x06,0x53,0xbb,0x20,0x8d,
- 0x03,0x0a,0x78,0x24,0xc1,0x1e,0x6e,0x73,0x93,0xa5,0x08,0xe3,0x20,0x8d,
- 0x03,0x0a,0x78,0xc0,0xf5,0x28,0xea,0xf3,0xc2,0x2c,0x6a,0x69,0x20,0x8d,
- 0x03,0x0a,0x79,0x0f,0xd0,0x25,0xd4,0xa5,0xbc,0xcb,0x72,0x51,0x20,0x8d,
- 0x03,0x0a,0x7a,0xa9,0x41,0x75,0xf6,0x5f,0x6f,0x83,0x58,0xf1,0x20,0x8d,
- 0x03,0x0a,0x7c,0x39,0x64,0xaf,0xf5,0x37,0xe7,0x22,0xe0,0x42,0x20,0x8d,
- 0x03,0x0a,0x7c,0xc3,0x68,0x1e,0x92,0x7c,0xbb,0x04,0x12,0x0b,0x20,0x8d,
- 0x03,0x0a,0x7c,0xec,0xf0,0xdb,0x09,0xea,0xdb,0x82,0x5b,0x45,0x20,0x8d,
- 0x03,0x0a,0x7d,0x3f,0x6d,0xa4,0xb8,0x8e,0x5f,0xf9,0x5e,0x48,0x20,0x8d,
- 0x03,0x0a,0x7d,0xb0,0xb0,0xe2,0xa5,0xa0,0xbd,0xa3,0x9e,0xb7,0x20,0x8d,
- 0x03,0x0a,0x86,0x8a,0x76,0xb7,0x13,0xe8,0x74,0x0c,0x54,0x44,0x20,0x8d,
- 0x03,0x0a,0x86,0xd1,0xb0,0x3e,0x88,0x73,0x42,0x0c,0xb0,0xa4,0x20,0x8d,
- 0x03,0x0a,0x80,0xfc,0x51,0x3e,0x9b,0x7d,0x42,0x5d,0x63,0x77,0x20,0x8d,
- 0x03,0x0a,0x81,0x49,0x6a,0xef,0x1f,0x06,0xdf,0xc4,0x6c,0x23,0x20,0x8d,
- 0x03,0x0a,0x81,0xf1,0x31,0xce,0x65,0x59,0xc2,0x2e,0x46,0x47,0x20,0x8d,
- 0x03,0x0a,0x82,0x9b,0xbe,0xc4,0x3b,0xbe,0x8d,0x70,0xda,0x1c,0x20,0x8d,
- 0x03,0x0a,0x82,0xea,0xb2,0x5e,0x5f,0x7d,0x80,0x2d,0x17,0x81,0x20,0x8d,
- 0x03,0x0a,0x83,0x8c,0x28,0x22,0x33,0xa4,0xc1,0xe8,0xae,0xe6,0x20,0x8d,
- 0x03,0x0a,0x84,0x73,0x02,0xdd,0x47,0x8b,0x29,0xda,0xf6,0x2e,0x20,0x8d,
- 0x03,0x0a,0x84,0xb0,0x90,0x4a,0x1c,0xf0,0x75,0x2c,0x23,0x12,0x20,0x8d,
- 0x03,0x0a,0x85,0x29,0xc0,0xeb,0x29,0x0b,0x63,0xaa,0x13,0x98,0x20,0x8d,
- 0x03,0x0a,0x85,0x30,0x22,0xa7,0x56,0x23,0x73,0xe0,0x97,0x03,0x20,0x8d,
- 0x03,0x0a,0x85,0x47,0x8d,0x89,0x8e,0x13,0x57,0x5e,0xd7,0xe2,0x20,0x8d,
- 0x03,0x0a,0x85,0x6c,0x77,0xc3,0x06,0x03,0x75,0x75,0x63,0xa7,0x20,0x8d,
- 0x03,0x0a,0x86,0x29,0x3b,0x0b,0x5e,0xa2,0xd7,0x44,0x80,0xa1,0x20,0x8d,
- 0x03,0x0a,0x8f,0x3b,0x03,0x68,0x7e,0x45,0x8a,0x33,0xc2,0xcb,0x20,0x8d,
- 0x03,0x0a,0x8f,0x2f,0x41,0xc7,0xd4,0xe4,0x7a,0xdc,0x18,0x1c,0x20,0x8d,
- 0x03,0x0a,0x8f,0x80,0xf0,0x76,0x52,0xa2,0x6e,0x1b,0x0f,0x7c,0x20,0x8d,
- 0x03,0x0a,0x8f,0xb3,0xa3,0x0a,0x54,0xdf,0xd5,0xb3,0x00,0x07,0x20,0x8d,
- 0x03,0x0a,0x88,0x62,0x93,0x14,0x42,0x07,0xab,0xd0,0xff,0x0e,0x20,0x8d,
- 0x03,0x0a,0x88,0x90,0x5b,0xa0,0x20,0xb4,0x27,0xe8,0xdf,0x39,0x20,0x8d,
- 0x03,0x0a,0x88,0xdd,0xbb,0x8a,0x6a,0xde,0x55,0x94,0xd5,0x6d,0x20,0x8d,
- 0x03,0x0a,0x88,0xea,0xb2,0x3f,0x1e,0x31,0xcc,0xf0,0x3f,0x2e,0x20,0x8d,
- 0x03,0x0a,0x89,0x05,0x2d,0x83,0x5f,0x11,0xeb,0xa5,0x9b,0xdd,0x20,0x8d,
- 0x03,0x0a,0x89,0x58,0x14,0x63,0xb5,0xcc,0xea,0xdf,0x1f,0x0d,0x20,0x8d,
- 0x03,0x0a,0x8a,0xd1,0xd5,0x85,0x24,0xe2,0xbf,0xf4,0x37,0x36,0x20,0x8d,
- 0x03,0x0a,0x8b,0xa1,0x66,0xb0,0x8f,0x12,0x79,0xdd,0xd4,0xa7,0x20,0x8d,
- 0x03,0x0a,0x8b,0xc2,0xdb,0xf7,0x90,0x6a,0x11,0x58,0xb0,0xfb,0x20,0x8d,
- 0x03,0x0a,0x8c,0xab,0x57,0x1a,0x03,0x5a,0x12,0xff,0xfc,0xf5,0x20,0x8d,
- 0x03,0x0a,0x8d,0x99,0xd1,0xf0,0xe6,0xd9,0xc5,0xff,0xa8,0x73,0x20,0x8d,
- 0x03,0x0a,0x96,0xf0,0x45,0xaa,0xa2,0xe9,0x7b,0x72,0x62,0x56,0x20,0x8d,
- 0x03,0x0a,0x97,0xa3,0x7e,0xe8,0xe8,0x9b,0x1e,0xfe,0x2c,0xc4,0x20,0x8d,
- 0x03,0x0a,0x90,0x3c,0xcd,0xc6,0xb8,0x12,0x1e,0x62,0x31,0x58,0x20,0x8d,
- 0x03,0x0a,0x90,0x2a,0x40,0x90,0x92,0x62,0x91,0x56,0x14,0x2e,0x20,0x8d,
- 0x03,0x0a,0x90,0x70,0x98,0xf5,0xaf,0x56,0x98,0xb6,0x16,0xdf,0x20,0x8d,
- 0x03,0x0a,0x91,0x23,0x64,0xf3,0x49,0x61,0x3b,0x73,0x9d,0x96,0x20,0x8d,
- 0x03,0x0a,0x91,0xb9,0x56,0x50,0x35,0xd8,0xd3,0x1c,0xd6,0x87,0x20,0x8d,
- 0x03,0x0a,0x91,0xe4,0x65,0x49,0x74,0xcf,0x92,0xa3,0x3f,0xc6,0x20,0x8d,
- 0x03,0x0a,0x92,0x71,0x96,0x5a,0xd4,0xf0,0xd0,0x84,0x4f,0x71,0x20,0x8d,
- 0x03,0x0a,0x92,0xb6,0x46,0xee,0x24,0xa0,0xcd,0xb9,0x0c,0xdd,0x20,0x8d,
- 0x03,0x0a,0x92,0x9c,0x82,0xbf,0x8e,0x4f,0xd7,0xc7,0x4a,0x9d,0x20,0x8d,
- 0x03,0x0a,0x92,0xc9,0xa1,0x01,0xeb,0x52,0xdb,0xbd,0x93,0xf8,0x20,0x8d,
- 0x03,0x0a,0x93,0x06,0x3f,0xc3,0xe6,0x73,0x40,0x91,0xb1,0x30,0x20,0x8d,
- 0x03,0x0a,0x93,0xd8,0x7a,0x5d,0x21,0xd0,0x87,0xf5,0x92,0x8d,0x20,0x8d,
- 0x03,0x0a,0x94,0x54,0x6c,0x57,0xa4,0x1b,0x74,0xf0,0x7d,0x0b,0x20,0x8d,
- 0x03,0x0a,0x94,0x96,0xd4,0xa4,0xed,0x65,0x96,0xbc,0x4a,0xbc,0x20,0x8d,
- 0x03,0x0a,0x95,0x3d,0xec,0x1a,0x20,0x97,0xa2,0xa1,0xcd,0xab,0x20,0x8d,
- 0x03,0x0a,0x95,0x1a,0x3a,0xb0,0x29,0x8c,0xcc,0x32,0x80,0xf7,0x20,0x8d,
- 0x03,0x0a,0x95,0x47,0xee,0xab,0xa9,0x78,0x17,0xa7,0xed,0x73,0x20,0x8d,
- 0x03,0x0a,0x95,0x68,0x0e,0x9d,0x10,0x5d,0x2d,0xf7,0x6a,0x56,0x20,0x8d,
- 0x03,0x0a,0x95,0xe0,0x9a,0x05,0x94,0x67,0x22,0xc2,0x99,0xf4,0x20,0x8d,
- 0x03,0x0a,0x9e,0xb9,0xda,0xa3,0xfc,0xd4,0xd1,0xb9,0xb5,0x40,0x20,0x8d,
- 0x03,0x0a,0x9f,0x0a,0x17,0x56,0xa6,0xcb,0xda,0x86,0x0f,0x4f,0x20,0x8d,
- 0x03,0x0a,0x9f,0x17,0xcb,0x57,0x64,0x8a,0x8e,0xf1,0x93,0x4f,0x20,0x8d,
- 0x03,0x0a,0x9f,0x60,0x23,0xd8,0x31,0xf5,0x3b,0x5d,0x00,0xca,0x20,0x8d,
- 0x03,0x0a,0x9f,0xd2,0xb0,0x27,0xc6,0x36,0x2f,0xf9,0x76,0xb8,0x20,0x8d,
- 0x03,0x0a,0x98,0x3d,0x24,0x92,0x18,0x0e,0xbe,0x5e,0x37,0x80,0x20,0x8d,
- 0x03,0x0a,0x98,0x2b,0xfa,0x4d,0xf6,0xe3,0xcb,0x8f,0xa7,0xca,0x20,0x8d,
- 0x03,0x0a,0x98,0x2e,0x6e,0xe7,0x52,0xb9,0x59,0xd1,0x70,0x7e,0x20,0x8d,
- 0x03,0x0a,0x99,0x15,0x6a,0xb4,0x2e,0x18,0x73,0x15,0xd0,0xb2,0x20,0x8d,
- 0x03,0x0a,0x99,0xb9,0x4b,0x45,0x2c,0x9c,0x74,0x95,0x85,0x38,0x20,0x8d,
- 0x03,0x0a,0x99,0xf8,0x24,0xd4,0xa5,0x4c,0xed,0xea,0xb9,0x94,0x20,0x8d,
- 0x03,0x0a,0x99,0xfc,0x5b,0xe1,0x93,0xb3,0x4a,0x82,0xc0,0x94,0x20,0x8d,
- 0x03,0x0a,0x99,0xe6,0x23,0x9d,0x7a,0xed,0x35,0xe6,0x99,0x70,0x20,0x8d,
- 0x03,0x0a,0x9a,0x10,0x03,0xfc,0x52,0xa3,0x94,0xb1,0x55,0x1e,0x20,0x8d,
- 0x03,0x0a,0x9a,0xbd,0xbb,0xf4,0xaa,0xde,0xf7,0xfc,0xee,0x83,0x20,0x8d,
- 0x03,0x0a,0x9a,0x8c,0xe7,0x4c,0x13,0xf0,0xa0,0xdf,0xd7,0x18,0x20,0x8d,
- 0x03,0x0a,0x9b,0x53,0xdf,0x76,0xd6,0x86,0x7b,0x67,0xa6,0xb2,0x20,0x8d,
- 0x03,0x0a,0x9c,0xbd,0x0b,0xef,0xec,0x63,0xe9,0xe6,0xa7,0xb8,0x20,0x8d,
- 0x03,0x0a,0x9c,0xd2,0x89,0x56,0xf8,0x19,0x83,0x37,0xf7,0xc5,0x20,0x8d,
- 0x03,0x0a,0x9d,0x9b,0xde,0x57,0xf1,0x06,0xae,0x93,0x0f,0xbd,0x20,0x8d,
- 0x03,0x0a,0x9d,0xc8,0xce,0xb0,0x94,0x36,0xb8,0x6d,0x13,0x23,0x20,0x8d,
- 0x03,0x0a,0x9e,0x11,0x46,0xb7,0x7e,0x5b,0x0a,0x28,0x75,0x71,0x20,0x8d,
- 0x03,0x0a,0x9e,0x2b,0xdf,0x5e,0x5e,0x37,0x9a,0x3c,0xc2,0x97,0x20,0x8d,
- 0x03,0x0a,0xa7,0x3e,0x5d,0x9e,0xf6,0x87,0xbb,0x23,0x4b,0x8e,0x20,0x8d,
- 0x03,0x0a,0xa7,0x23,0xf2,0xb4,0xee,0x5c,0x47,0x6b,0x2d,0xa8,0x20,0x8d,
- 0x03,0x0a,0xa7,0x7b,0xe7,0x14,0x3b,0x66,0x01,0x10,0x16,0xcd,0x20,0x8d,
- 0x03,0x0a,0xa7,0x61,0xb3,0x07,0x3c,0x83,0xf3,0xcb,0x55,0x71,0x20,0x8d,
- 0x03,0x0a,0xa0,0x14,0xbc,0x6f,0x03,0x89,0x2b,0x57,0xde,0xc8,0x20,0x8d,
- 0x03,0x0a,0xa1,0xbc,0x70,0x3d,0x1c,0x84,0xc8,0xac,0x8b,0xf5,0x20,0x8d,
- 0x03,0x0a,0xa2,0x3b,0xdd,0xc1,0xd3,0x1f,0xa2,0xe6,0xee,0x25,0x20,0x8d,
- 0x03,0x0a,0xa2,0x23,0xf2,0xee,0xcb,0x9b,0x94,0x0f,0x04,0x21,0x20,0x8d,
- 0x03,0x0a,0xa2,0xa2,0x94,0x9e,0xce,0x1a,0xf9,0xcb,0x31,0xc5,0x20,0x8d,
- 0x03,0x0a,0xa2,0xfa,0x66,0x69,0x17,0xc7,0xd5,0x01,0x96,0xc6,0x20,0x8d,
- 0x03,0x0a,0xa3,0x46,0x3f,0xc6,0x49,0xe3,0xc8,0xdd,0xd9,0xdc,0x20,0x8d,
- 0x03,0x0a,0xa3,0xa3,0x67,0xe4,0xa4,0x3c,0xf0,0xa8,0x9b,0x9b,0x20,0x8d,
- 0x03,0x0a,0xa3,0xab,0x27,0xeb,0x0b,0x9b,0x40,0xe4,0xc3,0xcb,0x20,0x8d,
- 0x03,0x0a,0xa4,0x81,0x96,0x0c,0x52,0xde,0x9b,0x8d,0x70,0x78,0x20,0x8d,
- 0x03,0x0a,0xa4,0x81,0x99,0x7c,0xcb,0x67,0xcc,0x4c,0x5d,0x4b,0x20,0x8d,
- 0x03,0x0a,0xa4,0xa5,0xa5,0x10,0x66,0xfc,0x15,0x63,0x0e,0x3d,0x20,0x8d,
- 0x03,0x0a,0xa4,0xcd,0x88,0xd6,0xdf,0xed,0xab,0xa6,0xe1,0x88,0x20,0x8d,
- 0x03,0x0a,0xa6,0x6c,0x01,0x32,0x5f,0x56,0x32,0x72,0x1c,0x2b,0x20,0x8d,
- 0x03,0x0a,0xae,0x94,0x31,0x12,0x75,0x92,0xd8,0x32,0x8a,0xd1,0x20,0x8d,
- 0x03,0x0a,0xaf,0x56,0x76,0xe7,0x35,0xf3,0x5a,0x62,0x9b,0xa3,0x20,0x8d,
- 0x03,0x0a,0xa8,0xb9,0xc3,0x07,0x95,0x23,0xde,0xe0,0xc6,0x7b,0x20,0x8d,
- 0x03,0x0a,0xa9,0x6d,0x83,0xa6,0x9c,0xdd,0xae,0x7c,0xd6,0x97,0x20,0x8d,
- 0x03,0x0a,0xa9,0xa8,0x9a,0x15,0x5d,0xda,0xe1,0x87,0x2d,0x0e,0x20,0x8d,
- 0x03,0x0a,0xa9,0xc8,0x44,0xc2,0x1a,0xaf,0x46,0xa0,0xf2,0xf1,0x20,0x8d,
- 0x03,0x0a,0xaa,0x7d,0xc2,0x0c,0x95,0xe2,0x5b,0x02,0x8e,0x41,0x20,0x8d,
- 0x03,0x0a,0xab,0x00,0x8e,0xd1,0x06,0x26,0x63,0xa5,0x1d,0x49,0x20,0x8d,
- 0x03,0x0a,0xab,0x29,0x85,0x0f,0xf2,0xb8,0x58,0x8f,0xdb,0xbf,0x20,0x8d,
- 0x03,0x0a,0xab,0x98,0x40,0x0a,0x73,0x43,0x6f,0xb6,0x3d,0x8b,0x20,0x8d,
- 0x03,0x0a,0xab,0xdd,0x6d,0x5d,0xc5,0x36,0xcb,0x6c,0xc8,0x70,0x20,0x8d,
- 0x03,0x0a,0xac,0x81,0x65,0xa3,0x8b,0xea,0x0b,0x71,0xe4,0x16,0x20,0x8d,
- 0x03,0x0a,0xad,0x1b,0x40,0xc1,0x45,0x64,0xbf,0x24,0x15,0xca,0x20,0x8d,
- 0x03,0x0a,0xad,0x1c,0xc0,0xb4,0x95,0xb5,0x17,0xc0,0xc2,0x41,0x20,0x8d,
- 0x03,0x0a,0xad,0xc4,0xfa,0x8d,0xa6,0xf7,0x40,0x42,0xe7,0xd3,0x20,0x8d,
- 0x03,0x0a,0xae,0x2e,0xe4,0x64,0x79,0x05,0x5f,0xb7,0x04,0x14,0x20,0x8d,
- 0x03,0x0a,0xb0,0x48,0xe6,0xe8,0x48,0xfa,0xca,0x87,0x78,0x18,0x20,0x8d,
- 0x03,0x0a,0xb0,0x6c,0x4a,0x92,0xde,0xd3,0x0d,0x28,0xc4,0x79,0x20,0x8d,
- 0x03,0x0a,0xb1,0x41,0x81,0xac,0xde,0xce,0x0b,0x94,0x8a,0x9d,0x20,0x8d,
- 0x03,0x0a,0xb1,0x73,0xdf,0x4b,0xab,0xc3,0x7a,0x3c,0x48,0x99,0x20,0x8d,
- 0x03,0x0a,0xb1,0xb6,0xc8,0x72,0x86,0xc6,0x34,0x6b,0xef,0x41,0x20,0x8d,
- 0x03,0x0a,0xb1,0xd5,0x8e,0xf0,0x22,0x9a,0x8b,0xa6,0xf1,0xfb,0x20,0x8d,
- 0x03,0x0a,0xb1,0xd8,0x90,0x36,0x0e,0xc6,0x51,0x9c,0x8b,0x93,0x20,0x8d,
- 0x03,0x0a,0xb2,0xce,0xea,0x6a,0xd7,0x34,0x30,0x8d,0xdf,0x65,0x20,0x8d,
- 0x03,0x0a,0xb2,0xea,0xa2,0xc5,0xeb,0x2a,0x10,0xec,0xeb,0x4e,0x20,0x8d,
- 0x03,0x0a,0xbe,0xda,0x60,0xee,0xa0,0xf8,0xdd,0x5a,0x11,0xb6,0x20,0x8d,
- 0x03,0x0a,0xbf,0x7f,0x7f,0x68,0x2c,0x63,0x70,0xba,0xbb,0xf1,0x20,0x8d,
- 0x03,0x0a,0xb9,0xaa,0xce,0xfd,0x87,0x35,0x7b,0xee,0x0d,0x40,0x20,0x8d,
- 0x03,0x0a,0xb9,0xe5,0xb3,0x2c,0xb6,0x6d,0x91,0x46,0x22,0xad,0x20,0x8d,
- 0x03,0x0a,0xba,0x49,0xd2,0xda,0xb8,0x28,0xe8,0x4d,0x53,0xca,0x20,0x8d,
- 0x03,0x0a,0xba,0xcd,0x40,0x9b,0x0b,0xc6,0x82,0xba,0xc8,0xdd,0x20,0x8d,
- 0x03,0x0a,0xbb,0x57,0x4d,0xce,0xa0,0x53,0x4d,0x8f,0xcd,0x4f,0x20,0x8d,
- 0x03,0x0a,0xbb,0xba,0xc0,0x45,0x0b,0x3d,0x30,0xef,0x86,0x93,0x20,0x8d,
- 0x03,0x0a,0xbc,0x80,0x0b,0xa0,0xe3,0xc1,0x9b,0x6b,0xc5,0x17,0x20,0x8d,
- 0x03,0x0a,0xbd,0x3a,0xc5,0xd0,0xc3,0x93,0x32,0x55,0x57,0x27,0x20,0x8d,
- 0x03,0x0a,0xbd,0x63,0x78,0x09,0xf3,0x85,0x50,0x42,0x0c,0x3a,0x20,0x8d,
- 0x03,0x0a,0xbd,0xb2,0x78,0xc7,0x06,0x2c,0xe1,0xb8,0x72,0xdc,0x20,0x8d,
- 0x03,0x0a,0xc7,0x25,0x66,0x48,0x17,0x18,0x9d,0x2d,0x05,0xb4,0x20,0x8d,
- 0x03,0x0a,0xc7,0x66,0xbe,0x2e,0x08,0xdf,0xba,0xf7,0xae,0x83,0x20,0x8d,
- 0x03,0x0a,0xc7,0x6d,0x92,0x43,0x00,0x24,0xe5,0xd6,0x83,0xd3,0x20,0x8d,
- 0x03,0x0a,0xc7,0xf7,0x05,0x69,0x99,0x52,0x54,0x77,0x2b,0x1f,0x20,0x8d,
- 0x03,0x0a,0xc7,0xdd,0x9d,0xe0,0x6d,0xaa,0x03,0xcb,0x9c,0x21,0x20,0x8d,
- 0x03,0x0a,0xc0,0x0f,0xf8,0x18,0xb0,0x84,0x66,0x47,0x08,0xe4,0x20,0x8d,
- 0x03,0x0a,0xc0,0x41,0xc0,0xc5,0x9d,0xef,0x46,0x46,0xae,0x7f,0x20,0x8d,
- 0x03,0x0a,0xc1,0x89,0x05,0x1b,0x88,0x6b,0xd7,0x20,0x08,0x9b,0x20,0x8d,
- 0x03,0x0a,0xc2,0x4e,0xd2,0xd3,0xfd,0x58,0x32,0x14,0x6f,0x87,0x20,0x8d,
- 0x03,0x0a,0xc2,0x6d,0xf5,0x40,0x0f,0xbd,0xfb,0x53,0x19,0xc9,0x20,0x8d,
- 0x03,0x0a,0xc3,0x3e,0x86,0xb1,0xd5,0x0c,0x5a,0x0e,0x18,0x4e,0x20,0x8d,
- 0x03,0x0a,0xc4,0x3a,0x2a,0x49,0xb4,0x72,0xa4,0x2c,0x7b,0x99,0x20,0x8d,
- 0x03,0x0a,0xc4,0x44,0x04,0x3a,0x11,0x84,0x47,0x67,0x2a,0x13,0x20,0x8d,
- 0x03,0x0a,0xc4,0x45,0x1f,0xbc,0xc9,0xa0,0x32,0x01,0xeb,0xbc,0x20,0x8d,
- 0x03,0x0a,0xc4,0x55,0x2a,0xb9,0xbb,0x9b,0x2a,0xe7,0x1c,0x75,0x20,0x8d,
- 0x03,0x0a,0xc4,0xdf,0x49,0x72,0xb7,0xed,0xbe,0x9f,0x59,0xfa,0x20,0x8d,
- 0x03,0x0a,0xc4,0xe0,0x24,0x19,0x5a,0x39,0xc6,0xbe,0x74,0xee,0x20,0x8d,
- 0x03,0x0a,0xc5,0xdc,0x95,0xee,0xec,0x4d,0x25,0xb1,0xa1,0x5a,0x20,0x8d,
- 0x03,0x0a,0xc6,0x47,0x01,0xca,0x17,0xe1,0x47,0x46,0x9b,0xd6,0x20,0x8d,
- 0x03,0x0a,0xce,0xf6,0xda,0x2a,0x7f,0x69,0x90,0xad,0x89,0xe4,0x20,0x8d,
- 0x03,0x0a,0xce,0xf1,0x60,0x80,0x76,0xe7,0x9a,0x36,0xdc,0xc7,0x20,0x8d,
- 0x03,0x0a,0xcf,0x98,0x18,0x43,0xeb,0x5d,0xd7,0x16,0xf1,0x50,0x20,0x8d,
- 0x03,0x0a,0xc8,0x76,0xb8,0x89,0x52,0x6f,0x23,0x93,0xe5,0x24,0x20,0x8d,
- 0x03,0x0a,0xc9,0x3e,0xe1,0xbf,0xef,0xc8,0x22,0x97,0xae,0x51,0x20,0x8d,
- 0x03,0x0a,0xc9,0x82,0xc3,0xcc,0x29,0x07,0x0b,0x8d,0x6f,0xfb,0x20,0x8d,
- 0x03,0x0a,0xc9,0xfe,0x7a,0x81,0x62,0x35,0x52,0xf7,0x02,0x0c,0x20,0x8d,
- 0x03,0x0a,0xca,0x33,0x3a,0xdc,0x87,0x62,0x7a,0xc2,0x1d,0xe6,0x20,0x8d,
- 0x03,0x0a,0xca,0x50,0x8d,0xe0,0x82,0x1c,0x59,0x0f,0xef,0x1b,0x20,0x8d,
- 0x03,0x0a,0xca,0xa3,0x66,0x19,0x34,0xac,0xb2,0x0f,0x60,0x9a,0x20,0x8d,
- 0x03,0x0a,0xcb,0xb3,0xa0,0x39,0xf6,0x46,0xec,0x5a,0x42,0xc6,0x20,0x8d,
- 0x03,0x0a,0xcc,0xc6,0x22,0xb4,0xfc,0xf7,0xff,0xb0,0xa2,0xb4,0x20,0x8d,
- 0x03,0x0a,0xcd,0x2e,0x71,0x78,0x7b,0x6d,0x9e,0x61,0x70,0x05,0x20,0x8d,
- 0x03,0x0a,0xcd,0x31,0x38,0x94,0x95,0xca,0x44,0xf4,0x65,0x68,0x20,0x8d,
- 0x03,0x0a,0xcd,0x61,0xe1,0xbe,0x7b,0x46,0x9c,0x51,0xbf,0x66,0x20,0x8d,
- 0x03,0x0a,0xcd,0xac,0xcf,0x18,0x1f,0xa6,0x8f,0x02,0x6a,0x43,0x20,0x8d,
- 0x03,0x0a,0xce,0x20,0x1e,0x2c,0x8d,0x2c,0x9e,0xd9,0xa7,0xac,0x20,0x8d,
0x04,0x20,0xd1,0xbb,0x02,0x8d,0x4d,0xd5,0x6a,0x20,0xc0,0xf9,0x16,0x2b,0x84,0x22,0x66,0xe0,0x89,0x45,0x60,0x37,0x52,0xe2,0x0b,0xa5,0xb4,0xf8,0x26,0xb3,0x8f,0x5a,0x30,0xed,0x20,0x8d,
0x04,0x20,0xd2,0x59,0x3b,0xd7,0x14,0x7e,0xd0,0x98,0xfe,0x9e,0xa5,0x69,0xf4,0x26,0x6d,0x72,0x6f,0xc3,0x76,0xce,0x1d,0x40,0x41,0xa2,0xa1,0xaf,0xf9,0x6e,0x57,0x2d,0x9d,0xc3,0x20,0x8d,
0x04,0x20,0xdf,0xd9,0xed,0x59,0xbf,0x1e,0x77,0x48,0x3c,0x13,0x3b,0xc5,0xc8,0x15,0x86,0x88,0x68,0xf0,0x08,0xe9,0xee,0x9b,0x3d,0xa4,0x33,0x0a,0x68,0x67,0x86,0x9d,0xe2,0x83,0x20,0x8d,
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index e78dc9f2a5..d5fd43e131 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -24,7 +24,7 @@ namespace Consensus {
* @param[out] txfee Set to the transaction fee if successful.
* Preconditions: tx.IsCoinBase() is false.
*/
-bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
+[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
} // namespace Consensus
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 3a61af28b7..6fd2701e2e 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -340,7 +340,6 @@ void BaseIndex::Interrupt()
bool BaseIndex::Start(CChainState& active_chainstate)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
m_chainstate = &active_chainstate;
// Need to register this ValidationInterface before running Init(), so that
// callbacks are not missed if Init sets m_synced to true.
diff --git a/src/init.cpp b/src/init.cpp
index 7f64b1acfa..4dc82811f9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -283,7 +283,7 @@ void Shutdown(NodeContext& node)
init::UnsetGlobals();
node.mempool.reset();
node.fee_estimator.reset();
- node.chainman = nullptr;
+ node.chainman.reset();
node.scheduler.reset();
try {
@@ -347,12 +347,8 @@ static void OnRPCStopped()
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
-void SetupServerArgs(NodeContext& node)
+void SetupServerArgs(ArgsManager& argsman)
{
- assert(!node.args);
- node.args = &gArgs;
- ArgsManager& argsman = *node.args;
-
SetupHelpOptions(argsman);
argsman.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
@@ -1179,8 +1175,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get(), check_ratio);
assert(!node.chainman);
- node.chainman = &g_chainman;
- ChainstateManager& chainman = *Assert(node.chainman);
+ node.chainman = std::make_unique<ChainstateManager>();
+ ChainstateManager& chainman = *node.chainman;
assert(!node.peerman);
node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(),
@@ -1385,7 +1381,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
if (!chainman.BlockIndex().empty() &&
- !g_chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
+ !chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
}
@@ -1400,7 +1396,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// If we're not mid-reindex (based on disk + args), add a genesis block on disk
// (otherwise we use the one already on disk).
// This is called again in ThreadImport after the reindex completes.
- if (!fReindex && !::ChainstateActive().LoadGenesisBlock(chainparams)) {
+ if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock(chainparams)) {
strLoadError = _("Error initializing block database");
break;
}
@@ -1549,21 +1545,21 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 8: start indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
- if (!g_txindex->Start(::ChainstateActive())) {
+ if (!g_txindex->Start(chainman.ActiveChainstate())) {
return false;
}
}
for (const auto& filter_type : g_enabled_filter_types) {
InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex);
- if (!GetBlockFilterIndex(filter_type)->Start(::ChainstateActive())) {
+ if (!GetBlockFilterIndex(filter_type)->Start(chainman.ActiveChainstate())) {
return false;
}
}
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
g_coin_stats_index = std::make_unique<CoinStatsIndex>(/* cache size */ 0, false, fReindex);
- if (!g_coin_stats_index->Start(::ChainstateActive())) {
+ if (!g_coin_stats_index->Start(chainman.ActiveChainstate())) {
return false;
}
}
@@ -1611,7 +1607,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
- if (::ChainActive().Tip() == nullptr) {
+ if (chainman.ActiveChain().Tip() == nullptr) {
block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
} else {
fHaveGenesis = true;
diff --git a/src/init.h b/src/init.h
index 328eda9c7e..b856468e5d 100644
--- a/src/init.h
+++ b/src/init.h
@@ -69,7 +69,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info
/**
* Register all arguments with the ArgsManager
*/
-void SetupServerArgs(NodeContext& node);
+void SetupServerArgs(ArgsManager& argsman);
/** Returns licensing information (for -version) */
std::string LicenseInfo();
diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp
index 49684ede83..6b6157c139 100644
--- a/src/init/bitcoin-node.cpp
+++ b/src/init/bitcoin-node.cpp
@@ -6,6 +6,7 @@
#include <interfaces/init.h>
#include <interfaces/ipc.h>
#include <node/context.h>
+#include <util/system.h>
#include <memory>
@@ -20,6 +21,7 @@ public:
: m_node(node),
m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
{
+ m_node.args = &gArgs;
m_node.init = this;
}
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp
index 1e17ce4d3c..1d4504c24f 100644
--- a/src/init/bitcoind.cpp
+++ b/src/init/bitcoind.cpp
@@ -4,6 +4,7 @@
#include <interfaces/init.h>
#include <node/context.h>
+#include <util/system.h>
#include <memory>
@@ -14,6 +15,7 @@ class BitcoindInit : public interfaces::Init
public:
BitcoindInit(NodeContext& node) : m_node(node)
{
+ m_node.args = &gArgs;
m_node.init = this;
}
NodeContext& m_node;
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 3395741b1b..7cac435e96 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -277,6 +277,9 @@ public:
//! to be prepared to handle this by ignoring notifications about unknown
//! removed transactions and already added new transactions.
virtual void requestMempoolTransactions(Notifications& notifications) = 0;
+
+ //! Check if Taproot has activated
+ virtual bool isTaprootActive() const = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 1dd1e92e2f..35b6160cea 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -6,6 +6,7 @@
#define BITCOIN_INTERFACES_NODE_H
#include <amount.h> // For CAmount
+#include <external_signer.h>
#include <net.h> // For NodeId
#include <net_types.h> // For banmap_t
#include <netaddress.h> // For Network
@@ -110,6 +111,11 @@ public:
//! Disconnect node by id.
virtual bool disconnectById(NodeId id) = 0;
+#ifdef ENABLE_EXTERNAL_SIGNER
+ //! List external signers
+ virtual std::vector<ExternalSigner> externalSigners() = 0;
+#endif
+
//! Get total bytes recv.
virtual int64_t getTotalBytesRecv() = 0;
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 6ccfd7fc20..fb1febc11b 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -112,14 +112,14 @@ public:
//! Get wallet address list.
virtual std::vector<WalletAddress> getAddresses() = 0;
- //! Add dest data.
- virtual bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) = 0;
+ //! Get receive requests.
+ virtual std::vector<std::string> getAddressReceiveRequests() = 0;
- //! Erase dest data.
- virtual bool eraseDestData(const CTxDestination& dest, const std::string& key) = 0;
+ //! Save or remove receive request.
+ virtual bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) = 0;
- //! Get dest values with prefix.
- virtual std::vector<std::string> getDestValues(const std::string& prefix) = 0;
+ //! Display address on external signer
+ virtual bool displayAddress(const CTxDestination& dest) = 0;
//! Lock coin.
virtual void lockCoin(const COutPoint& output) = 0;
@@ -198,9 +198,9 @@ public:
virtual TransactionError fillPSBT(int sighash_type,
bool sign,
bool bip32derivs,
+ size_t* n_signed,
PartiallySignedTransaction& psbtx,
- bool& complete,
- size_t* n_signed) = 0;
+ bool& complete) = 0;
//! Get balances.
virtual WalletBalances getBalances() = 0;
@@ -255,6 +255,9 @@ public:
// Return whether private keys enabled.
virtual bool privateKeysDisabled() = 0;
+ // Return whether wallet uses an external signer.
+ virtual bool hasExternalSigner() = 0;
+
// Get default address type.
virtual OutputType getDefaultAddressType() = 0;
diff --git a/src/key_io.cpp b/src/key_io.cpp
index dbcbfa1f29..615f4c9312 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -54,6 +54,14 @@ public:
return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data);
}
+ std::string operator()(const WitnessV1Taproot& tap) const
+ {
+ std::vector<unsigned char> data = {1};
+ data.reserve(53);
+ ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, tap.begin(), tap.end());
+ return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data);
+ }
+
std::string operator()(const WitnessUnknown& id) const
{
if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) {
@@ -135,6 +143,13 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
return CNoDestination();
}
+ if (version == 1 && data.size() == WITNESS_V1_TAPROOT_SIZE) {
+ static_assert(WITNESS_V1_TAPROOT_SIZE == WitnessV1Taproot::size());
+ WitnessV1Taproot tap;
+ std::copy(data.begin(), data.end(), tap.begin());
+ return tap;
+ }
+
if (version > 16) {
error_str = "Invalid Bech32 address witness version";
return CNoDestination();
diff --git a/src/miner.cpp b/src/miner.cpp
index eccddbb04f..0cf303eb3c 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -45,15 +45,7 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
block.vtx.at(0) = MakeTransactionRef(tx);
- CBlockIndex* prev_block;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman.m_blockman));
- prev_block = chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
- }
+ CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
GenerateCoinbaseCommitment(block, prev_block, Params().GetConsensus());
block.hashMerkleRoot = BlockMerkleRoot(block);
@@ -124,7 +116,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
LOCK2(cs_main, m_mempool.cs);
- assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*m_chainstate.m_chain.Tip()));
CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
@@ -184,7 +175,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_chainstate));
if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
}
diff --git a/src/net.cpp b/src/net.cpp
index 9c6cb379d2..60059249ed 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -42,6 +42,7 @@
#endif
#include <algorithm>
+#include <array>
#include <cstdint>
#include <functional>
#include <optional>
@@ -841,18 +842,6 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
return a.nTimeConnected > b.nTimeConnected;
}
-static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
-{
- if (a.m_is_local != b.m_is_local) return b.m_is_local;
- return a.nTimeConnected > b.nTimeConnected;
-}
-
-static bool CompareOnionTimeConnected(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b)
-{
- if (a.m_is_onion != b.m_is_onion) return b.m_is_onion;
- return a.nTimeConnected > b.nTimeConnected;
-}
-
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
return a.nKeyedNetGroup < b.nKeyedNetGroup;
}
@@ -883,6 +872,26 @@ static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const
return a.nTimeConnected > b.nTimeConnected;
}
+/**
+ * Sort eviction candidates by network/localhost and connection uptime.
+ * Candidates near the beginning are more likely to be evicted, and those
+ * near the end are more likely to be protected, e.g. less likely to be evicted.
+ * - First, nodes that are not `is_local` and that do not belong to `network`,
+ * sorted by increasing uptime (from most recently connected to connected longer).
+ * - Then, nodes that are `is_local` or belong to `network`, sorted by increasing uptime.
+ */
+struct CompareNodeNetworkTime {
+ const bool m_is_local;
+ const Network m_network;
+ CompareNodeNetworkTime(bool is_local, Network network) : m_is_local(is_local), m_network(network) {}
+ bool operator()(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b) const
+ {
+ if (m_is_local && a.m_is_local != b.m_is_local) return b.m_is_local;
+ if ((a.m_network == m_network) != (b.m_network == m_network)) return b.m_network == m_network;
+ return a.nTimeConnected > b.nTimeConnected;
+ };
+};
+
//! Sort an array by the specified comparator, then erase the last K elements where predicate is true.
template <typename T, typename Comparator>
static void EraseLastKElements(
@@ -894,40 +903,72 @@ static void EraseLastKElements(
elements.erase(std::remove_if(elements.end() - eraseSize, elements.end(), predicate), elements.end());
}
-void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates)
+void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& eviction_candidates)
{
// Protect the half of the remaining nodes which have been connected the longest.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- // To favorise the diversity of our peer connections, reserve up to (half + 2) of
- // these protected spots for onion and localhost peers, if any, even if they're not
- // longest uptime overall. This helps protect tor peers, which tend to be otherwise
+ // To favorise the diversity of our peer connections, reserve up to half of these protected
+ // spots for Tor/onion, localhost and I2P peers, even if they're not longest uptime overall.
+ // This helps protect these higher-latency peers that tend to be otherwise
// disadvantaged under our eviction criteria.
- const size_t initial_size = vEvictionCandidates.size();
- size_t total_protect_size = initial_size / 2;
- const size_t onion_protect_size = total_protect_size / 2;
-
- if (onion_protect_size) {
- // Pick out up to 1/4 peers connected via our onion service, sorted by longest uptime.
- EraseLastKElements(vEvictionCandidates, CompareOnionTimeConnected, onion_protect_size,
- [](const NodeEvictionCandidate& n) { return n.m_is_onion; });
- }
-
- const size_t localhost_min_protect_size{2};
- if (onion_protect_size >= localhost_min_protect_size) {
- // Allocate any remaining slots of the 1/4, or minimum 2 additional slots,
- // to localhost peers, sorted by longest uptime, as manually configured
- // hidden services not using `-bind=addr[:port]=onion` will not be detected
- // as inbound onion connections.
- const size_t remaining_tor_slots{onion_protect_size - (initial_size - vEvictionCandidates.size())};
- const size_t localhost_protect_size{std::max(remaining_tor_slots, localhost_min_protect_size)};
- EraseLastKElements(vEvictionCandidates, CompareLocalHostTimeConnected, localhost_protect_size,
- [](const NodeEvictionCandidate& n) { return n.m_is_local; });
+ const size_t initial_size = eviction_candidates.size();
+ const size_t total_protect_size{initial_size / 2};
+
+ // Disadvantaged networks to protect: I2P, localhost, Tor/onion. In case of equal counts, earlier
+ // array members have first opportunity to recover unused slots from the previous iteration.
+ struct Net { bool is_local; Network id; size_t count; };
+ std::array<Net, 3> networks{
+ {{false, NET_I2P, 0}, {/* localhost */ true, NET_MAX, 0}, {false, NET_ONION, 0}}};
+
+ // Count and store the number of eviction candidates per network.
+ for (Net& n : networks) {
+ n.count = std::count_if(eviction_candidates.cbegin(), eviction_candidates.cend(),
+ [&n](const NodeEvictionCandidate& c) {
+ return n.is_local ? c.m_is_local : c.m_network == n.id;
+ });
+ }
+ // Sort `networks` by ascending candidate count, to give networks having fewer candidates
+ // the first opportunity to recover unused protected slots from the previous iteration.
+ std::stable_sort(networks.begin(), networks.end(), [](Net a, Net b) { return a.count < b.count; });
+
+ // Protect up to 25% of the eviction candidates by disadvantaged network.
+ const size_t max_protect_by_network{total_protect_size / 2};
+ size_t num_protected{0};
+
+ while (num_protected < max_protect_by_network) {
+ const size_t disadvantaged_to_protect{max_protect_by_network - num_protected};
+ const size_t protect_per_network{
+ std::max(disadvantaged_to_protect / networks.size(), static_cast<size_t>(1))};
+
+ // Early exit flag if there are no remaining candidates by disadvantaged network.
+ bool protected_at_least_one{false};
+
+ for (const Net& n : networks) {
+ if (n.count == 0) continue;
+ const size_t before = eviction_candidates.size();
+ EraseLastKElements(eviction_candidates, CompareNodeNetworkTime(n.is_local, n.id),
+ protect_per_network, [&n](const NodeEvictionCandidate& c) {
+ return n.is_local ? c.m_is_local : c.m_network == n.id;
+ });
+ const size_t after = eviction_candidates.size();
+ if (before > after) {
+ protected_at_least_one = true;
+ num_protected += before - after;
+ if (num_protected >= max_protect_by_network) {
+ break;
+ }
+ }
+ }
+ if (!protected_at_least_one) {
+ break;
+ }
}
// Calculate how many we removed, and update our total number of peers that
// we want to protect based on uptime accordingly.
- total_protect_size -= initial_size - vEvictionCandidates.size();
- EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
+ assert(num_protected == initial_size - eviction_candidates.size());
+ const size_t remaining_to_protect{total_protect_size - num_protected};
+ EraseLastKElements(eviction_candidates, ReverseCompareNodeTimeConnected, remaining_to_protect);
}
[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
@@ -944,8 +985,7 @@ void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvict
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
// Protect up to 8 non-tx-relay peers that have sent us novel blocks.
- const size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
- EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, erase_size,
+ EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, 8,
[](const NodeEvictionCandidate& n) { return !n.fRelayTxes && n.fRelevantServices; });
// Protect 4 nodes that most recently sent us novel blocks.
@@ -1024,7 +1064,7 @@ bool CConnman::AttemptToEvictConnection()
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup,
node->m_prefer_evict, node->addr.IsLocal(),
- node->m_inbound_onion};
+ node->ConnectedThroughNetwork()};
vEvictionCandidates.push_back(candidate);
}
}
diff --git a/src/net.h b/src/net.h
index b43916c55e..01658e8973 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1209,7 +1209,7 @@ struct NodeEvictionCandidate
uint64_t nKeyedNetGroup;
bool prefer_evict;
bool m_is_local;
- bool m_is_onion;
+ Network m_network;
};
/**
@@ -1227,20 +1227,20 @@ struct NodeEvictionCandidate
* longest, to replicate the non-eviction implicit behavior and preclude attacks
* that start later.
*
- * Half of these protected spots (1/4 of the total) are reserved for onion peers
- * connected via our tor control service, if any, sorted by longest uptime, even
- * if they're not longest uptime overall. Any remaining slots of the 1/4 are
- * then allocated to protect localhost peers, if any (or up to 2 localhost peers
- * if no slots remain and 2 or more onion peers were protected), sorted by
- * longest uptime, as manually configured hidden services not using
- * `-bind=addr[:port]=onion` will not be detected as inbound onion connections.
+ * Half of these protected spots (1/4 of the total) are reserved for the
+ * following categories of peers, sorted by longest uptime, even if they're not
+ * longest uptime overall:
+ *
+ * - onion peers connected via our tor control service
+ *
+ * - localhost peers, as manually configured hidden services not using
+ * `-bind=addr[:port]=onion` will not be detected as inbound onion connections
*
- * This helps protect onion peers, which tend to be otherwise disadvantaged
- * under our eviction criteria for their higher min ping times relative to IPv4
- * and IPv6 peers, and favorise the diversity of peer connections.
+ * - I2P peers
*
- * This function was extracted from SelectNodeToEvict() to be able to test the
- * ratio-based protection logic deterministically.
+ * This helps protect these privacy network peers, which tend to be otherwise
+ * disadvantaged under our eviction criteria for their higher min ping times
+ * relative to IPv4/IPv6 peers, and favorise the diversity of peer connections.
*/
void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 65224b4259..b2112cfd2e 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -159,10 +159,10 @@ static constexpr size_t MAX_ADDR_TO_SEND{1000};
namespace {
/** Blocks that are in flight, and that are in the queue to be downloaded. */
struct QueuedBlock {
- uint256 hash;
- const CBlockIndex* pindex; //!< Optional.
- bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
- std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
+ /** BlockIndex. We must have this since we only request blocks when we've already validated the header. */
+ const CBlockIndex* pindex;
+ /** Optional, used for CMPCTBLOCK downloads */
+ std::unique_ptr<PartiallyDownloadedBlock> partialBlock;
};
/**
@@ -463,16 +463,20 @@ private:
Mutex m_recent_confirmed_transactions_mutex;
std::unique_ptr<CRollingBloomFilter> m_recent_confirmed_transactions GUARDED_BY(m_recent_confirmed_transactions_mutex);
- /* Returns a bool indicating whether we requested this block.
- * Also used if a block was /not/ received and timed out or started with another peer
+ /** Have we requested this block from a peer */
+ bool IsBlockRequested(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /** Remove this block from our tracked requested blocks. Called if:
+ * - the block has been recieved from a peer
+ * - the request for the block has timed out
*/
- bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void RemoveBlockRequest(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/* Mark a block as in flight
* Returns false, still setting pit, if the block was already in flight from the same peer
* pit will only be valid as long as the same cs_main lock is being held
*/
- bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool BlockRequested(NodeId nodeid, const CBlockIndex& block, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -512,7 +516,7 @@ private:
std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
/** Number of peers from which we're downloading blocks. */
- int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
+ int m_peers_downloading_from GUARDED_BY(cs_main) = 0;
/** Storage for orphan information */
TxOrphanage m_orphanage;
@@ -627,7 +631,6 @@ struct CNodeState {
//! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
std::chrono::microseconds m_downloading_since{0us};
int nBlocksInFlight{0};
- int nBlocksInFlightValidHeaders{0};
//! Whether we consider this a preferred download peer.
bool fPreferredDownload{false};
//! Whether this peer wants invs or headers (when possible) for block announcements.
@@ -758,32 +761,42 @@ static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUS
nPreferredDownload += state->fPreferredDownload;
}
-bool PeerManagerImpl::MarkBlockAsReceived(const uint256& hash)
+bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
{
- std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
- if (itInFlight != mapBlocksInFlight.end()) {
- CNodeState *state = State(itInFlight->second.first);
- assert(state != nullptr);
- state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
- if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) {
- // Last validated block on the queue was received.
- nPeersWithValidatedDownloads--;
- }
- if (state->vBlocksInFlight.begin() == itInFlight->second.second) {
- // First block on the queue was received, update the start download time for the next one
- state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
- }
- state->vBlocksInFlight.erase(itInFlight->second.second);
- state->nBlocksInFlight--;
- state->m_stalling_since = 0us;
- mapBlocksInFlight.erase(itInFlight);
- return true;
+ return mapBlocksInFlight.find(hash) != mapBlocksInFlight.end();
+}
+
+void PeerManagerImpl::RemoveBlockRequest(const uint256& hash)
+{
+ auto it = mapBlocksInFlight.find(hash);
+ if (it == mapBlocksInFlight.end()) {
+ // Block was not requested
+ return;
}
- return false;
+
+ auto [node_id, list_it] = it->second;
+ CNodeState *state = State(node_id);
+ assert(state != nullptr);
+
+ if (state->vBlocksInFlight.begin() == list_it) {
+ // First block on the queue was received, update the start download time for the next one
+ state->m_downloading_since = std::max(state->m_downloading_since, GetTime<std::chrono::microseconds>());
+ }
+ state->vBlocksInFlight.erase(list_it);
+
+ state->nBlocksInFlight--;
+ if (state->nBlocksInFlight == 0) {
+ // Last validated block on the queue was received.
+ m_peers_downloading_from--;
+ }
+ state->m_stalling_since = 0us;
+ mapBlocksInFlight.erase(it);
}
-bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex, std::list<QueuedBlock>::iterator** pit)
+bool PeerManagerImpl::BlockRequested(NodeId nodeid, const CBlockIndex& block, std::list<QueuedBlock>::iterator** pit)
{
+ const uint256& hash{block.GetBlockHash()};
+
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -797,22 +810,20 @@ bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, co
}
// Make sure it's not listed somewhere already.
- MarkBlockAsReceived(hash);
+ RemoveBlockRequest(hash);
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
- {hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
+ {&block, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
state->nBlocksInFlight++;
- state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
// We're starting a block download (batch) from this peer.
state->m_downloading_since = GetTime<std::chrono::microseconds>();
- }
- if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) {
- nPeersWithValidatedDownloads++;
+ m_peers_downloading_from++;
}
itInFlight = mapBlocksInFlight.insert(std::make_pair(hash, std::make_pair(nodeid, it))).first;
- if (pit)
+ if (pit) {
*pit = &itInFlight->second.second;
+ }
return true;
}
@@ -978,7 +989,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
if (pindex->nStatus & BLOCK_HAVE_DATA || m_chainman.ActiveChain().Contains(pindex)) {
if (pindex->HaveTxsDownloaded())
state->pindexLastCommonBlock = pindex;
- } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
+ } else if (!IsBlockRequested(pindex->GetBlockHash())) {
// The block is not already downloaded, and not yet in flight.
if (pindex->nHeight > nWindowEnd) {
// We reached the end of the window.
@@ -1129,13 +1140,13 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
nSyncStarted--;
for (const QueuedBlock& entry : state->vBlocksInFlight) {
- mapBlocksInFlight.erase(entry.hash);
+ mapBlocksInFlight.erase(entry.pindex->GetBlockHash());
}
WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
m_txrequest.DisconnectedPeer(nodeid);
nPreferredDownload -= state->fPreferredDownload;
- nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
- assert(nPeersWithValidatedDownloads >= 0);
+ m_peers_downloading_from -= (state->nBlocksInFlight != 0);
+ assert(m_peers_downloading_from >= 0);
m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
assert(m_outbound_peers_with_protect_from_disconnect >= 0);
m_wtxid_relay_peers -= state->m_wtxid_relay;
@@ -1147,7 +1158,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
// Do a consistency check after the last peer is removed.
assert(mapBlocksInFlight.empty());
assert(nPreferredDownload == 0);
- assert(nPeersWithValidatedDownloads == 0);
+ assert(m_peers_downloading_from == 0);
assert(m_outbound_peers_with_protect_from_disconnect == 0);
assert(m_wtxid_relay_peers == 0);
assert(m_txrequest.Size() == 0);
@@ -1350,7 +1361,6 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
m_stale_tip_check_time(0),
m_ignore_incoming_txs(ignore_incoming_txs)
{
- assert(std::addressof(g_chainman) == std::addressof(m_chainman));
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -2056,7 +2066,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
- !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
+ !IsBlockRequested(pindexWalk->GetBlockHash()) &&
(!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
@@ -2081,7 +2091,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex);
+ BlockRequested(pfrom.GetId(), *pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
pindex->GetBlockHash().ToString(), pfrom.GetId());
}
@@ -2827,7 +2837,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
UpdateBlockAvailability(pfrom.GetId(), inv.hash);
- if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
+ if (!fAlreadyHave && !fImporting && !fReindex && !IsBlockRequested(inv.hash)) {
// Headers-first is the primary method of announcement on
// the network. If a node fell back to sending blocks by inv,
// it's probably for a re-org. The final block hash
@@ -3384,7 +3394,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!BlockRequested(pfrom.GetId(), *pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
else {
@@ -3397,7 +3407,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
PartiallyDownloadedBlock& partialBlock = *(*queuedBlockIt)->partialBlock;
ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ RemoveBlockRequest(pindex->GetBlockHash()); // Reset in-flight state in case Misbehaving does not result in a disconnect
Misbehaving(pfrom.GetId(), 100, "invalid compact block");
return;
} else if (status == READ_STATUS_FAILED) {
@@ -3492,7 +3502,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// process from some other peer. We do this after calling
// ProcessNewBlock so that a malleated cmpctblock announcement
// can't be used to interfere with block relay.
- MarkBlockAsReceived(pblock->GetHash());
+ RemoveBlockRequest(pblock->GetHash());
}
}
return;
@@ -3524,7 +3534,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
PartiallyDownloadedBlock& partialBlock = *it->second.second->partialBlock;
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
if (status == READ_STATUS_INVALID) {
- MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
+ RemoveBlockRequest(resp.blockhash); // Reset in-flight state in case Misbehaving does not result in a disconnect
Misbehaving(pfrom.GetId(), 100, "invalid compact block/non-matching block transactions");
return;
} else if (status == READ_STATUS_FAILED) {
@@ -3550,7 +3560,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// though the block was successfully read, and rely on the
// handling in ProcessNewBlock to ensure the block index is
// updated, etc.
- MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
+ RemoveBlockRequest(resp.blockhash); // it is now an empty pointer
fBlockRead = true;
// mapBlockSource is used for potentially punishing peers and
// updating which peers send us compact blocks, so the race
@@ -3615,9 +3625,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const uint256 hash(pblock->GetHash());
{
LOCK(cs_main);
- // Also always process if we requested the block explicitly, as we may
- // need it even though it is not a candidate for a new best tip.
- forceProcessing |= MarkBlockAsReceived(hash);
+ // Always process the block if we requested it, since we may
+ // need it even when it's not a candidate for a new best tip.
+ forceProcessing = IsBlockRequested(hash);
+ RemoveBlockRequest(hash);
// mapBlockSource is only used for punishing peers and setting
// which peers send us compact blocks, so the race between here and
// cs_main in ProcessNewBlock is fine.
@@ -4712,9 +4723,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// to unreasonably increase our timeout.
if (state.vBlocksInFlight.size() > 0) {
QueuedBlock &queuedBlock = state.vBlocksInFlight.front();
- int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0);
+ int nOtherPeersWithValidatedDownloads = m_peers_downloading_from - 1;
if (current_time > state.m_downloading_since + std::chrono::seconds{consensusParams.nPowTargetSpacing} * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) {
- LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->GetId());
+ LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.pindex->GetBlockHash().ToString(), pto->GetId());
pto->fDisconnect = true;
return true;
}
@@ -4767,7 +4778,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
for (const CBlockIndex *pindex : vToDownload) {
uint32_t nFetchFlags = GetFetchFlags(*pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex);
+ BlockRequested(pto->GetId(), *pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
pindex->nHeight, pto->GetId());
}
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 352abae298..1ea3969978 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -32,14 +32,7 @@ CNetAddr::BIP155Network CNetAddr::GetBIP155Network() const
case NET_IPV6:
return BIP155Network::IPV6;
case NET_ONION:
- switch (m_addr.size()) {
- case ADDR_TORV2_SIZE:
- return BIP155Network::TORV2;
- case ADDR_TORV3_SIZE:
- return BIP155Network::TORV3;
- default:
- assert(false);
- }
+ return BIP155Network::TORV3;
case NET_I2P:
return BIP155Network::I2P;
case NET_CJDNS:
@@ -72,14 +65,6 @@ bool CNetAddr::SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t addre
throw std::ios_base::failure(
strprintf("BIP155 IPv6 address with length %u (should be %u)", address_size,
ADDR_IPV6_SIZE));
- case BIP155Network::TORV2:
- if (address_size == ADDR_TORV2_SIZE) {
- m_net = NET_ONION;
- return true;
- }
- throw std::ios_base::failure(
- strprintf("BIP155 TORv2 address with length %u (should be %u)", address_size,
- ADDR_TORV2_SIZE));
case BIP155Network::TORV3:
if (address_size == ADDR_TORV3_SIZE) {
m_net = NET_ONION;
@@ -130,7 +115,7 @@ void CNetAddr::SetIP(const CNetAddr& ipIn)
assert(ipIn.m_addr.size() == ADDR_IPV6_SIZE);
break;
case NET_ONION:
- assert(ipIn.m_addr.size() == ADDR_TORV2_SIZE || ipIn.m_addr.size() == ADDR_TORV3_SIZE);
+ assert(ipIn.m_addr.size() == ADDR_TORV3_SIZE);
break;
case NET_I2P:
assert(ipIn.m_addr.size() == ADDR_I2P_SIZE);
@@ -161,9 +146,12 @@ void CNetAddr::SetLegacyIPv6(Span<const uint8_t> ipv6)
m_net = NET_IPV4;
skip = sizeof(IPV4_IN_IPV6_PREFIX);
} else if (HasPrefix(ipv6, TORV2_IN_IPV6_PREFIX)) {
- // TORv2-in-IPv6
- m_net = NET_ONION;
- skip = sizeof(TORV2_IN_IPV6_PREFIX);
+ // TORv2-in-IPv6 (unsupported). Unserialize as !IsValid(), thus ignoring them.
+ // Mimic a default-constructed CNetAddr object which is !IsValid() and thus
+ // will not be gossiped, but continue reading next addresses from the stream.
+ m_net = NET_IPV6;
+ m_addr.assign(ADDR_IPV6_SIZE, 0x0);
+ return;
} else if (HasPrefix(ipv6, INTERNAL_IN_IPV6_PREFIX)) {
// Internal-in-IPv6
m_net = NET_INTERNAL;
@@ -254,12 +242,7 @@ bool CNetAddr::SetTor(const std::string& addr)
return false;
}
- switch (input.size()) {
- case ADDR_TORV2_SIZE:
- m_net = NET_ONION;
- m_addr.assign(input.begin(), input.end());
- return true;
- case torv3::TOTAL_LEN: {
+ if (input.size() == torv3::TOTAL_LEN) {
Span<const uint8_t> input_pubkey{input.data(), ADDR_TORV3_SIZE};
Span<const uint8_t> input_checksum{input.data() + ADDR_TORV3_SIZE, torv3::CHECKSUM_LEN};
Span<const uint8_t> input_version{input.data() + ADDR_TORV3_SIZE + torv3::CHECKSUM_LEN, sizeof(torv3::VERSION)};
@@ -279,7 +262,6 @@ bool CNetAddr::SetTor(const std::string& addr)
m_addr.assign(input_pubkey.begin(), input_pubkey.end());
return true;
}
- }
return false;
}
@@ -528,7 +510,6 @@ bool CNetAddr::IsAddrV1Compatible() const
case NET_INTERNAL:
return true;
case NET_ONION:
- return m_addr.size() == ADDR_TORV2_SIZE;
case NET_I2P:
case NET_CJDNS:
return false;
@@ -613,33 +594,26 @@ static std::string IPv6ToString(Span<const uint8_t> a, uint32_t scope_id)
return r;
}
+static std::string OnionToString(Span<const uint8_t> addr)
+{
+ uint8_t checksum[torv3::CHECKSUM_LEN];
+ torv3::Checksum(addr, checksum);
+ // TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
+ prevector<torv3::TOTAL_LEN, uint8_t> address{addr.begin(), addr.end()};
+ address.insert(address.end(), checksum, checksum + torv3::CHECKSUM_LEN);
+ address.insert(address.end(), torv3::VERSION, torv3::VERSION + sizeof(torv3::VERSION));
+ return EncodeBase32(address) + ".onion";
+}
+
std::string CNetAddr::ToStringIP() const
{
switch (m_net) {
case NET_IPV4:
return IPv4ToString(m_addr);
- case NET_IPV6: {
+ case NET_IPV6:
return IPv6ToString(m_addr, m_scope_id);
- }
case NET_ONION:
- switch (m_addr.size()) {
- case ADDR_TORV2_SIZE:
- return EncodeBase32(m_addr) + ".onion";
- case ADDR_TORV3_SIZE: {
-
- uint8_t checksum[torv3::CHECKSUM_LEN];
- torv3::Checksum(m_addr, checksum);
-
- // TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
- prevector<torv3::TOTAL_LEN, uint8_t> address{m_addr.begin(), m_addr.end()};
- address.insert(address.end(), checksum, checksum + torv3::CHECKSUM_LEN);
- address.insert(address.end(), torv3::VERSION, torv3::VERSION + sizeof(torv3::VERSION));
-
- return EncodeBase32(address) + ".onion";
- }
- default:
- assert(false);
- }
+ return OnionToString(m_addr);
case NET_I2P:
return EncodeBase32(m_addr, false /* don't pad with = */) + ".b32.i2p";
case NET_CJDNS:
diff --git a/src/netaddress.h b/src/netaddress.h
index 897ce46cda..dd47ab5749 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -11,7 +11,9 @@
#include <attributes.h>
#include <compat.h>
+#include <crypto/siphash.h>
#include <prevector.h>
+#include <random.h>
#include <serialize.h>
#include <tinyformat.h>
#include <util/strencodings.h>
@@ -97,9 +99,6 @@ static constexpr size_t ADDR_IPV4_SIZE = 4;
/// Size of IPv6 address (in bytes).
static constexpr size_t ADDR_IPV6_SIZE = 16;
-/// Size of TORv2 address (in bytes).
-static constexpr size_t ADDR_TORV2_SIZE = 10;
-
/// Size of TORv3 address (in bytes). This is the length of just the address
/// as used in BIP155, without the checksum and the version byte.
static constexpr size_t ADDR_TORV3_SIZE = 32;
@@ -254,14 +253,14 @@ class CNetAddr
}
}
+ friend class CNetAddrHash;
friend class CSubNet;
private:
/**
* Parse a Tor address and set this object to it.
* @param[in] addr Address to parse, must be a valid C string, for example
- * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
- * 6hzph5hv6337r6p2.onion.
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.
* @returns Whether the operation was successful.
* @see CNetAddr::IsTor()
*/
@@ -303,7 +302,7 @@ class CNetAddr
/**
* Get the BIP155 network id of this address.
* Must not be called for IsInternal() objects.
- * @returns BIP155 network id
+ * @returns BIP155 network id, except TORV2 which is no longer supported.
*/
BIP155Network GetBIP155Network() const;
@@ -334,23 +333,14 @@ class CNetAddr
memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
return;
- case NET_ONION:
- if (m_addr.size() == ADDR_TORV3_SIZE) {
- break;
- }
- prefix_size = sizeof(TORV2_IN_IPV6_PREFIX);
- assert(prefix_size + m_addr.size() == sizeof(arr));
- memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size);
- memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
- return;
case NET_INTERNAL:
prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX);
assert(prefix_size + m_addr.size() == sizeof(arr));
memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
return;
+ case NET_ONION:
case NET_I2P:
- break;
case NET_CJDNS:
break;
case NET_UNROUTABLE:
@@ -358,7 +348,7 @@ class CNetAddr
assert(false);
} // no default case, so the compiler can warn about missing cases
- // Serialize TORv3, I2P and CJDNS as all-zeros.
+ // Serialize ONION, I2P and CJDNS as all-zeros.
memset(arr, 0x0, V1_SERIALIZATION_SIZE);
}
@@ -477,6 +467,22 @@ class CNetAddr
}
};
+class CNetAddrHash
+{
+public:
+ size_t operator()(const CNetAddr& a) const noexcept
+ {
+ CSipHasher hasher(m_salt_k0, m_salt_k1);
+ hasher.Write(a.m_net);
+ hasher.Write(a.m_addr.data(), a.m_addr.size());
+ return static_cast<size_t>(hasher.Finalize());
+ }
+
+private:
+ const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
+ const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
+};
+
class CSubNet
{
protected:
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 6c66c565ad..013d61282b 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -248,7 +248,6 @@ bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight,
// when the undo file is keeping up with the block file, we want to flush it explicitly
// when it is lagging behind (more blocks arrive than are being connected), we let the
// undo block write case handle it
- assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
index 23d4fa2aae..50fddf3ab0 100644
--- a/src/node/coin.cpp
+++ b/src/node/coin.cpp
@@ -13,7 +13,6 @@ void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins)
assert(node.mempool);
assert(node.chainman);
LOCK2(cs_main, node.mempool->cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
CCoinsViewCache& chain_view = node.chainman->ActiveChainstate().CoinsTip();
CCoinsViewMemPool mempool_view(&chain_view, *node.mempool);
for (auto& coin : coins) {
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 38c1d29250..67e497c218 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -97,7 +97,6 @@ static bool GetUTXOStats(CCoinsView* view, BlockManager& blockman, CCoinsStats&
if (!pindex) {
{
LOCK(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
pindex = blockman.LookupBlockIndex(view->GetBestBlock());
}
}
diff --git a/src/node/context.cpp b/src/node/context.cpp
index 6d22a6b110..9afadd09a9 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -12,6 +12,7 @@
#include <policy/fees.h>
#include <scheduler.h>
#include <txmempool.h>
+#include <validation.h>
NodeContext::NodeContext() {}
NodeContext::~NodeContext() {}
diff --git a/src/node/context.h b/src/node/context.h
index 06adb33a80..135f9ea1c6 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -44,7 +44,7 @@ struct NodeContext {
std::unique_ptr<CTxMemPool> mempool;
std::unique_ptr<CBlockPolicyEstimator> fee_estimator;
std::unique_ptr<PeerManager> peerman;
- ChainstateManager* chainman{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
+ std::unique_ptr<ChainstateManager> chainman;
std::unique_ptr<BanMan> banman;
ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<interfaces::Chain> chain;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 8befbf5e30..2d05f9d5fb 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -170,6 +170,16 @@ public:
}
return false;
}
+#ifdef ENABLE_EXTERNAL_SIGNER
+ std::vector<ExternalSigner> externalSigners() override
+ {
+ std::vector<ExternalSigner> signers = {};
+ const std::string command = gArgs.GetArg("-signer", "");
+ if (command == "") return signers;
+ ExternalSigner::Enumerate(command, signers, Params().NetworkIDString());
+ return signers;
+ }
+#endif
int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; }
int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; }
size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; }
@@ -187,26 +197,16 @@ public:
int getNumBlocks() override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
return chainman().ActiveChain().Height();
}
uint256 getBestBlockHash() override
{
- const CBlockIndex* tip;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
- tip = chainman().ActiveChain().Tip();
- }
+ const CBlockIndex* tip = WITH_LOCK(::cs_main, return chainman().ActiveChain().Tip());
return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash();
}
int64_t getLastBlockTime() override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
if (chainman().ActiveChain().Tip()) {
return chainman().ActiveChain().Tip()->GetBlockTime();
}
@@ -217,22 +217,12 @@ public:
const CBlockIndex* tip;
{
LOCK(::cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
tip = chainman().ActiveChain().Tip();
}
return GuessVerificationProgress(Params().TxData(), tip);
}
bool isInitialBlockDownload() override {
- const CChainState* active_chainstate;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- active_chainstate = &m_context->chainman->ActiveChainstate();
- assert(std::addressof(::ChainstateActive()) == std::addressof(*active_chainstate));
- }
- return active_chainstate->IsInitialBlockDownload();
+ return chainman().ActiveChainstate().IsInitialBlockDownload();
}
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
@@ -259,7 +249,6 @@ public:
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{
LOCK(::cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainman().ActiveChainstate()));
return chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin);
}
WalletClient& walletClient() override
@@ -466,14 +455,12 @@ public:
bool checkFinalTx(const CTransaction& tx) override
{
LOCK(cs_main);
- assert(std::addressof(::ChainActive()) == std::addressof(chainman().ActiveChain()));
return CheckFinalTx(chainman().ActiveChain().Tip(), tx);
}
std::optional<int> findLocatorFork(const CBlockLocator& locator) override
{
LOCK(cs_main);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
if (CBlockIndex* fork = m_node.chainman->m_blockman.FindForkInGlobalIndex(active, locator)) {
return fork->nHeight;
}
@@ -483,7 +470,6 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
return FillBlock(m_node.chainman->m_blockman.LookupBlockIndex(hash), block, lock, active);
}
bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height, const FoundBlock& block) override
@@ -496,7 +482,6 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
if (const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash)) {
if (const CBlockIndex* ancestor = block->GetAncestor(ancestor_height)) {
return FillBlock(ancestor, ancestor_out, lock, active);
@@ -508,9 +493,7 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block = m_node.chainman->m_blockman.LookupBlockIndex(block_hash);
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* ancestor = m_node.chainman->m_blockman.LookupBlockIndex(ancestor_hash);
if (block && ancestor && block->GetAncestor(ancestor->nHeight) != ancestor) ancestor = nullptr;
return FillBlock(ancestor, ancestor_out, lock, active);
@@ -519,9 +502,7 @@ public:
{
WAIT_LOCK(cs_main, lock);
const CChain& active = Assert(m_node.chainman)->ActiveChain();
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block1 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash1);
- assert(std::addressof(g_chainman) == std::addressof(*m_node.chainman));
const CBlockIndex* block2 = m_node.chainman->m_blockman.LookupBlockIndex(block_hash2);
const CBlockIndex* ancestor = block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
// Using & instead of && below to avoid short circuiting and leaving
@@ -532,7 +513,6 @@ public:
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman().m_blockman));
return GuessVerificationProgress(Params().TxData(), chainman().m_blockman.LookupBlockIndex(block_hash));
}
bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override
@@ -545,7 +525,6 @@ public:
// used to limit the range, and passing min_height that's too low or
// max_height that's too high will not crash or change the result.
LOCK(::cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainman().m_blockman));
if (CBlockIndex* block = chainman().m_blockman.LookupBlockIndex(block_hash)) {
if (max_height && block->nHeight >= *max_height) block = block->GetAncestor(*max_height);
for (; block->nStatus & BLOCK_HAVE_DATA; block = block->pprev) {
@@ -637,16 +616,7 @@ public:
}
bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); }
bool isInitialBlockDownload() override {
- const CChainState* active_chainstate;
- {
- // TODO: Temporary scope to check correctness of refactored code.
- // Should be removed manually after merge of
- // https://github.com/bitcoin/bitcoin/pull/20158
- LOCK(::cs_main);
- active_chainstate = &chainman().ActiveChainstate();
- assert(std::addressof(::ChainstateActive()) == std::addressof(*active_chainstate));
- }
- return active_chainstate->IsInitialBlockDownload();
+ return chainman().ActiveChainstate().IsInitialBlockDownload();
}
bool shutdownRequested() override { return ShutdownRequested(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
@@ -709,6 +679,12 @@ public:
notifications.transactionAddedToMempool(entry.GetSharedTx(), 0 /* mempool_sequence */);
}
}
+ bool isTaprootActive() const override
+ {
+ LOCK(::cs_main);
+ const CBlockIndex* tip = Assert(m_node.chainman)->ActiveChain().Tip();
+ return VersionBitsState(tip, Params().GetConsensus(), Consensus::DEPLOYMENT_TAPROOT, versionbitscache) == ThresholdState::ACTIVE;
+ }
NodeContext& m_node;
};
} // namespace
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index a1e7a71e2c..f21b390915 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -40,7 +40,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
{ // cs_main scope
assert(node.chainman);
LOCK(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(node.chainman->ActiveChainstate()));
// If the transaction is already confirmed in the chain, don't do anything
// and return early.
CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
diff --git a/src/policy/packages.cpp b/src/policy/packages.cpp
new file mode 100644
index 0000000000..cfd0539965
--- /dev/null
+++ b/src/policy/packages.cpp
@@ -0,0 +1,62 @@
+// Copyright (c) 2021 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 <consensus/validation.h>
+#include <policy/packages.h>
+#include <primitives/transaction.h>
+#include <uint256.h>
+#include <util/hasher.h>
+
+#include <numeric>
+#include <unordered_set>
+
+bool CheckPackage(const Package& txns, PackageValidationState& state)
+{
+ const unsigned int package_count = txns.size();
+
+ if (package_count > MAX_PACKAGE_COUNT) {
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
+ }
+
+ const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
+ [](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
+ // If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
+ if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
+ }
+
+ // Require the package to be sorted in order of dependency, i.e. parents appear before children.
+ // An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
+ // fail on something less ambiguous (missing-inputs could also be an orphan or trying to
+ // spend nonexistent coins).
+ std::unordered_set<uint256, SaltedTxidHasher> later_txids;
+ std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
+ [](const auto& tx) { return tx->GetHash(); });
+ for (const auto& tx : txns) {
+ for (const auto& input : tx->vin) {
+ if (later_txids.find(input.prevout.hash) != later_txids.end()) {
+ // The parent is a subsequent transaction in the package.
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
+ }
+ }
+ later_txids.erase(tx->GetHash());
+ }
+
+ // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
+ std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
+ for (const auto& tx : txns) {
+ for (const auto& input : tx->vin) {
+ if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
+ // This input is also present in another tx in the package.
+ return state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
+ }
+ }
+ // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
+ // catch duplicate inputs within a single tx. This is a more severe, consensus error,
+ // and we want to report that from CheckTransaction instead.
+ std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
+ [](const auto& input) { return input.prevout; });
+ }
+ return true;
+}
diff --git a/src/policy/packages.h b/src/policy/packages.h
index 4b1463dcb3..6b7ac3e450 100644
--- a/src/policy/packages.h
+++ b/src/policy/packages.h
@@ -6,6 +6,7 @@
#define BITCOIN_POLICY_PACKAGES_H
#include <consensus/validation.h>
+#include <policy/policy.h>
#include <primitives/transaction.h>
#include <vector>
@@ -14,6 +15,7 @@
static constexpr uint32_t MAX_PACKAGE_COUNT{25};
/** Default maximum total virtual size of transactions in a package in KvB. */
static constexpr uint32_t MAX_PACKAGE_SIZE{101};
+static_assert(MAX_PACKAGE_SIZE * WITNESS_SCALE_FACTOR * 1000 >= MAX_STANDARD_TX_WEIGHT);
/** A "reason" why a package was invalid. It may be that one or more of the included
* transactions is invalid or the package itself violates our rules.
@@ -31,4 +33,12 @@ using Package = std::vector<CTransactionRef>;
class PackageValidationState : public ValidationState<PackageValidationResult> {};
+/** Context-free package policy checks:
+ * 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT.
+ * 2. The total virtual size cannot exceed MAX_PACKAGE_SIZE.
+ * 3. If any dependencies exist between transactions, parents must appear before children.
+ * 4. Transactions cannot conflict, i.e., spend the same inputs.
+ */
+bool CheckPackage(const Package& txns, PackageValidationState& state);
+
#endif // BITCOIN_POLICY_PACKAGES_H
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 334acb454e..51cc826b00 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -180,6 +180,12 @@ XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes)
std::copy(bytes.begin(), bytes.end(), m_keydata.begin());
}
+bool XOnlyPubKey::IsFullyValid() const
+{
+ secp256k1_xonly_pubkey pubkey;
+ return secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.data());
+}
+
bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const
{
assert(sigbytes.size() == 64);
@@ -188,13 +194,45 @@ bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> si
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
}
-bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const
+static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
+
+uint256 XOnlyPubKey::ComputeTapTweakHash(const uint256* merkle_root) const
+{
+ if (merkle_root == nullptr) {
+ // We have no scripts. The actual tweak does not matter, but follow BIP341 here to
+ // allow for reproducible tweaking.
+ return (CHashWriter(HASHER_TAPTWEAK) << m_keydata).GetSHA256();
+ } else {
+ return (CHashWriter(HASHER_TAPTWEAK) << m_keydata << *merkle_root).GetSHA256();
+ }
+}
+
+bool XOnlyPubKey::CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const
+{
+ secp256k1_xonly_pubkey internal_key;
+ if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &internal_key, internal.data())) return false;
+ uint256 tweak = internal.ComputeTapTweakHash(&merkle_root);
+ return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &internal_key, tweak.begin());
+}
+
+std::optional<std::pair<XOnlyPubKey, bool>> XOnlyPubKey::CreateTapTweak(const uint256* merkle_root) const
{
secp256k1_xonly_pubkey base_point;
- if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, base.data())) return false;
- return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point, hash.begin());
+ if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, data())) return std::nullopt;
+ secp256k1_pubkey out;
+ uint256 tweak = ComputeTapTweakHash(merkle_root);
+ if (!secp256k1_xonly_pubkey_tweak_add(secp256k1_context_verify, &out, &base_point, tweak.data())) return std::nullopt;
+ int parity = -1;
+ std::pair<XOnlyPubKey, bool> ret;
+ secp256k1_xonly_pubkey out_xonly;
+ if (!secp256k1_xonly_pubkey_from_pubkey(secp256k1_context_verify, &out_xonly, &parity, &out)) return std::nullopt;
+ secp256k1_xonly_pubkey_serialize(secp256k1_context_verify, ret.first.begin(), &out_xonly);
+ assert(parity == 0 || parity == 1);
+ ret.second = parity;
+ return ret;
}
+
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
if (!IsValid())
return false;
diff --git a/src/pubkey.h b/src/pubkey.h
index 1af1187006..152a48dd18 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -13,6 +13,7 @@
#include <uint256.h>
#include <cstring>
+#include <optional>
#include <vector>
const unsigned int BIP32_EXTKEY_SIZE = 74;
@@ -222,19 +223,56 @@ private:
uint256 m_keydata;
public:
+ /** Construct an empty x-only pubkey. */
+ XOnlyPubKey() = default;
+
+ XOnlyPubKey(const XOnlyPubKey&) = default;
+ XOnlyPubKey& operator=(const XOnlyPubKey&) = default;
+
+ /** Determine if this pubkey is fully valid. This is true for approximately 50% of all
+ * possible 32-byte arrays. If false, VerifySchnorr and CreatePayToContract will always
+ * fail. */
+ bool IsFullyValid() const;
+
/** Construct an x-only pubkey from exactly 32 bytes. */
explicit XOnlyPubKey(Span<const unsigned char> bytes);
+ /** Construct an x-only pubkey from a normal pubkey. */
+ explicit XOnlyPubKey(const CPubKey& pubkey) : XOnlyPubKey(Span<const unsigned char>(pubkey.begin() + 1, pubkey.begin() + 33)) {}
+
/** Verify a Schnorr signature against this public key.
*
* sigbytes must be exactly 64 bytes.
*/
bool VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const;
- bool CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const;
+
+ /** Compute the Taproot tweak as specified in BIP341, with *this as internal
+ * key:
+ * - if merkle_root == nullptr: H_TapTweak(xonly_pubkey)
+ * - otherwise: H_TapTweak(xonly_pubkey || *merkle_root)
+ *
+ * Note that the behavior of this function with merkle_root != nullptr is
+ * consensus critical.
+ */
+ uint256 ComputeTapTweakHash(const uint256* merkle_root) const;
+
+ /** Verify that this is a Taproot tweaked output point, against a specified internal key,
+ * Merkle root, and parity. */
+ bool CheckTapTweak(const XOnlyPubKey& internal, const uint256& merkle_root, bool parity) const;
+
+ /** Construct a Taproot tweaked output point with this point as internal key. */
+ std::optional<std::pair<XOnlyPubKey, bool>> CreateTapTweak(const uint256* merkle_root) const;
const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }
const unsigned char* data() const { return m_keydata.begin(); }
- size_t size() const { return m_keydata.size(); }
+ static constexpr size_t size() { return decltype(m_keydata)::size(); }
+ const unsigned char* begin() const { return m_keydata.begin(); }
+ const unsigned char* end() const { return m_keydata.end(); }
+ unsigned char* begin() { return m_keydata.begin(); }
+ unsigned char* end() { return m_keydata.end(); }
+ bool operator==(const XOnlyPubKey& other) const { return m_keydata == other.m_keydata; }
+ bool operator!=(const XOnlyPubKey& other) const { return m_keydata != other.m_keydata; }
+ bool operator<(const XOnlyPubKey& other) const { return m_keydata < other.m_keydata; }
};
struct CExtPubKey {
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 7024fc7654..c31f0aceea 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -114,12 +114,12 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
// Build context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
- contextMenu->addAction(tr("Copy Label"), this, &AddressBookPage::onCopyLabelAction);
- contextMenu->addAction(tr("Edit"), this, &AddressBookPage::onEditAction);
+ contextMenu->addAction(tr("&Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
+ contextMenu->addAction(tr("Copy &Label"), this, &AddressBookPage::onCopyLabelAction);
+ contextMenu->addAction(tr("&Edit"), this, &AddressBookPage::onEditAction);
if (tab == SendingTab) {
- contextMenu->addAction(tr("Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
+ contextMenu->addAction(tr("&Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
}
connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 69948402d0..442c813a5a 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -60,6 +60,7 @@
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_WINDOWS)
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
+Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin);
#elif defined(QT_QPA_PLATFORM_COCOA)
Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
Q_IMPORT_PLUGIN(QMacStylePlugin);
@@ -489,7 +490,8 @@ int GuiMain(int argc, char* argv[])
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
- SetupServerArgs(node_context);
+ node_context.args = &gArgs;
+ SetupServerArgs(gArgs);
SetupUIArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index e5dde88bd1..3d632ec702 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -105,6 +105,11 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
{
/** Create wallet frame and make it the central widget */
walletFrame = new WalletFrame(_platformStyle, this);
+ connect(walletFrame, &WalletFrame::createWalletButtonClicked, [this] {
+ auto activity = new CreateWalletActivity(getWalletController(), this);
+ connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
+ activity->create();
+ });
setCentralWidget(walletFrame);
} else
#endif // ENABLE_WALLET
@@ -206,11 +211,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay);
connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
-#ifdef ENABLE_WALLET
- if(enableWallet) {
- connect(walletFrame, &WalletFrame::requestedSyncWarningInfo, this, &BitcoinGUI::showModalOverlay);
- }
-#endif
#ifdef Q_OS_MAC
m_app_nap_inhibitor = new CAppNapInhibitor;
@@ -671,7 +671,10 @@ WalletController* BitcoinGUI::getWalletController()
void BitcoinGUI::addWallet(WalletModel* walletModel)
{
if (!walletFrame) return;
- if (!walletFrame->addWallet(walletModel)) return;
+
+ WalletView* wallet_view = new WalletView(platformStyle, walletFrame);
+ if (!walletFrame->addWallet(walletModel, wallet_view)) return;
+
rpcConsole->addWallet(walletModel);
if (m_wallet_selector->count() == 0) {
setWalletActionsEnabled(true);
@@ -681,6 +684,18 @@ void BitcoinGUI::addWallet(WalletModel* walletModel)
}
const QString display_name = walletModel->getDisplayName();
m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
+
+ connect(wallet_view, &WalletView::outOfSyncWarningClicked, this, &BitcoinGUI::showModalOverlay);
+ connect(wallet_view, &WalletView::transactionClicked, this, &BitcoinGUI::gotoHistoryPage);
+ connect(wallet_view, &WalletView::coinsSent, this, &BitcoinGUI::gotoHistoryPage);
+ connect(wallet_view, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
+ this->message(title, message, style);
+ });
+ connect(wallet_view, &WalletView::encryptionStatusChanged, this, &BitcoinGUI::updateWalletStatus);
+ connect(wallet_view, &WalletView::incomingTransaction, this, &BitcoinGUI::incomingTransaction);
+ connect(wallet_view, &WalletView::hdEnabledStatusChanged, this, &BitcoinGUI::updateWalletStatus);
+ connect(this, &BitcoinGUI::setPrivacy, wallet_view, &WalletView::setPrivacy);
+ wallet_view->setPrivacy(isPrivacyModeActivated());
}
void BitcoinGUI::removeWallet(WalletModel* walletModel)
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 8ae0648141..2360fa9b37 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -52,13 +52,13 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m
// context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy address"), this, &CoinControlDialog::copyAddress);
- contextMenu->addAction(tr("Copy label"), this, &CoinControlDialog::copyLabel);
- contextMenu->addAction(tr("Copy amount"), this, &CoinControlDialog::copyAmount);
- copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction ID"), this, &CoinControlDialog::copyTransactionHash);
+ contextMenu->addAction(tr("&Copy address"), this, &CoinControlDialog::copyAddress);
+ contextMenu->addAction(tr("Copy &label"), this, &CoinControlDialog::copyLabel);
+ contextMenu->addAction(tr("Copy &amount"), this, &CoinControlDialog::copyAmount);
+ copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction &ID"), this, &CoinControlDialog::copyTransactionHash);
contextMenu->addSeparator();
- lockAction = contextMenu->addAction(tr("Lock unspent"), this, &CoinControlDialog::lockCoin);
- unlockAction = contextMenu->addAction(tr("Unlock unspent"), this, &CoinControlDialog::unlockCoin);
+ lockAction = contextMenu->addAction(tr("L&ock unspent"), this, &CoinControlDialog::lockCoin);
+ unlockAction = contextMenu->addAction(tr("&Unlock unspent"), this, &CoinControlDialog::unlockCoin);
connect(ui->treeWidget, &QWidget::customContextMenuRequested, this, &CoinControlDialog::showMenu);
// clipboard actions
diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp
index 113bd30a0c..e593697b46 100644
--- a/src/qt/createwalletdialog.cpp
+++ b/src/qt/createwalletdialog.cpp
@@ -6,6 +6,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <external_signer.h>
#include <qt/createwalletdialog.h>
#include <qt/forms/ui_createwalletdialog.h>
@@ -27,14 +28,39 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
});
connect(ui->encrypt_wallet_checkbox, &QCheckBox::toggled, [this](bool checked) {
- // Disable the disable_privkeys_checkbox when isEncryptWalletChecked is
+ // Disable the disable_privkeys_checkbox and external_signer_checkbox when isEncryptWalletChecked is
// set to true, enable it when isEncryptWalletChecked is false.
ui->disable_privkeys_checkbox->setEnabled(!checked);
+ ui->external_signer_checkbox->setEnabled(!checked);
// When the disable_privkeys_checkbox is disabled, uncheck it.
if (!ui->disable_privkeys_checkbox->isEnabled()) {
ui->disable_privkeys_checkbox->setChecked(false);
}
+
+ // When the external_signer_checkbox box is disabled, uncheck it.
+ if (!ui->external_signer_checkbox->isEnabled()) {
+ ui->external_signer_checkbox->setChecked(false);
+ }
+
+ });
+
+ connect(ui->external_signer_checkbox, &QCheckBox::toggled, [this](bool checked) {
+ ui->encrypt_wallet_checkbox->setEnabled(!checked);
+ ui->blank_wallet_checkbox->setEnabled(!checked);
+ ui->disable_privkeys_checkbox->setEnabled(!checked);
+ ui->descriptor_checkbox->setEnabled(!checked);
+
+ // The external signer checkbox is only enabled when a device is detected.
+ // In that case it is checked by default. Toggling it restores the other
+ // options to their default.
+ ui->descriptor_checkbox->setChecked(checked);
+ ui->encrypt_wallet_checkbox->setChecked(false);
+ ui->disable_privkeys_checkbox->setChecked(checked);
+ // The blank check box is ambiguous. This flag is always true for a
+ // watch-only wallet, even though we immedidately fetch keys from the
+ // external signer.
+ ui->blank_wallet_checkbox->setChecked(checked);
});
connect(ui->disable_privkeys_checkbox, &QCheckBox::toggled, [this](bool checked) {
@@ -63,11 +89,22 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
ui->descriptor_checkbox->setToolTip(tr("Compiled without sqlite support (required for descriptor wallets)"));
ui->descriptor_checkbox->setEnabled(false);
ui->descriptor_checkbox->setChecked(false);
+ ui->external_signer_checkbox->setEnabled(false);
+ ui->external_signer_checkbox->setChecked(false);
#endif
+
#ifndef USE_BDB
ui->descriptor_checkbox->setEnabled(false);
ui->descriptor_checkbox->setChecked(true);
#endif
+
+#ifndef ENABLE_EXTERNAL_SIGNER
+ //: "External signing" means using devices such as hardware wallets.
+ ui->external_signer_checkbox->setToolTip(tr("Compiled without external signing support (required for external signing)"));
+ ui->external_signer_checkbox->setEnabled(false);
+ ui->external_signer_checkbox->setChecked(false);
+#endif
+
}
CreateWalletDialog::~CreateWalletDialog()
@@ -75,6 +112,28 @@ CreateWalletDialog::~CreateWalletDialog()
delete ui;
}
+#ifdef ENABLE_EXTERNAL_SIGNER
+void CreateWalletDialog::setSigners(std::vector<ExternalSigner>& signers)
+{
+ if (!signers.empty()) {
+ ui->external_signer_checkbox->setEnabled(true);
+ ui->external_signer_checkbox->setChecked(true);
+ ui->encrypt_wallet_checkbox->setEnabled(false);
+ ui->encrypt_wallet_checkbox->setChecked(false);
+ // The order matters, because connect() is called when toggling a checkbox:
+ ui->blank_wallet_checkbox->setEnabled(false);
+ ui->blank_wallet_checkbox->setChecked(false);
+ ui->disable_privkeys_checkbox->setEnabled(false);
+ ui->disable_privkeys_checkbox->setChecked(true);
+ const std::string label = signers[0].m_name;
+ ui->wallet_name_line_edit->setText(QString::fromStdString(label));
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+ } else {
+ ui->external_signer_checkbox->setEnabled(false);
+ }
+}
+#endif
+
QString CreateWalletDialog::walletName() const
{
return ui->wallet_name_line_edit->text();
@@ -99,3 +158,8 @@ bool CreateWalletDialog::isDescriptorWalletChecked() const
{
return ui->descriptor_checkbox->isChecked();
}
+
+bool CreateWalletDialog::isExternalSignerChecked() const
+{
+ return ui->external_signer_checkbox->isChecked();
+}
diff --git a/src/qt/createwalletdialog.h b/src/qt/createwalletdialog.h
index 20cce937c8..585b1461f7 100644
--- a/src/qt/createwalletdialog.h
+++ b/src/qt/createwalletdialog.h
@@ -9,6 +9,10 @@
class WalletModel;
+#ifdef ENABLE_EXTERNAL_SIGNER
+class ExternalSigner;
+#endif
+
namespace Ui {
class CreateWalletDialog;
}
@@ -23,11 +27,16 @@ public:
explicit CreateWalletDialog(QWidget* parent);
virtual ~CreateWalletDialog();
+#ifdef ENABLE_EXTERNAL_SIGNER
+ void setSigners(std::vector<ExternalSigner>& signers);
+#endif
+
QString walletName() const;
bool isEncryptWalletChecked() const;
bool isDisablePrivateKeysChecked() const;
bool isMakeBlankWalletChecked() const;
bool isDescriptorWalletChecked() const;
+ bool isExternalSignerChecked() const;
private:
Ui::CreateWalletDialog *ui;
diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui
index 881869a46c..b11fb026b0 100644
--- a/src/qt/forms/createwalletdialog.ui
+++ b/src/qt/forms/createwalletdialog.ui
@@ -109,6 +109,16 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QCheckBox" name="external_signer_checkbox">
+ <property name="toolTip">
+ <string>Use an external signing device such as a hardware wallet. Configure the external signer script in wallet preferences first.</string>
+ </property>
+ <property name="text">
+ <string>External signer</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -143,6 +153,7 @@
<tabstop>disable_privkeys_checkbox</tabstop>
<tabstop>blank_wallet_checkbox</tabstop>
<tabstop>descriptor_checkbox</tabstop>
+ <tabstop>external_signer_checkbox</tabstop>
</tabstops>
<resources/>
<connections>
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 7a25ba907e..15e0d3fad9 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -470,13 +470,7 @@
</spacer>
</item>
<item>
- <widget class="QPushButton" name="fontSmallerButton">
- <property name="maximumSize">
- <size>
- <width>24</width>
- <height>24</height>
- </size>
- </property>
+ <widget class="QToolButton" name="fontSmallerButton">
<property name="toolTip">
<string>Decrease font size</string>
</property>
@@ -489,26 +483,14 @@
</property>
<property name="iconSize">
<size>
- <width>24</width>
- <height>16</height>
+ <width>22</width>
+ <height>22</height>
</size>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="fontBiggerButton">
- <property name="maximumSize">
- <size>
- <width>24</width>
- <height>24</height>
- </size>
- </property>
+ <widget class="QToolButton" name="fontBiggerButton">
<property name="toolTip">
<string>Increase font size</string>
</property>
@@ -521,26 +503,14 @@
</property>
<property name="iconSize">
<size>
- <width>24</width>
- <height>16</height>
+ <width>22</width>
+ <height>22</height>
</size>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="clearButton">
- <property name="maximumSize">
- <size>
- <width>24</width>
- <height>24</height>
- </size>
- </property>
+ <widget class="QToolButton" name="clearButton">
<property name="toolTip">
<string>Clear console</string>
</property>
@@ -554,15 +524,15 @@
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
+ <property name="iconSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
<property name="shortcut">
<string notr="true">Ctrl+L</string>
</property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>true</bool>
- </property>
</widget>
</item>
</layout>
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index f199e8c1a1..bd72328c02 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -230,6 +230,36 @@
</widget>
</item>
<item>
+ <widget class="QGroupBox" name="groupBoxHww">
+ <property name="title">
+ <string>External Signer (e.g. hardware wallet)</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayoutHww">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutHww">
+ <item>
+ <widget class="QLabel" name="externalSignerPathLabel">
+ <property name="text">
+ <string>&amp;External signer script path</string>
+ </property>
+ <property name="buddy">
+ <cstring>externalSignerPath</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="externalSignerPath">
+ <property name="toolTip">
+ <string>Full path to a Bitcoin Core compatible script (e.g. C:\Downloads\hwi.exe or /Users/you/Downloads/hwi.py). Beware: malware can steal your coins!</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer_Wallet">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui
index 7d95a8bc90..70a7cf71de 100644
--- a/src/qt/forms/receiverequestdialog.ui
+++ b/src/qt/forms/receiverequestdialog.ui
@@ -255,6 +255,19 @@
</widget>
</item>
<item>
+ <widget class="QPushButton" name="btnVerify">
+ <property name="text">
+ <string>&amp;Verify</string>
+ </property>
+ <property name="toolTip">
+ <string>Verify this address on e.g. a hardware wallet screen</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="btnSaveAs">
<property name="text">
<string>&amp;Save Image…</string>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 8a32994e3f..6ad8db4348 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -199,6 +199,7 @@ void OptionsDialog::setModel(OptionsModel *_model)
connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::togglePruneWarning);
connect(ui->pruneSize, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
connect(ui->databaseCache, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
+ connect(ui->externalSignerPath, &QLineEdit::textChanged, [this]{ showRestartWarning(); });
connect(ui->threadsScriptVerif, qOverload<int>(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning);
/* Wallet */
connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
@@ -233,6 +234,7 @@ void OptionsDialog::setMapper()
/* Wallet */
mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange);
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
+ mapper->addMapping(ui->externalSignerPath, OptionsModel::ExternalSignerPath);
/* Network */
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index abdf9e9ae6..24a4e9ee96 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -117,6 +117,13 @@ void OptionsModel::Init(bool resetSettings)
settings.setValue("bSpendZeroConfChange", true);
if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
addOverriddenOption("-spendzeroconfchange");
+
+ if (!settings.contains("external_signer_path"))
+ settings.setValue("external_signer_path", "");
+
+ if (!gArgs.SoftSetArg("-signer", settings.value("external_signer_path").toString().toStdString())) {
+ addOverriddenOption("-signer");
+ }
#endif
// Network
@@ -326,6 +333,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
#ifdef ENABLE_WALLET
case SpendZeroConfChange:
return settings.value("bSpendZeroConfChange");
+ case ExternalSignerPath:
+ return settings.value("external_signer_path");
#endif
case DisplayUnit:
return nDisplayUnit;
@@ -445,6 +454,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
setRestartRequired(true);
}
break;
+ case ExternalSignerPath:
+ if (settings.value("external_signer_path") != value.toString()) {
+ settings.setValue("external_signer_path", value.toString());
+ setRestartRequired(true);
+ }
+ break;
#endif
case DisplayUnit:
setDisplayUnit(value);
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 4d012a9b8f..535843e8ba 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -65,6 +65,7 @@ public:
Prune, // bool
PruneSize, // int
DatabaseCache, // int
+ ExternalSignerPath, // QString
SpendZeroConfChange, // bool
Listen, // bool
OptionIDRowCount,
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 27783bdf87..26e3dd0d60 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -166,8 +166,8 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
// start with displaying the "out of sync" warnings
showOutOfSyncWarning(true);
- connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
- connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks);
+ connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::outOfSyncWarningClicked);
+ connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::outOfSyncWarningClicked);
}
void OverviewPage::handleTransactionClicked(const QModelIndex &index)
@@ -176,11 +176,6 @@ void OverviewPage::handleTransactionClicked(const QModelIndex &index)
Q_EMIT transactionClicked(filter->mapToSource(index));
}
-void OverviewPage::handleOutOfSyncWarningClicks()
-{
- Q_EMIT outOfSyncWarningClicked();
-}
-
void OverviewPage::setPrivacy(bool privacy)
{
m_privacy = privacy;
diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h
index 755a107a00..5270741c0d 100644
--- a/src/qt/overviewpage.h
+++ b/src/qt/overviewpage.h
@@ -65,7 +65,6 @@ private Q_SLOTS:
void handleTransactionClicked(const QModelIndex &index);
void updateAlerts(const QString &warnings);
void updateWatchOnlyLabels(bool showWatchOnly);
- void handleOutOfSyncWarningClicks();
void setMonospacedFont(bool use_embedded_font);
};
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 11441481bb..b324693692 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -14,52 +14,11 @@
#include <QList>
#include <QTimer>
-// private implementation
-class PeerTablePriv
-{
-public:
- /** Local cache of peer information */
- QList<CNodeCombinedStats> cachedNodeStats;
-
- /** Pull a full list of peers from vNodes into our cache */
- void refreshPeers(interfaces::Node& node)
- {
- cachedNodeStats.clear();
-
- interfaces::Node::NodesStats nodes_stats;
- node.getNodesStats(nodes_stats);
- cachedNodeStats.reserve(nodes_stats.size());
- for (const auto& node_stats : nodes_stats)
- {
- CNodeCombinedStats stats;
- stats.nodeStats = std::get<0>(node_stats);
- stats.fNodeStateStatsAvailable = std::get<1>(node_stats);
- stats.nodeStateStats = std::get<2>(node_stats);
- cachedNodeStats.append(stats);
- }
- }
-
- int size() const
- {
- return cachedNodeStats.size();
- }
-
- CNodeCombinedStats *index(int idx)
- {
- if (idx >= 0 && idx < cachedNodeStats.size())
- return &cachedNodeStats[idx];
-
- return nullptr;
- }
-};
-
PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) :
QAbstractTableModel(parent),
m_node(node),
timer(nullptr)
{
- priv.reset(new PeerTablePriv());
-
// set up timer for auto refresh
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh);
@@ -84,15 +43,15 @@ void PeerTableModel::stopAutoRefresh()
timer->stop();
}
-int PeerTableModel::rowCount(const QModelIndex &parent) const
+int PeerTableModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0;
}
- return priv->size();
+ return m_peers_data.size();
}
-int PeerTableModel::columnCount(const QModelIndex &parent) const
+int PeerTableModel::columnCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0;
@@ -100,7 +59,7 @@ int PeerTableModel::columnCount(const QModelIndex &parent) const
return columns.length();
}
-QVariant PeerTableModel::data(const QModelIndex &index, int role) const
+QVariant PeerTableModel::data(const QModelIndex& index, int role) const
{
if(!index.isValid())
return QVariant();
@@ -132,6 +91,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
} else if (role == Qt::TextAlignmentRole) {
switch (column) {
case NetNodeId:
+ return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case Address:
return {};
case ConnectionType:
@@ -172,19 +132,52 @@ Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const
return retval;
}
-QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const
+QModelIndex PeerTableModel::index(int row, int column, const QModelIndex& parent) const
{
Q_UNUSED(parent);
- CNodeCombinedStats *data = priv->index(row);
- if (data)
- return createIndex(row, column, data);
+ if (0 <= row && row < rowCount() && 0 <= column && column < columnCount()) {
+ return createIndex(row, column, const_cast<CNodeCombinedStats*>(&m_peers_data[row]));
+ }
+
return QModelIndex();
}
void PeerTableModel::refresh()
{
- Q_EMIT layoutAboutToBeChanged();
- priv->refreshPeers(m_node);
- Q_EMIT layoutChanged();
+ interfaces::Node::NodesStats nodes_stats;
+ m_node.getNodesStats(nodes_stats);
+ decltype(m_peers_data) new_peers_data;
+ new_peers_data.reserve(nodes_stats.size());
+ for (const auto& node_stats : nodes_stats) {
+ const CNodeCombinedStats stats{std::get<0>(node_stats), std::get<2>(node_stats), std::get<1>(node_stats)};
+ new_peers_data.append(stats);
+ }
+
+ // Handle peer addition or removal as suggested in Qt Docs. See:
+ // - https://doc.qt.io/qt-5/model-view-programming.html#inserting-and-removing-rows
+ // - https://doc.qt.io/qt-5/model-view-programming.html#resizable-models
+ // We take advantage of the fact that the std::vector returned
+ // by interfaces::Node::getNodesStats is sorted by nodeid.
+ for (int i = 0; i < m_peers_data.size();) {
+ if (i < new_peers_data.size() && m_peers_data.at(i).nodeStats.nodeid == new_peers_data.at(i).nodeStats.nodeid) {
+ ++i;
+ continue;
+ }
+ // A peer has been removed from the table.
+ beginRemoveRows(QModelIndex(), i, i);
+ m_peers_data.erase(m_peers_data.begin() + i);
+ endRemoveRows();
+ }
+
+ if (m_peers_data.size() < new_peers_data.size()) {
+ // Some peers have been added to the end of the table.
+ beginInsertRows(QModelIndex(), m_peers_data.size(), new_peers_data.size() - 1);
+ m_peers_data.swap(new_peers_data);
+ endInsertRows();
+ } else {
+ m_peers_data.swap(new_peers_data);
+ }
+
+ Q_EMIT changed();
}
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index 3d195342f1..0ff1b5dba7 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -8,10 +8,11 @@
#include <net_processing.h> // For CNodeStateStats
#include <net.h>
-#include <memory>
-
#include <QAbstractTableModel>
+#include <QList>
+#include <QModelIndex>
#include <QStringList>
+#include <QVariant>
class PeerTablePriv;
@@ -61,18 +62,23 @@ public:
/** @name Methods overridden from QAbstractTableModel
@{*/
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &index, int role) const override;
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
- QModelIndex index(int row, int column, const QModelIndex &parent) const override;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
/*@}*/
public Q_SLOTS:
void refresh();
+Q_SIGNALS:
+ void changed();
+
private:
+ //! Internal peer data structure.
+ QList<CNodeCombinedStats> m_peers_data{};
interfaces::Node& m_node;
const QStringList columns{
/*: Title of Peers Table column which contains a
@@ -99,7 +105,6 @@ private:
/*: Title of Peers Table column which contains the peer's
User Agent string. */
tr("User Agent")};
- std::unique_ptr<PeerTablePriv> priv;
QTimer *timer;
};
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 99318c3bc0..2adfeeaaf0 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -50,7 +50,7 @@ void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
bool complete;
size_t n_could_sign;
FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_could_sign);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, &n_could_sign, m_transaction_data, complete);
if (err != TransactionError::OK) {
showStatus(tr("Failed to load transaction: %1")
.arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
@@ -67,7 +67,7 @@ void PSBTOperationsDialog::signTransaction()
{
bool complete;
size_t n_signed;
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_signed);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
showStatus(tr("Failed to sign transaction: %1")
@@ -226,7 +226,7 @@ void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &psbtx) {
size_t n_signed;
bool complete;
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, m_transaction_data, complete, &n_signed);
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
return 0;
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
index f5200bb5c0..7cdd568644 100644
--- a/src/qt/qrimagewidget.cpp
+++ b/src/qt/qrimagewidget.cpp
@@ -27,8 +27,8 @@ QRImageWidget::QRImageWidget(QWidget *parent):
QLabel(parent), contextMenu(nullptr)
{
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Save Image…"), this, &QRImageWidget::saveImage);
- contextMenu->addAction(tr("Copy Image"), this, &QRImageWidget::copyImage);
+ contextMenu->addAction(tr("&Save Image…"), this, &QRImageWidget::saveImage);
+ contextMenu->addAction(tr("&Copy Image"), this, &QRImageWidget::copyImage);
}
bool QRImageWidget::setQR(const QString& data, const QString& text)
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 3f4d7f85e6..d47ee95826 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -44,11 +44,11 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
// context menu
contextMenu = new QMenu(this);
- contextMenu->addAction(tr("Copy URI"), this, &ReceiveCoinsDialog::copyURI);
- contextMenu->addAction(tr("Copy address"), this, &ReceiveCoinsDialog::copyAddress);
- copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &ReceiveCoinsDialog::copyLabel);
- copyMessageAction = contextMenu->addAction(tr("Copy message"), this, &ReceiveCoinsDialog::copyMessage);
- copyAmountAction = contextMenu->addAction(tr("Copy amount"), this, &ReceiveCoinsDialog::copyAmount);
+ contextMenu->addAction(tr("Copy &URI"), this, &ReceiveCoinsDialog::copyURI);
+ contextMenu->addAction(tr("&Copy address"), this, &ReceiveCoinsDialog::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy &label"), this, &ReceiveCoinsDialog::copyLabel);
+ copyMessageAction = contextMenu->addAction(tr("Copy &message"), this, &ReceiveCoinsDialog::copyMessage);
+ copyAmountAction = contextMenu->addAction(tr("Copy &amount"), this, &ReceiveCoinsDialog::copyAmount);
connect(ui->recentRequestsView, &QWidget::customContextMenuRequested, this, &ReceiveCoinsDialog::showMenu);
connect(ui->clearButton, &QPushButton::clicked, this, &ReceiveCoinsDialog::clear);
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index 78ae5c07da..abe7de8f89 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -89,6 +89,12 @@ void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info)
ui->wallet_tag->hide();
ui->wallet_content->hide();
}
+
+ ui->btnVerify->setVisible(this->model->wallet().hasExternalSigner());
+
+ connect(ui->btnVerify, &QPushButton::clicked, [this] {
+ model->displayAddress(info.address.toStdString());
+ });
}
void ReceiveRequestDialog::updateDisplayUnit()
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 1ecc2f67ef..ec3d970a7f 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -10,7 +10,10 @@
#include <qt/walletmodel.h>
#include <clientversion.h>
+#include <interfaces/wallet.h>
+#include <key_io.h>
#include <streams.h>
+#include <util/string.h>
#include <utility>
@@ -21,10 +24,9 @@ RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) :
QAbstractTableModel(parent), walletModel(parent)
{
// Load entries from wallet
- std::vector<std::string> vReceiveRequests;
- parent->loadReceiveRequests(vReceiveRequests);
- for (const std::string& request : vReceiveRequests)
+ for (const std::string& request : parent->wallet().getAddressReceiveRequests()) {
addNewRequest(request);
+ }
/* These columns must match the indices in the ColumnIndex enumeration */
columns << tr("Date") << tr("Label") << tr("Message") << getAmountTitle();
@@ -150,7 +152,7 @@ bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex
for (int i = 0; i < count; ++i)
{
const RecentRequestEntry* rec = &list[row+i];
- if (!walletModel->saveReceiveRequest(rec->recipient.address.toStdString(), rec->id, ""))
+ if (!walletModel->wallet().setAddressReceiveRequest(DecodeDestination(rec->recipient.address.toStdString()), ToString(rec->id), ""))
return false;
}
@@ -179,7 +181,7 @@ void RecentRequestsTableModel::addNewRequest(const SendCoinsRecipient &recipient
CDataStream ss(SER_DISK, CLIENT_VERSION);
ss << newEntry;
- if (!walletModel->saveReceiveRequest(recipient.address.toStdString(), newEntry.id, ss.str()))
+ if (!walletModel->wallet().setAddressReceiveRequest(DecodeDestination(recipient.address.toStdString()), ToString(newEntry.id), ss.str()))
return;
addNewRequest(newEntry);
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 548b8cb34d..ff4bfb16f6 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -34,9 +34,12 @@
#include <wallet/wallet.h>
#endif
+#include <QAbstractButton>
#include <QDateTime>
#include <QFont>
#include <QKeyEvent>
+#include <QLatin1String>
+#include <QLocale>
#include <QMenu>
#include <QMessageBox>
#include <QScreen>
@@ -44,9 +47,10 @@
#include <QSettings>
#include <QString>
#include <QStringList>
+#include <QStyledItemDelegate>
#include <QTime>
#include <QTimer>
-
+#include <QVariant>
const int CONSOLE_HISTORY = 50;
const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
@@ -128,6 +132,20 @@ public:
}
};
+class PeerIdViewDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+ explicit PeerIdViewDelegate(QObject* parent = nullptr)
+ : QStyledItemDelegate(parent) {}
+
+ QString displayText(const QVariant& value, const QLocale& locale) const override
+ {
+ // Additional spaces should visually separate right-aligned content
+ // from the next column to the right.
+ return value.toString() + QLatin1String(" ");
+ }
+};
#include <qt/rpcconsole.moc>
@@ -469,6 +487,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
ui->splitter->restoreState(settings.value("RPCConsoleWidgetPeersTabSplitterSizes").toByteArray());
}
+ m_peer_widget_header_state = settings.value("PeersTabPeerHeaderState").toByteArray();
+ m_banlist_widget_header_state = settings.value("PeersTabBanlistHeaderState").toByteArray();
+
constexpr QChar nonbreaking_hyphen(8209);
const std::vector<QString> CONNECTION_TYPE_DOC{
tr("Inbound: initiated by peer"),
@@ -513,9 +534,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
ui->lineEdit->setMaxLength(16 * 1024 * 1024);
ui->messagesWidget->installEventFilter(this);
- connect(ui->clearButton, &QPushButton::clicked, [this] { clear(); });
- connect(ui->fontBiggerButton, &QPushButton::clicked, this, &RPCConsole::fontBigger);
- connect(ui->fontSmallerButton, &QPushButton::clicked, this, &RPCConsole::fontSmaller);
+ connect(ui->clearButton, &QAbstractButton::clicked, [this] { clear(); });
+ connect(ui->fontBiggerButton, &QAbstractButton::clicked, this, &RPCConsole::fontBigger);
+ connect(ui->fontSmallerButton, &QAbstractButton::clicked, this, &RPCConsole::fontSmaller);
connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear);
// disable the wallet selector by default
@@ -552,6 +573,9 @@ RPCConsole::~RPCConsole()
settings.setValue("RPCConsoleWidgetPeersTabSplitterSizes", ui->splitter->saveState());
}
+ settings.setValue("PeersTabPeerHeaderState", m_peer_widget_header_state);
+ settings.setValue("PeersTabBanlistHeaderState", m_banlist_widget_header_state);
+
m_node.rpcUnsetTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete ui;
@@ -640,23 +664,27 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
- ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
- ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
- ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
+
+ if (!ui->peerWidget->horizontalHeader()->restoreState(m_peer_widget_header_state)) {
+ ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
+ ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
+ ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
+ }
ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
+ ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this));
// create peer table context menu
peersTableContextMenu = new QMenu(this);
- peersTableContextMenu->addAction(tr("Disconnect"), this, &RPCConsole::disconnectSelectedNode);
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 hour"), [this] { banSelectedNode(60 * 60); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 day"), [this] { banSelectedNode(60 * 60 * 24); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
- peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
+ peersTableContextMenu->addAction(tr("&Disconnect"), this, &RPCConsole::disconnectSelectedNode);
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &hour"), [this] { banSelectedNode(60 * 60); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 d&ay"), [this] { banSelectedNode(60 * 60 * 24); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &week"), [this] { banSelectedNode(60 * 60 * 24 * 7); });
+ peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &year"), [this] { banSelectedNode(60 * 60 * 24 * 365); });
connect(ui->peerWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showPeersTableContextMenu);
// peer table signal handling - update peer details when selecting new node
connect(ui->peerWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RPCConsole::updateDetailWidget);
- connect(model->getPeerTableModel(), &PeerTableModel::layoutChanged, this, &RPCConsole::updateDetailWidget);
+ connect(model->getPeerTableModel(), &PeerTableModel::changed, this, &RPCConsole::updateDetailWidget);
// set up ban table
ui->banlistWidget->setModel(model->getBanTableModel());
@@ -664,13 +692,16 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
- ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
- ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
+
+ if (!ui->banlistWidget->horizontalHeader()->restoreState(m_banlist_widget_header_state)) {
+ ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
+ ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
+ }
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
// create ban table context menu
banTableContextMenu = new QMenu(this);
- banTableContextMenu->addAction(tr("Unban"), this, &RPCConsole::unbanSelectedNode);
+ banTableContextMenu->addAction(tr("&Unban"), this, &RPCConsole::unbanSelectedNode);
connect(ui->banlistWidget, &QTableView::customContextMenuRequested, this, &RPCConsole::showBanTableContextMenu);
// ban table signal handling - clear peer details when clicking a peer in the ban table
@@ -1171,6 +1202,11 @@ void RPCConsole::showEvent(QShowEvent *event)
void RPCConsole::hideEvent(QHideEvent *event)
{
+ // It is too late to call QHeaderView::saveState() in ~RPCConsole(), as all of
+ // the columns of QTableView child widgets will have zero width at that moment.
+ m_peer_widget_header_state = ui->peerWidget->horizontalHeader()->saveState();
+ m_banlist_widget_header_state = ui->banlistWidget->horizontalHeader()->saveState();
+
QWidget::hideEvent(event);
if (!clientModel || !clientModel->getPeerTableModel())
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 75f466642b..2412ae543c 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -10,9 +10,10 @@
#include <net.h>
-#include <QWidget>
+#include <QByteArray>
#include <QCompleter>
#include <QThread>
+#include <QWidget>
class ClientModel;
class PlatformStyle;
@@ -167,6 +168,8 @@ private:
QThread thread;
WalletModel* m_last_wallet_model{nullptr};
bool m_is_executing{false};
+ QByteArray m_peer_widget_header_state;
+ QByteArray m_banlist_widget_header_state;
/** Update UI with latest network info from model. */
void updateNetworkState();
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 160b43324f..6a5ec435cd 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -199,7 +199,16 @@ void SendCoinsDialog::setModel(WalletModel *_model)
// set default rbf checkbox state
ui->optInRBF->setCheckState(Qt::Checked);
- if (model->wallet().privateKeysDisabled()) {
+ if (model->wallet().hasExternalSigner()) {
+ ui->sendButton->setText(tr("Sign on device"));
+ if (gArgs.GetArg("-signer", "") != "") {
+ ui->sendButton->setEnabled(true);
+ ui->sendButton->setToolTip(tr("Connect your hardware wallet first."));
+ } else {
+ ui->sendButton->setEnabled(false);
+ ui->sendButton->setToolTip(tr("Set external signer script path in Options -> Wallet"));
+ }
+ } else if (model->wallet().privateKeysDisabled()) {
ui->sendButton->setText(tr("Cr&eate Unsigned"));
ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
}
@@ -313,14 +322,14 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
formatted.append(recipientElement);
}
- if (model->wallet().privateKeysDisabled()) {
+ if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
question_string.append(tr("Do you want to draft this transaction?"));
} else {
question_string.append(tr("Are you sure you want to send?"));
}
question_string.append("<br /><span style='font-size:10pt;'>");
- if (model->wallet().privateKeysDisabled()) {
+ if (model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner()) {
question_string.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
} else {
question_string.append(tr("Please, review your transaction."));
@@ -386,8 +395,8 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
assert(m_current_transaction);
- const QString confirmation = model->wallet().privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
- const QString confirmButtonText = model->wallet().privateKeysDisabled() ? tr("Create Unsigned") : tr("Send");
+ const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
+ const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and send");
SendConfirmationDialog confirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
confirmationDialog.exec();
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
@@ -403,9 +412,58 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ // Always fill without signing first. This prevents an external signer
+ // from being called prematurely and is not expensive.
+ TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
assert(!complete);
assert(err == TransactionError::OK);
+ if (model->wallet().hasExternalSigner()) {
+ try {
+ err = model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
+ } catch (const std::runtime_error& e) {
+ QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
+ send_failure = true;
+ return;
+ }
+ if (err == TransactionError::EXTERNAL_SIGNER_NOT_FOUND) {
+ QMessageBox::critical(nullptr, tr("External signer not found"), "External signer not found");
+ send_failure = true;
+ return;
+ }
+ if (err == TransactionError::EXTERNAL_SIGNER_FAILED) {
+ QMessageBox::critical(nullptr, tr("External signer failure"), "External signer failure");
+ send_failure = true;
+ return;
+ }
+ if (err != TransactionError::OK) {
+ tfm::format(std::cerr, "Failed to sign PSBT");
+ processSendCoinsReturn(WalletModel::TransactionCreationFailed);
+ send_failure = true;
+ return;
+ }
+ // fillPSBT does not always properly finalize
+ complete = FinalizeAndExtractPSBT(psbtx, mtx);
+ }
+
+ // Broadcast transaction if complete (even with an external signer this
+ // is not always the case, e.g. in a multisig wallet).
+ if (complete) {
+ const CTransactionRef tx = MakeTransactionRef(mtx);
+ m_current_transaction->setWtx(tx);
+ WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
+ // process sendStatus and on error generate message shown to user
+ processSendCoinsReturn(sendStatus);
+
+ if (sendStatus.status == WalletModel::OK) {
+ Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
+ } else {
+ send_failure = true;
+ }
+ return;
+ }
+
+ // Copy PSBT to clipboard and offer to save
+ assert(!complete);
// Serialize the PSBT
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
@@ -447,7 +505,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
break;
default:
assert(false);
- }
+ } // msgBox.exec()
} else {
// now send the prepared transaction
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
@@ -614,7 +672,9 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances)
if(model && model->getOptionsModel())
{
CAmount balance = balances.balance;
- if (model->wallet().privateKeysDisabled()) {
+ if (model->wallet().hasExternalSigner()) {
+ ui->labelBalanceName->setText(tr("External balance:"));
+ } else if (model->wallet().privateKeysDisabled()) {
balance = balances.watch_only_balance;
ui->labelBalanceName->setText(tr("Watch-only balance:"));
}
@@ -698,7 +758,7 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked()
void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
{
// Include watch-only for wallets without private key
- m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
+ m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner();
// Calculate available amount to send.
CAmount amount = model->wallet().getAvailableBalance(*m_coin_control);
@@ -753,7 +813,7 @@ void SendCoinsDialog::updateCoinControlState()
m_coin_control->m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
m_coin_control->m_signal_bip125_rbf = ui->optInRBF->isChecked();
// Include watch-only for wallets without private key
- m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled();
+ m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner();
}
void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state) {
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index cb3dbd2267..9c31cd50df 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -85,11 +85,6 @@ void AppTests::appTests()
// Reset global state to avoid interfering with later tests.
LogInstance().DisconnectTestLogger();
AbortShutdown();
- {
- LOCK(cs_main);
- UnloadBlockIndex(/* mempool */ nullptr, g_chainman);
- g_chainman.Reset();
- }
}
//! Entry point for BitcoinGUI tests.
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 26f568b16a..3e1a0e0fa9 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -146,14 +146,14 @@ void TestGUI(interfaces::Node& node)
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
- wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(105, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
}
{
WalletRescanReserver reserver(*wallet);
reserver.reserve();
CWallet::ScanResult result = wallet->ScanForWalletTransactions(Params().GetConsensus().hashGenesisBlock, 0 /* block height */, {} /* max height */, reserver, true /* fUpdate */);
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
- QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
+ QCOMPARE(result.last_scanned_block, node.context()->chainman->ActiveChain().Tip()->GetBlockHash());
QVERIFY(result.last_failed_block.IsNull());
}
wallet->SetBroadcastTransactions(true);
@@ -224,6 +224,7 @@ void TestGUI(interfaces::Node& node)
int initialRowCount = requestTableModel->rowCount({});
QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton");
requestPaymentButton->click();
+ QString address;
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("ReceiveRequestDialog")) {
ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget);
@@ -232,6 +233,9 @@ void TestGUI(interfaces::Node& node)
QString uri = receiveRequestDialog->QObject::findChild<QLabel*>("uri_content")->text();
QCOMPARE(uri.count("bitcoin:"), 2);
QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("address_tag")->text(), QString("Address:"));
+ QVERIFY(address.isEmpty());
+ address = receiveRequestDialog->QObject::findChild<QLabel*>("address_content")->text();
+ QVERIFY(!address.isEmpty());
QCOMPARE(uri.count("amount=0.00000001"), 2);
QCOMPARE(receiveRequestDialog->QObject::findChild<QLabel*>("amount_tag")->text(), QString("Amount:"));
@@ -258,12 +262,30 @@ void TestGUI(interfaces::Node& node)
int currentRowCount = requestTableModel->rowCount({});
QCOMPARE(currentRowCount, initialRowCount+1);
+ // Check addition to wallet
+ std::vector<std::string> requests = walletModel.wallet().getAddressReceiveRequests();
+ QCOMPARE(requests.size(), size_t{1});
+ RecentRequestEntry entry;
+ CDataStream{MakeUCharSpan(requests[0]), SER_DISK, CLIENT_VERSION} >> entry;
+ QCOMPARE(entry.nVersion, int{1});
+ QCOMPARE(entry.id, int64_t{1});
+ QVERIFY(entry.date.isValid());
+ QCOMPARE(entry.recipient.address, address);
+ QCOMPARE(entry.recipient.label, QString{"TEST_LABEL_1"});
+ QCOMPARE(entry.recipient.amount, CAmount{1});
+ QCOMPARE(entry.recipient.message, QString{"TEST_MESSAGE_1"});
+ QCOMPARE(entry.recipient.sPaymentRequest, std::string{});
+ QCOMPARE(entry.recipient.authenticatedMerchant, QString{});
+
// Check Remove button
QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView");
table->selectRow(currentRowCount-1);
QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");
removeRequestButton->click();
QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);
+
+ // Check removal from wallet
+ QCOMPARE(walletModel.wallet().getAddressReceiveRequests().size(), size_t{0});
}
} // namespace
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 1e8e012dcf..4b1a546c7c 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -163,19 +163,19 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
contextMenu = new QMenu(this);
contextMenu->setObjectName("contextMenu");
- copyAddressAction = contextMenu->addAction(tr("Copy address"), this, &TransactionView::copyAddress);
- copyLabelAction = contextMenu->addAction(tr("Copy label"), this, &TransactionView::copyLabel);
- contextMenu->addAction(tr("Copy amount"), this, &TransactionView::copyAmount);
- contextMenu->addAction(tr("Copy transaction ID"), this, &TransactionView::copyTxID);
- contextMenu->addAction(tr("Copy raw transaction"), this, &TransactionView::copyTxHex);
- contextMenu->addAction(tr("Copy full transaction details"), this, &TransactionView::copyTxPlainText);
- contextMenu->addAction(tr("Show transaction details"), this, &TransactionView::showDetails);
+ copyAddressAction = contextMenu->addAction(tr("&Copy address"), this, &TransactionView::copyAddress);
+ copyLabelAction = contextMenu->addAction(tr("Copy &label"), this, &TransactionView::copyLabel);
+ contextMenu->addAction(tr("Copy &amount"), this, &TransactionView::copyAmount);
+ contextMenu->addAction(tr("Copy transaction &ID"), this, &TransactionView::copyTxID);
+ contextMenu->addAction(tr("Copy &raw transaction"), this, &TransactionView::copyTxHex);
+ contextMenu->addAction(tr("Copy full transaction &details"), this, &TransactionView::copyTxPlainText);
+ contextMenu->addAction(tr("&Show transaction details"), this, &TransactionView::showDetails);
contextMenu->addSeparator();
- bumpFeeAction = contextMenu->addAction(tr("Increase transaction fee"));
+ bumpFeeAction = contextMenu->addAction(tr("Increase transaction &fee"));
GUIUtil::ExceptionSafeConnect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee);
bumpFeeAction->setObjectName("bumpFeeAction");
- abandonAction = contextMenu->addAction(tr("Abandon transaction"), this, &TransactionView::abandonTx);
- contextMenu->addAction(tr("Edit address label"), this, &TransactionView::editLabel);
+ abandonAction = contextMenu->addAction(tr("A&bandon transaction"), this, &TransactionView::abandonTx);
+ contextMenu->addAction(tr("&Edit address label"), this, &TransactionView::editLabel);
connect(dateWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseDate);
connect(typeWidget, qOverload<int>(&QComboBox::activated), this, &TransactionView::chooseType);
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index aa26a01541..7e5790fd87 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -263,6 +263,9 @@ void CreateWalletActivity::createWallet()
if (m_create_wallet_dialog->isDescriptorWalletChecked()) {
flags |= WALLET_FLAG_DESCRIPTORS;
}
+ if (m_create_wallet_dialog->isExternalSignerChecked()) {
+ flags |= WALLET_FLAG_EXTERNAL_SIGNER;
+ }
QTimer::singleShot(500, worker(), [this, name, flags] {
std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message);
@@ -291,6 +294,17 @@ void CreateWalletActivity::finish()
void CreateWalletActivity::create()
{
m_create_wallet_dialog = new CreateWalletDialog(m_parent_widget);
+
+#ifdef ENABLE_EXTERNAL_SIGNER
+ std::vector<ExternalSigner> signers;
+ try {
+ signers = node().externalSigners();
+ } catch (const std::runtime_error& e) {
+ QMessageBox::critical(nullptr, tr("Can't list signers"), e.what());
+ }
+ m_create_wallet_dialog->setSigners(signers);
+#endif
+
m_create_wallet_dialog->setWindowModality(Qt::ApplicationModal);
m_create_wallet_dialog->show();
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 02b3c62867..a1f357e0db 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -4,10 +4,7 @@
#include <qt/walletframe.h>
-#include <qt/bitcoingui.h>
-#include <qt/createwalletdialog.h>
#include <qt/overviewpage.h>
-#include <qt/walletcontroller.h>
#include <qt/walletmodel.h>
#include <qt/walletview.h>
@@ -19,9 +16,8 @@
#include <QPushButton>
#include <QVBoxLayout>
-WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, BitcoinGUI* _gui)
- : QFrame(_gui),
- gui(_gui),
+WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, QWidget* parent)
+ : QFrame(parent),
platformStyle(_platformStyle),
m_size_hint(OverviewPage{platformStyle, nullptr}.sizeHint())
{
@@ -42,11 +38,7 @@ WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, BitcoinGUI* _gui)
// A button for create wallet dialog
QPushButton* create_wallet_button = new QPushButton(tr("Create a new wallet"), walletStack);
- connect(create_wallet_button, &QPushButton::clicked, [this] {
- auto activity = new CreateWalletActivity(gui->getWalletController(), this);
- connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
- activity->create();
- });
+ connect(create_wallet_button, &QPushButton::clicked, this, &WalletFrame::createWalletButtonClicked);
no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop);
no_wallet_group->setLayout(no_wallet_layout);
@@ -66,17 +58,15 @@ void WalletFrame::setClientModel(ClientModel *_clientModel)
}
}
-bool WalletFrame::addWallet(WalletModel *walletModel)
+bool WalletFrame::addWallet(WalletModel* walletModel, WalletView* walletView)
{
- if (!gui || !clientModel || !walletModel) return false;
+ if (!clientModel || !walletModel) return false;
if (mapWalletViews.count(walletModel) > 0) return false;
- WalletView *walletView = new WalletView(platformStyle, this);
walletView->setClientModel(clientModel);
walletView->setWalletModel(walletModel);
walletView->showOutOfSyncWarning(bOutOfSync);
- walletView->setPrivacy(gui->isPrivacyModeActivated());
WalletView* current_wallet_view = currentWalletView();
if (current_wallet_view) {
@@ -88,17 +78,6 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
walletStack->addWidget(walletView);
mapWalletViews[walletModel] = walletView;
- connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked);
- connect(walletView, &WalletView::transactionClicked, gui, &BitcoinGUI::gotoHistoryPage);
- connect(walletView, &WalletView::coinsSent, gui, &BitcoinGUI::gotoHistoryPage);
- connect(walletView, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
- gui->message(title, message, style);
- });
- connect(walletView, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
- connect(walletView, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction);
- connect(walletView, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus);
- connect(gui, &BitcoinGUI::setPrivacy, walletView, &WalletView::setPrivacy);
-
return true;
}
@@ -263,8 +242,3 @@ WalletModel* WalletFrame::currentWalletModel() const
WalletView* wallet_view = currentWalletView();
return wallet_view ? wallet_view->getWalletModel() : nullptr;
}
-
-void WalletFrame::outOfSyncWarningClicked()
-{
- Q_EMIT requestedSyncWarningInfo();
-}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index f57f8678d6..4f77bd716f 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -8,7 +8,6 @@
#include <QFrame>
#include <QMap>
-class BitcoinGUI;
class ClientModel;
class PlatformStyle;
class SendCoinsRecipient;
@@ -31,12 +30,12 @@ class WalletFrame : public QFrame
Q_OBJECT
public:
- explicit WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui = nullptr);
+ explicit WalletFrame(const PlatformStyle* platformStyle, QWidget* parent);
~WalletFrame();
void setClientModel(ClientModel *clientModel);
- bool addWallet(WalletModel *walletModel);
+ bool addWallet(WalletModel* walletModel, WalletView* walletView);
void setCurrentWallet(WalletModel* wallet_model);
void removeWallet(WalletModel* wallet_model);
void removeAllWallets();
@@ -48,12 +47,10 @@ public:
QSize sizeHint() const override { return m_size_hint; }
Q_SIGNALS:
- /** Notify that the user has requested more information about the out-of-sync warning */
- void requestedSyncWarningInfo();
+ void createWalletButtonClicked();
private:
QStackedWidget *walletStack;
- BitcoinGUI *gui;
ClientModel *clientModel;
QMap<WalletModel*, WalletView*> mapWalletViews;
@@ -98,8 +95,6 @@ public Q_SLOTS:
void usedSendingAddresses();
/** Show used receiving addresses */
void usedReceivingAddresses();
- /** Pass on signal over requested out-of-sync-warning information */
- void outOfSyncWarningClicked();
};
#endif // BITCOIN_QT_WALLETFRAME_H
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index cc6db8d33e..967dd588b4 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -466,25 +466,6 @@ void WalletModel::UnlockContext::CopyFrom(UnlockContext&& rhs)
rhs.relock = false;
}
-void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
-{
- vReceiveRequests = m_wallet->getDestValues("rr"); // receive request
-}
-
-bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
-{
- CTxDestination dest = DecodeDestination(sAddress);
-
- std::stringstream ss;
- ss << nId;
- std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata
-
- if (sRequest.empty())
- return m_wallet->eraseDestData(dest, key);
- else
- return m_wallet->addDestData(dest, key, sRequest);
-}
-
bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
{
CCoinControl coin_control;
@@ -544,7 +525,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
if (create_psbt) {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
+ const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, nullptr, psbtx, complete);
if (err != TransactionError::OK || complete) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
return false;
@@ -571,6 +552,18 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
return true;
}
+bool WalletModel::displayAddress(std::string sAddress)
+{
+ CTxDestination dest = DecodeDestination(sAddress);
+ bool res = false;
+ try {
+ res = m_wallet->displayAddress(dest);
+ } catch (const std::runtime_error& e) {
+ QMessageBox::critical(nullptr, tr("Can't display address"), e.what());
+ }
+ return res;
+}
+
bool WalletModel::isWalletEnabled()
{
return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 4ca8643444..47a21bcfcf 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -135,10 +135,8 @@ public:
UnlockContext requestUnlock();
- void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
- bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);
-
bool bumpFee(uint256 hash, uint256& new_hash);
+ bool displayAddress(std::string sAddress);
static bool isWalletEnabled();
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index 25172e774c..d185ddb7e8 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -26,6 +26,11 @@ CTransactionRef& WalletModelTransaction::getWtx()
return wtx;
}
+void WalletModelTransaction::setWtx(const CTransactionRef& newTx)
+{
+ wtx = newTx;
+}
+
unsigned int WalletModelTransaction::getTransactionSize()
{
return wtx ? GetVirtualTransactionSize(*wtx) : 0;
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index f9a95362c8..120d240d91 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -27,6 +27,8 @@ public:
QList<SendCoinsRecipient> getRecipients() const;
CTransactionRef& getWtx();
+ void setWtx(const CTransactionRef&);
+
unsigned int getTransactionSize();
void setTransactionFee(const CAmount& newFee);
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index cc9e1502f0..3b8cf4c7ed 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -73,7 +73,7 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
// Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
connect(overviewPage, &OverviewPage::transactionClicked, transactionView, qOverload<const QModelIndex&>(&TransactionView::focusTransaction));
- connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo);
+ connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::outOfSyncWarningClicked);
connect(sendCoinsPage, &SendCoinsDialog::coinsSent, this, &WalletView::coinsSent);
// Highlight transaction after send
@@ -347,8 +347,3 @@ void WalletView::showProgress(const QString &title, int nProgress)
}
}
}
-
-void WalletView::requestedSyncWarningInfo()
-{
- Q_EMIT outOfSyncWarningClicked();
-}
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index 68f8a5e95b..fedf06b710 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -111,9 +111,6 @@ public Q_SLOTS:
/** Show progress dialog e.g. for rescan */
void showProgress(const QString &title, int nProgress);
- /** User has requested more information about the out of sync state */
- void requestedSyncWarningInfo();
-
Q_SIGNALS:
void setPrivacy(bool privacy);
void transactionClicked();
diff --git a/src/rest.cpp b/src/rest.cpp
index 747c7aea19..d599f381e3 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -125,7 +125,7 @@ static ChainstateManager* GetChainman(const std::any& context, HTTPRequest* req)
__FILE__, __LINE__, __func__, PACKAGE_BUGREPORT));
return nullptr;
}
- return node_context->chainman;
+ return node_context->chainman.get();
}
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 83c1975d38..63897e0e05 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -84,7 +84,6 @@ ChainstateManager& EnsureChainman(const NodeContext& node)
if (!node.chainman) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found");
}
- WITH_LOCK(::cs_main, CHECK_NONFATAL(std::addressof(g_chainman) == std::addressof(*node.chainman)));
return *node.chainman;
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 6826e6fd07..327f961196 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -114,7 +114,6 @@ static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t&
{
LOCK(cs_main);
- CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
IncrementExtraNonce(&block, chainman.ActiveChain().Tip(), extra_nonce);
}
@@ -147,7 +146,6 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
{ // Don't keep cs_main locked
LOCK(cs_main);
- CHECK_NONFATAL(std::addressof(::ChainActive()) == std::addressof(chainman.ActiveChain()));
nHeight = chainman.ActiveChain().Height();
nHeightEnd = nHeight+nGenerate;
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index c4d6e3d546..3013c76825 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -939,7 +939,7 @@ static RPCHelpMan addpeeraddress()
bool success{false};
if (LookupHost(addr_string, net_addr, false)) {
- CAddress address{CAddress({net_addr, port}, ServiceFlags(NODE_NETWORK | NODE_WITNESS))};
+ CAddress address{{net_addr, port}, ServiceFlags{NODE_NETWORK | NODE_WITNESS}};
address.nTime = GetAdjustedTime();
// The source address is set equal to the address. This is equivalent to the peer
// announcing itself.
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 339d711ac9..414c6637a5 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -889,7 +889,7 @@ static RPCHelpMan testmempoolaccept()
"\nReturns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n"
"\nIf multiple transactions are passed in, parents must come before children and package policies apply: the transactions cannot conflict with any mempool transactions or each other.\n"
"\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n"
- "\nThe maximum number of transactions allowed is 25 (MAX_PACKAGE_COUNT)\n"
+ "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n"
"\nThis checks if transactions violate the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n",
{
@@ -905,7 +905,7 @@ static RPCHelpMan testmempoolaccept()
RPCResult{
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
"Returns results for each transaction in the same order they were passed in.\n"
- "It is possible for transactions to not be fully validated ('allowed' unset) if an earlier transaction failed.\n",
+ "It is possible for transactions to not be fully validated ('allowed' unset) if another transaction failed.\n",
{
{RPCResult::Type::OBJ, "", "",
{
@@ -939,7 +939,6 @@ static RPCHelpMan testmempoolaccept()
UniValue::VARR,
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
});
-
const UniValue raw_transactions = request.params[0].get_array();
if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
@@ -951,6 +950,7 @@ static RPCHelpMan testmempoolaccept()
CFeeRate(AmountFromValue(request.params[1]));
std::vector<CTransactionRef> txns;
+ txns.reserve(raw_transactions.size());
for (const auto& rawtx : raw_transactions.getValues()) {
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, rawtx.get_str())) {
@@ -971,8 +971,8 @@ static RPCHelpMan testmempoolaccept()
}();
UniValue rpc_result(UniValue::VARR);
- // We will check transaction fees we iterate through txns in order. If any transaction fee
- // exceeds maxfeerate, we will keave the rest of the validation results blank, because it
+ // We will check transaction fees while we iterate through txns in order. If any transaction fee
+ // exceeds maxfeerate, we will leave the rest of the validation results blank, because it
// doesn't make sense to return a validation result for a transaction if its ancestor(s) would
// not be submitted.
bool exit_early{false};
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 7cf25e0c82..2059628b54 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -301,6 +301,16 @@ public:
return obj;
}
+ UniValue operator()(const WitnessV1Taproot& tap) const
+ {
+ UniValue obj(UniValue::VOBJ);
+ obj.pushKV("isscript", true);
+ obj.pushKV("iswitness", true);
+ obj.pushKV("witness_version", 1);
+ obj.pushKV("witness_program", HexStr(tap));
+ return obj;
+ }
+
UniValue operator()(const WitnessUnknown& id) const
{
UniValue obj(UniValue::VOBJ);
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index b54ba204f0..51cf8a7d62 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -241,9 +241,10 @@ public:
class ConstPubkeyProvider final : public PubkeyProvider
{
CPubKey m_pubkey;
+ bool m_xonly;
public:
- ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey) : PubkeyProvider(exp_index), m_pubkey(pubkey) {}
+ ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey, bool xonly = false) : PubkeyProvider(exp_index), m_pubkey(pubkey), m_xonly(xonly) {}
bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) override
{
key = m_pubkey;
@@ -254,7 +255,7 @@ public:
}
bool IsRange() const override { return false; }
size_t GetSize() const override { return m_pubkey.size(); }
- std::string ToString() const override { return HexStr(m_pubkey); }
+ std::string ToString() const override { return m_xonly ? HexStr(m_pubkey).substr(2) : HexStr(m_pubkey); }
bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
{
CKey key;
@@ -505,6 +506,7 @@ protected:
public:
DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args() {}
DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(Vector(std::move(script))) {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::vector<std::unique_ptr<DescriptorImpl>> scripts, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_name(name), m_subdescriptor_args(std::move(scripts)) {}
bool IsSolvable() const override
{
@@ -638,6 +640,20 @@ public:
std::optional<OutputType> GetOutputType() const override { return std::nullopt; }
};
+static std::optional<OutputType> OutputTypeFromDestination(const CTxDestination& dest) {
+ if (std::holds_alternative<PKHash>(dest) ||
+ std::holds_alternative<ScriptHash>(dest)) {
+ return OutputType::LEGACY;
+ }
+ if (std::holds_alternative<WitnessV0KeyHash>(dest) ||
+ std::holds_alternative<WitnessV0ScriptHash>(dest) ||
+ std::holds_alternative<WitnessV1Taproot>(dest) ||
+ std::holds_alternative<WitnessUnknown>(dest)) {
+ return OutputType::BECH32;
+ }
+ return std::nullopt;
+}
+
/** A parsed addr(A) descriptor. */
class AddressDescriptor final : public DescriptorImpl
{
@@ -651,15 +667,7 @@ public:
std::optional<OutputType> GetOutputType() const override
{
- switch (m_destination.index()) {
- case 1 /* PKHash */:
- case 2 /* ScriptHash */: return OutputType::LEGACY;
- case 3 /* WitnessV0ScriptHash */:
- case 4 /* WitnessV0KeyHash */:
- case 5 /* WitnessUnknown */: return OutputType::BECH32;
- case 0 /* CNoDestination */:
- default: return std::nullopt;
- }
+ return OutputTypeFromDestination(m_destination);
}
bool IsSingleType() const final { return true; }
};
@@ -679,15 +687,7 @@ public:
{
CTxDestination dest;
ExtractDestination(m_script, dest);
- switch (dest.index()) {
- case 1 /* PKHash */:
- case 2 /* ScriptHash */: return OutputType::LEGACY;
- case 3 /* WitnessV0ScriptHash */:
- case 4 /* WitnessV0KeyHash */:
- case 5 /* WitnessUnknown */: return OutputType::BECH32;
- case 0 /* CNoDestination */:
- default: return std::nullopt;
- }
+ return OutputTypeFromDestination(dest);
}
bool IsSingleType() const final { return true; }
};
@@ -695,10 +695,20 @@ public:
/** A parsed pk(P) descriptor. */
class PKDescriptor final : public DescriptorImpl
{
+private:
+ const bool m_xonly;
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override { return Vector(GetScriptForRawPubKey(keys[0])); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript>, FlatSigningProvider&) const override
+ {
+ if (m_xonly) {
+ CScript script = CScript() << ToByteVector(XOnlyPubKey(keys[0])) << OP_CHECKSIG;
+ return Vector(std::move(script));
+ } else {
+ return Vector(GetScriptForRawPubKey(keys[0]));
+ }
+ }
public:
- PKDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Vector(std::move(prov)), "pk") {}
+ PKDescriptor(std::unique_ptr<PubkeyProvider> prov, bool xonly = false) : DescriptorImpl(Vector(std::move(prov)), "pk"), m_xonly(xonly) {}
bool IsSingleType() const final { return true; }
};
@@ -816,6 +826,56 @@ public:
bool IsSingleType() const final { return true; }
};
+/** A parsed tr(...) descriptor. */
+class TRDescriptor final : public DescriptorImpl
+{
+ std::vector<int> m_depths;
+protected:
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, Span<const CScript> scripts, FlatSigningProvider& out) const override
+ {
+ TaprootBuilder builder;
+ assert(m_depths.size() == scripts.size());
+ for (size_t pos = 0; pos < m_depths.size(); ++pos) {
+ builder.Add(m_depths[pos], scripts[pos], TAPROOT_LEAF_TAPSCRIPT);
+ }
+ if (!builder.IsComplete()) return {};
+ assert(keys.size() == 1);
+ XOnlyPubKey xpk(keys[0]);
+ if (!xpk.IsFullyValid()) return {};
+ builder.Finalize(xpk);
+ return Vector(GetScriptForDestination(builder.GetOutput()));
+ }
+ bool ToStringSubScriptHelper(const SigningProvider* arg, std::string& ret, bool priv, bool normalized) const override
+ {
+ if (m_depths.empty()) return true;
+ std::vector<bool> path;
+ for (size_t pos = 0; pos < m_depths.size(); ++pos) {
+ if (pos) ret += ',';
+ while ((int)path.size() <= m_depths[pos]) {
+ if (path.size()) ret += '{';
+ path.push_back(false);
+ }
+ std::string tmp;
+ if (!m_subdescriptor_args[pos]->ToStringHelper(arg, tmp, priv, normalized)) return false;
+ ret += std::move(tmp);
+ while (!path.empty() && path.back()) {
+ if (path.size() > 1) ret += '}';
+ path.pop_back();
+ }
+ if (!path.empty()) path.back() = true;
+ }
+ return true;
+ }
+public:
+ TRDescriptor(std::unique_ptr<PubkeyProvider> internal_key, std::vector<std::unique_ptr<DescriptorImpl>> descs, std::vector<int> depths) :
+ DescriptorImpl(Vector(std::move(internal_key)), std::move(descs), "tr"), m_depths(std::move(depths))
+ {
+ assert(m_subdescriptor_args.size() == m_depths.size());
+ }
+ std::optional<OutputType> GetOutputType() const override { return OutputType::BECH32; }
+ bool IsSingleType() const final { return true; }
+};
+
////////////////////////////////////////////////////////////////////////////
// Parser //
////////////////////////////////////////////////////////////////////////////
@@ -825,6 +885,7 @@ enum class ParseScriptContext {
P2SH, //!< Inside sh() (script becomes P2SH redeemScript)
P2WPKH, //!< Inside wpkh() (no script, pubkey only)
P2WSH, //!< Inside wsh() (script becomes v0 witness script)
+ P2TR, //!< Inside tr() (either internal key, or BIP342 script leaf)
};
/** Parse a key path, being passed a split list of elements (the first element is ignored). */
@@ -873,6 +934,13 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
error = "Uncompressed keys are not allowed";
return nullptr;
}
+ } else if (data.size() == 32 && ctx == ParseScriptContext::P2TR) {
+ unsigned char fullkey[33] = {0x02};
+ std::copy(data.begin(), data.end(), fullkey + 1);
+ pubkey.Set(std::begin(fullkey), std::end(fullkey));
+ if (pubkey.IsFullyValid()) {
+ return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, true);
+ }
}
error = strprintf("Pubkey '%s' is invalid", str);
return nullptr;
@@ -960,13 +1028,16 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
if (!pubkey) return nullptr;
++key_exp_index;
- return std::make_unique<PKDescriptor>(std::move(pubkey));
+ return std::make_unique<PKDescriptor>(std::move(pubkey), ctx == ParseScriptContext::P2TR);
}
- if (Func("pkh", expr)) {
+ if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && Func("pkh", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
if (!pubkey) return nullptr;
++key_exp_index;
return std::make_unique<PKHDescriptor>(std::move(pubkey));
+ } else if (Func("pkh", expr)) {
+ error = "Can only have pkh at top level, in sh(), or in wsh()";
+ return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ctx, out, error);
@@ -977,7 +1048,7 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
error = "Can only have combo() at top level";
return nullptr;
}
- if ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr)) {
+ if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && ((sorted_multi = Func("sortedmulti", expr)) || Func("multi", expr))) {
auto threshold = Expr(expr);
uint32_t thres;
std::vector<std::unique_ptr<PubkeyProvider>> providers;
@@ -1022,6 +1093,9 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
}
}
return std::make_unique<MultisigDescriptor>(thres, std::move(providers), sorted_multi);
+ } else if (Func("sortedmulti", expr) || Func("multi", expr)) {
+ error = "Can only have multi/sortedmulti at top level, in sh(), or in wsh()";
+ return nullptr;
}
if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH) && Func("wpkh", expr)) {
auto pubkey = ParsePubkey(key_exp_index, expr, ParseScriptContext::P2WPKH, out, error);
@@ -1059,6 +1133,67 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const
error = "Can only have addr() at top level";
return nullptr;
}
+ if (ctx == ParseScriptContext::TOP && Func("tr", expr)) {
+ auto arg = Expr(expr);
+ auto internal_key = ParsePubkey(key_exp_index, arg, ParseScriptContext::P2TR, out, error);
+ if (!internal_key) return nullptr;
+ ++key_exp_index;
+ std::vector<std::unique_ptr<DescriptorImpl>> subscripts; //!< list of script subexpressions
+ std::vector<int> depths; //!< depth in the tree of each subexpression (same length subscripts)
+ if (expr.size()) {
+ if (!Const(",", expr)) {
+ error = strprintf("tr: expected ',', got '%c'", expr[0]);
+ return nullptr;
+ }
+ /** The path from the top of the tree to what we're currently processing.
+ * branches[i] == false: left branch in the i'th step from the top; true: right branch.
+ */
+ std::vector<bool> branches;
+ // Loop over all provided scripts. In every iteration exactly one script will be processed.
+ // Use a do-loop because inside this if-branch we expect at least one script.
+ do {
+ // First process all open braces.
+ while (Const("{", expr)) {
+ branches.push_back(false); // new left branch
+ if (branches.size() > TAPROOT_CONTROL_MAX_NODE_COUNT) {
+ error = strprintf("tr() supports at most %i nesting levels", TAPROOT_CONTROL_MAX_NODE_COUNT);
+ return nullptr;
+ }
+ }
+ // Process the actual script expression.
+ auto sarg = Expr(expr);
+ subscripts.emplace_back(ParseScript(key_exp_index, sarg, ParseScriptContext::P2TR, out, error));
+ if (!subscripts.back()) return nullptr;
+ depths.push_back(branches.size());
+ // Process closing braces; one is expected for every right branch we were in.
+ while (branches.size() && branches.back()) {
+ if (!Const("}", expr)) {
+ error = strprintf("tr(): expected '}' after script expression");
+ return nullptr;
+ }
+ branches.pop_back(); // move up one level after encountering '}'
+ }
+ // If after that, we're at the end of a left branch, expect a comma.
+ if (branches.size() && !branches.back()) {
+ if (!Const(",", expr)) {
+ error = strprintf("tr(): expected ',' after script expression");
+ return nullptr;
+ }
+ branches.back() = true; // And now we're in a right branch.
+ }
+ } while (branches.size());
+ // After we've explored a whole tree, we must be at the end of the expression.
+ if (expr.size()) {
+ error = strprintf("tr(): expected ')' after script expression");
+ return nullptr;
+ }
+ }
+ assert(TaprootBuilder::ValidDepths(depths));
+ return std::make_unique<TRDescriptor>(std::move(internal_key), std::move(subscripts), std::move(depths));
+ } else if (Func("tr", expr)) {
+ error = "Can only have tr at top level";
+ return nullptr;
+ }
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
std::string str(expr.begin(), expr.end());
if (!IsHex(str)) {
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index dc0f165be0..3c3c3ac1a8 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1484,9 +1484,8 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CTransacti
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash");
-static const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
-static const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
-static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
+const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
+const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
static bool HandleMissingData(MissingDataBehavior mdb)
{
@@ -1869,10 +1868,8 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
}
k = ss_branch.GetSHA256();
}
- // Compute the tweak from the Merkle root and the internal pubkey.
- k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256();
// Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity.
- return q.CheckPayToContract(p, k, control[0] & 1);
+ return q.CheckTapTweak(p, k, control[0] & 1);
}
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 212de17c7b..fa4ee83e04 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_INTERPRETER_H
#define BITCOIN_SCRIPT_INTERPRETER_H
+#include <hash.h>
#include <script/script_error.h>
#include <span.h>
#include <primitives/transaction.h>
@@ -218,6 +219,9 @@ static constexpr size_t TAPROOT_CONTROL_NODE_SIZE = 32;
static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT = 128;
static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;
+extern const CHashWriter HASHER_TAPLEAF; //!< Hasher with tag "TapLeaf" pre-fed to it.
+extern const CHashWriter HASHER_TAPBRANCH; //!< Hasher with tag "TapBranch" pre-fed to it.
+
template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 364fac3c84..a4b11cc0a9 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -6,8 +6,11 @@
#include <script/standard.h>
#include <crypto/sha256.h>
+#include <hash.h>
#include <pubkey.h>
+#include <script/interpreter.h>
#include <script/script.h>
+#include <util/strencodings.h>
#include <string>
@@ -155,15 +158,14 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
std::vector<unsigned char> witnessprogram;
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
- vSolutionsRet.push_back(witnessprogram);
+ vSolutionsRet.push_back(std::move(witnessprogram));
return TxoutType::WITNESS_V0_KEYHASH;
}
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
- vSolutionsRet.push_back(witnessprogram);
+ vSolutionsRet.push_back(std::move(witnessprogram));
return TxoutType::WITNESS_V0_SCRIPTHASH;
}
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
- vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
vSolutionsRet.push_back(std::move(witnessprogram));
return TxoutType::WITNESS_V1_TAPROOT;
}
@@ -242,8 +244,13 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = hash;
return true;
}
- case TxoutType::WITNESS_UNKNOWN:
case TxoutType::WITNESS_V1_TAPROOT: {
+ WitnessV1Taproot tap;
+ std::copy(vSolutions[0].begin(), vSolutions[0].end(), tap.begin());
+ addressRet = tap;
+ return true;
+ }
+ case TxoutType::WITNESS_UNKNOWN: {
WitnessUnknown unk;
unk.version = vSolutions[0][0];
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
@@ -329,6 +336,11 @@ public:
return CScript() << OP_0 << ToByteVector(id);
}
+ CScript operator()(const WitnessV1Taproot& tap) const
+ {
+ return CScript() << OP_1 << ToByteVector(tap);
+ }
+
CScript operator()(const WitnessUnknown& id) const
{
return CScript() << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length);
@@ -361,3 +373,99 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
bool IsValidDestination(const CTxDestination& dest) {
return dest.index() != 0;
}
+
+/*static*/ TaprootBuilder::NodeInfo TaprootBuilder::Combine(NodeInfo&& a, NodeInfo&& b)
+{
+ NodeInfo ret;
+ /* Lexicographically sort a and b's hash, and compute parent hash. */
+ if (a.hash < b.hash) {
+ ret.hash = (CHashWriter(HASHER_TAPBRANCH) << a.hash << b.hash).GetSHA256();
+ } else {
+ ret.hash = (CHashWriter(HASHER_TAPBRANCH) << b.hash << a.hash).GetSHA256();
+ }
+ return ret;
+}
+
+void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth)
+{
+ assert(depth >= 0 && (size_t)depth <= TAPROOT_CONTROL_MAX_NODE_COUNT);
+ /* We cannot insert a leaf at a lower depth while a deeper branch is unfinished. Doing
+ * so would mean the Add() invocations do not correspond to a DFS traversal of a
+ * binary tree. */
+ if ((size_t)depth + 1 < m_branch.size()) {
+ m_valid = false;
+ return;
+ }
+ /* As long as an entry in the branch exists at the specified depth, combine it and propagate up.
+ * The 'node' variable is overwritten here with the newly combined node. */
+ while (m_valid && m_branch.size() > (size_t)depth && m_branch[depth].has_value()) {
+ node = Combine(std::move(node), std::move(*m_branch[depth]));
+ m_branch.pop_back();
+ if (depth == 0) m_valid = false; /* Can't propagate further up than the root */
+ --depth;
+ }
+ if (m_valid) {
+ /* Make sure the branch is big enough to place the new node. */
+ if (m_branch.size() <= (size_t)depth) m_branch.resize((size_t)depth + 1);
+ assert(!m_branch[depth].has_value());
+ m_branch[depth] = std::move(node);
+ }
+}
+
+/*static*/ bool TaprootBuilder::ValidDepths(const std::vector<int>& depths)
+{
+ std::vector<bool> branch;
+ for (int depth : depths) {
+ // This inner loop corresponds to effectively the same logic on branch
+ // as what Insert() performs on the m_branch variable. Instead of
+ // storing a NodeInfo object, just remember whether or not there is one
+ // at that depth.
+ if (depth < 0 || (size_t)depth > TAPROOT_CONTROL_MAX_NODE_COUNT) return false;
+ if ((size_t)depth + 1 < branch.size()) return false;
+ while (branch.size() > (size_t)depth && branch[depth]) {
+ branch.pop_back();
+ if (depth == 0) return false;
+ --depth;
+ }
+ if (branch.size() <= (size_t)depth) branch.resize((size_t)depth + 1);
+ assert(!branch[depth]);
+ branch[depth] = true;
+ }
+ // And this check corresponds to the IsComplete() check on m_branch.
+ return branch.size() == 0 || (branch.size() == 1 && branch[0]);
+}
+
+TaprootBuilder& TaprootBuilder::Add(int depth, const CScript& script, int leaf_version)
+{
+ assert((leaf_version & ~TAPROOT_LEAF_MASK) == 0);
+ if (!IsValid()) return *this;
+ /* Construct NodeInfo object with leaf hash. */
+ NodeInfo node;
+ node.hash = (CHashWriter{HASHER_TAPLEAF} << uint8_t(leaf_version) << script).GetSHA256();
+ /* Insert into the branch. */
+ Insert(std::move(node), depth);
+ return *this;
+}
+
+TaprootBuilder& TaprootBuilder::AddOmitted(int depth, const uint256& hash)
+{
+ if (!IsValid()) return *this;
+ /* Construct NodeInfo object with the hash directly, and insert it into the branch. */
+ NodeInfo node;
+ node.hash = hash;
+ Insert(std::move(node), depth);
+ return *this;
+}
+
+TaprootBuilder& TaprootBuilder::Finalize(const XOnlyPubKey& internal_key)
+{
+ /* Can only call this function when IsComplete() is true. */
+ assert(IsComplete());
+ m_internal_key = internal_key;
+ auto ret = m_internal_key.CreateTapTweak(m_branch.size() == 0 ? nullptr : &m_branch[0]->hash);
+ assert(ret.has_value());
+ std::tie(m_output_key, std::ignore) = *ret;
+ return *this;
+}
+
+WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_key}; }
diff --git a/src/script/standard.h b/src/script/standard.h
index 12ab9979a8..d7ea5cef27 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_STANDARD_H
#define BITCOIN_SCRIPT_STANDARD_H
+#include <pubkey.h>
#include <script/interpreter.h>
#include <uint256.h>
#include <util/hash_type.h>
@@ -113,6 +114,12 @@ struct WitnessV0KeyHash : public BaseHash<uint160>
};
CKeyID ToKeyID(const WitnessV0KeyHash& key_hash);
+struct WitnessV1Taproot : public XOnlyPubKey
+{
+ WitnessV1Taproot() : XOnlyPubKey() {}
+ explicit WitnessV1Taproot(const XOnlyPubKey& xpk) : XOnlyPubKey(xpk) {}
+};
+
//! CTxDestination subtype to encode any future Witness version
struct WitnessUnknown
{
@@ -142,11 +149,11 @@ struct WitnessUnknown
* * ScriptHash: TxoutType::SCRIPTHASH destination (P2SH)
* * WitnessV0ScriptHash: TxoutType::WITNESS_V0_SCRIPTHASH destination (P2WSH)
* * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH)
- * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN/WITNESS_V1_TAPROOT destination (P2W???)
- * (taproot outputs do not require their own type as long as no wallet support exists)
+ * * WitnessV1Taproot: TxoutType::WITNESS_V1_TAPROOT destination (P2TR)
+ * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W???)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
-using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown>;
+using CTxDestination = std::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown>;
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
@@ -202,4 +209,82 @@ CScript GetScriptForRawPubKey(const CPubKey& pubkey);
/** Generate a multisig script. */
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
+/** Utility class to construct Taproot outputs from internal key and script tree. */
+class TaprootBuilder
+{
+private:
+ /** Information associated with a node in the Merkle tree. */
+ struct NodeInfo
+ {
+ /** Merkle hash of this node. */
+ uint256 hash;
+ };
+ /** Whether the builder is in a valid state so far. */
+ bool m_valid = true;
+
+ /** The current state of the builder.
+ *
+ * For each level in the tree, one NodeInfo object may be present. m_branch[0]
+ * is information about the root; further values are for deeper subtrees being
+ * explored.
+ *
+ * For every right branch taken to reach the position we're currently
+ * working in, there will be a (non-nullopt) entry in m_branch corresponding
+ * to the left branch at that level.
+ *
+ * For example, imagine this tree: - N0 -
+ * / \
+ * N1 N2
+ * / \ / \
+ * A B C N3
+ * / \
+ * D E
+ *
+ * Initially, m_branch is empty. After processing leaf A, it would become
+ * {nullopt, nullopt, A}. When processing leaf B, an entry at level 2 already
+ * exists, and it would thus be combined with it to produce a level 1 one,
+ * resulting in {nullopt, N1}. Adding C and D takes us to {nullopt, N1, C}
+ * and {nullopt, N1, C, D} respectively. When E is processed, it is combined
+ * with D, and then C, and then N1, to produce the root, resulting in {N0}.
+ *
+ * This structure allows processing with just O(log n) overhead if the leaves
+ * are computed on the fly.
+ *
+ * As an invariant, there can never be nullopt entries at the end. There can
+ * also not be more than 128 entries (as that would mean more than 128 levels
+ * in the tree). The depth of newly added entries will always be at least
+ * equal to the current size of m_branch (otherwise it does not correspond
+ * to a depth-first traversal of a tree). m_branch is only empty if no entries
+ * have ever be processed. m_branch having length 1 corresponds to being done.
+ */
+ std::vector<std::optional<NodeInfo>> m_branch;
+
+ XOnlyPubKey m_internal_key; //!< The internal key, set when finalizing.
+ XOnlyPubKey m_output_key; //!< The output key, computed when finalizing. */
+
+ /** Combine information about a parent Merkle tree node from its child nodes. */
+ static NodeInfo Combine(NodeInfo&& a, NodeInfo&& b);
+ /** Insert information about a node at a certain depth, and propagate information up. */
+ void Insert(NodeInfo&& node, int depth);
+
+public:
+ /** Add a new script at a certain depth in the tree. Add() operations must be called
+ * in depth-first traversal order of binary tree. */
+ TaprootBuilder& Add(int depth, const CScript& script, int leaf_version);
+ /** Like Add(), but for a Merkle node with a given hash to the tree. */
+ TaprootBuilder& AddOmitted(int depth, const uint256& hash);
+ /** Finalize the construction. Can only be called when IsComplete() is true.
+ internal_key.IsFullyValid() must be true. */
+ TaprootBuilder& Finalize(const XOnlyPubKey& internal_key);
+
+ /** Return true if so far all input was valid. */
+ bool IsValid() const { return m_valid; }
+ /** Return whether there were either no leaves, or the leaves form a Huffman tree. */
+ bool IsComplete() const { return m_valid && (m_branch.size() == 0 || (m_branch.size() == 1 && m_branch[0].has_value())); }
+ /** Compute scriptPubKey (after Finalize()). */
+ WitnessV1Taproot GetOutput();
+ /** Check if a list of depths is legal (will lead to IsComplete()). */
+ static bool ValidDepths(const std::vector<int>& depths);
+};
+
#endif // BITCOIN_SCRIPT_STANDARD_H
diff --git a/src/secp256k1/.cirrus.yml b/src/secp256k1/.cirrus.yml
new file mode 100644
index 0000000000..506a860336
--- /dev/null
+++ b/src/secp256k1/.cirrus.yml
@@ -0,0 +1,198 @@
+env:
+ WIDEMUL: auto
+ STATICPRECOMPUTATION: yes
+ ECMULTGENPRECISION: auto
+ ASM: no
+ BUILD: check
+ WITH_VALGRIND: yes
+ RUN_VALGRIND: no
+ EXTRAFLAGS:
+ HOST:
+ ECDH: no
+ RECOVERY: no
+ SCHNORRSIG: no
+ EXPERIMENTAL: no
+ CTIMETEST: yes
+ BENCH: yes
+ ITERS: 2
+ MAKEFLAGS: -j2
+
+cat_logs_snippet: &CAT_LOGS
+ always:
+ cat_tests_log_script:
+ - cat tests.log || true
+ cat_exhaustive_tests_log_script:
+ - cat exhaustive_tests.log || true
+ cat_valgrind_ctime_test_log_script:
+ - cat valgrind_ctime_test.log || true
+ cat_bench_log_script:
+ - cat bench.log || true
+ on_failure:
+ cat_config_log_script:
+ - cat config.log || true
+ cat_test_env_script:
+ - cat test_env.log || true
+ cat_ci_env_script:
+ - env
+
+merge_base_script_snippet: &MERGE_BASE
+ merge_base_script:
+ - if [ "$CIRRUS_PR" = "" ]; then exit 0; fi
+ - git fetch $CIRRUS_REPO_CLONE_URL $CIRRUS_BASE_BRANCH
+ - git config --global user.email "ci@ci.ci"
+ - git config --global user.name "ci"
+ - git merge FETCH_HEAD # Merge base to detect silent merge conflicts
+
+task:
+ name: "x86_64: Linux (Debian stable)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ # Reduce number of CPUs to be able to do more builds in parallel.
+ cpu: 1
+ # More than enough for our scripts.
+ memory: 1G
+ matrix: &ENV_MATRIX
+ - env: {WIDEMUL: int64, RECOVERY: yes}
+ - env: {WIDEMUL: int64, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
+ - env: {WIDEMUL: int128}
+ - env: {WIDEMUL: int128, RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
+ - env: {WIDEMUL: int128, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
+ - env: {WIDEMUL: int128, ASM: x86_64}
+ - env: { RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
+ - env: { STATICPRECOMPUTATION: no}
+ - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no}
+ - env: {CPPFLAGS: -DDETERMINISTIC}
+ - env: {CFLAGS: -O0, CTIMETEST: no}
+ - env:
+ CFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
+ LDFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
+ UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
+ ASM: x86_64
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ CTIMETEST: no
+ - env: { ECMULTGENPRECISION: 2 }
+ - env: { ECMULTGENPRECISION: 8 }
+ - env:
+ RUN_VALGRIND: yes
+ ASM: x86_64
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ EXTRAFLAGS: "--disable-openssl-tests"
+ BUILD:
+ matrix:
+ - env:
+ CC: gcc
+ - env:
+ CC: clang
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "i686: Linux (Debian stable)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ HOST: i686-linux-gnu
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ matrix:
+ - env:
+ CC: i686-linux-gnu-gcc
+ - env:
+ CC: clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "x86_64: macOS Catalina"
+ macos_instance:
+ image: catalina-base
+ env:
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ HOMEBREW_NO_INSTALL_CLEANUP: 1
+ # Cirrus gives us a fixed number of 12 virtual CPUs. Not that we even have that many jobs at the moment...
+ MAKEFLAGS: -j13
+ matrix:
+ << : *ENV_MATRIX
+ matrix:
+ - env:
+ CC: gcc-9
+ - env:
+ CC: clang
+ # Update Command Line Tools
+ # Uncomment this if the Command Line Tools on the CirrusCI macOS image are too old to brew valgrind.
+ # See https://apple.stackexchange.com/a/195963 for the implementation.
+ ## update_clt_script:
+ ## - system_profiler SPSoftwareDataType
+ ## - touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
+ ## - |-
+ ## PROD=$(softwareupdate -l | grep "*.*Command Line" | tail -n 1 | awk -F"*" '{print $2}' | sed -e 's/^ *//' | sed 's/Label: //g' | tr -d '\n')
+ ## # For debugging
+ ## - softwareupdate -l && echo "PROD: $PROD"
+ ## - softwareupdate -i "$PROD" --verbose
+ ## - rm /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
+ ##
+ brew_valgrind_pre_script:
+ - brew config
+ - brew tap --shallow LouisBrunner/valgrind
+ # Fetch valgrind source but don't build it yet.
+ - brew fetch --HEAD LouisBrunner/valgrind/valgrind
+ brew_valgrind_cache:
+ # This is $(brew --cellar valgrind) but command substition does not work here.
+ folder: /usr/local/Cellar/valgrind
+ # Rebuild cache if ...
+ fingerprint_script:
+ # ... macOS version changes:
+ - sw_vers
+ # ... brew changes:
+ - brew config
+ # ... valgrind changes:
+ - git -C "$(brew --cache)/valgrind--git" rev-parse HEAD
+ populate_script:
+ # If there's no hit in the cache, build and install valgrind.
+ - brew install --HEAD LouisBrunner/valgrind/valgrind
+ brew_valgrind_post_script:
+ # If we have restored valgrind from the cache, tell brew to create symlink to the PATH.
+ # If we haven't restored from cached (and just run brew install), this is a no-op.
+ - brew link valgrind
+ brew_script:
+ - brew install automake libtool gcc@9
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "s390x (big-endian): Linux (Debian stable, QEMU)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ QEMU_CMD: qemu-s390x
+ HOST: s390x-linux-gnu
+ BUILD:
+ WITH_VALGRIND: no
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ CTIMETEST: no
+ << : *MERGE_BASE
+ test_script:
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=27008
+ - rm /etc/ld.so.cache
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
deleted file mode 100644
index ce8d6391b2..0000000000
--- a/src/secp256k1/.travis.yml
+++ /dev/null
@@ -1,108 +0,0 @@
-language: c
-os:
- - linux
- - osx
-
-dist: bionic
-# Valgrind currently supports upto macOS 10.13, the latest xcode of that version is 10.1
-osx_image: xcode10.1
-addons:
- apt:
- packages:
- - libgmp-dev
- - valgrind
- - libtool-bin
-compiler:
- - clang
- - gcc
-env:
- global:
- - WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2
- matrix:
- - WIDEMUL=int64 RECOVERY=yes
- - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- - WIDEMUL=int128
- - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- - WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- - WIDEMUL=int128 ASM=x86_64
- - BIGNUM=no
- - BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- - BIGNUM=no STATICPRECOMPUTATION=no
- - BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no
- - CPPFLAGS=-DDETERMINISTIC
- - CFLAGS=-O0 CTIMETEST=no
- - ECMULTGENPRECISION=2
- - ECMULTGENPRECISION=8
- - RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes EXTRAFLAGS="--disable-openssl-tests" BUILD=
-matrix:
- fast_finish: true
- include:
- - compiler: clang
- os: linux
- env: HOST=i686-linux-gnu
- addons:
- apt:
- packages:
- - gcc-multilib
- - libgmp-dev:i386
- - valgrind
- - libtool-bin
- - libc6-dbg:i386
- - compiler: clang
- env: HOST=i686-linux-gnu
- os: linux
- addons:
- apt:
- packages:
- - gcc-multilib
- - valgrind
- - libtool-bin
- - libc6-dbg:i386
- - compiler: gcc
- env: HOST=i686-linux-gnu
- os: linux
- addons:
- apt:
- packages:
- - gcc-multilib
- - valgrind
- - libtool-bin
- - libc6-dbg:i386
- - compiler: gcc
- os: linux
- env: HOST=i686-linux-gnu
- addons:
- apt:
- packages:
- - gcc-multilib
- - libgmp-dev:i386
- - valgrind
- - libtool-bin
- - libc6-dbg:i386
- # S390x build (big endian system)
- - compiler: gcc
- env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes CTIMETEST=
- arch: s390x
-
-# We use this to install macOS dependencies instead of the built in `homebrew` plugin,
-# because in xcode earlier than 11 they have a bug requiring updating the system which overall takes ~8 minutes.
-# https://travis-ci.community/t/macos-build-fails-because-of-homebrew-bundle-unknown-command/7296
-before_install:
- - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install gmp valgrind gcc@9; fi
-
-before_script: ./autogen.sh
-
-# travis auto terminates jobs that go for 10 minutes without printing to stdout, but travis_wait doesn't work well with forking programs like valgrind (https://docs.travis-ci.com/user/common-build-problems/#build-times-out-because-no-output-was-received https://github.com/bitcoin-core/secp256k1/pull/750#issuecomment-623476860)
-script:
- - function keep_alive() { while true; do echo -en "\a"; sleep 60; done }
- - keep_alive &
- - ./contrib/travis.sh
- - kill %keep_alive
-
-after_script:
- - cat ./tests.log
- - cat ./exhaustive_tests.log
- - cat ./valgrind_ctime_test.log
- - cat ./bench.log
- - $CC --version
- - valgrind --version
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index 023fa6067f..58c9635e53 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -14,8 +14,6 @@ noinst_HEADERS += src/scalar_8x32_impl.h
noinst_HEADERS += src/scalar_low_impl.h
noinst_HEADERS += src/group.h
noinst_HEADERS += src/group_impl.h
-noinst_HEADERS += src/num_gmp.h
-noinst_HEADERS += src/num_gmp_impl.h
noinst_HEADERS += src/ecdsa.h
noinst_HEADERS += src/ecdsa_impl.h
noinst_HEADERS += src/eckey.h
@@ -26,14 +24,16 @@ noinst_HEADERS += src/ecmult_const.h
noinst_HEADERS += src/ecmult_const_impl.h
noinst_HEADERS += src/ecmult_gen.h
noinst_HEADERS += src/ecmult_gen_impl.h
-noinst_HEADERS += src/num.h
-noinst_HEADERS += src/num_impl.h
noinst_HEADERS += src/field_10x26.h
noinst_HEADERS += src/field_10x26_impl.h
noinst_HEADERS += src/field_5x52.h
noinst_HEADERS += src/field_5x52_impl.h
noinst_HEADERS += src/field_5x52_int128_impl.h
noinst_HEADERS += src/field_5x52_asm_impl.h
+noinst_HEADERS += src/modinv32.h
+noinst_HEADERS += src/modinv32_impl.h
+noinst_HEADERS += src/modinv64.h
+noinst_HEADERS += src/modinv64_impl.h
noinst_HEADERS += src/assumptions.h
noinst_HEADERS += src/util.h
noinst_HEADERS += src/scratch.h
diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md
index e070937235..197a56fff8 100644
--- a/src/secp256k1/README.md
+++ b/src/secp256k1/README.md
@@ -1,7 +1,7 @@
libsecp256k1
============
-[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1)
+[![Build Status](https://api.cirrus-ci.com/github/bitcoin-core/secp256k1.svg?branch=master)](https://cirrus-ci.com/github/bitcoin-core/secp256k1)
Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1.
@@ -34,11 +34,11 @@ Implementation details
* Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1).
* Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys).
* Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan).
- * Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman).
* Scalar operations
* Optimized implementation without data-dependent branches of arithmetic modulo the curve's order.
* Using 4 64-bit limbs (relying on __int128 support in the compiler).
* Using 8 32-bit limbs.
+* Modular inverses (both field elements and scalars) based on [safegcd](https://gcd.cr.yp.to/index.html) with some modifications, and a variable-time variant (by Peter Dettman).
* Group operations
* Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7).
* Use addition between points in Jacobian and affine coordinates where possible.
diff --git a/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 b/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4
index 77fd346a79..7bcbf3200c 100644
--- a/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4
+++ b/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html
+# https://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html
# ===========================================================================
#
# SYNOPSIS
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index ece3d655ed..e57888ca18 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -75,15 +75,10 @@ if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
fi
])
-dnl
-AC_DEFUN([SECP_GMP_CHECK],[
-if test x"$has_gmp" != x"yes"; then
+AC_DEFUN([SECP_VALGRIND_CHECK],[
+if test x"$has_valgrind" != x"yes"; then
CPPFLAGS_TEMP="$CPPFLAGS"
- CPPFLAGS="$GMP_CPPFLAGS $CPPFLAGS"
- LIBS_TEMP="$LIBS"
- LIBS="$GMP_LIBS $LIBS"
- AC_CHECK_HEADER(gmp.h,[AC_CHECK_LIB(gmp, __gmpz_init,[has_gmp=yes; GMP_LIBS="$GMP_LIBS -lgmp"; AC_DEFINE(HAVE_LIBGMP,1,[Define this symbol if libgmp is installed])])])
- CPPFLAGS="$CPPFLAGS_TEMP"
- LIBS="$LIBS_TEMP"
+ CPPFLAGS="$VALGRIND_CPPFLAGS $CPPFLAGS"
+ AC_CHECK_HEADER([valgrind/memcheck.h], [has_valgrind=yes; AC_DEFINE(HAVE_VALGRIND,1,[Define this symbol if valgrind is installed])])
fi
])
diff --git a/src/secp256k1/contrib/travis.sh b/src/secp256k1/ci/cirrus.sh
index 24cc9315cb..f26ca98d1d 100755
--- a/src/secp256k1/contrib/travis.sh
+++ b/src/secp256k1/ci/cirrus.sh
@@ -3,45 +3,63 @@
set -e
set -x
-if [ "$HOST" = "i686-linux-gnu" ]
-then
- export CC="$CC -m32"
-fi
-if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$TRAVIS_COMPILER" = "gcc" ]
-then
- export CC="gcc-9"
-fi
+export LC_ALL=C
+
+env >> test_env.log
+
+$CC -v || true
+valgrind --version || true
+
+./autogen.sh
./configure \
--enable-experimental="$EXPERIMENTAL" \
- --with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \
+ --with-test-override-wide-multiply="$WIDEMUL" --with-asm="$ASM" \
--enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
--enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
--enable-module-schnorrsig="$SCHNORRSIG" \
--with-valgrind="$WITH_VALGRIND" \
--host="$HOST" $EXTRAFLAGS
+# We have set "-j<n>" in MAKEFLAGS.
+make
+
+# Print information about binaries so that we can see that the architecture is correct
+file *tests || true
+file bench_* || true
+file .libs/* || true
+
if [ -n "$BUILD" ]
then
- make -j2 "$BUILD"
+ make "$BUILD"
fi
+
if [ "$RUN_VALGRIND" = "yes" ]
then
- make -j2
- # the `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (http://valgrind.org/docs/manual/manual-core.html)
+ # the `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (https://www.valgrind.org/docs/manual/manual-core.html)
valgrind --error-exitcode=42 ./tests 16
valgrind --error-exitcode=42 ./exhaustive_tests
fi
+
+if [ -n "$QEMU_CMD" ]
+then
+ $QEMU_CMD ./tests 16
+ $QEMU_CMD ./exhaustive_tests
+fi
+
if [ "$BENCH" = "yes" ]
then
+ # Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
+ EXEC='./libtool --mode=execute'
+ if [ -n "$QEMU_CMD" ]
+ then
+ EXEC="$EXEC $QEMU_CMD"
+ fi
if [ "$RUN_VALGRIND" = "yes" ]
then
- # Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
- EXEC='./libtool --mode=execute valgrind --error-exitcode=42'
- else
- EXEC=
+ EXEC="$EXEC valgrind --error-exitcode=42"
fi
- # This limits the iterations in the benchmarks below to ITER(set in .travis.yml) iterations.
+ # This limits the iterations in the benchmarks below to ITER iterations.
export SECP256K1_BENCH_ITERS="$ITERS"
{
$EXEC ./bench_ecmult
diff --git a/src/secp256k1/ci/linux-debian.Dockerfile b/src/secp256k1/ci/linux-debian.Dockerfile
new file mode 100644
index 0000000000..5967cf8b31
--- /dev/null
+++ b/src/secp256k1/ci/linux-debian.Dockerfile
@@ -0,0 +1,13 @@
+FROM debian:stable
+
+RUN dpkg --add-architecture i386
+RUN dpkg --add-architecture s390x
+RUN apt-get update
+
+# dkpg-dev: to make pkg-config work in cross-builds
+RUN apt-get install --no-install-recommends --no-upgrade -y \
+ git ca-certificates \
+ make automake libtool pkg-config dpkg-dev valgrind qemu-user \
+ gcc clang libc6-dbg \
+ gcc-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 \
+ gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index eb3b449bec..1ed991afa7 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -14,7 +14,7 @@ AM_INIT_AUTOMAKE([foreign subdir-objects])
: ${CFLAGS="-g"}
LT_INIT
-dnl make the compilation flags quiet unless V=1 is used
+# Make the compilation flags quiet unless V=1 is used.
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
PKG_PROG_PKG_CONFIG
@@ -22,9 +22,16 @@ PKG_PROG_PKG_CONFIG
AC_PATH_TOOL(AR, ar)
AC_PATH_TOOL(RANLIB, ranlib)
AC_PATH_TOOL(STRIP, strip)
-AX_PROG_CC_FOR_BUILD
+# Save definition of AC_PROG_CC because AM_PROG_CC_C_O in automake<=1.13 will
+# redefine AC_PROG_CC to exit with an error, which avoids the user calling it
+# accidently and screwing up the effect of AM_PROG_CC_C_O. However, we'll need
+# AC_PROG_CC later on in AX_PROG_CC_FOR_BUILD, where its usage is fine, and
+# we'll carefully make sure not to call AC_PROG_CC anywhere else.
+m4_copy([AC_PROG_CC], [saved_AC_PROG_CC])
AM_PROG_CC_C_O
+# Restore AC_PROG_CC
+m4_rename_force([saved_AC_PROG_CC], [AC_PROG_CC])
AC_PROG_CC_C89
if test x"$ac_cv_prog_cc_c89" = x"no"; then
@@ -37,25 +44,23 @@ case $host_os in
if test x$cross_compiling != xyes; then
AC_PATH_PROG([BREW],brew,)
if test x$BREW != x; then
- dnl These Homebrew packages may be keg-only, meaning that they won't be found
- dnl in expected paths because they may conflict with system files. Ask
- dnl Homebrew where each one is located, then adjust paths accordingly.
-
+ # These Homebrew packages may be keg-only, meaning that they won't be found
+ # in expected paths because they may conflict with system files. Ask
+ # Homebrew where each one is located, then adjust paths accordingly.
openssl_prefix=`$BREW --prefix openssl 2>/dev/null`
- gmp_prefix=`$BREW --prefix gmp 2>/dev/null`
+ valgrind_prefix=`$BREW --prefix valgrind 2>/dev/null`
if test x$openssl_prefix != x; then
PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH"
export PKG_CONFIG_PATH
CRYPTO_CPPFLAGS="-I$openssl_prefix/include"
fi
- if test x$gmp_prefix != x; then
- GMP_CPPFLAGS="-I$gmp_prefix/include"
- GMP_LIBS="-L$gmp_prefix/lib"
+ if test x$valgrind_prefix != x; then
+ VALGRIND_CPPFLAGS="-I$valgrind_prefix/include"
fi
else
AC_PATH_PROG([PORT],port,)
- dnl if homebrew isn't installed and macports is, add the macports default paths
- dnl as a last resort.
+ # If homebrew isn't installed and macports is, add the macports default paths
+ # as a last resort.
if test x$PORT != x; then
CPPFLAGS="$CPPFLAGS -isystem /opt/local/include"
LDFLAGS="$LDFLAGS -L/opt/local/lib"
@@ -78,6 +83,15 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
])
saved_CFLAGS="$CFLAGS"
+CFLAGS="-Wconditional-uninitialized $CFLAGS"
+AC_MSG_CHECKING([if ${CC} supports -Wconditional-uninitialized])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
+ [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS"
+ ])
+
+saved_CFLAGS="$CFLAGS"
CFLAGS="-fvisibility=hidden $CFLAGS"
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
@@ -86,6 +100,10 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
CFLAGS="$saved_CFLAGS"
])
+###
+### Define config arguments
+###
+
AC_ARG_ENABLE(benchmark,
AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]),
[use_benchmark=$enableval],
@@ -146,13 +164,10 @@ AC_ARG_ENABLE(external_default_callbacks,
[use_external_default_callbacks=$enableval],
[use_external_default_callbacks=no])
-dnl Test-only override of the (autodetected by the C code) "widemul" setting.
-dnl Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default).
+# Test-only override of the (autodetected by the C code) "widemul" setting.
+# Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default).
AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto])
-AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
-[bignum implementation to use [default=auto]])],[req_bignum=$withval], [req_bignum=auto])
-
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto],
[assembly optimizations to use (experimental: arm) [default=auto]])],[req_asm=$withval], [req_asm=auto])
@@ -177,15 +192,22 @@ AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind=yes|no|auto],
)],
[req_valgrind=$withval], [req_valgrind=auto])
+###
+### Handle config options (except for modules)
+###
+
if test x"$req_valgrind" = x"no"; then
enable_valgrind=no
else
- AC_CHECK_HEADER([valgrind/memcheck.h], [enable_valgrind=yes], [
+ SECP_VALGRIND_CHECK
+ if test x"$has_valgrind" != x"yes"; then
if test x"$req_valgrind" = x"yes"; then
AC_MSG_ERROR([Valgrind support explicitly requested but valgrind/memcheck.h header not available])
fi
enable_valgrind=no
- ], [])
+ else
+ enable_valgrind=yes
+ fi
fi
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
@@ -197,61 +219,6 @@ else
CFLAGS="-O2 $CFLAGS"
fi
-if test x"$use_ecmult_static_precomputation" != x"no"; then
- # Temporarily switch to an environment for the native compiler
- save_cross_compiling=$cross_compiling
- cross_compiling=no
- SAVE_CC="$CC"
- CC="$CC_FOR_BUILD"
- SAVE_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS_FOR_BUILD"
- SAVE_CPPFLAGS="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS_FOR_BUILD"
- SAVE_LDFLAGS="$LDFLAGS"
- LDFLAGS="$LDFLAGS_FOR_BUILD"
-
- warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
- saved_CFLAGS="$CFLAGS"
- CFLAGS="$warn_CFLAGS_FOR_BUILD $CFLAGS"
- AC_MSG_CHECKING([if native ${CC_FOR_BUILD} supports ${warn_CFLAGS_FOR_BUILD}])
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
- [ AC_MSG_RESULT([yes]) ],
- [ AC_MSG_RESULT([no])
- CFLAGS="$saved_CFLAGS"
- ])
-
- AC_MSG_CHECKING([for working native compiler: ${CC_FOR_BUILD}])
- AC_RUN_IFELSE(
- [AC_LANG_PROGRAM([], [])],
- [working_native_cc=yes],
- [working_native_cc=no],[:])
-
- CFLAGS_FOR_BUILD="$CFLAGS"
-
- # Restore the environment
- cross_compiling=$save_cross_compiling
- CC="$SAVE_CC"
- CFLAGS="$SAVE_CFLAGS"
- CPPFLAGS="$SAVE_CPPFLAGS"
- LDFLAGS="$SAVE_LDFLAGS"
-
- if test x"$working_native_cc" = x"no"; then
- AC_MSG_RESULT([no])
- set_precomp=no
- m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
- if test x"$use_ecmult_static_precomputation" = x"yes"; then
- AC_MSG_ERROR([native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
- else
- AC_MSG_WARN([Disabling statically generated ecmult table because the native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
- fi
- else
- AC_MSG_RESULT([yes])
- set_precomp=yes
- fi
-else
- set_precomp=no
-fi
-
if test x"$req_asm" = x"auto"; then
SECP_64BIT_ASM_CHECK
if test x"$has_64bit_asm" = x"yes"; then
@@ -279,33 +246,7 @@ else
esac
fi
-if test x"$req_bignum" = x"auto"; then
- SECP_GMP_CHECK
- if test x"$has_gmp" = x"yes"; then
- set_bignum=gmp
- fi
-
- if test x"$set_bignum" = x; then
- set_bignum=no
- fi
-else
- set_bignum=$req_bignum
- case $set_bignum in
- gmp)
- SECP_GMP_CHECK
- if test x"$has_gmp" != x"yes"; then
- AC_MSG_ERROR([gmp bignum explicitly requested but libgmp not available])
- fi
- ;;
- no)
- ;;
- *)
- AC_MSG_ERROR([invalid bignum implementation selection])
- ;;
- esac
-fi
-
-# select assembly optimization
+# Select assembly optimization
use_external_asm=no
case $set_asm in
@@ -322,7 +263,12 @@ no)
;;
esac
-# select wide multiplication implementation
+if test x"$use_external_asm" = x"yes"; then
+ AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
+fi
+
+
+# Select wide multiplication implementation
case $set_widemul in
int128)
AC_DEFINE(USE_FORCE_WIDEMUL_INT128, 1, [Define this symbol to force the use of the (unsigned) __int128 based wide multiplication implementation])
@@ -337,25 +283,7 @@ auto)
;;
esac
-# select bignum implementation
-case $set_bignum in
-gmp)
- AC_DEFINE(HAVE_LIBGMP, 1, [Define this symbol if libgmp is installed])
- AC_DEFINE(USE_NUM_GMP, 1, [Define this symbol to use the gmp implementation for num])
- AC_DEFINE(USE_FIELD_INV_NUM, 1, [Define this symbol to use the num-based field inverse implementation])
- AC_DEFINE(USE_SCALAR_INV_NUM, 1, [Define this symbol to use the num-based scalar inverse implementation])
- ;;
-no)
- AC_DEFINE(USE_NUM_NONE, 1, [Define this symbol to use no num implementation])
- AC_DEFINE(USE_FIELD_INV_BUILTIN, 1, [Define this symbol to use the native field inverse implementation])
- AC_DEFINE(USE_SCALAR_INV_BUILTIN, 1, [Define this symbol to use the native scalar inverse implementation])
- ;;
-*)
- AC_MSG_ERROR([invalid bignum implementation])
- ;;
-esac
-
-#set ecmult window size
+# Set ecmult window size
if test x"$req_ecmult_window" = x"auto"; then
set_ecmult_window=15
else
@@ -377,7 +305,7 @@ case $set_ecmult_window in
;;
esac
-#set ecmult gen precision
+# Set ecmult gen precision
if test x"$req_ecmult_gen_precision" = x"auto"; then
set_ecmult_gen_precision=4
else
@@ -419,15 +347,93 @@ else
enable_openssl_tests=no
fi
-if test x"$set_bignum" = x"gmp"; then
- SECP_LIBS="$SECP_LIBS $GMP_LIBS"
- SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS"
+if test x"$enable_valgrind" = x"yes"; then
+ SECP_INCLUDES="$SECP_INCLUDES $VALGRIND_CPPFLAGS"
+fi
+
+# Handle static precomputation (after everything which modifies CFLAGS and friends)
+if test x"$use_ecmult_static_precomputation" != x"no"; then
+ if test x"$cross_compiling" = x"no"; then
+ set_precomp=yes
+ if test x"${CC_FOR_BUILD+x}${CFLAGS_FOR_BUILD+x}${CPPFLAGS_FOR_BUILD+x}${LDFLAGS_FOR_BUILD+x}" != x; then
+ AC_MSG_WARN([CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD is set but ignored because we are not cross-compiling.])
+ fi
+ # If we're not cross-compiling, simply use the same compiler for building the static precompation code.
+ CC_FOR_BUILD="$CC"
+ CFLAGS_FOR_BUILD="$CFLAGS"
+ CPPFLAGS_FOR_BUILD="$CPPFLAGS"
+ LDFLAGS_FOR_BUILD="$LDFLAGS"
+ else
+ AX_PROG_CC_FOR_BUILD
+
+ # Temporarily switch to an environment for the native compiler
+ save_cross_compiling=$cross_compiling
+ cross_compiling=no
+ SAVE_CC="$CC"
+ CC="$CC_FOR_BUILD"
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS_FOR_BUILD"
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS_FOR_BUILD"
+ SAVE_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS_FOR_BUILD"
+
+ warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$warn_CFLAGS_FOR_BUILD $CFLAGS"
+ AC_MSG_CHECKING([if native ${CC_FOR_BUILD} supports ${warn_CFLAGS_FOR_BUILD}])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
+ [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_RESULT([no])
+ CFLAGS="$saved_CFLAGS"
+ ])
+
+ AC_MSG_CHECKING([for working native compiler: ${CC_FOR_BUILD}])
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([], [])],
+ [working_native_cc=yes],
+ [working_native_cc=no],[:])
+
+ CFLAGS_FOR_BUILD="$CFLAGS"
+
+ # Restore the environment
+ cross_compiling=$save_cross_compiling
+ CC="$SAVE_CC"
+ CFLAGS="$SAVE_CFLAGS"
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+
+ if test x"$working_native_cc" = x"no"; then
+ AC_MSG_RESULT([no])
+ set_precomp=no
+ m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
+ if test x"$use_ecmult_static_precomputation" = x"yes"; then
+ AC_MSG_ERROR([native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
+ else
+ AC_MSG_WARN([Disabling statically generated ecmult table because the native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
+ fi
+ else
+ AC_MSG_RESULT([yes])
+ set_precomp=yes
+ fi
+ fi
+
+ AC_SUBST(CC_FOR_BUILD)
+ AC_SUBST(CFLAGS_FOR_BUILD)
+ AC_SUBST(CPPFLAGS_FOR_BUILD)
+ AC_SUBST(LDFLAGS_FOR_BUILD)
+else
+ set_precomp=no
fi
if test x"$set_precomp" = x"yes"; then
AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table])
fi
+###
+### Handle module options
+###
+
if test x"$enable_module_ecdh" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
fi
@@ -447,14 +453,14 @@ if test x"$enable_module_extrakeys" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module])
fi
-if test x"$use_external_asm" = x"yes"; then
- AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
-fi
-
if test x"$use_external_default_callbacks" = x"yes"; then
AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used])
fi
+###
+### Check for --enable-experimental if necessary
+###
+
if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([******])
AC_MSG_NOTICE([WARNING: experimental build])
@@ -474,6 +480,10 @@ else
fi
fi
+###
+### Generate output
+###
+
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
AC_SUBST(SECP_INCLUDES)
@@ -492,7 +502,7 @@ AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" =
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
-dnl make sure nothing new is exported so that we don't break the cache
+# Make sure nothing new is exported so that we don't break the cache.
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
unset PKG_CONFIG_PATH
PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP"
@@ -513,10 +523,9 @@ echo " module extrakeys = $enable_module_extrakeys"
echo " module schnorrsig = $enable_module_schnorrsig"
echo
echo " asm = $set_asm"
-echo " bignum = $set_bignum"
echo " ecmult window size = $set_ecmult_window"
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
-dnl Hide test-only options unless they're used.
+# Hide test-only options unless they're used.
if test x"$set_widemul" != xauto; then
echo " wide multiplication = $set_widemul"
fi
@@ -527,3 +536,9 @@ echo " CFLAGS = $CFLAGS"
echo " CPPFLAGS = $CPPFLAGS"
echo " LDFLAGS = $LDFLAGS"
echo
+if test x"$set_precomp" = x"yes"; then
+echo " CC_FOR_BUILD = $CC_FOR_BUILD"
+echo " CFLAGS_FOR_BUILD = $CFLAGS_FOR_BUILD"
+echo " CPPFLAGS_FOR_BUILD = $CPPFLAGS_FOR_BUILD"
+echo " LDFLAGS_FOR_BUILD = $LDFLAGS_FOR_BUILD"
+fi
diff --git a/src/secp256k1/contrib/lax_der_parsing.c b/src/secp256k1/contrib/lax_der_parsing.c
index f71db4b535..c1627e37e9 100644
--- a/src/secp256k1/contrib/lax_der_parsing.c
+++ b/src/secp256k1/contrib/lax_der_parsing.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <string.h>
#include <secp256k1.h>
diff --git a/src/secp256k1/contrib/lax_der_parsing.h b/src/secp256k1/contrib/lax_der_parsing.h
index 7eaf63bf6a..6b7255e28f 100644
--- a/src/secp256k1/contrib/lax_der_parsing.h
+++ b/src/secp256k1/contrib/lax_der_parsing.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
/****
* Please do not link this file directly. It is not part of the libsecp256k1
diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.c b/src/secp256k1/contrib/lax_der_privatekey_parsing.c
index c2e63b4b8d..429760fbb6 100644
--- a/src/secp256k1/contrib/lax_der_privatekey_parsing.c
+++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014, 2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014, 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <string.h>
#include <secp256k1.h>
diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.h b/src/secp256k1/contrib/lax_der_privatekey_parsing.h
index fece261fb9..602c7c556a 100644
--- a/src/secp256k1/contrib/lax_der_privatekey_parsing.h
+++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014, 2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014, 2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
/****
* Please do not link this file directly. It is not part of the libsecp256k1
diff --git a/src/secp256k1/doc/safegcd_implementation.md b/src/secp256k1/doc/safegcd_implementation.md
new file mode 100644
index 0000000000..3ae556f9a7
--- /dev/null
+++ b/src/secp256k1/doc/safegcd_implementation.md
@@ -0,0 +1,765 @@
+# The safegcd implementation in libsecp256k1 explained
+
+This document explains the modular inverse implementation in the `src/modinv*.h` files. It is based
+on the paper
+["Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/papers.html#safegcd)
+by Daniel J. Bernstein and Bo-Yin Yang. The references below are for the Date: 2019.04.13 version.
+
+The actual implementation is in C of course, but for demonstration purposes Python3 is used here.
+Most implementation aspects and optimizations are explained, except those that depend on the specific
+number representation used in the C code.
+
+## 1. Computing the Greatest Common Divisor (GCD) using divsteps
+
+The algorithm from the paper (section 11), at a very high level, is this:
+
+```python
+def gcd(f, g):
+ """Compute the GCD of an odd integer f and another integer g."""
+ assert f & 1 # require f to be odd
+ delta = 1 # additional state variable
+ while g != 0:
+ assert f & 1 # f will be odd in every iteration
+ if delta > 0 and g & 1:
+ delta, f, g = 1 - delta, g, (g - f) // 2
+ elif g & 1:
+ delta, f, g = 1 + delta, f, (g + f) // 2
+ else:
+ delta, f, g = 1 + delta, f, (g ) // 2
+ return abs(f)
+```
+
+It computes the greatest common divisor of an odd integer *f* and any integer *g*. Its inner loop
+keeps rewriting the variables *f* and *g* alongside a state variable *&delta;* that starts at *1*, until
+*g=0* is reached. At that point, *|f|* gives the GCD. Each of the transitions in the loop is called a
+"division step" (referred to as divstep in what follows).
+
+For example, *gcd(21, 14)* would be computed as:
+- Start with *&delta;=1 f=21 g=14*
+- Take the third branch: *&delta;=2 f=21 g=7*
+- Take the first branch: *&delta;=-1 f=7 g=-7*
+- Take the second branch: *&delta;=0 f=7 g=0*
+- The answer *|f| = 7*.
+
+Why it works:
+- Divsteps can be decomposed into two steps (see paragraph 8.2 in the paper):
+ - (a) If *g* is odd, replace *(f,g)* with *(g,g-f)* or (f,g+f), resulting in an even *g*.
+ - (b) Replace *(f,g)* with *(f,g/2)* (where *g* is guaranteed to be even).
+- Neither of those two operations change the GCD:
+ - For (a), assume *gcd(f,g)=c*, then it must be the case that *f=a&thinsp;c* and *g=b&thinsp;c* for some integers *a*
+ and *b*. As *(g,g-f)=(b&thinsp;c,(b-a)c)* and *(f,f+g)=(a&thinsp;c,(a+b)c)*, the result clearly still has
+ common factor *c*. Reasoning in the other direction shows that no common factor can be added by
+ doing so either.
+ - For (b), we know that *f* is odd, so *gcd(f,g)* clearly has no factor *2*, and we can remove
+ it from *g*.
+- The algorithm will eventually converge to *g=0*. This is proven in the paper (see theorem G.3).
+- It follows that eventually we find a final value *f'* for which *gcd(f,g) = gcd(f',0)*. As the
+ gcd of *f'* and *0* is *|f'|* by definition, that is our answer.
+
+Compared to more [traditional GCD algorithms](https://en.wikipedia.org/wiki/Euclidean_algorithm), this one has the property of only ever looking at
+the low-order bits of the variables to decide the next steps, and being easy to make
+constant-time (in more low-level languages than Python). The *&delta;* parameter is necessary to
+guide the algorithm towards shrinking the numbers' magnitudes without explicitly needing to look
+at high order bits.
+
+Properties that will become important later:
+- Performing more divsteps than needed is not a problem, as *f* does not change anymore after *g=0*.
+- Only even numbers are divided by *2*. This means that when reasoning about it algebraically we
+ do not need to worry about rounding.
+- At every point during the algorithm's execution the next *N* steps only depend on the bottom *N*
+ bits of *f* and *g*, and on *&delta;*.
+
+
+## 2. From GCDs to modular inverses
+
+We want an algorithm to compute the inverse *a* of *x* modulo *M*, i.e. the number a such that *a&thinsp;x=1
+mod M*. This inverse only exists if the GCD of *x* and *M* is *1*, but that is always the case if *M* is
+prime and *0 < x < M*. In what follows, assume that the modular inverse exists.
+It turns out this inverse can be computed as a side effect of computing the GCD by keeping track
+of how the internal variables can be written as linear combinations of the inputs at every step
+(see the [extended Euclidean algorithm](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm)).
+Since the GCD is *1*, such an algorithm will compute numbers *a* and *b* such that a&thinsp;x + b&thinsp;M = 1*.
+Taking that expression *mod M* gives *a&thinsp;x mod M = 1*, and we see that *a* is the modular inverse of *x
+mod M*.
+
+A similar approach can be used to calculate modular inverses using the divsteps-based GCD
+algorithm shown above, if the modulus *M* is odd. To do so, compute *gcd(f=M,g=x)*, while keeping
+track of extra variables *d* and *e*, for which at every step *d = f/x (mod M)* and *e = g/x (mod M)*.
+*f/x* here means the number which multiplied with *x* gives *f mod M*. As *f* and *g* are initialized to *M*
+and *x* respectively, *d* and *e* just start off being *0* (*M/x mod M = 0/x mod M = 0*) and *1* (*x/x mod M
+= 1*).
+
+```python
+def div2(M, x):
+ """Helper routine to compute x/2 mod M (where M is odd)."""
+ assert M & 1
+ if x & 1: # If x is odd, make it even by adding M.
+ x += M
+ # x must be even now, so a clean division by 2 is possible.
+ return x // 2
+
+def modinv(M, x):
+ """Compute the inverse of x mod M (given that it exists, and M is odd)."""
+ assert M & 1
+ delta, f, g, d, e = 1, M, x, 0, 1
+ while g != 0:
+ # Note that while division by two for f and g is only ever done on even inputs, this is
+ # not true for d and e, so we need the div2 helper function.
+ if delta > 0 and g & 1:
+ delta, f, g, d, e = 1 - delta, g, (g - f) // 2, e, div2(M, e - d)
+ elif g & 1:
+ delta, f, g, d, e = 1 + delta, f, (g + f) // 2, d, div2(M, e + d)
+ else:
+ delta, f, g, d, e = 1 + delta, f, (g ) // 2, d, div2(M, e )
+ # Verify that the invariants d=f/x mod M, e=g/x mod M are maintained.
+ assert f % M == (d * x) % M
+ assert g % M == (e * x) % M
+ assert f == 1 or f == -1 # |f| is the GCD, it must be 1
+ # Because of invariant d = f/x (mod M), 1/x = d/f (mod M). As |f|=1, d/f = d*f.
+ return (d * f) % M
+```
+
+Also note that this approach to track *d* and *e* throughout the computation to determine the inverse
+is different from the paper. There (see paragraph 12.1 in the paper) a transition matrix for the
+entire computation is determined (see section 3 below) and the inverse is computed from that.
+The approach here avoids the need for 2x2 matrix multiplications of various sizes, and appears to
+be faster at the level of optimization we're able to do in C.
+
+
+## 3. Batching multiple divsteps
+
+Every divstep can be expressed as a matrix multiplication, applying a transition matrix *(1/2 t)*
+to both vectors *[f, g]* and *[d, e]* (see paragraph 8.1 in the paper):
+
+```
+ t = [ u, v ]
+ [ q, r ]
+
+ [ out_f ] = (1/2 * t) * [ in_f ]
+ [ out_g ] = [ in_g ]
+
+ [ out_d ] = (1/2 * t) * [ in_d ] (mod M)
+ [ out_e ] [ in_e ]
+```
+
+where *(u, v, q, r)* is *(0, 2, -1, 1)*, *(2, 0, 1, 1)*, or *(2, 0, 0, 1)*, depending on which branch is
+taken. As above, the resulting *f* and *g* are always integers.
+
+Performing multiple divsteps corresponds to a multiplication with the product of all the
+individual divsteps' transition matrices. As each transition matrix consists of integers
+divided by *2*, the product of these matrices will consist of integers divided by *2<sup>N</sup>* (see also
+theorem 9.2 in the paper). These divisions are expensive when updating *d* and *e*, so we delay
+them: we compute the integer coefficients of the combined transition matrix scaled by *2<sup>N</sup>*, and
+do one division by *2<sup>N</sup>* as a final step:
+
+```python
+def divsteps_n_matrix(delta, f, g):
+ """Compute delta and transition matrix t after N divsteps (multiplied by 2^N)."""
+ u, v, q, r = 1, 0, 0, 1 # start with identity matrix
+ for _ in range(N):
+ if delta > 0 and g & 1:
+ delta, f, g, u, v, q, r = 1 - delta, g, (g - f) // 2, 2*q, 2*r, q-u, r-v
+ elif g & 1:
+ delta, f, g, u, v, q, r = 1 + delta, f, (g + f) // 2, 2*u, 2*v, q+u, r+v
+ else:
+ delta, f, g, u, v, q, r = 1 + delta, f, (g ) // 2, 2*u, 2*v, q , r
+ return delta, (u, v, q, r)
+```
+
+As the branches in the divsteps are completely determined by the bottom *N* bits of *f* and *g*, this
+function to compute the transition matrix only needs to see those bottom bits. Furthermore all
+intermediate results and outputs fit in *(N+1)*-bit numbers (unsigned for *f* and *g*; signed for *u*, *v*,
+*q*, and *r*) (see also paragraph 8.3 in the paper). This means that an implementation using 64-bit
+integers could set *N=62* and compute the full transition matrix for 62 steps at once without any
+big integer arithmetic at all. This is the reason why this algorithm is efficient: it only needs
+to update the full-size *f*, *g*, *d*, and *e* numbers once every *N* steps.
+
+We still need functions to compute:
+
+```
+ [ out_f ] = (1/2^N * [ u, v ]) * [ in_f ]
+ [ out_g ] ( [ q, r ]) [ in_g ]
+
+ [ out_d ] = (1/2^N * [ u, v ]) * [ in_d ] (mod M)
+ [ out_e ] ( [ q, r ]) [ in_e ]
+```
+
+Because the divsteps transformation only ever divides even numbers by two, the result of *t&thinsp;[f,g]* is always even. When *t* is a composition of *N* divsteps, it follows that the resulting *f*
+and *g* will be multiple of *2<sup>N</sup>*, and division by *2<sup>N</sup>* is simply shifting them down:
+
+```python
+def update_fg(f, g, t):
+ """Multiply matrix t/2^N with [f, g]."""
+ u, v, q, r = t
+ cf, cg = u*f + v*g, q*f + r*g
+ # (t / 2^N) should cleanly apply to [f,g] so the result of t*[f,g] should have N zero
+ # bottom bits.
+ assert cf % 2**N == 0
+ assert cg % 2**N == 0
+ return cf >> N, cg >> N
+```
+
+The same is not true for *d* and *e*, and we need an equivalent of the `div2` function for division by *2<sup>N</sup> mod M*.
+This is easy if we have precomputed *1/M mod 2<sup>N</sup>* (which always exists for odd *M*):
+
+```python
+def div2n(M, Mi, x):
+ """Compute x/2^N mod M, given Mi = 1/M mod 2^N."""
+ assert (M * Mi) % 2**N == 1
+ # Find a factor m such that m*M has the same bottom N bits as x. We want:
+ # (m * M) mod 2^N = x mod 2^N
+ # <=> m mod 2^N = (x / M) mod 2^N
+ # <=> m mod 2^N = (x * Mi) mod 2^N
+ m = (Mi * x) % 2**N
+ # Subtract that multiple from x, cancelling its bottom N bits.
+ x -= m * M
+ # Now a clean division by 2^N is possible.
+ assert x % 2**N == 0
+ return (x >> N) % M
+
+def update_de(d, e, t, M, Mi):
+ """Multiply matrix t/2^N with [d, e], modulo M."""
+ u, v, q, r = t
+ cd, ce = u*d + v*e, q*d + r*e
+ return div2n(M, Mi, cd), div2n(M, Mi, ce)
+```
+
+With all of those, we can write a version of `modinv` that performs *N* divsteps at once:
+
+```python3
+def modinv(M, Mi, x):
+ """Compute the modular inverse of x mod M, given Mi=1/M mod 2^N."""
+ assert M & 1
+ delta, f, g, d, e = 1, M, x, 0, 1
+ while g != 0:
+ # Compute the delta and transition matrix t for the next N divsteps (this only needs
+ # (N+1)-bit signed integer arithmetic).
+ delta, t = divsteps_n_matrix(delta, f % 2**N, g % 2**N)
+ # Apply the transition matrix t to [f, g]:
+ f, g = update_fg(f, g, t)
+ # Apply the transition matrix t to [d, e]:
+ d, e = update_de(d, e, t, M, Mi)
+ return (d * f) % M
+```
+
+This means that in practice we'll always perform a multiple of *N* divsteps. This is not a problem
+because once *g=0*, further divsteps do not affect *f*, *g*, *d*, or *e* anymore (only *&delta;* keeps
+increasing). For variable time code such excess iterations will be mostly optimized away in later
+sections.
+
+
+## 4. Avoiding modulus operations
+
+So far, there are two places where we compute a remainder of big numbers modulo *M*: at the end of
+`div2n` in every `update_de`, and at the very end of `modinv` after potentially negating *d* due to the
+sign of *f*. These are relatively expensive operations when done generically.
+
+To deal with the modulus operation in `div2n`, we simply stop requiring *d* and *e* to be in range
+*[0,M)* all the time. Let's start by inlining `div2n` into `update_de`, and dropping the modulus
+operation at the end:
+
+```python
+def update_de(d, e, t, M, Mi):
+ """Multiply matrix t/2^N with [d, e] mod M, given Mi=1/M mod 2^N."""
+ u, v, q, r = t
+ cd, ce = u*d + v*e, q*d + r*e
+ # Cancel out bottom N bits of cd and ce.
+ md = -((Mi * cd) % 2**N)
+ me = -((Mi * ce) % 2**N)
+ cd += md * M
+ ce += me * M
+ # And cleanly divide by 2**N.
+ return cd >> N, ce >> N
+```
+
+Let's look at bounds on the ranges of these numbers. It can be shown that *|u|+|v|* and *|q|+|r|*
+never exceed *2<sup>N</sup>* (see paragraph 8.3 in the paper), and thus a multiplication with *t* will have
+outputs whose absolute values are at most *2<sup>N</sup>* times the maximum absolute input value. In case the
+inputs *d* and *e* are in *(-M,M)*, which is certainly true for the initial values *d=0* and *e=1* assuming
+*M > 1*, the multiplication results in numbers in range *(-2<sup>N</sup>M,2<sup>N</sup>M)*. Subtracting less than *2<sup>N</sup>*
+times *M* to cancel out *N* bits brings that up to *(-2<sup>N+1</sup>M,2<sup>N</sup>M)*, and
+dividing by *2<sup>N</sup>* at the end takes it to *(-2M,M)*. Another application of `update_de` would take that
+to *(-3M,2M)*, and so forth. This progressive expansion of the variables' ranges can be
+counteracted by incrementing *d* and *e* by *M* whenever they're negative:
+
+```python
+ ...
+ if d < 0:
+ d += M
+ if e < 0:
+ e += M
+ cd, ce = u*d + v*e, q*d + r*e
+ # Cancel out bottom N bits of cd and ce.
+ ...
+```
+
+With inputs in *(-2M,M)*, they will first be shifted into range *(-M,M)*, which means that the
+output will again be in *(-2M,M)*, and this remains the case regardless of how many `update_de`
+invocations there are. In what follows, we will try to make this more efficient.
+
+Note that increasing *d* by *M* is equal to incrementing *cd* by *u&thinsp;M* and *ce* by *q&thinsp;M*. Similarly,
+increasing *e* by *M* is equal to incrementing *cd* by *v&thinsp;M* and *ce* by *r&thinsp;M*. So we could instead write:
+
+```python
+ ...
+ cd, ce = u*d + v*e, q*d + r*e
+ # Perform the equivalent of incrementing d, e by M when they're negative.
+ if d < 0:
+ cd += u*M
+ ce += q*M
+ if e < 0:
+ cd += v*M
+ ce += r*M
+ # Cancel out bottom N bits of cd and ce.
+ md = -((Mi * cd) % 2**N)
+ me = -((Mi * ce) % 2**N)
+ cd += md * M
+ ce += me * M
+ ...
+```
+
+Now note that we have two steps of corrections to *cd* and *ce* that add multiples of *M*: this
+increment, and the decrement that cancels out bottom bits. The second one depends on the first
+one, but they can still be efficiently combined by only computing the bottom bits of *cd* and *ce*
+at first, and using that to compute the final *md*, *me* values:
+
+```python
+def update_de(d, e, t, M, Mi):
+ """Multiply matrix t/2^N with [d, e], modulo M."""
+ u, v, q, r = t
+ md, me = 0, 0
+ # Compute what multiples of M to add to cd and ce.
+ if d < 0:
+ md += u
+ me += q
+ if e < 0:
+ md += v
+ me += r
+ # Compute bottom N bits of t*[d,e] + M*[md,me].
+ cd, ce = (u*d + v*e + md*M) % 2**N, (q*d + r*e + me*M) % 2**N
+ # Correct md and me such that the bottom N bits of t*[d,e] + M*[md,me] are zero.
+ md -= (Mi * cd) % 2**N
+ me -= (Mi * ce) % 2**N
+ # Do the full computation.
+ cd, ce = u*d + v*e + md*M, q*d + r*e + me*M
+ # And cleanly divide by 2**N.
+ return cd >> N, ce >> N
+```
+
+One last optimization: we can avoid the *md&thinsp;M* and *me&thinsp;M* multiplications in the bottom bits of *cd*
+and *ce* by moving them to the *md* and *me* correction:
+
+```python
+ ...
+ # Compute bottom N bits of t*[d,e].
+ cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N
+ # Correct md and me such that the bottom N bits of t*[d,e]+M*[md,me] are zero.
+ # Note that this is not the same as {md = (-Mi * cd) % 2**N} etc. That would also result in N
+ # zero bottom bits, but isn't guaranteed to be a reduction of [0,2^N) compared to the
+ # previous md and me values, and thus would violate our bounds analysis.
+ md -= (Mi*cd + md) % 2**N
+ me -= (Mi*ce + me) % 2**N
+ ...
+```
+
+The resulting function takes *d* and *e* in range *(-2M,M)* as inputs, and outputs values in the same
+range. That also means that the *d* value at the end of `modinv` will be in that range, while we want
+a result in *[0,M)*. To do that, we need a normalization function. It's easy to integrate the
+conditional negation of *d* (based on the sign of *f*) into it as well:
+
+```python
+def normalize(sign, v, M):
+ """Compute sign*v mod M, where v is in range (-2*M,M); output in [0,M)."""
+ assert sign == 1 or sign == -1
+ # v in (-2*M,M)
+ if v < 0:
+ v += M
+ # v in (-M,M). Now multiply v with sign (which can only be 1 or -1).
+ if sign == -1:
+ v = -v
+ # v in (-M,M)
+ if v < 0:
+ v += M
+ # v in [0,M)
+ return v
+```
+
+And calling it in `modinv` is simply:
+
+```python
+ ...
+ return normalize(f, d, M)
+```
+
+
+## 5. Constant-time operation
+
+The primary selling point of the algorithm is fast constant-time operation. What code flow still
+depends on the input data so far?
+
+- the number of iterations of the while *g &ne; 0* loop in `modinv`
+- the branches inside `divsteps_n_matrix`
+- the sign checks in `update_de`
+- the sign checks in `normalize`
+
+To make the while loop in `modinv` constant time it can be replaced with a constant number of
+iterations. The paper proves (Theorem 11.2) that *741* divsteps are sufficient for any *256*-bit
+inputs, and [safegcd-bounds](https://github.com/sipa/safegcd-bounds) shows that the slightly better bound *724* is
+sufficient even. Given that every loop iteration performs *N* divsteps, it will run a total of
+*&lceil;724/N&rceil;* times.
+
+To deal with the branches in `divsteps_n_matrix` we will replace them with constant-time bitwise
+operations (and hope the C compiler isn't smart enough to turn them back into branches; see
+`valgrind_ctime_test.c` for automated tests that this isn't the case). To do so, observe that a
+divstep can be written instead as (compare to the inner loop of `gcd` in section 1).
+
+```python
+ x = -f if delta > 0 else f # set x equal to (input) -f or f
+ if g & 1:
+ g += x # set g to (input) g-f or g+f
+ if delta > 0:
+ delta = -delta
+ f += g # set f to (input) g (note that g was set to g-f before)
+ delta += 1
+ g >>= 1
+```
+
+To convert the above to bitwise operations, we rely on a trick to negate conditionally: per the
+definition of negative numbers in two's complement, (*-v == ~v + 1*) holds for every number *v*. As
+*-1* in two's complement is all *1* bits, bitflipping can be expressed as xor with *-1*. It follows
+that *-v == (v ^ -1) - (-1)*. Thus, if we have a variable *c* that takes on values *0* or *-1*, then
+*(v ^ c) - c* is *v* if *c=0* and *-v* if *c=-1*.
+
+Using this we can write:
+
+```python
+ x = -f if delta > 0 else f
+```
+
+in constant-time form as:
+
+```python
+ c1 = (-delta) >> 63
+ # Conditionally negate f based on c1:
+ x = (f ^ c1) - c1
+```
+
+To use that trick, we need a helper mask variable *c1* that resolves the condition *&delta;>0* to *-1*
+(if true) or *0* (if false). We compute *c1* using right shifting, which is equivalent to dividing by
+the specified power of *2* and rounding down (in Python, and also in C under the assumption of a typical two's complement system; see
+`assumptions.h` for tests that this is the case). Right shifting by *63* thus maps all
+numbers in range *[-2<sup>63</sup>,0)* to *-1*, and numbers in range *[0,2<sup>63</sup>)* to *0*.
+
+Using the facts that *x&0=0* and *x&(-1)=x* (on two's complement systems again), we can write:
+
+```python
+ if g & 1:
+ g += x
+```
+
+as:
+
+```python
+ # Compute c2=0 if g is even and c2=-1 if g is odd.
+ c2 = -(g & 1)
+ # This masks out x if g is even, and leaves x be if g is odd.
+ g += x & c2
+```
+
+Using the conditional negation trick again we can write:
+
+```python
+ if g & 1:
+ if delta > 0:
+ delta = -delta
+```
+
+as:
+
+```python
+ # Compute c3=-1 if g is odd and delta>0, and 0 otherwise.
+ c3 = c1 & c2
+ # Conditionally negate delta based on c3:
+ delta = (delta ^ c3) - c3
+```
+
+Finally:
+
+```python
+ if g & 1:
+ if delta > 0:
+ f += g
+```
+
+becomes:
+
+```python
+ f += g & c3
+```
+
+It turns out that this can be implemented more efficiently by applying the substitution
+*&eta;=-&delta;*. In this representation, negating *&delta;* corresponds to negating *&eta;*, and incrementing
+*&delta;* corresponds to decrementing *&eta;*. This allows us to remove the negation in the *c1*
+computation:
+
+```python
+ # Compute a mask c1 for eta < 0, and compute the conditional negation x of f:
+ c1 = eta >> 63
+ x = (f ^ c1) - c1
+ # Compute a mask c2 for odd g, and conditionally add x to g:
+ c2 = -(g & 1)
+ g += x & c2
+ # Compute a mask c for (eta < 0) and odd (input) g, and use it to conditionally negate eta,
+ # and add g to f:
+ c3 = c1 & c2
+ eta = (eta ^ c3) - c3
+ f += g & c3
+ # Incrementing delta corresponds to decrementing eta.
+ eta -= 1
+ g >>= 1
+```
+
+A variant of divsteps with better worst-case performance can be used instead: starting *&delta;* at
+*1/2* instead of *1*. This reduces the worst case number of iterations to *590* for *256*-bit inputs
+(which can be shown using convex hull analysis). In this case, the substitution *&zeta;=-(&delta;+1/2)*
+is used instead to keep the variable integral. Incrementing *&delta;* by *1* still translates to
+decrementing *&zeta;* by *1*, but negating *&delta;* now corresponds to going from *&zeta;* to *-(&zeta;+1)*, or
+*~&zeta;*. Doing that conditionally based on *c3* is simply:
+
+```python
+ ...
+ c3 = c1 & c2
+ zeta ^= c3
+ ...
+```
+
+By replacing the loop in `divsteps_n_matrix` with a variant of the divstep code above (extended to
+also apply all *f* operations to *u*, *v* and all *g* operations to *q*, *r*), a constant-time version of
+`divsteps_n_matrix` is obtained. The full code will be in section 7.
+
+These bit fiddling tricks can also be used to make the conditional negations and additions in
+`update_de` and `normalize` constant-time.
+
+
+## 6. Variable-time optimizations
+
+In section 5, we modified the `divsteps_n_matrix` function (and a few others) to be constant time.
+Constant time operations are only necessary when computing modular inverses of secret data. In
+other cases, it slows down calculations unnecessarily. In this section, we will construct a
+faster non-constant time `divsteps_n_matrix` function.
+
+To do so, first consider yet another way of writing the inner loop of divstep operations in
+`gcd` from section 1. This decomposition is also explained in the paper in section 8.2. We use
+the original version with initial *&delta;=1* and *&eta;=-&delta;* here.
+
+```python
+for _ in range(N):
+ if g & 1 and eta < 0:
+ eta, f, g = -eta, g, -f
+ if g & 1:
+ g += f
+ eta -= 1
+ g >>= 1
+```
+
+Whenever *g* is even, the loop only shifts *g* down and decreases *&eta;*. When *g* ends in multiple zero
+bits, these iterations can be consolidated into one step. This requires counting the bottom zero
+bits efficiently, which is possible on most platforms; it is abstracted here as the function
+`count_trailing_zeros`.
+
+```python
+def count_trailing_zeros(v):
+ """For a non-zero value v, find z such that v=(d<<z) for some odd d."""
+ return (v & -v).bit_length() - 1
+
+i = N # divsteps left to do
+while True:
+ # Get rid of all bottom zeros at once. In the first iteration, g may be odd and the following
+ # lines have no effect (until "if eta < 0").
+ zeros = min(i, count_trailing_zeros(g))
+ eta -= zeros
+ g >>= zeros
+ i -= zeros
+ if i == 0:
+ break
+ # We know g is odd now
+ if eta < 0:
+ eta, f, g = -eta, g, -f
+ g += f
+ # g is even now, and the eta decrement and g shift will happen in the next loop.
+```
+
+We can now remove multiple bottom *0* bits from *g* at once, but still need a full iteration whenever
+there is a bottom *1* bit. In what follows, we will get rid of multiple *1* bits simultaneously as
+well.
+
+Observe that as long as *&eta; &geq; 0*, the loop does not modify *f*. Instead, it cancels out bottom
+bits of *g* and shifts them out, and decreases *&eta;* and *i* accordingly - interrupting only when *&eta;*
+becomes negative, or when *i* reaches *0*. Combined, this is equivalent to adding a multiple of *f* to
+*g* to cancel out multiple bottom bits, and then shifting them out.
+
+It is easy to find what that multiple is: we want a number *w* such that *g+w&thinsp;f* has a few bottom
+zero bits. If that number of bits is *L*, we want *g+w&thinsp;f mod 2<sup>L</sup> = 0*, or *w = -g/f mod 2<sup>L</sup>*. Since *f*
+is odd, such a *w* exists for any *L*. *L* cannot be more than *i* steps (as we'd finish the loop before
+doing more) or more than *&eta;+1* steps (as we'd run `eta, f, g = -eta, g, f` at that point), but
+apart from that, we're only limited by the complexity of computing *w*.
+
+This code demonstrates how to cancel up to 4 bits per step:
+
+```python
+NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n
+i = N
+while True:
+ zeros = min(i, count_trailing_zeros(g))
+ eta -= zeros
+ g >>= zeros
+ i -= zeros
+ if i == 0:
+ break
+ # We know g is odd now
+ if eta < 0:
+ eta, f, g = -eta, g, f
+ # Compute limit on number of bits to cancel
+ limit = min(min(eta + 1, i), 4)
+ # Compute w = -g/f mod 2**limit, using the table value for -1/f mod 2**4. Note that f is
+ # always odd, so its inverse modulo a power of two always exists.
+ w = (g * NEGINV16[(f & 15) // 2]) % (2**limit)
+ # As w = -g/f mod (2**limit), g+w*f mod 2**limit = 0 mod 2**limit.
+ g += w * f
+ assert g % (2**limit) == 0
+ # The next iteration will now shift out at least limit bottom zero bits from g.
+```
+
+By using a bigger table more bits can be cancelled at once. The table can also be implemented
+as a formula. Several formulas are known for computing modular inverses modulo powers of two;
+some can be found in Hacker's Delight second edition by Henry S. Warren, Jr. pages 245-247.
+Here we need the negated modular inverse, which is a simple transformation of those:
+
+- Instead of a 3-bit table:
+ - *-f* or *f ^ 6*
+- Instead of a 4-bit table:
+ - *1 - f(f + 1)*
+ - *-(f + (((f + 1) & 4) << 1))*
+- For larger tables the following technique can be used: if *w=-1/f mod 2<sup>L</sup>*, then *w(w&thinsp;f+2)* is
+ *-1/f mod 2<sup>2L</sup>*. This allows extending the previous formulas (or tables). In particular we
+ have this 6-bit function (based on the 3-bit function above):
+ - *f(f<sup>2</sup> - 2)*
+
+This loop, again extended to also handle *u*, *v*, *q*, and *r* alongside *f* and *g*, placed in
+`divsteps_n_matrix`, gives a significantly faster, but non-constant time version.
+
+
+## 7. Final Python version
+
+All together we need the following functions:
+
+- A way to compute the transition matrix in constant time, using the `divsteps_n_matrix` function
+ from section 2, but with its loop replaced by a variant of the constant-time divstep from
+ section 5, extended to handle *u*, *v*, *q*, *r*:
+
+```python
+def divsteps_n_matrix(zeta, f, g):
+ """Compute zeta and transition matrix t after N divsteps (multiplied by 2^N)."""
+ u, v, q, r = 1, 0, 0, 1 # start with identity matrix
+ for _ in range(N):
+ c1 = zeta >> 63
+ # Compute x, y, z as conditionally-negated versions of f, u, v.
+ x, y, z = (f ^ c1) - c1, (u ^ c1) - c1, (v ^ c1) - c1
+ c2 = -(g & 1)
+ # Conditionally add x, y, z to g, q, r.
+ g, q, r = g + (x & c2), q + (y & c2), r + (z & c2)
+ c1 &= c2 # reusing c1 here for the earlier c3 variable
+ zeta = (zeta ^ c1) - 1 # inlining the unconditional zeta decrement here
+ # Conditionally add g, q, r to f, u, v.
+ f, u, v = f + (g & c1), u + (q & c1), v + (r & c1)
+ # When shifting g down, don't shift q, r, as we construct a transition matrix multiplied
+ # by 2^N. Instead, shift f's coefficients u and v up.
+ g, u, v = g >> 1, u << 1, v << 1
+ return zeta, (u, v, q, r)
+```
+
+- The functions to update *f* and *g*, and *d* and *e*, from section 2 and section 4, with the constant-time
+ changes to `update_de` from section 5:
+
+```python
+def update_fg(f, g, t):
+ """Multiply matrix t/2^N with [f, g]."""
+ u, v, q, r = t
+ cf, cg = u*f + v*g, q*f + r*g
+ return cf >> N, cg >> N
+
+def update_de(d, e, t, M, Mi):
+ """Multiply matrix t/2^N with [d, e], modulo M."""
+ u, v, q, r = t
+ d_sign, e_sign = d >> 257, e >> 257
+ md, me = (u & d_sign) + (v & e_sign), (q & d_sign) + (r & e_sign)
+ cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N
+ md -= (Mi*cd + md) % 2**N
+ me -= (Mi*ce + me) % 2**N
+ cd, ce = u*d + v*e + M*md, q*d + r*e + M*me
+ return cd >> N, ce >> N
+```
+
+- The `normalize` function from section 4, made constant time as well:
+
+```python
+def normalize(sign, v, M):
+ """Compute sign*v mod M, where v in (-2*M,M); output in [0,M)."""
+ v_sign = v >> 257
+ # Conditionally add M to v.
+ v += M & v_sign
+ c = (sign - 1) >> 1
+ # Conditionally negate v.
+ v = (v ^ c) - c
+ v_sign = v >> 257
+ # Conditionally add M to v again.
+ v += M & v_sign
+ return v
+```
+
+- And finally the `modinv` function too, adapted to use *&zeta;* instead of *&delta;*, and using the fixed
+ iteration count from section 5:
+
+```python
+def modinv(M, Mi, x):
+ """Compute the modular inverse of x mod M, given Mi=1/M mod 2^N."""
+ zeta, f, g, d, e = -1, M, x, 0, 1
+ for _ in range((590 + N - 1) // N):
+ zeta, t = divsteps_n_matrix(zeta, f % 2**N, g % 2**N)
+ f, g = update_fg(f, g, t)
+ d, e = update_de(d, e, t, M, Mi)
+ return normalize(f, d, M)
+```
+
+- To get a variable time version, replace the `divsteps_n_matrix` function with one that uses the
+ divsteps loop from section 5, and a `modinv` version that calls it without the fixed iteration
+ count:
+
+```python
+NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n
+def divsteps_n_matrix_var(eta, f, g):
+ """Compute eta and transition matrix t after N divsteps (multiplied by 2^N)."""
+ u, v, q, r = 1, 0, 0, 1
+ i = N
+ while True:
+ zeros = min(i, count_trailing_zeros(g))
+ eta, i = eta - zeros, i - zeros
+ g, u, v = g >> zeros, u << zeros, v << zeros
+ if i == 0:
+ break
+ if eta < 0:
+ eta, f, u, v, g, q, r = -eta, g, q, r, -f, -u, -v
+ limit = min(min(eta + 1, i), 4)
+ w = (g * NEGINV16[(f & 15) // 2]) % (2**limit)
+ g, q, r = g + w*f, q + w*u, r + w*v
+ return eta, (u, v, q, r)
+
+def modinv_var(M, Mi, x):
+ """Compute the modular inverse of x mod M, given Mi = 1/M mod 2^N."""
+ eta, f, g, d, e = -1, M, x, 0, 1
+ while g != 0:
+ eta, t = divsteps_n_matrix_var(eta, f % 2**N, g % 2**N)
+ f, g = update_fg(f, g, t)
+ d, e = update_de(d, e, t, M, Mi)
+ return normalize(f, d, Mi)
+```
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 2178c8e2d6..d368488af2 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -11,7 +11,7 @@ extern "C" {
*
* 1. Context pointers go first, followed by output arguments, combined
* output/input arguments, and finally input-only arguments.
- * 2. Array lengths always immediately the follow the argument whose length
+ * 2. Array lengths always immediately follow the argument whose length
* they describe, even if this violates rule 1.
* 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated
* later go first. This means: signatures, public nonces, secret nonces,
@@ -452,7 +452,14 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
* 0: incorrect or unparseable signature
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig: the signature being verified (cannot be NULL)
- * msg32: the 32-byte message hash being verified (cannot be NULL)
+ * msghash32: the 32-byte message hash being verified (cannot be NULL).
+ * The verifier must make sure to apply a cryptographic
+ * hash function to the message by itself and not accept an
+ * msghash32 value directly. Otherwise, it would be easy to
+ * create a "valid" signature without knowledge of the
+ * secret key. See also
+ * https://bitcoin.stackexchange.com/a/81116/35586 for more
+ * background on this topic.
* pubkey: pointer to an initialized public key to verify with (cannot be NULL)
*
* To avoid accepting malleable signatures, only ECDSA signatures in lower-S
@@ -467,7 +474,7 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
const secp256k1_context* ctx,
const secp256k1_ecdsa_signature *sig,
- const unsigned char *msg32,
+ const unsigned char *msghash32,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
@@ -532,12 +539,12 @@ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_def
*
* Returns: 1: signature created
* 0: the nonce generation function failed, or the secret key was invalid.
- * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
- * Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
- * In: msg32: the 32-byte message hash being signed (cannot be NULL)
- * seckey: pointer to a 32-byte secret key (cannot be NULL)
- * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
- * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
+ * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
+ * Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
+ * In: msghash32: the 32-byte message hash being signed (cannot be NULL)
+ * seckey: pointer to a 32-byte secret key (cannot be NULL)
+ * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
+ * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
*
* The created signature is always in lower-S form. See
* secp256k1_ecdsa_signature_normalize for more details.
@@ -545,7 +552,7 @@ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_def
SECP256K1_API int secp256k1_ecdsa_sign(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature *sig,
- const unsigned char *msg32,
+ const unsigned char *msghash32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
const void *ndata
@@ -626,7 +633,7 @@ 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. (cannot be NULL)
- * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * 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) (cannot be NULL).
@@ -634,7 +641,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add(
const secp256k1_context* ctx,
unsigned char *seckey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Same as secp256k1_ec_seckey_tweak_add, but DEPRECATED. Will be removed in
@@ -642,7 +649,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
const secp256k1_context* ctx,
unsigned char *seckey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by adding tweak times the generator to it.
@@ -654,7 +661,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
* (cannot be NULL).
* In/Out: pubkey: pointer to a public key object. pubkey will be set to an
* invalid value if this function returns 0 (cannot be NULL).
- * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * 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) (cannot be NULL).
@@ -662,7 +669,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a secret key by multiplying it by a tweak.
@@ -673,7 +680,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
* invalid according to secp256k1_ec_seckey_verify, this
* function returns 0. seckey will be set to some unspecified
* value if this function returns 0. (cannot be NULL)
- * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * 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) (cannot be NULL).
@@ -681,7 +688,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul(
const secp256k1_context* ctx,
unsigned char *seckey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Same as secp256k1_ec_seckey_tweak_mul, but DEPRECATED. Will be removed in
@@ -689,7 +696,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
const secp256k1_context* ctx,
unsigned char *seckey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a public key by multiplying it by a tweak value.
@@ -699,7 +706,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
* (cannot be NULL).
* In/Out: pubkey: pointer to a public key object. pubkey will be set to an
* invalid value if this function returns 0 (cannot be NULL).
- * In: tweak: pointer to a 32-byte tweak. If the tweak is invalid according to
+ * 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) (cannot be NULL).
@@ -707,7 +714,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
- const unsigned char *tweak
+ const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Updates the context randomization to protect against side-channel leakage.
diff --git a/src/secp256k1/include/secp256k1_extrakeys.h b/src/secp256k1/include/secp256k1_extrakeys.h
index 0c5dff2c94..6fc7b290f8 100644
--- a/src/secp256k1/include/secp256k1_extrakeys.h
+++ b/src/secp256k1/include/secp256k1_extrakeys.h
@@ -165,6 +165,19 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+/** Get the secret key from a keypair.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL)
+ * Out: seckey: pointer to a 32-byte buffer for the secret key (cannot be NULL)
+ * In: keypair: pointer to a keypair (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec(
+ const secp256k1_context* ctx,
+ unsigned char *seckey,
+ const secp256k1_keypair *keypair
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
/** Get the public key from a keypair.
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h
index f8ccaecd3d..aa16532ce8 100644
--- a/src/secp256k1/include/secp256k1_recovery.h
+++ b/src/secp256k1/include/secp256k1_recovery.h
@@ -71,17 +71,17 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
*
* Returns: 1: signature created
* 0: the nonce generation function failed, or the secret key was invalid.
- * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
- * Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
- * In: msg32: the 32-byte message hash being signed (cannot be NULL)
- * seckey: pointer to a 32-byte secret key (cannot be NULL)
- * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
- * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
+ * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
+ * Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
+ * In: msghash32: the 32-byte message hash being signed (cannot be NULL)
+ * seckey: pointer to a 32-byte secret key (cannot be NULL)
+ * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
+ * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
*/
SECP256K1_API int secp256k1_ecdsa_sign_recoverable(
const secp256k1_context* ctx,
secp256k1_ecdsa_recoverable_signature *sig,
- const unsigned char *msg32,
+ const unsigned char *msghash32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
const void *ndata
@@ -91,16 +91,16 @@ SECP256K1_API int secp256k1_ecdsa_sign_recoverable(
*
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
* 0: otherwise.
- * Args: ctx: pointer to a context object, initialized for verification (cannot be NULL)
- * Out: pubkey: pointer to the recovered public key (cannot be NULL)
- * In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL)
- * msg32: the 32-byte message hash assumed to be signed (cannot be NULL)
+ * Args: ctx: pointer to a context object, initialized for verification (cannot be NULL)
+ * Out: pubkey: pointer to the recovered public key (cannot be NULL)
+ * In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL)
+ * msghash32: the 32-byte message hash assumed to be signed (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const secp256k1_ecdsa_recoverable_signature *sig,
- const unsigned char *msg32
+ const unsigned char *msghash32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
diff --git a/src/secp256k1/sage/gen_exhaustive_groups.sage b/src/secp256k1/sage/gen_exhaustive_groups.sage
index 3c3c984811..01d15dcdea 100644
--- a/src/secp256k1/sage/gen_exhaustive_groups.sage
+++ b/src/secp256k1/sage/gen_exhaustive_groups.sage
@@ -1,9 +1,4 @@
-# Define field size and field
-P = 2^256 - 2^32 - 977
-F = GF(P)
-BETA = F(0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee)
-
-assert(BETA != F(1) and BETA^3 == F(1))
+load("secp256k1_params.sage")
orders_done = set()
results = {}
diff --git a/src/secp256k1/sage/gen_split_lambda_constants.sage b/src/secp256k1/sage/gen_split_lambda_constants.sage
new file mode 100644
index 0000000000..7d4359e0f6
--- /dev/null
+++ b/src/secp256k1/sage/gen_split_lambda_constants.sage
@@ -0,0 +1,114 @@
+""" Generates the constants used in secp256k1_scalar_split_lambda.
+
+See the comments for secp256k1_scalar_split_lambda in src/scalar_impl.h for detailed explanations.
+"""
+
+load("secp256k1_params.sage")
+
+def inf_norm(v):
+ """Returns the infinity norm of a vector."""
+ return max(map(abs, v))
+
+def gauss_reduction(i1, i2):
+ v1, v2 = i1.copy(), i2.copy()
+ while True:
+ if inf_norm(v2) < inf_norm(v1):
+ v1, v2 = v2, v1
+ # This is essentially
+ # m = round((v1[0]*v2[0] + v1[1]*v2[1]) / (inf_norm(v1)**2))
+ # (rounding to the nearest integer) without relying on floating point arithmetic.
+ m = ((v1[0]*v2[0] + v1[1]*v2[1]) + (inf_norm(v1)**2) // 2) // (inf_norm(v1)**2)
+ if m == 0:
+ return v1, v2
+ v2[0] -= m*v1[0]
+ v2[1] -= m*v1[1]
+
+def find_split_constants_gauss():
+ """Find constants for secp256k1_scalar_split_lamdba using gauss reduction."""
+ (v11, v12), (v21, v22) = gauss_reduction([0, N], [1, int(LAMBDA)])
+
+ # We use related vectors in secp256k1_scalar_split_lambda.
+ A1, B1 = -v21, -v11
+ A2, B2 = v22, -v21
+
+ return A1, B1, A2, B2
+
+def find_split_constants_explicit_tof():
+ """Find constants for secp256k1_scalar_split_lamdba using the trace of Frobenius.
+
+ See Benjamin Smith: "Easy scalar decompositions for efficient scalar multiplication on
+ elliptic curves and genus 2 Jacobians" (https://eprint.iacr.org/2013/672), Example 2
+ """
+ assert P % 3 == 1 # The paper says P % 3 == 2 but that appears to be a mistake, see [10].
+ assert C.j_invariant() == 0
+
+ t = C.trace_of_frobenius()
+
+ c = Integer(sqrt((4*P - t**2)/3))
+ A1 = Integer((t - c)/2 - 1)
+ B1 = c
+
+ A2 = Integer((t + c)/2 - 1)
+ B2 = Integer(1 - (t - c)/2)
+
+ # We use a negated b values in secp256k1_scalar_split_lambda.
+ B1, B2 = -B1, -B2
+
+ return A1, B1, A2, B2
+
+A1, B1, A2, B2 = find_split_constants_explicit_tof()
+
+# For extra fun, use an independent method to recompute the constants.
+assert (A1, B1, A2, B2) == find_split_constants_gauss()
+
+# PHI : Z[l] -> Z_n where phi(a + b*l) == a + b*lambda mod n.
+def PHI(a,b):
+ return Z(a + LAMBDA*b)
+
+# Check that (A1, B1) and (A2, B2) are in the kernel of PHI.
+assert PHI(A1, B1) == Z(0)
+assert PHI(A2, B2) == Z(0)
+
+# Check that the parallelogram generated by (A1, A2) and (B1, B2)
+# is a fundamental domain by containing exactly N points.
+# Since the LHS is the determinant and N != 0, this also checks that
+# (A1, A2) and (B1, B2) are linearly independent. By the previous
+# assertions, (A1, A2) and (B1, B2) are a basis of the kernel.
+assert A1*B2 - B1*A2 == N
+
+# Check that their components are short enough.
+assert (A1 + A2)/2 < sqrt(N)
+assert B1 < sqrt(N)
+assert B2 < sqrt(N)
+
+G1 = round((2**384)*B2/N)
+G2 = round((2**384)*(-B1)/N)
+
+def rnddiv2(v):
+ if v & 1:
+ v += 1
+ return v >> 1
+
+def scalar_lambda_split(k):
+ """Equivalent to secp256k1_scalar_lambda_split()."""
+ c1 = rnddiv2((k * G1) >> 383)
+ c2 = rnddiv2((k * G2) >> 383)
+ c1 = (c1 * -B1) % N
+ c2 = (c2 * -B2) % N
+ r2 = (c1 + c2) % N
+ r1 = (k + r2 * -LAMBDA) % N
+ return (r1, r2)
+
+# The result of scalar_lambda_split can depend on the representation of k (mod n).
+SPECIAL = (2**383) // G2 + 1
+assert scalar_lambda_split(SPECIAL) != scalar_lambda_split(SPECIAL + N)
+
+print(' A1 =', hex(A1))
+print(' -B1 =', hex(-B1))
+print(' A2 =', hex(A2))
+print(' -B2 =', hex(-B2))
+print(' =', hex(Z(-B2)))
+print(' -LAMBDA =', hex(-LAMBDA))
+
+print(' G1 =', hex(G1))
+print(' G2 =', hex(G2))
diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage
index 8521f07999..b200bfeae3 100644
--- a/src/secp256k1/sage/group_prover.sage
+++ b/src/secp256k1/sage/group_prover.sage
@@ -42,7 +42,7 @@
# as we assume that all constraints in it are complementary with each other.
#
# Based on the sage verification scripts used in the Explicit-Formulas Database
-# by Tanja Lange and others, see http://hyperelliptic.org/EFD
+# by Tanja Lange and others, see https://hyperelliptic.org/EFD
class fastfrac:
"""Fractions over rings."""
@@ -65,7 +65,7 @@ class fastfrac:
return self.top in I and self.bot not in I
def reduce(self,assumeZero):
- zero = self.R.ideal(map(numerator, assumeZero))
+ zero = self.R.ideal(list(map(numerator, assumeZero)))
return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot))
def __add__(self,other):
@@ -100,7 +100,7 @@ class fastfrac:
"""Multiply something else with a fraction."""
return self.__mul__(other)
- def __div__(self,other):
+ def __truediv__(self,other):
"""Divide two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top,self.bot * other)
@@ -108,6 +108,11 @@ class fastfrac:
return fastfrac(self.R,self.top * other.bot,self.bot * other.top)
return NotImplemented
+ # Compatibility wrapper for Sage versions based on Python 2
+ def __div__(self,other):
+ """Divide two fractions."""
+ return self.__truediv__(other)
+
def __pow__(self,other):
"""Compute a power of a fraction."""
if parent(other) == ZZ:
@@ -175,7 +180,7 @@ class constraints:
def conflicts(R, con):
"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
- zero = R.ideal(map(numerator, con.zero))
+ zero = R.ideal(list(map(numerator, con.zero)))
if 1 in zero:
return True
# First a cheap check whether any of the individual nonzero terms conflict on
@@ -195,7 +200,7 @@ def conflicts(R, con):
def get_nonzero_set(R, assume):
"""Calculate a simple set of nonzero expressions"""
- zero = R.ideal(map(numerator, assume.zero))
+ zero = R.ideal(list(map(numerator, assume.zero)))
nonzero = set()
for nz in map(numerator, assume.nonzero):
for (f,n) in nz.factor():
@@ -208,7 +213,7 @@ def get_nonzero_set(R, assume):
def prove_nonzero(R, exprs, assume):
"""Check whether an expression is provably nonzero, given assumptions"""
- zero = R.ideal(map(numerator, assume.zero))
+ zero = R.ideal(list(map(numerator, assume.zero)))
nonzero = get_nonzero_set(R, assume)
expl = set()
ok = True
@@ -250,7 +255,7 @@ def prove_zero(R, exprs, assume):
r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
if not r:
return (False, map(lambda x: "Possibly zero denominator: %s" % x, e))
- zero = R.ideal(map(numerator, assume.zero))
+ zero = R.ideal(list(map(numerator, assume.zero)))
nonzero = prod(x for x in assume.nonzero)
expl = []
for expr in exprs:
@@ -265,8 +270,8 @@ def describe_extra(R, assume, assumeExtra):
"""Describe what assumptions are added, given existing assumptions"""
zerox = assume.zero.copy()
zerox.update(assumeExtra.zero)
- zero = R.ideal(map(numerator, assume.zero))
- zeroextra = R.ideal(map(numerator, zerox))
+ zero = R.ideal(list(map(numerator, assume.zero)))
+ zeroextra = R.ideal(list(map(numerator, zerox)))
nonzero = get_nonzero_set(R, assume)
ret = set()
# Iterate over the extra zero expressions
diff --git a/src/secp256k1/sage/secp256k1.sage b/src/secp256k1/sage/prove_group_implementations.sage
index a97e732f7f..a97e732f7f 100644
--- a/src/secp256k1/sage/secp256k1.sage
+++ b/src/secp256k1/sage/prove_group_implementations.sage
diff --git a/src/secp256k1/sage/secp256k1_params.sage b/src/secp256k1/sage/secp256k1_params.sage
new file mode 100644
index 0000000000..4e000726ed
--- /dev/null
+++ b/src/secp256k1/sage/secp256k1_params.sage
@@ -0,0 +1,36 @@
+"""Prime order of finite field underlying secp256k1 (2^256 - 2^32 - 977)"""
+P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
+
+"""Finite field underlying secp256k1"""
+F = FiniteField(P)
+
+"""Elliptic curve secp256k1: y^2 = x^3 + 7"""
+C = EllipticCurve([F(0), F(7)])
+
+"""Base point of secp256k1"""
+G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
+
+"""Prime order of secp256k1"""
+N = C.order()
+
+"""Finite field of scalars of secp256k1"""
+Z = FiniteField(N)
+
+""" Beta value of secp256k1 non-trivial endomorphism: lambda * (x, y) = (beta * x, y)"""
+BETA = F(2)^((P-1)/3)
+
+""" Lambda value of secp256k1 non-trivial endomorphism: lambda * (x, y) = (beta * x, y)"""
+LAMBDA = Z(3)^((N-1)/3)
+
+assert is_prime(P)
+assert is_prime(N)
+
+assert BETA != F(1)
+assert BETA^3 == F(1)
+assert BETA^2 + BETA + 1 == 0
+
+assert LAMBDA != Z(1)
+assert LAMBDA^3 == Z(1)
+assert LAMBDA^2 + LAMBDA + 1 == 0
+
+assert Integer(LAMBDA)*G == C(BETA*G[0], G[1])
diff --git a/src/secp256k1/sage/weierstrass_prover.sage b/src/secp256k1/sage/weierstrass_prover.sage
index 03ef2ec901..b770c6dafe 100644
--- a/src/secp256k1/sage/weierstrass_prover.sage
+++ b/src/secp256k1/sage/weierstrass_prover.sage
@@ -175,24 +175,24 @@ laws_jacobian_weierstrass = {
def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
"""Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field"""
F = Integers(p)
- print "Formula %s on Z%i:" % (name, p)
+ print("Formula %s on Z%i:" % (name, p))
points = []
- for x in xrange(0, p):
- for y in xrange(0, p):
+ for x in range(0, p):
+ for y in range(0, p):
point = affinepoint(F(x), F(y))
r, e = concrete_verify(on_weierstrass_curve(A, B, point))
if r:
points.append(point)
- for za in xrange(1, p):
- for zb in xrange(1, p):
+ for za in range(1, p):
+ for zb in range(1, p):
for pa in points:
for pb in points:
- for ia in xrange(2):
- for ib in xrange(2):
+ for ia in range(2):
+ for ib in range(2):
pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia)
pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib)
- for branch in xrange(0, branches):
+ for branch in range(0, branches):
assumeAssert, assumeBranch, pC = formula(branch, pA, pB)
pC.X = F(pC.X)
pC.Y = F(pC.Y)
@@ -206,13 +206,13 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
r, e = concrete_verify(assumeLaw)
if r:
if match:
- print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity)
+ print(" multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity))
else:
match = True
r, e = concrete_verify(require)
if not r:
- print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e)
- print
+ print(" failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e))
+ print()
def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
@@ -242,9 +242,9 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
for key in laws_jacobian_weierstrass:
res[key] = []
- print ("Formula " + name + ":")
+ print("Formula " + name + ":")
count = 0
- for branch in xrange(branches):
+ for branch in range(branches):
assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
pC.X = lift(pC.X)
pC.Y = lift(pC.Y)
@@ -255,10 +255,10 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))
for key in res:
- print " %s:" % key
+ print(" %s:" % key)
val = res[key]
for x in val:
if x[0] is not None:
- print " branch %i: %s" % (x[1], x[0])
+ print(" branch %i: %s" % (x[1], x[0]))
- print
+ print()
diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s
index 9a5bd06721..5f68cefc46 100644
--- a/src/secp256k1/src/asm/field_10x26_arm.s
+++ b/src/secp256k1/src/asm/field_10x26_arm.s
@@ -1,9 +1,9 @@
@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm:
-/**********************************************************************
- * Copyright (c) 2014 Wladimir J. van der Laan *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Wladimir J. van der Laan *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
/*
ARM implementation of field_10x26 inner loops.
diff --git a/src/secp256k1/src/assumptions.h b/src/secp256k1/src/assumptions.h
index 77204de2b8..6dc527b288 100644
--- a/src/secp256k1/src/assumptions.h
+++ b/src/secp256k1/src/assumptions.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2020 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ASSUMPTIONS_H
#define SECP256K1_ASSUMPTIONS_H
diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h
index b0d82e89b4..6f7693cb8f 100644
--- a/src/secp256k1/src/basic-config.h
+++ b/src/secp256k1/src/basic-config.h
@@ -1,33 +1,16 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_BASIC_CONFIG_H
#define SECP256K1_BASIC_CONFIG_H
#ifdef USE_BASIC_CONFIG
-#undef USE_ASM_X86_64
-#undef USE_ECMULT_STATIC_PRECOMPUTATION
-#undef USE_EXTERNAL_ASM
-#undef USE_EXTERNAL_DEFAULT_CALLBACKS
-#undef USE_FIELD_INV_BUILTIN
-#undef USE_FIELD_INV_NUM
-#undef USE_NUM_GMP
-#undef USE_NUM_NONE
-#undef USE_SCALAR_INV_BUILTIN
-#undef USE_SCALAR_INV_NUM
-#undef USE_FORCE_WIDEMUL_INT64
-#undef USE_FORCE_WIDEMUL_INT128
-#undef ECMULT_WINDOW_SIZE
-
-#define USE_NUM_NONE 1
-#define USE_FIELD_INV_BUILTIN 1
-#define USE_SCALAR_INV_BUILTIN 1
-#define USE_WIDEMUL_64 1
#define ECMULT_WINDOW_SIZE 15
+#define ECMULT_GEN_PREC_BITS 4
#endif /* USE_BASIC_CONFIG */
diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h
index 9bfed903e0..63c55df44d 100644
--- a/src/secp256k1/src/bench.h
+++ b/src/secp256k1/src/bench.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_BENCH_H
#define SECP256K1_BENCH_H
diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c
index f099d33884..ab4b8f4244 100644
--- a/src/secp256k1/src/bench_ecdh.c
+++ b/src/secp256k1/src/bench_ecdh.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <string.h>
diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c
index facd07ef31..204e85a5dd 100644
--- a/src/secp256k1/src/bench_ecmult.c
+++ b/src/secp256k1/src/bench_ecmult.c
@@ -1,15 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2017 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2017 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <stdio.h>
#include "include/secp256k1.h"
#include "util.h"
#include "hash_impl.h"
-#include "num_impl.h"
#include "field_impl.h"
#include "group_impl.h"
#include "scalar_impl.h"
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 5f2b7a9759..73b8a24ccb 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <stdio.h>
#include "include/secp256k1.h"
@@ -10,7 +10,6 @@
#include "assumptions.h"
#include "util.h"
#include "hash_impl.h"
-#include "num_impl.h"
#include "field_impl.h"
#include "group_impl.h"
#include "scalar_impl.h"
@@ -99,15 +98,6 @@ void bench_scalar_negate(void* arg, int iters) {
}
}
-void bench_scalar_sqr(void* arg, int iters) {
- int i;
- bench_inv *data = (bench_inv*)arg;
-
- for (i = 0; i < iters; i++) {
- secp256k1_scalar_sqr(&data->scalar[0], &data->scalar[0]);
- }
-}
-
void bench_scalar_mul(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
@@ -255,26 +245,6 @@ void bench_group_add_affine_var(void* arg, int iters) {
}
}
-void bench_group_jacobi_var(void* arg, int iters) {
- int i, j = 0;
- bench_inv *data = (bench_inv*)arg;
-
- for (i = 0; i < iters; i++) {
- j += secp256k1_gej_has_quad_y_var(&data->gej[0]);
- /* Vary the Y and Z coordinates of the input (the X coordinate doesn't matter to
- secp256k1_gej_has_quad_y_var). Note that the resulting coordinates will
- generally not correspond to a point on the curve, but this is not a problem
- for the code being benchmarked here. Adding and normalizing have less
- overhead than EC operations (which could guarantee the point remains on the
- curve). */
- secp256k1_fe_add(&data->gej[0].y, &data->fe[1]);
- secp256k1_fe_add(&data->gej[0].z, &data->fe[2]);
- secp256k1_fe_normalize_var(&data->gej[0].y);
- secp256k1_fe_normalize_var(&data->gej[0].z);
- }
- CHECK(j <= iters);
-}
-
void bench_group_to_affine_var(void* arg, int iters) {
int i;
bench_inv *data = (bench_inv*)arg;
@@ -282,8 +252,10 @@ void bench_group_to_affine_var(void* arg, int iters) {
for (i = 0; i < iters; ++i) {
secp256k1_ge_set_gej_var(&data->ge[1], &data->gej[0]);
/* Use the output affine X/Y coordinates to vary the input X/Y/Z coordinates.
- Similar to bench_group_jacobi_var, this approach does not result in
- coordinates of points on the curve. */
+ Note that the resulting coordinates will generally not correspond to a point
+ on the curve, but this is not a problem for the code being benchmarked here.
+ Adding and normalizing have less overhead than EC operations (which could
+ guarantee the point remains on the curve). */
secp256k1_fe_add(&data->gej[0].x, &data->ge[1].y);
secp256k1_fe_add(&data->gej[0].y, &data->fe[2]);
secp256k1_fe_add(&data->gej[0].z, &data->ge[1].x);
@@ -369,35 +341,16 @@ void bench_context_sign(void* arg, int iters) {
}
}
-#ifndef USE_NUM_NONE
-void bench_num_jacobi(void* arg, int iters) {
- int i, j = 0;
- bench_inv *data = (bench_inv*)arg;
- secp256k1_num nx, na, norder;
-
- secp256k1_scalar_get_num(&nx, &data->scalar[0]);
- secp256k1_scalar_order_get_num(&norder);
- secp256k1_scalar_get_num(&na, &data->scalar[1]);
-
- for (i = 0; i < iters; i++) {
- j += secp256k1_num_jacobi(&nx, &norder);
- secp256k1_num_add(&nx, &nx, &na);
- }
- CHECK(j <= iters);
-}
-#endif
-
int main(int argc, char **argv) {
bench_inv data;
int iters = get_iters(20000);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, iters*100);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, iters*100);
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, iters*10);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, iters*10);
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters);
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000);
- if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, iters);
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, iters*100);
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, iters*100);
@@ -411,7 +364,6 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, iters*10);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, iters*10);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, iters*10);
- if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, iters);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "to_affine")) run_benchmark("group_to_affine_var", bench_group_to_affine_var, bench_setup, NULL, &data, 10, iters);
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, iters);
@@ -424,8 +376,5 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 1 + iters/1000);
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 1 + iters/100);
-#ifndef USE_NUM_NONE
- if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, iters*10);
-#endif
return 0;
}
diff --git a/src/secp256k1/src/bench_recover.c b/src/secp256k1/src/bench_recover.c
index e952ed1215..3f6270ce84 100644
--- a/src/secp256k1/src/bench_recover.c
+++ b/src/secp256k1/src/bench_recover.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include "include/secp256k1.h"
#include "include/secp256k1_recovery.h"
diff --git a/src/secp256k1/src/bench_schnorrsig.c b/src/secp256k1/src/bench_schnorrsig.c
index 315f5af28e..f7f591c41d 100644
--- a/src/secp256k1/src/bench_schnorrsig.c
+++ b/src/secp256k1/src/bench_schnorrsig.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <string.h>
#include <stdlib.h>
diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c
index c6b2942cc0..933f367c4b 100644
--- a/src/secp256k1/src/bench_sign.c
+++ b/src/secp256k1/src/bench_sign.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include "include/secp256k1.h"
#include "util.h"
@@ -12,11 +12,11 @@ typedef struct {
secp256k1_context* ctx;
unsigned char msg[32];
unsigned char key[32];
-} bench_sign;
+} bench_sign_data;
static void bench_sign_setup(void* arg) {
int i;
- bench_sign *data = (bench_sign*)arg;
+ bench_sign_data *data = (bench_sign_data*)arg;
for (i = 0; i < 32; i++) {
data->msg[i] = i + 1;
@@ -28,7 +28,7 @@ static void bench_sign_setup(void* arg) {
static void bench_sign_run(void* arg, int iters) {
int i;
- bench_sign *data = (bench_sign*)arg;
+ bench_sign_data *data = (bench_sign_data*)arg;
unsigned char sig[74];
for (i = 0; i < iters; i++) {
@@ -45,7 +45,7 @@ static void bench_sign_run(void* arg, int iters) {
}
int main(void) {
- bench_sign data;
+ bench_sign_data data;
int iters = get_iters(20000);
diff --git a/src/secp256k1/src/bench_verify.c b/src/secp256k1/src/bench_verify.c
index 272d3e5cc4..c56aefd369 100644
--- a/src/secp256k1/src/bench_verify.c
+++ b/src/secp256k1/src/bench_verify.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <stdio.h>
#include <string.h>
@@ -29,11 +29,11 @@ typedef struct {
#ifdef ENABLE_OPENSSL_TESTS
EC_GROUP* ec_group;
#endif
-} benchmark_verify_t;
+} bench_verify_data;
-static void benchmark_verify(void* arg, int iters) {
+static void bench_verify(void* arg, int iters) {
int i;
- benchmark_verify_t* data = (benchmark_verify_t*)arg;
+ bench_verify_data* data = (bench_verify_data*)arg;
for (i = 0; i < iters; i++) {
secp256k1_pubkey pubkey;
@@ -51,9 +51,9 @@ static void benchmark_verify(void* arg, int iters) {
}
#ifdef ENABLE_OPENSSL_TESTS
-static void benchmark_verify_openssl(void* arg, int iters) {
+static void bench_verify_openssl(void* arg, int iters) {
int i;
- benchmark_verify_t* data = (benchmark_verify_t*)arg;
+ bench_verify_data* data = (bench_verify_data*)arg;
for (i = 0; i < iters; i++) {
data->sig[data->siglen - 1] ^= (i & 0xFF);
@@ -84,7 +84,7 @@ int main(void) {
int i;
secp256k1_pubkey pubkey;
secp256k1_ecdsa_signature sig;
- benchmark_verify_t data;
+ bench_verify_data data;
int iters = get_iters(20000);
@@ -103,10 +103,10 @@ int main(void) {
data.pubkeylen = 33;
CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
- run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, iters);
+ run_benchmark("ecdsa_verify", bench_verify, NULL, NULL, &data, 10, iters);
#ifdef ENABLE_OPENSSL_TESTS
data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
- run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, iters);
+ run_benchmark("ecdsa_verify_openssl", bench_verify_openssl, NULL, NULL, &data, 10, iters);
EC_GROUP_free(data.ec_group);
#endif
diff --git a/src/secp256k1/src/ecdsa.h b/src/secp256k1/src/ecdsa.h
index 80590c7cc8..d5e54d8ce6 100644
--- a/src/secp256k1/src/ecdsa.h
+++ b/src/secp256k1/src/ecdsa.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECDSA_H
#define SECP256K1_ECDSA_H
diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h
index 5f54b59faa..156a33d112 100644
--- a/src/secp256k1/src/ecdsa_impl.h
+++ b/src/secp256k1/src/ecdsa_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECDSA_IMPL_H
diff --git a/src/secp256k1/src/eckey.h b/src/secp256k1/src/eckey.h
index b621f1e6c3..5be3a64b84 100644
--- a/src/secp256k1/src/eckey.h
+++ b/src/secp256k1/src/eckey.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECKEY_H
#define SECP256K1_ECKEY_H
diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h
index e2e72d9303..a39cb79653 100644
--- a/src/secp256k1/src/eckey_impl.h
+++ b/src/secp256k1/src/eckey_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECKEY_IMPL_H
#define SECP256K1_ECKEY_IMPL_H
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index 09e8146414..7ab617e20e 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -1,13 +1,12 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_H
#define SECP256K1_ECMULT_H
-#include "num.h"
#include "group.h"
#include "scalar.h"
#include "scratch.h"
diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h
index 03bb33257d..d6f0ea2227 100644
--- a/src/secp256k1/src/ecmult_const.h
+++ b/src/secp256k1/src/ecmult_const.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_CONST_H
#define SECP256K1_ECMULT_CONST_H
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index bb9511108b..0e1fb965cb 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_CONST_IMPL_H
#define SECP256K1_ECMULT_CONST_IMPL_H
diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h
index 30815e5aa1..539618dcbb 100644
--- a/src/secp256k1/src/ecmult_gen.h
+++ b/src/secp256k1/src/ecmult_gen.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_GEN_H
#define SECP256K1_ECMULT_GEN_H
diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h
index 30ac16518b..384a67faed 100644
--- a/src/secp256k1/src/ecmult_gen_impl.h
+++ b/src/secp256k1/src/ecmult_gen_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_ECMULT_GEN_IMPL_H
#define SECP256K1_ECMULT_GEN_IMPL_H
@@ -144,7 +144,7 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
* (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and
* "Cache Attacks and Countermeasures: the Case of AES", RSA 2006,
* by Dag Arne Osvik, Adi Shamir, and Eran Tromer
- * (http://www.tau.ac.il/~tromer/papers/cache.pdf)
+ * (https://www.tau.ac.il/~tromer/papers/cache.pdf)
*/
secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits);
}
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index a9e8b3c76c..5c2edac68f 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -1,8 +1,8 @@
-/*****************************************************************************
- * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php. *
- *****************************************************************************/
+/******************************************************************************
+ * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php. *
+ ******************************************************************************/
#ifndef SECP256K1_ECMULT_IMPL_H
#define SECP256K1_ECMULT_IMPL_H
@@ -595,11 +595,11 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
- state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
- state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A);
+ state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
+ state.pre_a_lam = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
- if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL) {
+ if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index aca1fb72c5..854aaebabc 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_H
#define SECP256K1_FIELD_H
@@ -43,13 +43,12 @@ static void secp256k1_fe_normalize_weak(secp256k1_fe *r);
/** Normalize a field element, without constant-time guarantee. */
static void secp256k1_fe_normalize_var(secp256k1_fe *r);
-/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field
- * implementation may optionally normalize the input, but this should not be relied upon. */
-static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r);
+/** Verify whether a field element represents zero i.e. would normalize to a zero value. */
+static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r);
-/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field
- * implementation may optionally normalize the input, but this should not be relied upon. */
-static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r);
+/** Verify whether a field element represents zero i.e. would normalize to a zero value,
+ * without constant-time guarantee. */
+static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r);
/** Set a field element equal to a small integer. Resulting field element is normalized. */
static void secp256k1_fe_set_int(secp256k1_fe *r, int a);
@@ -104,9 +103,6 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a);
* itself. */
static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a);
-/** Checks whether a field element is a quadratic residue. */
-static int secp256k1_fe_is_quad_var(const secp256k1_fe *a);
-
/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be
* at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */
static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a);
@@ -114,11 +110,6 @@ static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a);
/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */
static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a);
-/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be
- * at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and
- * outputs must not overlap in memory. */
-static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len);
-
/** Convert a field element to the storage type. */
static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a);
diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h
index 5ff03c8abc..9eb65607f1 100644
--- a/src/secp256k1/src/field_10x26.h
+++ b/src/secp256k1/src/field_10x26.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_H
#define SECP256K1_FIELD_REPR_H
diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h
index 651500ee8e..7a38c117f1 100644
--- a/src/secp256k1/src/field_10x26_impl.h
+++ b/src/secp256k1/src/field_10x26_impl.h
@@ -1,14 +1,15 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_IMPL_H
#define SECP256K1_FIELD_REPR_IMPL_H
#include "util.h"
#include "field.h"
+#include "modinv32_impl.h"
#ifdef VERIFY
static void secp256k1_fe_verify(const secp256k1_fe *a) {
@@ -181,7 +182,7 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) {
#endif
}
-static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) {
+static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) {
uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];
@@ -210,7 +211,7 @@ static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) {
return (z0 == 0) | (z1 == 0x3FFFFFFUL);
}
-static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) {
+static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) {
uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
uint32_t z0, z1;
uint32_t x;
@@ -1164,4 +1165,92 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se
#endif
}
+static void secp256k1_fe_from_signed30(secp256k1_fe *r, const secp256k1_modinv32_signed30 *a) {
+ const uint32_t M26 = UINT32_MAX >> 6;
+ const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4],
+ a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8];
+
+ /* The output from secp256k1_modinv32{_var} should be normalized to range [0,modulus), and
+ * have limbs in [0,2^30). The modulus is < 2^256, so the top limb must be below 2^(256-30*8).
+ */
+ VERIFY_CHECK(a0 >> 30 == 0);
+ VERIFY_CHECK(a1 >> 30 == 0);
+ VERIFY_CHECK(a2 >> 30 == 0);
+ VERIFY_CHECK(a3 >> 30 == 0);
+ VERIFY_CHECK(a4 >> 30 == 0);
+ VERIFY_CHECK(a5 >> 30 == 0);
+ VERIFY_CHECK(a6 >> 30 == 0);
+ VERIFY_CHECK(a7 >> 30 == 0);
+ VERIFY_CHECK(a8 >> 16 == 0);
+
+ r->n[0] = a0 & M26;
+ r->n[1] = (a0 >> 26 | a1 << 4) & M26;
+ r->n[2] = (a1 >> 22 | a2 << 8) & M26;
+ r->n[3] = (a2 >> 18 | a3 << 12) & M26;
+ r->n[4] = (a3 >> 14 | a4 << 16) & M26;
+ r->n[5] = (a4 >> 10 | a5 << 20) & M26;
+ r->n[6] = (a5 >> 6 | a6 << 24) & M26;
+ r->n[7] = (a6 >> 2 ) & M26;
+ r->n[8] = (a6 >> 28 | a7 << 2) & M26;
+ r->n[9] = (a7 >> 24 | a8 << 6);
+
+#ifdef VERIFY
+ r->magnitude = 1;
+ r->normalized = 1;
+ secp256k1_fe_verify(r);
+#endif
+}
+
+static void secp256k1_fe_to_signed30(secp256k1_modinv32_signed30 *r, const secp256k1_fe *a) {
+ const uint32_t M30 = UINT32_MAX >> 2;
+ const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4],
+ a5 = a->n[5], a6 = a->n[6], a7 = a->n[7], a8 = a->n[8], a9 = a->n[9];
+
+#ifdef VERIFY
+ VERIFY_CHECK(a->normalized);
+#endif
+
+ r->v[0] = (a0 | a1 << 26) & M30;
+ r->v[1] = (a1 >> 4 | a2 << 22) & M30;
+ r->v[2] = (a2 >> 8 | a3 << 18) & M30;
+ r->v[3] = (a3 >> 12 | a4 << 14) & M30;
+ r->v[4] = (a4 >> 16 | a5 << 10) & M30;
+ r->v[5] = (a5 >> 20 | a6 << 6) & M30;
+ r->v[6] = (a6 >> 24 | a7 << 2
+ | a8 << 28) & M30;
+ r->v[7] = (a8 >> 2 | a9 << 24) & M30;
+ r->v[8] = a9 >> 6;
+}
+
+static const secp256k1_modinv32_modinfo secp256k1_const_modinfo_fe = {
+ {{-0x3D1, -4, 0, 0, 0, 0, 0, 0, 65536}},
+ 0x2DDACACFL
+};
+
+static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) {
+ secp256k1_fe tmp;
+ secp256k1_modinv32_signed30 s;
+
+ tmp = *x;
+ secp256k1_fe_normalize(&tmp);
+ secp256k1_fe_to_signed30(&s, &tmp);
+ secp256k1_modinv32(&s, &secp256k1_const_modinfo_fe);
+ secp256k1_fe_from_signed30(r, &s);
+
+ VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
+}
+
+static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
+ secp256k1_fe tmp;
+ secp256k1_modinv32_signed30 s;
+
+ tmp = *x;
+ secp256k1_fe_normalize_var(&tmp);
+ secp256k1_fe_to_signed30(&s, &tmp);
+ secp256k1_modinv32_var(&s, &secp256k1_const_modinfo_fe);
+ secp256k1_fe_from_signed30(r, &s);
+
+ VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
+}
+
#endif /* SECP256K1_FIELD_REPR_IMPL_H */
diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h
index 6a068484c2..50ee3f9ec9 100644
--- a/src/secp256k1/src/field_5x52.h
+++ b/src/secp256k1/src/field_5x52.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_H
#define SECP256K1_FIELD_REPR_H
diff --git a/src/secp256k1/src/field_5x52_asm_impl.h b/src/secp256k1/src/field_5x52_asm_impl.h
index 1fc3171f6b..a2118044ab 100644
--- a/src/secp256k1/src/field_5x52_asm_impl.h
+++ b/src/secp256k1/src/field_5x52_asm_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
/**
* Changelog:
diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h
index 71a38f915b..60ded927f6 100644
--- a/src/secp256k1/src/field_5x52_impl.h
+++ b/src/secp256k1/src/field_5x52_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_REPR_IMPL_H
#define SECP256K1_FIELD_REPR_IMPL_H
@@ -13,6 +13,7 @@
#include "util.h"
#include "field.h"
+#include "modinv64_impl.h"
#if defined(USE_ASM_X86_64)
#include "field_5x52_asm_impl.h"
@@ -161,7 +162,7 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) {
#endif
}
-static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) {
+static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) {
uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
/* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */
@@ -184,7 +185,7 @@ static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) {
return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL);
}
-static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) {
+static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) {
uint64_t t0, t1, t2, t3, t4;
uint64_t z0, z1;
uint64_t x;
@@ -498,4 +499,80 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se
#endif
}
+static void secp256k1_fe_from_signed62(secp256k1_fe *r, const secp256k1_modinv64_signed62 *a) {
+ const uint64_t M52 = UINT64_MAX >> 12;
+ const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4];
+
+ /* The output from secp256k1_modinv64{_var} should be normalized to range [0,modulus), and
+ * have limbs in [0,2^62). The modulus is < 2^256, so the top limb must be below 2^(256-62*4).
+ */
+ VERIFY_CHECK(a0 >> 62 == 0);
+ VERIFY_CHECK(a1 >> 62 == 0);
+ VERIFY_CHECK(a2 >> 62 == 0);
+ VERIFY_CHECK(a3 >> 62 == 0);
+ VERIFY_CHECK(a4 >> 8 == 0);
+
+ r->n[0] = a0 & M52;
+ r->n[1] = (a0 >> 52 | a1 << 10) & M52;
+ r->n[2] = (a1 >> 42 | a2 << 20) & M52;
+ r->n[3] = (a2 >> 32 | a3 << 30) & M52;
+ r->n[4] = (a3 >> 22 | a4 << 40);
+
+#ifdef VERIFY
+ r->magnitude = 1;
+ r->normalized = 1;
+ secp256k1_fe_verify(r);
+#endif
+}
+
+static void secp256k1_fe_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_fe *a) {
+ const uint64_t M62 = UINT64_MAX >> 2;
+ const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4];
+
+#ifdef VERIFY
+ VERIFY_CHECK(a->normalized);
+#endif
+
+ r->v[0] = (a0 | a1 << 52) & M62;
+ r->v[1] = (a1 >> 10 | a2 << 42) & M62;
+ r->v[2] = (a2 >> 20 | a3 << 32) & M62;
+ r->v[3] = (a3 >> 30 | a4 << 22) & M62;
+ r->v[4] = a4 >> 40;
+}
+
+static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_fe = {
+ {{-0x1000003D1LL, 0, 0, 0, 256}},
+ 0x27C7F6E22DDACACFLL
+};
+
+static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) {
+ secp256k1_fe tmp;
+ secp256k1_modinv64_signed62 s;
+
+ tmp = *x;
+ secp256k1_fe_normalize(&tmp);
+ secp256k1_fe_to_signed62(&s, &tmp);
+ secp256k1_modinv64(&s, &secp256k1_const_modinfo_fe);
+ secp256k1_fe_from_signed62(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
+#endif
+}
+
+static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) {
+ secp256k1_fe tmp;
+ secp256k1_modinv64_signed62 s;
+
+ tmp = *x;
+ secp256k1_fe_normalize_var(&tmp);
+ secp256k1_fe_to_signed62(&s, &tmp);
+ secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_fe);
+ secp256k1_fe_from_signed62(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp));
+#endif
+}
+
#endif /* SECP256K1_FIELD_REPR_IMPL_H */
diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h
index bcbfb92ac2..314002ee39 100644
--- a/src/secp256k1/src/field_5x52_int128_impl.h
+++ b/src/secp256k1/src/field_5x52_int128_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H
#define SECP256K1_FIELD_INNER5X52_IMPL_H
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 18e4d2f30e..374284a1f4 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_FIELD_IMPL_H
#define SECP256K1_FIELD_IMPL_H
@@ -12,7 +12,6 @@
#endif
#include "util.h"
-#include "num.h"
#if defined(SECP256K1_WIDEMUL_INT128)
#include "field_5x52_impl.h"
@@ -136,185 +135,6 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
return secp256k1_fe_equal(&t1, a);
}
-static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) {
- secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
- int j;
-
- /** The binary representation of (p - 2) has 5 blocks of 1s, with lengths in
- * { 1, 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
- * [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
- */
-
- secp256k1_fe_sqr(&x2, a);
- secp256k1_fe_mul(&x2, &x2, a);
-
- secp256k1_fe_sqr(&x3, &x2);
- secp256k1_fe_mul(&x3, &x3, a);
-
- x6 = x3;
- for (j=0; j<3; j++) {
- secp256k1_fe_sqr(&x6, &x6);
- }
- secp256k1_fe_mul(&x6, &x6, &x3);
-
- x9 = x6;
- for (j=0; j<3; j++) {
- secp256k1_fe_sqr(&x9, &x9);
- }
- secp256k1_fe_mul(&x9, &x9, &x3);
-
- x11 = x9;
- for (j=0; j<2; j++) {
- secp256k1_fe_sqr(&x11, &x11);
- }
- secp256k1_fe_mul(&x11, &x11, &x2);
-
- x22 = x11;
- for (j=0; j<11; j++) {
- secp256k1_fe_sqr(&x22, &x22);
- }
- secp256k1_fe_mul(&x22, &x22, &x11);
-
- x44 = x22;
- for (j=0; j<22; j++) {
- secp256k1_fe_sqr(&x44, &x44);
- }
- secp256k1_fe_mul(&x44, &x44, &x22);
-
- x88 = x44;
- for (j=0; j<44; j++) {
- secp256k1_fe_sqr(&x88, &x88);
- }
- secp256k1_fe_mul(&x88, &x88, &x44);
-
- x176 = x88;
- for (j=0; j<88; j++) {
- secp256k1_fe_sqr(&x176, &x176);
- }
- secp256k1_fe_mul(&x176, &x176, &x88);
-
- x220 = x176;
- for (j=0; j<44; j++) {
- secp256k1_fe_sqr(&x220, &x220);
- }
- secp256k1_fe_mul(&x220, &x220, &x44);
-
- x223 = x220;
- for (j=0; j<3; j++) {
- secp256k1_fe_sqr(&x223, &x223);
- }
- secp256k1_fe_mul(&x223, &x223, &x3);
-
- /* The final result is then assembled using a sliding window over the blocks. */
-
- t1 = x223;
- for (j=0; j<23; j++) {
- secp256k1_fe_sqr(&t1, &t1);
- }
- secp256k1_fe_mul(&t1, &t1, &x22);
- for (j=0; j<5; j++) {
- secp256k1_fe_sqr(&t1, &t1);
- }
- secp256k1_fe_mul(&t1, &t1, a);
- for (j=0; j<3; j++) {
- secp256k1_fe_sqr(&t1, &t1);
- }
- secp256k1_fe_mul(&t1, &t1, &x2);
- for (j=0; j<2; j++) {
- secp256k1_fe_sqr(&t1, &t1);
- }
- secp256k1_fe_mul(r, a, &t1);
-}
-
-static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
-#if defined(USE_FIELD_INV_BUILTIN)
- secp256k1_fe_inv(r, a);
-#elif defined(USE_FIELD_INV_NUM)
- secp256k1_num n, m;
- static const secp256k1_fe negone = SECP256K1_FE_CONST(
- 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL,
- 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, 0xFFFFFC2EUL
- );
- /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
- static const unsigned char prime[32] = {
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F
- };
- unsigned char b[32];
- int res;
- secp256k1_fe c = *a;
- secp256k1_fe_normalize_var(&c);
- secp256k1_fe_get_b32(b, &c);
- secp256k1_num_set_bin(&n, b, 32);
- secp256k1_num_set_bin(&m, prime, 32);
- secp256k1_num_mod_inverse(&n, &n, &m);
- secp256k1_num_get_bin(b, 32, &n);
- res = secp256k1_fe_set_b32(r, b);
- (void)res;
- VERIFY_CHECK(res);
- /* Verify the result is the (unique) valid inverse using non-GMP code. */
- secp256k1_fe_mul(&c, &c, r);
- secp256k1_fe_add(&c, &negone);
- CHECK(secp256k1_fe_normalizes_to_zero_var(&c));
-#else
-#error "Please select field inverse implementation"
-#endif
-}
-
-static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) {
- secp256k1_fe u;
- size_t i;
- if (len < 1) {
- return;
- }
-
- VERIFY_CHECK((r + len <= a) || (a + len <= r));
-
- r[0] = a[0];
-
- i = 0;
- while (++i < len) {
- secp256k1_fe_mul(&r[i], &r[i - 1], &a[i]);
- }
-
- secp256k1_fe_inv_var(&u, &r[--i]);
-
- while (i > 0) {
- size_t j = i--;
- secp256k1_fe_mul(&r[j], &r[i], &u);
- secp256k1_fe_mul(&u, &u, &a[j]);
- }
-
- r[0] = u;
-}
-
-static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
-#ifndef USE_NUM_NONE
- unsigned char b[32];
- secp256k1_num n;
- secp256k1_num m;
- /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
- static const unsigned char prime[32] = {
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F
- };
-
- secp256k1_fe c = *a;
- secp256k1_fe_normalize_var(&c);
- secp256k1_fe_get_b32(b, &c);
- secp256k1_num_set_bin(&n, b, 32);
- secp256k1_num_set_bin(&m, prime, 32);
- return secp256k1_num_jacobi(&n, &m) >= 0;
-#else
- secp256k1_fe r;
- return secp256k1_fe_sqrt(&r, a);
-#endif
-}
-
static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
#endif /* SECP256K1_FIELD_IMPL_H */
diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c
index 8b7729aee4..024c557261 100644
--- a/src/secp256k1/src/gen_context.c
+++ b/src/secp256k1/src/gen_context.c
@@ -1,16 +1,17 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-// Autotools creates libsecp256k1-config.h, of which ECMULT_GEN_PREC_BITS is needed.
-// ifndef guard so downstream users can define their own if they do not use autotools.
+/* Autotools creates libsecp256k1-config.h, of which ECMULT_GEN_PREC_BITS is needed.
+ ifndef guard so downstream users can define their own if they do not use autotools. */
#if !defined(ECMULT_GEN_PREC_BITS)
#include "libsecp256k1-config.h"
#endif
-#define USE_BASIC_CONFIG 1
-#include "basic-config.h"
+
+/* We can't require the precomputed tables when creating them. */
+#undef USE_ECMULT_STATIC_PRECOMPUTATION
#include "include/secp256k1.h"
#include "assumptions.h"
@@ -47,8 +48,8 @@ int main(int argc, char **argv) {
return -1;
}
- fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
- fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
+ fprintf(fp, "#ifndef SECP256K1_ECMULT_STATIC_CONTEXT_H\n");
+ fprintf(fp, "#define SECP256K1_ECMULT_STATIC_CONTEXT_H\n");
fprintf(fp, "#include \"src/group.h\"\n");
fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n");
fprintf(fp, "#if ECMULT_GEN_PREC_N != %d || ECMULT_GEN_PREC_G != %d\n", ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G);
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index 36e39ecf0f..b9cd334dae 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -1,13 +1,12 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_GROUP_H
#define SECP256K1_GROUP_H
-#include "num.h"
#include "field.h"
/** A group element of the secp256k1 curve, in affine coordinates. */
@@ -43,12 +42,6 @@ typedef struct {
/** Set a group element equal to the point with given X and Y coordinates */
static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y);
-/** Set a group element (affine) equal to the point with the given X coordinate
- * and a Y coordinate that is a quadratic residue modulo p. The return value
- * is true iff a coordinate with the given X coordinate exists.
- */
-static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x);
-
/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness
* for Y. Return value indicates whether the result is valid. */
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd);
@@ -62,9 +55,12 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a);
/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */
static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
-/** Set a group element equal to another which is given in jacobian coordinates */
+/** Set a group element equal to another which is given in jacobian coordinates. Constant time. */
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
+/** Set a group element equal to another which is given in jacobian coordinates. */
+static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a);
+
/** Set a batch of group elements equal to the inputs given in jacobian coordinates */
static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len);
@@ -93,9 +89,6 @@ static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a);
/** Check whether a group element is the point at infinity. */
static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
-/** Check whether a group element's y coordinate is a quadratic residue. */
-static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
-
/** Set r equal to the double of a. Constant time. */
static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a);
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index a5fbc91a0f..19ebd8f44e 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -1,13 +1,12 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_GROUP_IMPL_H
#define SECP256K1_GROUP_IMPL_H
-#include "num.h"
#include "field.h"
#include "group.h"
@@ -207,18 +206,14 @@ static void secp256k1_ge_clear(secp256k1_ge *r) {
secp256k1_fe_clear(&r->y);
}
-static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) {
+static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
secp256k1_fe x2, x3;
r->x = *x;
secp256k1_fe_sqr(&x2, x);
secp256k1_fe_mul(&x3, x, &x2);
r->infinity = 0;
secp256k1_fe_add(&x3, &secp256k1_fe_const_b);
- return secp256k1_fe_sqrt(&r->y, &x3);
-}
-
-static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
- if (!secp256k1_ge_set_xquad(r, x)) {
+ if (!secp256k1_fe_sqrt(&r->y, &x3)) {
return 0;
}
secp256k1_fe_normalize_var(&r->y);
@@ -591,7 +586,7 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const
secp256k1_fe_cmov(&n, &m, degenerate); /* n = M^3 * Malt (2) */
secp256k1_fe_sqr(&t, &rr_alt); /* t = Ralt^2 (1) */
secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Malt*Z (1) */
- infinity = secp256k1_fe_normalizes_to_zero(&r->z) * (1 - a->infinity);
+ infinity = secp256k1_fe_normalizes_to_zero(&r->z) & ~a->infinity;
secp256k1_fe_mul_int(&r->z, 2); /* r->z = Z3 = 2*Malt*Z (2) */
secp256k1_fe_negate(&q, &q, 1); /* q = -Q (2) */
secp256k1_fe_add(&t, &q); /* t = Ralt^2-Q (3) */
@@ -655,26 +650,12 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
secp256k1_fe_mul(&r->x, &r->x, &beta);
}
-static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
- secp256k1_fe yz;
-
- if (a->infinity) {
- return 0;
- }
-
- /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as
- * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z
- is */
- secp256k1_fe_mul(&yz, &a->y, &a->z);
- return secp256k1_fe_is_quad_var(&yz);
-}
-
static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) {
#ifdef EXHAUSTIVE_TEST_ORDER
secp256k1_gej out;
int i;
- /* A very simple EC multiplication ladder that avoids a dependecy on ecmult. */
+ /* A very simple EC multiplication ladder that avoids a dependency on ecmult. */
secp256k1_gej_set_infinity(&out);
for (i = 0; i < 32; ++i) {
secp256k1_gej_double_var(&out, &out, NULL);
diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h
index de26e4b89f..0947a09694 100644
--- a/src/secp256k1/src/hash.h
+++ b/src/secp256k1/src/hash.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_HASH_H
#define SECP256K1_HASH_H
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index 409772587b..f8cd3a1634 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_HASH_IMPL_H
#define SECP256K1_HASH_IMPL_H
diff --git a/src/secp256k1/src/modinv32.h b/src/secp256k1/src/modinv32.h
new file mode 100644
index 0000000000..0efdda9ab5
--- /dev/null
+++ b/src/secp256k1/src/modinv32.h
@@ -0,0 +1,42 @@
+/***********************************************************************
+ * Copyright (c) 2020 Peter Dettman *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_MODINV32_H
+#define SECP256K1_MODINV32_H
+
+#if defined HAVE_CONFIG_H
+#include "libsecp256k1-config.h"
+#endif
+
+#include "util.h"
+
+/* A signed 30-bit limb representation of integers.
+ *
+ * Its value is sum(v[i] * 2^(30*i), i=0..8). */
+typedef struct {
+ int32_t v[9];
+} secp256k1_modinv32_signed30;
+
+typedef struct {
+ /* The modulus in signed30 notation, must be odd and in [3, 2^256]. */
+ secp256k1_modinv32_signed30 modulus;
+
+ /* modulus^{-1} mod 2^30 */
+ uint32_t modulus_inv30;
+} secp256k1_modinv32_modinfo;
+
+/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus).
+ * If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of
+ * x and modulus must be 1). These rules are automatically satisfied if the modulus is prime.
+ *
+ * On output, all of x's limbs will be in [0, 2^30).
+ */
+static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo);
+
+/* Same as secp256k1_modinv32_var, but constant time in x (not in the modulus). */
+static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo);
+
+#endif /* SECP256K1_MODINV32_H */
diff --git a/src/secp256k1/src/modinv32_impl.h b/src/secp256k1/src/modinv32_impl.h
new file mode 100644
index 0000000000..661c5fc04c
--- /dev/null
+++ b/src/secp256k1/src/modinv32_impl.h
@@ -0,0 +1,587 @@
+/***********************************************************************
+ * Copyright (c) 2020 Peter Dettman *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_MODINV32_IMPL_H
+#define SECP256K1_MODINV32_IMPL_H
+
+#include "modinv32.h"
+
+#include "util.h"
+
+#include <stdlib.h>
+
+/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and
+ * modular inversion" by Daniel J. Bernstein and Bo-Yin Yang.
+ *
+ * For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an
+ * implementation for N=30, using 30-bit signed limbs represented as int32_t.
+ */
+
+#ifdef VERIFY
+static const secp256k1_modinv32_signed30 SECP256K1_SIGNED30_ONE = {{1}};
+
+/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^30). */
+static void secp256k1_modinv32_mul_30(secp256k1_modinv32_signed30 *r, const secp256k1_modinv32_signed30 *a, int alen, int32_t factor) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ int64_t c = 0;
+ int i;
+ for (i = 0; i < 8; ++i) {
+ if (i < alen) c += (int64_t)a->v[i] * factor;
+ r->v[i] = (int32_t)c & M30; c >>= 30;
+ }
+ if (8 < alen) c += (int64_t)a->v[8] * factor;
+ VERIFY_CHECK(c == (int32_t)c);
+ r->v[8] = (int32_t)c;
+}
+
+/* Return -1 for a<b*factor, 0 for a==b*factor, 1 for a>b*factor. A consists of alen limbs; b has 9. */
+static int secp256k1_modinv32_mul_cmp_30(const secp256k1_modinv32_signed30 *a, int alen, const secp256k1_modinv32_signed30 *b, int32_t factor) {
+ int i;
+ secp256k1_modinv32_signed30 am, bm;
+ secp256k1_modinv32_mul_30(&am, a, alen, 1); /* Normalize all but the top limb of a. */
+ secp256k1_modinv32_mul_30(&bm, b, 9, factor);
+ for (i = 0; i < 8; ++i) {
+ /* Verify that all but the top limb of a and b are normalized. */
+ VERIFY_CHECK(am.v[i] >> 30 == 0);
+ VERIFY_CHECK(bm.v[i] >> 30 == 0);
+ }
+ for (i = 8; i >= 0; --i) {
+ if (am.v[i] < bm.v[i]) return -1;
+ if (am.v[i] > bm.v[i]) return 1;
+ }
+ return 0;
+}
+#endif
+
+/* Take as input a signed30 number in range (-2*modulus,modulus), and add a multiple of the modulus
+ * to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the
+ * process. The input must have limbs in range (-2^30,2^30). The output will have limbs in range
+ * [0,2^30). */
+static void secp256k1_modinv32_normalize_30(secp256k1_modinv32_signed30 *r, int32_t sign, const secp256k1_modinv32_modinfo *modinfo) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ int32_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4],
+ r5 = r->v[5], r6 = r->v[6], r7 = r->v[7], r8 = r->v[8];
+ int32_t cond_add, cond_negate;
+
+#ifdef VERIFY
+ /* Verify that all limbs are in range (-2^30,2^30). */
+ int i;
+ for (i = 0; i < 9; ++i) {
+ VERIFY_CHECK(r->v[i] >= -M30);
+ VERIFY_CHECK(r->v[i] <= M30);
+ }
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, -2) > 0); /* r > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */
+#endif
+
+ /* In a first step, add the modulus if the input is negative, and then negate if requested.
+ * This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input
+ * limbs are in range (-2^30,2^30), this cannot overflow an int32_t. Note that the right
+ * shifts below are signed sign-extending shifts (see assumptions.h for tests that that is
+ * indeed the behavior of the right shift operator). */
+ cond_add = r8 >> 31;
+ r0 += modinfo->modulus.v[0] & cond_add;
+ r1 += modinfo->modulus.v[1] & cond_add;
+ r2 += modinfo->modulus.v[2] & cond_add;
+ r3 += modinfo->modulus.v[3] & cond_add;
+ r4 += modinfo->modulus.v[4] & cond_add;
+ r5 += modinfo->modulus.v[5] & cond_add;
+ r6 += modinfo->modulus.v[6] & cond_add;
+ r7 += modinfo->modulus.v[7] & cond_add;
+ r8 += modinfo->modulus.v[8] & cond_add;
+ cond_negate = sign >> 31;
+ r0 = (r0 ^ cond_negate) - cond_negate;
+ r1 = (r1 ^ cond_negate) - cond_negate;
+ r2 = (r2 ^ cond_negate) - cond_negate;
+ r3 = (r3 ^ cond_negate) - cond_negate;
+ r4 = (r4 ^ cond_negate) - cond_negate;
+ r5 = (r5 ^ cond_negate) - cond_negate;
+ r6 = (r6 ^ cond_negate) - cond_negate;
+ r7 = (r7 ^ cond_negate) - cond_negate;
+ r8 = (r8 ^ cond_negate) - cond_negate;
+ /* Propagate the top bits, to bring limbs back to range (-2^30,2^30). */
+ r1 += r0 >> 30; r0 &= M30;
+ r2 += r1 >> 30; r1 &= M30;
+ r3 += r2 >> 30; r2 &= M30;
+ r4 += r3 >> 30; r3 &= M30;
+ r5 += r4 >> 30; r4 &= M30;
+ r6 += r5 >> 30; r5 &= M30;
+ r7 += r6 >> 30; r6 &= M30;
+ r8 += r7 >> 30; r7 &= M30;
+
+ /* In a second step add the modulus again if the result is still negative, bringing r to range
+ * [0,modulus). */
+ cond_add = r8 >> 31;
+ r0 += modinfo->modulus.v[0] & cond_add;
+ r1 += modinfo->modulus.v[1] & cond_add;
+ r2 += modinfo->modulus.v[2] & cond_add;
+ r3 += modinfo->modulus.v[3] & cond_add;
+ r4 += modinfo->modulus.v[4] & cond_add;
+ r5 += modinfo->modulus.v[5] & cond_add;
+ r6 += modinfo->modulus.v[6] & cond_add;
+ r7 += modinfo->modulus.v[7] & cond_add;
+ r8 += modinfo->modulus.v[8] & cond_add;
+ /* And propagate again. */
+ r1 += r0 >> 30; r0 &= M30;
+ r2 += r1 >> 30; r1 &= M30;
+ r3 += r2 >> 30; r2 &= M30;
+ r4 += r3 >> 30; r3 &= M30;
+ r5 += r4 >> 30; r4 &= M30;
+ r6 += r5 >> 30; r5 &= M30;
+ r7 += r6 >> 30; r6 &= M30;
+ r8 += r7 >> 30; r7 &= M30;
+
+ r->v[0] = r0;
+ r->v[1] = r1;
+ r->v[2] = r2;
+ r->v[3] = r3;
+ r->v[4] = r4;
+ r->v[5] = r5;
+ r->v[6] = r6;
+ r->v[7] = r7;
+ r->v[8] = r8;
+
+#ifdef VERIFY
+ VERIFY_CHECK(r0 >> 30 == 0);
+ VERIFY_CHECK(r1 >> 30 == 0);
+ VERIFY_CHECK(r2 >> 30 == 0);
+ VERIFY_CHECK(r3 >> 30 == 0);
+ VERIFY_CHECK(r4 >> 30 == 0);
+ VERIFY_CHECK(r5 >> 30 == 0);
+ VERIFY_CHECK(r6 >> 30 == 0);
+ VERIFY_CHECK(r7 >> 30 == 0);
+ VERIFY_CHECK(r8 >> 30 == 0);
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 0) >= 0); /* r >= 0 */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */
+#endif
+}
+
+/* Data type for transition matrices (see section 3 of explanation).
+ *
+ * t = [ u v ]
+ * [ q r ]
+ */
+typedef struct {
+ int32_t u, v, q, r;
+} secp256k1_modinv32_trans2x2;
+
+/* Compute the transition matrix and zeta for 30 divsteps.
+ *
+ * Input: zeta: initial zeta
+ * f0: bottom limb of initial f
+ * g0: bottom limb of initial g
+ * Output: t: transition matrix
+ * Return: final zeta
+ *
+ * Implements the divsteps_n_matrix function from the explanation.
+ */
+static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) {
+ /* u,v,q,r are the elements of the transformation matrix being built up,
+ * starting with the identity matrix. Semantically they are signed integers
+ * in range [-2^30,2^30], but here represented as unsigned mod 2^32. This
+ * permits left shifting (which is UB for negative numbers). The range
+ * being inside [-2^31,2^31) means that casting to signed works correctly.
+ */
+ uint32_t u = 1, v = 0, q = 0, r = 1;
+ uint32_t c1, c2, f = f0, g = g0, x, y, z;
+ int i;
+
+ for (i = 0; i < 30; ++i) {
+ VERIFY_CHECK((f & 1) == 1); /* f must always be odd */
+ VERIFY_CHECK((u * f0 + v * g0) == f << i);
+ VERIFY_CHECK((q * f0 + r * g0) == g << i);
+ /* Compute conditional masks for (zeta < 0) and for (g & 1). */
+ c1 = zeta >> 31;
+ c2 = -(g & 1);
+ /* Compute x,y,z, conditionally negated versions of f,u,v. */
+ x = (f ^ c1) - c1;
+ y = (u ^ c1) - c1;
+ z = (v ^ c1) - c1;
+ /* Conditionally add x,y,z to g,q,r. */
+ g += x & c2;
+ q += y & c2;
+ r += z & c2;
+ /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */
+ c1 &= c2;
+ /* Conditionally change zeta into -zeta-2 or zeta-1. */
+ zeta = (zeta ^ c1) - 1;
+ /* Conditionally add g,q,r to f,u,v. */
+ f += g & c1;
+ u += q & c1;
+ v += r & c1;
+ /* Shifts */
+ g >>= 1;
+ u <<= 1;
+ v <<= 1;
+ /* Bounds on zeta that follow from the bounds on iteration count (max 20*30 divsteps). */
+ VERIFY_CHECK(zeta >= -601 && zeta <= 601);
+ }
+ /* Return data in t and return value. */
+ t->u = (int32_t)u;
+ t->v = (int32_t)v;
+ t->q = (int32_t)q;
+ t->r = (int32_t)r;
+ /* The determinant of t must be a power of two. This guarantees that multiplication with t
+ * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
+ * will be divided out again). As each divstep's individual matrix has determinant 2, the
+ * aggregate of 30 of them will have determinant 2^30. */
+ VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30);
+ return zeta;
+}
+
+/* Compute the transition matrix and eta for 30 divsteps (variable time).
+ *
+ * Input: eta: initial eta
+ * f0: bottom limb of initial f
+ * g0: bottom limb of initial g
+ * Output: t: transition matrix
+ * Return: final eta
+ *
+ * Implements the divsteps_n_matrix_var function from the explanation.
+ */
+static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) {
+ /* inv256[i] = -(2*i+1)^-1 (mod 256) */
+ static const uint8_t inv256[128] = {
+ 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59,
+ 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31,
+ 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89,
+ 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61,
+ 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9,
+ 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91,
+ 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9,
+ 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1,
+ 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19,
+ 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1,
+ 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01
+ };
+
+ /* Transformation matrix; see comments in secp256k1_modinv32_divsteps_30. */
+ uint32_t u = 1, v = 0, q = 0, r = 1;
+ uint32_t f = f0, g = g0, m;
+ uint16_t w;
+ int i = 30, limit, zeros;
+
+ for (;;) {
+ /* Use a sentinel bit to count zeros only up to i. */
+ zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i));
+ /* Perform zeros divsteps at once; they all just divide g by two. */
+ g >>= zeros;
+ u <<= zeros;
+ v <<= zeros;
+ eta -= zeros;
+ i -= zeros;
+ /* We're done once we've done 30 divsteps. */
+ if (i == 0) break;
+ VERIFY_CHECK((f & 1) == 1);
+ VERIFY_CHECK((g & 1) == 1);
+ VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i));
+ VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i));
+ /* Bounds on eta that follow from the bounds on iteration count (max 25*30 divsteps). */
+ VERIFY_CHECK(eta >= -751 && eta <= 751);
+ /* If eta is negative, negate it and replace f,g with g,-f. */
+ if (eta < 0) {
+ uint32_t tmp;
+ eta = -eta;
+ tmp = f; f = g; g = -tmp;
+ tmp = u; u = q; q = -tmp;
+ tmp = v; v = r; r = -tmp;
+ }
+ /* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more
+ * than i can be cancelled out (as we'd be done before that point), and no more than eta+1
+ * can be done as its sign will flip once that happens. */
+ limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
+ /* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */
+ VERIFY_CHECK(limit > 0 && limit <= 30);
+ m = (UINT32_MAX >> (32 - limit)) & 255U;
+ /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */
+ w = (g * inv256[(f >> 1) & 127]) & m;
+ /* Do so. */
+ g += f * w;
+ q += u * w;
+ r += v * w;
+ VERIFY_CHECK((g & m) == 0);
+ }
+ /* Return data in t and return value. */
+ t->u = (int32_t)u;
+ t->v = (int32_t)v;
+ t->q = (int32_t)q;
+ t->r = (int32_t)r;
+ /* The determinant of t must be a power of two. This guarantees that multiplication with t
+ * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
+ * will be divided out again). As each divstep's individual matrix has determinant 2, the
+ * aggregate of 30 of them will have determinant 2^30. */
+ VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30);
+ return eta;
+}
+
+/* Compute (t/2^30) * [d, e] mod modulus, where t is a transition matrix for 30 divsteps.
+ *
+ * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range
+ * (-2^30,2^30).
+ *
+ * This implements the update_de function from the explanation.
+ */
+static void secp256k1_modinv32_update_de_30(secp256k1_modinv32_signed30 *d, secp256k1_modinv32_signed30 *e, const secp256k1_modinv32_trans2x2 *t, const secp256k1_modinv32_modinfo* modinfo) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ const int32_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int32_t di, ei, md, me, sd, se;
+ int64_t cd, ce;
+ int i;
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */
+ VERIFY_CHECK((labs(u) + labs(v)) >= 0); /* |u|+|v| doesn't overflow */
+ VERIFY_CHECK((labs(q) + labs(r)) >= 0); /* |q|+|r| doesn't overflow */
+ VERIFY_CHECK((labs(u) + labs(v)) <= M30 + 1); /* |u|+|v| <= 2^30 */
+ VERIFY_CHECK((labs(q) + labs(r)) <= M30 + 1); /* |q|+|r| <= 2^30 */
+#endif
+ /* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */
+ sd = d->v[8] >> 31;
+ se = e->v[8] >> 31;
+ md = (u & sd) + (v & se);
+ me = (q & sd) + (r & se);
+ /* Begin computing t*[d,e]. */
+ di = d->v[0];
+ ei = e->v[0];
+ cd = (int64_t)u * di + (int64_t)v * ei;
+ ce = (int64_t)q * di + (int64_t)r * ei;
+ /* Correct md,me so that t*[d,e]+modulus*[md,me] has 30 zero bottom bits. */
+ md -= (modinfo->modulus_inv30 * (uint32_t)cd + md) & M30;
+ me -= (modinfo->modulus_inv30 * (uint32_t)ce + me) & M30;
+ /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */
+ cd += (int64_t)modinfo->modulus.v[0] * md;
+ ce += (int64_t)modinfo->modulus.v[0] * me;
+ /* Verify that the low 30 bits of the computation are indeed zero, and then throw them away. */
+ VERIFY_CHECK(((int32_t)cd & M30) == 0); cd >>= 30;
+ VERIFY_CHECK(((int32_t)ce & M30) == 0); ce >>= 30;
+ /* Now iteratively compute limb i=1..8 of t*[d,e]+modulus*[md,me], and store them in output
+ * limb i-1 (shifting down by 30 bits). */
+ for (i = 1; i < 9; ++i) {
+ di = d->v[i];
+ ei = e->v[i];
+ cd += (int64_t)u * di + (int64_t)v * ei;
+ ce += (int64_t)q * di + (int64_t)r * ei;
+ cd += (int64_t)modinfo->modulus.v[i] * md;
+ ce += (int64_t)modinfo->modulus.v[i] * me;
+ d->v[i - 1] = (int32_t)cd & M30; cd >>= 30;
+ e->v[i - 1] = (int32_t)ce & M30; ce >>= 30;
+ }
+ /* What remains is limb 9 of t*[d,e]+modulus*[md,me]; store it as output limb 8. */
+ d->v[8] = (int32_t)cd;
+ e->v[8] = (int32_t)ce;
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */
+#endif
+}
+
+/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps.
+ *
+ * This implements the update_fg function from the explanation.
+ */
+static void secp256k1_modinv32_update_fg_30(secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ const int32_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int32_t fi, gi;
+ int64_t cf, cg;
+ int i;
+ /* Start computing t*[f,g]. */
+ fi = f->v[0];
+ gi = g->v[0];
+ cf = (int64_t)u * fi + (int64_t)v * gi;
+ cg = (int64_t)q * fi + (int64_t)r * gi;
+ /* Verify that the bottom 30 bits of the result are zero, and then throw them away. */
+ VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30;
+ VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30;
+ /* Now iteratively compute limb i=1..8 of t*[f,g], and store them in output limb i-1 (shifting
+ * down by 30 bits). */
+ for (i = 1; i < 9; ++i) {
+ fi = f->v[i];
+ gi = g->v[i];
+ cf += (int64_t)u * fi + (int64_t)v * gi;
+ cg += (int64_t)q * fi + (int64_t)r * gi;
+ f->v[i - 1] = (int32_t)cf & M30; cf >>= 30;
+ g->v[i - 1] = (int32_t)cg & M30; cg >>= 30;
+ }
+ /* What remains is limb 9 of t*[f,g]; store it as output limb 8. */
+ f->v[8] = (int32_t)cf;
+ g->v[8] = (int32_t)cg;
+}
+
+/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps.
+ *
+ * Version that operates on a variable number of limbs in f and g.
+ *
+ * This implements the update_fg function from the explanation in modinv64_impl.h.
+ */
+static void secp256k1_modinv32_update_fg_30_var(int len, secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) {
+ const int32_t M30 = (int32_t)(UINT32_MAX >> 2);
+ const int32_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int32_t fi, gi;
+ int64_t cf, cg;
+ int i;
+ VERIFY_CHECK(len > 0);
+ /* Start computing t*[f,g]. */
+ fi = f->v[0];
+ gi = g->v[0];
+ cf = (int64_t)u * fi + (int64_t)v * gi;
+ cg = (int64_t)q * fi + (int64_t)r * gi;
+ /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */
+ VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30;
+ VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30;
+ /* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting
+ * down by 30 bits). */
+ for (i = 1; i < len; ++i) {
+ fi = f->v[i];
+ gi = g->v[i];
+ cf += (int64_t)u * fi + (int64_t)v * gi;
+ cg += (int64_t)q * fi + (int64_t)r * gi;
+ f->v[i - 1] = (int32_t)cf & M30; cf >>= 30;
+ g->v[i - 1] = (int32_t)cg & M30; cg >>= 30;
+ }
+ /* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */
+ f->v[len - 1] = (int32_t)cf;
+ g->v[len - 1] = (int32_t)cg;
+}
+
+/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */
+static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) {
+ /* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */
+ secp256k1_modinv32_signed30 d = {{0}};
+ secp256k1_modinv32_signed30 e = {{1}};
+ secp256k1_modinv32_signed30 f = modinfo->modulus;
+ secp256k1_modinv32_signed30 g = *x;
+ int i;
+ int32_t zeta = -1; /* zeta = -(delta+1/2); delta is initially 1/2. */
+
+ /* Do 20 iterations of 30 divsteps each = 600 divsteps. 590 suffices for 256-bit inputs. */
+ for (i = 0; i < 20; ++i) {
+ /* Compute transition matrix and new zeta after 30 divsteps. */
+ secp256k1_modinv32_trans2x2 t;
+ zeta = secp256k1_modinv32_divsteps_30(zeta, f.v[0], g.v[0], &t);
+ /* Update d,e using that transition matrix. */
+ secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo);
+ /* Update f,g using that transition matrix. */
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ secp256k1_modinv32_update_fg_30(&f, &g, &t);
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ }
+
+ /* At this point sufficient iterations have been performed that g must have reached 0
+ * and (if g was not originally 0) f must now equal +/- GCD of the initial f, g
+ * values i.e. +/- 1, and d now contains +/- the modular inverse. */
+#ifdef VERIFY
+ /* g == 0 */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &SECP256K1_SIGNED30_ONE, 0) == 0);
+ /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, -1) == 0 ||
+ secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, 1) == 0 ||
+ (secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
+ secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
+ (secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) == 0 ||
+ secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) == 0)));
+#endif
+
+ /* Optionally negate d, normalize to [0,modulus), and return it. */
+ secp256k1_modinv32_normalize_30(&d, f.v[8], modinfo);
+ *x = d;
+}
+
+/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */
+static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) {
+ /* Start with d=0, e=1, f=modulus, g=x, eta=-1. */
+ secp256k1_modinv32_signed30 d = {{0, 0, 0, 0, 0, 0, 0, 0, 0}};
+ secp256k1_modinv32_signed30 e = {{1, 0, 0, 0, 0, 0, 0, 0, 0}};
+ secp256k1_modinv32_signed30 f = modinfo->modulus;
+ secp256k1_modinv32_signed30 g = *x;
+#ifdef VERIFY
+ int i = 0;
+#endif
+ int j, len = 9;
+ int32_t eta = -1; /* eta = -delta; delta is initially 1 (faster for the variable-time code) */
+ int32_t cond, fn, gn;
+
+ /* Do iterations of 30 divsteps each until g=0. */
+ while (1) {
+ /* Compute transition matrix and new eta after 30 divsteps. */
+ secp256k1_modinv32_trans2x2 t;
+ eta = secp256k1_modinv32_divsteps_30_var(eta, f.v[0], g.v[0], &t);
+ /* Update d,e using that transition matrix. */
+ secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo);
+ /* Update f,g using that transition matrix. */
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t);
+ /* If the bottom limb of g is 0, there is a chance g=0. */
+ if (g.v[0] == 0) {
+ cond = 0;
+ /* Check if all other limbs are also 0. */
+ for (j = 1; j < len; ++j) {
+ cond |= g.v[j];
+ }
+ /* If so, we're done. */
+ if (cond == 0) break;
+ }
+
+ /* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */
+ fn = f.v[len - 1];
+ gn = g.v[len - 1];
+ cond = ((int32_t)len - 2) >> 31;
+ cond |= fn ^ (fn >> 31);
+ cond |= gn ^ (gn >> 31);
+ /* If so, reduce length, propagating the sign of f and g's top limb into the one below. */
+ if (cond == 0) {
+ f.v[len - 2] |= (uint32_t)fn << 30;
+ g.v[len - 2] |= (uint32_t)gn << 30;
+ --len;
+ }
+#ifdef VERIFY
+ VERIFY_CHECK(++i < 25); /* We should never need more than 25*30 = 750 divsteps */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ }
+
+ /* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of
+ * the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */
+#ifdef VERIFY
+ /* g == 0 */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &SECP256K1_SIGNED30_ONE, 0) == 0);
+ /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */
+ VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, -1) == 0 ||
+ secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, 1) == 0 ||
+ (secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
+ secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 &&
+ (secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) == 0 ||
+ secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) == 0)));
+#endif
+
+ /* Optionally negate d, normalize to [0,modulus), and return it. */
+ secp256k1_modinv32_normalize_30(&d, f.v[len - 1], modinfo);
+ *x = d;
+}
+
+#endif /* SECP256K1_MODINV32_IMPL_H */
diff --git a/src/secp256k1/src/modinv64.h b/src/secp256k1/src/modinv64.h
new file mode 100644
index 0000000000..da506dfa9f
--- /dev/null
+++ b/src/secp256k1/src/modinv64.h
@@ -0,0 +1,46 @@
+/***********************************************************************
+ * Copyright (c) 2020 Peter Dettman *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_MODINV64_H
+#define SECP256K1_MODINV64_H
+
+#if defined HAVE_CONFIG_H
+#include "libsecp256k1-config.h"
+#endif
+
+#include "util.h"
+
+#ifndef SECP256K1_WIDEMUL_INT128
+#error "modinv64 requires 128-bit wide multiplication support"
+#endif
+
+/* A signed 62-bit limb representation of integers.
+ *
+ * Its value is sum(v[i] * 2^(62*i), i=0..4). */
+typedef struct {
+ int64_t v[5];
+} secp256k1_modinv64_signed62;
+
+typedef struct {
+ /* The modulus in signed62 notation, must be odd and in [3, 2^256]. */
+ secp256k1_modinv64_signed62 modulus;
+
+ /* modulus^{-1} mod 2^62 */
+ uint64_t modulus_inv62;
+} secp256k1_modinv64_modinfo;
+
+/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus).
+ * If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of
+ * x and modulus must be 1). These rules are automatically satisfied if the modulus is prime.
+ *
+ * On output, all of x's limbs will be in [0, 2^62).
+ */
+static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo);
+
+/* Same as secp256k1_modinv64_var, but constant time in x (not in the modulus). */
+static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo);
+
+#endif /* SECP256K1_MODINV64_H */
diff --git a/src/secp256k1/src/modinv64_impl.h b/src/secp256k1/src/modinv64_impl.h
new file mode 100644
index 0000000000..0743a9c821
--- /dev/null
+++ b/src/secp256k1/src/modinv64_impl.h
@@ -0,0 +1,593 @@
+/***********************************************************************
+ * Copyright (c) 2020 Peter Dettman *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_MODINV64_IMPL_H
+#define SECP256K1_MODINV64_IMPL_H
+
+#include "modinv64.h"
+
+#include "util.h"
+
+/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and
+ * modular inversion" by Daniel J. Bernstein and Bo-Yin Yang.
+ *
+ * For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an
+ * implementation for N=62, using 62-bit signed limbs represented as int64_t.
+ */
+
+#ifdef VERIFY
+/* Helper function to compute the absolute value of an int64_t.
+ * (we don't use abs/labs/llabs as it depends on the int sizes). */
+static int64_t secp256k1_modinv64_abs(int64_t v) {
+ VERIFY_CHECK(v > INT64_MIN);
+ if (v < 0) return -v;
+ return v;
+}
+
+static const secp256k1_modinv64_signed62 SECP256K1_SIGNED62_ONE = {{1}};
+
+/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^62). */
+static void secp256k1_modinv64_mul_62(secp256k1_modinv64_signed62 *r, const secp256k1_modinv64_signed62 *a, int alen, int64_t factor) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ int128_t c = 0;
+ int i;
+ for (i = 0; i < 4; ++i) {
+ if (i < alen) c += (int128_t)a->v[i] * factor;
+ r->v[i] = (int64_t)c & M62; c >>= 62;
+ }
+ if (4 < alen) c += (int128_t)a->v[4] * factor;
+ VERIFY_CHECK(c == (int64_t)c);
+ r->v[4] = (int64_t)c;
+}
+
+/* Return -1 for a<b*factor, 0 for a==b*factor, 1 for a>b*factor. A has alen limbs; b has 5. */
+static int secp256k1_modinv64_mul_cmp_62(const secp256k1_modinv64_signed62 *a, int alen, const secp256k1_modinv64_signed62 *b, int64_t factor) {
+ int i;
+ secp256k1_modinv64_signed62 am, bm;
+ secp256k1_modinv64_mul_62(&am, a, alen, 1); /* Normalize all but the top limb of a. */
+ secp256k1_modinv64_mul_62(&bm, b, 5, factor);
+ for (i = 0; i < 4; ++i) {
+ /* Verify that all but the top limb of a and b are normalized. */
+ VERIFY_CHECK(am.v[i] >> 62 == 0);
+ VERIFY_CHECK(bm.v[i] >> 62 == 0);
+ }
+ for (i = 4; i >= 0; --i) {
+ if (am.v[i] < bm.v[i]) return -1;
+ if (am.v[i] > bm.v[i]) return 1;
+ }
+ return 0;
+}
+#endif
+
+/* Take as input a signed62 number in range (-2*modulus,modulus), and add a multiple of the modulus
+ * to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the
+ * process. The input must have limbs in range (-2^62,2^62). The output will have limbs in range
+ * [0,2^62). */
+static void secp256k1_modinv64_normalize_62(secp256k1_modinv64_signed62 *r, int64_t sign, const secp256k1_modinv64_modinfo *modinfo) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ int64_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4];
+ int64_t cond_add, cond_negate;
+
+#ifdef VERIFY
+ /* Verify that all limbs are in range (-2^62,2^62). */
+ int i;
+ for (i = 0; i < 5; ++i) {
+ VERIFY_CHECK(r->v[i] >= -M62);
+ VERIFY_CHECK(r->v[i] <= M62);
+ }
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, -2) > 0); /* r > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */
+#endif
+
+ /* In a first step, add the modulus if the input is negative, and then negate if requested.
+ * This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input
+ * limbs are in range (-2^62,2^62), this cannot overflow an int64_t. Note that the right
+ * shifts below are signed sign-extending shifts (see assumptions.h for tests that that is
+ * indeed the behavior of the right shift operator). */
+ cond_add = r4 >> 63;
+ r0 += modinfo->modulus.v[0] & cond_add;
+ r1 += modinfo->modulus.v[1] & cond_add;
+ r2 += modinfo->modulus.v[2] & cond_add;
+ r3 += modinfo->modulus.v[3] & cond_add;
+ r4 += modinfo->modulus.v[4] & cond_add;
+ cond_negate = sign >> 63;
+ r0 = (r0 ^ cond_negate) - cond_negate;
+ r1 = (r1 ^ cond_negate) - cond_negate;
+ r2 = (r2 ^ cond_negate) - cond_negate;
+ r3 = (r3 ^ cond_negate) - cond_negate;
+ r4 = (r4 ^ cond_negate) - cond_negate;
+ /* Propagate the top bits, to bring limbs back to range (-2^62,2^62). */
+ r1 += r0 >> 62; r0 &= M62;
+ r2 += r1 >> 62; r1 &= M62;
+ r3 += r2 >> 62; r2 &= M62;
+ r4 += r3 >> 62; r3 &= M62;
+
+ /* In a second step add the modulus again if the result is still negative, bringing
+ * r to range [0,modulus). */
+ cond_add = r4 >> 63;
+ r0 += modinfo->modulus.v[0] & cond_add;
+ r1 += modinfo->modulus.v[1] & cond_add;
+ r2 += modinfo->modulus.v[2] & cond_add;
+ r3 += modinfo->modulus.v[3] & cond_add;
+ r4 += modinfo->modulus.v[4] & cond_add;
+ /* And propagate again. */
+ r1 += r0 >> 62; r0 &= M62;
+ r2 += r1 >> 62; r1 &= M62;
+ r3 += r2 >> 62; r2 &= M62;
+ r4 += r3 >> 62; r3 &= M62;
+
+ r->v[0] = r0;
+ r->v[1] = r1;
+ r->v[2] = r2;
+ r->v[3] = r3;
+ r->v[4] = r4;
+
+#ifdef VERIFY
+ VERIFY_CHECK(r0 >> 62 == 0);
+ VERIFY_CHECK(r1 >> 62 == 0);
+ VERIFY_CHECK(r2 >> 62 == 0);
+ VERIFY_CHECK(r3 >> 62 == 0);
+ VERIFY_CHECK(r4 >> 62 == 0);
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 0) >= 0); /* r >= 0 */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */
+#endif
+}
+
+/* Data type for transition matrices (see section 3 of explanation).
+ *
+ * t = [ u v ]
+ * [ q r ]
+ */
+typedef struct {
+ int64_t u, v, q, r;
+} secp256k1_modinv64_trans2x2;
+
+/* Compute the transition matrix and eta for 59 divsteps (where zeta=-(delta+1/2)).
+ * Note that the transformation matrix is scaled by 2^62 and not 2^59.
+ *
+ * Input: zeta: initial zeta
+ * f0: bottom limb of initial f
+ * g0: bottom limb of initial g
+ * Output: t: transition matrix
+ * Return: final zeta
+ *
+ * Implements the divsteps_n_matrix function from the explanation.
+ */
+static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) {
+ /* u,v,q,r are the elements of the transformation matrix being built up,
+ * starting with the identity matrix times 8 (because the caller expects
+ * a result scaled by 2^62). Semantically they are signed integers
+ * in range [-2^62,2^62], but here represented as unsigned mod 2^64. This
+ * permits left shifting (which is UB for negative numbers). The range
+ * being inside [-2^63,2^63) means that casting to signed works correctly.
+ */
+ uint64_t u = 8, v = 0, q = 0, r = 8;
+ uint64_t c1, c2, f = f0, g = g0, x, y, z;
+ int i;
+
+ for (i = 3; i < 62; ++i) {
+ VERIFY_CHECK((f & 1) == 1); /* f must always be odd */
+ VERIFY_CHECK((u * f0 + v * g0) == f << i);
+ VERIFY_CHECK((q * f0 + r * g0) == g << i);
+ /* Compute conditional masks for (zeta < 0) and for (g & 1). */
+ c1 = zeta >> 63;
+ c2 = -(g & 1);
+ /* Compute x,y,z, conditionally negated versions of f,u,v. */
+ x = (f ^ c1) - c1;
+ y = (u ^ c1) - c1;
+ z = (v ^ c1) - c1;
+ /* Conditionally add x,y,z to g,q,r. */
+ g += x & c2;
+ q += y & c2;
+ r += z & c2;
+ /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */
+ c1 &= c2;
+ /* Conditionally change zeta into -zeta-2 or zeta-1. */
+ zeta = (zeta ^ c1) - 1;
+ /* Conditionally add g,q,r to f,u,v. */
+ f += g & c1;
+ u += q & c1;
+ v += r & c1;
+ /* Shifts */
+ g >>= 1;
+ u <<= 1;
+ v <<= 1;
+ /* Bounds on zeta that follow from the bounds on iteration count (max 10*59 divsteps). */
+ VERIFY_CHECK(zeta >= -591 && zeta <= 591);
+ }
+ /* Return data in t and return value. */
+ t->u = (int64_t)u;
+ t->v = (int64_t)v;
+ t->q = (int64_t)q;
+ t->r = (int64_t)r;
+ /* The determinant of t must be a power of two. This guarantees that multiplication with t
+ * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
+ * will be divided out again). As each divstep's individual matrix has determinant 2, the
+ * aggregate of 59 of them will have determinant 2^59. Multiplying with the initial
+ * 8*identity (which has determinant 2^6) means the overall outputs has determinant
+ * 2^65. */
+ VERIFY_CHECK((int128_t)t->u * t->r - (int128_t)t->v * t->q == ((int128_t)1) << 65);
+ return zeta;
+}
+
+/* Compute the transition matrix and eta for 62 divsteps (variable time, eta=-delta).
+ *
+ * Input: eta: initial eta
+ * f0: bottom limb of initial f
+ * g0: bottom limb of initial g
+ * Output: t: transition matrix
+ * Return: final eta
+ *
+ * Implements the divsteps_n_matrix_var function from the explanation.
+ */
+static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) {
+ /* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */
+ uint64_t u = 1, v = 0, q = 0, r = 1;
+ uint64_t f = f0, g = g0, m;
+ uint32_t w;
+ int i = 62, limit, zeros;
+
+ for (;;) {
+ /* Use a sentinel bit to count zeros only up to i. */
+ zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i));
+ /* Perform zeros divsteps at once; they all just divide g by two. */
+ g >>= zeros;
+ u <<= zeros;
+ v <<= zeros;
+ eta -= zeros;
+ i -= zeros;
+ /* We're done once we've done 62 divsteps. */
+ if (i == 0) break;
+ VERIFY_CHECK((f & 1) == 1);
+ VERIFY_CHECK((g & 1) == 1);
+ VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i));
+ VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i));
+ /* Bounds on eta that follow from the bounds on iteration count (max 12*62 divsteps). */
+ VERIFY_CHECK(eta >= -745 && eta <= 745);
+ /* If eta is negative, negate it and replace f,g with g,-f. */
+ if (eta < 0) {
+ uint64_t tmp;
+ eta = -eta;
+ tmp = f; f = g; g = -tmp;
+ tmp = u; u = q; q = -tmp;
+ tmp = v; v = r; r = -tmp;
+ /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled
+ * out (as we'd be done before that point), and no more than eta+1 can be done as its
+ * will flip again once that happens. */
+ limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
+ VERIFY_CHECK(limit > 0 && limit <= 62);
+ /* m is a mask for the bottom min(limit, 6) bits. */
+ m = (UINT64_MAX >> (64 - limit)) & 63U;
+ /* Find what multiple of f must be added to g to cancel its bottom min(limit, 6)
+ * bits. */
+ w = (f * g * (f * f - 2)) & m;
+ } else {
+ /* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as
+ * eta tends to be smaller here. */
+ limit = ((int)eta + 1) > i ? i : ((int)eta + 1);
+ VERIFY_CHECK(limit > 0 && limit <= 62);
+ /* m is a mask for the bottom min(limit, 4) bits. */
+ m = (UINT64_MAX >> (64 - limit)) & 15U;
+ /* Find what multiple of f must be added to g to cancel its bottom min(limit, 4)
+ * bits. */
+ w = f + (((f + 1) & 4) << 1);
+ w = (-w * g) & m;
+ }
+ g += f * w;
+ q += u * w;
+ r += v * w;
+ VERIFY_CHECK((g & m) == 0);
+ }
+ /* Return data in t and return value. */
+ t->u = (int64_t)u;
+ t->v = (int64_t)v;
+ t->q = (int64_t)q;
+ t->r = (int64_t)r;
+ /* The determinant of t must be a power of two. This guarantees that multiplication with t
+ * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
+ * will be divided out again). As each divstep's individual matrix has determinant 2, the
+ * aggregate of 62 of them will have determinant 2^62. */
+ VERIFY_CHECK((int128_t)t->u * t->r - (int128_t)t->v * t->q == ((int128_t)1) << 62);
+ return eta;
+}
+
+/* Compute (t/2^62) * [d, e] mod modulus, where t is a transition matrix scaled by 2^62.
+ *
+ * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range
+ * (-2^62,2^62).
+ *
+ * This implements the update_de function from the explanation.
+ */
+static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp256k1_modinv64_signed62 *e, const secp256k1_modinv64_trans2x2 *t, const secp256k1_modinv64_modinfo* modinfo) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ const int64_t d0 = d->v[0], d1 = d->v[1], d2 = d->v[2], d3 = d->v[3], d4 = d->v[4];
+ const int64_t e0 = e->v[0], e1 = e->v[1], e2 = e->v[2], e3 = e->v[3], e4 = e->v[4];
+ const int64_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int64_t md, me, sd, se;
+ int128_t cd, ce;
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */
+ VERIFY_CHECK((secp256k1_modinv64_abs(u) + secp256k1_modinv64_abs(v)) >= 0); /* |u|+|v| doesn't overflow */
+ VERIFY_CHECK((secp256k1_modinv64_abs(q) + secp256k1_modinv64_abs(r)) >= 0); /* |q|+|r| doesn't overflow */
+ VERIFY_CHECK((secp256k1_modinv64_abs(u) + secp256k1_modinv64_abs(v)) <= M62 + 1); /* |u|+|v| <= 2^62 */
+ VERIFY_CHECK((secp256k1_modinv64_abs(q) + secp256k1_modinv64_abs(r)) <= M62 + 1); /* |q|+|r| <= 2^62 */
+#endif
+ /* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */
+ sd = d4 >> 63;
+ se = e4 >> 63;
+ md = (u & sd) + (v & se);
+ me = (q & sd) + (r & se);
+ /* Begin computing t*[d,e]. */
+ cd = (int128_t)u * d0 + (int128_t)v * e0;
+ ce = (int128_t)q * d0 + (int128_t)r * e0;
+ /* Correct md,me so that t*[d,e]+modulus*[md,me] has 62 zero bottom bits. */
+ md -= (modinfo->modulus_inv62 * (uint64_t)cd + md) & M62;
+ me -= (modinfo->modulus_inv62 * (uint64_t)ce + me) & M62;
+ /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */
+ cd += (int128_t)modinfo->modulus.v[0] * md;
+ ce += (int128_t)modinfo->modulus.v[0] * me;
+ /* Verify that the low 62 bits of the computation are indeed zero, and then throw them away. */
+ VERIFY_CHECK(((int64_t)cd & M62) == 0); cd >>= 62;
+ VERIFY_CHECK(((int64_t)ce & M62) == 0); ce >>= 62;
+ /* Compute limb 1 of t*[d,e]+modulus*[md,me], and store it as output limb 0 (= down shift). */
+ cd += (int128_t)u * d1 + (int128_t)v * e1;
+ ce += (int128_t)q * d1 + (int128_t)r * e1;
+ if (modinfo->modulus.v[1]) { /* Optimize for the case where limb of modulus is zero. */
+ cd += (int128_t)modinfo->modulus.v[1] * md;
+ ce += (int128_t)modinfo->modulus.v[1] * me;
+ }
+ d->v[0] = (int64_t)cd & M62; cd >>= 62;
+ e->v[0] = (int64_t)ce & M62; ce >>= 62;
+ /* Compute limb 2 of t*[d,e]+modulus*[md,me], and store it as output limb 1. */
+ cd += (int128_t)u * d2 + (int128_t)v * e2;
+ ce += (int128_t)q * d2 + (int128_t)r * e2;
+ if (modinfo->modulus.v[2]) { /* Optimize for the case where limb of modulus is zero. */
+ cd += (int128_t)modinfo->modulus.v[2] * md;
+ ce += (int128_t)modinfo->modulus.v[2] * me;
+ }
+ d->v[1] = (int64_t)cd & M62; cd >>= 62;
+ e->v[1] = (int64_t)ce & M62; ce >>= 62;
+ /* Compute limb 3 of t*[d,e]+modulus*[md,me], and store it as output limb 2. */
+ cd += (int128_t)u * d3 + (int128_t)v * e3;
+ ce += (int128_t)q * d3 + (int128_t)r * e3;
+ if (modinfo->modulus.v[3]) { /* Optimize for the case where limb of modulus is zero. */
+ cd += (int128_t)modinfo->modulus.v[3] * md;
+ ce += (int128_t)modinfo->modulus.v[3] * me;
+ }
+ d->v[2] = (int64_t)cd & M62; cd >>= 62;
+ e->v[2] = (int64_t)ce & M62; ce >>= 62;
+ /* Compute limb 4 of t*[d,e]+modulus*[md,me], and store it as output limb 3. */
+ cd += (int128_t)u * d4 + (int128_t)v * e4;
+ ce += (int128_t)q * d4 + (int128_t)r * e4;
+ cd += (int128_t)modinfo->modulus.v[4] * md;
+ ce += (int128_t)modinfo->modulus.v[4] * me;
+ d->v[3] = (int64_t)cd & M62; cd >>= 62;
+ e->v[3] = (int64_t)ce & M62; ce >>= 62;
+ /* What remains is limb 5 of t*[d,e]+modulus*[md,me]; store it as output limb 4. */
+ d->v[4] = (int64_t)cd;
+ e->v[4] = (int64_t)ce;
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */
+#endif
+}
+
+/* Compute (t/2^62) * [f, g], where t is a transition matrix scaled by 2^62.
+ *
+ * This implements the update_fg function from the explanation.
+ */
+static void secp256k1_modinv64_update_fg_62(secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ const int64_t f0 = f->v[0], f1 = f->v[1], f2 = f->v[2], f3 = f->v[3], f4 = f->v[4];
+ const int64_t g0 = g->v[0], g1 = g->v[1], g2 = g->v[2], g3 = g->v[3], g4 = g->v[4];
+ const int64_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int128_t cf, cg;
+ /* Start computing t*[f,g]. */
+ cf = (int128_t)u * f0 + (int128_t)v * g0;
+ cg = (int128_t)q * f0 + (int128_t)r * g0;
+ /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */
+ VERIFY_CHECK(((int64_t)cf & M62) == 0); cf >>= 62;
+ VERIFY_CHECK(((int64_t)cg & M62) == 0); cg >>= 62;
+ /* Compute limb 1 of t*[f,g], and store it as output limb 0 (= down shift). */
+ cf += (int128_t)u * f1 + (int128_t)v * g1;
+ cg += (int128_t)q * f1 + (int128_t)r * g1;
+ f->v[0] = (int64_t)cf & M62; cf >>= 62;
+ g->v[0] = (int64_t)cg & M62; cg >>= 62;
+ /* Compute limb 2 of t*[f,g], and store it as output limb 1. */
+ cf += (int128_t)u * f2 + (int128_t)v * g2;
+ cg += (int128_t)q * f2 + (int128_t)r * g2;
+ f->v[1] = (int64_t)cf & M62; cf >>= 62;
+ g->v[1] = (int64_t)cg & M62; cg >>= 62;
+ /* Compute limb 3 of t*[f,g], and store it as output limb 2. */
+ cf += (int128_t)u * f3 + (int128_t)v * g3;
+ cg += (int128_t)q * f3 + (int128_t)r * g3;
+ f->v[2] = (int64_t)cf & M62; cf >>= 62;
+ g->v[2] = (int64_t)cg & M62; cg >>= 62;
+ /* Compute limb 4 of t*[f,g], and store it as output limb 3. */
+ cf += (int128_t)u * f4 + (int128_t)v * g4;
+ cg += (int128_t)q * f4 + (int128_t)r * g4;
+ f->v[3] = (int64_t)cf & M62; cf >>= 62;
+ g->v[3] = (int64_t)cg & M62; cg >>= 62;
+ /* What remains is limb 5 of t*[f,g]; store it as output limb 4. */
+ f->v[4] = (int64_t)cf;
+ g->v[4] = (int64_t)cg;
+}
+
+/* Compute (t/2^62) * [f, g], where t is a transition matrix for 62 divsteps.
+ *
+ * Version that operates on a variable number of limbs in f and g.
+ *
+ * This implements the update_fg function from the explanation.
+ */
+static void secp256k1_modinv64_update_fg_62_var(int len, secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) {
+ const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ const int64_t u = t->u, v = t->v, q = t->q, r = t->r;
+ int64_t fi, gi;
+ int128_t cf, cg;
+ int i;
+ VERIFY_CHECK(len > 0);
+ /* Start computing t*[f,g]. */
+ fi = f->v[0];
+ gi = g->v[0];
+ cf = (int128_t)u * fi + (int128_t)v * gi;
+ cg = (int128_t)q * fi + (int128_t)r * gi;
+ /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */
+ VERIFY_CHECK(((int64_t)cf & M62) == 0); cf >>= 62;
+ VERIFY_CHECK(((int64_t)cg & M62) == 0); cg >>= 62;
+ /* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting
+ * down by 62 bits). */
+ for (i = 1; i < len; ++i) {
+ fi = f->v[i];
+ gi = g->v[i];
+ cf += (int128_t)u * fi + (int128_t)v * gi;
+ cg += (int128_t)q * fi + (int128_t)r * gi;
+ f->v[i - 1] = (int64_t)cf & M62; cf >>= 62;
+ g->v[i - 1] = (int64_t)cg & M62; cg >>= 62;
+ }
+ /* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */
+ f->v[len - 1] = (int64_t)cf;
+ g->v[len - 1] = (int64_t)cg;
+}
+
+/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */
+static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) {
+ /* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */
+ secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}};
+ secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}};
+ secp256k1_modinv64_signed62 f = modinfo->modulus;
+ secp256k1_modinv64_signed62 g = *x;
+ int i;
+ int64_t zeta = -1; /* zeta = -(delta+1/2); delta starts at 1/2. */
+
+ /* Do 10 iterations of 59 divsteps each = 590 divsteps. This suffices for 256-bit inputs. */
+ for (i = 0; i < 10; ++i) {
+ /* Compute transition matrix and new zeta after 59 divsteps. */
+ secp256k1_modinv64_trans2x2 t;
+ zeta = secp256k1_modinv64_divsteps_59(zeta, f.v[0], g.v[0], &t);
+ /* Update d,e using that transition matrix. */
+ secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo);
+ /* Update f,g using that transition matrix. */
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ secp256k1_modinv64_update_fg_62(&f, &g, &t);
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ }
+
+ /* At this point sufficient iterations have been performed that g must have reached 0
+ * and (if g was not originally 0) f must now equal +/- GCD of the initial f, g
+ * values i.e. +/- 1, and d now contains +/- the modular inverse. */
+#ifdef VERIFY
+ /* g == 0 */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &SECP256K1_SIGNED62_ONE, 0) == 0);
+ /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, -1) == 0 ||
+ secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, 1) == 0 ||
+ (secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
+ secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
+ (secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) == 0 ||
+ secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) == 0)));
+#endif
+
+ /* Optionally negate d, normalize to [0,modulus), and return it. */
+ secp256k1_modinv64_normalize_62(&d, f.v[4], modinfo);
+ *x = d;
+}
+
+/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */
+static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) {
+ /* Start with d=0, e=1, f=modulus, g=x, eta=-1. */
+ secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}};
+ secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}};
+ secp256k1_modinv64_signed62 f = modinfo->modulus;
+ secp256k1_modinv64_signed62 g = *x;
+#ifdef VERIFY
+ int i = 0;
+#endif
+ int j, len = 5;
+ int64_t eta = -1; /* eta = -delta; delta is initially 1 */
+ int64_t cond, fn, gn;
+
+ /* Do iterations of 62 divsteps each until g=0. */
+ while (1) {
+ /* Compute transition matrix and new eta after 62 divsteps. */
+ secp256k1_modinv64_trans2x2 t;
+ eta = secp256k1_modinv64_divsteps_62_var(eta, f.v[0], g.v[0], &t);
+ /* Update d,e using that transition matrix. */
+ secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo);
+ /* Update f,g using that transition matrix. */
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t);
+ /* If the bottom limb of g is zero, there is a chance that g=0. */
+ if (g.v[0] == 0) {
+ cond = 0;
+ /* Check if the other limbs are also 0. */
+ for (j = 1; j < len; ++j) {
+ cond |= g.v[j];
+ }
+ /* If so, we're done. */
+ if (cond == 0) break;
+ }
+
+ /* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */
+ fn = f.v[len - 1];
+ gn = g.v[len - 1];
+ cond = ((int64_t)len - 2) >> 63;
+ cond |= fn ^ (fn >> 63);
+ cond |= gn ^ (gn >> 63);
+ /* If so, reduce length, propagating the sign of f and g's top limb into the one below. */
+ if (cond == 0) {
+ f.v[len - 2] |= (uint64_t)fn << 62;
+ g.v[len - 2] |= (uint64_t)gn << 62;
+ --len;
+ }
+#ifdef VERIFY
+ VERIFY_CHECK(++i < 12); /* We should never need more than 12*62 = 744 divsteps */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */
+#endif
+ }
+
+ /* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of
+ * the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */
+#ifdef VERIFY
+ /* g == 0 */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &SECP256K1_SIGNED62_ONE, 0) == 0);
+ /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */
+ VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, -1) == 0 ||
+ secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, 1) == 0 ||
+ (secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
+ secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 &&
+ (secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) == 0 ||
+ secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) == 0)));
+#endif
+
+ /* Optionally negate d, normalize to [0,modulus), and return it. */
+ secp256k1_modinv64_normalize_62(&d, f.v[len - 1], modinfo);
+ *x = d;
+}
+
+#endif /* SECP256K1_MODINV64_IMPL_H */
diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h
index 07a25b80d4..1ac67086be 100644
--- a/src/secp256k1/src/modules/ecdh/main_impl.h
+++ b/src/secp256k1/src/modules/ecdh/main_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_ECDH_MAIN_H
#define SECP256K1_MODULE_ECDH_MAIN_H
diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h
index e8d2aeab9a..be07447a4b 100644
--- a/src/secp256k1/src/modules/ecdh/tests_impl.h
+++ b/src/secp256k1/src/modules/ecdh/tests_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_ECDH_TESTS_H
#define SECP256K1_MODULE_ECDH_TESTS_H
diff --git a/src/secp256k1/src/modules/extrakeys/main_impl.h b/src/secp256k1/src/modules/extrakeys/main_impl.h
index 5378d2f301..7390b22718 100644
--- a/src/secp256k1/src/modules/extrakeys/main_impl.h
+++ b/src/secp256k1/src/modules/extrakeys/main_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2020 Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_EXTRAKEYS_MAIN_
-#define _SECP256K1_MODULE_EXTRAKEYS_MAIN_
+#ifndef SECP256K1_MODULE_EXTRAKEYS_MAIN_H
+#define SECP256K1_MODULE_EXTRAKEYS_MAIN_H
#include "include/secp256k1.h"
#include "include/secp256k1_extrakeys.h"
@@ -180,12 +180,22 @@ int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *ke
ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32);
secp256k1_keypair_save(keypair, &sk, &pk);
- memczero(keypair, sizeof(*keypair), !ret);
+ secp256k1_memczero(keypair, sizeof(*keypair), !ret);
secp256k1_scalar_clear(&sk);
return ret;
}
+int secp256k1_keypair_sec(const secp256k1_context* ctx, unsigned char *seckey, const secp256k1_keypair *keypair) {
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(seckey != NULL);
+ memset(seckey, 0, 32);
+ ARG_CHECK(keypair != NULL);
+
+ memcpy(seckey, &keypair->data[0], 32);
+ return 1;
+}
+
int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
diff --git a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
index 0e29bc6b09..0aca4fb72d 100644
--- a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
+++ b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2020 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
-#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
+#ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H
+#define SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H
#include "src/modules/extrakeys/main_impl.h"
#include "include/secp256k1_extrakeys.h"
diff --git a/src/secp256k1/src/modules/extrakeys/tests_impl.h b/src/secp256k1/src/modules/extrakeys/tests_impl.h
index 5ee135849e..9473a7dd48 100644
--- a/src/secp256k1/src/modules/extrakeys/tests_impl.h
+++ b/src/secp256k1/src/modules/extrakeys/tests_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2020 Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_
-#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_
+#ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_H
+#define SECP256K1_MODULE_EXTRAKEYS_TESTS_H
#include "secp256k1_extrakeys.h"
@@ -311,6 +311,7 @@ void test_xonly_pubkey_tweak_recursive(void) {
void test_keypair(void) {
unsigned char sk[32];
+ unsigned char sk_tmp[32];
unsigned char zeros96[96] = { 0 };
unsigned char overflows[32];
secp256k1_keypair keypair;
@@ -396,6 +397,28 @@ void test_keypair(void) {
CHECK(secp256k1_memcmp_var(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0);
CHECK(pk_parity == pk_parity_tmp);
+ /* Test keypair_seckey */
+ ecount = 0;
+ secp256k1_testrand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_sec(none, sk_tmp, &keypair) == 1);
+ CHECK(secp256k1_keypair_sec(none, NULL, &keypair) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_sec(none, sk_tmp, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_memcmp_var(zeros96, sk_tmp, sizeof(sk_tmp)) == 0);
+
+ /* keypair returns the same seckey it got */
+ CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_sec(none, sk_tmp, &keypair) == 1);
+ CHECK(secp256k1_memcmp_var(sk, sk_tmp, sizeof(sk_tmp)) == 0);
+
+
+ /* Using an invalid keypair is fine for keypair_seckey */
+ memset(&keypair, 0, sizeof(keypair));
+ CHECK(secp256k1_keypair_sec(none, sk_tmp, &keypair) == 1);
+ CHECK(secp256k1_memcmp_var(zeros96, sk_tmp, sizeof(sk_tmp)) == 0);
+
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(verify);
@@ -484,6 +507,7 @@ void test_keypair_add(void) {
secp256k1_pubkey output_pk_xy;
secp256k1_pubkey output_pk_expected;
unsigned char pk32[32];
+ unsigned char sk32[32];
int pk_parity;
secp256k1_testrand256(tweak);
@@ -501,7 +525,8 @@ void test_keypair_add(void) {
CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
/* Check that the secret key in the keypair is tweaked correctly */
- CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, &keypair.data[0]) == 1);
+ CHECK(secp256k1_keypair_sec(none, sk32, &keypair) == 1);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, sk32) == 1);
CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
}
secp256k1_context_destroy(none);
diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h
index e2576aa953..7a440a729b 100644
--- a/src/secp256k1/src/modules/recovery/main_impl.h
+++ b/src/secp256k1/src/modules/recovery/main_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_RECOVERY_MAIN_H
#define SECP256K1_MODULE_RECOVERY_MAIN_H
@@ -120,34 +120,34 @@ static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, cons
return !secp256k1_gej_is_infinity(&qj);
}
-int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
+int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msghash32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
secp256k1_scalar r, s;
int ret, recid;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(msghash32 != NULL);
ARG_CHECK(signature != NULL);
ARG_CHECK(seckey != NULL);
- ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msg32, seckey, noncefp, noncedata);
+ ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msghash32, seckey, noncefp, noncedata);
secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
return ret;
}
-int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) {
+int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msghash32) {
secp256k1_ge q;
secp256k1_scalar r, s;
secp256k1_scalar m;
int recid;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
- ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(msghash32 != NULL);
ARG_CHECK(signature != NULL);
ARG_CHECK(pubkey != NULL);
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature);
VERIFY_CHECK(recid >= 0 && recid < 4); /* should have been caught in parse_compact */
- secp256k1_scalar_set_b32(&m, msg32, NULL);
+ secp256k1_scalar_set_b32(&m, msghash32, NULL);
if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) {
secp256k1_pubkey_save(pubkey, &q);
return 1;
diff --git a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
index a2f381d77a..0ba9409c69 100644
--- a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
+++ b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2016 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2016 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
#define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h
index 09cae38403..40dba87ce3 100644
--- a/src/secp256k1/src/modules/recovery/tests_impl.h
+++ b/src/secp256k1/src/modules/recovery/tests_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_MODULE_RECOVERY_TESTS_H
#define SECP256K1_MODULE_RECOVERY_TESTS_H
diff --git a/src/secp256k1/src/modules/schnorrsig/main_impl.h b/src/secp256k1/src/modules/schnorrsig/main_impl.h
index b0d8481f9b..22e1b33a5a 100644
--- a/src/secp256k1/src/modules/schnorrsig/main_impl.h
+++ b/src/secp256k1/src/modules/schnorrsig/main_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_
-#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_
+#ifndef SECP256K1_MODULE_SCHNORRSIG_MAIN_H
+#define SECP256K1_MODULE_SCHNORRSIG_MAIN_H
#include "include/secp256k1.h"
#include "include/secp256k1_schnorrsig.h"
@@ -179,7 +179,7 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64
secp256k1_scalar_add(&e, &e, &k);
secp256k1_scalar_get_b32(&sig64[32], &e);
- memczero(sig64, 64, !ret);
+ secp256k1_memczero(sig64, 64, !ret);
secp256k1_scalar_clear(&k);
secp256k1_scalar_clear(&sk);
memset(seckey, 0, sizeof(seckey));
diff --git a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
index 4bf0bc1680..b4a428729f 100644
--- a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
+++ b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2020 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
-#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
+#ifndef SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H
+#define SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H
#include "include/secp256k1_schnorrsig.h"
#include "src/modules/schnorrsig/main_impl.h"
diff --git a/src/secp256k1/src/modules/schnorrsig/tests_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
index f522fcb320..338462fc9d 100644
--- a/src/secp256k1/src/modules/schnorrsig/tests_impl.h
+++ b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_
-#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_
+/***********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
+
+#ifndef SECP256K1_MODULE_SCHNORRSIG_TESTS_H
+#define SECP256K1_MODULE_SCHNORRSIG_TESTS_H
#include "secp256k1_schnorrsig.h"
diff --git a/src/secp256k1/src/num.h b/src/secp256k1/src/num.h
deleted file mode 100644
index 49f2dd791d..0000000000
--- a/src/secp256k1/src/num.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_NUM_H
-#define SECP256K1_NUM_H
-
-#ifndef USE_NUM_NONE
-
-#if defined HAVE_CONFIG_H
-#include "libsecp256k1-config.h"
-#endif
-
-#if defined(USE_NUM_GMP)
-#include "num_gmp.h"
-#else
-#error "Please select num implementation"
-#endif
-
-/** Copy a number. */
-static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a);
-
-/** Convert a number's absolute value to a binary big-endian string.
- * There must be enough place. */
-static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a);
-
-/** Set a number to the value of a binary big-endian string. */
-static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen);
-
-/** Compute a modular inverse. The input must be less than the modulus. */
-static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m);
-
-/** Compute the jacobi symbol (a|b). b must be positive and odd. */
-static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b);
-
-/** Compare the absolute value of two numbers. */
-static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b);
-
-/** Test whether two number are equal (including sign). */
-static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b);
-
-/** Add two (signed) numbers. */
-static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b);
-
-/** Subtract two (signed) numbers. */
-static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b);
-
-/** Multiply two (signed) numbers. */
-static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b);
-
-/** Replace a number by its remainder modulo m. M's sign is ignored. The result is a number between 0 and m-1,
- even if r was negative. */
-static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m);
-
-/** Right-shift the passed number by bits bits. */
-static void secp256k1_num_shift(secp256k1_num *r, int bits);
-
-/** Check whether a number is zero. */
-static int secp256k1_num_is_zero(const secp256k1_num *a);
-
-/** Check whether a number is one. */
-static int secp256k1_num_is_one(const secp256k1_num *a);
-
-/** Check whether a number is strictly negative. */
-static int secp256k1_num_is_neg(const secp256k1_num *a);
-
-/** Change a number's sign. */
-static void secp256k1_num_negate(secp256k1_num *r);
-
-#endif
-
-#endif /* SECP256K1_NUM_H */
diff --git a/src/secp256k1/src/num_gmp.h b/src/secp256k1/src/num_gmp.h
deleted file mode 100644
index 3619844bd5..0000000000
--- a/src/secp256k1/src/num_gmp.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_NUM_REPR_H
-#define SECP256K1_NUM_REPR_H
-
-#include <gmp.h>
-
-#define NUM_LIMBS ((256+GMP_NUMB_BITS-1)/GMP_NUMB_BITS)
-
-typedef struct {
- mp_limb_t data[2*NUM_LIMBS];
- int neg;
- int limbs;
-} secp256k1_num;
-
-#endif /* SECP256K1_NUM_REPR_H */
diff --git a/src/secp256k1/src/num_gmp_impl.h b/src/secp256k1/src/num_gmp_impl.h
deleted file mode 100644
index 0ae2a8ba0e..0000000000
--- a/src/secp256k1/src/num_gmp_impl.h
+++ /dev/null
@@ -1,288 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_NUM_REPR_IMPL_H
-#define SECP256K1_NUM_REPR_IMPL_H
-
-#include <string.h>
-#include <stdlib.h>
-#include <gmp.h>
-
-#include "util.h"
-#include "num.h"
-
-#ifdef VERIFY
-static void secp256k1_num_sanity(const secp256k1_num *a) {
- VERIFY_CHECK(a->limbs == 1 || (a->limbs > 1 && a->data[a->limbs-1] != 0));
-}
-#else
-#define secp256k1_num_sanity(a) do { } while(0)
-#endif
-
-static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a) {
- *r = *a;
-}
-
-static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a) {
- unsigned char tmp[65];
- int len = 0;
- int shift = 0;
- if (a->limbs>1 || a->data[0] != 0) {
- len = mpn_get_str(tmp, 256, (mp_limb_t*)a->data, a->limbs);
- }
- while (shift < len && tmp[shift] == 0) shift++;
- VERIFY_CHECK(len-shift <= (int)rlen);
- memset(r, 0, rlen - len + shift);
- if (len > shift) {
- memcpy(r + rlen - len + shift, tmp + shift, len - shift);
- }
- memset(tmp, 0, sizeof(tmp));
-}
-
-static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen) {
- int len;
- VERIFY_CHECK(alen > 0);
- VERIFY_CHECK(alen <= 64);
- len = mpn_set_str(r->data, a, alen, 256);
- if (len == 0) {
- r->data[0] = 0;
- len = 1;
- }
- VERIFY_CHECK(len <= NUM_LIMBS*2);
- r->limbs = len;
- r->neg = 0;
- while (r->limbs > 1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
-}
-
-static void secp256k1_num_add_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- mp_limb_t c = mpn_add(r->data, a->data, a->limbs, b->data, b->limbs);
- r->limbs = a->limbs;
- if (c != 0) {
- VERIFY_CHECK(r->limbs < 2*NUM_LIMBS);
- r->data[r->limbs++] = c;
- }
-}
-
-static void secp256k1_num_sub_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- mp_limb_t c = mpn_sub(r->data, a->data, a->limbs, b->data, b->limbs);
- (void)c;
- VERIFY_CHECK(c == 0);
- r->limbs = a->limbs;
- while (r->limbs > 1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
-}
-
-static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m) {
- secp256k1_num_sanity(r);
- secp256k1_num_sanity(m);
-
- if (r->limbs >= m->limbs) {
- mp_limb_t t[2*NUM_LIMBS];
- mpn_tdiv_qr(t, r->data, 0, r->data, r->limbs, m->data, m->limbs);
- memset(t, 0, sizeof(t));
- r->limbs = m->limbs;
- while (r->limbs > 1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
- }
-
- if (r->neg && (r->limbs > 1 || r->data[0] != 0)) {
- secp256k1_num_sub_abs(r, m, r);
- r->neg = 0;
- }
-}
-
-static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m) {
- int i;
- mp_limb_t g[NUM_LIMBS+1];
- mp_limb_t u[NUM_LIMBS+1];
- mp_limb_t v[NUM_LIMBS+1];
- mp_size_t sn;
- mp_size_t gn;
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(m);
-
- /** mpn_gcdext computes: (G,S) = gcdext(U,V), where
- * * G = gcd(U,V)
- * * G = U*S + V*T
- * * U has equal or more limbs than V, and V has no padding
- * If we set U to be (a padded version of) a, and V = m:
- * G = a*S + m*T
- * G = a*S mod m
- * Assuming G=1:
- * S = 1/a mod m
- */
- VERIFY_CHECK(m->limbs <= NUM_LIMBS);
- VERIFY_CHECK(m->data[m->limbs-1] != 0);
- for (i = 0; i < m->limbs; i++) {
- u[i] = (i < a->limbs) ? a->data[i] : 0;
- v[i] = m->data[i];
- }
- sn = NUM_LIMBS+1;
- gn = mpn_gcdext(g, r->data, &sn, u, m->limbs, v, m->limbs);
- (void)gn;
- VERIFY_CHECK(gn == 1);
- VERIFY_CHECK(g[0] == 1);
- r->neg = a->neg ^ m->neg;
- if (sn < 0) {
- mpn_sub(r->data, m->data, m->limbs, r->data, -sn);
- r->limbs = m->limbs;
- while (r->limbs > 1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
- } else {
- r->limbs = sn;
- }
- memset(g, 0, sizeof(g));
- memset(u, 0, sizeof(u));
- memset(v, 0, sizeof(v));
-}
-
-static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) {
- int ret;
- mpz_t ga, gb;
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(b);
- VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1));
-
- mpz_inits(ga, gb, NULL);
-
- mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data);
- mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data);
- if (a->neg) {
- mpz_neg(ga, ga);
- }
-
- ret = mpz_jacobi(ga, gb);
-
- mpz_clears(ga, gb, NULL);
-
- return ret;
-}
-
-static int secp256k1_num_is_one(const secp256k1_num *a) {
- return (a->limbs == 1 && a->data[0] == 1);
-}
-
-static int secp256k1_num_is_zero(const secp256k1_num *a) {
- return (a->limbs == 1 && a->data[0] == 0);
-}
-
-static int secp256k1_num_is_neg(const secp256k1_num *a) {
- return (a->limbs > 1 || a->data[0] != 0) && a->neg;
-}
-
-static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b) {
- if (a->limbs > b->limbs) {
- return 1;
- }
- if (a->limbs < b->limbs) {
- return -1;
- }
- return mpn_cmp(a->data, b->data, a->limbs);
-}
-
-static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b) {
- if (a->limbs > b->limbs) {
- return 0;
- }
- if (a->limbs < b->limbs) {
- return 0;
- }
- if ((a->neg && !secp256k1_num_is_zero(a)) != (b->neg && !secp256k1_num_is_zero(b))) {
- return 0;
- }
- return mpn_cmp(a->data, b->data, a->limbs) == 0;
-}
-
-static void secp256k1_num_subadd(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b, int bneg) {
- if (!(b->neg ^ bneg ^ a->neg)) { /* a and b have the same sign */
- r->neg = a->neg;
- if (a->limbs >= b->limbs) {
- secp256k1_num_add_abs(r, a, b);
- } else {
- secp256k1_num_add_abs(r, b, a);
- }
- } else {
- if (secp256k1_num_cmp(a, b) > 0) {
- r->neg = a->neg;
- secp256k1_num_sub_abs(r, a, b);
- } else {
- r->neg = b->neg ^ bneg;
- secp256k1_num_sub_abs(r, b, a);
- }
- }
-}
-
-static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(b);
- secp256k1_num_subadd(r, a, b, 0);
-}
-
-static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(b);
- secp256k1_num_subadd(r, a, b, 1);
-}
-
-static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
- mp_limb_t tmp[2*NUM_LIMBS+1];
- secp256k1_num_sanity(a);
- secp256k1_num_sanity(b);
-
- VERIFY_CHECK(a->limbs + b->limbs <= 2*NUM_LIMBS+1);
- if ((a->limbs==1 && a->data[0]==0) || (b->limbs==1 && b->data[0]==0)) {
- r->limbs = 1;
- r->neg = 0;
- r->data[0] = 0;
- return;
- }
- if (a->limbs >= b->limbs) {
- mpn_mul(tmp, a->data, a->limbs, b->data, b->limbs);
- } else {
- mpn_mul(tmp, b->data, b->limbs, a->data, a->limbs);
- }
- r->limbs = a->limbs + b->limbs;
- if (r->limbs > 1 && tmp[r->limbs - 1]==0) {
- r->limbs--;
- }
- VERIFY_CHECK(r->limbs <= 2*NUM_LIMBS);
- mpn_copyi(r->data, tmp, r->limbs);
- r->neg = a->neg ^ b->neg;
- memset(tmp, 0, sizeof(tmp));
-}
-
-static void secp256k1_num_shift(secp256k1_num *r, int bits) {
- if (bits % GMP_NUMB_BITS) {
- /* Shift within limbs. */
- mpn_rshift(r->data, r->data, r->limbs, bits % GMP_NUMB_BITS);
- }
- if (bits >= GMP_NUMB_BITS) {
- int i;
- /* Shift full limbs. */
- for (i = 0; i < r->limbs; i++) {
- int index = i + (bits / GMP_NUMB_BITS);
- if (index < r->limbs && index < 2*NUM_LIMBS) {
- r->data[i] = r->data[index];
- } else {
- r->data[i] = 0;
- }
- }
- }
- while (r->limbs>1 && r->data[r->limbs-1]==0) {
- r->limbs--;
- }
-}
-
-static void secp256k1_num_negate(secp256k1_num *r) {
- r->neg ^= 1;
-}
-
-#endif /* SECP256K1_NUM_REPR_IMPL_H */
diff --git a/src/secp256k1/src/num_impl.h b/src/secp256k1/src/num_impl.h
deleted file mode 100644
index c45193b033..0000000000
--- a/src/secp256k1/src/num_impl.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef SECP256K1_NUM_IMPL_H
-#define SECP256K1_NUM_IMPL_H
-
-#if defined HAVE_CONFIG_H
-#include "libsecp256k1-config.h"
-#endif
-
-#include "num.h"
-
-#if defined(USE_NUM_GMP)
-#include "num_gmp_impl.h"
-#elif defined(USE_NUM_NONE)
-/* Nothing. */
-#else
-#error "Please select num implementation"
-#endif
-
-#endif /* SECP256K1_NUM_IMPL_H */
diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h
index fb3fb187ce..aaaa3d8827 100644
--- a/src/secp256k1/src/scalar.h
+++ b/src/secp256k1/src/scalar.h
@@ -1,13 +1,12 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_H
#define SECP256K1_SCALAR_H
-#include "num.h"
#include "util.h"
#if defined HAVE_CONFIG_H
@@ -63,9 +62,6 @@ static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a,
* the low bits that were shifted off */
static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n);
-/** Compute the square of a scalar (modulo the group order). */
-static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a);
-
/** Compute the inverse of a scalar (modulo the group order). */
static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a);
@@ -91,14 +87,6 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar *a);
* Returns -1 if the number was negated, 1 otherwise */
static int secp256k1_scalar_cond_negate(secp256k1_scalar *a, int flag);
-#ifndef USE_NUM_NONE
-/** Convert a scalar to a number. */
-static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a);
-
-/** Get the order of the group as a number. */
-static void secp256k1_scalar_order_get_num(secp256k1_num *r);
-#endif
-
/** Compare two scalars. */
static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b);
diff --git a/src/secp256k1/src/scalar_4x64.h b/src/secp256k1/src/scalar_4x64.h
index 19c7495d1c..700964291e 100644
--- a/src/secp256k1/src/scalar_4x64.h
+++ b/src/secp256k1/src/scalar_4x64.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_H
#define SECP256K1_SCALAR_REPR_H
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index 73cbd5e18a..a1def26fca 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -1,12 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_IMPL_H
#define SECP256K1_SCALAR_REPR_IMPL_H
+#include "modinv64_impl.h"
+
/* Limbs of the secp256k1 order. */
#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL)
#define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL)
@@ -212,28 +214,6 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
VERIFY_CHECK(c1 >= th); \
}
-/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */
-#define muladd2(a,b) { \
- uint64_t tl, th, th2, tl2; \
- { \
- uint128_t t = (uint128_t)a * b; \
- th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \
- tl = t; \
- } \
- th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \
- c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
- tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \
- th2 += (tl2 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
- c0 += tl2; /* overflow is handled on the next line */ \
- th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
- c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
- c1 += th2; /* overflow is handled on the next line */ \
- c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
-}
-
/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */
#define sumadd(a) { \
unsigned int over; \
@@ -743,148 +723,10 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c
#endif
}
-static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) {
-#ifdef USE_ASM_X86_64
- __asm__ __volatile__(
- /* Preload */
- "movq 0(%%rdi), %%r11\n"
- "movq 8(%%rdi), %%r12\n"
- "movq 16(%%rdi), %%r13\n"
- "movq 24(%%rdi), %%r14\n"
- /* (rax,rdx) = a0 * a0 */
- "movq %%r11, %%rax\n"
- "mulq %%r11\n"
- /* Extract l0 */
- "movq %%rax, 0(%%rsi)\n"
- /* (r8,r9,r10) = (rdx,0) */
- "movq %%rdx, %%r8\n"
- "xorq %%r9, %%r9\n"
- "xorq %%r10, %%r10\n"
- /* (r8,r9,r10) += 2 * a0 * a1 */
- "movq %%r11, %%rax\n"
- "mulq %%r12\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- /* Extract l1 */
- "movq %%r8, 8(%%rsi)\n"
- "xorq %%r8, %%r8\n"
- /* (r9,r10,r8) += 2 * a0 * a2 */
- "movq %%r11, %%rax\n"
- "mulq %%r13\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- /* (r9,r10,r8) += a1 * a1 */
- "movq %%r12, %%rax\n"
- "mulq %%r12\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- /* Extract l2 */
- "movq %%r9, 16(%%rsi)\n"
- "xorq %%r9, %%r9\n"
- /* (r10,r8,r9) += 2 * a0 * a3 */
- "movq %%r11, %%rax\n"
- "mulq %%r14\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- "adcq $0, %%r9\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- "adcq $0, %%r9\n"
- /* (r10,r8,r9) += 2 * a1 * a2 */
- "movq %%r12, %%rax\n"
- "mulq %%r13\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- "adcq $0, %%r9\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- "adcq $0, %%r9\n"
- /* Extract l3 */
- "movq %%r10, 24(%%rsi)\n"
- "xorq %%r10, %%r10\n"
- /* (r8,r9,r10) += 2 * a1 * a3 */
- "movq %%r12, %%rax\n"
- "mulq %%r14\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- /* (r8,r9,r10) += a2 * a2 */
- "movq %%r13, %%rax\n"
- "mulq %%r13\n"
- "addq %%rax, %%r8\n"
- "adcq %%rdx, %%r9\n"
- "adcq $0, %%r10\n"
- /* Extract l4 */
- "movq %%r8, 32(%%rsi)\n"
- "xorq %%r8, %%r8\n"
- /* (r9,r10,r8) += 2 * a2 * a3 */
- "movq %%r13, %%rax\n"
- "mulq %%r14\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- "addq %%rax, %%r9\n"
- "adcq %%rdx, %%r10\n"
- "adcq $0, %%r8\n"
- /* Extract l5 */
- "movq %%r9, 40(%%rsi)\n"
- /* (r10,r8) += a3 * a3 */
- "movq %%r14, %%rax\n"
- "mulq %%r14\n"
- "addq %%rax, %%r10\n"
- "adcq %%rdx, %%r8\n"
- /* Extract l6 */
- "movq %%r10, 48(%%rsi)\n"
- /* Extract l7 */
- "movq %%r8, 56(%%rsi)\n"
- :
- : "S"(l), "D"(a->d)
- : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory");
-#else
- /* 160 bit accumulator. */
- uint64_t c0 = 0, c1 = 0;
- uint32_t c2 = 0;
-
- /* l[0..7] = a[0..3] * b[0..3]. */
- muladd_fast(a->d[0], a->d[0]);
- extract_fast(l[0]);
- muladd2(a->d[0], a->d[1]);
- extract(l[1]);
- muladd2(a->d[0], a->d[2]);
- muladd(a->d[1], a->d[1]);
- extract(l[2]);
- muladd2(a->d[0], a->d[3]);
- muladd2(a->d[1], a->d[2]);
- extract(l[3]);
- muladd2(a->d[1], a->d[3]);
- muladd(a->d[2], a->d[2]);
- extract(l[4]);
- muladd2(a->d[2], a->d[3]);
- extract(l[5]);
- muladd_fast(a->d[3], a->d[3]);
- extract_fast(l[6]);
- VERIFY_CHECK(c1 == 0);
- l[7] = c0;
-#endif
-}
-
#undef sumadd
#undef sumadd_fast
#undef muladd
#undef muladd_fast
-#undef muladd2
#undef extract
#undef extract_fast
@@ -906,12 +748,6 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
return ret;
}
-static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
- uint64_t l[8];
- secp256k1_scalar_sqr_512(l, a);
- secp256k1_scalar_reduce_512(r, l);
-}
-
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
r1->d[0] = k->d[0];
r1->d[1] = k->d[1];
@@ -955,4 +791,78 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se
r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1);
}
+static void secp256k1_scalar_from_signed62(secp256k1_scalar *r, const secp256k1_modinv64_signed62 *a) {
+ const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4];
+
+ /* The output from secp256k1_modinv64{_var} should be normalized to range [0,modulus), and
+ * have limbs in [0,2^62). The modulus is < 2^256, so the top limb must be below 2^(256-62*4).
+ */
+ VERIFY_CHECK(a0 >> 62 == 0);
+ VERIFY_CHECK(a1 >> 62 == 0);
+ VERIFY_CHECK(a2 >> 62 == 0);
+ VERIFY_CHECK(a3 >> 62 == 0);
+ VERIFY_CHECK(a4 >> 8 == 0);
+
+ r->d[0] = a0 | a1 << 62;
+ r->d[1] = a1 >> 2 | a2 << 60;
+ r->d[2] = a2 >> 4 | a3 << 58;
+ r->d[3] = a3 >> 6 | a4 << 56;
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
+#endif
+}
+
+static void secp256k1_scalar_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_scalar *a) {
+ const uint64_t M62 = UINT64_MAX >> 2;
+ const uint64_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3];
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(a) == 0);
+#endif
+
+ r->v[0] = a0 & M62;
+ r->v[1] = (a0 >> 62 | a1 << 2) & M62;
+ r->v[2] = (a1 >> 60 | a2 << 4) & M62;
+ r->v[3] = (a2 >> 58 | a3 << 6) & M62;
+ r->v[4] = a3 >> 56;
+}
+
+static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_scalar = {
+ {{0x3FD25E8CD0364141LL, 0x2ABB739ABD2280EELL, -0x15LL, 0, 256}},
+ 0x34F20099AA774EC1LL
+};
+
+static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_modinv64_signed62 s;
+#ifdef VERIFY
+ int zero_in = secp256k1_scalar_is_zero(x);
+#endif
+ secp256k1_scalar_to_signed62(&s, x);
+ secp256k1_modinv64(&s, &secp256k1_const_modinfo_scalar);
+ secp256k1_scalar_from_signed62(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in);
+#endif
+}
+
+static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_modinv64_signed62 s;
+#ifdef VERIFY
+ int zero_in = secp256k1_scalar_is_zero(x);
+#endif
+ secp256k1_scalar_to_signed62(&s, x);
+ secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_scalar);
+ secp256k1_scalar_from_signed62(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in);
+#endif
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
+ return !(a->d[0] & 1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_8x32.h b/src/secp256k1/src/scalar_8x32.h
index 2c9a348e24..17863ef937 100644
--- a/src/secp256k1/src/scalar_8x32.h
+++ b/src/secp256k1/src/scalar_8x32.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_H
#define SECP256K1_SCALAR_REPR_H
diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h
index 6853f79ecc..62c7ae7156 100644
--- a/src/secp256k1/src/scalar_8x32_impl.h
+++ b/src/secp256k1/src/scalar_8x32_impl.h
@@ -1,12 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_IMPL_H
#define SECP256K1_SCALAR_REPR_IMPL_H
+#include "modinv32_impl.h"
+
/* Limbs of the secp256k1 order. */
#define SECP256K1_N_0 ((uint32_t)0xD0364141UL)
#define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL)
@@ -291,28 +293,6 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
VERIFY_CHECK(c1 >= th); \
}
-/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */
-#define muladd2(a,b) { \
- uint32_t tl, th, th2, tl2; \
- { \
- uint64_t t = (uint64_t)a * b; \
- th = t >> 32; /* at most 0xFFFFFFFE */ \
- tl = t; \
- } \
- th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \
- c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
- tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \
- th2 += (tl2 < tl); /* at most 0xFFFFFFFF */ \
- c0 += tl2; /* overflow is handled on the next line */ \
- th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
- c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
- c1 += th2; /* overflow is handled on the next line */ \
- c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
- VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
-}
-
/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */
#define sumadd(a) { \
unsigned int over; \
@@ -576,71 +556,10 @@ static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, con
l[15] = c0;
}
-static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) {
- /* 96 bit accumulator. */
- uint32_t c0 = 0, c1 = 0, c2 = 0;
-
- /* l[0..15] = a[0..7]^2. */
- muladd_fast(a->d[0], a->d[0]);
- extract_fast(l[0]);
- muladd2(a->d[0], a->d[1]);
- extract(l[1]);
- muladd2(a->d[0], a->d[2]);
- muladd(a->d[1], a->d[1]);
- extract(l[2]);
- muladd2(a->d[0], a->d[3]);
- muladd2(a->d[1], a->d[2]);
- extract(l[3]);
- muladd2(a->d[0], a->d[4]);
- muladd2(a->d[1], a->d[3]);
- muladd(a->d[2], a->d[2]);
- extract(l[4]);
- muladd2(a->d[0], a->d[5]);
- muladd2(a->d[1], a->d[4]);
- muladd2(a->d[2], a->d[3]);
- extract(l[5]);
- muladd2(a->d[0], a->d[6]);
- muladd2(a->d[1], a->d[5]);
- muladd2(a->d[2], a->d[4]);
- muladd(a->d[3], a->d[3]);
- extract(l[6]);
- muladd2(a->d[0], a->d[7]);
- muladd2(a->d[1], a->d[6]);
- muladd2(a->d[2], a->d[5]);
- muladd2(a->d[3], a->d[4]);
- extract(l[7]);
- muladd2(a->d[1], a->d[7]);
- muladd2(a->d[2], a->d[6]);
- muladd2(a->d[3], a->d[5]);
- muladd(a->d[4], a->d[4]);
- extract(l[8]);
- muladd2(a->d[2], a->d[7]);
- muladd2(a->d[3], a->d[6]);
- muladd2(a->d[4], a->d[5]);
- extract(l[9]);
- muladd2(a->d[3], a->d[7]);
- muladd2(a->d[4], a->d[6]);
- muladd(a->d[5], a->d[5]);
- extract(l[10]);
- muladd2(a->d[4], a->d[7]);
- muladd2(a->d[5], a->d[6]);
- extract(l[11]);
- muladd2(a->d[5], a->d[7]);
- muladd(a->d[6], a->d[6]);
- extract(l[12]);
- muladd2(a->d[6], a->d[7]);
- extract(l[13]);
- muladd_fast(a->d[7], a->d[7]);
- extract_fast(l[14]);
- VERIFY_CHECK(c1 == 0);
- l[15] = c0;
-}
-
#undef sumadd
#undef sumadd_fast
#undef muladd
#undef muladd_fast
-#undef muladd2
#undef extract
#undef extract_fast
@@ -666,12 +585,6 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
return ret;
}
-static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
- uint32_t l[16];
- secp256k1_scalar_sqr_512(l, a);
- secp256k1_scalar_reduce_512(r, l);
-}
-
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
r1->d[0] = k->d[0];
r1->d[1] = k->d[1];
@@ -731,4 +644,92 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se
r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1);
}
+static void secp256k1_scalar_from_signed30(secp256k1_scalar *r, const secp256k1_modinv32_signed30 *a) {
+ const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4],
+ a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8];
+
+ /* The output from secp256k1_modinv32{_var} should be normalized to range [0,modulus), and
+ * have limbs in [0,2^30). The modulus is < 2^256, so the top limb must be below 2^(256-30*8).
+ */
+ VERIFY_CHECK(a0 >> 30 == 0);
+ VERIFY_CHECK(a1 >> 30 == 0);
+ VERIFY_CHECK(a2 >> 30 == 0);
+ VERIFY_CHECK(a3 >> 30 == 0);
+ VERIFY_CHECK(a4 >> 30 == 0);
+ VERIFY_CHECK(a5 >> 30 == 0);
+ VERIFY_CHECK(a6 >> 30 == 0);
+ VERIFY_CHECK(a7 >> 30 == 0);
+ VERIFY_CHECK(a8 >> 16 == 0);
+
+ r->d[0] = a0 | a1 << 30;
+ r->d[1] = a1 >> 2 | a2 << 28;
+ r->d[2] = a2 >> 4 | a3 << 26;
+ r->d[3] = a3 >> 6 | a4 << 24;
+ r->d[4] = a4 >> 8 | a5 << 22;
+ r->d[5] = a5 >> 10 | a6 << 20;
+ r->d[6] = a6 >> 12 | a7 << 18;
+ r->d[7] = a7 >> 14 | a8 << 16;
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
+#endif
+}
+
+static void secp256k1_scalar_to_signed30(secp256k1_modinv32_signed30 *r, const secp256k1_scalar *a) {
+ const uint32_t M30 = UINT32_MAX >> 2;
+ const uint32_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3],
+ a4 = a->d[4], a5 = a->d[5], a6 = a->d[6], a7 = a->d[7];
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_check_overflow(a) == 0);
+#endif
+
+ r->v[0] = a0 & M30;
+ r->v[1] = (a0 >> 30 | a1 << 2) & M30;
+ r->v[2] = (a1 >> 28 | a2 << 4) & M30;
+ r->v[3] = (a2 >> 26 | a3 << 6) & M30;
+ r->v[4] = (a3 >> 24 | a4 << 8) & M30;
+ r->v[5] = (a4 >> 22 | a5 << 10) & M30;
+ r->v[6] = (a5 >> 20 | a6 << 12) & M30;
+ r->v[7] = (a6 >> 18 | a7 << 14) & M30;
+ r->v[8] = a7 >> 16;
+}
+
+static const secp256k1_modinv32_modinfo secp256k1_const_modinfo_scalar = {
+ {{0x10364141L, 0x3F497A33L, 0x348A03BBL, 0x2BB739ABL, -0x146L, 0, 0, 0, 65536}},
+ 0x2A774EC1L
+};
+
+static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_modinv32_signed30 s;
+#ifdef VERIFY
+ int zero_in = secp256k1_scalar_is_zero(x);
+#endif
+ secp256k1_scalar_to_signed30(&s, x);
+ secp256k1_modinv32(&s, &secp256k1_const_modinfo_scalar);
+ secp256k1_scalar_from_signed30(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in);
+#endif
+}
+
+static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_modinv32_signed30 s;
+#ifdef VERIFY
+ int zero_in = secp256k1_scalar_is_zero(x);
+#endif
+ secp256k1_scalar_to_signed30(&s, x);
+ secp256k1_modinv32_var(&s, &secp256k1_const_modinfo_scalar);
+ secp256k1_scalar_from_signed30(r, &s);
+
+#ifdef VERIFY
+ VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in);
+#endif
+}
+
+SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
+ return !(a->d[0] & 1);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index fc75891818..e124474773 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_IMPL_H
#define SECP256K1_SCALAR_IMPL_H
@@ -31,231 +31,12 @@
static const secp256k1_scalar secp256k1_scalar_one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1);
static const secp256k1_scalar secp256k1_scalar_zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
-#ifndef USE_NUM_NONE
-static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a) {
- unsigned char c[32];
- secp256k1_scalar_get_b32(c, a);
- secp256k1_num_set_bin(r, c, 32);
-}
-
-/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */
-static void secp256k1_scalar_order_get_num(secp256k1_num *r) {
-#if defined(EXHAUSTIVE_TEST_ORDER)
- static const unsigned char order[32] = {
- 0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER
- };
-#else
- static const unsigned char order[32] = {
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
- 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
- 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41
- };
-#endif
- secp256k1_num_set_bin(r, order, 32);
-}
-#endif
-
static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin) {
int overflow;
secp256k1_scalar_set_b32(r, bin, &overflow);
return (!overflow) & (!secp256k1_scalar_is_zero(r));
}
-static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
-#if defined(EXHAUSTIVE_TEST_ORDER)
- int i;
- *r = 0;
- for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++)
- if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1)
- *r = i;
- /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus
- * have a composite group order; fix it in exhaustive_tests.c). */
- VERIFY_CHECK(*r != 0);
-}
-#else
- secp256k1_scalar *t;
- int i;
- /* First compute xN as x ^ (2^N - 1) for some values of N,
- * and uM as x ^ M for some values of M. */
- secp256k1_scalar x2, x3, x6, x8, x14, x28, x56, x112, x126;
- secp256k1_scalar u2, u5, u9, u11, u13;
-
- secp256k1_scalar_sqr(&u2, x);
- secp256k1_scalar_mul(&x2, &u2, x);
- secp256k1_scalar_mul(&u5, &u2, &x2);
- secp256k1_scalar_mul(&x3, &u5, &u2);
- secp256k1_scalar_mul(&u9, &x3, &u2);
- secp256k1_scalar_mul(&u11, &u9, &u2);
- secp256k1_scalar_mul(&u13, &u11, &u2);
-
- secp256k1_scalar_sqr(&x6, &u13);
- secp256k1_scalar_sqr(&x6, &x6);
- secp256k1_scalar_mul(&x6, &x6, &u11);
-
- secp256k1_scalar_sqr(&x8, &x6);
- secp256k1_scalar_sqr(&x8, &x8);
- secp256k1_scalar_mul(&x8, &x8, &x2);
-
- secp256k1_scalar_sqr(&x14, &x8);
- for (i = 0; i < 5; i++) {
- secp256k1_scalar_sqr(&x14, &x14);
- }
- secp256k1_scalar_mul(&x14, &x14, &x6);
-
- secp256k1_scalar_sqr(&x28, &x14);
- for (i = 0; i < 13; i++) {
- secp256k1_scalar_sqr(&x28, &x28);
- }
- secp256k1_scalar_mul(&x28, &x28, &x14);
-
- secp256k1_scalar_sqr(&x56, &x28);
- for (i = 0; i < 27; i++) {
- secp256k1_scalar_sqr(&x56, &x56);
- }
- secp256k1_scalar_mul(&x56, &x56, &x28);
-
- secp256k1_scalar_sqr(&x112, &x56);
- for (i = 0; i < 55; i++) {
- secp256k1_scalar_sqr(&x112, &x112);
- }
- secp256k1_scalar_mul(&x112, &x112, &x56);
-
- secp256k1_scalar_sqr(&x126, &x112);
- for (i = 0; i < 13; i++) {
- secp256k1_scalar_sqr(&x126, &x126);
- }
- secp256k1_scalar_mul(&x126, &x126, &x14);
-
- /* Then accumulate the final result (t starts at x126). */
- t = &x126;
- for (i = 0; i < 3; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u5); /* 101 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u5); /* 101 */
- for (i = 0; i < 5; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u11); /* 1011 */
- for (i = 0; i < 4; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u11); /* 1011 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 5; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 6; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u13); /* 1101 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u5); /* 101 */
- for (i = 0; i < 3; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 5; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u9); /* 1001 */
- for (i = 0; i < 6; i++) { /* 000 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u5); /* 101 */
- for (i = 0; i < 10; i++) { /* 0000000 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 4; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x3); /* 111 */
- for (i = 0; i < 9; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x8); /* 11111111 */
- for (i = 0; i < 5; i++) { /* 0 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u9); /* 1001 */
- for (i = 0; i < 6; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u11); /* 1011 */
- for (i = 0; i < 4; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u13); /* 1101 */
- for (i = 0; i < 5; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &x2); /* 11 */
- for (i = 0; i < 6; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u13); /* 1101 */
- for (i = 0; i < 10; i++) { /* 000000 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u13); /* 1101 */
- for (i = 0; i < 4; i++) {
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, &u9); /* 1001 */
- for (i = 0; i < 6; i++) { /* 00000 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(t, t, x); /* 1 */
- for (i = 0; i < 8; i++) { /* 00 */
- secp256k1_scalar_sqr(t, t);
- }
- secp256k1_scalar_mul(r, t, &x6); /* 111111 */
-}
-
-SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
- return !(a->d[0] & 1);
-}
-#endif
-
-static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
-#if defined(USE_SCALAR_INV_BUILTIN)
- secp256k1_scalar_inverse(r, x);
-#elif defined(USE_SCALAR_INV_NUM)
- unsigned char b[32];
- secp256k1_num n, m;
- secp256k1_scalar t = *x;
- secp256k1_scalar_get_b32(b, &t);
- secp256k1_num_set_bin(&n, b, 32);
- secp256k1_scalar_order_get_num(&m);
- secp256k1_num_mod_inverse(&n, &n, &m);
- secp256k1_num_get_bin(b, 32, &n);
- secp256k1_scalar_set_b32(r, b, NULL);
- /* Verify that the inverse was computed correctly, without GMP code. */
- secp256k1_scalar_mul(&t, &t, r);
- CHECK(secp256k1_scalar_is_one(&t));
-#else
-#error "Please select scalar inverse implementation"
-#endif
-}
-
/* These parameters are generated using sage/gen_exhaustive_groups.sage. */
#if defined(EXHAUSTIVE_TEST_ORDER)
# if EXHAUSTIVE_TEST_ORDER == 13
diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h
index 2794a7f171..67051bd30b 100644
--- a/src/secp256k1/src/scalar_low.h
+++ b/src/secp256k1/src/scalar_low.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_H
#define SECP256K1_SCALAR_REPR_H
diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h
index a615ec074b..7176f0b2ca 100644
--- a/src/secp256k1/src/scalar_low_impl.h
+++ b/src/secp256k1/src/scalar_low_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2015 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SCALAR_REPR_IMPL_H
#define SECP256K1_SCALAR_REPR_IMPL_H
@@ -104,10 +104,6 @@ static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
return ret;
}
-static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
- *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER;
-}
-
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
*r1 = *a;
*r2 = 0;
@@ -125,4 +121,19 @@ static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const se
*r = (*r & mask0) | (*a & mask1);
}
+static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ int i;
+ *r = 0;
+ for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++)
+ if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1)
+ *r = i;
+ /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus
+ * have a composite group order; fix it in exhaustive_tests.c). */
+ VERIFY_CHECK(*r != 0);
+}
+
+static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
+ secp256k1_scalar_inverse(r, x);
+}
+
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
diff --git a/src/secp256k1/src/scratch.h b/src/secp256k1/src/scratch.h
index 77b35d126b..9dcb7581f6 100644
--- a/src/secp256k1/src/scratch.h
+++ b/src/secp256k1/src/scratch.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2017 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
-
-#ifndef _SECP256K1_SCRATCH_
-#define _SECP256K1_SCRATCH_
+/***********************************************************************
+ * Copyright (c) 2017 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
+
+#ifndef SECP256K1_SCRATCH_H
+#define SECP256K1_SCRATCH_H
/* The typedef is used internally; the struct name is used in the public API
* (where it is exposed as a different typedef) */
diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h
index f381e2e322..688e18eb66 100644
--- a/src/secp256k1/src/scratch_impl.h
+++ b/src/secp256k1/src/scratch_impl.h
@@ -1,11 +1,11 @@
-/**********************************************************************
- * Copyright (c) 2017 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2017 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
-#ifndef _SECP256K1_SCRATCH_IMPL_H_
-#define _SECP256K1_SCRATCH_IMPL_H_
+#ifndef SECP256K1_SCRATCH_IMPL_H
+#define SECP256K1_SCRATCH_IMPL_H
#include "util.h"
#include "scratch.h"
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index dae506d08c..aef3f99ac3 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -1,15 +1,14 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include "include/secp256k1.h"
#include "include/secp256k1_preallocated.h"
#include "assumptions.h"
#include "util.h"
-#include "num_impl.h"
#include "field_impl.h"
#include "scalar_impl.h"
#include "group_impl.h"
@@ -86,6 +85,8 @@ const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_pr
size_t secp256k1_context_preallocated_size(unsigned int flags) {
size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context));
+ /* A return value of 0 is reserved as an indicator for errors when we call this function internally. */
+ VERIFY_CHECK(ret != 0);
if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
secp256k1_callback_call(&default_illegal_callback,
@@ -122,21 +123,21 @@ secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigne
if (!secp256k1_selftest()) {
secp256k1_callback_call(&default_error_callback, "self test failed");
}
- VERIFY_CHECK(prealloc != NULL);
+
prealloc_size = secp256k1_context_preallocated_size(flags);
+ if (prealloc_size == 0) {
+ return NULL;
+ }
+ VERIFY_CHECK(prealloc != NULL);
ret = (secp256k1_context*)manual_alloc(&prealloc, sizeof(secp256k1_context), base, prealloc_size);
ret->illegal_callback = default_illegal_callback;
ret->error_callback = default_error_callback;
- if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
- secp256k1_callback_call(&ret->illegal_callback,
- "Invalid flags");
- return NULL;
- }
-
secp256k1_ecmult_context_init(&ret->ecmult_ctx);
secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx);
+ /* Flags have been checked by secp256k1_context_preallocated_size. */
+ VERIFY_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_CONTEXT);
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &prealloc);
}
@@ -420,17 +421,17 @@ int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_
return ret;
}
-int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey) {
+int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msghash32, const secp256k1_pubkey *pubkey) {
secp256k1_ge q;
secp256k1_scalar r, s;
secp256k1_scalar m;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
- ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(msghash32 != NULL);
ARG_CHECK(sig != NULL);
ARG_CHECK(pubkey != NULL);
- secp256k1_scalar_set_b32(&m, msg32, NULL);
+ secp256k1_scalar_set_b32(&m, msghash32, NULL);
secp256k1_ecdsa_signature_load(ctx, &r, &s, sig);
return (!secp256k1_scalar_is_high(&s) &&
secp256k1_pubkey_load(ctx, &q, pubkey) &&
@@ -531,16 +532,16 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc
return ret;
}
-int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
+int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msghash32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
secp256k1_scalar r, s;
int ret;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
- ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(msghash32 != NULL);
ARG_CHECK(signature != NULL);
ARG_CHECK(seckey != NULL);
- ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msg32, seckey, noncefp, noncedata);
+ ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msghash32, seckey, noncefp, noncedata);
secp256k1_ecdsa_signature_save(signature, &r, &s);
return ret;
}
@@ -580,7 +581,7 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey);
secp256k1_pubkey_save(pubkey, &p);
- memczero(pubkey, sizeof(*pubkey), !ret);
+ secp256k1_memczero(pubkey, sizeof(*pubkey), !ret);
secp256k1_scalar_clear(&seckey_scalar);
return ret;
@@ -621,26 +622,26 @@ int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *p
}
-static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak) {
+static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak32) {
secp256k1_scalar term;
int overflow = 0;
int ret = 0;
- secp256k1_scalar_set_b32(&term, tweak, &overflow);
+ secp256k1_scalar_set_b32(&term, tweak32, &overflow);
ret = (!overflow) & secp256k1_eckey_privkey_tweak_add(sec, &term);
secp256k1_scalar_clear(&term);
return ret;
}
-int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) {
secp256k1_scalar sec;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- ARG_CHECK(tweak != NULL);
+ ARG_CHECK(tweak32 != NULL);
ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
- ret &= secp256k1_ec_seckey_tweak_add_helper(&sec, tweak);
+ ret &= secp256k1_ec_seckey_tweak_add_helper(&sec, tweak32);
secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
secp256k1_scalar_get_b32(seckey, &sec);
@@ -648,28 +649,28 @@ int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *s
return ret;
}
-int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
- return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak);
+int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) {
+ return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak32);
}
-static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak) {
+static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak32) {
secp256k1_scalar term;
int overflow = 0;
- secp256k1_scalar_set_b32(&term, tweak, &overflow);
+ secp256k1_scalar_set_b32(&term, tweak32, &overflow);
return !overflow && secp256k1_eckey_pubkey_tweak_add(ecmult_ctx, p, &term);
}
-int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
+int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32) {
secp256k1_ge p;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(pubkey != NULL);
- ARG_CHECK(tweak != NULL);
+ ARG_CHECK(tweak32 != NULL);
ret = secp256k1_pubkey_load(ctx, &p, pubkey);
memset(pubkey, 0, sizeof(*pubkey));
- ret = ret && secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &p, tweak);
+ ret = ret && secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &p, tweak32);
if (ret) {
secp256k1_pubkey_save(pubkey, &p);
}
@@ -677,16 +678,16 @@ int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey
return ret;
}
-int secp256k1_ec_seckey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+int secp256k1_ec_seckey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) {
secp256k1_scalar factor;
secp256k1_scalar sec;
int ret = 0;
int overflow = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
- ARG_CHECK(tweak != NULL);
+ ARG_CHECK(tweak32 != NULL);
- secp256k1_scalar_set_b32(&factor, tweak, &overflow);
+ secp256k1_scalar_set_b32(&factor, tweak32, &overflow);
ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
ret &= (!overflow) & secp256k1_eckey_privkey_tweak_mul(&sec, &factor);
secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
@@ -697,11 +698,11 @@ int secp256k1_ec_seckey_tweak_mul(const secp256k1_context* ctx, unsigned char *s
return ret;
}
-int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
- return secp256k1_ec_seckey_tweak_mul(ctx, seckey, tweak);
+int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) {
+ return secp256k1_ec_seckey_tweak_mul(ctx, seckey, tweak32);
}
-int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
+int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32) {
secp256k1_ge p;
secp256k1_scalar factor;
int ret = 0;
@@ -709,9 +710,9 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(pubkey != NULL);
- ARG_CHECK(tweak != NULL);
+ ARG_CHECK(tweak32 != NULL);
- secp256k1_scalar_set_b32(&factor, tweak, &overflow);
+ secp256k1_scalar_set_b32(&factor, tweak32, &overflow);
ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey);
memset(pubkey, 0, sizeof(*pubkey));
if (ret) {
diff --git a/src/secp256k1/src/selftest.h b/src/secp256k1/src/selftest.h
index 0e37510c1e..52f1b8442e 100644
--- a/src/secp256k1/src/selftest.h
+++ b/src/secp256k1/src/selftest.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2020 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_SELFTEST_H
#define SECP256K1_SELFTEST_H
diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h
index a76003d5b8..667d1867bd 100644
--- a/src/secp256k1/src/testrand.h
+++ b/src/secp256k1/src/testrand.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_TESTRAND_H
#define SECP256K1_TESTRAND_H
diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h
index 3392566329..e643778f36 100644
--- a/src/secp256k1/src/testrand_impl.h
+++ b/src/secp256k1/src/testrand_impl.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013-2015 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013-2015 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_TESTRAND_IMPL_H
#define SECP256K1_TESTRAND_IMPL_H
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index bb4b5b4c07..a146394305 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -18,12 +18,13 @@
#include "include/secp256k1.h"
#include "include/secp256k1_preallocated.h"
#include "testrand_impl.h"
+#include "util.h"
#ifdef ENABLE_OPENSSL_TESTS
-#include "openssl/bn.h"
-#include "openssl/ec.h"
-#include "openssl/ecdsa.h"
-#include "openssl/obj_mac.h"
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/obj_mac.h>
# if OPENSSL_VERSION_NUMBER < 0x10100000L
void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {*pr = sig->r; *ps = sig->s;}
# endif
@@ -32,6 +33,11 @@ void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
#include "contrib/lax_der_parsing.c"
#include "contrib/lax_der_privatekey_parsing.c"
+#include "modinv32_impl.h"
+#ifdef SECP256K1_WIDEMUL_INT128
+#include "modinv64_impl.h"
+#endif
+
static int count = 64;
static secp256k1_context *ctx = NULL;
@@ -416,6 +422,25 @@ void run_scratch_tests(void) {
secp256k1_context_destroy(none);
}
+void run_ctz_tests(void) {
+ static const uint32_t b32[] = {1, 0xffffffff, 0x5e56968f, 0xe0d63129};
+ static const uint64_t b64[] = {1, 0xffffffffffffffff, 0xbcd02462139b3fc3, 0x98b5f80c769693ef};
+ int shift;
+ unsigned i;
+ for (i = 0; i < sizeof(b32) / sizeof(b32[0]); ++i) {
+ for (shift = 0; shift < 32; ++shift) {
+ CHECK(secp256k1_ctz32_var_debruijn(b32[i] << shift) == shift);
+ CHECK(secp256k1_ctz32_var(b32[i] << shift) == shift);
+ }
+ }
+ for (i = 0; i < sizeof(b64) / sizeof(b64[0]); ++i) {
+ for (shift = 0; shift < 64; ++shift) {
+ CHECK(secp256k1_ctz64_var_debruijn(b64[i] << shift) == shift);
+ CHECK(secp256k1_ctz64_var(b64[i] << shift) == shift);
+ }
+ }
+}
+
/***** HASH TESTS *****/
void run_sha256_tests(void) {
@@ -611,202 +636,924 @@ void run_rand_int(void) {
}
}
-/***** NUM TESTS *****/
+/***** MODINV TESTS *****/
+
+/* Compute the modular inverse of (odd) x mod 2^64. */
+uint64_t modinv2p64(uint64_t x) {
+ /* If w = 1/x mod 2^(2^L), then w*(2 - w*x) = 1/x mod 2^(2^(L+1)). See
+ * Hacker's Delight second edition, Henry S. Warren, Jr., pages 245-247 for
+ * why. Start with L=0, for which it is true for every odd x that
+ * 1/x=1 mod 2. Iterating 6 times gives us 1/x mod 2^64. */
+ int l;
+ uint64_t w = 1;
+ CHECK(x & 1);
+ for (l = 0; l < 6; ++l) w *= (2 - w*x);
+ return w;
+}
-#ifndef USE_NUM_NONE
-void random_num_negate(secp256k1_num *num) {
- if (secp256k1_testrand_bits(1)) {
- secp256k1_num_negate(num);
+/* compute out = (a*b) mod m; if b=NULL, treat b=1.
+ *
+ * Out is a 512-bit number (represented as 32 uint16_t's in LE order). The other
+ * arguments are 256-bit numbers (represented as 16 uint16_t's in LE order). */
+void mulmod256(uint16_t* out, const uint16_t* a, const uint16_t* b, const uint16_t* m) {
+ uint16_t mul[32];
+ uint64_t c = 0;
+ int i, j;
+ int m_bitlen = 0;
+ int mul_bitlen = 0;
+
+ if (b != NULL) {
+ /* Compute the product of a and b, and put it in mul. */
+ for (i = 0; i < 32; ++i) {
+ for (j = i <= 15 ? 0 : i - 15; j <= i && j <= 15; j++) {
+ c += (uint64_t)a[j] * b[i - j];
+ }
+ mul[i] = c & 0xFFFF;
+ c >>= 16;
+ }
+ CHECK(c == 0);
+
+ /* compute the highest set bit in mul */
+ for (i = 511; i >= 0; --i) {
+ if ((mul[i >> 4] >> (i & 15)) & 1) {
+ mul_bitlen = i;
+ break;
+ }
+ }
+ } else {
+ /* if b==NULL, set mul=a. */
+ memcpy(mul, a, 32);
+ memset(mul + 16, 0, 32);
+ /* compute the highest set bit in mul */
+ for (i = 255; i >= 0; --i) {
+ if ((mul[i >> 4] >> (i & 15)) & 1) {
+ mul_bitlen = i;
+ break;
+ }
+ }
}
-}
-void random_num_order_test(secp256k1_num *num) {
- secp256k1_scalar sc;
- random_scalar_order_test(&sc);
- secp256k1_scalar_get_num(num, &sc);
+ /* Compute the highest set bit in m. */
+ for (i = 255; i >= 0; --i) {
+ if ((m[i >> 4] >> (i & 15)) & 1) {
+ m_bitlen = i;
+ break;
+ }
+ }
+
+ /* Try do mul -= m<<i, for i going down to 0, whenever the result is not negative */
+ for (i = mul_bitlen - m_bitlen; i >= 0; --i) {
+ uint16_t mul2[32];
+ int64_t cs;
+
+ /* Compute mul2 = mul - m<<i. */
+ cs = 0; /* accumulator */
+ for (j = 0; j < 32; ++j) { /* j loops over the output limbs in mul2. */
+ /* Compute sub: the 16 bits in m that will be subtracted from mul2[j]. */
+ uint16_t sub = 0;
+ int p;
+ for (p = 0; p < 16; ++p) { /* p loops over the bit positions in mul2[j]. */
+ int bitpos = j * 16 - i + p; /* bitpos is the correspond bit position in m. */
+ if (bitpos >= 0 && bitpos < 256) {
+ sub |= ((m[bitpos >> 4] >> (bitpos & 15)) & 1) << p;
+ }
+ }
+ /* Add mul[j]-sub to accumulator, and shift bottom 16 bits out to mul2[j]. */
+ cs += mul[j];
+ cs -= sub;
+ mul2[j] = (cs & 0xFFFF);
+ cs >>= 16;
+ }
+ /* If remainder of subtraction is 0, set mul = mul2. */
+ if (cs == 0) {
+ memcpy(mul, mul2, sizeof(mul));
+ }
+ }
+ /* Sanity check: test that all limbs higher than m's highest are zero */
+ for (i = (m_bitlen >> 4) + 1; i < 32; ++i) {
+ CHECK(mul[i] == 0);
+ }
+ memcpy(out, mul, 32);
}
-void random_num_order(secp256k1_num *num) {
- secp256k1_scalar sc;
- random_scalar_order(&sc);
- secp256k1_scalar_get_num(num, &sc);
+/* Convert a 256-bit number represented as 16 uint16_t's to signed30 notation. */
+void uint16_to_signed30(secp256k1_modinv32_signed30* out, const uint16_t* in) {
+ int i;
+ memset(out->v, 0, sizeof(out->v));
+ for (i = 0; i < 256; ++i) {
+ out->v[i / 30] |= (int32_t)(((in[i >> 4]) >> (i & 15)) & 1) << (i % 30);
+ }
}
-void test_num_negate(void) {
- secp256k1_num n1;
- secp256k1_num n2;
- random_num_order_test(&n1); /* n1 = R */
- random_num_negate(&n1);
- secp256k1_num_copy(&n2, &n1); /* n2 = R */
- secp256k1_num_sub(&n1, &n2, &n1); /* n1 = n2-n1 = 0 */
- CHECK(secp256k1_num_is_zero(&n1));
- secp256k1_num_copy(&n1, &n2); /* n1 = R */
- secp256k1_num_negate(&n1); /* n1 = -R */
- CHECK(!secp256k1_num_is_zero(&n1));
- secp256k1_num_add(&n1, &n2, &n1); /* n1 = n2+n1 = 0 */
- CHECK(secp256k1_num_is_zero(&n1));
- secp256k1_num_copy(&n1, &n2); /* n1 = R */
- secp256k1_num_negate(&n1); /* n1 = -R */
- CHECK(secp256k1_num_is_neg(&n1) != secp256k1_num_is_neg(&n2));
- secp256k1_num_negate(&n1); /* n1 = R */
- CHECK(secp256k1_num_eq(&n1, &n2));
+/* Convert a 256-bit number in signed30 notation to a representation as 16 uint16_t's. */
+void signed30_to_uint16(uint16_t* out, const secp256k1_modinv32_signed30* in) {
+ int i;
+ memset(out, 0, 32);
+ for (i = 0; i < 256; ++i) {
+ out[i >> 4] |= (((in->v[i / 30]) >> (i % 30)) & 1) << (i & 15);
+ }
}
-void test_num_add_sub(void) {
+/* Randomly mutate the sign of limbs in signed30 representation, without changing the value. */
+void mutate_sign_signed30(secp256k1_modinv32_signed30* x) {
int i;
- secp256k1_scalar s;
- secp256k1_num n1;
- secp256k1_num n2;
- secp256k1_num n1p2, n2p1, n1m2, n2m1;
- random_num_order_test(&n1); /* n1 = R1 */
- if (secp256k1_testrand_bits(1)) {
- random_num_negate(&n1);
+ for (i = 0; i < 16; ++i) {
+ int pos = secp256k1_testrand_int(8);
+ if (x->v[pos] > 0 && x->v[pos + 1] <= 0x3fffffff) {
+ x->v[pos] -= 0x40000000;
+ x->v[pos + 1] += 1;
+ } else if (x->v[pos] < 0 && x->v[pos + 1] >= 0x3fffffff) {
+ x->v[pos] += 0x40000000;
+ x->v[pos + 1] -= 1;
+ }
}
- random_num_order_test(&n2); /* n2 = R2 */
- if (secp256k1_testrand_bits(1)) {
- random_num_negate(&n2);
- }
- secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = R1 + R2 */
- secp256k1_num_add(&n2p1, &n2, &n1); /* n2p1 = R2 + R1 */
- secp256k1_num_sub(&n1m2, &n1, &n2); /* n1m2 = R1 - R2 */
- secp256k1_num_sub(&n2m1, &n2, &n1); /* n2m1 = R2 - R1 */
- CHECK(secp256k1_num_eq(&n1p2, &n2p1));
- CHECK(!secp256k1_num_eq(&n1p2, &n1m2));
- secp256k1_num_negate(&n2m1); /* n2m1 = -R2 + R1 */
- CHECK(secp256k1_num_eq(&n2m1, &n1m2));
- CHECK(!secp256k1_num_eq(&n2m1, &n1));
- secp256k1_num_add(&n2m1, &n2m1, &n2); /* n2m1 = -R2 + R1 + R2 = R1 */
- CHECK(secp256k1_num_eq(&n2m1, &n1));
- CHECK(!secp256k1_num_eq(&n2p1, &n1));
- secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */
- CHECK(secp256k1_num_eq(&n2p1, &n1));
-
- /* check is_one */
- secp256k1_scalar_set_int(&s, 1);
- secp256k1_scalar_get_num(&n1, &s);
- CHECK(secp256k1_num_is_one(&n1));
- /* check that 2^n + 1 is never 1 */
- secp256k1_scalar_get_num(&n2, &s);
- for (i = 0; i < 250; ++i) {
- secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */
- secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */
- CHECK(!secp256k1_num_is_one(&n1p2));
+}
+
+/* Test secp256k1_modinv32{_var}, using inputs in 16-bit limb format, and returning inverse. */
+void test_modinv32_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod) {
+ uint16_t tmp[16];
+ secp256k1_modinv32_signed30 x;
+ secp256k1_modinv32_modinfo m;
+ int i, vartime, nonzero;
+
+ uint16_to_signed30(&x, in);
+ nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4] | x.v[5] | x.v[6] | x.v[7] | x.v[8]) != 0;
+ uint16_to_signed30(&m.modulus, mod);
+ mutate_sign_signed30(&m.modulus);
+
+ /* compute 1/modulus mod 2^30 */
+ m.modulus_inv30 = modinv2p64(m.modulus.v[0]) & 0x3fffffff;
+ CHECK(((m.modulus_inv30 * m.modulus.v[0]) & 0x3fffffff) == 1);
+
+ for (vartime = 0; vartime < 2; ++vartime) {
+ /* compute inverse */
+ (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m);
+
+ /* produce output */
+ signed30_to_uint16(out, &x);
+
+ /* check if the inverse times the input is 1 (mod m), unless x is 0. */
+ mulmod256(tmp, out, in, mod);
+ CHECK(tmp[0] == nonzero);
+ for (i = 1; i < 16; ++i) CHECK(tmp[i] == 0);
+
+ /* invert again */
+ (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m);
+
+ /* check if the result is equal to the input */
+ signed30_to_uint16(tmp, &x);
+ for (i = 0; i < 16; ++i) CHECK(tmp[i] == in[i]);
}
}
-void test_num_mod(void) {
+#ifdef SECP256K1_WIDEMUL_INT128
+/* Convert a 256-bit number represented as 16 uint16_t's to signed62 notation. */
+void uint16_to_signed62(secp256k1_modinv64_signed62* out, const uint16_t* in) {
int i;
- secp256k1_scalar s;
- secp256k1_num order, n;
-
- /* check that 0 mod anything is 0 */
- random_scalar_order_test(&s);
- secp256k1_scalar_get_num(&order, &s);
- secp256k1_scalar_set_int(&s, 0);
- secp256k1_scalar_get_num(&n, &s);
- secp256k1_num_mod(&n, &order);
- CHECK(secp256k1_num_is_zero(&n));
-
- /* check that anything mod 1 is 0 */
- secp256k1_scalar_set_int(&s, 1);
- secp256k1_scalar_get_num(&order, &s);
- secp256k1_scalar_get_num(&n, &s);
- secp256k1_num_mod(&n, &order);
- CHECK(secp256k1_num_is_zero(&n));
-
- /* check that increasing the number past 2^256 does not break this */
- random_scalar_order_test(&s);
- secp256k1_scalar_get_num(&n, &s);
- /* multiply by 2^8, which'll test this case with high probability */
- for (i = 0; i < 8; ++i) {
- secp256k1_num_add(&n, &n, &n);
+ memset(out->v, 0, sizeof(out->v));
+ for (i = 0; i < 256; ++i) {
+ out->v[i / 62] |= (int64_t)(((in[i >> 4]) >> (i & 15)) & 1) << (i % 62);
}
- secp256k1_num_mod(&n, &order);
- CHECK(secp256k1_num_is_zero(&n));
}
-void test_num_jacobi(void) {
- secp256k1_scalar sqr;
- secp256k1_scalar small;
- secp256k1_scalar five; /* five is not a quadratic residue */
- secp256k1_num order, n;
+/* Convert a 256-bit number in signed62 notation to a representation as 16 uint16_t's. */
+void signed62_to_uint16(uint16_t* out, const secp256k1_modinv64_signed62* in) {
int i;
- /* squares mod 5 are 1, 4 */
- const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 };
+ memset(out, 0, 32);
+ for (i = 0; i < 256; ++i) {
+ out[i >> 4] |= (((in->v[i / 62]) >> (i % 62)) & 1) << (i & 15);
+ }
+}
- /* check some small values with 5 as the order */
- secp256k1_scalar_set_int(&five, 5);
- secp256k1_scalar_get_num(&order, &five);
- for (i = 0; i < 10; ++i) {
- secp256k1_scalar_set_int(&small, i);
- secp256k1_scalar_get_num(&n, &small);
- CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]);
+/* Randomly mutate the sign of limbs in signed62 representation, without changing the value. */
+void mutate_sign_signed62(secp256k1_modinv64_signed62* x) {
+ static const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ int i;
+ for (i = 0; i < 8; ++i) {
+ int pos = secp256k1_testrand_int(4);
+ if (x->v[pos] > 0 && x->v[pos + 1] <= M62) {
+ x->v[pos] -= (M62 + 1);
+ x->v[pos + 1] += 1;
+ } else if (x->v[pos] < 0 && x->v[pos + 1] >= -M62) {
+ x->v[pos] += (M62 + 1);
+ x->v[pos + 1] -= 1;
+ }
}
+}
- /** test large values with 5 as group order */
- secp256k1_scalar_get_num(&order, &five);
- /* we first need a scalar which is not a multiple of 5 */
- do {
- secp256k1_num fiven;
- random_scalar_order_test(&sqr);
- secp256k1_scalar_get_num(&fiven, &five);
- secp256k1_scalar_get_num(&n, &sqr);
- secp256k1_num_mod(&n, &fiven);
- } while (secp256k1_num_is_zero(&n));
- /* next force it to be a residue. 2 is a nonresidue mod 5 so we can
- * just multiply by two, i.e. add the number to itself */
- if (secp256k1_num_jacobi(&n, &order) == -1) {
- secp256k1_num_add(&n, &n, &n);
- }
-
- /* test residue */
- CHECK(secp256k1_num_jacobi(&n, &order) == 1);
- /* test nonresidue */
- secp256k1_num_add(&n, &n, &n);
- CHECK(secp256k1_num_jacobi(&n, &order) == -1);
-
- /** test with secp group order as order */
- secp256k1_scalar_order_get_num(&order);
- random_scalar_order_test(&sqr);
- secp256k1_scalar_sqr(&sqr, &sqr);
- /* test residue */
- secp256k1_scalar_get_num(&n, &sqr);
- CHECK(secp256k1_num_jacobi(&n, &order) == 1);
- /* test nonresidue */
- secp256k1_scalar_mul(&sqr, &sqr, &five);
- secp256k1_scalar_get_num(&n, &sqr);
- CHECK(secp256k1_num_jacobi(&n, &order) == -1);
- /* test multiple of the order*/
- CHECK(secp256k1_num_jacobi(&order, &order) == 0);
-
- /* check one less than the order */
- secp256k1_scalar_set_int(&small, 1);
- secp256k1_scalar_get_num(&n, &small);
- secp256k1_num_sub(&n, &order, &n);
- CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */
+/* Test secp256k1_modinv64{_var}, using inputs in 16-bit limb format, and returning inverse. */
+void test_modinv64_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod) {
+ static const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
+ uint16_t tmp[16];
+ secp256k1_modinv64_signed62 x;
+ secp256k1_modinv64_modinfo m;
+ int i, vartime, nonzero;
+
+ uint16_to_signed62(&x, in);
+ nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4]) != 0;
+ uint16_to_signed62(&m.modulus, mod);
+ mutate_sign_signed62(&m.modulus);
+
+ /* compute 1/modulus mod 2^62 */
+ m.modulus_inv62 = modinv2p64(m.modulus.v[0]) & M62;
+ CHECK(((m.modulus_inv62 * m.modulus.v[0]) & M62) == 1);
+
+ for (vartime = 0; vartime < 2; ++vartime) {
+ /* compute inverse */
+ (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m);
+
+ /* produce output */
+ signed62_to_uint16(out, &x);
+
+ /* check if the inverse times the input is 1 (mod m), unless x is 0. */
+ mulmod256(tmp, out, in, mod);
+ CHECK(tmp[0] == nonzero);
+ for (i = 1; i < 16; ++i) CHECK(tmp[i] == 0);
+
+ /* invert again */
+ (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m);
+
+ /* check if the result is equal to the input */
+ signed62_to_uint16(tmp, &x);
+ for (i = 0; i < 16; ++i) CHECK(tmp[i] == in[i]);
+ }
}
+#endif
-void run_num_smalltests(void) {
+/* test if a and b are coprime */
+int coprime(const uint16_t* a, const uint16_t* b) {
+ uint16_t x[16], y[16], t[16];
int i;
- for (i = 0; i < 100*count; i++) {
- test_num_negate();
- test_num_add_sub();
- test_num_mod();
- test_num_jacobi();
+ int iszero;
+ memcpy(x, a, 32);
+ memcpy(y, b, 32);
+
+ /* simple gcd loop: while x!=0, (x,y)=(y%x,x) */
+ while (1) {
+ iszero = 1;
+ for (i = 0; i < 16; ++i) {
+ if (x[i] != 0) {
+ iszero = 0;
+ break;
+ }
+ }
+ if (iszero) break;
+ mulmod256(t, y, NULL, x);
+ memcpy(y, x, 32);
+ memcpy(x, t, 32);
}
+
+ /* return whether y=1 */
+ if (y[0] != 1) return 0;
+ for (i = 1; i < 16; ++i) {
+ if (y[i] != 0) return 0;
+ }
+ return 1;
}
+
+void run_modinv_tests(void) {
+ /* Fixed test cases. Each tuple is (input, modulus, output), each as 16x16 bits in LE order. */
+ static const uint16_t CASES[][3][16] = {
+ /* Test cases triggering edge cases in divsteps */
+
+ /* Test case known to need 713 divsteps */
+ {{0x1513, 0x5389, 0x54e9, 0x2798, 0x1957, 0x66a0, 0x8057, 0x3477,
+ 0x7784, 0x1052, 0x326a, 0x9331, 0x6506, 0xa95c, 0x91f3, 0xfb5e},
+ {0x2bdd, 0x8df4, 0xcc61, 0x481f, 0xdae5, 0x5ca7, 0xf43b, 0x7d54,
+ 0x13d6, 0x469b, 0x2294, 0x20f4, 0xb2a4, 0xa2d1, 0x3ff1, 0xfd4b},
+ {0xffd8, 0xd9a0, 0x456e, 0x81bb, 0xbabd, 0x6cea, 0x6dbd, 0x73ab,
+ 0xbb94, 0x3d3c, 0xdf08, 0x31c4, 0x3e32, 0xc179, 0x2486, 0xb86b}},
+ /* Test case known to need 589 divsteps, reaching delta=-140 and
+ delta=141. */
+ {{0x3fb1, 0x903b, 0x4eb7, 0x4813, 0xd863, 0x26bf, 0xd89f, 0xa8a9,
+ 0x02fe, 0x57c6, 0x554a, 0x4eab, 0x165e, 0x3d61, 0xee1e, 0x456c},
+ {0x9295, 0x823b, 0x5c1f, 0x5386, 0x48e0, 0x02ff, 0x4c2a, 0xa2da,
+ 0xe58f, 0x967c, 0xc97e, 0x3f5a, 0x69fb, 0x52d9, 0x0a86, 0xb4a3},
+ {0x3d30, 0xb893, 0xa809, 0xa7a8, 0x26f5, 0x5b42, 0x55be, 0xf4d0,
+ 0x12c2, 0x7e6a, 0xe41a, 0x90c7, 0xebfa, 0xf920, 0x304e, 0x1419}},
+ /* Test case known to need 650 divsteps, and doing 65 consecutive (f,g/2) steps. */
+ {{0x8583, 0x5058, 0xbeae, 0xeb69, 0x48bc, 0x52bb, 0x6a9d, 0xcc94,
+ 0x2a21, 0x87d5, 0x5b0d, 0x42f6, 0x5b8a, 0x2214, 0xe9d6, 0xa040},
+ {0x7531, 0x27cb, 0x7e53, 0xb739, 0x6a5f, 0x83f5, 0xa45c, 0xcb1d,
+ 0x8a87, 0x1c9c, 0x51d7, 0x851c, 0xb9d8, 0x1fbe, 0xc241, 0xd4a3},
+ {0xcdb4, 0x275c, 0x7d22, 0xa906, 0x0173, 0xc054, 0x7fdf, 0x5005,
+ 0x7fb8, 0x9059, 0xdf51, 0x99df, 0x2654, 0x8f6e, 0x070f, 0xb347}},
+ /* example needing 713 divsteps; delta=-2..3 */
+ {{0xe2e9, 0xee91, 0x4345, 0xe5ad, 0xf3ec, 0x8f42, 0x0364, 0xd5c9,
+ 0xff49, 0xbef5, 0x4544, 0x4c7c, 0xae4b, 0xfd9d, 0xb35b, 0xda9d},
+ {0x36e7, 0x8cca, 0x2ed0, 0x47b3, 0xaca4, 0xb374, 0x7d2a, 0x0772,
+ 0x6bdb, 0xe0a7, 0x900b, 0xfe10, 0x788c, 0x6f22, 0xd909, 0xf298},
+ {0xd8c6, 0xba39, 0x13ed, 0x198c, 0x16c8, 0xb837, 0xa5f2, 0x9797,
+ 0x0113, 0x882a, 0x15b5, 0x324c, 0xabee, 0xe465, 0x8170, 0x85ac}},
+ /* example needing 713 divsteps; delta=-2..3 */
+ {{0xd5b7, 0x2966, 0x040e, 0xf59a, 0x0387, 0xd96d, 0xbfbc, 0xd850,
+ 0x2d96, 0x872a, 0xad81, 0xc03c, 0xbb39, 0xb7fa, 0xd904, 0xef78},
+ {0x6279, 0x4314, 0xfdd3, 0x1568, 0x0982, 0x4d13, 0x625f, 0x010c,
+ 0x22b1, 0x0cc3, 0xf22d, 0x5710, 0x1109, 0x5751, 0x7714, 0xfcf2},
+ {0xdb13, 0x5817, 0x232e, 0xe456, 0xbbbc, 0x6fbe, 0x4572, 0xa358,
+ 0xc76d, 0x928e, 0x0162, 0x5314, 0x8325, 0x5683, 0xe21b, 0xda88}},
+ /* example needing 713 divsteps; delta=-2..3 */
+ {{0xa06f, 0x71ee, 0x3bac, 0x9ebb, 0xdeaa, 0x09ed, 0x1cf7, 0x9ec9,
+ 0x7158, 0x8b72, 0x5d53, 0x5479, 0x5c75, 0xbb66, 0x9125, 0xeccc},
+ {0x2941, 0xd46c, 0x3cd4, 0x4a9d, 0x5c4a, 0x256b, 0xbd6c, 0x9b8e,
+ 0x8fe0, 0x8a14, 0xffe8, 0x2496, 0x618d, 0xa9d7, 0x5018, 0xfb29},
+ {0x437c, 0xbd60, 0x7590, 0x94bb, 0x0095, 0xd35e, 0xd4fe, 0xd6da,
+ 0x0d4e, 0x5342, 0x4cd2, 0x169b, 0x661c, 0x1380, 0xed2d, 0x85c1}},
+ /* example reaching delta=-64..65; 661 divsteps */
+ {{0xfde4, 0x68d6, 0x6c48, 0x7f77, 0x1c78, 0x96de, 0x2fd9, 0xa6c2,
+ 0xbbb5, 0xd319, 0x69cf, 0xd4b3, 0xa321, 0xcda0, 0x172e, 0xe530},
+ {0xd9e3, 0x0f60, 0x3d86, 0xeeab, 0x25ee, 0x9582, 0x2d50, 0xfe16,
+ 0xd4e2, 0xe3ba, 0x94e2, 0x9833, 0x6c5e, 0x8982, 0x13b6, 0xe598},
+ {0xe675, 0xf55a, 0x10f6, 0xabde, 0x5113, 0xecaa, 0x61ae, 0xad9f,
+ 0x0c27, 0xef33, 0x62e5, 0x211d, 0x08fa, 0xa78d, 0xc675, 0x8bae}},
+ /* example reaching delta=-64..65; 661 divsteps */
+ {{0x21bf, 0x52d5, 0x8fd4, 0xaa18, 0x156a, 0x7247, 0xebb8, 0x5717,
+ 0x4eb5, 0x1421, 0xb58f, 0x3b0b, 0x5dff, 0xe533, 0xb369, 0xd28a},
+ {0x9f6b, 0xe463, 0x2563, 0xc74d, 0x6d81, 0x636a, 0x8fc8, 0x7a94,
+ 0x9429, 0x1585, 0xf35e, 0x7ff5, 0xb64f, 0x9720, 0xba74, 0xe108},
+ {0xa5ab, 0xea7b, 0xfe5e, 0x8a85, 0x13be, 0x7934, 0xe8a0, 0xa187,
+ 0x86b5, 0xe477, 0xb9a4, 0x75d7, 0x538f, 0xdd70, 0xc781, 0xb67d}},
+ /* example reaching delta=-64..65; 661 divsteps */
+ {{0xa41a, 0x3e8d, 0xf1f5, 0x9493, 0x868c, 0x5103, 0x2725, 0x3ceb,
+ 0x6032, 0x3624, 0xdc6b, 0x9120, 0xbf4c, 0x8821, 0x91ad, 0xb31a},
+ {0x5c0b, 0xdda5, 0x20f8, 0x32a1, 0xaf73, 0x6ec5, 0x4779, 0x43d6,
+ 0xd454, 0x9573, 0xbf84, 0x5a58, 0xe04e, 0x307e, 0xd1d5, 0xe230},
+ {0xda15, 0xbcd6, 0x7180, 0xabd3, 0x04e6, 0x6986, 0xc0d7, 0x90bb,
+ 0x3a4d, 0x7c95, 0xaaab, 0x9ab3, 0xda34, 0xa7f6, 0x9636, 0x6273}},
+ /* example doing 123 consecutive (f,g/2) steps; 615 divsteps */
+ {{0xb4d6, 0xb38f, 0x00aa, 0xebda, 0xd4c2, 0x70b8, 0x9dad, 0x58ee,
+ 0x68f8, 0x48d3, 0xb5ff, 0xf422, 0x9e46, 0x2437, 0x18d0, 0xd9cc},
+ {0x5c83, 0xfed7, 0x97f5, 0x3f07, 0xcaad, 0x95b1, 0xb4a4, 0xb005,
+ 0x23af, 0xdd27, 0x6c0d, 0x932c, 0xe2b2, 0xe3ae, 0xfb96, 0xdf67},
+ {0x3105, 0x0127, 0xfd48, 0x039b, 0x35f1, 0xbc6f, 0x6c0a, 0xb572,
+ 0xe4df, 0xebad, 0x8edc, 0xb89d, 0x9555, 0x4c26, 0x1fef, 0x997c}},
+ /* example doing 123 consecutive (f,g/2) steps; 614 divsteps */
+ {{0x5138, 0xd474, 0x385f, 0xc964, 0x00f2, 0x6df7, 0x862d, 0xb185,
+ 0xb264, 0xe9e1, 0x466c, 0xf39e, 0xafaf, 0x5f41, 0x47e2, 0xc89d},
+ {0x8607, 0x9c81, 0x46a2, 0x7dcc, 0xcb0c, 0x9325, 0xe149, 0x2bde,
+ 0x6632, 0x2869, 0xa261, 0xb163, 0xccee, 0x22ae, 0x91e0, 0xcfd5},
+ {0x831c, 0xda22, 0xb080, 0xba7a, 0x26e2, 0x54b0, 0x073b, 0x5ea0,
+ 0xed4b, 0xcb3d, 0xbba1, 0xbec8, 0xf2ad, 0xae0d, 0x349b, 0x17d1}},
+ /* example doing 123 consecutive (f,g/2) steps; 614 divsteps */
+ {{0xe9a5, 0xb4ad, 0xd995, 0x9953, 0xcdff, 0x50d7, 0xf715, 0x9dc7,
+ 0x3e28, 0x15a9, 0x95a3, 0x8554, 0x5b5e, 0xad1d, 0x6d57, 0x3d50},
+ {0x3ad9, 0xbd60, 0x5cc7, 0x6b91, 0xadeb, 0x71f6, 0x7cc4, 0xa58a,
+ 0x2cce, 0xf17c, 0x38c9, 0x97ed, 0x65fb, 0x3fa6, 0xa6bc, 0xeb24},
+ {0xf96c, 0x1963, 0x8151, 0xa0cc, 0x299b, 0xf277, 0x001a, 0x16bb,
+ 0xfd2e, 0x532d, 0x0410, 0xe117, 0x6b00, 0x44ec, 0xca6a, 0x1745}},
+ /* example doing 446 (f,g/2) steps; 523 divsteps */
+ {{0x3758, 0xa56c, 0xe41e, 0x4e47, 0x0975, 0xa82b, 0x107c, 0x89cf,
+ 0x2093, 0x5a0c, 0xda37, 0xe007, 0x6074, 0x4f68, 0x2f5a, 0xbb8a},
+ {0x4beb, 0xa40f, 0x2c42, 0xd9d6, 0x97e8, 0xca7c, 0xd395, 0x894f,
+ 0x1f50, 0x8067, 0xa233, 0xb850, 0x1746, 0x1706, 0xbcda, 0xdf32},
+ {0x762a, 0xceda, 0x4c45, 0x1ca0, 0x8c37, 0xd8c5, 0xef57, 0x7a2c,
+ 0x6e98, 0xe38a, 0xc50e, 0x2ca9, 0xcb85, 0x24d5, 0xc29c, 0x61f6}},
+ /* example doing 446 (f,g/2) steps; 523 divsteps */
+ {{0x6f38, 0x74ad, 0x7332, 0x4073, 0x6521, 0xb876, 0xa370, 0xa6bd,
+ 0xcea5, 0xbd06, 0x969f, 0x77c6, 0x1e69, 0x7c49, 0x7d51, 0xb6e7},
+ {0x3f27, 0x4be4, 0xd81e, 0x1396, 0xb21f, 0x92aa, 0x6dc3, 0x6283,
+ 0x6ada, 0x3ca2, 0xc1e5, 0x8b9b, 0xd705, 0x5598, 0x8ba1, 0xe087},
+ {0x6a22, 0xe834, 0xbc8d, 0xcee9, 0x42fc, 0xfc77, 0x9c45, 0x1ca8,
+ 0xeb66, 0xed74, 0xaaf9, 0xe75f, 0xfe77, 0x46d2, 0x179b, 0xbf3e}},
+ /* example doing 336 (f,(f+g)/2) steps; 693 divsteps */
+ {{0x7ea7, 0x444e, 0x84ea, 0xc447, 0x7c1f, 0xab97, 0x3de6, 0x5878,
+ 0x4e8b, 0xc017, 0x03e0, 0xdc40, 0xbbd0, 0x74ce, 0x0169, 0x7ab5},
+ {0x4023, 0x154f, 0xfbe4, 0x8195, 0xfda0, 0xef54, 0x9e9a, 0xc703,
+ 0x2803, 0xf760, 0x6302, 0xed5b, 0x7157, 0x6456, 0xdd7d, 0xf14b},
+ {0xb6fb, 0xe3b3, 0x0733, 0xa77e, 0x44c5, 0x3003, 0xc937, 0xdd4d,
+ 0x5355, 0x14e9, 0x184e, 0xcefe, 0xe6b5, 0xf2e0, 0x0a28, 0x5b74}},
+ /* example doing 336 (f,(f+g)/2) steps; 687 divsteps */
+ {{0xa893, 0xb5f4, 0x1ede, 0xa316, 0x242c, 0xbdcc, 0xb017, 0x0836,
+ 0x3a37, 0x27fb, 0xfb85, 0x251e, 0xa189, 0xb15d, 0xa4b8, 0xc24c},
+ {0xb0b7, 0x57ba, 0xbb6d, 0x9177, 0xc896, 0xc7f2, 0x43b4, 0x85a6,
+ 0xe6c4, 0xe50e, 0x3109, 0x7ca5, 0xd73d, 0x13ff, 0x0c3d, 0xcd62},
+ {0x48ca, 0xdb34, 0xe347, 0x2cef, 0x4466, 0x10fb, 0x7ee1, 0x6344,
+ 0x4308, 0x966d, 0xd4d1, 0xb099, 0x994f, 0xd025, 0x2187, 0x5866}},
+ /* example doing 267 (g,(g-f)/2) steps; 678 divsteps */
+ {{0x0775, 0x1754, 0x01f6, 0xdf37, 0xc0be, 0x8197, 0x072f, 0x6cf5,
+ 0x8b36, 0x8069, 0x5590, 0xb92d, 0x6084, 0x47a4, 0x23fe, 0xddd5},
+ {0x8e1b, 0xda37, 0x27d9, 0x312e, 0x3a2f, 0xef6d, 0xd9eb, 0x8153,
+ 0xdcba, 0x9fa3, 0x9f80, 0xead5, 0x134d, 0x2ebb, 0x5ec0, 0xe032},
+ {0x1cb6, 0x5a61, 0x1bed, 0x77d6, 0xd5d1, 0x7498, 0xef33, 0x2dd2,
+ 0x1089, 0xedbd, 0x6958, 0x16ae, 0x336c, 0x45e6, 0x4361, 0xbadc}},
+ /* example doing 267 (g,(g-f)/2) steps; 676 divsteps */
+ {{0x0207, 0xf948, 0xc430, 0xf36b, 0xf0a7, 0x5d36, 0x751f, 0x132c,
+ 0x6f25, 0xa630, 0xca1f, 0xc967, 0xaf9c, 0x34e7, 0xa38f, 0xbe9f},
+ {0x5fb9, 0x7321, 0x6561, 0x5fed, 0x54ec, 0x9c3a, 0xee0e, 0x6717,
+ 0x49af, 0xb896, 0xf4f5, 0x451c, 0x722a, 0xf116, 0x64a9, 0xcf0b},
+ {0xf4d7, 0xdb47, 0xfef2, 0x4806, 0x4cb8, 0x18c7, 0xd9a7, 0x4951,
+ 0x14d8, 0x5c3a, 0xd22d, 0xd7b2, 0x750c, 0x3de7, 0x8b4a, 0x19aa}},
+
+ /* Test cases triggering edge cases in divsteps variant starting with delta=1/2 */
+
+ /* example needing 590 divsteps; delta=-5/2..7/2 */
+ {{0x9118, 0xb640, 0x53d7, 0x30ab, 0x2a23, 0xd907, 0x9323, 0x5b3a,
+ 0xb6d4, 0x538a, 0x7637, 0xfe97, 0xfd05, 0x3cc0, 0x453a, 0xfb7e},
+ {0x6983, 0x4f75, 0x4ad1, 0x48ad, 0xb2d9, 0x521d, 0x3dbc, 0x9cc0,
+ 0x4b60, 0x0ac6, 0xd3be, 0x0fb6, 0xd305, 0x3895, 0x2da5, 0xfdf8},
+ {0xcec1, 0x33ac, 0xa801, 0x8194, 0xe36c, 0x65ef, 0x103b, 0xca54,
+ 0xfa9b, 0xb41d, 0x9b52, 0xb6f7, 0xa611, 0x84aa, 0x3493, 0xbf54}},
+ /* example needing 590 divsteps; delta=-3/2..5/2 */
+ {{0xb5f2, 0x42d0, 0x35e8, 0x8ca0, 0x4b62, 0x6e1d, 0xbdf3, 0x890e,
+ 0x8c82, 0x23d8, 0xc79a, 0xc8e8, 0x789e, 0x353d, 0x9766, 0xea9d},
+ {0x6fa1, 0xacba, 0x4b7a, 0x5de1, 0x95d0, 0xc845, 0xebbf, 0x6f5a,
+ 0x30cf, 0x52db, 0x69b7, 0xe278, 0x4b15, 0x8411, 0x2ab2, 0xf3e7},
+ {0xf12c, 0x9d6d, 0x95fa, 0x1878, 0x9f13, 0x4fb5, 0x3c8b, 0xa451,
+ 0x7182, 0xc4b6, 0x7e2a, 0x7bb7, 0x6e0e, 0x5b68, 0xde55, 0x9927}},
+ /* example needing 590 divsteps; delta=-3/2..5/2 */
+ {{0x229c, 0x4ef8, 0x1e93, 0xe5dc, 0xcde5, 0x6d62, 0x263b, 0xad11,
+ 0xced0, 0x88ff, 0xae8e, 0x3183, 0x11d2, 0xa50b, 0x350d, 0xeb40},
+ {0x3157, 0xe2ea, 0x8a02, 0x0aa3, 0x5ae1, 0xb26c, 0xea27, 0x6805,
+ 0x87e2, 0x9461, 0x37c1, 0x2f8d, 0x85d2, 0x77a8, 0xf805, 0xeec9},
+ {0x6f4e, 0x2748, 0xf7e5, 0xd8d3, 0xabe2, 0x7270, 0xc4e0, 0xedc7,
+ 0xf196, 0x78ca, 0x9139, 0xd8af, 0x72c6, 0xaf2f, 0x85d2, 0x6cd3}},
+ /* example needing 590 divsteps; delta=-5/2..7/2 */
+ {{0xdce8, 0xf1fe, 0x6708, 0x021e, 0xf1ca, 0xd609, 0x5443, 0x85ce,
+ 0x7a05, 0x8f9c, 0x90c3, 0x52e7, 0x8e1d, 0x97b8, 0xc0bf, 0xf2a1},
+ {0xbd3d, 0xed11, 0x1625, 0xb4c5, 0x844c, 0xa413, 0x2569, 0xb9ba,
+ 0xcd35, 0xff84, 0xcd6e, 0x7f0b, 0x7d5d, 0x10df, 0x3efe, 0xfbe5},
+ {0xa9dd, 0xafef, 0xb1b7, 0x4c8d, 0x50e4, 0xafbf, 0x2d5a, 0xb27c,
+ 0x0653, 0x66b6, 0x5d36, 0x4694, 0x7e35, 0xc47c, 0x857f, 0x32c5}},
+ /* example needing 590 divsteps; delta=-3/2..5/2 */
+ {{0x7902, 0xc9f8, 0x926b, 0xaaeb, 0x90f8, 0x1c89, 0xcce3, 0x96b7,
+ 0x28b2, 0x87a2, 0x136d, 0x695a, 0xa8df, 0x9061, 0x9e31, 0xee82},
+ {0xd3a9, 0x3c02, 0x818c, 0x6b81, 0x34b3, 0xebbb, 0xe2c8, 0x7712,
+ 0xbfd6, 0x8248, 0xa6f4, 0xba6f, 0x03bb, 0xfb54, 0x7575, 0xfe89},
+ {0x8246, 0x0d63, 0x478e, 0xf946, 0xf393, 0x0451, 0x08c2, 0x5919,
+ 0x5fd6, 0x4c61, 0xbeb7, 0x9a15, 0x30e1, 0x55fc, 0x6a01, 0x3724}},
+ /* example reaching delta=-127/2..129/2; 571 divsteps */
+ {{0x3eff, 0x926a, 0x77f5, 0x1fff, 0x1a5b, 0xf3ef, 0xf64b, 0x8681,
+ 0xf800, 0xf9bc, 0x761d, 0xe268, 0x62b0, 0xa032, 0xba9c, 0xbe56},
+ {0xb8f9, 0x00e7, 0x47b7, 0xdffc, 0xfd9d, 0x5abb, 0xa19b, 0x1868,
+ 0x31fd, 0x3b29, 0x3674, 0x5449, 0xf54d, 0x1d19, 0x6ac7, 0xff6f},
+ {0xf1d7, 0x3551, 0x5682, 0x9adf, 0xe8aa, 0x19a5, 0x8340, 0x71db,
+ 0xb7ab, 0x4cfd, 0xf661, 0x632c, 0xc27e, 0xd3c6, 0xdf42, 0xd306}},
+ /* example reaching delta=-127/2..129/2; 571 divsteps */
+ {{0x0000, 0x0000, 0x0000, 0x0000, 0x3aff, 0x2ed7, 0xf2e0, 0xabc7,
+ 0x8aee, 0x166e, 0x7ed0, 0x9ac7, 0x714a, 0xb9c5, 0x4d58, 0xad6c},
+ {0x9cf9, 0x47e2, 0xa421, 0xb277, 0xffc2, 0x2747, 0x6486, 0x94c1,
+ 0x1d99, 0xd49b, 0x1096, 0x991a, 0xe986, 0xae02, 0xe89b, 0xea36},
+ {0x1fb4, 0x98d8, 0x19b7, 0x80e9, 0xcdac, 0xaa5a, 0xf1e6, 0x0074,
+ 0xe393, 0xed8b, 0x8d5c, 0xe17d, 0x81b3, 0xc16d, 0x54d3, 0x9be3}},
+ /* example reaching delta=-127/2..129/2; 571 divsteps */
+ {{0xd047, 0x7e36, 0x3157, 0x7ab6, 0xb4d9, 0x8dae, 0x7534, 0x4f5d,
+ 0x489e, 0xa8ab, 0x8a3d, 0xd52c, 0x62af, 0xa032, 0xba9c, 0xbe56},
+ {0xb1f1, 0x737f, 0x5964, 0x5afb, 0x3712, 0x8ef9, 0x19f7, 0x9669,
+ 0x664d, 0x03ad, 0xc352, 0xf7a5, 0xf545, 0x1d19, 0x6ac7, 0xff6f},
+ {0xa834, 0x5256, 0x27bc, 0x33bd, 0xba11, 0x5a7b, 0x791e, 0xe6c0,
+ 0x9ac4, 0x9370, 0x1130, 0x28b4, 0x2b2e, 0x231b, 0x082a, 0x796e}},
+ /* example doing 123 consecutive (f,g/2) steps; 554 divsteps */
+ {{0x6ab1, 0x6ea0, 0x1a99, 0xe0c2, 0xdd45, 0x645d, 0x8dbc, 0x466a,
+ 0xfa64, 0x4289, 0xd3f7, 0xfc8f, 0x2894, 0xe3c5, 0xa008, 0xcc14},
+ {0xc75f, 0xc083, 0x4cc2, 0x64f2, 0x2aff, 0x4c12, 0x8461, 0xc4ae,
+ 0xbbfa, 0xb336, 0xe4b2, 0x3ac5, 0x2c22, 0xf56c, 0x5381, 0xe943},
+ {0xcd80, 0x760d, 0x4395, 0xb3a6, 0xd497, 0xf583, 0x82bd, 0x1daa,
+ 0xbe92, 0x2613, 0xfdfb, 0x869b, 0x0425, 0xa333, 0x7056, 0xc9c5}},
+ /* example doing 123 consecutive (f,g/2) steps; 554 divsteps */
+ {{0x71d4, 0x64df, 0xec4f, 0x74d8, 0x7e0c, 0x40d3, 0x7073, 0x4cc8,
+ 0x2a2a, 0xb1ff, 0x8518, 0x6513, 0xb0ea, 0x640a, 0x62d9, 0xd5f4},
+ {0xdc75, 0xd937, 0x3b13, 0x1d36, 0xdf83, 0xd034, 0x1c1c, 0x4332,
+ 0x4cc3, 0xeeec, 0x7d94, 0x6771, 0x3384, 0x74b0, 0x947d, 0xf2c4},
+ {0x0a82, 0x37a4, 0x12d5, 0xec97, 0x972c, 0xe6bf, 0xc348, 0xa0a9,
+ 0xc50c, 0xdc7c, 0xae30, 0x19d1, 0x0fca, 0x35e1, 0xd6f6, 0x81ee}},
+ /* example doing 123 consecutive (f,g/2) steps; 554 divsteps */
+ {{0xa6b1, 0xabc5, 0x5bbc, 0x7f65, 0xdd32, 0xaa73, 0xf5a3, 0x1982,
+ 0xced4, 0xe949, 0x0fd6, 0x2bc4, 0x2bd7, 0xe3c5, 0xa008, 0xcc14},
+ {0x4b5f, 0x8f96, 0xa375, 0xfbcf, 0x1c7d, 0xf1ec, 0x03f5, 0xb35d,
+ 0xb999, 0xdb1f, 0xc9a1, 0xb4c7, 0x1dd5, 0xf56c, 0x5381, 0xe943},
+ {0xaa3d, 0x38b9, 0xf17d, 0xeed9, 0x9988, 0x69ee, 0xeb88, 0x1495,
+ 0x203f, 0x18c8, 0x82b7, 0xdcb2, 0x34a7, 0x6b00, 0x6998, 0x589a}},
+ /* example doing 453 (f,g/2) steps; 514 divsteps */
+ {{0xa478, 0xe60d, 0x3244, 0x60e6, 0xada3, 0xfe50, 0xb6b1, 0x2eae,
+ 0xd0ef, 0xa7b1, 0xef63, 0x05c0, 0xe213, 0x443e, 0x4427, 0x2448},
+ {0x258f, 0xf9ef, 0xe02b, 0x92dd, 0xd7f3, 0x252b, 0xa503, 0x9089,
+ 0xedff, 0x96c1, 0xfe3a, 0x3a39, 0x198a, 0x981d, 0x0627, 0xedb7},
+ {0x595a, 0x45be, 0x8fb0, 0x2265, 0xc210, 0x02b8, 0xdce9, 0xe241,
+ 0xcab6, 0xbf0d, 0x0049, 0x8d9a, 0x2f51, 0xae54, 0x5785, 0xb411}},
+ /* example doing 453 (f,g/2) steps; 514 divsteps */
+ {{0x48f0, 0x7db3, 0xdafe, 0x1c92, 0x5912, 0xe11a, 0xab52, 0xede1,
+ 0x3182, 0x8980, 0x5d2b, 0x9b5b, 0x8718, 0xda27, 0x1683, 0x1de2},
+ {0x168f, 0x6f36, 0xce7a, 0xf435, 0x19d4, 0xda5e, 0x2351, 0x9af5,
+ 0xb003, 0x0ef5, 0x3b4c, 0xecec, 0xa9f0, 0x78e1, 0xdfef, 0xe823},
+ {0x5f55, 0xfdcc, 0xb233, 0x2914, 0x84f0, 0x97d1, 0x9cf4, 0x2159,
+ 0xbf56, 0xb79c, 0x17a3, 0x7cef, 0xd5de, 0x34f0, 0x5311, 0x4c54}},
+ /* example doing 510 (f,(f+g)/2) steps; 512 divsteps */
+ {{0x2789, 0x2e04, 0x6e0e, 0xb6cd, 0xe4de, 0x4dbf, 0x228d, 0x7877,
+ 0xc335, 0x806b, 0x38cd, 0x8049, 0xa73b, 0xcfa2, 0x82f7, 0x9e19},
+ {0xc08d, 0xb99d, 0xb8f3, 0x663d, 0xbbb3, 0x1284, 0x1485, 0x1d49,
+ 0xc98f, 0x9e78, 0x1588, 0x11e3, 0xd91a, 0xa2c7, 0xfff1, 0xc7b9},
+ {0x1e1f, 0x411d, 0x7c49, 0x0d03, 0xe789, 0x2f8e, 0x5d55, 0xa95e,
+ 0x826e, 0x8de5, 0x52a0, 0x1abc, 0x4cd7, 0xd13a, 0x4395, 0x63e1}},
+ /* example doing 510 (f,(f+g)/2) steps; 512 divsteps */
+ {{0xd5a1, 0xf786, 0x555c, 0xb14b, 0x44ae, 0x535f, 0x4a49, 0xffc3,
+ 0xf497, 0x70d1, 0x57c8, 0xa933, 0xc85a, 0x1910, 0x75bf, 0x960b},
+ {0xfe53, 0x5058, 0x496d, 0xfdff, 0x6fb8, 0x4100, 0x92bd, 0xe0c4,
+ 0xda89, 0xe0a4, 0x841b, 0x43d4, 0xa388, 0x957f, 0x99ca, 0x9abf},
+ {0xe530, 0x05bc, 0xfeec, 0xfc7e, 0xbcd3, 0x1239, 0x54cb, 0x7042,
+ 0xbccb, 0x139e, 0x9076, 0x0203, 0x6068, 0x90c7, 0x1ddf, 0x488d}},
+ /* example doing 228 (g,(g-f)/2) steps; 538 divsteps */
+ {{0x9488, 0xe54b, 0x0e43, 0x81d2, 0x06e7, 0x4b66, 0x36d0, 0x53d6,
+ 0x2b68, 0x22ec, 0x3fa9, 0xc1a7, 0x9ad2, 0xa596, 0xb3ac, 0xdf42},
+ {0xe31f, 0x0b28, 0x5f3b, 0xc1ff, 0x344c, 0xbf5f, 0xd2ec, 0x2936,
+ 0x9995, 0xdeb2, 0xae6c, 0x2852, 0xa2c6, 0xb306, 0x8120, 0xe305},
+ {0xa56e, 0xfb98, 0x1537, 0x4d85, 0x619e, 0x866c, 0x3cd4, 0x779a,
+ 0xdd66, 0xa80d, 0xdc2f, 0xcae4, 0xc74c, 0x5175, 0xa65d, 0x605e}},
+ /* example doing 228 (g,(g-f)/2) steps; 537 divsteps */
+ {{0x8cd5, 0x376d, 0xd01b, 0x7176, 0x19ef, 0xcf09, 0x8403, 0x5e52,
+ 0x83c1, 0x44de, 0xb91e, 0xb33d, 0xe15c, 0x51e7, 0xbad8, 0x6359},
+ {0x3b75, 0xf812, 0x5f9e, 0xa04e, 0x92d3, 0x226e, 0x540e, 0x7c9a,
+ 0x31c6, 0x46d2, 0x0b7b, 0xdb4a, 0xe662, 0x4950, 0x0265, 0xf76f},
+ {0x09ed, 0x692f, 0xe8f1, 0x3482, 0xab54, 0x36b4, 0x8442, 0x6ae9,
+ 0x4329, 0x6505, 0x183b, 0x1c1d, 0x482d, 0x7d63, 0xb44f, 0xcc09}},
+
+ /* Test cases with the group order as modulus. */
+
+ /* Test case with the group order as modulus, needing 635 divsteps. */
+ {{0x95ed, 0x6c01, 0xd113, 0x5ff1, 0xd7d0, 0x29cc, 0x5817, 0x6120,
+ 0xca8e, 0xaad1, 0x25ae, 0x8e84, 0x9af6, 0x30bf, 0xf0ed, 0x1686},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x1631, 0xbf4a, 0x286a, 0x2716, 0x469f, 0x2ac8, 0x1312, 0xe9bc,
+ 0x04f4, 0x304b, 0x9931, 0x113b, 0xd932, 0xc8f4, 0x0d0d, 0x01a1}},
+ /* example with group size as modulus needing 631 divsteps */
+ {{0x85ed, 0xc284, 0x9608, 0x3c56, 0x19b6, 0xbb5b, 0x2850, 0xdab7,
+ 0xa7f5, 0xe9ab, 0x06a4, 0x5bbb, 0x1135, 0xa186, 0xc424, 0xc68b},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x8479, 0x450a, 0x8fa3, 0xde05, 0xb2f5, 0x7793, 0x7269, 0xbabb,
+ 0xc3b3, 0xd49b, 0x3377, 0x03c6, 0xe694, 0xc760, 0xd3cb, 0x2811}},
+ /* example with group size as modulus needing 565 divsteps starting at delta=1/2 */
+ {{0x8432, 0x5ceb, 0xa847, 0x6f1e, 0x51dd, 0x535a, 0x6ddc, 0x70ce,
+ 0x6e70, 0xc1f6, 0x18f2, 0x2a7e, 0xc8e7, 0x39f8, 0x7e96, 0xebbf},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x257e, 0x449f, 0x689f, 0x89aa, 0x3989, 0xb661, 0x376c, 0x1e32,
+ 0x654c, 0xee2e, 0xf4e2, 0x33c8, 0x3f2f, 0x9716, 0x6046, 0xcaa3}},
+ /* Test case with the group size as modulus, needing 981 divsteps with
+ broken eta handling. */
+ {{0xfeb9, 0xb877, 0xee41, 0x7fa3, 0x87da, 0x94c4, 0x9d04, 0xc5ae,
+ 0x5708, 0x0994, 0xfc79, 0x0916, 0xbf32, 0x3ad8, 0xe11c, 0x5ca2},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0f12, 0x075e, 0xce1c, 0x6f92, 0xc80f, 0xca92, 0x9a04, 0x6126,
+ 0x4b6c, 0x57d6, 0xca31, 0x97f3, 0x1f99, 0xf4fd, 0xda4d, 0x42ce}},
+ /* Test case with the group size as modulus, input = 0. */
+ {{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}},
+ /* Test case with the group size as modulus, input = 1. */
+ {{0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}},
+ /* Test case with the group size as modulus, input = 2. */
+ {{0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x20a1, 0x681b, 0x2f46, 0xdfe9, 0x501d, 0x57a4, 0x6e73, 0x5d57,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x7fff}},
+ /* Test case with the group size as modulus, input = group - 1. */
+ {{0x4140, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x4140, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae,
+ 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}},
+
+ /* Test cases with the field size as modulus. */
+
+ /* Test case with the field size as modulus, needing 637 divsteps. */
+ {{0x9ec3, 0x1919, 0xca84, 0x7c11, 0xf996, 0x06f3, 0x5408, 0x6688,
+ 0x1320, 0xdb8a, 0x632a, 0x0dcb, 0x8a84, 0x6bee, 0x9c95, 0xe34e},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x18e5, 0x19b6, 0xdf92, 0x1aaa, 0x09fb, 0x8a3f, 0x52b0, 0x8701,
+ 0xac0c, 0x2582, 0xda44, 0x9bcc, 0x6828, 0x1c53, 0xbd8f, 0xbd2c}},
+ /* example with field size as modulus needing 637 divsteps */
+ {{0xaec3, 0xa7cf, 0x2f2d, 0x0693, 0x5ad5, 0xa8ff, 0x7ec7, 0x30ff,
+ 0x0c8b, 0xc242, 0xcab2, 0x063a, 0xf86e, 0x6057, 0x9cbd, 0xf6d8},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0310, 0x579d, 0xcb38, 0x9030, 0x3ded, 0x9bb9, 0x1234, 0x63ce,
+ 0x0c63, 0x8e3d, 0xacfe, 0x3c20, 0xdc85, 0xf859, 0x919e, 0x1d45}},
+ /* example with field size as modulus needing 564 divsteps starting at delta=1/2 */
+ {{0x63ae, 0x8d10, 0x0071, 0xdb5c, 0xb454, 0x78d1, 0x744a, 0x5f8e,
+ 0xe4d8, 0x87b1, 0x8e62, 0x9590, 0xcede, 0xa070, 0x36b4, 0x7f6f},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0xfdc8, 0xe8d5, 0xbe15, 0x9f86, 0xa5fe, 0xf18e, 0xa7ff, 0xd291,
+ 0xf4c2, 0x9c87, 0xf150, 0x073e, 0x69b8, 0xf7c4, 0xee4b, 0xc7e6}},
+ /* Test case with the field size as modulus, needing 935 divsteps with
+ broken eta handling. */
+ {{0x1b37, 0xbdc3, 0x8bcd, 0x25e3, 0x1eae, 0x567d, 0x30b6, 0xf0d8,
+ 0x9277, 0x0cf8, 0x9c2e, 0xecd7, 0x631d, 0xe38f, 0xd4f8, 0x5c93},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x1622, 0xe05b, 0xe880, 0x7de9, 0x3e45, 0xb682, 0xee6c, 0x67ed,
+ 0xa179, 0x15db, 0x6b0d, 0xa656, 0x7ccb, 0x8ef7, 0xa2ff, 0xe279}},
+ /* Test case with the field size as modulus, input = 0. */
+ {{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}},
+ /* Test case with the field size as modulus, input = 1. */
+ {{0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}},
+ /* Test case with the field size as modulus, input = 2. */
+ {{0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0xfe18, 0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x7fff}},
+ /* Test case with the field size as modulus, input = field - 1. */
+ {{0xfc2e, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff},
+ {0xfc2e, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}},
+
+ /* Selected from a large number of random inputs to reach small/large
+ * d/e values in various configurations. */
+ {{0x3a08, 0x23e1, 0x4d8c, 0xe606, 0x3263, 0x67af, 0x9bf1, 0x9d70,
+ 0xf5fd, 0x12e4, 0x03c8, 0xb9ca, 0xe847, 0x8c5d, 0x6322, 0xbd30},
+ {0x8359, 0x59dd, 0x1831, 0x7c1a, 0x1e83, 0xaee1, 0x770d, 0xcea8,
+ 0xfbb1, 0xeed6, 0x10b5, 0xe2c6, 0x36ea, 0xee17, 0xe32c, 0xffff},
+ {0x1727, 0x0f36, 0x6f85, 0x5d0c, 0xca6c, 0x3072, 0x9628, 0x5842,
+ 0xcb44, 0x7c2b, 0xca4f, 0x62e5, 0x29b1, 0x6ffd, 0x9055, 0xc196}},
+ {{0x905d, 0x41c8, 0xa2ff, 0x295b, 0x72bb, 0x4679, 0x6d01, 0x2c98,
+ 0xb3e0, 0xc537, 0xa310, 0xe07e, 0xe72f, 0x4999, 0x1148, 0xf65e},
+ {0x5b41, 0x4239, 0x3c37, 0x5130, 0x30e3, 0xff35, 0xc51f, 0x1a43,
+ 0xdb23, 0x13cf, 0x9f49, 0xf70c, 0x5e70, 0xd411, 0x3005, 0xf8c6},
+ {0xc30e, 0x68f0, 0x201a, 0xe10c, 0x864a, 0x6243, 0xe946, 0x43ae,
+ 0xf3f1, 0x52dc, 0x1f7f, 0x50d4, 0x2797, 0x064c, 0x5ca4, 0x90e3}},
+ {{0xf1b5, 0xc6e5, 0xd2c4, 0xff95, 0x27c5, 0x0c92, 0x5d19, 0x7ae5,
+ 0x4fbe, 0x5438, 0x99e1, 0x880d, 0xd892, 0xa05c, 0x6ffd, 0x7eac},
+ {0x2153, 0xcc9d, 0xfc6c, 0x8358, 0x49a1, 0x01e2, 0xcef0, 0x4969,
+ 0xd69a, 0x8cef, 0xf5b2, 0xfd95, 0xdcc2, 0x71f4, 0x6ae2, 0xceeb},
+ {0x9b2e, 0xcdc6, 0x0a5c, 0x7317, 0x9084, 0xe228, 0x56cf, 0xd512,
+ 0x628a, 0xce21, 0x3473, 0x4e13, 0x8823, 0x1ed0, 0x34d0, 0xbfa3}},
+ {{0x5bae, 0x53e5, 0x5f4d, 0x21ca, 0xb875, 0x8ecf, 0x9aa6, 0xbe3c,
+ 0x9f96, 0x7b82, 0x375d, 0x4d3e, 0x491c, 0xb1eb, 0x04c9, 0xb6c8},
+ {0xfcfd, 0x10b7, 0x73b2, 0xd23b, 0xa357, 0x67da, 0x0d9f, 0x8702,
+ 0xa037, 0xff8e, 0x0e8b, 0x1801, 0x2c5c, 0x4e6e, 0x4558, 0xfff2},
+ {0xc50f, 0x5654, 0x6713, 0x5ef5, 0xa7ce, 0xa647, 0xc832, 0x69ce,
+ 0x1d5c, 0x4310, 0x0746, 0x5a01, 0x96ea, 0xde4b, 0xa88b, 0x5543}},
+ {{0xdc7f, 0x5e8c, 0x89d1, 0xb077, 0xd521, 0xcf90, 0x32fa, 0x5737,
+ 0x839e, 0x1464, 0x007c, 0x09c6, 0x9371, 0xe8ea, 0xc1cb, 0x75c4},
+ {0xe3a3, 0x107f, 0xa82a, 0xa375, 0x4578, 0x60f4, 0x75c9, 0x5ee4,
+ 0x3fd7, 0x2736, 0x2871, 0xd3d2, 0x5f1d, 0x1abb, 0xa764, 0xffff},
+ {0x45c6, 0x1f2e, 0xb14c, 0x84d7, 0x7bb7, 0x5a04, 0x0504, 0x3f33,
+ 0x5cc1, 0xb07a, 0x6a6c, 0x786f, 0x647f, 0xe1d7, 0x78a2, 0x4cf4}},
+ {{0xc006, 0x356f, 0x8cd2, 0x967b, 0xb49e, 0x2d4e, 0x14bf, 0x4bcb,
+ 0xddab, 0xd3f9, 0xa068, 0x2c1c, 0xd242, 0xa56d, 0xf2c7, 0x5f97},
+ {0x465b, 0xb745, 0x0e0d, 0x69a9, 0x987d, 0xcb37, 0xf637, 0xb311,
+ 0xc4d6, 0x2ddb, 0xf68f, 0x2af9, 0x959d, 0x3f53, 0x98f2, 0xf640},
+ {0xc0f2, 0x6bfb, 0xf5c3, 0x91c1, 0x6b05, 0x0825, 0x5ca0, 0x7df7,
+ 0x9d55, 0x6d9e, 0xfe94, 0x2ad9, 0xd9f0, 0xe68b, 0xa72b, 0xd1b2}},
+ {{0x2279, 0x61ba, 0x5bc6, 0x136b, 0xf544, 0x717c, 0xafda, 0x02bd,
+ 0x79af, 0x1fad, 0xea09, 0x81bb, 0x932b, 0x32c9, 0xdf1d, 0xe576},
+ {0x8215, 0x7817, 0xca82, 0x43b0, 0x9b06, 0xea65, 0x1291, 0x0621,
+ 0x0089, 0x46fe, 0xc5a6, 0xddd7, 0x8065, 0xc6a0, 0x214b, 0xfc64},
+ {0x04bf, 0x6f2a, 0x86b2, 0x841a, 0x4a95, 0xc632, 0x97b7, 0x5821,
+ 0x2b18, 0x1bb0, 0x3e97, 0x935e, 0xcc7d, 0x066b, 0xd513, 0xc251}},
+ {{0x76e8, 0x5bc2, 0x3eaa, 0x04fc, 0x9974, 0x92c1, 0x7c15, 0xfa89,
+ 0x1151, 0x36ee, 0x48b2, 0x049c, 0x5f16, 0xcee4, 0x925b, 0xe98e},
+ {0x913f, 0x0a2d, 0xa185, 0x9fea, 0xda5a, 0x4025, 0x40d7, 0x7cfa,
+ 0x88ca, 0xbbe8, 0xb265, 0xb7e4, 0x6cb1, 0xed64, 0xc6f9, 0xffb5},
+ {0x6ab1, 0x1a86, 0x5009, 0x152b, 0x1cc4, 0xe2c8, 0x960b, 0x19d0,
+ 0x3554, 0xc562, 0xd013, 0xcf91, 0x10e1, 0x7933, 0xe195, 0xcf49}},
+ {{0x9cb5, 0xd2d7, 0xc6ed, 0xa818, 0xb495, 0x06ee, 0x0f4a, 0x06e3,
+ 0x4c5a, 0x80ce, 0xd49a, 0x4cd7, 0x7487, 0x92af, 0xe516, 0x676c},
+ {0xd6e9, 0x6b85, 0x619a, 0xb52c, 0x20a0, 0x2f79, 0x3545, 0x1edd,
+ 0x5a6f, 0x8082, 0x9b80, 0xf8f8, 0xc78a, 0xd0a3, 0xadf4, 0xffff},
+ {0x01c2, 0x2118, 0xef5e, 0xa877, 0x046a, 0xd2c2, 0x2ad5, 0x951c,
+ 0x8900, 0xa5c9, 0x8d0f, 0x6b61, 0x55d3, 0xd572, 0x48de, 0x9219}},
+ {{0x5114, 0x0644, 0x23dd, 0x01d3, 0xc101, 0xa659, 0xea17, 0x640f,
+ 0xf767, 0x2644, 0x9cec, 0xd8ba, 0xd6da, 0x9156, 0x8aeb, 0x875a},
+ {0xc1bf, 0xdae9, 0xe96b, 0xce77, 0xf7a1, 0x3e99, 0x5c2e, 0x973b,
+ 0xd048, 0x5bd0, 0x4e8a, 0xcb85, 0xce39, 0x37f5, 0x815d, 0xffff},
+ {0x48cc, 0x35b6, 0x26d4, 0x2ea6, 0x50d6, 0xa2f9, 0x64b6, 0x03bf,
+ 0xd00c, 0xe057, 0x3343, 0xfb79, 0x3ce5, 0xf717, 0xc5af, 0xe185}},
+ {{0x13ff, 0x6c76, 0x2077, 0x16e0, 0xd5ca, 0xf2ad, 0x8dba, 0x8f49,
+ 0x7887, 0x16f9, 0xb646, 0xfc87, 0xfa31, 0x5096, 0xf08c, 0x3fbe},
+ {0x8139, 0x6fd7, 0xf6df, 0xa7bf, 0x6699, 0x5361, 0x6f65, 0x13c8,
+ 0xf4d1, 0xe28f, 0xc545, 0x0a8c, 0x5274, 0xb0a6, 0xffff, 0xffff},
+ {0x22ca, 0x0cd6, 0xc1b5, 0xb064, 0x44a7, 0x297b, 0x495f, 0x34ac,
+ 0xfa95, 0xec62, 0xf08d, 0x621c, 0x66a6, 0xba94, 0x84c6, 0x8ee0}},
+ {{0xaa30, 0x312e, 0x439c, 0x4e88, 0x2e2f, 0x32dc, 0xb880, 0xa28e,
+ 0xf795, 0xc910, 0xb406, 0x8dd7, 0xb187, 0xa5a5, 0x38f1, 0xe49e},
+ {0xfb19, 0xf64a, 0xba6a, 0x8ec2, 0x7255, 0xce89, 0x2cf9, 0x9cba,
+ 0xe1fe, 0x50da, 0x1705, 0xac52, 0xe3d4, 0x4269, 0x0648, 0xfd77},
+ {0xb4c8, 0x6e8a, 0x2b5f, 0x4c2d, 0x5a67, 0xa7bb, 0x7d6d, 0x5569,
+ 0xa0ea, 0x244a, 0xc0f2, 0xf73d, 0x58cf, 0xac7f, 0xd32b, 0x3018}},
+ {{0xc953, 0x1ae1, 0xae46, 0x8709, 0x19c2, 0xa986, 0x9abe, 0x1611,
+ 0x0395, 0xd5ab, 0xf0f6, 0xb5b0, 0x5b2b, 0x0317, 0x80ba, 0x376d},
+ {0xfe77, 0xbc03, 0xac2f, 0x9d00, 0xa175, 0x293d, 0x3b56, 0x0e3a,
+ 0x0a9c, 0xf40c, 0x690e, 0x1508, 0x95d4, 0xddc4, 0xe805, 0xffff},
+ {0xb1ce, 0x0929, 0xa5fe, 0x4b50, 0x9d5d, 0x8187, 0x2557, 0x4376,
+ 0x11ba, 0xdcef, 0xc1f3, 0xd531, 0x1824, 0x93f6, 0xd81f, 0x8f83}},
+ {{0xb8d2, 0xb900, 0x4a0c, 0x7188, 0xa5bf, 0x1b0b, 0x2ae5, 0xa35b,
+ 0x98e0, 0x610c, 0x86db, 0x2487, 0xa267, 0x002c, 0xebb6, 0xc5f4},
+ {0x9cdd, 0x1c1b, 0x2f06, 0x43d1, 0xce47, 0xc334, 0x6e60, 0xc016,
+ 0x989e, 0x0ab2, 0x0cac, 0x1196, 0xe2d9, 0x2e04, 0xc62b, 0xffff},
+ {0xdc36, 0x1f05, 0x6aa9, 0x7a20, 0x944f, 0x2fd3, 0xa553, 0xdb4f,
+ 0xbd5c, 0x3a75, 0x25d4, 0xe20e, 0xa387, 0x1410, 0xdbb1, 0x1b60}},
+ {{0x76b3, 0x2207, 0x4930, 0x5dd7, 0x65a0, 0xd55c, 0xb443, 0x53b7,
+ 0x5c22, 0x818a, 0xb2e7, 0x9de8, 0x9985, 0xed45, 0x33b1, 0x53e8},
+ {0x7913, 0x44e1, 0xf15b, 0x5edd, 0x34f3, 0x4eba, 0x0758, 0x7104,
+ 0x32d9, 0x28f3, 0x4401, 0x85c5, 0xb695, 0xb899, 0xc0f2, 0xffff},
+ {0x7f43, 0xd202, 0x24c9, 0x69f3, 0x74dc, 0x1a69, 0xeaee, 0x5405,
+ 0x1755, 0x4bb8, 0x04e3, 0x2fd2, 0xada8, 0x39eb, 0x5b4d, 0x96ca}},
+ {{0x807b, 0x7112, 0xc088, 0xdafd, 0x02fa, 0x9d95, 0x5e42, 0xc033,
+ 0xde0a, 0xeecf, 0x8e90, 0x8da1, 0xb17e, 0x9a5b, 0x4c6d, 0x1914},
+ {0x4871, 0xd1cb, 0x47d7, 0x327f, 0x09ec, 0x97bb, 0x2fae, 0xd346,
+ 0x6b78, 0x3707, 0xfeb2, 0xa6ab, 0x13df, 0x76b0, 0x8fb9, 0xffb3},
+ {0x179e, 0xb63b, 0x4784, 0x231e, 0x9f42, 0x7f1a, 0xa3fb, 0xdd8c,
+ 0xd1eb, 0xb4c9, 0x8ca7, 0x018c, 0xf691, 0x576c, 0xa7d6, 0xce27}},
+ {{0x5f45, 0x7c64, 0x083d, 0xedd5, 0x08a0, 0x0c64, 0x6c6f, 0xec3c,
+ 0xe2fb, 0x352c, 0x9303, 0x75e4, 0xb4e0, 0x8b09, 0xaca4, 0x7025},
+ {0x1025, 0xb482, 0xfed5, 0xa678, 0x8966, 0x9359, 0x5329, 0x98bb,
+ 0x85b2, 0x73ba, 0x9982, 0x6fdc, 0xf190, 0xbe8c, 0xdc5c, 0xfd93},
+ {0x83a2, 0x87a4, 0xa680, 0x52a1, 0x1ba1, 0x8848, 0x5db7, 0x9744,
+ 0x409c, 0x0745, 0x0e1e, 0x1cfc, 0x00cd, 0xf573, 0x2071, 0xccaa}},
+ {{0xf61f, 0x63d4, 0x536c, 0x9eb9, 0x5ddd, 0xbb11, 0x9014, 0xe904,
+ 0xfe01, 0x6b45, 0x1858, 0xcb5b, 0x4c38, 0x43e1, 0x381d, 0x7f94},
+ {0xf61f, 0x63d4, 0xd810, 0x7ca3, 0x8a04, 0x4b83, 0x11fc, 0xdf94,
+ 0x4169, 0xbd05, 0x608e, 0x7151, 0x4fbf, 0xb31a, 0x38a7, 0xa29b},
+ {0xe621, 0xdfa5, 0x3d06, 0x1d03, 0x81e6, 0x00da, 0x53a6, 0x965e,
+ 0x93e5, 0x2164, 0x5b61, 0x59b8, 0xa629, 0x8d73, 0x699a, 0x6111}},
+ {{0x4cc3, 0xd29e, 0xf4a3, 0x3428, 0x2048, 0xeec9, 0x5f50, 0x99a4,
+ 0x6de9, 0x05f2, 0x5aa9, 0x5fd2, 0x98b4, 0x1adc, 0x225f, 0x777f},
+ {0xe649, 0x37da, 0x5ba6, 0x5765, 0x3f4a, 0x8a1c, 0x2e79, 0xf550,
+ 0x1a54, 0xcd1e, 0x7218, 0x3c3c, 0x6311, 0xfe28, 0x95fb, 0xed97},
+ {0xe9b6, 0x0c47, 0x3f0e, 0x849b, 0x11f8, 0xe599, 0x5e4d, 0xd618,
+ 0xa06d, 0x33a0, 0x9a3e, 0x44db, 0xded8, 0x10f0, 0x94d2, 0x81fb}},
+ {{0x2e59, 0x7025, 0xd413, 0x455a, 0x1ce3, 0xbd45, 0x7263, 0x27f7,
+ 0x23e3, 0x518e, 0xbe06, 0xc8c4, 0xe332, 0x4276, 0x68b4, 0xb166},
+ {0x596f, 0x0cf6, 0xc8ec, 0x787b, 0x04c1, 0x473c, 0xd2b8, 0x8d54,
+ 0x9cdf, 0x77f2, 0xd3f3, 0x6735, 0x0638, 0xf80e, 0x9467, 0xc6aa},
+ {0xc7e7, 0x1822, 0xb62a, 0xec0d, 0x89cd, 0x7846, 0xbfa2, 0x35d5,
+ 0xfa38, 0x870f, 0x494b, 0x1697, 0x8b17, 0xf904, 0x10b6, 0x9822}},
+ {{0x6d5b, 0x1d4f, 0x0aaf, 0x807b, 0x35fb, 0x7ee8, 0x00c6, 0x059a,
+ 0xddf0, 0x1fb1, 0xc38a, 0xd78e, 0x2aa4, 0x79e7, 0xad28, 0xc3f1},
+ {0xe3bb, 0x174e, 0xe0a8, 0x74b6, 0xbd5b, 0x35f6, 0x6d23, 0x6328,
+ 0xc11f, 0x83e1, 0xf928, 0xa918, 0x838e, 0xbf43, 0xe243, 0xfffb},
+ {0x9cf2, 0x6b8b, 0x3476, 0x9d06, 0xdcf2, 0xdb8a, 0x89cd, 0x4857,
+ 0x75c2, 0xabb8, 0x490b, 0xc9bd, 0x890e, 0xe36e, 0xd552, 0xfffa}},
+ {{0x2f09, 0x9d62, 0xa9fc, 0xf090, 0xd6d1, 0x9d1d, 0x1828, 0xe413,
+ 0xc92b, 0x3d5a, 0x1373, 0x368c, 0xbaf2, 0x2158, 0x71eb, 0x08a3},
+ {0x2f09, 0x1d62, 0x4630, 0x0de1, 0x06dc, 0xf7f1, 0xc161, 0x1e92,
+ 0x7495, 0x97e4, 0x94b6, 0xa39e, 0x4f1b, 0x18f8, 0x7bd4, 0x0c4c},
+ {0xeb3d, 0x723d, 0x0907, 0x525b, 0x463a, 0x49a8, 0xc6b8, 0xce7f,
+ 0x740c, 0x0d7d, 0xa83b, 0x457f, 0xae8e, 0xc6af, 0xd331, 0x0475}},
+ {{0x6abd, 0xc7af, 0x3e4e, 0x95fd, 0x8fc4, 0xee25, 0x1f9c, 0x0afe,
+ 0x291d, 0xcde0, 0x48f4, 0xb2e8, 0xf7af, 0x8f8d, 0x0bd6, 0x078d},
+ {0x4037, 0xbf0e, 0x2081, 0xf363, 0x13b2, 0x381e, 0xfb6e, 0x818e,
+ 0x27e4, 0x5662, 0x18b0, 0x0cd2, 0x81f5, 0x9415, 0x0d6c, 0xf9fb},
+ {0xd205, 0x0981, 0x0498, 0x1f08, 0xdb93, 0x1732, 0x0579, 0x1424,
+ 0xad95, 0x642f, 0x050c, 0x1d6d, 0xfc95, 0xfc4a, 0xd41b, 0x3521}},
+ {{0xf23a, 0x4633, 0xaef4, 0x1a92, 0x3c8b, 0x1f09, 0x30f3, 0x4c56,
+ 0x2a2f, 0x4f62, 0xf5e4, 0x8329, 0x63cc, 0xb593, 0xec6a, 0xc428},
+ {0x93a7, 0xfcf6, 0x606d, 0xd4b2, 0x2aad, 0x28b4, 0xc65b, 0x8998,
+ 0x4e08, 0xd178, 0x0900, 0xc82b, 0x7470, 0xa342, 0x7c0f, 0xffff},
+ {0x315f, 0xf304, 0xeb7b, 0xe5c3, 0x1451, 0x6311, 0x8f37, 0x93a8,
+ 0x4a38, 0xa6c6, 0xe393, 0x1087, 0x6301, 0xd673, 0x4ec4, 0xffff}},
+ {{0x892e, 0xeed0, 0x1165, 0xcbc1, 0x5545, 0xa280, 0x7243, 0x10c9,
+ 0x9536, 0x36af, 0xb3fc, 0x2d7c, 0xe8a5, 0x09d6, 0xe1d4, 0xe85d},
+ {0xae09, 0xc28a, 0xd777, 0xbd80, 0x23d6, 0xf980, 0xeb7c, 0x4e0e,
+ 0xf7dc, 0x6475, 0xf10a, 0x2d33, 0x5dfd, 0x797a, 0x7f1c, 0xf71a},
+ {0x4064, 0x8717, 0xd091, 0x80b0, 0x4527, 0x8442, 0xac8b, 0x9614,
+ 0xc633, 0x35f5, 0x7714, 0x2e83, 0x4aaa, 0xd2e4, 0x1acd, 0x0562}},
+ {{0xdb64, 0x0937, 0x308b, 0x53b0, 0x00e8, 0xc77f, 0x2f30, 0x37f7,
+ 0x79ce, 0xeb7f, 0xde81, 0x9286, 0xafda, 0x0e62, 0xae00, 0x0067},
+ {0x2cc7, 0xd362, 0xb161, 0x0557, 0x4ff2, 0xb9c8, 0x06fe, 0x5f2b,
+ 0xde33, 0x0190, 0x28c6, 0xb886, 0xee2b, 0x5a4e, 0x3289, 0x0185},
+ {0x4215, 0x923e, 0xf34f, 0xb362, 0x88f8, 0xceec, 0xafdd, 0x7f42,
+ 0x0c57, 0x56b2, 0xa366, 0x6a08, 0x0826, 0xfb8f, 0x1b03, 0x0163}},
+ {{0xa4ba, 0x8408, 0x810a, 0xdeba, 0x47a3, 0x853a, 0xeb64, 0x2f74,
+ 0x3039, 0x038c, 0x7fbb, 0x498e, 0xd1e9, 0x46fb, 0x5691, 0x32a4},
+ {0xd749, 0xb49d, 0x20b7, 0x2af6, 0xd34a, 0xd2da, 0x0a10, 0xf781,
+ 0x58c9, 0x171f, 0x3cb6, 0x6337, 0x88cd, 0xcf1e, 0xb246, 0x7351},
+ {0xf729, 0xcf0a, 0x96ea, 0x032c, 0x4a8f, 0x42fe, 0xbac8, 0xec65,
+ 0x1510, 0x0d75, 0x4c17, 0x8d29, 0xa03f, 0x8b7e, 0x2c49, 0x0000}},
+ {{0x0fa4, 0x8e1c, 0x3788, 0xba3c, 0x8d52, 0xd89d, 0x12c8, 0xeced,
+ 0x9fe6, 0x9b88, 0xecf3, 0xe3c8, 0xac48, 0x76ed, 0xf23e, 0xda79},
+ {0x1103, 0x227c, 0x5b00, 0x3fcf, 0xc5d0, 0x2d28, 0x8020, 0x4d1c,
+ 0xc6b9, 0x67f9, 0x6f39, 0x989a, 0xda53, 0x3847, 0xd416, 0xe0d0},
+ {0xdd8e, 0xcf31, 0x3710, 0x7e44, 0xa511, 0x933c, 0x0cc3, 0x5145,
+ 0xf632, 0x5e1d, 0x038f, 0x5ce7, 0x7265, 0xda9d, 0xded6, 0x08f8}},
+ {{0xe2c8, 0x91d5, 0xa5f5, 0x735f, 0x6b58, 0x56dc, 0xb39d, 0x5c4a,
+ 0x57d0, 0xa1c2, 0xd92f, 0x9ad4, 0xf7c4, 0x51dd, 0xaf5c, 0x0096},
+ {0x1739, 0x7207, 0x7505, 0xbf35, 0x42de, 0x0a29, 0xa962, 0xdedf,
+ 0x53e8, 0x12bf, 0xcde7, 0xd8e2, 0x8d4d, 0x2c4b, 0xb1b1, 0x0628},
+ {0x992d, 0xe3a7, 0xb422, 0xc198, 0x23ab, 0xa6ef, 0xb45d, 0x50da,
+ 0xa738, 0x014a, 0x2310, 0x85fb, 0x5fe8, 0x1b18, 0x1774, 0x03a7}},
+ {{0x1f16, 0x2b09, 0x0236, 0xee90, 0xccf9, 0x9775, 0x8130, 0x4c91,
+ 0x9091, 0x310b, 0x6dc4, 0x86f6, 0xc2e8, 0xef60, 0xfc0e, 0xf3a4},
+ {0x9f49, 0xac15, 0x02af, 0x110f, 0xc59d, 0x5677, 0xa1a9, 0x38d5,
+ 0x914f, 0xa909, 0x3a3a, 0x4a39, 0x3703, 0xea30, 0x73da, 0xffad},
+ {0x15ed, 0xdd16, 0x83c7, 0x270a, 0x862f, 0xd8ad, 0xcaa1, 0x5f41,
+ 0x99a9, 0x3fc8, 0x7bb2, 0x360a, 0xb06d, 0xfadc, 0x1b36, 0xffa8}},
+ {{0xc4e0, 0xb8fd, 0x5106, 0xe169, 0x754c, 0xa58c, 0xc413, 0x8224,
+ 0x5483, 0x63ec, 0xd477, 0x8473, 0x4778, 0x9281, 0x0000, 0x0000},
+ {0x85e1, 0xff54, 0xb200, 0xe413, 0xf4f4, 0x4c0f, 0xfcec, 0xc183,
+ 0x60d3, 0x1b0c, 0x3834, 0x601c, 0x943c, 0xbe6e, 0x0002, 0x0000},
+ {0xf4f8, 0xfd5e, 0x61ef, 0xece8, 0x9199, 0xe5c4, 0x05a6, 0xe6c3,
+ 0xc4ae, 0x8b28, 0x66b1, 0x8a95, 0x9ece, 0x8f4a, 0x0001, 0x0000}},
+ {{0xeae9, 0xa1b4, 0xc6d8, 0x2411, 0x2b5a, 0x1dd0, 0x2dc9, 0xb57b,
+ 0x5ccd, 0x4957, 0xaf59, 0xa04b, 0x5f42, 0xab7c, 0x2826, 0x526f},
+ {0xf407, 0x165a, 0xb724, 0x2f12, 0x2ea1, 0x470b, 0x4464, 0xbd35,
+ 0x606f, 0xd73e, 0x50d3, 0x8a7f, 0x8029, 0x7ffc, 0xbe31, 0x6cfb},
+ {0x8171, 0x1f4c, 0xced2, 0x9c99, 0x6d7e, 0x5a0f, 0xfefb, 0x59e3,
+ 0xa0c8, 0xabd9, 0xc4c5, 0x57d3, 0xbfa3, 0x4f11, 0x96a2, 0x5a7d}},
+ {{0xe068, 0x4cc0, 0x8bcd, 0xc903, 0x9e52, 0xb3e1, 0xd745, 0x0995,
+ 0xdd8f, 0xf14b, 0xd2ac, 0xd65a, 0xda1d, 0xa742, 0xbac5, 0x474c},
+ {0x7481, 0xf2ad, 0x9757, 0x2d82, 0xb683, 0xb16b, 0x0002, 0x7b60,
+ 0x8f0c, 0x2594, 0x8f64, 0x3b7a, 0x3552, 0x8d9d, 0xb9d7, 0x67eb},
+ {0xcaab, 0xb9a1, 0xf966, 0xe311, 0x5b34, 0x0fa0, 0x6abc, 0x8134,
+ 0xab3d, 0x90f6, 0x1984, 0x9232, 0xec17, 0x74e5, 0x2ceb, 0x434e}},
+ {{0x0fb1, 0x7a55, 0x1a5c, 0x53eb, 0xd7b3, 0x7a01, 0xca32, 0x31f6,
+ 0x3b74, 0x679e, 0x1501, 0x6c57, 0xdb20, 0x8b7c, 0xd7d0, 0x8097},
+ {0xb127, 0xb20c, 0xe3a2, 0x96f3, 0xe0d8, 0xd50c, 0x14b4, 0x0b40,
+ 0x6eeb, 0xa258, 0x99db, 0x3c8c, 0x0f51, 0x4198, 0x3887, 0xffd0},
+ {0x0273, 0x9f8c, 0x9669, 0xbbba, 0x1c49, 0x767c, 0xc2af, 0x59f0,
+ 0x1366, 0xd397, 0x63ac, 0x6fe8, 0x1a9a, 0x1259, 0x01d0, 0x0016}},
+ {{0x7876, 0x2a35, 0xa24a, 0x433e, 0x5501, 0x573c, 0xd76d, 0xcb82,
+ 0x1334, 0xb4a6, 0xf290, 0xc797, 0xeae9, 0x2b83, 0x1e2b, 0x8b14},
+ {0x3885, 0x8aef, 0x9dea, 0x2b8c, 0xdd7c, 0xd7cd, 0xb0cc, 0x05ee,
+ 0x361b, 0x3800, 0xb0d4, 0x4c23, 0xbd3f, 0x5180, 0x9783, 0xff80},
+ {0xab36, 0x3104, 0xdae8, 0x0704, 0x4a28, 0x6714, 0x824b, 0x0051,
+ 0x8134, 0x1f6a, 0x712d, 0x1f03, 0x03b2, 0xecac, 0x377d, 0xfef9}}
+ };
+
+ int i, j, ok;
+
+ /* Test known inputs/outputs */
+ for (i = 0; (size_t)i < sizeof(CASES) / sizeof(CASES[0]); ++i) {
+ uint16_t out[16];
+ test_modinv32_uint16(out, CASES[i][0], CASES[i][1]);
+ for (j = 0; j < 16; ++j) CHECK(out[j] == CASES[i][2][j]);
+#ifdef SECP256K1_WIDEMUL_INT128
+ test_modinv64_uint16(out, CASES[i][0], CASES[i][1]);
+ for (j = 0; j < 16; ++j) CHECK(out[j] == CASES[i][2][j]);
#endif
+ }
+
+ for (i = 0; i < 100 * count; ++i) {
+ /* 256-bit numbers in 16-uint16_t's notation */
+ static const uint16_t ZERO[16] = {0};
+ uint16_t xd[16]; /* the number (in range [0,2^256)) to be inverted */
+ uint16_t md[16]; /* the modulus (odd, in range [3,2^256)) */
+ uint16_t id[16]; /* the inverse of xd mod md */
+
+ /* generate random xd and md, so that md is odd, md>1, xd<md, and gcd(xd,md)=1 */
+ do {
+ /* generate random xd and md (with many subsequent 0s and 1s) */
+ secp256k1_testrand256_test((unsigned char*)xd);
+ secp256k1_testrand256_test((unsigned char*)md);
+ md[0] |= 1; /* modulus must be odd */
+ /* If modulus is 1, find another one. */
+ ok = md[0] != 1;
+ for (j = 1; j < 16; ++j) ok |= md[j] != 0;
+ mulmod256(xd, xd, NULL, md); /* Make xd = xd mod md */
+ } while (!(ok && coprime(xd, md)));
+
+ test_modinv32_uint16(id, xd, md);
+#ifdef SECP256K1_WIDEMUL_INT128
+ test_modinv64_uint16(id, xd, md);
+#endif
+
+ /* In a few cases, also test with input=0 */
+ if (i < count) {
+ test_modinv32_uint16(id, ZERO, md);
+#ifdef SECP256K1_WIDEMUL_INT128
+ test_modinv64_uint16(id, ZERO, md);
+#endif
+ }
+ }
+}
/***** SCALAR TESTS *****/
+
void scalar_test(void) {
secp256k1_scalar s;
secp256k1_scalar s1;
secp256k1_scalar s2;
-#ifndef USE_NUM_NONE
- secp256k1_num snum, s1num, s2num;
- secp256k1_num order, half_order;
-#endif
unsigned char c[32];
/* Set 's' to a random scalar, with value 'snum'. */
@@ -819,16 +1566,6 @@ void scalar_test(void) {
random_scalar_order_test(&s2);
secp256k1_scalar_get_b32(c, &s2);
-#ifndef USE_NUM_NONE
- secp256k1_scalar_get_num(&snum, &s);
- secp256k1_scalar_get_num(&s1num, &s1);
- secp256k1_scalar_get_num(&s2num, &s2);
-
- secp256k1_scalar_order_get_num(&order);
- half_order = order;
- secp256k1_num_shift(&half_order, 1);
-#endif
-
{
int i;
/* Test that fetching groups of 4 bits from a scalar and recursing n(i)=16*n(i-1)+p(i) reconstructs it. */
@@ -868,80 +1605,6 @@ void scalar_test(void) {
CHECK(secp256k1_scalar_eq(&n, &s));
}
-#ifndef USE_NUM_NONE
- {
- /* Test that adding the scalars together is equal to adding their numbers together modulo the order. */
- secp256k1_num rnum;
- secp256k1_num r2num;
- secp256k1_scalar r;
- secp256k1_num_add(&rnum, &snum, &s2num);
- secp256k1_num_mod(&rnum, &order);
- secp256k1_scalar_add(&r, &s, &s2);
- secp256k1_scalar_get_num(&r2num, &r);
- CHECK(secp256k1_num_eq(&rnum, &r2num));
- }
-
- {
- /* Test that multiplying the scalars is equal to multiplying their numbers modulo the order. */
- secp256k1_scalar r;
- secp256k1_num r2num;
- secp256k1_num rnum;
- secp256k1_num_mul(&rnum, &snum, &s2num);
- secp256k1_num_mod(&rnum, &order);
- secp256k1_scalar_mul(&r, &s, &s2);
- secp256k1_scalar_get_num(&r2num, &r);
- CHECK(secp256k1_num_eq(&rnum, &r2num));
- /* The result can only be zero if at least one of the factors was zero. */
- CHECK(secp256k1_scalar_is_zero(&r) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_zero(&s2)));
- /* The results can only be equal to one of the factors if that factor was zero, or the other factor was one. */
- CHECK(secp256k1_num_eq(&rnum, &snum) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_one(&s2)));
- CHECK(secp256k1_num_eq(&rnum, &s2num) == (secp256k1_scalar_is_zero(&s2) || secp256k1_scalar_is_one(&s)));
- }
-
- {
- secp256k1_scalar neg;
- secp256k1_num negnum;
- secp256k1_num negnum2;
- /* Check that comparison with zero matches comparison with zero on the number. */
- CHECK(secp256k1_num_is_zero(&snum) == secp256k1_scalar_is_zero(&s));
- /* Check that comparison with the half order is equal to testing for high scalar. */
- CHECK(secp256k1_scalar_is_high(&s) == (secp256k1_num_cmp(&snum, &half_order) > 0));
- secp256k1_scalar_negate(&neg, &s);
- secp256k1_num_sub(&negnum, &order, &snum);
- secp256k1_num_mod(&negnum, &order);
- /* Check that comparison with the half order is equal to testing for high scalar after negation. */
- CHECK(secp256k1_scalar_is_high(&neg) == (secp256k1_num_cmp(&negnum, &half_order) > 0));
- /* Negating should change the high property, unless the value was already zero. */
- CHECK((secp256k1_scalar_is_high(&s) == secp256k1_scalar_is_high(&neg)) == secp256k1_scalar_is_zero(&s));
- secp256k1_scalar_get_num(&negnum2, &neg);
- /* Negating a scalar should be equal to (order - n) mod order on the number. */
- CHECK(secp256k1_num_eq(&negnum, &negnum2));
- secp256k1_scalar_add(&neg, &neg, &s);
- /* Adding a number to its negation should result in zero. */
- CHECK(secp256k1_scalar_is_zero(&neg));
- secp256k1_scalar_negate(&neg, &neg);
- /* Negating zero should still result in zero. */
- CHECK(secp256k1_scalar_is_zero(&neg));
- }
-
- {
- /* Test secp256k1_scalar_mul_shift_var. */
- secp256k1_scalar r;
- secp256k1_num one;
- secp256k1_num rnum;
- secp256k1_num rnum2;
- unsigned char cone[1] = {0x01};
- unsigned int shift = 256 + secp256k1_testrand_int(257);
- secp256k1_scalar_mul_shift_var(&r, &s1, &s2, shift);
- secp256k1_num_mul(&rnum, &s1num, &s2num);
- secp256k1_num_shift(&rnum, shift - 1);
- secp256k1_num_set_bin(&one, cone, 1);
- secp256k1_num_add(&rnum, &rnum, &one);
- secp256k1_num_shift(&rnum, 1);
- secp256k1_scalar_get_num(&rnum2, &r);
- CHECK(secp256k1_num_eq(&rnum, &rnum2));
- }
-
{
/* test secp256k1_scalar_shr_int */
secp256k1_scalar r;
@@ -955,34 +1618,6 @@ void scalar_test(void) {
CHECK(expected == low);
}
}
-#endif
-
- {
- /* Test that scalar inverses are equal to the inverse of their number modulo the order. */
- if (!secp256k1_scalar_is_zero(&s)) {
- secp256k1_scalar inv;
-#ifndef USE_NUM_NONE
- secp256k1_num invnum;
- secp256k1_num invnum2;
-#endif
- secp256k1_scalar_inverse(&inv, &s);
-#ifndef USE_NUM_NONE
- secp256k1_num_mod_inverse(&invnum, &snum, &order);
- secp256k1_scalar_get_num(&invnum2, &inv);
- CHECK(secp256k1_num_eq(&invnum, &invnum2));
-#endif
- secp256k1_scalar_mul(&inv, &inv, &s);
- /* Multiplying a scalar with its inverse must result in one. */
- CHECK(secp256k1_scalar_is_one(&inv));
- secp256k1_scalar_inverse(&inv, &inv);
- /* Inverting one must result in one. */
- CHECK(secp256k1_scalar_is_one(&inv));
-#ifndef USE_NUM_NONE
- secp256k1_scalar_get_num(&invnum, &inv);
- CHECK(secp256k1_num_is_one(&invnum));
-#endif
- }
- }
{
/* Test commutativity of add. */
@@ -1055,14 +1690,6 @@ void scalar_test(void) {
}
{
- /* Test square. */
- secp256k1_scalar r1, r2;
- secp256k1_scalar_sqr(&r1, &s1);
- secp256k1_scalar_mul(&r2, &s1, &s1);
- CHECK(secp256k1_scalar_eq(&r1, &r2));
- }
-
- {
/* Test multiplicative identity. */
secp256k1_scalar r1, v1;
secp256k1_scalar_set_int(&v1,1);
@@ -1126,48 +1753,6 @@ void run_scalar_tests(void) {
CHECK(secp256k1_scalar_is_zero(&o));
}
-#ifndef USE_NUM_NONE
- {
- /* Test secp256k1_scalar_set_b32 boundary conditions */
- secp256k1_num order;
- secp256k1_scalar scalar;
- unsigned char bin[32];
- unsigned char bin_tmp[32];
- int overflow = 0;
- /* 2^256-1 - order */
- static const secp256k1_scalar all_ones_minus_order = SECP256K1_SCALAR_CONST(
- 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000001UL,
- 0x45512319UL, 0x50B75FC4UL, 0x402DA173UL, 0x2FC9BEBEUL
- );
-
- /* A scalar set to 0s should be 0. */
- memset(bin, 0, 32);
- secp256k1_scalar_set_b32(&scalar, bin, &overflow);
- CHECK(overflow == 0);
- CHECK(secp256k1_scalar_is_zero(&scalar));
-
- /* A scalar with value of the curve order should be 0. */
- secp256k1_scalar_order_get_num(&order);
- secp256k1_num_get_bin(bin, 32, &order);
- secp256k1_scalar_set_b32(&scalar, bin, &overflow);
- CHECK(overflow == 1);
- CHECK(secp256k1_scalar_is_zero(&scalar));
-
- /* A scalar with value of the curve order minus one should not overflow. */
- bin[31] -= 1;
- secp256k1_scalar_set_b32(&scalar, bin, &overflow);
- CHECK(overflow == 0);
- secp256k1_scalar_get_b32(bin_tmp, &scalar);
- CHECK(secp256k1_memcmp_var(bin, bin_tmp, 32) == 0);
-
- /* A scalar set to all 1s should overflow. */
- memset(bin, 0xFF, 32);
- secp256k1_scalar_set_b32(&scalar, bin, &overflow);
- CHECK(overflow == 1);
- CHECK(secp256k1_scalar_eq(&scalar, &all_ones_minus_order));
- }
-#endif
-
{
/* Does check_overflow check catch all ones? */
static const secp256k1_scalar overflowed = SECP256K1_SCALAR_CONST(
@@ -1190,9 +1775,7 @@ void run_scalar_tests(void) {
secp256k1_scalar one;
secp256k1_scalar r1;
secp256k1_scalar r2;
-#if defined(USE_SCALAR_INV_NUM)
secp256k1_scalar zzv;
-#endif
int overflow;
unsigned char chal[33][2][32] = {
{{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00,
@@ -1742,10 +2325,8 @@ void run_scalar_tests(void) {
if (!secp256k1_scalar_is_zero(&y)) {
secp256k1_scalar_inverse(&zz, &y);
CHECK(!secp256k1_scalar_check_overflow(&zz));
-#if defined(USE_SCALAR_INV_NUM)
secp256k1_scalar_inverse_var(&zzv, &y);
CHECK(secp256k1_scalar_eq(&zzv, &zz));
-#endif
secp256k1_scalar_mul(&z, &z, &zz);
CHECK(!secp256k1_scalar_check_overflow(&z));
CHECK(secp256k1_scalar_eq(&x, &z));
@@ -1753,12 +2334,6 @@ void run_scalar_tests(void) {
CHECK(!secp256k1_scalar_check_overflow(&zz));
CHECK(secp256k1_scalar_eq(&one, &zz));
}
- secp256k1_scalar_mul(&z, &x, &x);
- CHECK(!secp256k1_scalar_check_overflow(&z));
- secp256k1_scalar_sqr(&zz, &x);
- CHECK(!secp256k1_scalar_check_overflow(&zz));
- CHECK(secp256k1_scalar_eq(&zz, &z));
- CHECK(secp256k1_scalar_eq(&r2, &zz));
}
}
}
@@ -1814,13 +2389,6 @@ int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
return secp256k1_fe_equal_var(&an, &bn);
}
-int check_fe_inverse(const secp256k1_fe *a, const secp256k1_fe *ai) {
- secp256k1_fe x;
- secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
- secp256k1_fe_mul(&x, a, ai);
- return check_fe_equal(&x, &one);
-}
-
void run_field_convert(void) {
static const unsigned char b32[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -1940,52 +2508,6 @@ void run_field_misc(void) {
}
}
-void run_field_inv(void) {
- secp256k1_fe x, xi, xii;
- int i;
- for (i = 0; i < 10*count; i++) {
- random_fe_non_zero(&x);
- secp256k1_fe_inv(&xi, &x);
- CHECK(check_fe_inverse(&x, &xi));
- secp256k1_fe_inv(&xii, &xi);
- CHECK(check_fe_equal(&x, &xii));
- }
-}
-
-void run_field_inv_var(void) {
- secp256k1_fe x, xi, xii;
- int i;
- for (i = 0; i < 10*count; i++) {
- random_fe_non_zero(&x);
- secp256k1_fe_inv_var(&xi, &x);
- CHECK(check_fe_inverse(&x, &xi));
- secp256k1_fe_inv_var(&xii, &xi);
- CHECK(check_fe_equal(&x, &xii));
- }
-}
-
-void run_field_inv_all_var(void) {
- secp256k1_fe x[16], xi[16], xii[16];
- int i;
- /* Check it's safe to call for 0 elements */
- secp256k1_fe_inv_all_var(xi, x, 0);
- for (i = 0; i < count; i++) {
- size_t j;
- size_t len = secp256k1_testrand_int(15) + 1;
- for (j = 0; j < len; j++) {
- random_fe_non_zero(&x[j]);
- }
- secp256k1_fe_inv_all_var(xi, x, len);
- for (j = 0; j < len; j++) {
- CHECK(check_fe_inverse(&x[j], &xi[j]));
- }
- secp256k1_fe_inv_all_var(xii, xi, len);
- for (j = 0; j < len; j++) {
- CHECK(check_fe_equal(&x[j], &xii[j]));
- }
- }
-}
-
void run_sqr(void) {
secp256k1_fe x, s;
@@ -2050,6 +2572,318 @@ void run_sqrt(void) {
}
}
+/***** FIELD/SCALAR INVERSE TESTS *****/
+
+static const secp256k1_scalar scalar_minus_one = SECP256K1_SCALAR_CONST(
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE,
+ 0xBAAEDCE6, 0xAF48A03B, 0xBFD25E8C, 0xD0364140
+);
+
+static const secp256k1_fe fe_minus_one = SECP256K1_FE_CONST(
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFC2E
+);
+
+/* These tests test the following identities:
+ *
+ * for x==0: 1/x == 0
+ * for x!=0: x*(1/x) == 1
+ * for x!=0 and x!=1: 1/(1/x - 1) + 1 == -1/(x-1)
+ */
+
+void test_inverse_scalar(secp256k1_scalar* out, const secp256k1_scalar* x, int var)
+{
+ secp256k1_scalar l, r, t;
+
+ (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&l, x); /* l = 1/x */
+ if (out) *out = l;
+ if (secp256k1_scalar_is_zero(x)) {
+ CHECK(secp256k1_scalar_is_zero(&l));
+ return;
+ }
+ secp256k1_scalar_mul(&t, x, &l); /* t = x*(1/x) */
+ CHECK(secp256k1_scalar_is_one(&t)); /* x*(1/x) == 1 */
+ secp256k1_scalar_add(&r, x, &scalar_minus_one); /* r = x-1 */
+ if (secp256k1_scalar_is_zero(&r)) return;
+ (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&r, &r); /* r = 1/(x-1) */
+ secp256k1_scalar_add(&l, &scalar_minus_one, &l); /* l = 1/x-1 */
+ (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&l, &l); /* l = 1/(1/x-1) */
+ secp256k1_scalar_add(&l, &l, &secp256k1_scalar_one); /* l = 1/(1/x-1)+1 */
+ secp256k1_scalar_add(&l, &r, &l); /* l = 1/(1/x-1)+1 + 1/(x-1) */
+ CHECK(secp256k1_scalar_is_zero(&l)); /* l == 0 */
+}
+
+void test_inverse_field(secp256k1_fe* out, const secp256k1_fe* x, int var)
+{
+ secp256k1_fe l, r, t;
+
+ (var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&l, x) ; /* l = 1/x */
+ if (out) *out = l;
+ t = *x; /* t = x */
+ if (secp256k1_fe_normalizes_to_zero_var(&t)) {
+ CHECK(secp256k1_fe_normalizes_to_zero(&l));
+ return;
+ }
+ secp256k1_fe_mul(&t, x, &l); /* t = x*(1/x) */
+ secp256k1_fe_add(&t, &fe_minus_one); /* t = x*(1/x)-1 */
+ CHECK(secp256k1_fe_normalizes_to_zero(&t)); /* x*(1/x)-1 == 0 */
+ r = *x; /* r = x */
+ secp256k1_fe_add(&r, &fe_minus_one); /* r = x-1 */
+ if (secp256k1_fe_normalizes_to_zero_var(&r)) return;
+ (var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&r, &r); /* r = 1/(x-1) */
+ secp256k1_fe_add(&l, &fe_minus_one); /* l = 1/x-1 */
+ (var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&l, &l); /* l = 1/(1/x-1) */
+ secp256k1_fe_add(&l, &secp256k1_fe_one); /* l = 1/(1/x-1)+1 */
+ secp256k1_fe_add(&l, &r); /* l = 1/(1/x-1)+1 + 1/(x-1) */
+ CHECK(secp256k1_fe_normalizes_to_zero_var(&l)); /* l == 0 */
+}
+
+void run_inverse_tests(void)
+{
+ /* Fixed test cases for field inverses: pairs of (x, 1/x) mod p. */
+ static const secp256k1_fe fe_cases[][2] = {
+ /* 0 */
+ {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0),
+ SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)},
+ /* 1 */
+ {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1),
+ SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1)},
+ /* -1 */
+ {SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc2e),
+ SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc2e)},
+ /* 2 */
+ {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2),
+ SECP256K1_FE_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ffffe18)},
+ /* 2**128 */
+ {SECP256K1_FE_CONST(0, 0, 0, 1, 0, 0, 0, 0),
+ SECP256K1_FE_CONST(0xbcb223fe, 0xdc24a059, 0xd838091d, 0xd2253530, 0xffffffff, 0xffffffff, 0xffffffff, 0x434dd931)},
+ /* Input known to need 637 divsteps */
+ {SECP256K1_FE_CONST(0xe34e9c95, 0x6bee8a84, 0x0dcb632a, 0xdb8a1320, 0x66885408, 0x06f3f996, 0x7c11ca84, 0x19199ec3),
+ SECP256K1_FE_CONST(0xbd2cbd8f, 0x1c536828, 0x9bccda44, 0x2582ac0c, 0x870152b0, 0x8a3f09fb, 0x1aaadf92, 0x19b618e5)},
+ /* Input known to need 567 divsteps starting with delta=1/2. */
+ {SECP256K1_FE_CONST(0xf6bc3ba3, 0x636451c4, 0x3e46357d, 0x2c21d619, 0x0988e234, 0x15985661, 0x6672982b, 0xa7549bfc),
+ SECP256K1_FE_CONST(0xb024fdc7, 0x5547451e, 0x426c585f, 0xbd481425, 0x73df6b75, 0xeef6d9d0, 0x389d87d4, 0xfbb440ba)},
+ /* Input known to need 566 divsteps starting with delta=1/2. */
+ {SECP256K1_FE_CONST(0xb595d81b, 0x2e3c1e2f, 0x482dbc65, 0xe4865af7, 0x9a0a50aa, 0x29f9e618, 0x6f87d7a5, 0x8d1063ae),
+ SECP256K1_FE_CONST(0xc983337c, 0x5d5c74e1, 0x49918330, 0x0b53afb5, 0xa0428a0b, 0xce6eef86, 0x059bd8ef, 0xe5b908de)},
+ /* Set of 10 inputs accessing all 128 entries in the modinv32 divsteps_var table */
+ {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0xe0ff1f80, 0x1f000000, 0x00000000, 0x00000000, 0xfeff0100, 0x00000000),
+ SECP256K1_FE_CONST(0x9faf9316, 0x77e5049d, 0x0b5e7a1b, 0xef70b893, 0x18c9e30c, 0x045e7fd7, 0x29eddf8c, 0xd62e9e3d)},
+ {SECP256K1_FE_CONST(0x621a538d, 0x511b2780, 0x35688252, 0x53f889a4, 0x6317c3ac, 0x32ba0a46, 0x6277c0d1, 0xccd31192),
+ SECP256K1_FE_CONST(0x38513b0c, 0x5eba856f, 0xe29e882e, 0x9b394d8c, 0x34bda011, 0xeaa66943, 0x6a841a4c, 0x6ae8bcff)},
+ {SECP256K1_FE_CONST(0x00000200, 0xf0ffff1f, 0x00000000, 0x0000e0ff, 0xffffffff, 0xfffcffff, 0xffffffff, 0xffff0100),
+ SECP256K1_FE_CONST(0x5da42a52, 0x3640de9e, 0x13e64343, 0x0c7591b7, 0x6c1e3519, 0xf048c5b6, 0x0484217c, 0xedbf8b2f)},
+ {SECP256K1_FE_CONST(0xd1343ef9, 0x4b952621, 0x7c52a2ee, 0x4ea1281b, 0x4ab46410, 0x9f26998d, 0xa686a8ff, 0x9f2103e8),
+ SECP256K1_FE_CONST(0x84044385, 0x9a4619bf, 0x74e35b6d, 0xa47e0c46, 0x6b7fb47d, 0x9ffab128, 0xb0775aa3, 0xcb318bd1)},
+ {SECP256K1_FE_CONST(0xb27235d2, 0xc56a52be, 0x210db37a, 0xd50d23a4, 0xbe621bdd, 0x5df22c6a, 0xe926ba62, 0xd2e4e440),
+ SECP256K1_FE_CONST(0x67a26e54, 0x483a9d3c, 0xa568469e, 0xd258ab3d, 0xb9ec9981, 0xdca9b1bd, 0x8d2775fe, 0x53ae429b)},
+ {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00e0ffff, 0xffffff83, 0xffffffff, 0x3f00f00f, 0x000000e0, 0xffffffff),
+ SECP256K1_FE_CONST(0x310e10f8, 0x23bbfab0, 0xac94907d, 0x076c9a45, 0x8d357d7f, 0xc763bcee, 0x00d0e615, 0x5a6acef6)},
+ {SECP256K1_FE_CONST(0xfeff0300, 0x001c0000, 0xf80700c0, 0x0ff0ffff, 0xffffffff, 0x0fffffff, 0xffff0100, 0x7f0000fe),
+ SECP256K1_FE_CONST(0x28e2fdb4, 0x0709168b, 0x86f598b0, 0x3453a370, 0x530cf21f, 0x32f978d5, 0x1d527a71, 0x59269b0c)},
+ {SECP256K1_FE_CONST(0xc2591afa, 0x7bb98ef7, 0x090bb273, 0x85c14f87, 0xbb0b28e0, 0x54d3c453, 0x85c66753, 0xd5574d2f),
+ SECP256K1_FE_CONST(0xfdca70a2, 0x70ce627c, 0x95e66fae, 0x848a6dbb, 0x07ffb15c, 0x5f63a058, 0xba4140ed, 0x6113b503)},
+ {SECP256K1_FE_CONST(0xf5475db3, 0xedc7b5a3, 0x411c047e, 0xeaeb452f, 0xc625828e, 0x1cf5ad27, 0x8eec1060, 0xc7d3e690),
+ SECP256K1_FE_CONST(0x5eb756c0, 0xf963f4b9, 0xdc6a215e, 0xec8cc2d8, 0x2e9dec01, 0xde5eb88d, 0x6aba7164, 0xaecb2c5a)},
+ {SECP256K1_FE_CONST(0x00000000, 0x00f8ffff, 0xffffffff, 0x01000000, 0xe0ff1f00, 0x00000000, 0xffffff7f, 0x00000000),
+ SECP256K1_FE_CONST(0xe0d2e3d8, 0x49b6157d, 0xe54e88c2, 0x1a7f02ca, 0x7dd28167, 0xf1125d81, 0x7bfa444e, 0xbe110037)},
+ /* Selection of randomly generated inputs that reach high/low d/e values in various configurations. */
+ {SECP256K1_FE_CONST(0x13cc08a4, 0xd8c41f0f, 0x179c3e67, 0x54c46c67, 0xc4109221, 0x09ab3b13, 0xe24d9be1, 0xffffe950),
+ SECP256K1_FE_CONST(0xb80c8006, 0xd16abaa7, 0xcabd71e5, 0xcf6714f4, 0x966dd3d0, 0x64767a2d, 0xe92c4441, 0x51008cd1)},
+ {SECP256K1_FE_CONST(0xaa6db990, 0x95efbca1, 0x3cc6ff71, 0x0602e24a, 0xf49ff938, 0x99fffc16, 0x46f40993, 0xc6e72057),
+ SECP256K1_FE_CONST(0xd5d3dd69, 0xb0c195e5, 0x285f1d49, 0xe639e48c, 0x9223f8a9, 0xca1d731d, 0x9ca482f9, 0xa5b93e06)},
+ {SECP256K1_FE_CONST(0x1c680eac, 0xaeabffd8, 0x9bdc4aee, 0x1781e3de, 0xa3b08108, 0x0015f2e0, 0x94449e1b, 0x2f67a058),
+ SECP256K1_FE_CONST(0x7f083f8d, 0x31254f29, 0x6510f475, 0x245c373d, 0xc5622590, 0x4b323393, 0x32ed1719, 0xc127444b)},
+ {SECP256K1_FE_CONST(0x147d44b3, 0x012d83f8, 0xc160d386, 0x1a44a870, 0x9ba6be96, 0x8b962707, 0x267cbc1a, 0xb65b2f0a),
+ SECP256K1_FE_CONST(0x555554ff, 0x170aef1e, 0x50a43002, 0xe51fbd36, 0xafadb458, 0x7a8aded1, 0x0ca6cd33, 0x6ed9087c)},
+ {SECP256K1_FE_CONST(0x12423796, 0x22f0fe61, 0xf9ca017c, 0x5384d107, 0xa1fbf3b2, 0x3b018013, 0x916a3c37, 0x4000b98c),
+ SECP256K1_FE_CONST(0x20257700, 0x08668f94, 0x1177e306, 0x136c01f5, 0x8ed1fbd2, 0x95ec4589, 0xae38edb9, 0xfd19b6d7)},
+ {SECP256K1_FE_CONST(0xdcf2d030, 0x9ab42cb4, 0x93ffa181, 0xdcd23619, 0x39699b52, 0x08909a20, 0xb5a17695, 0x3a9dcf21),
+ SECP256K1_FE_CONST(0x1f701dea, 0xe211fb1f, 0x4f37180d, 0x63a0f51c, 0x29fe1e40, 0xa40b6142, 0x2e7b12eb, 0x982b06b6)},
+ {SECP256K1_FE_CONST(0x79a851f6, 0xa6314ed3, 0xb35a55e6, 0xca1c7d7f, 0xe32369ea, 0xf902432e, 0x375308c5, 0xdfd5b600),
+ SECP256K1_FE_CONST(0xcaae00c5, 0xe6b43851, 0x9dabb737, 0x38cba42c, 0xa02c8549, 0x7895dcbf, 0xbd183d71, 0xafe4476a)},
+ {SECP256K1_FE_CONST(0xede78fdd, 0xcfc92bf1, 0x4fec6c6c, 0xdb8d37e2, 0xfb66bc7b, 0x28701870, 0x7fa27c9a, 0x307196ec),
+ SECP256K1_FE_CONST(0x68193a6c, 0x9a8b87a7, 0x2a760c64, 0x13e473f6, 0x23ae7bed, 0x1de05422, 0x88865427, 0xa3418265)},
+ {SECP256K1_FE_CONST(0xa40b2079, 0xb8f88e89, 0xa7617997, 0x89baf5ae, 0x174df343, 0x75138eae, 0x2711595d, 0x3fc3e66c),
+ SECP256K1_FE_CONST(0x9f99c6a5, 0x6d685267, 0xd4b87c37, 0x9d9c4576, 0x358c692b, 0x6bbae0ed, 0x3389c93d, 0x7fdd2655)},
+ {SECP256K1_FE_CONST(0x7c74c6b6, 0xe98d9151, 0x72645cf1, 0x7f06e321, 0xcefee074, 0x15b2113a, 0x10a9be07, 0x08a45696),
+ SECP256K1_FE_CONST(0x8c919a88, 0x898bc1e0, 0x77f26f97, 0x12e655b7, 0x9ba0ac40, 0xe15bb19e, 0x8364cc3b, 0xe227a8ee)},
+ {SECP256K1_FE_CONST(0x109ba1ce, 0xdafa6d4a, 0xa1cec2b2, 0xeb1069f4, 0xb7a79e5b, 0xec6eb99b, 0xaec5f643, 0xee0e723e),
+ SECP256K1_FE_CONST(0x93d13eb8, 0x4bb0bcf9, 0xe64f5a71, 0xdbe9f359, 0x7191401c, 0x6f057a4a, 0xa407fe1b, 0x7ecb65cc)},
+ {SECP256K1_FE_CONST(0x3db076cd, 0xec74a5c9, 0xf61dd138, 0x90e23e06, 0xeeedd2d0, 0x74cbc4e0, 0x3dbe1e91, 0xded36a78),
+ SECP256K1_FE_CONST(0x3f07f966, 0x8e2a1e09, 0x706c71df, 0x02b5e9d5, 0xcb92ddbf, 0xcdd53010, 0x16545564, 0xe660b107)},
+ {SECP256K1_FE_CONST(0xe31c73ed, 0xb4c4b82c, 0x02ae35f7, 0x4cdec153, 0x98b522fd, 0xf7d2460c, 0x6bf7c0f8, 0x4cf67b0d),
+ SECP256K1_FE_CONST(0x4b8f1faf, 0x94e8b070, 0x19af0ff6, 0xa319cd31, 0xdf0a7ffb, 0xefaba629, 0x59c50666, 0x1fe5b843)},
+ {SECP256K1_FE_CONST(0x4c8b0e6e, 0x83392ab6, 0xc0e3e9f1, 0xbbd85497, 0x16698897, 0xf552d50d, 0x79652ddb, 0x12f99870),
+ SECP256K1_FE_CONST(0x56d5101f, 0xd23b7949, 0x17dc38d6, 0xf24022ef, 0xcf18e70a, 0x5cc34424, 0x438544c3, 0x62da4bca)},
+ {SECP256K1_FE_CONST(0xb0e040e2, 0x40cc35da, 0x7dd5c611, 0x7fccb178, 0x28888137, 0xbc930358, 0xea2cbc90, 0x775417dc),
+ SECP256K1_FE_CONST(0xca37f0d4, 0x016dd7c8, 0xab3ae576, 0x96e08d69, 0x68ed9155, 0xa9b44270, 0x900ae35d, 0x7c7800cd)},
+ {SECP256K1_FE_CONST(0x8a32ea49, 0x7fbb0bae, 0x69724a9d, 0x8e2105b2, 0xbdf69178, 0x862577ef, 0x35055590, 0x667ddaef),
+ SECP256K1_FE_CONST(0xd02d7ead, 0xc5e190f0, 0x559c9d72, 0xdaef1ffc, 0x64f9f425, 0xf43645ea, 0x7341e08d, 0x11768e96)},
+ {SECP256K1_FE_CONST(0xa3592d98, 0x9abe289d, 0x579ebea6, 0xbb0857a8, 0xe242ab73, 0x85f9a2ce, 0xb6998f0f, 0xbfffbfc6),
+ SECP256K1_FE_CONST(0x093c1533, 0x32032efa, 0x6aa46070, 0x0039599e, 0x589c35f4, 0xff525430, 0x7fe3777a, 0x44b43ddc)},
+ {SECP256K1_FE_CONST(0x647178a3, 0x229e607b, 0xcc98521a, 0xcce3fdd9, 0x1e1bc9c9, 0x97fb7c6a, 0x61b961e0, 0x99b10709),
+ SECP256K1_FE_CONST(0x98217c13, 0xd51ddf78, 0x96310e77, 0xdaebd908, 0x602ca683, 0xcb46d07a, 0xa1fcf17e, 0xc8e2feb3)},
+ {SECP256K1_FE_CONST(0x7334627c, 0x73f98968, 0x99464b4b, 0xf5964958, 0x1b95870d, 0xc658227e, 0x5e3235d8, 0xdcab5787),
+ SECP256K1_FE_CONST(0x000006fd, 0xc7e9dd94, 0x40ae367a, 0xe51d495c, 0x07603b9b, 0x2d088418, 0x6cc5c74c, 0x98514307)},
+ {SECP256K1_FE_CONST(0x82e83876, 0x96c28938, 0xa50dd1c5, 0x605c3ad1, 0xc048637d, 0x7a50825f, 0x335ed01a, 0x00005760),
+ SECP256K1_FE_CONST(0xb0393f9f, 0x9f2aa55e, 0xf5607e2e, 0x5287d961, 0x60b3e704, 0xf3e16e80, 0xb4f9a3ea, 0xfec7f02d)},
+ {SECP256K1_FE_CONST(0xc97b6cec, 0x3ee6b8dc, 0x98d24b58, 0x3c1970a1, 0xfe06297a, 0xae813529, 0xe76bb6bd, 0x771ae51d),
+ SECP256K1_FE_CONST(0x0507c702, 0xd407d097, 0x47ddeb06, 0xf6625419, 0x79f48f79, 0x7bf80d0b, 0xfc34b364, 0x253a5db1)},
+ {SECP256K1_FE_CONST(0xd559af63, 0x77ea9bc4, 0x3cf1ad14, 0x5c7a4bbb, 0x10e7d18b, 0x7ce0dfac, 0x380bb19d, 0x0bb99bd3),
+ SECP256K1_FE_CONST(0x00196119, 0xb9b00d92, 0x34edfdb5, 0xbbdc42fc, 0xd2daa33a, 0x163356ca, 0xaa8754c8, 0xb0ec8b0b)},
+ {SECP256K1_FE_CONST(0x8ddfa3dc, 0x52918da0, 0x640519dc, 0x0af8512a, 0xca2d33b2, 0xbde52514, 0xda9c0afc, 0xcb29fce4),
+ SECP256K1_FE_CONST(0xb3e4878d, 0x5cb69148, 0xcd54388b, 0xc23acce0, 0x62518ba8, 0xf09def92, 0x7b31e6aa, 0x6ba35b02)},
+ {SECP256K1_FE_CONST(0xf8207492, 0xe3049f0a, 0x65285f2b, 0x0bfff996, 0x00ca112e, 0xc05da837, 0x546d41f9, 0x5194fb91),
+ SECP256K1_FE_CONST(0x7b7ee50b, 0xa8ed4bbd, 0xf6469930, 0x81419a5c, 0x071441c7, 0x290d046e, 0x3b82ea41, 0x611c5f95)},
+ {SECP256K1_FE_CONST(0x050f7c80, 0x5bcd3c6b, 0x823cb724, 0x5ce74db7, 0xa4e39f5c, 0xbd8828d7, 0xfd4d3e07, 0x3ec2926a),
+ SECP256K1_FE_CONST(0x000d6730, 0xb0171314, 0x4764053d, 0xee157117, 0x48fd61da, 0xdea0b9db, 0x1d5e91c6, 0xbdc3f59e)},
+ {SECP256K1_FE_CONST(0x3e3ea8eb, 0x05d760cf, 0x23009263, 0xb3cb3ac9, 0x088f6f0d, 0x3fc182a3, 0xbd57087c, 0xe67c62f9),
+ SECP256K1_FE_CONST(0xbe988716, 0xa29c1bf6, 0x4456aed6, 0xab1e4720, 0x49929305, 0x51043bf4, 0xebd833dd, 0xdd511e8b)},
+ {SECP256K1_FE_CONST(0x6964d2a9, 0xa7fa6501, 0xa5959249, 0x142f4029, 0xea0c1b5f, 0x2f487ef6, 0x301ac80a, 0x768be5cd),
+ SECP256K1_FE_CONST(0x3918ffe4, 0x07492543, 0xed24d0b7, 0x3df95f8f, 0xaffd7cb4, 0x0de2191c, 0x9ec2f2ad, 0x2c0cb3c6)},
+ {SECP256K1_FE_CONST(0x37c93520, 0xf6ddca57, 0x2b42fd5e, 0xb5c7e4de, 0x11b5b81c, 0xb95e91f3, 0x95c4d156, 0x39877ccb),
+ SECP256K1_FE_CONST(0x9a94b9b5, 0x57eb71ee, 0x4c975b8b, 0xac5262a8, 0x077b0595, 0xe12a6b1f, 0xd728edef, 0x1a6bf956)}
+ };
+ /* Fixed test cases for scalar inverses: pairs of (x, 1/x) mod n. */
+ static const secp256k1_scalar scalar_cases[][2] = {
+ /* 0 */
+ {SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0),
+ SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0)},
+ /* 1 */
+ {SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1),
+ SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1)},
+ /* -1 */
+ {SECP256K1_SCALAR_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xbaaedce6, 0xaf48a03b, 0xbfd25e8c, 0xd0364140),
+ SECP256K1_SCALAR_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xbaaedce6, 0xaf48a03b, 0xbfd25e8c, 0xd0364140)},
+ /* 2 */
+ {SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 2),
+ SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x5d576e73, 0x57a4501d, 0xdfe92f46, 0x681b20a1)},
+ /* 2**128 */
+ {SECP256K1_SCALAR_CONST(0, 0, 0, 1, 0, 0, 0, 0),
+ SECP256K1_SCALAR_CONST(0x50a51ac8, 0x34b9ec24, 0x4b0dff66, 0x5588b13e, 0x9984d5b3, 0xcf80ef0f, 0xd6a23766, 0xa3ee9f22)},
+ /* Input known to need 635 divsteps */
+ {SECP256K1_SCALAR_CONST(0xcb9f1d35, 0xdd4416c2, 0xcd71bf3f, 0x6365da66, 0x3c9b3376, 0x8feb7ae9, 0x32a5ef60, 0x19199ec3),
+ SECP256K1_SCALAR_CONST(0x1d7c7bba, 0xf1893d53, 0xb834bd09, 0x36b411dc, 0x42c2e42f, 0xec72c428, 0x5e189791, 0x8e9bc708)},
+ /* Input known to need 566 divsteps starting with delta=1/2. */
+ {SECP256K1_SCALAR_CONST(0x7e3c993d, 0xa4272488, 0xbc015b49, 0x2db54174, 0xd382083a, 0xebe6db35, 0x80f82eff, 0xcd132c72),
+ SECP256K1_SCALAR_CONST(0x086f34a0, 0x3e631f76, 0x77418f28, 0xcc84ac95, 0x6304439d, 0x365db268, 0x312c6ded, 0xd0b934f8)},
+ /* Input known to need 565 divsteps starting with delta=1/2. */
+ {SECP256K1_SCALAR_CONST(0xbad7e587, 0x3f307859, 0x60d93147, 0x8a18491e, 0xb38a9fd5, 0x254350d3, 0x4b1f0e4b, 0x7dd6edc4),
+ SECP256K1_SCALAR_CONST(0x89f2df26, 0x39e2b041, 0xf19bd876, 0xd039c8ac, 0xc2223add, 0x29c4943e, 0x6632d908, 0x515f467b)},
+ /* Selection of randomly generated inputs that reach low/high d/e values in various configurations. */
+ {SECP256K1_SCALAR_CONST(0x1950d757, 0xb37a5809, 0x435059bb, 0x0bb8997e, 0x07e1e3c8, 0x5e5d7d2c, 0x6a0ed8e3, 0xdbde180e),
+ SECP256K1_SCALAR_CONST(0xbf72af9b, 0x750309e2, 0x8dda230b, 0xfe432b93, 0x7e25e475, 0x4388251e, 0x633d894b, 0x3bcb6f8c)},
+ {SECP256K1_SCALAR_CONST(0x9bccf4e7, 0xc5a515e3, 0x50637aa9, 0xbb65a13f, 0x391749a1, 0x62de7d4e, 0xf6d7eabb, 0x3cd10ce0),
+ SECP256K1_SCALAR_CONST(0xaf2d5623, 0xb6385a33, 0xcd0365be, 0x5e92a70d, 0x7f09179c, 0x3baaf30f, 0x8f9cc83b, 0x20092f67)},
+ {SECP256K1_SCALAR_CONST(0x73a57111, 0xb242952a, 0x5c5dee59, 0xf3be2ace, 0xa30a7659, 0xa46e5f47, 0xd21267b1, 0x39e642c9),
+ SECP256K1_SCALAR_CONST(0xa711df07, 0xcbcf13ef, 0xd61cc6be, 0xbcd058ce, 0xb02cf157, 0x272d4a18, 0x86d0feb3, 0xcd5fa004)},
+ {SECP256K1_SCALAR_CONST(0x04884963, 0xce0580b1, 0xba547030, 0x3c691db3, 0x9cd2c84f, 0x24c7cebd, 0x97ebfdba, 0x3e785ec2),
+ SECP256K1_SCALAR_CONST(0xaaaaaf14, 0xd7c99ba7, 0x517ce2c1, 0x78a28b4c, 0x3769a851, 0xe5c5a03d, 0x4cc28f33, 0x0ec4dc5d)},
+ {SECP256K1_SCALAR_CONST(0x1679ed49, 0x21f537b1, 0x815cb8ae, 0x9efc511c, 0x5b9fa037, 0x0b0f275e, 0x6c985281, 0x6c4a9905),
+ SECP256K1_SCALAR_CONST(0xb14ac3d5, 0x62b52999, 0xef34ead1, 0xffca4998, 0x0294341a, 0x1f8172aa, 0xea1624f9, 0x302eea62)},
+ {SECP256K1_SCALAR_CONST(0x626b37c0, 0xf0057c35, 0xee982f83, 0x452a1fd3, 0xea826506, 0x48b08a9d, 0x1d2c4799, 0x4ad5f6ec),
+ SECP256K1_SCALAR_CONST(0xe38643b7, 0x567bfc2f, 0x5d2f1c15, 0xe327239c, 0x07112443, 0x69509283, 0xfd98e77a, 0xdb71c1e8)},
+ {SECP256K1_SCALAR_CONST(0x1850a3a7, 0x759efc56, 0x54f287b2, 0x14d1234b, 0xe263bbc9, 0xcf4d8927, 0xd5f85f27, 0x965bd816),
+ SECP256K1_SCALAR_CONST(0x3b071831, 0xcac9619a, 0xcceb0596, 0xf614d63b, 0x95d0db2f, 0xc6a00901, 0x8eaa2621, 0xabfa0009)},
+ {SECP256K1_SCALAR_CONST(0x94ae5d06, 0xa27dc400, 0x487d72be, 0xaa51ebed, 0xe475b5c0, 0xea675ffc, 0xf4df627a, 0xdca4222f),
+ SECP256K1_SCALAR_CONST(0x01b412ed, 0xd7830956, 0x1532537e, 0xe5e3dc99, 0x8fd3930a, 0x54f8d067, 0x32ef5760, 0x594438a5)},
+ {SECP256K1_SCALAR_CONST(0x1f24278a, 0xb5bfe374, 0xa328dbbc, 0xebe35f48, 0x6620e009, 0xd58bb1b4, 0xb5a6bf84, 0x8815f63a),
+ SECP256K1_SCALAR_CONST(0xfe928416, 0xca5ba2d3, 0xfde513da, 0x903a60c7, 0x9e58ad8a, 0x8783bee4, 0x083a3843, 0xa608c914)},
+ {SECP256K1_SCALAR_CONST(0xdc107d58, 0x274f6330, 0x67dba8bc, 0x26093111, 0x5201dfb8, 0x968ce3f5, 0xf34d1bd4, 0xf2146504),
+ SECP256K1_SCALAR_CONST(0x660cfa90, 0x13c3d93e, 0x7023b1e5, 0xedd09e71, 0x6d9c9d10, 0x7a3d2cdb, 0xdd08edc3, 0xaa78fcfb)},
+ {SECP256K1_SCALAR_CONST(0x7cd1e905, 0xc6f02776, 0x2f551cc7, 0x5da61cff, 0x7da05389, 0x1119d5a4, 0x631c7442, 0x894fd4f7),
+ SECP256K1_SCALAR_CONST(0xff20862a, 0x9d3b1a37, 0x1628803b, 0x3004ccae, 0xaa23282a, 0xa89a1109, 0xd94ece5e, 0x181bdc46)},
+ {SECP256K1_SCALAR_CONST(0x5b9dade8, 0x23d26c58, 0xcd12d818, 0x25b8ae97, 0x3dea04af, 0xf482c96b, 0xa062f254, 0x9e453640),
+ SECP256K1_SCALAR_CONST(0x50c38800, 0x15fa53f4, 0xbe1e5392, 0x5c9b120a, 0x262c22c7, 0x18fa0816, 0x5f2baab4, 0x8cb5db46)},
+ {SECP256K1_SCALAR_CONST(0x11cdaeda, 0x969c464b, 0xef1f4ab0, 0x5b01d22e, 0x656fd098, 0x882bea84, 0x65cdbe7a, 0x0c19ff03),
+ SECP256K1_SCALAR_CONST(0x1968d0fa, 0xac46f103, 0xb55f1f72, 0xb3820bed, 0xec6b359a, 0x4b1ae0ad, 0x7e38e1fb, 0x295ccdfb)},
+ {SECP256K1_SCALAR_CONST(0x2c351aa1, 0x26e91589, 0x194f8a1e, 0x06561f66, 0x0cb97b7f, 0x10914454, 0x134d1c03, 0x157266b4),
+ SECP256K1_SCALAR_CONST(0xbe49ada6, 0x92bd8711, 0x41b176c4, 0xa478ba95, 0x14883434, 0x9d1cd6f3, 0xcc4b847d, 0x22af80f5)},
+ {SECP256K1_SCALAR_CONST(0x6ba07c6e, 0x13a60edb, 0x6247f5c3, 0x84b5fa56, 0x76fe3ec5, 0x80426395, 0xf65ec2ae, 0x623ba730),
+ SECP256K1_SCALAR_CONST(0x25ac23f7, 0x418cd747, 0x98376f9d, 0x4a11c7bf, 0x24c8ebfe, 0x4c8a8655, 0x345f4f52, 0x1c515595)},
+ {SECP256K1_SCALAR_CONST(0x9397a712, 0x8abb6951, 0x2d4a3d54, 0x703b1c2a, 0x0661dca8, 0xd75c9b31, 0xaed4d24b, 0xd2ab2948),
+ SECP256K1_SCALAR_CONST(0xc52e8bef, 0xd55ce3eb, 0x1c897739, 0xeb9fb606, 0x36b9cd57, 0x18c51cc2, 0x6a87489e, 0xffd0dcf3)},
+ {SECP256K1_SCALAR_CONST(0xe6a808cc, 0xeb437888, 0xe97798df, 0x4e224e44, 0x7e3b380a, 0x207c1653, 0x889f3212, 0xc6738b6f),
+ SECP256K1_SCALAR_CONST(0x31f9ae13, 0xd1e08b20, 0x757a2e5e, 0x5243a0eb, 0x8ae35f73, 0x19bb6122, 0xb910f26b, 0xda70aa55)},
+ {SECP256K1_SCALAR_CONST(0xd0320548, 0xab0effe7, 0xa70779e0, 0x61a347a6, 0xb8c1e010, 0x9d5281f8, 0x2ee588a6, 0x80000000),
+ SECP256K1_SCALAR_CONST(0x1541897e, 0x78195c90, 0x7583dd9e, 0x728b6100, 0xbce8bc6d, 0x7a53b471, 0x5dcd9e45, 0x4425fcaf)},
+ {SECP256K1_SCALAR_CONST(0x93d623f1, 0xd45b50b0, 0x796e9186, 0x9eac9407, 0xd30edc20, 0xef6304cf, 0x250494e7, 0xba503de9),
+ SECP256K1_SCALAR_CONST(0x7026d638, 0x1178b548, 0x92043952, 0x3c7fb47c, 0xcd3ea236, 0x31d82b01, 0x612fc387, 0x80b9b957)},
+ {SECP256K1_SCALAR_CONST(0xf860ab39, 0x55f5d412, 0xa4d73bcc, 0x3b48bd90, 0xc248ffd3, 0x13ca10be, 0x8fba84cc, 0xdd28d6a3),
+ SECP256K1_SCALAR_CONST(0x5c32fc70, 0xe0b15d67, 0x76694700, 0xfe62be4d, 0xeacdb229, 0x7a4433d9, 0x52155cd0, 0x7649ab59)},
+ {SECP256K1_SCALAR_CONST(0x4e41311c, 0x0800af58, 0x7a690a8e, 0xe175c9ba, 0x6981ab73, 0xac532ea8, 0x5c1f5e63, 0x6ac1f189),
+ SECP256K1_SCALAR_CONST(0xfffffff9, 0xd075982c, 0x7fbd3825, 0xc05038a2, 0x4533b91f, 0x94ec5f45, 0xb280b28f, 0x842324dc)},
+ {SECP256K1_SCALAR_CONST(0x48e473bf, 0x3555eade, 0xad5d7089, 0x2424c4e4, 0x0a99397c, 0x2dc796d8, 0xb7a43a69, 0xd0364141),
+ SECP256K1_SCALAR_CONST(0x634976b2, 0xa0e47895, 0x1ec38593, 0x266d6fd0, 0x6f602644, 0x9bb762f1, 0x7180c704, 0xe23a4daa)},
+ {SECP256K1_SCALAR_CONST(0xbe83878d, 0x3292fc54, 0x26e71c62, 0x556ccedc, 0x7cbb8810, 0x4032a720, 0x34ead589, 0xe4d6bd13),
+ SECP256K1_SCALAR_CONST(0x6cd150ad, 0x25e59d0f, 0x74cbae3d, 0x6377534a, 0x1e6562e8, 0xb71b9d18, 0xe1e5d712, 0x8480abb3)},
+ {SECP256K1_SCALAR_CONST(0xcdddf2e5, 0xefc15f88, 0xc9ee06de, 0x8a846ca9, 0x28561581, 0x68daa5fb, 0xd1cf3451, 0xeb1782d0),
+ SECP256K1_SCALAR_CONST(0xffffffd9, 0xed8d2af4, 0x993c865a, 0x23e9681a, 0x3ca3a3dc, 0xe6d5a46e, 0xbd86bd87, 0x61b55c70)},
+ {SECP256K1_SCALAR_CONST(0xb6a18f1f, 0x04872df9, 0x08165ec4, 0x319ca19c, 0x6c0359ab, 0x1f7118fb, 0xc2ef8082, 0xca8b7785),
+ SECP256K1_SCALAR_CONST(0xff55b19b, 0x0f1ac78c, 0x0f0c88c2, 0x2358d5ad, 0x5f455e4e, 0x3330b72f, 0x274dc153, 0xffbf272b)},
+ {SECP256K1_SCALAR_CONST(0xea4898e5, 0x30eba3e8, 0xcf0e5c3d, 0x06ec6844, 0x01e26fb6, 0x75636225, 0xc5d08f4c, 0x1decafa0),
+ SECP256K1_SCALAR_CONST(0xe5a014a8, 0xe3c4ec1e, 0xea4f9b32, 0xcfc7b386, 0x00630806, 0x12c08d02, 0x6407ccc2, 0xb067d90e)},
+ {SECP256K1_SCALAR_CONST(0x70e9aea9, 0x7e933af0, 0x8a23bfab, 0x23e4b772, 0xff951863, 0x5ffcf47d, 0x6bebc918, 0x2ca58265),
+ SECP256K1_SCALAR_CONST(0xf4e00006, 0x81bc6441, 0x4eb6ec02, 0xc194a859, 0x80ad7c48, 0xba4e9afb, 0x8b6bdbe0, 0x989d8f77)},
+ {SECP256K1_SCALAR_CONST(0x3c56c774, 0x46efe6f0, 0xe93618b8, 0xf9b5a846, 0xd247df61, 0x83b1e215, 0x06dc8bcc, 0xeefc1bf5),
+ SECP256K1_SCALAR_CONST(0xfff8937a, 0x2cd9586b, 0x43c25e57, 0xd1cefa7a, 0x9fb91ed3, 0x95b6533d, 0x8ad0de5b, 0xafb93f00)},
+ {SECP256K1_SCALAR_CONST(0xfb5c2772, 0x5cb30e83, 0xe38264df, 0xe4e3ebf3, 0x392aa92e, 0xa68756a1, 0x51279ac5, 0xb50711a8),
+ SECP256K1_SCALAR_CONST(0x000013af, 0x1105bfe7, 0xa6bbd7fb, 0x3d638f99, 0x3b266b02, 0x072fb8bc, 0x39251130, 0x2e0fd0ea)}
+ };
+ int i, var, testrand;
+ unsigned char b32[32];
+ secp256k1_fe x_fe;
+ secp256k1_scalar x_scalar;
+ memset(b32, 0, sizeof(b32));
+ /* Test fixed test cases through test_inverse_{scalar,field}, both ways. */
+ for (i = 0; (size_t)i < sizeof(fe_cases)/sizeof(fe_cases[0]); ++i) {
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_field(&x_fe, &fe_cases[i][0], var);
+ check_fe_equal(&x_fe, &fe_cases[i][1]);
+ test_inverse_field(&x_fe, &fe_cases[i][1], var);
+ check_fe_equal(&x_fe, &fe_cases[i][0]);
+ }
+ }
+ for (i = 0; (size_t)i < sizeof(scalar_cases)/sizeof(scalar_cases[0]); ++i) {
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_scalar(&x_scalar, &scalar_cases[i][0], var);
+ CHECK(secp256k1_scalar_eq(&x_scalar, &scalar_cases[i][1]));
+ test_inverse_scalar(&x_scalar, &scalar_cases[i][1], var);
+ CHECK(secp256k1_scalar_eq(&x_scalar, &scalar_cases[i][0]));
+ }
+ }
+ /* Test inputs 0..999 and their respective negations. */
+ for (i = 0; i < 1000; ++i) {
+ b32[31] = i & 0xff;
+ b32[30] = (i >> 8) & 0xff;
+ secp256k1_scalar_set_b32(&x_scalar, b32, NULL);
+ secp256k1_fe_set_b32(&x_fe, b32);
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_scalar(NULL, &x_scalar, var);
+ test_inverse_field(NULL, &x_fe, var);
+ }
+ secp256k1_scalar_negate(&x_scalar, &x_scalar);
+ secp256k1_fe_negate(&x_fe, &x_fe, 1);
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_scalar(NULL, &x_scalar, var);
+ test_inverse_field(NULL, &x_fe, var);
+ }
+ }
+ /* test 128*count random inputs; half with testrand256_test, half with testrand256 */
+ for (testrand = 0; testrand <= 1; ++testrand) {
+ for (i = 0; i < 64 * count; ++i) {
+ (testrand ? secp256k1_testrand256_test : secp256k1_testrand256)(b32);
+ secp256k1_scalar_set_b32(&x_scalar, b32, NULL);
+ secp256k1_fe_set_b32(&x_fe, b32);
+ for (var = 0; var <= 1; ++var) {
+ test_inverse_scalar(NULL, &x_scalar, var);
+ test_inverse_field(NULL, &x_fe, var);
+ }
+ }
+ }
+}
+
/***** GROUP TESTS *****/
void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
@@ -2111,7 +2945,6 @@ void test_ge(void) {
*/
secp256k1_ge *ge = (secp256k1_ge *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_ge) * (1 + 4 * runs));
secp256k1_gej *gej = (secp256k1_gej *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_gej) * (1 + 4 * runs));
- secp256k1_fe *zinv = (secp256k1_fe *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_fe) * (1 + 4 * runs));
secp256k1_fe zf;
secp256k1_fe zfi2, zfi3;
@@ -2145,23 +2978,6 @@ void test_ge(void) {
}
}
- /* Compute z inverses. */
- {
- secp256k1_fe *zs = checked_malloc(&ctx->error_callback, sizeof(secp256k1_fe) * (1 + 4 * runs));
- for (i = 0; i < 4 * runs + 1; i++) {
- if (i == 0) {
- /* The point at infinity does not have a meaningful z inverse. Any should do. */
- do {
- random_field_element_test(&zs[i]);
- } while(secp256k1_fe_is_zero(&zs[i]));
- } else {
- zs[i] = gej[i].z;
- }
- }
- secp256k1_fe_inv_all_var(zinv, zs, 4 * runs + 1);
- free(zs);
- }
-
/* Generate random zf, and zfi2 = 1/zf^2, zfi3 = 1/zf^3 */
do {
random_field_element_test(&zf);
@@ -2270,16 +3086,9 @@ void test_ge(void) {
free(gej_shuffled);
}
- /* Test batch gej -> ge conversion with and without known z ratios. */
+ /* Test batch gej -> ge conversion without known z ratios. */
{
- secp256k1_fe *zr = (secp256k1_fe *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_fe));
secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge));
- for (i = 0; i < 4 * runs + 1; i++) {
- /* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */
- if (i < 4 * runs) {
- secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z);
- }
- }
secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1);
for (i = 0; i < 4 * runs + 1; i++) {
secp256k1_fe s;
@@ -2288,7 +3097,6 @@ void test_ge(void) {
ge_equals_gej(&ge_set_all[i], &gej[i]);
}
free(ge_set_all);
- free(zr);
}
/* Test batch gej -> ge conversion with many infinities. */
@@ -2309,7 +3117,6 @@ void test_ge(void) {
free(ge);
free(gej);
- free(zinv);
}
@@ -2456,64 +3263,35 @@ void run_ec_combine(void) {
void test_group_decompress(const secp256k1_fe* x) {
/* The input itself, normalized. */
secp256k1_fe fex = *x;
- secp256k1_fe fez;
- /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */
- secp256k1_ge ge_quad, ge_even, ge_odd;
- secp256k1_gej gej_quad;
+ /* Results of set_xo_var(..., 0), set_xo_var(..., 1). */
+ secp256k1_ge ge_even, ge_odd;
/* Return values of the above calls. */
- int res_quad, res_even, res_odd;
+ int res_even, res_odd;
secp256k1_fe_normalize_var(&fex);
- res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex);
res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0);
res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1);
- CHECK(res_quad == res_even);
- CHECK(res_quad == res_odd);
+ CHECK(res_even == res_odd);
- if (res_quad) {
- secp256k1_fe_normalize_var(&ge_quad.x);
+ if (res_even) {
secp256k1_fe_normalize_var(&ge_odd.x);
secp256k1_fe_normalize_var(&ge_even.x);
- secp256k1_fe_normalize_var(&ge_quad.y);
secp256k1_fe_normalize_var(&ge_odd.y);
secp256k1_fe_normalize_var(&ge_even.y);
/* No infinity allowed. */
- CHECK(!ge_quad.infinity);
CHECK(!ge_even.infinity);
CHECK(!ge_odd.infinity);
/* Check that the x coordinates check out. */
- CHECK(secp256k1_fe_equal_var(&ge_quad.x, x));
CHECK(secp256k1_fe_equal_var(&ge_even.x, x));
CHECK(secp256k1_fe_equal_var(&ge_odd.x, x));
- /* Check that the Y coordinate result in ge_quad is a square. */
- CHECK(secp256k1_fe_is_quad_var(&ge_quad.y));
-
/* Check odd/even Y in ge_odd, ge_even. */
CHECK(secp256k1_fe_is_odd(&ge_odd.y));
CHECK(!secp256k1_fe_is_odd(&ge_even.y));
-
- /* Check secp256k1_gej_has_quad_y_var. */
- secp256k1_gej_set_ge(&gej_quad, &ge_quad);
- CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
- do {
- random_fe_test(&fez);
- } while (secp256k1_fe_is_zero(&fez));
- secp256k1_gej_rescale(&gej_quad, &fez);
- CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
- secp256k1_gej_neg(&gej_quad, &gej_quad);
- CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad));
- do {
- random_fe_test(&fez);
- } while (secp256k1_fe_is_zero(&fez));
- secp256k1_gej_rescale(&gej_quad, &fez);
- CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad));
- secp256k1_gej_neg(&gej_quad, &gej_quad);
- CHECK(secp256k1_gej_has_quad_y_var(&gej_quad));
}
}
@@ -4373,8 +5151,10 @@ void test_ecdsa_sign_verify(void) {
secp256k1_scalar one;
secp256k1_scalar msg, key;
secp256k1_scalar sigr, sigs;
- int recid;
int getrec;
+ /* Initialize recid to suppress a false positive -Wconditional-uninitialized in clang.
+ VG_UNDEF ensures that valgrind will still treat the variable as uninitialized. */
+ int recid = -1; VG_UNDEF(&recid, sizeof(recid));
random_scalar_order_test(&msg);
random_scalar_order_test(&key);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key);
@@ -5444,18 +6224,18 @@ void run_ecdsa_openssl(void) {
# include "modules/schnorrsig/tests_impl.h"
#endif
-void run_memczero_test(void) {
+void run_secp256k1_memczero_test(void) {
unsigned char buf1[6] = {1, 2, 3, 4, 5, 6};
unsigned char buf2[sizeof(buf1)];
- /* memczero(..., ..., 0) is a noop. */
+ /* secp256k1_memczero(..., ..., 0) is a noop. */
memcpy(buf2, buf1, sizeof(buf1));
- memczero(buf1, sizeof(buf1), 0);
+ secp256k1_memczero(buf1, sizeof(buf1), 0);
CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0);
- /* memczero(..., ..., 1) zeros the buffer. */
+ /* secp256k1_memczero(..., ..., 1) zeros the buffer. */
memset(buf2, 0, sizeof(buf2));
- memczero(buf1, sizeof(buf1) , 1);
+ secp256k1_memczero(buf1, sizeof(buf1) , 1);
CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0);
}
@@ -5626,6 +6406,15 @@ int main(int argc, char **argv) {
/* find iteration count */
if (argc > 1) {
count = strtol(argv[1], NULL, 0);
+ } else {
+ const char* env = getenv("SECP256K1_TEST_ITERS");
+ if (env) {
+ count = strtol(env, NULL, 0);
+ }
+ }
+ if (count <= 0) {
+ fputs("An iteration count of 0 or less is not allowed.\n", stderr);
+ return EXIT_FAILURE;
}
printf("test count = %i\n", count);
@@ -5646,22 +6435,18 @@ int main(int argc, char **argv) {
run_rand_bits();
run_rand_int();
+ run_ctz_tests();
+ run_modinv_tests();
+ run_inverse_tests();
+
run_sha256_tests();
run_hmac_sha256_tests();
run_rfc6979_hmac_sha256_tests();
-#ifndef USE_NUM_NONE
- /* num tests */
- run_num_smalltests();
-#endif
-
/* scalar tests */
run_scalar_tests();
/* field tests */
- run_field_inv();
- run_field_inv_var();
- run_field_inv_all_var();
run_field_misc();
run_field_convert();
run_sqr();
@@ -5723,7 +6508,7 @@ int main(int argc, char **argv) {
#endif
/* util tests */
- run_memczero_test();
+ run_secp256k1_memczero_test();
run_cmov_tests();
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index f4d5b8e176..2bb5381446 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -1,8 +1,8 @@
/***********************************************************************
- * Copyright (c) 2016 Andrew Poelstra *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+ * Copyright (c) 2016 Andrew Poelstra *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index 3a88a41bc6..f78846836c 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -1,8 +1,8 @@
-/**********************************************************************
- * Copyright (c) 2013, 2014 Pieter Wuille *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2013, 2014 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#ifndef SECP256K1_UTIL_H
#define SECP256K1_UTIL_H
@@ -113,7 +113,7 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void
#define ALIGNMENT 16
#endif
-#define ROUND_TO_ALIGN(size) (((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT)
+#define ROUND_TO_ALIGN(size) ((((size) + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT)
/* Assume there is a contiguous memory object with bounds [base, base + max_size)
* of which the memory range [base, *prealloc_ptr) is already allocated for usage,
@@ -141,7 +141,7 @@ static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_siz
VERIFY_CHECK(((unsigned char*)*prealloc_ptr - (unsigned char*)base) % ALIGNMENT == 0);
VERIFY_CHECK((unsigned char*)*prealloc_ptr - (unsigned char*)base + aligned_alloc_size <= max_size);
ret = *prealloc_ptr;
- *((unsigned char**)prealloc_ptr) += aligned_alloc_size;
+ *prealloc_ptr = (unsigned char*)*prealloc_ptr + aligned_alloc_size;
return ret;
}
@@ -202,7 +202,7 @@ static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_siz
#endif
/* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */
-static SECP256K1_INLINE void memczero(void *s, size_t len, int flag) {
+static SECP256K1_INLINE void secp256k1_memczero(void *s, size_t len, int flag) {
unsigned char *p = (unsigned char *)s;
/* Access flag with a volatile-qualified lvalue.
This prevents clang from figuring out (after inlining) that flag can
@@ -260,14 +260,85 @@ static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag)
# define SECP256K1_WIDEMUL_INT128 1
#elif defined(USE_FORCE_WIDEMUL_INT64)
# define SECP256K1_WIDEMUL_INT64 1
-#elif defined(__SIZEOF_INT128__)
+#elif defined(UINT128_MAX) || defined(__SIZEOF_INT128__)
# define SECP256K1_WIDEMUL_INT128 1
#else
# define SECP256K1_WIDEMUL_INT64 1
#endif
#if defined(SECP256K1_WIDEMUL_INT128)
+# if !defined(UINT128_MAX) && defined(__SIZEOF_INT128__)
SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
SECP256K1_GNUC_EXT typedef __int128 int128_t;
+#define UINT128_MAX ((uint128_t)(-1))
+#define INT128_MAX ((int128_t)(UINT128_MAX >> 1))
+#define INT128_MIN (-INT128_MAX - 1)
+/* No (U)INT128_C macros because compilers providing __int128 do not support 128-bit literals. */
+# endif
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+/* Determine the number of trailing zero bits in a (non-zero) 32-bit x.
+ * This function is only intended to be used as fallback for
+ * secp256k1_ctz32_var, but permits it to be tested separately. */
+static SECP256K1_INLINE int secp256k1_ctz32_var_debruijn(uint32_t x) {
+ static const uint8_t debruijn[32] = {
+ 0x00, 0x01, 0x02, 0x18, 0x03, 0x13, 0x06, 0x19, 0x16, 0x04, 0x14, 0x0A,
+ 0x10, 0x07, 0x0C, 0x1A, 0x1F, 0x17, 0x12, 0x05, 0x15, 0x09, 0x0F, 0x0B,
+ 0x1E, 0x11, 0x08, 0x0E, 0x1D, 0x0D, 0x1C, 0x1B
+ };
+ return debruijn[((x & -x) * 0x04D7651F) >> 27];
+}
+
+/* Determine the number of trailing zero bits in a (non-zero) 64-bit x.
+ * This function is only intended to be used as fallback for
+ * secp256k1_ctz64_var, but permits it to be tested separately. */
+static SECP256K1_INLINE int secp256k1_ctz64_var_debruijn(uint64_t x) {
+ static const uint8_t debruijn[64] = {
+ 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
+ 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
+ 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
+ 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12
+ };
+ return debruijn[((x & -x) * 0x022FDD63CC95386D) >> 58];
+}
+
+/* Determine the number of trailing zero bits in a (non-zero) 32-bit x. */
+static SECP256K1_INLINE int secp256k1_ctz32_var(uint32_t x) {
+ VERIFY_CHECK(x != 0);
+#if (__has_builtin(__builtin_ctz) || SECP256K1_GNUC_PREREQ(3,4))
+ /* If the unsigned type is sufficient to represent the largest uint32_t, consider __builtin_ctz. */
+ if (((unsigned)UINT32_MAX) == UINT32_MAX) {
+ return __builtin_ctz(x);
+ }
#endif
+#if (__has_builtin(__builtin_ctzl) || SECP256K1_GNUC_PREREQ(3,4))
+ /* Otherwise consider __builtin_ctzl (the unsigned long type is always at least 32 bits). */
+ return __builtin_ctzl(x);
+#else
+ /* If no suitable CTZ builtin is available, use a (variable time) software emulation. */
+ return secp256k1_ctz32_var_debruijn(x);
+#endif
+}
+
+/* Determine the number of trailing zero bits in a (non-zero) 64-bit x. */
+static SECP256K1_INLINE int secp256k1_ctz64_var(uint64_t x) {
+ VERIFY_CHECK(x != 0);
+#if (__has_builtin(__builtin_ctzl) || SECP256K1_GNUC_PREREQ(3,4))
+ /* If the unsigned long type is sufficient to represent the largest uint64_t, consider __builtin_ctzl. */
+ if (((unsigned long)UINT64_MAX) == UINT64_MAX) {
+ return __builtin_ctzl(x);
+ }
+#endif
+#if (__has_builtin(__builtin_ctzll) || SECP256K1_GNUC_PREREQ(3,4))
+ /* Otherwise consider __builtin_ctzll (the unsigned long long type is always at least 64 bits). */
+ return __builtin_ctzll(x);
+#else
+ /* If no suitable CTZ builtin is available, use a (variable time) software emulation. */
+ return secp256k1_ctz64_var_debruijn(x);
+#endif
+}
#endif /* SECP256K1_UTIL_H */
diff --git a/src/secp256k1/src/valgrind_ctime_test.c b/src/secp256k1/src/valgrind_ctime_test.c
index 3169e3651c..cfca5a196e 100644
--- a/src/secp256k1/src/valgrind_ctime_test.c
+++ b/src/secp256k1/src/valgrind_ctime_test.c
@@ -1,10 +1,12 @@
-/**********************************************************************
- * Copyright (c) 2020 Gregory Maxwell *
- * Distributed under the MIT software license, see the accompanying *
- * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
- **********************************************************************/
+/***********************************************************************
+ * Copyright (c) 2020 Gregory Maxwell *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
#include <valgrind/memcheck.h>
+#include <stdio.h>
+
#include "include/secp256k1.h"
#include "assumptions.h"
#include "util.h"
@@ -25,8 +27,42 @@
#include "include/secp256k1_schnorrsig.h"
#endif
+void run_tests(secp256k1_context *ctx, unsigned char *key);
+
int main(void) {
secp256k1_context* ctx;
+ unsigned char key[32];
+ int ret, i;
+
+ if (!RUNNING_ON_VALGRIND) {
+ fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
+ fprintf(stderr, "Usage: libtool --mode=execute valgrind ./valgrind_ctime_test\n");
+ return 1;
+ }
+ ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN
+ | SECP256K1_CONTEXT_VERIFY
+ | SECP256K1_CONTEXT_DECLASSIFY);
+ /** In theory, testing with a single secret input should be sufficient:
+ * If control flow depended on secrets the tool would generate an error.
+ */
+ for (i = 0; i < 32; i++) {
+ key[i] = i + 65;
+ }
+
+ run_tests(ctx, key);
+
+ /* Test context randomisation. Do this last because it leaves the context
+ * tainted. */
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_context_randomize(ctx, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret);
+
+ secp256k1_context_destroy(ctx);
+ return 0;
+}
+
+void run_tests(secp256k1_context *ctx, unsigned char *key) {
secp256k1_ecdsa_signature signature;
secp256k1_pubkey pubkey;
size_t siglen = 74;
@@ -34,7 +70,6 @@ int main(void) {
int i;
int ret;
unsigned char msg[32];
- unsigned char key[32];
unsigned char sig[74];
unsigned char spubkey[33];
#ifdef ENABLE_MODULE_RECOVERY
@@ -45,26 +80,10 @@ int main(void) {
secp256k1_keypair keypair;
#endif
- if (!RUNNING_ON_VALGRIND) {
- fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
- fprintf(stderr, "Usage: libtool --mode=execute valgrind ./valgrind_ctime_test\n");
- exit(1);
- }
-
- /** In theory, testing with a single secret input should be sufficient:
- * If control flow depended on secrets the tool would generate an error.
- */
- for (i = 0; i < 32; i++) {
- key[i] = i + 65;
- }
for (i = 0; i < 32; i++) {
msg[i] = i + 1;
}
- ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN
- | SECP256K1_CONTEXT_VERIFY
- | SECP256K1_CONTEXT_DECLASSIFY);
-
/* Test keygen. */
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key);
@@ -122,12 +141,6 @@ int main(void) {
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret == 1);
- /* Test context randomisation. Do this last because it leaves the context tainted. */
- VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
- ret = secp256k1_context_randomize(ctx, key);
- VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
- CHECK(ret);
-
/* Test keypair_create and keypair_xonly_tweak_add. */
#ifdef ENABLE_MODULE_EXTRAKEYS
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
@@ -140,6 +153,12 @@ int main(void) {
ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg);
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret == 1);
+
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ VALGRIND_MAKE_MEM_UNDEFINED(&keypair, sizeof(keypair));
+ ret = secp256k1_keypair_sec(ctx, key, &keypair);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
@@ -151,7 +170,4 @@ int main(void) {
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret == 1);
#endif
-
- secp256k1_context_destroy(ctx);
- return 0;
}
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 49b40924e0..eb5c37b34d 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -74,9 +74,9 @@ public:
// Simulates connection failure so that we can test eviction of offline nodes
void SimConnFail(const CService& addr)
{
- LOCK(cs);
int64_t nLastSuccess = 1;
- Good_(addr, true, nLastSuccess); // Set last good connection in the deep past.
+ // Set last good connection in the deep past.
+ Good(addr, true, nLastSuccess);
bool count_failure = false;
int64_t nLastTry = GetAdjustedTime()-61;
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index 2f532ef598..2eb653e9ec 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -62,7 +62,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
- std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(::ChainstateActive(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
CBlock& block = pblocktemplate->block;
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;
@@ -117,9 +117,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
std::vector<BlockFilter> filters;
std::vector<uint256> filter_hashes;
- for (const CBlockIndex* block_index = ::ChainActive().Genesis();
+ for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
block_index != nullptr;
- block_index = ::ChainActive().Next(block_index)) {
+ block_index = m_node.chainman->ActiveChain().Next(block_index)) {
BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
@@ -131,7 +131,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
// BlockUntilSyncedToCurrentChain should return false before index is started.
BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(filter_index.Start(::ChainstateActive()));
+ BOOST_REQUIRE(filter_index.Start(m_node.chainman->ActiveChainstate()));
// Allow filter index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
@@ -145,9 +145,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
const CBlockIndex* block_index;
- for (block_index = ::ChainActive().Genesis();
+ for (block_index = m_node.chainman->ActiveChain().Genesis();
block_index != nullptr;
- block_index = ::ChainActive().Next(block_index)) {
+ block_index = m_node.chainman->ActiveChain().Next(block_index)) {
CheckFilterLookups(filter_index, block_index, last_header);
}
}
@@ -156,7 +156,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* tip;
{
LOCK(cs_main);
- tip = ::ChainActive().Tip();
+ tip = m_node.chainman->ActiveChain().Tip();
}
CKey coinbase_key_A, coinbase_key_B;
coinbase_key_A.MakeNewKey(true);
@@ -178,7 +178,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -196,7 +196,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -210,7 +210,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
@@ -231,14 +231,14 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(chainA[i]->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainA_last_header);
{
LOCK(cs_main);
- block_index = g_chainman.m_blockman.LookupBlockIndex(chainB[i]->GetHash());
+ block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainB_last_header);
@@ -250,7 +250,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
{
LOCK(cs_main);
- tip = ::ChainActive().Tip();
+ tip = m_node.chainman->ActiveChain().Tip();
}
BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
diff --git a/src/test/coinstatsindex_tests.cpp b/src/test/coinstatsindex_tests.cpp
index 106fcd2a33..597d7a7340 100644
--- a/src/test/coinstatsindex_tests.cpp
+++ b/src/test/coinstatsindex_tests.cpp
@@ -22,7 +22,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* block_index;
{
LOCK(cs_main);
- block_index = ChainActive().Tip();
+ block_index = m_node.chainman->ActiveChain().Tip();
}
// CoinStatsIndex should not be found before it is started.
@@ -32,7 +32,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
// is started.
BOOST_CHECK(!coin_stats_index.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(coin_stats_index.Start(::ChainstateActive()));
+ BOOST_REQUIRE(coin_stats_index.Start(m_node.chainman->ActiveChainstate()));
// Allow the CoinStatsIndex to catch up with the block index that is syncing
// in a background thread.
@@ -46,7 +46,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* genesis_block_index;
{
LOCK(cs_main);
- genesis_block_index = ChainActive().Genesis();
+ genesis_block_index = m_node.chainman->ActiveChain().Genesis();
}
BOOST_CHECK(coin_stats_index.LookUpStats(genesis_block_index, coin_stats));
@@ -64,7 +64,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
const CBlockIndex* new_block_index;
{
LOCK(cs_main);
- new_block_index = ChainActive().Tip();
+ new_block_index = m_node.chainman->ActiveChain().Tip();
}
coin_stats_index.LookUpStats(new_block_index, new_coin_stats);
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index a56ce51acb..57178d015d 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -14,36 +14,19 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <serialize.h>
+#include <test/util/net.h>
+#include <test/util/setup_common.h>
#include <txorphanage.h>
#include <util/string.h>
#include <util/system.h>
#include <util/time.h>
#include <validation.h>
-#include <test/util/setup_common.h>
-
#include <array>
#include <stdint.h>
#include <boost/test/unit_test.hpp>
-struct CConnmanTest : public CConnman {
- using CConnman::CConnman;
- void AddNode(CNode& node)
- {
- LOCK(cs_vNodes);
- vNodes.push_back(&node);
- }
- void ClearNodes()
- {
- LOCK(cs_vNodes);
- for (CNode* node : vNodes) {
- delete node;
- }
- vNodes.clear();
- }
-};
-
static CService ip(uint32_t i)
{
struct in_addr s;
@@ -83,8 +66,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// This test requires that we have a chain with non-zero work.
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip() != nullptr);
- BOOST_CHECK(::ChainActive().Tip()->nChainWork > 0);
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip() != nullptr);
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->nChainWork > 0);
}
// Test starts here
@@ -120,7 +103,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
peerLogic->FinalizeNode(dummyNode1);
}
-static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
+static void AddRandomOutboundPeer(std::vector<CNode*>& vNodes, PeerManager& peerLogic, ConnmanTestMsg& connman)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false));
@@ -130,13 +113,13 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &pee
peerLogic.InitializeNode(&node);
node.fSuccessfullyConnected = true;
- connman->AddNode(node);
+ connman.AddTestNode(node);
}
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
const CChainParams& chainparams = Params();
- auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman);
+ auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
@@ -150,8 +133,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
std::vector<CNode *> vNodes;
// Mock some outbound peers
- for (int i=0; i<max_outbound_full_relay; ++i) {
- AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
+ for (int i = 0; i < max_outbound_full_relay; ++i) {
+ AddRandomOutboundPeer(vNodes, *peerLogic, *connman);
}
peerLogic->CheckForStaleTipAndEvictPeers();
@@ -176,7 +159,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// If we add one more peer, something should get marked for eviction
// on the next check (since we're mocking the time to be in the future, the
// required time connected check should be satisfied).
- AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
+ AddRandomOutboundPeer(vNodes, *peerLogic, *connman);
peerLogic->CheckForStaleTipAndEvictPeers();
for (int i = 0; i < max_outbound_full_relay; ++i) {
@@ -202,14 +185,14 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
peerLogic->FinalizeNode(*node);
}
- connman->ClearNodes();
+ connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
const CChainParams& chainparams = Params();
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- auto connman = std::make_unique<CConnmanTest>(0x1337, 0x1337, *m_node.addrman);
+ auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
@@ -233,7 +216,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[0]);
nodes[0]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[0]);
+ connman->AddTestNode(*nodes[0]);
peerLogic->Misbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
LOCK(nodes[0]->cs_sendProcessing);
@@ -249,7 +232,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[1]);
nodes[1]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[1]);
+ connman->AddTestNode(*nodes[1]);
peerLogic->Misbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
{
LOCK(nodes[1]->cs_sendProcessing);
@@ -280,7 +263,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[2]);
nodes[2]->fSuccessfullyConnected = true;
- connman->AddNode(*nodes[2]);
+ connman->AddTestNode(*nodes[2]);
peerLogic->Misbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
{
LOCK(nodes[2]->cs_sendProcessing);
@@ -296,7 +279,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
for (CNode* node : nodes) {
peerLogic->FinalizeNode(*node);
}
- connman->ClearNodes();
+ connman->ClearTestNodes();
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 98ae32a8d0..db0b461873 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -57,15 +57,6 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
(void)addr_man.SelectTriedCollision();
},
[&] {
- (void)addr_man.Select(fuzzed_data_provider.ConsumeBool());
- },
- [&] {
- (void)addr_man.GetAddr(
- /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
- /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
- /* network */ std::nullopt);
- },
- [&] {
const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
if (opt_address && opt_net_addr) {
@@ -109,12 +100,15 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
if (opt_service) {
addr_man.SetServices(*opt_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS));
}
- },
- [&] {
- (void)addr_man.Check();
});
}
- (void)addr_man.size();
+ const CAddrMan& const_addr_man{addr_man};
+ (void)/*const_*/addr_man.GetAddr(
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* network */ std::nullopt);
+ (void)/*const_*/addr_man.Select(fuzzed_data_provider.ConsumeBool());
+ (void)const_addr_man.size();
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
- data_stream << addr_man;
+ data_stream << const_addr_man;
}
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index 878b5a27da..42f19d16c6 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -27,6 +27,7 @@
#include <vector>
namespace {
+const TestingSetup* g_setup;
const Coin EMPTY_COIN{};
bool operator==(const Coin& a, const Coin& b)
@@ -39,6 +40,7 @@ bool operator==(const Coin& a, const Coin& b)
void initialize_coins_view()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
@@ -236,8 +238,9 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
// It is not allowed to call CheckTxInputs if CheckTransaction failed
return;
}
- (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out);
- assert(MoneyRange(tx_fee_out));
+ if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out)) {
+ assert(MoneyRange(tx_fee_out));
+ }
},
[&] {
const CTransaction transaction{random_mutable_transaction};
@@ -267,7 +270,7 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
bool expected_code_path = false;
try {
- (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats);
+ (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_setup->m_node.chainman->m_blockman)), stats);
} catch (const std::logic_error&) {
expected_code_path = true;
}
diff --git a/src/test/fuzz/float.cpp b/src/test/fuzz/float.cpp
index adef66a3ee..2f77c8949e 100644
--- a/src/test/fuzz/float.cpp
+++ b/src/test/fuzz/float.cpp
@@ -5,6 +5,7 @@
#include <memusage.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
#include <util/serfloat.h>
#include <version.h>
@@ -17,7 +18,33 @@ FUZZ_TARGET(float)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
{
- const double d = fuzzed_data_provider.ConsumeFloatingPoint<double>();
+ const double d{[&] {
+ double tmp;
+ CallOneOf(
+ fuzzed_data_provider,
+ // an actual number
+ [&] { tmp = fuzzed_data_provider.ConsumeFloatingPoint<double>(); },
+ // special numbers and NANs
+ [&] { tmp = fuzzed_data_provider.PickValueInArray({
+ std::numeric_limits<double>::infinity(),
+ -std::numeric_limits<double>::infinity(),
+ std::numeric_limits<double>::min(),
+ -std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max(),
+ -std::numeric_limits<double>::max(),
+ std::numeric_limits<double>::lowest(),
+ -std::numeric_limits<double>::lowest(),
+ std::numeric_limits<double>::quiet_NaN(),
+ -std::numeric_limits<double>::quiet_NaN(),
+ std::numeric_limits<double>::signaling_NaN(),
+ -std::numeric_limits<double>::signaling_NaN(),
+ std::numeric_limits<double>::denorm_min(),
+ -std::numeric_limits<double>::denorm_min(),
+ }); },
+ // Anything from raw memory (also checks that DecodeDouble doesn't crash on any input)
+ [&] { tmp = DecodeDouble(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); });
+ return tmp;
+ }()};
(void)memusage::DynamicUsage(d);
uint64_t encoded = EncodeDouble(d);
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 631c861bb6..a33297e0ed 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -13,6 +13,7 @@
#include <cstdint>
#include <exception>
#include <memory>
+#include <string>
#include <unistd.h>
#include <vector>
@@ -37,6 +38,14 @@ void initialize()
// Terminate immediately if a fuzzing harness ever tries to create a TCP socket.
CreateSock = [](const CService&) -> std::unique_ptr<Sock> { std::terminate(); };
+ // Terminate immediately if a fuzzing harness ever tries to perform a DNS lookup.
+ g_dns_lookup = [](const std::string& name, bool allow_lookup) {
+ if (allow_lookup) {
+ std::terminate();
+ }
+ return WrappedGetAddrInfo(name, false);
+ };
+
bool should_abort{false};
if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) {
for (const auto& t : FuzzTargets()) {
diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp
index dbd0c76d42..a7770c90e8 100644
--- a/src/test/fuzz/load_external_block_file.cpp
+++ b/src/test/fuzz/load_external_block_file.cpp
@@ -13,9 +13,14 @@
#include <cstdint>
#include <vector>
+namespace {
+const TestingSetup* g_setup;
+} // namespace
+
void initialize_load_external_block_file()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
@@ -27,5 +32,5 @@ FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file)
return;
}
FlatFilePos flat_file_pos;
- ::ChainstateActive().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
+ g_setup->m_node.chainman->ActiveChainstate().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr);
}
diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp
index 70ffc6bf37..a3f71426fa 100644
--- a/src/test/fuzz/node_eviction.cpp
+++ b/src/test/fuzz/node_eviction.cpp
@@ -31,7 +31,7 @@ FUZZ_TARGET(node_eviction)
/* nKeyedNetGroup */ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
/* prefer_evict */ fuzzed_data_provider.ConsumeBool(),
/* m_is_local */ fuzzed_data_provider.ConsumeBool(),
- /* m_is_onion */ fuzzed_data_provider.ConsumeBool(),
+ /* m_network */ fuzzed_data_provider.PickValueInArray(ALL_NETWORKS),
});
}
// Make a copy since eviction_candidates may be in some valid but otherwise
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 36b1d5035c..023dcdb3e5 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -44,7 +44,7 @@ void CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables)
const size_t call_index{fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, call_size - 1)};
size_t i{0};
- return ((i++ == call_index ? callables() : void()), ...);
+ ((i++ == call_index ? callables() : void()), ...);
}
template <typename Collection>
diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp
index e1a21b6c53..c2aaf486c5 100644
--- a/src/test/fuzz/validation_load_mempool.cpp
+++ b/src/test/fuzz/validation_load_mempool.cpp
@@ -14,9 +14,14 @@
#include <cstdint>
#include <vector>
+namespace {
+const TestingSetup* g_setup;
+} // namespace
+
void initialize_validation_load_mempool()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ g_setup = testing_setup.get();
}
FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
@@ -29,6 +34,6 @@ FUZZ_TARGET_INIT(validation_load_mempool, initialize_validation_load_mempool)
auto fuzzed_fopen = [&](const fs::path&, const char*) {
return fuzzed_file_provider.open();
};
- (void)LoadMempool(pool, ::ChainstateActive(), fuzzed_fopen);
+ (void)LoadMempool(pool, g_setup->m_node.chainman->ActiveChainstate(), fuzzed_fopen);
(void)DumpMempool(pool, fuzzed_fopen, true);
}
diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp
index 73463b071e..42a7c7798c 100644
--- a/src/test/interfaces_tests.cpp
+++ b/src/test/interfaces_tests.cpp
@@ -98,7 +98,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor)
auto* orig_tip = active.Tip();
for (int i = 0; i < 10; ++i) {
BlockValidationState state;
- ChainstateActive().InvalidateBlock(state, Params(), active.Tip());
+ m_node.chainman->ActiveChainstate().InvalidateBlock(state, Params(), active.Tip());
}
BOOST_CHECK_EQUAL(active.Height(), orig_tip->nHeight - 10);
coinbaseKey.MakeNewKey(true);
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index c47d0eae1e..e20c5e4e8f 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -28,8 +28,8 @@ struct MinerTestingSetup : public TestingSetup {
void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
{
- CCoinsViewMemPool viewMempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
- return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), viewMempool, tx, flags);
+ CCoinsViewMemPool view_mempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
+ return CheckSequenceLocks(m_node.chainman->ActiveChain().Tip(), view_mempool, tx, flags);
}
BlockAssembler AssemblerForTest(const CChainParams& params);
};
@@ -45,7 +45,7 @@ BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
options.blockMinFeeRate = blockMinFeeRate;
- return BlockAssembler(::ChainstateActive(), *m_node.mempool, params, options);
+ return BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, params, options);
}
constexpr static struct {
@@ -82,11 +82,11 @@ constexpr static struct {
{2, 0xbbbeb305}, {2, 0xfe1c810a},
};
-static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static CBlockIndex CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
CBlockIndex index;
index.nHeight = nHeight;
- index.pprev = ::ChainActive().Tip();
+ index.pprev = active_chain_tip;
return index;
}
@@ -228,17 +228,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
LOCK(cs_main);
pblock->nVersion = 1;
- pblock->nTime = ::ChainActive().Tip()->GetMedianTimePast()+1;
+ pblock->nTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(bi.extranonce);
- txCoinbase.vin[0].scriptSig.push_back(::ChainActive().Height());
+ txCoinbase.vin[0].scriptSig.push_back(m_node.chainman->ActiveChain().Height());
txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
txCoinbase.vout[0].scriptPubKey = CScript();
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
if (txFirst.size() == 0)
- baseheight = ::ChainActive().Height();
+ baseheight = m_node.chainman->ActiveChain().Height();
if (txFirst.size() < 4)
txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -364,29 +364,29 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->clear();
// subsidy changing
- int nHeight = ::ChainActive().Height();
+ int nHeight = m_node.chainman->ActiveChain().Height();
// Create an actual 209999-long block chain (without valid blocks).
- while (::ChainActive().Tip()->nHeight < 209999) {
- CBlockIndex* prev = ::ChainActive().Tip();
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 209999) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- ::ChainActive().SetTip(next);
+ m_node.chainman->ActiveChain().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
// Extend to a 210000-long block chain.
- while (::ChainActive().Tip()->nHeight < 210000) {
- CBlockIndex* prev = ::ChainActive().Tip();
+ while (m_node.chainman->ActiveChain().Tip()->nHeight < 210000) {
+ CBlockIndex* prev = m_node.chainman->ActiveChain().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
- ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
- ::ChainActive().SetTip(next);
+ m_node.chainman->ActiveChain().SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
@@ -409,16 +409,16 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
m_node.mempool->clear();
// Delete the dummy blocks again.
- while (::ChainActive().Tip()->nHeight > nHeight) {
- CBlockIndex* del = ::ChainActive().Tip();
- ::ChainActive().SetTip(del->pprev);
- ::ChainstateActive().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
+ while (m_node.chainman->ActiveChain().Tip()->nHeight > nHeight) {
+ CBlockIndex* del = m_node.chainman->ActiveChain().Tip();
+ m_node.chainman->ActiveChain().SetTip(del->pprev);
+ m_node.chainman->ActiveChainstate().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
// non-final txs in mempool
- SetMockTime(::ChainActive().Tip()->GetMedianTimePast()+1);
+ SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1);
int flags = LOCKTIME_VERIFY_SEQUENCE|LOCKTIME_MEDIAN_TIME_PAST;
// height map
std::vector<int> prevheights;
@@ -430,7 +430,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = txFirst[0]->GetHash(); // only 1 transaction
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
- tx.vin[0].nSequence = ::ChainActive().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
+ tx.vin[0].nSequence = m_node.chainman->ActiveChain().Tip()->nHeight + 1; // txFirst[0] is the 2nd block
prevheights[0] = baseheight + 1;
tx.vout.resize(1);
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
@@ -438,53 +438,62 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.nLockTime = 0;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
+
+ {
+ CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 2, active_chain_tip))); // Sequence locks pass on 2nd block
+ }
// relative time locked
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
- tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((::ChainActive().Tip()->GetMedianTimePast()+1-::ChainActive()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
+ tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1-m_node.chainman->ActiveChain()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+
+ {
+ CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
+ BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip))); // Sequence locks pass 512 seconds later
+ }
+
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
// absolute height locked
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
prevheights[0] = baseheight + 3;
- tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
+ tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
// absolute time locked
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
- tx.nLockTime = ::ChainActive().Tip()->GetMedianTimePast();
+ tx.nLockTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast();
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
- BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails
+ BOOST_CHECK(!CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
- BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
+ BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
// mempool-dependent transactions (not added)
tx.vin[0].prevout.hash = hash;
- prevheights[0] = ::ChainActive().Tip()->nHeight + 1;
+ prevheights[0] = m_node.chainman->ActiveChain().Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
- BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes
+ BOOST_CHECK(CheckFinalTx(m_node.chainman->ActiveChain().Tip(), CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
tx.vin[0].nSequence = 1;
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
@@ -502,14 +511,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3U);
// However if we advance height by 1 and time by 512, all of them should be mined
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
- ::ChainActive().Tip()->GetAncestor(::ChainActive().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
- ::ChainActive().Tip()->nHeight++;
- SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1);
+ m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
+ m_node.chainman->ActiveChain().Tip()->nHeight++;
+ SetMockTime(m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U);
- ::ChainActive().Tip()->nHeight--;
+ m_node.chainman->ActiveChain().Tip()->nHeight--;
SetMockTime(0);
m_node.mempool->clear();
diff --git a/src/test/net_peer_eviction_tests.cpp b/src/test/net_peer_eviction_tests.cpp
index 31d391bf7d..4bfd487b86 100644
--- a/src/test/net_peer_eviction_tests.cpp
+++ b/src/test/net_peer_eviction_tests.cpp
@@ -2,7 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <netaddress.h>
#include <net.h>
+#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -15,11 +17,6 @@
BOOST_FIXTURE_TEST_SUITE(net_peer_eviction_tests, BasicTestingSetup)
-namespace {
-constexpr int NODE_EVICTION_TEST_ROUNDS{10};
-constexpr int NODE_EVICTION_TEST_UP_TO_N_NODES{200};
-} // namespace
-
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_candidates, FastRandomContext& random_context)
{
std::vector<NodeEvictionCandidate> candidates;
@@ -36,7 +33,7 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c
/* nKeyedNetGroup */ random_context.randrange(100),
/* prefer_evict */ random_context.randbool(),
/* m_is_local */ random_context.randbool(),
- /* m_is_onion */ random_context.randbool(),
+ /* m_network */ ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
});
}
return candidates;
@@ -94,7 +91,8 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = c.m_is_local = false;
+ c.m_is_local = false;
+ c.m_network = NET_IPV4;
},
/* protected_peer_ids */ {0, 1, 2, 3, 4, 5},
/* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11},
@@ -104,129 +102,359 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
BOOST_CHECK(IsProtected(
num_peers, [num_peers](NodeEvictionCandidate& c) {
c.nTimeConnected = num_peers - c.id;
- c.m_is_onion = c.m_is_local = false;
+ c.m_is_local = false;
+ c.m_network = NET_IPV6;
},
/* protected_peer_ids */ {6, 7, 8, 9, 10, 11},
/* unprotected_peer_ids */ {0, 1, 2, 3, 4, 5},
random_context));
- // Test protection of onion and localhost peers...
+ // Test protection of onion, localhost, and I2P peers...
// Expect 1/4 onion peers to be protected from eviction,
- // independently of other characteristics.
+ // if no localhost or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
- c.m_is_onion = (c.id == 3 || c.id == 8 || c.id == 9);
+ c.m_is_local = false;
+ c.m_network = (c.id == 3 || c.id == 8 || c.id == 9) ? NET_ONION : NET_IPV4;
},
/* protected_peer_ids */ {3, 8, 9},
/* unprotected_peer_ids */ {},
random_context));
- // Expect 1/4 onion peers and 1/4 of the others to be protected
- // from eviction, sorted by longest uptime (lowest nTimeConnected).
+ // Expect 1/4 onion peers and 1/4 of the other peers to be protected,
+ // sorted by longest uptime (lowest nTimeConnected), if no localhost or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
c.m_is_local = false;
- c.m_is_onion = (c.id == 3 || c.id > 7);
+ c.m_network = (c.id == 3 || c.id > 7) ? NET_ONION : NET_IPV6;
},
/* protected_peer_ids */ {0, 1, 2, 3, 8, 9},
/* unprotected_peer_ids */ {4, 5, 6, 7, 10, 11},
random_context));
// Expect 1/4 localhost peers to be protected from eviction,
- // if no onion peers.
+ // if no onion or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
- c.m_is_onion = false;
c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
+ c.m_network = NET_IPV4;
},
/* protected_peer_ids */ {1, 9, 11},
/* unprotected_peer_ids */ {},
random_context));
// Expect 1/4 localhost peers and 1/4 of the other peers to be protected,
- // sorted by longest uptime (lowest nTimeConnected), if no onion peers.
+ // sorted by longest uptime (lowest nTimeConnected), if no onion or I2P peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = false;
c.m_is_local = (c.id > 6);
+ c.m_network = NET_IPV6;
},
/* protected_peer_ids */ {0, 1, 2, 7, 8, 9},
/* unprotected_peer_ids */ {3, 4, 5, 6, 10, 11},
random_context));
- // Combined test: expect 1/4 onion and 2 localhost peers to be protected
- // from eviction, sorted by longest uptime.
+ // Expect 1/4 I2P peers to be protected from eviction,
+ // if no onion or localhost peers.
+ BOOST_CHECK(IsProtected(
+ num_peers, [](NodeEvictionCandidate& c) {
+ c.m_is_local = false;
+ c.m_network = (c.id == 2 || c.id == 7 || c.id == 10) ? NET_I2P : NET_IPV4;
+ },
+ /* protected_peer_ids */ {2, 7, 10},
+ /* unprotected_peer_ids */ {},
+ random_context));
+
+ // Expect 1/4 I2P peers and 1/4 of the other peers to be protected,
+ // sorted by longest uptime (lowest nTimeConnected), if no onion or localhost peers.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 0 || c.id == 5 || c.id == 10);
- c.m_is_local = (c.id == 1 || c.id == 9 || c.id == 11);
+ c.m_is_local = false;
+ c.m_network = (c.id == 4 || c.id > 8) ? NET_I2P : NET_IPV6;
},
- /* protected_peer_ids */ {0, 1, 2, 5, 9, 10},
- /* unprotected_peer_ids */ {3, 4, 6, 7, 8, 11},
+ /* protected_peer_ids */ {0, 1, 2, 4, 9, 10},
+ /* unprotected_peer_ids */ {3, 5, 6, 7, 8, 11},
random_context));
- // Combined test: expect having only 1 onion to allow allocating the
- // remaining 2 of the 1/4 to localhost peers, sorted by longest uptime.
+ // Tests with 2 networks...
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 4 to
+ // protect 1 localhost, 0 onion and 1 other peer, sorted by longest uptime;
+ // stable sort breaks tie with array order of localhost first.
BOOST_CHECK(IsProtected(
- num_peers + 4, [](NodeEvictionCandidate& c) {
+ 4, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 15);
- c.m_is_local = (c.id > 6 && c.id < 11);
+ c.m_is_local = (c.id == 4);
+ c.m_network = (c.id == 3) ? NET_ONION : NET_IPV4;
},
- /* protected_peer_ids */ {0, 1, 2, 3, 7, 8, 9, 15},
- /* unprotected_peer_ids */ {4, 5, 6, 10, 11, 12, 13, 14},
+ /* protected_peer_ids */ {0, 4},
+ /* unprotected_peer_ids */ {1, 2},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 7 to
+ // protect 1 localhost, 0 onion, and 2 other peers (3 total), sorted by
+ // uptime; stable sort breaks tie with array order of localhost first.
+ BOOST_CHECK(IsProtected(
+ 7, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 5},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 1 onion peer out of 8 to
+ // protect protect 1 localhost, 1 onion and 2 other peers (4 total), sorted
+ // by uptime; stable sort breaks tie with array order of localhost first.
+ BOOST_CHECK(IsProtected(
+ 8, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ c.m_network = (c.id == 5) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 5, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 7},
random_context));
- // Combined test: expect 2 onions (< 1/4) to allow allocating the minimum 2
- // localhost peers, sorted by longest uptime.
+ // Combined test: expect having 3 localhost and 3 onion peers out of 12 to
+ // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest
+ // uptime; stable sort breaks ties with the array order of localhost first.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 7 || c.id == 9);
- c.m_is_local = (c.id == 6 || c.id == 11);
+ c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11);
+ c.m_network = (c.id == 7 || c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
},
- /* protected_peer_ids */ {0, 1, 6, 7, 9, 11},
- /* unprotected_peer_ids */ {2, 3, 4, 5, 8, 10},
+ /* protected_peer_ids */ {0, 1, 2, 6, 7, 9},
+ /* unprotected_peer_ids */ {3, 4, 5, 8, 10, 11},
random_context));
- // Combined test: when > 1/4, expect max 1/4 onion and 2 localhost peers
- // to be protected from eviction, sorted by longest uptime.
+ // Combined test: expect having 4 localhost and 1 onion peer out of 12 to
+ // protect 2 localhost and 1 onion, plus 3 other peers, sorted by longest uptime.
BOOST_CHECK(IsProtected(
num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id > 3 && c.id < 8);
- c.m_is_local = (c.id > 7);
+ c.m_is_local = (c.id > 4 && c.id < 9);
+ c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
},
- /* protected_peer_ids */ {0, 4, 5, 6, 8, 9},
- /* unprotected_peer_ids */ {1, 2, 3, 7, 10, 11},
+ /* protected_peer_ids */ {0, 1, 2, 5, 6, 10},
+ /* unprotected_peer_ids */ {3, 4, 7, 8, 9, 11},
random_context));
- // Combined test: idem > 1/4 with only 8 peers: expect 2 onion and 2
- // localhost peers (1/4 + 2) to be protected, sorted by longest uptime.
+ // Combined test: expect having 4 localhost and 2 onion peers out of 16 to
+ // protect 2 localhost and 2 onions, plus 4 other peers, sorted by longest uptime.
BOOST_CHECK(IsProtected(
- 8, [](NodeEvictionCandidate& c) {
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12);
+ c.m_network = (c.id == 8 || c.id == 10) ? NET_ONION : NET_IPV6;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 6, 8, 9, 10},
+ /* unprotected_peer_ids */ {4, 5, 7, 11, 12, 13, 14, 15},
+ random_context));
+
+ // Combined test: expect having 5 localhost and 1 onion peer out of 16 to
+ // protect 3 localhost (recovering the unused onion slot), 1 onion, and 4
+ // others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id > 10);
+ c.m_network = (c.id == 10) ? NET_ONION : NET_IPV4;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 10, 11, 12, 13},
+ /* unprotected_peer_ids */ {4, 5, 6, 7, 8, 9, 14, 15},
+ random_context));
+
+ // Combined test: expect having 1 localhost and 4 onion peers out of 16 to
+ // protect 1 localhost and 3 onions (recovering the unused localhost slot),
+ // plus 4 others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 15);
+ c.m_network = (c.id > 6 && c.id < 11) ? NET_ONION : NET_IPV6;
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 7, 8, 9, 15},
+ /* unprotected_peer_ids */ {5, 6, 10, 11, 12, 13, 14},
+ random_context));
+
+ // Combined test: expect having 2 onion and 4 I2P out of 12 peers to protect
+ // 2 onion (prioritized for having fewer candidates) and 1 I2P, plus 3
+ // others, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ num_peers, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id > 1 && c.id < 5);
- c.m_is_local = (c.id > 4);
+ c.m_is_local = false;
+ if (c.id == 8 || c.id == 10) {
+ c.m_network = NET_ONION;
+ } else if (c.id == 6 || c.id == 9 || c.id == 11 || c.id == 12) {
+ c.m_network = NET_I2P;
+ } else {
+ c.m_network = NET_IPV4;
+ }
},
- /* protected_peer_ids */ {2, 3, 5, 6},
- /* unprotected_peer_ids */ {0, 1, 4, 7},
+ /* protected_peer_ids */ {0, 1, 2, 6, 8, 10},
+ /* unprotected_peer_ids */ {3, 4, 5, 7, 9, 11},
random_context));
- // Combined test: idem > 1/4 with only 6 peers: expect 1 onion peer and no
- // localhost peers (1/4 + 0) to be protected, sorted by longest uptime.
+ // Tests with 3 networks...
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 4
+ // to protect 1 I2P, 0 localhost, 0 onion and 1 other peer (2 total), sorted
+ // by longest uptime; stable sort breaks tie with array order of I2P first.
BOOST_CHECK(IsProtected(
- 6, [](NodeEvictionCandidate& c) {
+ 4, [](NodeEvictionCandidate& c) {
c.nTimeConnected = c.id;
- c.m_is_onion = (c.id == 4 || c.id == 5);
c.m_is_local = (c.id == 3);
+ if (c.id == 4) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 2) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
},
- /* protected_peer_ids */ {0, 1, 4},
- /* unprotected_peer_ids */ {2, 3, 5},
+ /* protected_peer_ids */ {0, 4},
+ /* unprotected_peer_ids */ {1, 2},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 7
+ // to protect 1 I2P, 0 localhost, 0 onion and 2 other peers (3 total) sorted
+ // by longest uptime; stable sort breaks tie with array order of I2P first.
+ BOOST_CHECK(IsProtected(
+ 7, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 4);
+ if (c.id == 6) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 5) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 5},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 1 I2P and 1 onion peer out of 8
+ // to protect 1 I2P, 1 localhost, 0 onion and 2 other peers (4 total) sorted
+ // by uptime; stable sort breaks tie with array order of I2P then localhost.
+ BOOST_CHECK(IsProtected(
+ 8, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6);
+ if (c.id == 5) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 4) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 5, 6},
+ /* unprotected_peer_ids */ {2, 3, 4, 7},
+ random_context));
+
+ // Combined test: expect having 4 localhost, 2 I2P, and 2 onion peers out of
+ // 16 to protect 1 localhost, 2 I2P, and 1 onion (4/16 total), plus 4 others
+ // for 8 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 16, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 6 || c.id > 11);
+ if (c.id == 7 || c.id == 11) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 9 || c.id == 10) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 6, 7, 9, 11},
+ /* unprotected_peer_ids */ {4, 5, 8, 10, 12, 13, 14, 15},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 8 I2P and 1 onion peer out of
+ // 24 to protect 1, 4, and 1 (6 total), plus 6 others for 12/24 total,
+ // sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 12);
+ if (c.id > 14 && c.id < 23) { // 4 protected instead of usual 2
+ c.m_network = NET_I2P;
+ } else if (c.id == 23) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 15, 16, 17, 18, 23},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 14, 19, 20, 21, 22},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 3 I2P and 6 onion peers out of
+ // 24 to protect 1, 3, and 2 (6 total, I2P has fewer candidates and so gets the
+ // unused localhost slot), plus 6 others for 12/24 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 15);
+ if (c.id == 12 || c.id == 14 || c.id == 17) {
+ c.m_network = NET_I2P;
+ } else if (c.id > 17) { // 4 protected instead of usual 2
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 14, 15, 17, 18, 19},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 13, 16, 20, 21, 22, 23},
+ random_context));
+
+ // Combined test: expect having 1 localhost, 7 I2P and 4 onion peers out of
+ // 24 to protect 1 localhost, 2 I2P, and 3 onions (6 total), plus 6 others
+ // for 12/24 total, sorted by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id == 13);
+ if (c.id > 16) {
+ c.m_network = NET_I2P;
+ } else if (c.id == 12 || c.id == 14 || c.id == 15 || c.id == 16) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV6;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 17, 18},
+ /* unprotected_peer_ids */ {6, 7, 8, 9, 10, 11, 16, 19, 20, 21, 22, 23},
+ random_context));
+
+ // Combined test: expect having 8 localhost, 4 I2P, and 3 onion peers out of
+ // 24 to protect 2 of each (6 total), plus 6 others for 12/24 total, sorted
+ // by longest uptime.
+ BOOST_CHECK(IsProtected(
+ 24, [](NodeEvictionCandidate& c) {
+ c.nTimeConnected = c.id;
+ c.m_is_local = (c.id > 15);
+ if (c.id > 10 && c.id < 15) {
+ c.m_network = NET_I2P;
+ } else if (c.id > 6 && c.id < 10) {
+ c.m_network = NET_ONION;
+ } else {
+ c.m_network = NET_IPV4;
+ }
+ },
+ /* protected_peer_ids */ {0, 1, 2, 3, 4, 5, 7, 8, 11, 12, 16, 17},
+ /* unprotected_peer_ids */ {6, 9, 10, 13, 14, 15, 18, 19, 20, 21, 22, 23},
random_context));
}
@@ -257,91 +485,89 @@ BOOST_AUTO_TEST_CASE(peer_eviction_test)
{
FastRandomContext random_context{true};
- for (int i = 0; i < NODE_EVICTION_TEST_ROUNDS; ++i) {
- for (int number_of_nodes = 0; number_of_nodes < NODE_EVICTION_TEST_UP_TO_N_NODES; ++number_of_nodes) {
- // Four nodes with the highest keyed netgroup values should be
- // protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Eight nodes with the lowest minimum ping time should be protected
- // from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [](NodeEvictionCandidate& candidate) {
- candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
- },
- {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
-
- // Four nodes that most recently sent us novel transactions accepted
- // into our mempool should be protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastTXTime = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Up to eight non-tx-relay peers that most recently sent us novel
- // blocks should be protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- if (candidate.id <= 7) {
- candidate.fRelayTxes = false;
- candidate.fRelevantServices = true;
- }
- },
- {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
-
- // Four peers that most recently sent us novel blocks should be
- // protected from eviction.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- },
- {0, 1, 2, 3}, random_context));
-
- // Combination of the previous two tests.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nLastBlockTime = number_of_nodes - candidate.id;
- if (candidate.id <= 7) {
- candidate.fRelayTxes = false;
- candidate.fRelevantServices = true;
- }
- },
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
-
- // Combination of all tests above.
- BOOST_CHECK(!IsEvicted(
- number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
- candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
- candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
- candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
- candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
- },
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
-
- // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
- // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
- // peers by last novel block time, and four more peers by last novel block time.
- if (number_of_nodes >= 29) {
- BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
- }
-
- // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
- // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
- // novel block time.
- if (number_of_nodes <= 20) {
- BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
- }
+ for (int number_of_nodes = 0; number_of_nodes < 200; ++number_of_nodes) {
+ // Four nodes with the highest keyed netgroup values should be
+ // protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Eight nodes with the lowest minimum ping time should be protected
+ // from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [](NodeEvictionCandidate& candidate) {
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id};
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
+
+ // Four nodes that most recently sent us novel transactions accepted
+ // into our mempool should be protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastTXTime = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Up to eight non-tx-relay peers that most recently sent us novel
+ // blocks should be protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ if (candidate.id <= 7) {
+ candidate.fRelayTxes = false;
+ candidate.fRelevantServices = true;
+ }
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7}, random_context));
+
+ // Four peers that most recently sent us novel blocks should be
+ // protected from eviction.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ },
+ {0, 1, 2, 3}, random_context));
+
+ // Combination of the previous two tests.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nLastBlockTime = number_of_nodes - candidate.id;
+ if (candidate.id <= 7) {
+ candidate.fRelayTxes = false;
+ candidate.fRelevantServices = true;
+ }
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, random_context));
+
+ // Combination of all tests above.
+ BOOST_CHECK(!IsEvicted(
+ number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
+ candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
+ candidate.m_min_ping_time = std::chrono::microseconds{candidate.id}; // 8 protected
+ candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
+ candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
+ },
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, random_context));
+
+ // An eviction is expected given >= 29 random eviction candidates. The eviction logic protects at most
+ // four peers by net group, eight by lowest ping time, four by last time of novel tx, up to eight non-tx-relay
+ // peers by last novel block time, and four more peers by last novel block time.
+ if (number_of_nodes >= 29) {
+ BOOST_CHECK(SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
+ }
- // Cases left to test:
- // * "If any remaining peers are preferred for eviction consider only them. [...]"
- // * "Identify the network group with the most connections and youngest member. [...]"
+ // No eviction is expected given <= 20 random eviction candidates. The eviction logic protects at least
+ // four peers by net group, eight by lowest ping time, four by last time of novel tx and four peers by last
+ // novel block time.
+ if (number_of_nodes <= 20) {
+ BOOST_CHECK(!SelectNodeToEvict(GetRandomNodeEvictionCandidates(number_of_nodes, random_context)));
}
+
+ // Cases left to test:
+ // * "If any remaining peers are preferred for eviction consider only them. [...]"
+ // * "Identify the network group with the most connections and youngest member. [...]"
}
}
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 7a122bd8b0..46f88c1282 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -318,15 +318,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK_EQUAL(addr.ToString(), link_local);
- // TORv2
- BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
- BOOST_REQUIRE(addr.IsValid());
- BOOST_REQUIRE(addr.IsTor());
-
- BOOST_CHECK(!addr.IsI2P());
- BOOST_CHECK(!addr.IsBindAny());
- BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
+ // TORv2, no longer supported
+ BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion"));
// TORv3
const char* torv3_addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion";
@@ -470,10 +463,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v1)
BOOST_CHECK_EQUAL(HexStr(s), "1a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b");
s.clear();
- BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
- s << addr;
- BOOST_CHECK_EQUAL(HexStr(s), "fd87d87eeb43f1f2f3f4f5f6f7f8f9fa");
- s.clear();
+ // TORv2, no longer supported
+ BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion"));
BOOST_REQUIRE(addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"));
s << addr;
@@ -508,10 +499,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v2)
BOOST_CHECK_EQUAL(HexStr(s), "02101a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b");
s.clear();
- BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
- s << addr;
- BOOST_CHECK_EQUAL(HexStr(s), "030af1f2f3f4f5f6f7f8f9fa");
- s.clear();
+ // TORv2, no longer supported
+ BOOST_CHECK(!addr.SetSpecial("6hzph5hv6337r6p2.onion"));
BOOST_REQUIRE(addr.SetSpecial("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion"));
s << addr;
@@ -617,26 +606,14 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
BOOST_CHECK(!addr.IsValid());
BOOST_REQUIRE(s.empty());
- // Valid TORv2.
+ // TORv2, no longer supported.
s << MakeSpan(ParseHex("03" // network type (TORv2)
"0a" // address length
"f1f2f3f4f5f6f7f8f9fa")); // address
s >> addr;
- BOOST_CHECK(addr.IsValid());
- BOOST_CHECK(addr.IsTor());
- BOOST_CHECK(addr.IsAddrV1Compatible());
- BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
+ BOOST_CHECK(!addr.IsValid());
BOOST_REQUIRE(s.empty());
- // Invalid TORv2, with bogus length.
- s << MakeSpan(ParseHex("03" // network type (TORv2)
- "07" // address length
- "00")); // address
- BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
- HasReason("BIP155 TORv2 address with length 7 (should be 10)"));
- BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
- s.clear();
-
// Valid TORv3.
s << MakeSpan(ParseHex("04" // network type (TORv3)
"20" // address length
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 3c47cf83e2..687d2f6747 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -44,13 +44,12 @@ static CNetAddr CreateInternal(const std::string& host)
BOOST_AUTO_TEST_CASE(netbase_networks)
{
- BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE);
- BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE);
- BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4);
- BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6);
- BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_ONION);
- BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL);
-
+ BOOST_CHECK(ResolveIP("127.0.0.1").GetNetwork() == NET_UNROUTABLE);
+ BOOST_CHECK(ResolveIP("::1").GetNetwork() == NET_UNROUTABLE);
+ BOOST_CHECK(ResolveIP("8.8.8.8").GetNetwork() == NET_IPV4);
+ BOOST_CHECK(ResolveIP("2001::8888").GetNetwork() == NET_IPV6);
+ BOOST_CHECK(ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion").GetNetwork() == NET_ONION);
+ BOOST_CHECK(CreateInternal("foo.com").GetNetwork() == NET_INTERNAL);
}
BOOST_AUTO_TEST_CASE(netbase_properties)
@@ -73,7 +72,7 @@ BOOST_AUTO_TEST_CASE(netbase_properties)
BOOST_CHECK(ResolveIP("2001:20::").IsRFC7343());
BOOST_CHECK(ResolveIP("FE80::").IsRFC4862());
BOOST_CHECK(ResolveIP("64:FF9B::").IsRFC6052());
- BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
+ BOOST_CHECK(ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion").IsTor());
BOOST_CHECK(ResolveIP("127.0.0.1").IsLocal());
BOOST_CHECK(ResolveIP("::1").IsLocal());
BOOST_CHECK(ResolveIP("8.8.8.8").IsRoutable());
@@ -133,18 +132,6 @@ BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
BOOST_CHECK(TestParse("[fd6c:88c0:8724:1:2:3:4:5]", "[fd6c:88c0:8724:1:2:3:4:5]:65535"));
}
-BOOST_AUTO_TEST_CASE(onioncat_test)
-{
- // values from https://web.archive.org/web/20121122003543/http://www.cypherpunk.at/onioncat/wiki/OnionCat
- CNetAddr addr1(ResolveIP("5wyqrzbvrdsumnok.onion"));
- CNetAddr addr2(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca"));
- BOOST_CHECK(addr1 == addr2);
- BOOST_CHECK(addr1.IsTor());
- BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion");
- BOOST_CHECK(addr1.IsRoutable());
-
-}
-
BOOST_AUTO_TEST_CASE(embedded_test)
{
CNetAddr addr1(ResolveIP("1.2.3.4"));
@@ -338,7 +325,6 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup)
BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6052
BOOST_CHECK(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC3964
BOOST_CHECK(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC4380
- BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_ONION, 239})); // Tor
BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net
BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6
@@ -481,10 +467,10 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0"s, ret));
BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com"s, ret));
BOOST_CHECK(!LookupSubNet("1.2.3.0/24\0example.com\0"s, ret));
- BOOST_CHECK(LookupSubNet("5wyqrzbvrdsumnok.onion"s, ret));
- BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0"s, ret));
- BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com"s, ret));
- BOOST_CHECK(!LookupSubNet("5wyqrzbvrdsumnok.onion\0example.com\0"s, ret));
+ BOOST_CHECK(LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"s, ret));
+ BOOST_CHECK(!LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion\0"s, ret));
+ BOOST_CHECK(!LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion\0example.com"s, ret));
+ BOOST_CHECK(!LookupSubNet("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion\0example.com\0"s, ret));
}
// Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 44fbfa5970..a01d3fa03a 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -3,10 +3,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>
+#include <key_io.h>
#include <script/script.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <test/util/setup_common.h>
+#include <util/strencodings.h>
#include <boost/test/unit_test.hpp>
@@ -111,9 +113,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
s.clear();
s << OP_1 << ToByteVector(uint256::ZERO);
BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_V1_TAPROOT);
- BOOST_CHECK_EQUAL(solutions.size(), 2U);
- BOOST_CHECK(solutions[0] == std::vector<unsigned char>{1});
- BOOST_CHECK(solutions[1] == ToByteVector(uint256::ZERO));
+ BOOST_CHECK_EQUAL(solutions.size(), 1U);
+ BOOST_CHECK(solutions[0] == ToByteVector(uint256::ZERO));
// TxoutType::WITNESS_UNKNOWN
s.clear();
@@ -379,4 +380,70 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
BOOST_CHECK(result == expected);
}
+BOOST_AUTO_TEST_CASE(script_standard_taproot_builder)
+{
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,0,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,1,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({0,2,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,0,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,1,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({1,2,2}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,0,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1,1}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,1,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,0}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,1}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,2}), false);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,2,3,4,5,6,7,8,9,10,11,12,14,14,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,31,31,31,31,31,31,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,128}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({128,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}), true);
+ BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({129,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}), false);
+
+ XOnlyPubKey key_inner{ParseHex("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")};
+ XOnlyPubKey key_1{ParseHex("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5")};
+ XOnlyPubKey key_2{ParseHex("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")};
+ CScript script_1 = CScript() << ToByteVector(key_1) << OP_CHECKSIG;
+ CScript script_2 = CScript() << ToByteVector(key_2) << OP_CHECKSIG;
+ uint256 hash_3 = uint256S("31fe7061656bea2a36aa60a2f7ef940578049273746935d296426dc0afd86b68");
+
+ TaprootBuilder builder;
+ BOOST_CHECK(builder.IsValid() && builder.IsComplete());
+ builder.Add(2, script_2, 0xc0);
+ BOOST_CHECK(builder.IsValid() && !builder.IsComplete());
+ builder.AddOmitted(2, hash_3);
+ BOOST_CHECK(builder.IsValid() && !builder.IsComplete());
+ builder.Add(1, script_1, 0xc0);
+ BOOST_CHECK(builder.IsValid() && builder.IsComplete());
+ builder.Finalize(key_inner);
+ BOOST_CHECK(builder.IsValid() && builder.IsComplete());
+ BOOST_CHECK_EQUAL(EncodeDestination(builder.GetOutput()), "bc1pj6gaw944fy0xpmzzu45ugqde4rz7mqj5kj0tg8kmr5f0pjq8vnaqgynnge");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index d47c54fd6e..3ce7ecb5f2 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -28,7 +28,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// BlockUntilSyncedToCurrentChain should return false before txindex is started.
BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
- BOOST_REQUIRE(txindex.Start(::ChainstateActive()));
+ BOOST_REQUIRE(txindex.Start(m_node.chainman->ActiveChainstate()));
// Allow tx index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 95ad85d0f8..ade9e210f2 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -37,7 +37,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
true /* bypass_limits */);
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 3244b58082..23195c0a26 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -31,7 +31,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
const auto ToMemPool = [this](const CMutableTransaction& tx) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, MakeTransactionRef(tx),
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(tx),
true /* bypass_limits */);
return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
};
@@ -63,7 +63,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
// Test 2: ... and should be rejected if spend1 is in the memory pool
@@ -71,7 +71,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
m_node.mempool->clear();
@@ -80,7 +80,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(spends, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
}
m_node.mempool->clear();
@@ -91,7 +91,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
block = CreateAndProcessBlock(oneSpend, scriptPubKey);
{
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
}
// spends[1] should have been removed from the mempool when the
// block with spends[0] is accepted:
@@ -109,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code.
-static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
PrecomputedTransactionData txdata;
// If we add many more flags, this loop can get too expensive, but we can
@@ -126,7 +126,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
- bool ret = CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, nullptr);
+ bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, nullptr);
// CheckInputScripts should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
@@ -136,13 +136,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
@@ -205,20 +205,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
TxValidationState state;
PrecomputedTransactionData ptd_spend_tx;
- BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputScripts returns true iff DERSIG-enforcing flags are
// not present. Don't add these checks to the cache, so that we can
// test later that block validation works fine in the absence of cached
// successes.
- ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false);
+ ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip());
}
// And if we produce a block with this tx, it should be valid (DERSIG not
@@ -227,8 +227,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
LOCK(cs_main);
- BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
- BOOST_CHECK(::ChainstateActive().CoinsTip().GetBestBlock() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
+ BOOST_CHECK(m_node.chainman->ActiveChainstate().CoinsTip().GetBestBlock() == block.GetHash());
// Test P2SH: construct a transaction that is valid without P2SH, and
// then test validity with P2SH.
@@ -244,7 +244,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip());
}
// Test CHECKLOCKTIMEVERIFY
@@ -267,13 +267,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Make it valid, and check again
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, ::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -295,13 +295,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true);
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Make it valid, and check again
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
}
// TODO: add tests for remaining script flags
@@ -324,11 +324,11 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
// This should be valid under all script flags.
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Remove the witness, and check that it is now invalid.
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true);
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip());
}
{
@@ -353,7 +353,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
}
// This should be valid under all script flags
- ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true);
+ ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
// Check that if the second input is invalid, but the first input is
// valid, the transaction is not cached.
@@ -363,12 +363,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
TxValidationState state;
PrecomputedTransactionData txdata;
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
- BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
}
diff --git a/src/test/util/mining.cpp b/src/test/util/mining.cpp
index 1204873828..f6a11bc02e 100644
--- a/src/test/util/mining.cpp
+++ b/src/test/util/mining.cpp
@@ -74,12 +74,12 @@ CTxIn MineBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
{
auto block = std::make_shared<CBlock>(
- BlockAssembler{::ChainstateActive(), *Assert(node.mempool), Params()}
+ BlockAssembler{Assert(node.chainman)->ActiveChainstate(), *Assert(node.mempool), Params()}
.CreateNewBlock(coinbase_scriptPubKey)
->block);
LOCK(cs_main);
- block->nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1;
+ block->nTime = Assert(node.chainman)->ActiveChain().Tip()->GetMedianTimePast() + 1;
block->hashMerkleRoot = BlockMerkleRoot(*block);
return block;
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 71685d437a..1b49a671bd 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -6,9 +6,11 @@
#define BITCOIN_TEST_UTIL_NET_H
#include <compat.h>
+#include <netaddress.h>
#include <net.h>
#include <util/sock.h>
+#include <array>
#include <cassert>
#include <cstring>
#include <string>
@@ -67,6 +69,16 @@ constexpr ConnectionType ALL_CONNECTION_TYPES[]{
ConnectionType::ADDR_FETCH,
};
+constexpr auto ALL_NETWORKS = std::array{
+ Network::NET_UNROUTABLE,
+ Network::NET_IPV4,
+ Network::NET_IPV6,
+ Network::NET_ONION,
+ Network::NET_I2P,
+ Network::NET_CJDNS,
+ Network::NET_INTERNAL,
+};
+
/**
* A mocked Sock alternative that returns a statically contained data upon read and succeeds
* and ignores all writes. The data to be returned is given to the constructor and when it is
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 863c3ab565..e105e85e47 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -76,6 +76,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
: m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / g_insecure_rand_ctx_temp_path.rand256().ToString()},
m_args{}
{
+ m_node.args = &gArgs;
const std::vector<const char*> arguments = Cat(
{
"dummy",
@@ -94,7 +95,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
gArgs.ForceSetArg("-datadir", m_path_root.string());
gArgs.ClearPathCache();
{
- SetupServerArgs(m_node);
+ SetupServerArgs(*m_node.args);
std::string error;
const bool success{m_node.args->ParseParameters(arguments.size(), arguments.data(), error)};
assert(success);
@@ -145,7 +146,7 @@ ChainTestingSetup::ChainTestingSetup(const std::string& chainName, const std::ve
m_node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
m_node.mempool = std::make_unique<CTxMemPool>(m_node.fee_estimator.get(), 1);
- m_node.chainman = &::g_chainman;
+ m_node.chainman = std::make_unique<ChainstateManager>();
// Start script-checking threads. Set g_parallel_script_checks to true so they are used.
constexpr int script_check_threads = 2;
@@ -167,7 +168,7 @@ ChainTestingSetup::~ChainTestingSetup()
m_node.mempool.reset();
m_node.scheduler.reset();
m_node.chainman->Reset();
- m_node.chainman = nullptr;
+ m_node.chainman.reset();
pblocktree.reset();
}
@@ -180,17 +181,17 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
RegisterAllCoreRPCCommands(tableRPC);
m_node.chainman->InitializeChainstate(*m_node.mempool);
- ::ChainstateActive().InitCoinsDB(
+ m_node.chainman->ActiveChainstate().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
- assert(!::ChainstateActive().CanFlushToDisk());
- ::ChainstateActive().InitCoinsCache(1 << 23);
- assert(::ChainstateActive().CanFlushToDisk());
- if (!::ChainstateActive().LoadGenesisBlock(chainparams)) {
+ assert(!m_node.chainman->ActiveChainstate().CanFlushToDisk());
+ m_node.chainman->ActiveChainstate().InitCoinsCache(1 << 23);
+ assert(m_node.chainman->ActiveChainstate().CanFlushToDisk());
+ if (!m_node.chainman->ActiveChainstate().LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
BlockValidationState state;
- if (!::ChainstateActive().ActivateBestChain(state, chainparams)) {
+ if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state, chainparams)) {
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
}
@@ -240,7 +241,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
{
const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(::ChainstateActive(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler(m_node.chainman->ActiveChainstate(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
@@ -299,7 +300,7 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
// If submit=true, add transaction to the mempool.
if (submit) {
LOCK(cs_main);
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool.get(), MakeTransactionRef(mempool_txn), /* bypass_limits */ false);
assert(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index d463bcdd8e..7ce38519cf 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -329,6 +329,25 @@ BOOST_FIXTURE_TEST_CASE(util_CheckValue, CheckValueTest)
CheckValue(M::ALLOW_ANY, "-value=abc", Expect{"abc"}.String("abc").Int(0).Bool(false).List({"abc"}));
}
+struct NoIncludeConfTest {
+ std::string Parse(const char* arg)
+ {
+ TestArgsManager test;
+ test.SetupArgs({{"-includeconf", ArgsManager::ALLOW_ANY}});
+ std::array argv{"ignored", arg};
+ std::string error;
+ (void)test.ParseParameters(argv.size(), argv.data(), error);
+ return error;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(util_NoIncludeConf, NoIncludeConfTest)
+{
+ BOOST_CHECK_EQUAL(Parse("-noincludeconf"), "");
+ BOOST_CHECK_EQUAL(Parse("-includeconf"), "-includeconf cannot be used from commandline; -includeconf=\"\"");
+ BOOST_CHECK_EQUAL(Parse("-includeconf=file"), "-includeconf cannot be used from commandline; -includeconf=\"file\"");
+}
+
BOOST_AUTO_TEST_CASE(util_ParseParameters)
{
TestArgsManager testArgs;
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 552be0a2da..e0bc10d660 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -84,8 +84,8 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
- LOCK(cs_main); // For g_chainman.m_blockman.LookupBlockIndex
- GenerateCoinbaseCommitment(*pblock, g_chainman.m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
+ LOCK(cs_main); // For m_node.chainman->m_blockman.LookupBlockIndex
+ GenerateCoinbaseCommitment(*pblock, m_node.chainman->m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
const CBlockIndex* initial_tip = nullptr;
{
LOCK(cs_main);
- initial_tip = ::ChainActive().Tip();
+ initial_tip = m_node.chainman->ActiveChain().Tip();
}
auto sub = std::make_shared<TestSubscriber>(initial_tip->GetBlockHash());
RegisterSharedValidationInterface(sub);
@@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
UnregisterSharedValidationInterface(sub);
LOCK(cs_main);
- BOOST_CHECK_EQUAL(sub->m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(sub->m_expected_tip, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
/**
@@ -232,7 +232,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Run the test multiple times
for (int test_runs = 3; test_runs > 0; --test_runs) {
- BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
// Later on split from here
const uint256 split_hash{last_mined->hashPrevBlock};
@@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
LOCK(cs_main);
for (const auto& tx : txs) {
- const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, tx, false /* bypass_limits */);
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, tx, false /* bypass_limits */);
BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
@@ -306,7 +306,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
}
LOCK(cs_main);
// We are done with the reorg, so the tip must have changed
- assert(tip_init != ::ChainActive().Tip()->GetBlockHash());
+ assert(tip_init != m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}};
// Submit the reorg in this thread to invalidate and remove the txs from the tx pool
@@ -314,7 +314,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
ProcessBlock(b);
}
// Check that the reorg was eventually successful
- BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(last_mined->GetHash(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
// We can join the other thread, which returns when the reorg was successful
rpc_thread.join();
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 4413da7ea7..c5a4bbf1b0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -513,11 +513,10 @@ void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags)
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp);
- CCoinsViewMemPool viewMempool(&active_chainstate.CoinsTip(), *this);
+ CCoinsViewMemPool view_mempool(&active_chainstate.CoinsTip(), *this);
if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags)
- || !CheckSequenceLocks(active_chainstate.m_chain.Tip(), viewMempool, tx, flags, &lp, validLP)) {
+ || !CheckSequenceLocks(active_chainstate.m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
txToRemove.insert(it);
@@ -638,10 +637,8 @@ void CTxMemPool::check(CChainState& active_chainstate) const
uint64_t innerUsage = 0;
CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(active_coins_tip)); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
- assert(g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate) == spendheight); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT
std::list<const CTxMemPoolEntry*> waitingOnDependants;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
diff --git a/src/txmempool.h b/src/txmempool.h
index 46b89049bb..ae4b16d377 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -874,7 +874,8 @@ protected:
public:
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
- /** Add the coins created by this transaction. */
+ /** Add the coins created by this transaction. These coins are only temporarily stored in
+ * m_temp_added and cannot be flushed to the back end. Only used for package validation. */
void PackageAddTransaction(const CTransactionRef& tx);
};
diff --git a/src/uint256.h b/src/uint256.h
index fadf2320af..d4917d0eac 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -75,7 +75,7 @@ public:
return &m_data[WIDTH];
}
- unsigned int size() const
+ static constexpr unsigned int size()
{
return sizeof(m_data);
}
diff --git a/src/util/spanparsing.cpp b/src/util/spanparsing.cpp
index 0f68254f2c..e2e2782bec 100644
--- a/src/util/spanparsing.cpp
+++ b/src/util/spanparsing.cpp
@@ -34,11 +34,11 @@ Span<const char> Expr(Span<const char>& sp)
int level = 0;
auto it = sp.begin();
while (it != sp.end()) {
- if (*it == '(') {
+ if (*it == '(' || *it == '{') {
++level;
- } else if (level && *it == ')') {
+ } else if (level && (*it == ')' || *it == '}')) {
--level;
- } else if (level == 0 && (*it == ')' || *it == ',')) {
+ } else if (level == 0 && (*it == ')' || *it == '}' || *it == ',')) {
break;
}
++it;
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 5b87806a45..13ccf7463e 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -365,11 +365,14 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
m_settings.command_line_options[key].push_back(value);
}
- // we do not allow -includeconf from command line
+ // we do not allow -includeconf from command line, only -noincludeconf
if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
- const auto& include{*util::SettingsSpan(*includes).begin()}; // pick first value as example
- error = "-includeconf cannot be used from commandline; -includeconf=" + include.write();
- return false;
+ const util::SettingsSpan values{*includes};
+ // Range may be empty if -noincludeconf was passed
+ if (!values.empty()) {
+ error = "-includeconf cannot be used from commandline; -includeconf=" + values.begin()->write();
+ return false; // pick first value as example
+ }
}
return true;
}
diff --git a/src/validation.cpp b/src/validation.cpp
index 4c861599fd..b48e49a10b 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -101,21 +101,6 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
return false;
}
-ChainstateManager g_chainman;
-
-CChainState& ChainstateActive()
-{
- LOCK(::cs_main);
- assert(g_chainman.m_active_chainstate);
- return *g_chainman.m_active_chainstate;
-}
-
-CChain& ChainActive()
-{
- LOCK(::cs_main);
- return ::ChainstateActive().m_chain;
-}
-
/**
* Mutex to guard access to validation specific variables, such as reading
* or changing the chainstate.
@@ -161,7 +146,6 @@ void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.BlockIndex()) == std::addressof(m_block_index));
BlockMap::const_iterator it = m_block_index.find(hash);
return it == m_block_index.end() ? nullptr : it->second;
}
@@ -170,7 +154,6 @@ CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlo
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
// Find the latest block common to locator and chain - we expect that
// locator.vHave is sorted descending by height.
for (const uint256& hash : locator.vHave) {
@@ -198,7 +181,6 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
{
AssertLockHeld(cs_main);
assert(active_chain_tip); // TODO: Make active_chain_tip a reference
- assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*active_chain_tip));
// By convention a negative value for flags indicates that the
// current network-enforced consensus rules should be used. In
@@ -237,7 +219,6 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp)
if (lp->maxInputBlock) {
// Check whether ::ChainActive() is an extension of the block at which the LockPoints
// calculation was valid. If not LockPoints are no longer valid
- assert(std::addressof(::ChainActive()) == std::addressof(active_chain));
if (!active_chain.Contains(lp->maxInputBlock)) {
return false;
}
@@ -331,7 +312,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining);
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_cache));
for (const COutPoint& removed : vNoSpendsRemaining)
coins_cache.Uncache(removed);
}
@@ -339,7 +319,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (active_chainstate.IsInitialBlockDownload())
return false;
if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
@@ -366,7 +345,6 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me
{
AssertLockHeld(cs_main);
AssertLockHeld(mempool.cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
@@ -433,7 +411,6 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
assert(txFrom->vout.size() > txin.prevout.n);
assert(txFrom->vout[txin.prevout.n] == coin.out);
} else {
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_tip));
const Coin& coinFromUTXOSet = coins_tip.AccessCoin(txin.prevout);
assert(!coinFromUTXOSet.IsSpent());
assert(coinFromUTXOSet.out == coin.out);
@@ -454,7 +431,6 @@ public:
m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
}
// We put the arguments we're handed into a struct, so we can pass them
@@ -472,8 +448,10 @@ public:
*/
std::vector<COutPoint>& m_coins_to_uncache;
const bool m_test_accept;
- /** Disable BIP125 RBFing; disallow all conflicts with mempool transactions. */
- const bool disallow_mempool_conflicts;
+ /** Whether we allow transactions to replace mempool transactions by BIP125 rules. If false,
+ * any transaction spending the same inputs as a transaction in the mempool is considered
+ * a conflict. */
+ const bool m_allow_bip125_replacement{true};
};
// Single transaction acceptance
@@ -482,7 +460,7 @@ public:
/**
* Multiple transaction acceptance. Transactions may or may not be interdependent,
* but must not conflict with each other. Parents must come before children if any
- * dependencies exist, otherwise a TX_MISSING_INPUTS error will be returned.
+ * dependencies exist.
*/
PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -605,7 +583,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
@@ -619,6 +596,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
{
const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
if (ptxConflicting) {
+ if (!args.m_allow_bip125_replacement) {
+ // Transaction conflicts with a mempool tx, but we're not allowing replacements.
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
+ }
if (!setConflicts.count(ptxConflicting->GetHash()))
{
// Allow opt-out of transaction replacement by setting
@@ -629,10 +610,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// is for the sake of multi-party protocols, where we don't
// want a single party to be able to disable replacement.
//
- // The opt-out ignores descendants as anyone relying on
- // first-seen mempool behavior should be checking all
- // unconfirmed ancestors anyway; doing otherwise is hopelessly
- // insecure.
+ // Transactions that don't explicitly signal replaceability are
+ // *not* replaceable with the current logic, even if one of their
+ // unconfirmed ancestors signals replaceability. This diverges
+ // from BIP125's inherited signaling description (see CVE-2021-31876).
+ // Applications relying on first-seen mempool behavior should
+ // check all unconfirmed ancestors; otherwise an opt-in ancestor
+ // might be replaced, causing removal of this descendant.
bool fReplacementOptOut = true;
for (const CTxIn &_txin : ptxConflicting->vin)
{
@@ -642,7 +626,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
break;
}
}
- if (fReplacementOptOut || args.disallow_mempool_conflicts) {
+ if (fReplacementOptOut) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
}
@@ -654,7 +638,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
LockPoints lp;
m_view.SetBackend(m_viewmempool);
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
@@ -692,18 +675,15 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// be mined yet.
// Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
// backend was removed, it no longer pulls coins from the mempool.
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
if (!CheckSequenceLocks(m_active_chainstate.m_chain.Tip(), m_view, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman));
if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_blockman.GetSpendHeight(m_view), ws.m_base_fees)) {
return false; // state filled in by CheckTxInputs
}
// Check for non-standard pay-to-script-hash in inputs
const auto& params = args.m_chainparams.GetConsensus();
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
auto taproot_state = VersionBitsState(m_active_chainstate.m_chain.Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
if (fRequireStandard && !AreInputsStandard(tx, m_view, taproot_state == ThresholdState::ACTIVE)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
@@ -730,7 +710,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
}
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(),
fSpendsCoinbase, nSigOpsCost, lp));
unsigned int nSize = entry->GetTxSize();
@@ -983,9 +962,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, P
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
- assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain));
unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(m_active_chainstate.m_chain.Tip(), chainparams.GetConsensus());
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata, m_active_chainstate.CoinsTip())) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), state.ToString());
@@ -1026,7 +1003,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
// - the node is not behind
// - the transaction is not dependent on any other transactions in the mempool
- assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate));
bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
// Store transaction in memory
@@ -1034,7 +1010,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// trim mempool and check if tx was trimmed
if (!bypass_limits) {
- assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip()));
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
if (!m_pool.exists(hash))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
@@ -1077,65 +1052,15 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
{
AssertLockHeld(cs_main);
+ // These context-free package limits can be done before taking the mempool lock.
PackageValidationState package_state;
- const unsigned int package_count = txns.size();
-
- // These context-free package limits can be checked before taking the mempool lock.
- if (package_count > MAX_PACKAGE_COUNT) {
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
- return PackageMempoolAcceptResult(package_state, {});
- }
-
- const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0,
- [](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); });
- // If the package only contains 1 tx, it's better to report the policy violation on individual tx size.
- if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) {
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
- return PackageMempoolAcceptResult(package_state, {});
- }
+ if (!CheckPackage(txns, package_state)) return PackageMempoolAcceptResult(package_state, {});
- // Construct workspaces and check package policies.
std::vector<Workspace> workspaces{};
- workspaces.reserve(package_count);
- {
- std::unordered_set<uint256, SaltedTxidHasher> later_txids;
- std::transform(txns.cbegin(), txns.cend(), std::inserter(later_txids, later_txids.end()),
- [](const auto& tx) { return tx->GetHash(); });
- // Require the package to be sorted in order of dependency, i.e. parents appear before children.
- // An unsorted package will fail anyway on missing-inputs, but it's better to quit earlier and
- // fail on something less ambiguous (missing-inputs could also be an orphan or trying to
- // spend nonexistent coins).
- for (const auto& tx : txns) {
- for (const auto& input : tx->vin) {
- if (later_txids.find(input.prevout.hash) != later_txids.end()) {
- // The parent is a subsequent transaction in the package.
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-not-sorted");
- return PackageMempoolAcceptResult(package_state, {});
- }
- }
- later_txids.erase(tx->GetHash());
- workspaces.emplace_back(Workspace(tx));
- }
- }
+ workspaces.reserve(txns.size());
+ std::transform(txns.cbegin(), txns.cend(), std::back_inserter(workspaces),
+ [](const auto& tx) { return Workspace(tx); });
std::map<const uint256, const MempoolAcceptResult> results;
- {
- // Don't allow any conflicting transactions, i.e. spending the same inputs, in a package.
- std::unordered_set<COutPoint, SaltedOutpointHasher> inputs_seen;
- for (const auto& tx : txns) {
- for (const auto& input : tx->vin) {
- if (inputs_seen.find(input.prevout) != inputs_seen.end()) {
- // This input is also present in another tx in the package.
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "conflict-in-package");
- return PackageMempoolAcceptResult(package_state, {});
- }
- }
- // Batch-add all the inputs for a tx at a time. If we added them 1 at a time, we could
- // catch duplicate inputs within a single tx. This is a more severe, consensus error,
- // and we want to report that from CheckTransaction instead.
- std::transform(tx->vin.cbegin(), tx->vin.cend(), std::inserter(inputs_seen, inputs_seen.end()),
- [](const auto& input) { return input.prevout; });
- }
- }
LOCK(m_pool.cs);
@@ -1148,10 +1073,10 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
return PackageMempoolAcceptResult(package_state, std::move(results));
}
// Make the coins created by this transaction available for subsequent transactions in the
- // package to spend. Since we already checked conflicts in the package and RBFs are
- // impossible, we don't need to track the coins spent. Note that this logic will need to be
- // updated if RBFs in packages are allowed in the future.
- assert(args.disallow_mempool_conflicts);
+ // package to spend. Since we already checked conflicts in the package and we don't allow
+ // replacements, we don't need to track the coins spent. Note that this logic will need to be
+ // updated if package replace-by-fee is allowed in the future.
+ assert(!args.m_allow_bip125_replacement);
m_viewmempool.PackageAddTransaction(ws.m_ptx);
}
@@ -1185,9 +1110,8 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
{
std::vector<COutPoint> coins_to_uncache;
MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache,
- test_accept, /* disallow_mempool_conflicts */ false };
+ test_accept, /* m_allow_bip125_replacement */ true };
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
// Remove coins that were not present in the coins cache before calling
@@ -1207,7 +1131,6 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept);
}
@@ -1222,12 +1145,10 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
std::vector<COutPoint> coins_to_uncache;
const CChainParams& chainparams = Params();
MemPoolAccept::ATMPArgs args { chainparams, GetTime(), /* bypass_limits */ false, coins_to_uncache,
- test_accept, /* disallow_mempool_conflicts */ true };
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
+ test_accept, /* m_allow_bip125_replacement */ false };
const PackageMempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
// Uncache coins pertaining to transactions that were not submitted to the mempool.
- // Ensure the cache is still within its size limits.
for (const COutPoint& hashTx : coins_to_uncache) {
active_chainstate.CoinsTip().Uncache(hashTx);
}
@@ -1363,7 +1284,6 @@ static void AlertNotify(const std::string& strMessage)
void CChainState::CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
@@ -1382,7 +1302,6 @@ void CChainState::CheckForkWarningConditions()
// Called both upon regular invalid block discovery *and* InvalidateBlock
void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
@@ -1401,8 +1320,9 @@ void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
}
// Same as InvalidChainFound, above, except not called directly from InvalidateBlock,
-// which does its own setBlockIndexCandidates manageent.
-void CChainState::InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) {
+// which does its own setBlockIndexCandidates management.
+void CChainState::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state)
+{
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
m_blockman.m_failed_blocks.insert(pindex);
@@ -1442,7 +1362,6 @@ bool CScriptCheck::operator()() {
int BlockManager::GetSpendHeight(const CCoinsViewCache& inputs)
{
AssertLockHeld(cs_main);
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
CBlockIndex* pindexPrev = LookupBlockIndex(inputs.GetBestBlock());
return pindexPrev->nHeight + 1;
}
@@ -1819,8 +1738,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// may have let in a block that violates the rule prior to updating the
// software, and we would NOT be enforcing the rule here. Fully solving
// upgrade from one software version to the next after a consensus rule
- // change is potentially tricky and issue-specific (see RewindBlockIndex()
- // for one general approach that was used for BIP 141 deployment).
+ // change is potentially tricky and issue-specific (see NeedsRedownload()
+ // for one approach that was used for BIP 141 deployment).
// Also, currently the rule against blocks more than 2 hours in the future
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
// re-enforce that rule here (at least until we make it impossible for
@@ -2326,14 +2245,13 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
bilingual_str warning_messages;
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (!active_chainstate.IsInitialBlockDownload()) {
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
- const bilingual_str warning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
+ const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
DoWarning(warning);
} else {
@@ -2342,7 +2260,6 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
}
}
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
@@ -2602,7 +2519,6 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
{
AssertLockHeld(cs_main);
AssertLockHeld(m_mempool.cs);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
const CBlockIndex* pindexOldTip = m_chain.Tip();
const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2702,7 +2618,6 @@ static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) {
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
fInitialBlockDownload = chainstate.IsInitialBlockDownload();
pindexHeaderOld = pindexHeader;
}
@@ -2913,7 +2828,6 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@@ -3244,7 +3158,6 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
{
const uint256& hash = i.second;
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this));
CBlockIndex* pindex = LookupBlockIndex(hash);
if (pindex) {
return pindex;
@@ -3277,7 +3190,6 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// BlockIndex().
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman));
CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight);
@@ -3481,7 +3393,6 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
// Exposed wrapper for AcceptBlockHeader
bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
AssertLockNotHeld(cs_main);
{
LOCK(cs_main);
@@ -3572,7 +3483,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// Write block to history file
if (fNewBlock) *fNewBlock = true;
- assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, chainparams, dbp);
if (blockPos.IsNull()) {
@@ -3594,7 +3504,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
{
AssertLockNotHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate()));
{
CBlockIndex *pindex = nullptr;
@@ -3605,8 +3514,11 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
// Therefore, the following critical section must include the CheckBlock() call as well.
LOCK(cs_main);
- // Ensure that CheckBlock() passes before calling AcceptBlock, as
- // belt-and-suspenders.
+ // Skipping AcceptBlock() for CheckBlock() failures means that we will never mark a block as invalid if
+ // CheckBlock() fails. This is protective against consensus failure if there are any unknown forms of block
+ // malleability that cause CheckBlock() to fail; see e.g. CVE-2012-2459 and
+ // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html. Because CheckBlock() is
+ // not very expensive, the anti-DoS benefits of caching failure (of a definitely-invalid block) are not substantial.
bool ret = CheckBlock(*block, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
@@ -3636,7 +3548,6 @@ bool TestBlockValidity(BlockValidationState& state,
bool fCheckMerkleRoot)
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip());
CCoinsViewCache viewNew(&chainstate.CoinsTip());
uint256 block_hash(block.GetHash());
@@ -3646,7 +3557,6 @@ bool TestBlockValidity(BlockValidationState& state,
indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainstate.m_blockman));
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
@@ -3726,7 +3636,6 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh
{
BlockValidationState state;
const CChainParams& chainparams = Params();
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (!active_chainstate.FlushStateToDisk(
chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
@@ -3880,7 +3789,6 @@ void BlockManager::Unload() {
bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
{
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!m_blockman.LoadBlockIndex(
chainparams.GetConsensus(), *pblocktree,
setBlockIndexCandidates)) {
@@ -3937,7 +3845,6 @@ bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams)
void CChainState::LoadMempool(const ArgsManager& args)
{
if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
::LoadMempool(m_mempool, *this);
}
m_mempool.SetIsLoaded(!ShutdownRequested());
@@ -3989,7 +3896,6 @@ bool CVerifyDB::VerifyDB(
{
AssertLockHeld(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate));
if (chainstate.m_chain.Tip() == nullptr || chainstate.m_chain.Tip()->pprev == nullptr)
return true;
@@ -4258,7 +4164,6 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
if (m_blockman.m_block_index.count(chainparams.GenesisBlock().GetHash()))
return true;
- assert(std::addressof(::ChainActive()) == std::addressof(m_chain));
try {
const CBlock& block = chainparams.GenesisBlock();
FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr);
@@ -4322,7 +4227,6 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
{
LOCK(cs_main);
// detect out of order blocks, and store them for later
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
if (hash != chainparams.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
block.hashPrevBlock.ToString());
@@ -4332,11 +4236,9 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
}
// process in case the block isn't known yet
- assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman));
CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash);
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
nLoaded++;
}
@@ -4351,13 +4253,11 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
// Activate the genesis block so normal node progress can continue
if (hash == chainparams.GetConsensus().hashGenesisBlock) {
BlockValidationState state;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (!ActivateBestChain(state, chainparams, nullptr)) {
break;
}
}
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
NotifyHeaderTip(*this);
// Recursively process earlier encountered successors of this block
@@ -4376,7 +4276,6 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
head.ToString());
LOCK(cs_main);
BlockValidationState dummy;
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
{
nLoaded++;
@@ -4385,7 +4284,6 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f
}
range.first++;
mapBlocksUnknownParent.erase(it);
- assert(std::addressof(::ChainstateActive()) == std::addressof(*this));
NotifyHeaderTip(*this);
}
}
@@ -4666,7 +4564,6 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mocka
}
if (nTime > nNow - nExpiryTimeout) {
LOCK(cs_main);
- assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, tx, nTime, false /* bypass_limits */,
false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
++count;
@@ -5079,24 +4976,20 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
LOCK(::cs_main);
// Fake various pieces of CBlockIndex state:
- //
- // - nChainTx: so that we accurately report IBD-to-tip progress
- // - nTx: so that LoadBlockIndex() loads assumed-valid CBlockIndex entries
- // (among other things)
- // - nStatus & BLOCK_OPT_WITNESS: so that RewindBlockIndex() doesn't zealously
- // unwind the assumed-valid chain.
- //
CBlockIndex* index = nullptr;
for (int i = 0; i <= snapshot_chainstate.m_chain.Height(); ++i) {
index = snapshot_chainstate.m_chain[i];
+ // Fake nTx so that LoadBlockIndex() loads assumed-valid CBlockIndex
+ // entries (among other things)
if (!index->nTx) {
index->nTx = 1;
}
+ // Fake nChainTx so that GuessVerificationProgress reports accurately
index->nChainTx = index->pprev ? index->pprev->nChainTx + index->nTx : 1;
- // We need to fake this flag so that CChainState::RewindBlockIndex()
- // won't try to rewind the entire assumed-valid chain on startup.
+ // Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
+ // won't ask to rewind the entire assumed-valid chain on startup.
if (index->pprev && ::IsWitnessEnabled(index->pprev, ::Params().GetConsensus())) {
index->nStatus |= BLOCK_OPT_WITNESS;
}
diff --git a/src/validation.h b/src/validation.h
index 9c718b3d63..5720ba8071 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -234,11 +234,13 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
-* Atomically test acceptance of a package. If the package only contains one tx, package rules still apply.
+* Atomically test acceptance of a package. If the package only contains one tx, package rules still
+* apply. Package validation does not allow BIP125 replacements, so the transaction(s) cannot spend
+* the same inputs as any transaction in the mempool.
* @param[in] txns Group of transactions which may be independent or contain
-* parent-child dependencies. The transactions must not conflict, i.e.
-* must not spend the same inputs, even if it would be a valid BIP125
-* replace-by-fee. Parents must appear before children.
+* parent-child dependencies. The transactions must not conflict
+* with each other, i.e., must not spend the same inputs. If any
+* dependencies exist, parents must appear before children.
* @returns a PackageMempoolAcceptResult which includes a MempoolAcceptResult for each transaction.
* If a transaction fails, validation will exit early and some results may be missing.
*/
@@ -269,9 +271,13 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
* @param[in] tip Chain tip to check tx sequence locks against. For example,
* the tip of the current active chain.
- * @param[in] coins_view Any CCoinsView that provides access to the relevant coins
- * for checking sequence locks. Any CCoinsView can be passed in;
- * it is assumed to be consistent with the tip.
+ * @param[in] coins_view Any CCoinsView that provides access to the relevant coins for
+ * checking sequence locks. For example, it can be a CCoinsViewCache
+ * that isn't connected to anything but contains all the relevant
+ * coins, or a CCoinsViewMemPool that is connected to the
+ * mempool and chainstate UTXO set. In the latter case, the caller is
+ * responsible for holding the appropriate locks to ensure that
+ * calls to GetCoin() return correct coins.
* Simulates calling SequenceLocks() with data from the tip passed in.
* Optionally stores in LockPoints the resulting height and time calculated and the hash
* of the block needed for calculation or skips the calculation and uses the LockPoints
@@ -555,7 +561,7 @@ enum class CoinsCacheSizeState
*
* Anything that is contingent on the current tip of the chain is stored here,
* whereas block information and metadata independent of the current tip is
- * kept in `BlockMetadataManager`.
+ * kept in `BlockManager`.
*/
class CChainState
{
@@ -885,10 +891,6 @@ private:
CAutoFile& coins_file,
const SnapshotMetadata& metadata);
- // For access to m_active_chainstate.
- friend CChainState& ChainstateActive();
- friend CChain& ChainActive();
-
public:
std::thread m_load_block;
//! A single BlockManager instance is shared across each constructed
@@ -1011,16 +1013,13 @@ public:
//! Check to see if caches are out of balance and if so, call
//! ResizeCoinsCaches() as needed.
void MaybeRebalanceCaches() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
-};
-
-/** DEPRECATED! Please use node.chainman instead. May only be used in validation.cpp internally */
-extern ChainstateManager g_chainman GUARDED_BY(::cs_main);
-/** Please prefer the identical ChainstateManager::ActiveChainstate */
-CChainState& ChainstateActive();
-
-/** Please prefer the identical ChainstateManager::ActiveChain */
-CChain& ChainActive();
+ ~ChainstateManager() {
+ LOCK(::cs_main);
+ UnloadBlockIndex(/* mempool */ nullptr, *this);
+ Reset();
+ }
+};
/** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree;
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 5bf037b222..8d5316e0af 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -12,7 +12,7 @@
std::vector<fs::path> ListDatabases(const fs::path& wallet_dir)
{
- const size_t offset = wallet_dir.string().size() + 1;
+ const size_t offset = wallet_dir.string().size() + (wallet_dir == wallet_dir.root_name() ? 0 : 1);
std::vector<fs::path> paths;
boost::system::error_code ec;
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 64ce09d1d1..5a832d020b 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -197,22 +197,19 @@ public:
}
return result;
}
- bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override
- {
+ std::vector<std::string> getAddressReceiveRequests() override {
LOCK(m_wallet->cs_wallet);
- WalletBatch batch{m_wallet->GetDatabase()};
- return m_wallet->AddDestData(batch, dest, key, value);
+ return m_wallet->GetAddressReceiveRequests();
}
- bool eraseDestData(const CTxDestination& dest, const std::string& key) override
- {
+ bool setAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& value) override {
LOCK(m_wallet->cs_wallet);
WalletBatch batch{m_wallet->GetDatabase()};
- return m_wallet->EraseDestData(batch, dest, key);
+ return m_wallet->SetAddressReceiveRequest(batch, dest, id, value);
}
- std::vector<std::string> getDestValues(const std::string& prefix) override
+ bool displayAddress(const CTxDestination& dest) override
{
LOCK(m_wallet->cs_wallet);
- return m_wallet->GetDestValues(prefix);
+ return m_wallet->DisplayAddress(dest);
}
void lockCoin(const COutPoint& output) override
{
@@ -352,9 +349,9 @@ public:
TransactionError fillPSBT(int sighash_type,
bool sign,
bool bip32derivs,
+ size_t* n_signed,
PartiallySignedTransaction& psbtx,
- bool& complete,
- size_t* n_signed) override
+ bool& complete) override
{
return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs, n_signed);
}
@@ -454,6 +451,7 @@ public:
unsigned int getConfirmTarget() override { return m_wallet->m_confirm_target; }
bool hdEnabled() override { return m_wallet->IsHDEnabled(); }
bool canGetAddresses() override { return m_wallet->CanGetAddresses(); }
+ bool hasExternalSigner() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER); }
bool privateKeysDisabled() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); }
OutputType getDefaultAddressType() override { return m_wallet->m_default_address_type; }
CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; }
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 726b13beac..4e9ba83ead 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1530,6 +1530,18 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
}
}
+ // Taproot descriptors cannot be imported if Taproot is not yet active.
+ // Check if this is a Taproot descriptor
+ CTxDestination dest;
+ ExtractDestination(scripts[0], dest);
+ if (std::holds_alternative<WitnessV1Taproot>(dest)) {
+ // Check if Taproot is active
+ if (!wallet.chain().isTaprootActive()) {
+ // Taproot is not active, raise an error
+ throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import tr() descriptor when Taproot is not active");
+ }
+ }
+
// If private keys are enabled, check some things.
if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (keys.keys.empty()) {
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index f270e1ad05..534c974178 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3735,6 +3735,7 @@ public:
return obj;
}
+ UniValue operator()(const WitnessV1Taproot& id) const { return UniValue(UniValue::VOBJ); }
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
};
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 97fc7acca5..c8ded4c51e 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -352,7 +352,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
return groups_out;
}
-bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
+bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const
{
setCoinsRet.clear();
@@ -456,32 +456,32 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
// confirmations on outputs received from other wallets and only spend confirmed change.
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
// Fall back to using zero confirmation change (but with as few ancestors in the mempool as
// possible) if we cannot fund the transaction otherwise.
if (m_spend_zero_conf_change) {
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
}
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
}
// If partial groups are allowed, relax the requirement of spending OutputGroups (groups
// of UTXOs sent to the same address, which are obviously controlled by a single wallet)
// in their entirety.
- if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
+ if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
}
// Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs
// received from other wallets.
if (coin_control.m_include_unsafe_inputs
- && SelectCoinsMinConf(value_to_select,
+ && AttemptSelection(value_to_select,
CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
@@ -489,7 +489,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// Try with unlimited ancestors/descendants. The transaction will still need to meet
// mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but
// OutputGroups use heuristics that may overestimate ancestor/descendant counts.
- if (!fRejectLongChains && SelectCoinsMinConf(value_to_select,
+ if (!fRejectLongChains && AttemptSelection(value_to_select,
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true;
@@ -499,7 +499,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return false;
}();
- // SelectCoinsMinConf clears setCoinsRet, so add the preset inputs from coin_control to the coinset
+ // AttemptSelection clears setCoinsRet, so add the preset inputs from coin_control to the coinset
util::insert(setCoinsRet, setPresetCoins);
// add preset inputs to the total value selected
@@ -578,287 +578,266 @@ bool CWallet::CreateTransactionInternal(
FeeCalculation& fee_calc_out,
bool sign)
{
- CAmount nValue = 0;
+ AssertLockHeld(cs_wallet);
+
+ CMutableTransaction txNew; // The resulting transaction that we make
+ txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
+
+ CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
+ coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
+
+ CAmount recipients_sum = 0;
const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
ReserveDestination reservedest(this, change_type);
- unsigned int nSubtractFeeFromAmount = 0;
- for (const auto& recipient : vecSend)
- {
- if (nValue < 0 || recipient.nAmount < 0)
- {
- error = _("Transaction amounts must not be negative");
- return false;
+ unsigned int outputs_to_subtract_fee_from = 0; // The number of outputs which we are subtracting the fee from
+ for (const auto& recipient : vecSend) {
+ recipients_sum += recipient.nAmount;
+
+ if (recipient.fSubtractFeeFromAmount) {
+ outputs_to_subtract_fee_from++;
+ coin_selection_params.m_subtract_fee_outputs = true;
}
- nValue += recipient.nAmount;
+ }
- if (recipient.fSubtractFeeFromAmount)
- nSubtractFeeFromAmount++;
+ // Create change script that will be used if we need change
+ // TODO: pass in scriptChange instead of reservedest so
+ // change transaction isn't always pay-to-bitcoin-address
+ CScript scriptChange;
+
+ // coin control: send change to custom address
+ if (!std::get_if<CNoDestination>(&coin_control.destChange)) {
+ scriptChange = GetScriptForDestination(coin_control.destChange);
+ } else { // no coin control: send change to newly generated address
+ // Note: We use a new key here to keep it from being obvious which side is the change.
+ // The drawback is that by not reusing a previous key, the change may be lost if a
+ // backup is restored, if the backup doesn't have the new private key for the change.
+ // If we reused the old key, it would be possible to add code to look for and
+ // rediscover unknown transactions that were written with keys of ours to recover
+ // post-backup change.
+
+ // Reserve a new key pair from key pool. If it fails, provide a dummy
+ // destination in case we don't need change.
+ CTxDestination dest;
+ if (!reservedest.GetReservedDestination(dest, true)) {
+ error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.");
+ }
+ scriptChange = GetScriptForDestination(dest);
+ // A valid destination implies a change script (and
+ // vice-versa). An empty change script will abort later, if the
+ // change keypool ran out, but change is required.
+ CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty());
}
- if (vecSend.empty())
- {
- error = _("Transaction must have at least one recipient");
- return false;
+ CTxOut change_prototype_txout(0, scriptChange);
+ coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
+
+ // Get size of spending the change output
+ int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
+ // as lower-bound to allow BnB to do it's thing
+ if (change_spend_size == -1) {
+ coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
+ } else {
+ coin_selection_params.change_spend_size = (size_t)change_spend_size;
}
- CMutableTransaction txNew;
+ // Set discard feerate
+ coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
+
+ // Get the fee rate to use effective values in coin selection
FeeCalculation feeCalc;
- TxSize tx_sizes;
- int nBytes;
+ coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc);
+ // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
+ // provided one
+ if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
+ error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
+ return false;
+ }
+ if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
+ // eventually allow a fallback fee
+ error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
+ return false;
+ }
+
+ // Get long term estimate
+ CCoinControl cc_temp;
+ cc_temp.m_confirm_target = chain().estimateMaxBlocks();
+ coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr);
+
+ // Calculate the cost of change
+ // Cost of change is the cost of creating the change output + cost of spending the change output in the future.
+ // For creating the change output now, we use the effective feerate.
+ // For spending the change output in the future, we use the discard feerate for now.
+ // So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate)
+ coin_selection_params.m_change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
+ coin_selection_params.m_cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_change_fee;
+
+ // vouts to the payees
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
+ }
+ for (const auto& recipient : vecSend)
{
- std::set<CInputCoin> setCoins;
- LOCK(cs_wallet);
- txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
- {
- std::vector<COutput> vAvailableCoins;
- AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
- CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
- coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
-
- // Create change script that will be used if we need change
- // TODO: pass in scriptChange instead of reservedest so
- // change transaction isn't always pay-to-bitcoin-address
- CScript scriptChange;
-
- // coin control: send change to custom address
- if (!std::get_if<CNoDestination>(&coin_control.destChange)) {
- scriptChange = GetScriptForDestination(coin_control.destChange);
- } else { // no coin control: send change to newly generated address
- // Note: We use a new key here to keep it from being obvious which side is the change.
- // The drawback is that by not reusing a previous key, the change may be lost if a
- // backup is restored, if the backup doesn't have the new private key for the change.
- // If we reused the old key, it would be possible to add code to look for and
- // rediscover unknown transactions that were written with keys of ours to recover
- // post-backup change.
-
- // Reserve a new key pair from key pool. If it fails, provide a dummy
- // destination in case we don't need change.
- CTxDestination dest;
- if (!reservedest.GetReservedDestination(dest, true)) {
- error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.");
- }
- scriptChange = GetScriptForDestination(dest);
- // A valid destination implies a change script (and
- // vice-versa). An empty change script will abort later, if the
- // change keypool ran out, but change is required.
- CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty());
- }
- CTxOut change_prototype_txout(0, scriptChange);
- coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
-
- // Get size of spending the change output
- int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
- // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
- // as lower-bound to allow BnB to do it's thing
- if (change_spend_size == -1) {
- coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
- } else {
- coin_selection_params.change_spend_size = (size_t)change_spend_size;
- }
+ CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
- // Set discard feerate
- coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
+ // Include the fee cost for outputs.
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
+ }
- // Get the fee rate to use effective values in coin selection
- coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc);
- // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
- // provided one
- if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
- error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
- return false;
- }
- if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
- // eventually allow a fallback fee
- error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
- return false;
- }
+ if (IsDust(txout, chain().relayDustFee()))
+ {
+ error = _("Transaction amount too small");
+ return false;
+ }
+ txNew.vout.push_back(txout);
+ }
- // Get long term estimate
- CCoinControl cc_temp;
- cc_temp.m_confirm_target = chain().estimateMaxBlocks();
- coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr);
+ // Include the fees for things that aren't inputs, excluding the change output
+ const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
+ CAmount selection_target = recipients_sum + not_input_fees;
- // Calculate the cost of change
- // Cost of change is the cost of creating the change output + cost of spending the change output in the future.
- // For creating the change output now, we use the effective feerate.
- // For spending the change output in the future, we use the discard feerate for now.
- // So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate)
- coin_selection_params.m_change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
- coin_selection_params.m_cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_change_fee;
+ // Get available coins
+ std::vector<COutput> vAvailableCoins;
+ AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
- coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values
+ // Choose coins to use
+ CAmount inputs_sum = 0;
+ std::set<CInputCoin> setCoins;
+ if (!SelectCoins(vAvailableCoins, /* nTargetValue */ selection_target, setCoins, inputs_sum, coin_control, coin_selection_params))
+ {
+ error = _("Insufficient funds");
+ return false;
+ }
- // vouts to the payees
- if (!coin_selection_params.m_subtract_fee_outputs) {
- coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
- }
- for (const auto& recipient : vecSend)
- {
- CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
+ // Always make a change output
+ // We will reduce the fee from this change output later, and remove the output if it is too small.
+ const CAmount change_and_fee = inputs_sum - recipients_sum;
+ assert(change_and_fee >= 0);
+ CTxOut newTxOut(change_and_fee, scriptChange);
- // Include the fee cost for outputs.
- if (!coin_selection_params.m_subtract_fee_outputs) {
- coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
- }
+ if (nChangePosInOut == -1)
+ {
+ // Insert change txn at random position:
+ nChangePosInOut = GetRandInt(txNew.vout.size()+1);
+ }
+ else if ((unsigned int)nChangePosInOut > txNew.vout.size())
+ {
+ error = _("Change index out of range");
+ return false;
+ }
- if (IsDust(txout, chain().relayDustFee()))
- {
- error = _("Transaction amount too small");
- return false;
- }
- txNew.vout.push_back(txout);
- }
+ assert(nChangePosInOut != -1);
+ auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
- // Include the fees for things that aren't inputs, excluding the change output
- const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
- CAmount nValueToSelect = nValue + not_input_fees;
+ // Shuffle selected coins and fill in final vin
+ std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
+ Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
- // Choose coins to use
- CAmount inputs_sum = 0;
- setCoins.clear();
- if (!SelectCoins(vAvailableCoins, /* nTargetValue */ nValueToSelect, setCoins, inputs_sum, coin_control, coin_selection_params))
- {
- error = _("Insufficient funds");
- return false;
- }
-
- // Always make a change output
- // We will reduce the fee from this change output later, and remove the output if it is too small.
- const CAmount change_and_fee = inputs_sum - nValue;
- assert(change_and_fee >= 0);
- CTxOut newTxOut(change_and_fee, scriptChange);
+ // Note how the sequence number is set to non-maxint so that
+ // the nLockTime set above actually works.
+ //
+ // BIP125 defines opt-in RBF as any nSequence < maxint-1, so
+ // we use the highest possible value in that range (maxint-2)
+ // to avoid conflicting with other possible uses of nSequence,
+ // and in the spirit of "smallest possible change from prior
+ // behavior."
+ const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
+ for (const auto& coin : selected_coins) {
+ txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
+ }
- if (nChangePosInOut == -1)
- {
- // Insert change txn at random position:
- nChangePosInOut = GetRandInt(txNew.vout.size()+1);
- }
- else if ((unsigned int)nChangePosInOut > txNew.vout.size())
- {
- error = _("Change index out of range");
- return false;
- }
+ // Calculate the transaction fee
+ TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ int nBytes = tx_sizes.vsize;
+ if (nBytes < 0) {
+ error = _("Signing transaction failed");
+ return false;
+ }
+ nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
- assert(nChangePosInOut != -1);
- auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
+ // Subtract fee from the change output if not subtracting it from recipient outputs
+ CAmount fee_needed = nFeeRet;
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ change_position->nValue -= fee_needed;
+ }
- // Dummy fill vin for maximum size estimation
- //
- for (const auto& coin : setCoins) {
- txNew.vin.push_back(CTxIn(coin.outpoint,CScript()));
- }
+ // We want to drop the change to fees if:
+ // 1. The change output would be dust
+ // 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change)
+ CAmount change_amount = change_position->nValue;
+ if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change)
+ {
+ nChangePosInOut = -1;
+ change_amount = 0;
+ txNew.vout.erase(change_position);
+
+ // Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
+ tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ nBytes = tx_sizes.vsize;
+ fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
+ }
- // Calculate the transaction fee
- tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
- nBytes = tx_sizes.vsize;
- if (nBytes < 0) {
- error = _("Signing transaction failed");
- return false;
- }
- nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
+ // Update nFeeRet in case fee_needed changed due to dropping the change output
+ if (fee_needed <= change_and_fee - change_amount) {
+ nFeeRet = change_and_fee - change_amount;
+ }
- // Subtract fee from the change output if not subtrating it from recipient outputs
- CAmount fee_needed = nFeeRet;
- if (nSubtractFeeFromAmount == 0) {
- change_position->nValue -= fee_needed;
+ // Reduce output values for subtractFeeFromAmount
+ if (coin_selection_params.m_subtract_fee_outputs) {
+ CAmount to_reduce = fee_needed + change_amount - change_and_fee;
+ int i = 0;
+ bool fFirst = true;
+ for (const auto& recipient : vecSend)
+ {
+ if (i == nChangePosInOut) {
+ ++i;
}
+ CTxOut& txout = txNew.vout[i];
- // We want to drop the change to fees if:
- // 1. The change output would be dust
- // 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change)
- CAmount change_amount = change_position->nValue;
- if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change)
+ if (recipient.fSubtractFeeFromAmount)
{
- nChangePosInOut = -1;
- change_amount = 0;
- txNew.vout.erase(change_position);
-
- // Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
- tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
- nBytes = tx_sizes.vsize;
- fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
- }
+ txout.nValue -= to_reduce / outputs_to_subtract_fee_from; // Subtract fee equally from each selected recipient
- // Update nFeeRet in case fee_needed changed due to dropping the change output
- if (fee_needed <= change_and_fee - change_amount) {
- nFeeRet = change_and_fee - change_amount;
- }
-
- // Reduce output values for subtractFeeFromAmount
- if (nSubtractFeeFromAmount != 0) {
- CAmount to_reduce = fee_needed + change_amount - change_and_fee;
- int i = 0;
- bool fFirst = true;
- for (const auto& recipient : vecSend)
+ if (fFirst) // first receiver pays the remainder not divisible by output count
{
- if (i == nChangePosInOut) {
- ++i;
- }
- CTxOut& txout = txNew.vout[i];
-
- if (recipient.fSubtractFeeFromAmount)
- {
- txout.nValue -= to_reduce / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
-
- if (fFirst) // first receiver pays the remainder not divisible by output count
- {
- fFirst = false;
- txout.nValue -= to_reduce % nSubtractFeeFromAmount;
- }
-
- // Error if this output is reduced to be below dust
- if (IsDust(txout, chain().relayDustFee())) {
- if (txout.nValue < 0) {
- error = _("The transaction amount is too small to pay the fee");
- } else {
- error = _("The transaction amount is too small to send after the fee has been deducted");
- }
- return false;
- }
- }
- ++i;
+ fFirst = false;
+ txout.nValue -= to_reduce % outputs_to_subtract_fee_from;
}
- nFeeRet = fee_needed;
- }
- // Give up if change keypool ran out and change is required
- if (scriptChange.empty() && nChangePosInOut != -1) {
- return false;
+ // Error if this output is reduced to be below dust
+ if (IsDust(txout, chain().relayDustFee())) {
+ if (txout.nValue < 0) {
+ error = _("The transaction amount is too small to pay the fee");
+ } else {
+ error = _("The transaction amount is too small to send after the fee has been deducted");
+ }
+ return false;
+ }
}
+ ++i;
}
+ nFeeRet = fee_needed;
+ }
- // Shuffle selected coins and fill in final vin
- txNew.vin.clear();
- std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
- Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
-
- // Note how the sequence number is set to non-maxint so that
- // the nLockTime set above actually works.
- //
- // BIP125 defines opt-in RBF as any nSequence < maxint-1, so
- // we use the highest possible value in that range (maxint-2)
- // to avoid conflicting with other possible uses of nSequence,
- // and in the spirit of "smallest possible change from prior
- // behavior."
- const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
- for (const auto& coin : selected_coins) {
- txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
- }
+ // Give up if change keypool ran out and change is required
+ if (scriptChange.empty() && nChangePosInOut != -1) {
+ return false;
+ }
- if (sign && !SignTransaction(txNew)) {
- error = _("Signing transaction failed");
- return false;
- }
+ if (sign && !SignTransaction(txNew)) {
+ error = _("Signing transaction failed");
+ return false;
+ }
- // Return the constructed transaction data.
- tx = MakeTransactionRef(std::move(txNew));
+ // Return the constructed transaction data.
+ tx = MakeTransactionRef(std::move(txNew));
- // Limit size
- if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
- (!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
- {
- error = _("Transaction too large");
- return false;
- }
+ // Limit size
+ if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
+ (!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
+ {
+ error = _("Transaction too large");
+ return false;
}
if (nFeeRet > m_default_max_tx_fee) {
@@ -900,6 +879,18 @@ bool CWallet::CreateTransaction(
FeeCalculation& fee_calc_out,
bool sign)
{
+ if (vecSend.empty()) {
+ error = _("Transaction must have at least one recipient");
+ return false;
+ }
+
+ if (std::any_of(vecSend.cbegin(), vecSend.cend(), [](const auto& recipient){ return recipient.nAmount < 0; })) {
+ error = _("Transaction amounts must not be negative");
+ return false;
+ }
+
+ LOCK(cs_wallet);
+
int nChangePosIn = nChangePosInOut;
Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 14c3578473..c65ebad52f 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -270,7 +270,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT, selection, value_ret));
}
- // Make sure that effective value is working in SelectCoinsMinConf when BnB is used
+ // Make sure that effective value is working in AttemptSelection when BnB is used
CoinSelectionParams coin_selection_params_bnb(/* change_output_size= */ 0,
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
/* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
@@ -280,14 +280,14 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
empty_wallet();
add_coin(1);
vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
+ BOOST_CHECK(!testWallet.AttemptSelection( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
// Test fees subtracted from output:
empty_wallet();
add_coin(1 * CENT);
vCoins.at(0).nInputBytes = 40;
coin_selection_params_bnb.m_subtract_fee_outputs = true;
- BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
+ BOOST_CHECK(testWallet.AttemptSelection( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params_bnb));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
// Make sure that can use BnB when there are preset inputs
@@ -322,24 +322,24 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
empty_wallet();
// with an empty wallet we can't even pay one cent
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
add_coin(1*CENT, 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection( 1 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// but we can find a new 1 cent
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 1 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
add_coin(2*CENT); // add a mature 2 cent coin
// we can't make 3 cents of mature coins
- BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection( 3 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// we can make 3 cents of new coins
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 3 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
add_coin(5*CENT); // add a mature 5 cent coin,
@@ -349,33 +349,33 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
// we can't make 38 cents only if we disallow new coins:
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection(38 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// we can't even make 37 cents if we don't allow new coins even if they're from us
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection(38 * CENT, filter_standard_extra, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// but we can make 37 cents if we accept new coins from ourself
- BOOST_CHECK( testWallet.SelectCoinsMinConf(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(37 * CENT, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
// and we can make 38 cents if we accept all new coins
- BOOST_CHECK( testWallet.SelectCoinsMinConf(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(38 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
- BOOST_CHECK( testWallet.SelectCoinsMinConf(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(34 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
// when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 7 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 8 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK(nValueRet == 8 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
- BOOST_CHECK( testWallet.SelectCoinsMinConf( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection( 9 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -389,30 +389,30 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
// check that we have 71 and not 72
- BOOST_CHECK( testWallet.SelectCoinsMinConf(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
- BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(71 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(!testWallet.AttemptSelection(72 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
// now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
// now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30
// and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
- BOOST_CHECK( testWallet.SelectCoinsMinConf(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(16 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins
// now try making 11 cents. we should get 5+6
- BOOST_CHECK( testWallet.SelectCoinsMinConf(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(11 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -421,11 +421,11 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin( 2*COIN);
add_coin( 3*COIN);
add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
- BOOST_CHECK( testWallet.SelectCoinsMinConf(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(95 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(195 * CENT, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -440,14 +440,14 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
- BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
// but if we add a bigger coin, small change is avoided
add_coin(1111*MIN_CHANGE);
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// if we add more small coins:
@@ -455,7 +455,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 7 / 10);
// and try again to make 1.0 * MIN_CHANGE
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// run the 'mtgox' test (see https://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
@@ -464,7 +464,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
for (int j = 0; j < 20; j++)
add_coin(50000 * COIN);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(500000 * COIN, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
@@ -477,7 +477,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 7 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(1 * MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -487,7 +487,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 8 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( testWallet.SelectCoinsMinConf(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK( testWallet.AttemptSelection(MIN_CHANGE, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
@@ -498,12 +498,12 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
add_coin(MIN_CHANGE * 100);
// trying to make 100.01 from these three coins
- BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(testWallet.AttemptSelection(MIN_CHANGE * 10001 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
- BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(testWallet.AttemptSelection(MIN_CHANGE * 9990 / 100, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
}
@@ -517,7 +517,7 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
// We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
for (int i = 0; i < RUN_TESTS; i++) {
- BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(testWallet.AttemptSelection(2000, filter_confirmed, vCoins, setCoinsRet, nValueRet, coin_selection_params));
if (amt - 2000 < MIN_CHANGE) {
// needs more than one input:
@@ -602,7 +602,7 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
add_coin(1000 * COIN);
add_coin(3 * COIN);
- BOOST_CHECK(testWallet.SelectCoinsMinConf(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
+ BOOST_CHECK(testWallet.AttemptSelection(1003 * COIN, filter_standard, vCoins, setCoinsRet, nValueRet, coin_selection_params));
BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 34bb29f79f..a0070b8dd3 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -83,17 +83,17 @@ static void AddKey(CWallet& wallet, const CKey& key)
BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = ::ChainActive().Tip();
+ CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = ::ChainActive().Tip();
+ CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -112,7 +112,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -138,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -163,7 +163,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@@ -180,10 +180,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
{
// Cap last block file size, and mine new block in a new block file.
- CBlockIndex* oldTip = ::ChainActive().Tip();
+ CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- CBlockIndex* newTip = ::ChainActive().Tip();
+ CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Prune the older block file.
{
@@ -242,7 +242,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
- const int64_t BLOCK_TIME = ::ChainActive().Tip()->GetBlockTimeMax() + 5;
+ const int64_t BLOCK_TIME = m_node.chainman->ActiveChain().Tip()->GetBlockTimeMax() + 5;
SetMockTime(BLOCK_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
@@ -265,7 +265,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
AddWallet(wallet);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
JSONRPCRequest request;
request.params.setArray();
@@ -286,7 +286,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(backup_file);
AddWallet(wallet);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
::importwallet().HandleRequest(request);
RemoveWallet(wallet, std::nullopt);
@@ -313,9 +313,9 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
CWalletTx wtx(&wallet, m_coinbase_txns.back());
LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
- wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 0);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 0);
wtx.m_confirm = confirm;
// Call GetImmatureCredit() once before adding the key to the wallet to
@@ -385,11 +385,11 @@ BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
CTxDestination dest = PKHash();
LOCK(m_wallet.cs_wallet);
WalletBatch batch{m_wallet.GetDatabase()};
- m_wallet.AddDestData(batch, dest, "misc", "val_misc");
- m_wallet.AddDestData(batch, dest, "rr0", "val_rr0");
- m_wallet.AddDestData(batch, dest, "rr1", "val_rr1");
+ m_wallet.SetAddressUsed(batch, dest, true);
+ m_wallet.SetAddressReceiveRequest(batch, dest, "0", "val_rr0");
+ m_wallet.SetAddressReceiveRequest(batch, dest, "1", "val_rr1");
- auto values = m_wallet.GetDestValues("rr");
+ auto values = m_wallet.GetAddressReceiveRequests();
BOOST_CHECK_EQUAL(values.size(), 2U);
BOOST_CHECK_EQUAL(values[0], "val_rr0");
BOOST_CHECK_EQUAL(values[1], "val_rr1");
@@ -483,16 +483,16 @@ public:
wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
- wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
wallet->LoadWallet();
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(*wallet);
reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(m_node.chainman->ActiveChain().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK_EQUAL(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.last_scanned_height, ::ChainActive().Height());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, m_node.chainman->ActiveChain().Height());
BOOST_CHECK(result.last_failed_block.IsNull());
}
@@ -521,10 +521,10 @@ public:
CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
LOCK(wallet->cs_wallet);
- wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, ::ChainActive().Tip()->GetBlockHash());
+ wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 1);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash(), 1);
it->second.m_confirm = confirm;
return it->second;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 9986a02fc1..4b6630de3c 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -814,12 +814,11 @@ void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned
CTxDestination dst;
if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) {
if (IsMine(dst)) {
- if (used && !GetDestData(dst, "used", nullptr)) {
- if (AddDestData(batch, dst, "used", "p")) { // p for "present", opposite of absent (null)
+ if (used != IsAddressUsed(dst)) {
+ if (used) {
tx_destinations.insert(dst);
}
- } else if (!used && GetDestData(dst, "used", nullptr)) {
- EraseDestData(batch, dst, "used");
+ SetAddressUsed(batch, dst, used);
}
}
}
@@ -835,7 +834,7 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
if (!ExtractDestination(srctx->tx->vout[n].scriptPubKey, dest)) {
return false;
}
- if (GetDestData(dest, "used", nullptr)) {
+ if (IsAddressUsed(dest)) {
return true;
}
if (IsLegacy()) {
@@ -843,15 +842,15 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
assert(spk_man != nullptr);
for (const auto& keyid : GetAffectedKeys(srctx->tx->vout[n].scriptPubKey, *spk_man)) {
WitnessV0KeyHash wpkh_dest(keyid);
- if (GetDestData(wpkh_dest, "used", nullptr)) {
+ if (IsAddressUsed(wpkh_dest)) {
return true;
}
ScriptHash sh_wpkh_dest(GetScriptForDestination(wpkh_dest));
- if (GetDestData(sh_wpkh_dest, "used", nullptr)) {
+ if (IsAddressUsed(sh_wpkh_dest)) {
return true;
}
PKHash pkh_dest(keyid);
- if (GetDestData(pkh_dest, "used", nullptr)) {
+ if (IsAddressUsed(pkh_dest)) {
return true;
}
}
@@ -2387,45 +2386,45 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
return nTimeSmart;
}
-bool CWallet::AddDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key, const std::string &value)
+bool CWallet::SetAddressUsed(WalletBatch& batch, const CTxDestination& dest, bool used)
{
+ const std::string key{"used"};
if (std::get_if<CNoDestination>(&dest))
return false;
+ if (!used) {
+ if (auto* data = util::FindKey(m_address_book, dest)) data->destdata.erase(key);
+ return batch.EraseDestData(EncodeDestination(dest), key);
+ }
+
+ const std::string value{"1"};
m_address_book[dest].destdata.insert(std::make_pair(key, value));
return batch.WriteDestData(EncodeDestination(dest), key, value);
}
-bool CWallet::EraseDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key)
-{
- if (!m_address_book[dest].destdata.erase(key))
- return false;
- return batch.EraseDestData(EncodeDestination(dest), key);
-}
-
void CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
{
m_address_book[dest].destdata.insert(std::make_pair(key, value));
}
-bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const
+bool CWallet::IsAddressUsed(const CTxDestination& dest) const
{
+ const std::string key{"used"};
std::map<CTxDestination, CAddressBookData>::const_iterator i = m_address_book.find(dest);
if(i != m_address_book.end())
{
CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key);
if(j != i->second.destdata.end())
{
- if(value)
- *value = j->second;
return true;
}
}
return false;
}
-std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
+std::vector<std::string> CWallet::GetAddressReceiveRequests() const
{
+ const std::string prefix{"rr"};
std::vector<std::string> values;
for (const auto& address : m_address_book) {
for (const auto& data : address.second.destdata) {
@@ -2437,6 +2436,20 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
+bool CWallet::SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value)
+{
+ const std::string key{"rr" + id}; // "rr" prefix = "receive request" in destdata
+ CAddressBookData& data = m_address_book.at(dest);
+ if (value.empty()) {
+ if (!batch.EraseDestData(EncodeDestination(dest), key)) return false;
+ data.destdata.erase(key);
+ } else {
+ if (!batch.WriteDestData(EncodeDestination(dest), key, value)) return false;
+ data.destdata[key] = value;
+ }
+ return true;
+}
+
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
{
// Do some checking on wallet path. It should be either a:
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 4a3e0f7054..d0e26c416c 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -326,7 +326,7 @@ private:
// ScriptPubKeyMan::GetID. In many cases it will be the hash of an internal structure
std::map<uint256, std::unique_ptr<ScriptPubKeyMan>> m_spk_managers;
- bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign);
+ bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Catch wallet up to current chain, scanning new blocks, updating the best
@@ -445,7 +445,7 @@ public:
* param@[out] setCoinsRet Populated with the coins selected if successful.
* param@[out] nValueRet Used to return the total value of selected coins.
*/
- bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
+ bool AttemptSelection(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const;
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -479,19 +479,8 @@ public:
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; return true; }
- /**
- * Adds a destination data tuple to the store, and saves it to disk
- * When adding new fields, take care to consider how DelAddressBook should handle it!
- */
- bool AddDestData(WalletBatch& batch, const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- //! Erases a destination data tuple in the store and on disk
- bool EraseDestData(WalletBatch& batch, const CTxDestination& dest, const std::string& key) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a destination data tuple to the store, without saving it to disk
void LoadDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- //! Look up a destination data tuple in the store, return true if found false otherwise
- bool GetDestData(const CTxDestination& dest, const std::string& key, std::string* value) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- //! Get all destination values matching a prefix.
- std::vector<std::string> GetDestValues(const std::string& prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock().
int64_t nRelockTime GUARDED_BY(cs_wallet){0};
@@ -705,6 +694,12 @@ public:
bool DelAddressBook(const CTxDestination& address);
+ bool IsAddressUsed(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool SetAddressUsed(WalletBatch& batch, const CTxDestination& dest, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ std::vector<std::string> GetAddressReceiveRequests() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
unsigned int GetKeyPoolSize() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! signify that a particular wallet feature is now used.
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index c06b319b0b..24d5351945 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -712,6 +712,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
}
}
+#ifndef ENABLE_EXTERNAL_SIGNER
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
+ pwallet->WalletLogPrintf("Error: External signer wallet being loaded without external signer support compiled\n");
+ return DBErrors::TOO_NEW;
+ }
+#endif
+
// Get cursor
if (!m_batch->StartCursor())
{
diff --git a/src/zmq/zmqutil.cpp b/src/zmq/zmqutil.cpp
index f07a4ae9fd..b0f12388e5 100644
--- a/src/zmq/zmqutil.cpp
+++ b/src/zmq/zmqutil.cpp
@@ -5,10 +5,12 @@
#include <zmq/zmqutil.h>
#include <logging.h>
-
#include <zmq.h>
-void zmqError(const char* str)
+#include <cerrno>
+#include <string>
+
+void zmqError(const std::string& str)
{
- LogPrint(BCLog::ZMQ, "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno));
+ LogPrint(BCLog::ZMQ, "zmq: Error: %s, msg: %s\n", str, zmq_strerror(errno));
}
diff --git a/src/zmq/zmqutil.h b/src/zmq/zmqutil.h
index 4c1df5d6db..90c0b00edb 100644
--- a/src/zmq/zmqutil.h
+++ b/src/zmq/zmqutil.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_ZMQ_ZMQUTIL_H
#define BITCOIN_ZMQ_ZMQUTIL_H
-void zmqError(const char* str);
+#include <string>
+
+void zmqError(const std::string& str);
#endif // BITCOIN_ZMQ_ZMQUTIL_H