Merge #13644: 0.16: Remaining backports for 0.16.2

d9c563095d qa: Initialize lockstack to prevent null pointer deref (MarcoFalke)
21dd5127a4 gui: Show messages as text not html (Wladimir J. van der Laan)
f78e7f6589 [qt] send: Clear All also resets coin control options (Sjors Provoost)
657dfc5bca Fix csBestBlock/cvBlockChange waiting in rpc/mining (Pieter Wuille)
88d1a649a2 Consensus: Fix bug when compiler do not support __builtin_clz* (532479301)
b72c0bd4c9 [qa] Add a test for merkle proof malleation (Suhas Daftuar)
6b9dc8ceae have verifytxoutproof check the number of txns in proof structure (Gregory Sanders)
ed82e7176d wallet: Erase wtxOrderd wtx pointer on removeprunedfunds (MarcoFalke)
e15e3a9ddd Remove boost dependency (boost/assign/std/vector.hpp) (practicalswift)
10621995ed Fix incorrect tests (practicalswift)
170b309981 Make tests pass after 2020 (Bernhard M. Wiedemann)
cfc6f7413b [rpcwallet] Clamp walletpassphrase value at 100M seconds (Suhas Daftuar)
bf1f150190 rpc: fix type mistmatch in listreceivedbyaddress (joemphilips)
2291774bd5 [trivial] Add newlines to end of log messages. (John Newbery)
cf6feb7837 qt: Avoid resetting on resetguisettigs=0 (MarcoFalke)
cbdabef35e qa: Fix wallet_listreceivedby race (MarcoFalke)
79c4fff9ed [tests] Fixed intermittent failure in p2p_sendheaders.py. (lmanners)
c04a4a5ae9 Remove useless mapRequest tracking that just effects Qt display. (Matt Corallo)

Pull request description:

  Some gui/doc/rpc/qa backports

Tree-SHA512: f1e918d2ca6016fc7c5d5adf5d537553a1769731e2dcac788edf02c7354387861ec5bcd0cbf3c833c1485d500c55a73a2799a8b39ed14477524ac46b4ff2332d
This commit is contained in:
Wladimir J. van der Laan 2018-07-16 14:18:31 +02:00
commit 7f27af22b0
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
29 changed files with 211 additions and 209 deletions

View File

@ -82,12 +82,12 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x)
/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ /** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */
uint64_t static inline CountBits(uint64_t x) uint64_t static inline CountBits(uint64_t x)
{ {
#ifdef HAVE_DECL___BUILTIN_CLZL #if HAVE_DECL___BUILTIN_CLZL
if (sizeof(unsigned long) >= sizeof(uint64_t)) { if (sizeof(unsigned long) >= sizeof(uint64_t)) {
return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0;
} }
#endif #endif
#ifdef HAVE_DECL___BUILTIN_CLZLL #if HAVE_DECL___BUILTIN_CLZLL
if (sizeof(unsigned long long) >= sizeof(uint64_t)) { if (sizeof(unsigned long long) >= sizeof(uint64_t)) {
return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0;
} }

View File

@ -684,7 +684,7 @@ void ThreadImport(std::vector<fs::path> vImportFiles)
// scan for better chains in the block chain database, that are not yet connected in the active best chain // scan for better chains in the block chain database, that are not yet connected in the active best chain
CValidationState state; CValidationState state;
if (!ActivateBestChain(state, chainparams)) { if (!ActivateBestChain(state, chainparams)) {
LogPrintf("Failed to connect best block"); LogPrintf("Failed to connect best block\n");
StartShutdown(); StartShutdown();
return; return;
} }

View File

@ -115,6 +115,12 @@ public:
* returns the merkle root, or 0 in case of failure * returns the merkle root, or 0 in case of failure
*/ */
uint256 ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex); uint256 ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex);
/** Get number of transactions the merkle proof is indicating for cross-reference with
* local blockchain knowledge.
*/
unsigned int GetNumTransactions() const { return nTransactions; };
}; };

View File

@ -1221,9 +1221,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
if (!push) { if (!push) {
vNotFound.push_back(inv); vNotFound.push_back(inv);
} }
// Track requests for our stuff.
GetMainSignals().Inventory(inv.hash);
} }
} // release cs_main } // release cs_main
@ -1908,9 +1905,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->AskFor(inv); pfrom->AskFor(inv);
} }
} }
// Track requests for our stuff
GetMainSignals().Inventory(inv.hash);
} }
} }

View File

@ -689,7 +689,7 @@ int main(int argc, char *argv[])
// Allow parameter interaction before we create the options model // Allow parameter interaction before we create the options model
app.parameterSetup(); app.parameterSetup();
// Load GUI settings from QSettings // Load GUI settings from QSettings
app.createOptionsModel(gArgs.IsArgSet("-resetguisettings")); app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
// Subscribe to global signals from core // Subscribe to global signals from core
uiInterface.InitMessage.connect(InitMessage); uiInterface.InitMessage.connect(InitMessage);

View File

@ -923,6 +923,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned
showNormalIfMinimized(); showNormalIfMinimized();
QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this); QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this);
mBox.setTextFormat(Qt::PlainText);
int r = mBox.exec(); int r = mBox.exec();
if (ret != nullptr) if (ret != nullptr)
*ret = r == QMessageBox::Ok; *ret = r == QMessageBox::Ok;

View File

@ -27,8 +27,6 @@ static const bool DEFAULT_SPLASHSCREEN = true;
#define COLOR_BAREADDRESS QColor(140, 140, 140) #define COLOR_BAREADDRESS QColor(140, 140, 140)
/* Transaction list -- TX status decoration - open until date */ /* Transaction list -- TX status decoration - open until date */
#define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255) #define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255)
/* Transaction list -- TX status decoration - offline */
#define COLOR_TX_STATUS_OFFLINE QColor(192, 192, 192)
/* Transaction list -- TX status decoration - danger, tx needs attention */ /* Transaction list -- TX status decoration - danger, tx needs attention */
#define COLOR_TX_STATUS_DANGER QColor(200, 100, 100) #define COLOR_TX_STATUS_DANGER QColor(200, 100, 100)
/* Transaction list -- TX status decoration - default color */ /* Transaction list -- TX status decoration - default color */

View File

@ -375,6 +375,12 @@ void SendCoinsDialog::on_sendButton_clicked()
void SendCoinsDialog::clear() void SendCoinsDialog::clear()
{ {
// Clear coin control settings
CoinControlDialog::coinControl()->UnSelectAll();
ui->checkBoxCoinControlChange->setChecked(false);
ui->lineEditCoinControlChange->clear();
coinControlUpdateLabels();
// Remove entries until only one left // Remove entries until only one left
while(ui->entries->count()) while(ui->entries->count())
{ {

View File

@ -36,8 +36,6 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
int nDepth = wtx.GetDepthInMainChain(); int nDepth = wtx.GetDepthInMainChain();
if (nDepth < 0) if (nDepth < 0)
return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth); return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth);
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
return tr("%1/offline").arg(nDepth);
else if (nDepth == 0) else if (nDepth == 0)
return tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx.isAbandoned() ? ", "+tr("abandoned") : ""); return tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx.isAbandoned() ? ", "+tr("abandoned") : "");
else if (nDepth < 6) else if (nDepth < 6)
@ -61,14 +59,6 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
CAmount nNet = nCredit - nDebit; CAmount nNet = nCredit - nDebit;
strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx); strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx);
int nRequests = wtx.GetRequestCount();
if (nRequests != -1)
{
if (nRequests == 0)
strHTML += tr(", has not been successfully broadcast yet");
else if (nRequests > 0)
strHTML += tr(", broadcast through %n node(s)", "", nRequests);
}
strHTML += "<br>"; strHTML += "<br>";
strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>"; strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>";

View File

@ -205,10 +205,6 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
if (wtx.IsInMainChain()) if (wtx.IsInMainChain())
{ {
status.matures_in = wtx.GetBlocksToMaturity(); status.matures_in = wtx.GetBlocksToMaturity();
// Check if the block was requested by anyone
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
status.status = TransactionStatus::MaturesWarning;
} }
else else
{ {
@ -226,10 +222,6 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
{ {
status.status = TransactionStatus::Conflicted; status.status = TransactionStatus::Conflicted;
} }
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
{
status.status = TransactionStatus::Offline;
}
else if (status.depth == 0) else if (status.depth == 0)
{ {
status.status = TransactionStatus::Unconfirmed; status.status = TransactionStatus::Unconfirmed;

View File

@ -21,7 +21,7 @@ class TransactionStatus
public: public:
TransactionStatus(): TransactionStatus():
countsForBalance(false), sortKey(""), countsForBalance(false), sortKey(""),
matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) matures_in(0), status(Unconfirmed), depth(0), open_for(0), cur_num_blocks(-1)
{ } { }
enum Status { enum Status {
@ -29,14 +29,12 @@ public:
/// Normal (sent/received) transactions /// Normal (sent/received) transactions
OpenUntilDate, /**< Transaction not yet final, waiting for date */ OpenUntilDate, /**< Transaction not yet final, waiting for date */
OpenUntilBlock, /**< Transaction not yet final, waiting for block */ OpenUntilBlock, /**< Transaction not yet final, waiting for block */
Offline, /**< Not sent to any other nodes **/
Unconfirmed, /**< Not yet mined into a block **/ Unconfirmed, /**< Not yet mined into a block **/
Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/ Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/
Conflicted, /**< Conflicts with other transaction or mempool **/ Conflicted, /**< Conflicts with other transaction or mempool **/
Abandoned, /**< Abandoned from the wallet **/ Abandoned, /**< Abandoned from the wallet **/
/// Generated (mined) transactions /// Generated (mined) transactions
Immature, /**< Mined but waiting for maturity */ Immature, /**< Mined but waiting for maturity */
MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */
NotAccepted /**< Mined but not accepted */ NotAccepted /**< Mined but not accepted */
}; };

View File

@ -308,9 +308,6 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
case TransactionStatus::OpenUntilDate: case TransactionStatus::OpenUntilDate:
status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for)); status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
break; break;
case TransactionStatus::Offline:
status = tr("Offline");
break;
case TransactionStatus::Unconfirmed: case TransactionStatus::Unconfirmed:
status = tr("Unconfirmed"); status = tr("Unconfirmed");
break; break;
@ -329,9 +326,6 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
case TransactionStatus::Immature: case TransactionStatus::Immature:
status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in); status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in);
break; break;
case TransactionStatus::MaturesWarning:
status = tr("This block was not received by any other nodes and will probably not be accepted!");
break;
case TransactionStatus::NotAccepted: case TransactionStatus::NotAccepted:
status = tr("Generated but not accepted"); status = tr("Generated but not accepted");
break; break;
@ -469,8 +463,6 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
case TransactionStatus::OpenUntilBlock: case TransactionStatus::OpenUntilBlock:
case TransactionStatus::OpenUntilDate: case TransactionStatus::OpenUntilDate:
return COLOR_TX_STATUS_OPENUNTILDATE; return COLOR_TX_STATUS_OPENUNTILDATE;
case TransactionStatus::Offline:
return COLOR_TX_STATUS_OFFLINE;
case TransactionStatus::Unconfirmed: case TransactionStatus::Unconfirmed:
return QIcon(":/icons/transaction_0"); return QIcon(":/icons/transaction_0");
case TransactionStatus::Abandoned: case TransactionStatus::Abandoned:
@ -493,7 +485,6 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
int part = (wtx->status.depth * 4 / total) + 1; int part = (wtx->status.depth * 4 / total) + 1;
return QIcon(QString(":/icons/transaction_%1").arg(part)); return QIcon(QString(":/icons/transaction_%1").arg(part));
} }
case TransactionStatus::MaturesWarning:
case TransactionStatus::NotAccepted: case TransactionStatus::NotAccepted:
return QIcon(":/icons/transaction_0"); return QIcon(":/icons/transaction_0");
default: default:

View File

@ -477,7 +477,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1);
WaitableLock lock(csBestBlock); WaitableLock lock(csBestBlock);
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) while (hashBestBlock == hashWatchedChain && IsRPCRunning())
{ {
if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout) if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout)
{ {

View File

@ -290,7 +290,7 @@ UniValue verifytxoutproof(const JSONRPCRequest& request)
"\nArguments:\n" "\nArguments:\n"
"1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n" "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n"
"\nResult:\n" "\nResult:\n"
"[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof can not be validated.\n"
); );
CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
@ -306,11 +306,16 @@ UniValue verifytxoutproof(const JSONRPCRequest& request)
LOCK(cs_main); LOCK(cs_main);
if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()]) || mapBlockIndex[merkleBlock.header.GetHash()]->nTx == 0)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
for (const uint256& hash : vMatch) // Check if proof is valid, only add results if so
if (mapBlockIndex[merkleBlock.header.GetHash()]->nTx == merkleBlock.txn.GetNumTransactions()) {
for (const uint256& hash : vMatch) {
res.push_back(hash.GetHex()); res.push_back(hash.GetHex());
}
}
return res; return res;
} }

View File

@ -73,7 +73,7 @@ struct LockData {
std::mutex dd_mutex; std::mutex dd_mutex;
} static lockdata; } static lockdata;
static thread_local std::unique_ptr<LockStack> lockstack; static thread_local LockStack g_lockstack;
static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2) static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2)
{ {
@ -103,21 +103,18 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
static void push_lock(void* c, const CLockLocation& locklocation) static void push_lock(void* c, const CLockLocation& locklocation)
{ {
if (!lockstack)
lockstack.reset(new LockStack);
std::lock_guard<std::mutex> lock(lockdata.dd_mutex); std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
lockstack->push_back(std::make_pair(c, locklocation)); g_lockstack.push_back(std::make_pair(c, locklocation));
for (const std::pair<void*, CLockLocation> & i : (*lockstack)) { for (const std::pair<void*, CLockLocation>& i : g_lockstack) {
if (i.first == c) if (i.first == c)
break; break;
std::pair<void*, void*> p1 = std::make_pair(i.first, c); std::pair<void*, void*> p1 = std::make_pair(i.first, c);
if (lockdata.lockorders.count(p1)) if (lockdata.lockorders.count(p1))
continue; continue;
lockdata.lockorders[p1] = (*lockstack); lockdata.lockorders[p1] = g_lockstack;
std::pair<void*, void*> p2 = std::make_pair(c, i.first); std::pair<void*, void*> p2 = std::make_pair(c, i.first);
lockdata.invlockorders.insert(p2); lockdata.invlockorders.insert(p2);
@ -128,7 +125,7 @@ static void push_lock(void* c, const CLockLocation& locklocation)
static void pop_lock() static void pop_lock()
{ {
(*lockstack).pop_back(); g_lockstack.pop_back();
} }
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
@ -144,14 +141,14 @@ void LeaveCritical()
std::string LocksHeld() std::string LocksHeld()
{ {
std::string result; std::string result;
for (const std::pair<void*, CLockLocation> & i : *lockstack) for (const std::pair<void*, CLockLocation>& i : g_lockstack)
result += i.second.ToString() + std::string("\n"); result += i.second.ToString() + std::string("\n");
return result; return result;
} }
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
{ {
for (const std::pair<void*, CLockLocation> & i : *lockstack) for (const std::pair<void*, CLockLocation>& i : g_lockstack)
if (i.first == cs) if (i.first == cs)
return; return;
fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
@ -160,7 +157,7 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
{ {
for (const std::pair<void*, CLockLocation>& i : *lockstack) { for (const std::pair<void*, CLockLocation>& i : g_lockstack) {
if (i.first == cs) { if (i.first == cs) {
fprintf(stderr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); fprintf(stderr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
abort(); abort();

View File

@ -256,14 +256,14 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array(); ar = r.get_array();
BOOST_CHECK_EQUAL(ar.size(), 0); BOOST_CHECK_EQUAL(ar.size(), 0);
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 1607731200 true"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 9907731200 true")));
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned")));
ar = r.get_array(); ar = r.get_array();
o1 = ar[0].get_obj(); o1 = ar[0].get_obj();
adr = find_value(o1, "address"); adr = find_value(o1, "address");
UniValue banned_until = find_value(o1, "banned_until"); UniValue banned_until = find_value(o1, "banned_until");
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24");
BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check BOOST_CHECK_EQUAL(banned_until.get_int64(), 9907731200); // absolute time check
BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned"))); BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned")));

View File

@ -6,11 +6,8 @@
#include <support/allocators/zeroafterfree.h> #include <support/allocators/zeroafterfree.h>
#include <test/test_bitcoin.h> #include <test/test_bitcoin.h>
#include <boost/assign/std/vector.hpp> // for 'operator+=()'
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope
BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup) BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(streams_vector_writer) BOOST_AUTO_TEST_CASE(streams_vector_writer)
@ -80,14 +77,17 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
// Degenerate case // Degenerate case
key += '\x00','\x00'; key.push_back('\x00');
key.push_back('\x00');
ds.Xor(key); ds.Xor(key);
BOOST_CHECK_EQUAL( BOOST_CHECK_EQUAL(
std::string(expected_xor.begin(), expected_xor.end()), std::string(expected_xor.begin(), expected_xor.end()),
std::string(ds.begin(), ds.end())); std::string(ds.begin(), ds.end()));
in += '\x0f','\xf0'; in.push_back('\x0f');
expected_xor += '\xf0','\x0f'; in.push_back('\xf0');
expected_xor.push_back('\xf0');
expected_xor.push_back('\x0f');
// Single character key // Single character key
@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
ds.insert(ds.begin(), in.begin(), in.end()); ds.insert(ds.begin(), in.begin(), in.end());
key.clear(); key.clear();
key += '\xff'; key.push_back('\xff');
ds.Xor(key); ds.Xor(key);
BOOST_CHECK_EQUAL( BOOST_CHECK_EQUAL(
std::string(expected_xor.begin(), expected_xor.end()), std::string(expected_xor.begin(), expected_xor.end()),
@ -105,14 +105,17 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
in.clear(); in.clear();
expected_xor.clear(); expected_xor.clear();
in += '\xf0','\x0f'; in.push_back('\xf0');
expected_xor += '\x0f','\x00'; in.push_back('\x0f');
expected_xor.push_back('\x0f');
expected_xor.push_back('\x00');
ds.clear(); ds.clear();
ds.insert(ds.begin(), in.begin(), in.end()); ds.insert(ds.begin(), in.begin(), in.end());
key.clear(); key.clear();
key += '\xff','\x0f'; key.push_back('\xff');
key.push_back('\x0f');
ds.Xor(key); ds.Xor(key);
BOOST_CHECK_EQUAL( BOOST_CHECK_EQUAL(

View File

@ -210,6 +210,7 @@ CChain& chainActive = g_chainstate.chainActive;
CBlockIndex *pindexBestHeader = nullptr; CBlockIndex *pindexBestHeader = nullptr;
CWaitableCriticalSection csBestBlock; CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange; CConditionVariable cvBlockChange;
uint256 hashBestBlock;
int nScriptCheckThreads = 0; int nScriptCheckThreads = 0;
std::atomic_bool fImporting(false); std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false); std::atomic_bool fReindex(false);
@ -2151,7 +2152,11 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
// New best block // New best block
mempool.AddTransactionsUpdated(1); mempool.AddTransactionsUpdated(1);
{
WaitableLock lock(csBestBlock);
hashBestBlock = pindexNew->GetBlockHash();
cvBlockChange.notify_all(); cvBlockChange.notify_all();
}
std::vector<std::string> warningMessages; std::vector<std::string> warningMessages;
if (!IsInitialBlockDownload()) if (!IsInitialBlockDownload())

View File

@ -166,6 +166,7 @@ extern uint64_t nLastBlockWeight;
extern const std::string strMessageMagic; extern const std::string strMessageMagic;
extern CWaitableCriticalSection csBestBlock; extern CWaitableCriticalSection csBestBlock;
extern CConditionVariable cvBlockChange; extern CConditionVariable cvBlockChange;
extern uint256 hashBestBlock;
extern std::atomic_bool fImporting; extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex; extern std::atomic_bool fReindex;
extern int nScriptCheckThreads; extern int nScriptCheckThreads;

View File

@ -26,7 +26,6 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected; boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool; boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain; boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
boost::signals2::signal<void (const uint256 &)> Inventory;
boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast; boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked; boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock; boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
@ -81,7 +80,6 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1)); g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1));
g_signals.m_internals->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.m_internals->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.m_internals->Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.m_internals->NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); g_signals.m_internals->NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
@ -90,7 +88,6 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
void UnregisterValidationInterface(CValidationInterface* pwalletIn) { void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.m_internals->Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.m_internals->SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.m_internals->SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1)); g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
@ -106,7 +103,6 @@ void UnregisterAllValidationInterfaces() {
} }
g_signals.m_internals->BlockChecked.disconnect_all_slots(); g_signals.m_internals->BlockChecked.disconnect_all_slots();
g_signals.m_internals->Broadcast.disconnect_all_slots(); g_signals.m_internals->Broadcast.disconnect_all_slots();
g_signals.m_internals->Inventory.disconnect_all_slots();
g_signals.m_internals->SetBestChain.disconnect_all_slots(); g_signals.m_internals->SetBestChain.disconnect_all_slots();
g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots(); g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots();
g_signals.m_internals->BlockConnected.disconnect_all_slots(); g_signals.m_internals->BlockConnected.disconnect_all_slots();
@ -172,12 +168,6 @@ void CMainSignals::SetBestChain(const CBlockLocator &locator) {
}); });
} }
void CMainSignals::Inventory(const uint256 &hash) {
m_internals->m_schedulerClient.AddToProcessQueue([hash, this] {
m_internals->Inventory(hash);
});
}
void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) { void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) {
m_internals->Broadcast(nBestBlockTime, connman); m_internals->Broadcast(nBestBlockTime, connman);
} }

View File

@ -101,12 +101,6 @@ protected:
* Called on a background thread. * Called on a background thread.
*/ */
virtual void SetBestChain(const CBlockLocator &locator) {} virtual void SetBestChain(const CBlockLocator &locator) {}
/**
* Notifies listeners about an inventory item being seen on the network.
*
* Called on a background thread.
*/
virtual void Inventory(const uint256 &hash) {}
/** Tells listeners to broadcast their data. */ /** Tells listeners to broadcast their data. */
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {} virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
/** /**
@ -157,7 +151,6 @@ public:
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &); void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &);
void BlockDisconnected(const std::shared_ptr<const CBlock> &); void BlockDisconnected(const std::shared_ptr<const CBlock> &);
void SetBestChain(const CBlockLocator &); void SetBestChain(const CBlockLocator &);
void Inventory(const uint256 &);
void Broadcast(int64_t nBestBlockTime, CConnman* connman); void Broadcast(int64_t nBestBlockTime, CConnman* connman);
void BlockChecked(const CBlock&, const CValidationState&); void BlockChecked(const CBlock&, const CValidationState&);
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&); void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);

View File

@ -1537,7 +1537,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
" \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n"
" \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n"
" \"txids\": [\n" " \"txids\": [\n"
" n, (numeric) The ids of transactions received with the address \n" " \"txid\", (string) The ids of transactions received with the address \n"
" ...\n" " ...\n"
" ]\n" " ]\n"
" }\n" " }\n"
@ -2322,8 +2322,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
"This is needed prior to performing transactions related to private keys such as sending bitcoins\n" "This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
"\nArguments:\n" "\nArguments:\n"
"1. \"passphrase\" (string, required) The wallet passphrase\n" "1. \"passphrase\" (string, required) The wallet passphrase\n"
"2. timeout (numeric, required) The time to keep the decryption key in seconds. Limited to at most 1073741824 (2^30) seconds.\n" "2. timeout (numeric, required) The time to keep the decryption key in seconds; capped at 100000000 (~3 years).\n"
" Any value greater than 1073741824 seconds will be set to 1073741824 seconds.\n"
"\nNote:\n" "\nNote:\n"
"Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
"time that overrides the old one.\n" "time that overrides the old one.\n"
@ -2358,9 +2357,10 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
if (nSleepTime < 0) { if (nSleepTime < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative."); throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
} }
// Clamp timeout to 2^30 seconds // Clamp timeout
if (nSleepTime > (int64_t)1 << 30) { constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
nSleepTime = (int64_t)1 << 30; if (nSleepTime > MAX_SLEEP_TIME) {
nSleepTime = MAX_SLEEP_TIME;
} }
if (strWalletPass.length() > 0) if (strWalletPass.length() > 0)

View File

@ -891,7 +891,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
bool success = true; bool success = true;
if (!walletdb.WriteTx(wtx)) { if (!walletdb.WriteTx(wtx)) {
LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString()); LogPrintf("%s: Updating walletdb tx %s failed\n", __func__, wtx.GetHash().ToString());
success = false; success = false;
} }
@ -913,11 +913,10 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
CWalletTx& wtx = (*ret.first).second; CWalletTx& wtx = (*ret.first).second;
wtx.BindWallet(this); wtx.BindWallet(this);
bool fInsertedNew = ret.second; bool fInsertedNew = ret.second;
if (fInsertedNew) if (fInsertedNew) {
{
wtx.nTimeReceived = GetAdjustedTime(); wtx.nTimeReceived = GetAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(&walletdb); wtx.nOrderPos = IncOrderPosNext(&walletdb);
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr)));
wtx.nTimeSmart = ComputeTimeSmart(wtx); wtx.nTimeSmart = ComputeTimeSmart(wtx);
AddToSpends(hash); AddToSpends(hash);
} }
@ -987,9 +986,12 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
bool CWallet::LoadToWallet(const CWalletTx& wtxIn) bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
{ {
uint256 hash = wtxIn.GetHash(); uint256 hash = wtxIn.GetHash();
CWalletTx& wtx = mapWallet.emplace(hash, wtxIn).first->second; const auto& ins = mapWallet.emplace(hash, wtxIn);
CWalletTx& wtx = ins.first->second;
wtx.BindWallet(this); wtx.BindWallet(this);
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); if (/* insertion took place */ ins.second) {
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr)));
}
AddToSpends(hash); AddToSpends(hash);
for (const CTxIn& txin : wtx.tx->vin) { for (const CTxIn& txin : wtx.tx->vin) {
auto it = mapWallet.find(txin.prevout.hash); auto it = mapWallet.find(txin.prevout.hash);
@ -1506,45 +1508,6 @@ int64_t CWalletTx::GetTxTime() const
return n ? n : nTimeReceived; return n ? n : nTimeReceived;
} }
int CWalletTx::GetRequestCount() const
{
// Returns -1 if it wasn't being tracked
int nRequests = -1;
{
LOCK(pwallet->cs_wallet);
if (IsCoinBase())
{
// Generated block
if (!hashUnset())
{
std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second;
}
}
else
{
// Did anyone request this transaction?
std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());
if (mi != pwallet->mapRequestCount.end())
{
nRequests = (*mi).second;
// How about the block it's in?
if (nRequests == 0 && !hashUnset())
{
std::map<uint256, int>::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock);
if (_mi != pwallet->mapRequestCount.end())
nRequests = (*_mi).second;
else
nRequests = 1; // If it's in someone else's block it must have got out
}
}
}
}
return nRequests;
}
void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived, void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const
{ {
@ -3085,9 +3048,6 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
} }
} }
// Track how many getdata requests our transaction gets
mapRequestCount[wtxNew.GetHash()] = 0;
// Get the inserted-CWalletTx from mapWallet so that the // Get the inserted-CWalletTx from mapWallet so that the
// fInMempool flag is cached properly // fInMempool flag is cached properly
CWalletTx& wtx = mapWallet[wtxNew.GetHash()]; CWalletTx& wtx = mapWallet[wtxNew.GetHash()];
@ -3165,8 +3125,11 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
{ {
AssertLockHeld(cs_wallet); // mapWallet AssertLockHeld(cs_wallet); // mapWallet
DBErrors nZapSelectTxRet = CWalletDB(*dbw,"cr+").ZapSelectTx(vHashIn, vHashOut); DBErrors nZapSelectTxRet = CWalletDB(*dbw,"cr+").ZapSelectTx(vHashIn, vHashOut);
for (uint256 hash : vHashOut) for (uint256 hash : vHashOut) {
mapWallet.erase(hash); const auto& it = mapWallet.find(hash);
wtxOrdered.erase(it->second.m_it_wtxOrdered);
mapWallet.erase(it);
}
if (nZapSelectTxRet == DB_NEED_REWRITE) if (nZapSelectTxRet == DB_NEED_REWRITE)
{ {

View File

@ -327,6 +327,7 @@ public:
char fFromMe; char fFromMe;
std::string strFromAccount; std::string strFromAccount;
int64_t nOrderPos; //!< position in ordered transaction list int64_t nOrderPos; //!< position in ordered transaction list
std::multimap<int64_t, std::pair<CWalletTx*, CAccountingEntry*>>::const_iterator m_it_wtxOrdered;
// memory only // memory only
mutable bool fDebitCached; mutable bool fDebitCached;
@ -478,7 +479,6 @@ public:
bool IsTrusted() const; bool IsTrusted() const;
int64_t GetTxTime() const; int64_t GetTxTime() const;
int GetRequestCount() const;
// RelayWalletTransaction may only be called if fBroadcastTransactions! // RelayWalletTransaction may only be called if fBroadcastTransactions!
bool RelayWalletTransaction(CConnman* connman); bool RelayWalletTransaction(CConnman* connman);
@ -834,7 +834,6 @@ public:
int64_t nOrderPosNext; int64_t nOrderPosNext;
uint64_t nAccountingEntryNumber; uint64_t nAccountingEntryNumber;
std::map<uint256, int> mapRequestCount;
std::map<CTxDestination, CAddressBookData> mapAddressBook; std::map<CTxDestination, CAddressBookData> mapAddressBook;
@ -1043,16 +1042,6 @@ public:
const std::string& GetAccountName(const CScript& scriptPubKey) const; const std::string& GetAccountName(const CScript& scriptPubKey) const;
void Inventory(const uint256 &hash) override
{
{
LOCK(cs_wallet);
std::map<uint256, int>::iterator mi = mapRequestCount.find(hash);
if (mi != mapRequestCount.end())
(*mi).second++;
}
}
void GetScriptForMining(std::shared_ptr<CReserveScript> &script); void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
unsigned int GetKeyPoolSize() unsigned int GetKeyPoolSize()

View File

@ -116,6 +116,7 @@ class BaseNode(P2PInterface):
self.block_announced = False self.block_announced = False
self.last_blockhash_announced = None self.last_blockhash_announced = None
self.recent_headers_announced = []
def send_get_data(self, block_hashes): def send_get_data(self, block_hashes):
"""Request data for a list of block hashes.""" """Request data for a list of block hashes."""
@ -163,40 +164,45 @@ class BaseNode(P2PInterface):
def on_headers(self, message): def on_headers(self, message):
if len(message.headers): if len(message.headers):
self.block_announced = True self.block_announced = True
message.headers[-1].calc_sha256() for x in message.headers:
x.calc_sha256()
# append because headers may be announced over multiple messages.
self.recent_headers_announced.append(x.sha256)
self.last_blockhash_announced = message.headers[-1].sha256 self.last_blockhash_announced = message.headers[-1].sha256
def clear_last_announcement(self): def clear_block_announcements(self):
with mininode_lock: with mininode_lock:
self.block_announced = False self.block_announced = False
self.last_message.pop("inv", None) self.last_message.pop("inv", None)
self.last_message.pop("headers", None) self.last_message.pop("headers", None)
self.recent_headers_announced = []
def check_last_announcement(self, headers=None, inv=None):
"""Test whether the last announcement received had the right header or the right inv.
inv and headers should be lists of block hashes.""" def check_last_headers_announcement(self, headers):
"""Test whether the last headers announcements received are right.
Headers may be announced across more than one message."""
test_function = lambda: (len(self.recent_headers_announced) >= len(headers))
wait_until(test_function, timeout=60, lock=mininode_lock)
with mininode_lock:
assert_equal(self.recent_headers_announced, headers)
self.block_announced = False
self.last_message.pop("headers", None)
self.recent_headers_announced = []
def check_last_inv_announcement(self, inv):
"""Test whether the last announcement received had the right inv.
inv should be a list of block hashes."""
test_function = lambda: self.block_announced test_function = lambda: self.block_announced
wait_until(test_function, timeout=60, lock=mininode_lock) wait_until(test_function, timeout=60, lock=mininode_lock)
with mininode_lock: with mininode_lock:
self.block_announced = False
compare_inv = [] compare_inv = []
if "inv" in self.last_message: if "inv" in self.last_message:
compare_inv = [x.hash for x in self.last_message["inv"].inv] compare_inv = [x.hash for x in self.last_message["inv"].inv]
if inv is not None:
assert_equal(compare_inv, inv) assert_equal(compare_inv, inv)
self.block_announced = False
compare_headers = []
if "headers" in self.last_message:
compare_headers = [x.sha256 for x in self.last_message["headers"].headers]
if headers is not None:
assert_equal(compare_headers, headers)
self.last_message.pop("inv", None) self.last_message.pop("inv", None)
self.last_message.pop("headers", None)
class SendHeadersTest(BitcoinTestFramework): class SendHeadersTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
@ -206,8 +212,8 @@ class SendHeadersTest(BitcoinTestFramework):
def mine_blocks(self, count): def mine_blocks(self, count):
"""Mine count blocks and return the new tip.""" """Mine count blocks and return the new tip."""
# Clear out last block announcement from each p2p listener # Clear out block announcements from each p2p listener
[x.clear_last_announcement() for x in self.nodes[0].p2ps] [x.clear_block_announcements() for x in self.nodes[0].p2ps]
self.nodes[0].generate(count) self.nodes[0].generate(count)
return int(self.nodes[0].getbestblockhash(), 16) return int(self.nodes[0].getbestblockhash(), 16)
@ -222,7 +228,7 @@ class SendHeadersTest(BitcoinTestFramework):
sync_blocks(self.nodes, wait=0.1) sync_blocks(self.nodes, wait=0.1)
for x in self.nodes[0].p2ps: for x in self.nodes[0].p2ps:
x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16)) x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16))
x.clear_last_announcement() x.clear_block_announcements()
tip_height = self.nodes[1].getblockcount() tip_height = self.nodes[1].getblockcount()
hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1)) hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1))
@ -255,25 +261,25 @@ class SendHeadersTest(BitcoinTestFramework):
tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0]) tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0])
tip_hash = int(tip["hash"], 16) tip_hash = int(tip["hash"], 16)
inv_node.check_last_announcement(inv=[tip_hash], headers=[]) inv_node.check_last_inv_announcement(inv=[tip_hash])
test_node.check_last_announcement(inv=[tip_hash], headers=[]) test_node.check_last_inv_announcement(inv=[tip_hash])
self.log.info("Verify getheaders with null locator and valid hashstop returns headers.") self.log.info("Verify getheaders with null locator and valid hashstop returns headers.")
test_node.clear_last_announcement() test_node.clear_block_announcements()
test_node.send_get_headers(locator=[], hashstop=tip_hash) test_node.send_get_headers(locator=[], hashstop=tip_hash)
test_node.check_last_announcement(headers=[tip_hash]) test_node.check_last_headers_announcement(headers=[tip_hash])
self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.") self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.")
block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1) block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1)
block.solve() block.solve()
test_node.send_header_for_blocks([block]) test_node.send_header_for_blocks([block])
test_node.clear_last_announcement() test_node.clear_block_announcements()
test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16)) test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16))
test_node.sync_with_ping() test_node.sync_with_ping()
assert_equal(test_node.block_announced, False) assert_equal(test_node.block_announced, False)
inv_node.clear_last_announcement() inv_node.clear_block_announcements()
test_node.send_message(msg_block(block)) test_node.send_message(msg_block(block))
inv_node.check_last_announcement(inv=[int(block.hash, 16)], headers=[]) inv_node.check_last_inv_announcement(inv=[int(block.hash, 16)])
def test_nonnull_locators(self, test_node, inv_node): def test_nonnull_locators(self, test_node, inv_node):
tip = int(self.nodes[0].getbestblockhash(), 16) tip = int(self.nodes[0].getbestblockhash(), 16)
@ -284,8 +290,8 @@ class SendHeadersTest(BitcoinTestFramework):
for i in range(4): for i in range(4):
old_tip = tip old_tip = tip
tip = self.mine_blocks(1) tip = self.mine_blocks(1)
inv_node.check_last_announcement(inv=[tip], headers=[]) inv_node.check_last_inv_announcement(inv=[tip])
test_node.check_last_announcement(inv=[tip], headers=[]) test_node.check_last_inv_announcement(inv=[tip])
# Try a few different responses; none should affect next announcement # Try a few different responses; none should affect next announcement
if i == 0: if i == 0:
# first request the block # first request the block
@ -296,7 +302,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.send_get_headers(locator=[old_tip], hashstop=tip) test_node.send_get_headers(locator=[old_tip], hashstop=tip)
test_node.send_get_data([tip]) test_node.send_get_data([tip])
test_node.wait_for_block(tip) test_node.wait_for_block(tip)
test_node.clear_last_announcement() # since we requested headers... test_node.clear_block_announcements() # since we requested headers...
elif i == 2: elif i == 2:
# this time announce own block via headers # this time announce own block via headers
height = self.nodes[0].getblockcount() height = self.nodes[0].getblockcount()
@ -308,8 +314,8 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.wait_for_getdata([new_block.sha256]) test_node.wait_for_getdata([new_block.sha256])
test_node.send_message(msg_block(new_block)) test_node.send_message(msg_block(new_block))
test_node.sync_with_ping() # make sure this block is processed test_node.sync_with_ping() # make sure this block is processed
inv_node.clear_last_announcement() inv_node.clear_block_announcements()
test_node.clear_last_announcement() test_node.clear_block_announcements()
self.log.info("Part 1: success!") self.log.info("Part 1: success!")
self.log.info("Part 2: announce blocks with headers after sendheaders message...") self.log.info("Part 2: announce blocks with headers after sendheaders message...")
@ -323,8 +329,8 @@ class SendHeadersTest(BitcoinTestFramework):
# Now that we've synced headers, headers announcements should work # Now that we've synced headers, headers announcements should work
tip = self.mine_blocks(1) tip = self.mine_blocks(1)
inv_node.check_last_announcement(inv=[tip], headers=[]) inv_node.check_last_inv_announcement(inv=[tip])
test_node.check_last_announcement(headers=[tip]) test_node.check_last_headers_announcement(headers=[tip])
height = self.nodes[0].getblockcount() + 1 height = self.nodes[0].getblockcount() + 1
block_time += 10 # Advance far enough ahead block_time += 10 # Advance far enough ahead
@ -368,8 +374,8 @@ class SendHeadersTest(BitcoinTestFramework):
assert "inv" not in inv_node.last_message assert "inv" not in inv_node.last_message
assert "headers" not in inv_node.last_message assert "headers" not in inv_node.last_message
tip = self.mine_blocks(1) tip = self.mine_blocks(1)
inv_node.check_last_announcement(inv=[tip], headers=[]) inv_node.check_last_inv_announcement(inv=[tip])
test_node.check_last_announcement(headers=[tip]) test_node.check_last_headers_announcement(headers=[tip])
height += 1 height += 1
block_time += 1 block_time += 1
@ -383,16 +389,16 @@ class SendHeadersTest(BitcoinTestFramework):
# First try mining a reorg that can propagate with header announcement # First try mining a reorg that can propagate with header announcement
new_block_hashes = self.mine_reorg(length=7) new_block_hashes = self.mine_reorg(length=7)
tip = new_block_hashes[-1] tip = new_block_hashes[-1]
inv_node.check_last_announcement(inv=[tip], headers=[]) inv_node.check_last_inv_announcement(inv=[tip])
test_node.check_last_announcement(headers=new_block_hashes) test_node.check_last_headers_announcement(headers=new_block_hashes)
block_time += 8 block_time += 8
# Mine a too-large reorg, which should be announced with a single inv # Mine a too-large reorg, which should be announced with a single inv
new_block_hashes = self.mine_reorg(length=8) new_block_hashes = self.mine_reorg(length=8)
tip = new_block_hashes[-1] tip = new_block_hashes[-1]
inv_node.check_last_announcement(inv=[tip], headers=[]) inv_node.check_last_inv_announcement(inv=[tip])
test_node.check_last_announcement(inv=[tip], headers=[]) test_node.check_last_inv_announcement(inv=[tip])
block_time += 9 block_time += 9
@ -401,15 +407,15 @@ class SendHeadersTest(BitcoinTestFramework):
# Use getblocks/getdata # Use getblocks/getdata
test_node.send_getblocks(locator=[fork_point]) test_node.send_getblocks(locator=[fork_point])
test_node.check_last_announcement(inv=new_block_hashes, headers=[]) test_node.check_last_inv_announcement(inv=new_block_hashes)
test_node.send_get_data(new_block_hashes) test_node.send_get_data(new_block_hashes)
test_node.wait_for_block(new_block_hashes[-1]) test_node.wait_for_block(new_block_hashes[-1])
for i in range(3): for i in range(3):
# Mine another block, still should get only an inv # Mine another block, still should get only an inv
tip = self.mine_blocks(1) tip = self.mine_blocks(1)
inv_node.check_last_announcement(inv=[tip], headers=[]) inv_node.check_last_inv_announcement(inv=[tip])
test_node.check_last_announcement(inv=[tip], headers=[]) test_node.check_last_inv_announcement(inv=[tip])
if i == 0: if i == 0:
# Just get the data -- shouldn't cause headers announcements to resume # Just get the data -- shouldn't cause headers announcements to resume
test_node.send_get_data([tip]) test_node.send_get_data([tip])
@ -434,8 +440,8 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.sync_with_ping() test_node.sync_with_ping()
# New blocks should now be announced with header # New blocks should now be announced with header
tip = self.mine_blocks(1) tip = self.mine_blocks(1)
inv_node.check_last_announcement(inv=[tip], headers=[]) inv_node.check_last_inv_announcement(inv=[tip])
test_node.check_last_announcement(headers=[tip]) test_node.check_last_headers_announcement(headers=[tip])
self.log.info("Part 3: success!") self.log.info("Part 3: success!")

View File

@ -6,6 +6,8 @@
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import * from test_framework.util import *
from test_framework.mininode import FromHex, ToHex
from test_framework.messages import CMerkleBlock
class MerkleBlockTest(BitcoinTestFramework): class MerkleBlockTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
@ -78,6 +80,27 @@ class MerkleBlockTest(BitcoinTestFramework):
# We can't get a proof if we specify transactions from different blocks # We can't get a proof if we specify transactions from different blocks
assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3]) assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3])
# Now we'll try tweaking a proof.
proof = self.nodes[3].gettxoutproof([txid1, txid2])
assert txid1 in self.nodes[0].verifytxoutproof(proof)
assert txid2 in self.nodes[1].verifytxoutproof(proof)
tweaked_proof = FromHex(CMerkleBlock(), proof)
# Make sure that our serialization/deserialization is working
assert txid1 in self.nodes[2].verifytxoutproof(ToHex(tweaked_proof))
# Check to see if we can go up the merkle tree and pass this off as a
# single-transaction block
tweaked_proof.txn.nTransactions = 1
tweaked_proof.txn.vHash = [tweaked_proof.header.hashMerkleRoot]
tweaked_proof.txn.vBits = [True] + [False]*7
for n in self.nodes:
assert not n.verifytxoutproof(ToHex(tweaked_proof))
# TODO: try more variants, eg transactions at different depths, and
# verify that the proofs are invalid
if __name__ == '__main__': if __name__ == '__main__':
MerkleBlockTest().main() MerkleBlockTest().main()

View File

@ -830,6 +830,52 @@ class BlockTransactions():
def __repr__(self): def __repr__(self):
return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions)) return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions))
class CPartialMerkleTree():
def __init__(self):
self.nTransactions = 0
self.vHash = []
self.vBits = []
self.fBad = False
def deserialize(self, f):
self.nTransactions = struct.unpack("<i", f.read(4))[0]
self.vHash = deser_uint256_vector(f)
vBytes = deser_string(f)
self.vBits = []
for i in range(len(vBytes) * 8):
self.vBits.append(vBytes[i//8] & (1 << (i % 8)) != 0)
def serialize(self):
r = b""
r += struct.pack("<i", self.nTransactions)
r += ser_uint256_vector(self.vHash)
vBytesArray = bytearray([0x00] * ((len(self.vBits) + 7)//8))
for i in range(len(self.vBits)):
vBytesArray[i // 8] |= self.vBits[i] << (i % 8)
r += ser_string(bytes(vBytesArray))
return r
def __repr__(self):
return "CPartialMerkleTree(nTransactions=%d, vHash=%s, vBits=%s)" % (self.nTransactions, repr(self.vHash), repr(self.vBits))
class CMerkleBlock():
def __init__(self):
self.header = CBlockHeader()
self.txn = CPartialMerkleTree()
def deserialize(self, f):
self.header.deserialize(f)
self.txn.deserialize(f)
def serialize(self):
r = b""
r += self.header.serialize()
r += self.txn.serialize()
return r
def __repr__(self):
return "CMerkleBlock(header=%s, txn=%s)" % (repr(self.header), repr(self.txn))
# Objects that correspond to messages on the wire # Objects that correspond to messages on the wire
class msg_version(): class msg_version():

View File

@ -64,14 +64,15 @@ class WalletEncryptionTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Timeout cannot be negative.", self.nodes[0].walletpassphrase, passphrase2, -10) assert_raises_rpc_error(-8, "Timeout cannot be negative.", self.nodes[0].walletpassphrase, passphrase2, -10)
# Check the timeout # Check the timeout
# Check a time less than the limit # Check a time less than the limit
expected_time = int(time.time()) + (1 << 30) - 600 MAX_VALUE = 100000000
self.nodes[0].walletpassphrase(passphrase2, (1 << 30) - 600) expected_time = int(time.time()) + MAX_VALUE - 600
self.nodes[0].walletpassphrase(passphrase2, MAX_VALUE - 600)
actual_time = self.nodes[0].getwalletinfo()['unlocked_until'] actual_time = self.nodes[0].getwalletinfo()['unlocked_until']
assert_greater_than_or_equal(actual_time, expected_time) assert_greater_than_or_equal(actual_time, expected_time)
assert_greater_than(expected_time + 5, actual_time) # 5 second buffer assert_greater_than(expected_time + 5, actual_time) # 5 second buffer
# Check a time greater than the limit # Check a time greater than the limit
expected_time = int(time.time()) + (1 << 30) - 1 expected_time = int(time.time()) + MAX_VALUE - 1
self.nodes[0].walletpassphrase(passphrase2, (1 << 33)) self.nodes[0].walletpassphrase(passphrase2, MAX_VALUE + 1000)
actual_time = self.nodes[0].getwalletinfo()['unlocked_until'] actual_time = self.nodes[0].getwalletinfo()['unlocked_until']
assert_greater_than_or_equal(actual_time, expected_time) assert_greater_than_or_equal(actual_time, expected_time)
assert_greater_than(expected_time + 5, actual_time) # 5 second buffer assert_greater_than(expected_time + 5, actual_time) # 5 second buffer

View File

@ -6,10 +6,13 @@
from decimal import Decimal from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (assert_array_result, from test_framework.util import (
assert_array_result,
assert_equal, assert_equal,
assert_raises_rpc_error, assert_raises_rpc_error,
) sync_blocks,
)
class ReceivedByTest(BitcoinTestFramework): class ReceivedByTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
@ -18,6 +21,7 @@ class ReceivedByTest(BitcoinTestFramework):
def run_test(self): def run_test(self):
# Generate block to get out of IBD # Generate block to get out of IBD
self.nodes[0].generate(1) self.nodes[0].generate(1)
sync_blocks(self.nodes)
self.log.info("listreceivedbyaddress Test") self.log.info("listreceivedbyaddress Test")