diff options
Diffstat (limited to 'src')
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); } } @@ -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>&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>&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>&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 *δ* 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 *δ=1 f=21 g=14* +- Take the third branch: *δ=2 f=21 g=7* +- Take the first branch: *δ=-1 f=7 g=-7* +- Take the second branch: *δ=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 c* and *g=b c* for some integers *a* + and *b*. As *(g,g-f)=(b c,(b-a)c)* and *(f,f+g)=(a 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 *δ* 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 *δ*. + + +## 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 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 x + b M = 1*. +Taking that expression *mod M* gives *a 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 [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 *δ* 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 M* and *ce* by *q M*. Similarly, +increasing *e* by *M* is equal to incrementing *cd* by *v M* and *ce* by *r 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 M* and *me 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 ≠ 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 +*⌈724/N⌉* 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 *δ>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 +*η=-δ*. In this representation, negating *δ* corresponds to negating *η*, and incrementing +*δ* corresponds to decrementing *η*. 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 *δ* 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 *ζ=-(δ+1/2)* +is used instead to keep the variable integral. Incrementing *δ* by *1* still translates to +decrementing *ζ* by *1*, but negating *δ* now corresponds to going from *ζ* to *-(ζ+1)*, or +*~ζ*. 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 *δ=1* and *η=-δ* 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 *η*. 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 *η ≥ 0*, the loop does not modify *f*. Instead, it cancels out bottom +bits of *g* and shifts them out, and decreases *η* and *i* accordingly - interrupting only when *η* +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 f* has a few bottom +zero bits. If that number of bits is *L*, we want *g+w 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 *η+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 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 *ζ* instead of *δ*, 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 |