mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 19:42:46 +01:00
Merge pull request #3371 from PastaPastaPasta/backports-0.16-pr12
Backports 0.16 pr12
This commit is contained in:
commit
3c90da86b3
22
configure.ac
22
configure.ac
@ -762,6 +762,28 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
]
|
||||
)
|
||||
|
||||
TEMP_LDFLAGS="$LDFLAGS"
|
||||
LDFLAGS="$TEMP_LDFLAGS $PTHREAD_CFLAGS"
|
||||
AC_MSG_CHECKING([for thread_local support])
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
#include <thread>
|
||||
static thread_local int foo = 0;
|
||||
static void run_thread() { foo++;}
|
||||
int main(){
|
||||
for(int i = 0; i < 10; i++) { std::thread(run_thread).detach();}
|
||||
return foo;
|
||||
}
|
||||
])],
|
||||
[
|
||||
AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.])
|
||||
AC_MSG_RESULT(yes)
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT(no)
|
||||
]
|
||||
)
|
||||
LDFLAGS="$TEMP_LDFLAGS"
|
||||
|
||||
# Check for different ways of gathering OS randomness
|
||||
AC_MSG_CHECKING(for Linux getrandom syscall)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include <hash.h>
|
||||
#include <crypto/common.h>
|
||||
#include <crypto/hmac_sha512.h>
|
||||
#include <pubkey.h>
|
||||
|
||||
|
||||
inline uint32_t ROTL32(uint32_t x, int8_t r)
|
||||
|
@ -414,7 +414,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log,
|
||||
status.ok()) {
|
||||
if (record.size() < 12) {
|
||||
reporter.Corruption(
|
||||
record.size(), Status::Corruption("log record too small"));
|
||||
record.size(), Status::Corruption("log record too small", fname));
|
||||
continue;
|
||||
}
|
||||
WriteBatchInternal::SetContents(&batch, record);
|
||||
|
@ -19,6 +19,7 @@ class StdoutPrinter : public WritableFile {
|
||||
virtual Status Close() { return Status::OK(); }
|
||||
virtual Status Flush() { return Status::OK(); }
|
||||
virtual Status Sync() { return Status::OK(); }
|
||||
virtual std::string GetName() const { return "[stdout]"; }
|
||||
};
|
||||
|
||||
bool HandleDumpCommand(Env* env, char** files, int num) {
|
||||
|
@ -186,7 +186,7 @@ uint64_t Reader::LastRecordOffset() {
|
||||
}
|
||||
|
||||
void Reader::ReportCorruption(uint64_t bytes, const char* reason) {
|
||||
ReportDrop(bytes, Status::Corruption(reason));
|
||||
ReportDrop(bytes, Status::Corruption(reason, file_->GetName()));
|
||||
}
|
||||
|
||||
void Reader::ReportDrop(uint64_t bytes, const Status& reason) {
|
||||
|
@ -203,7 +203,7 @@ class Repairer {
|
||||
while (reader.ReadRecord(&record, &scratch)) {
|
||||
if (record.size() < 12) {
|
||||
reporter.Corruption(
|
||||
record.size(), Status::Corruption("log record too small"));
|
||||
record.size(), Status::Corruption("log record too small", logname));
|
||||
continue;
|
||||
}
|
||||
WriteBatchInternal::SetContents(&batch, record);
|
||||
|
@ -176,6 +176,7 @@ class SequentialFileImpl : public SequentialFile {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual std::string GetName() const { return "[memenv]"; }
|
||||
private:
|
||||
FileState* file_;
|
||||
uint64_t pos_;
|
||||
@ -196,6 +197,7 @@ class RandomAccessFileImpl : public RandomAccessFile {
|
||||
return file_->Read(offset, n, result, scratch);
|
||||
}
|
||||
|
||||
virtual std::string GetName() const { return "[memenv]"; }
|
||||
private:
|
||||
FileState* file_;
|
||||
};
|
||||
@ -218,6 +220,7 @@ class WritableFileImpl : public WritableFile {
|
||||
virtual Status Flush() { return Status::OK(); }
|
||||
virtual Status Sync() { return Status::OK(); }
|
||||
|
||||
virtual std::string GetName() const { return "[memenv]"; }
|
||||
private:
|
||||
FileState* file_;
|
||||
};
|
||||
|
@ -191,6 +191,9 @@ class SequentialFile {
|
||||
// REQUIRES: External synchronization
|
||||
virtual Status Skip(uint64_t n) = 0;
|
||||
|
||||
// Get a name for the file, only for error reporting
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
private:
|
||||
// No copying allowed
|
||||
SequentialFile(const SequentialFile&);
|
||||
@ -215,6 +218,9 @@ class RandomAccessFile {
|
||||
virtual Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const = 0;
|
||||
|
||||
// Get a name for the file, only for error reporting
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
private:
|
||||
// No copying allowed
|
||||
RandomAccessFile(const RandomAccessFile&);
|
||||
@ -234,6 +240,9 @@ class WritableFile {
|
||||
virtual Status Flush() = 0;
|
||||
virtual Status Sync() = 0;
|
||||
|
||||
// Get a name for the file, only for error reporting
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
private:
|
||||
// No copying allowed
|
||||
WritableFile(const WritableFile&);
|
||||
|
@ -82,7 +82,7 @@ Status ReadBlock(RandomAccessFile* file,
|
||||
}
|
||||
if (contents.size() != n + kBlockTrailerSize) {
|
||||
delete[] buf;
|
||||
return Status::Corruption("truncated block read");
|
||||
return Status::Corruption("truncated block read", file->GetName());
|
||||
}
|
||||
|
||||
// Check the crc of the type and the block contents
|
||||
@ -92,7 +92,7 @@ Status ReadBlock(RandomAccessFile* file,
|
||||
const uint32_t actual = crc32c::Value(data, n + 1);
|
||||
if (actual != crc) {
|
||||
delete[] buf;
|
||||
s = Status::Corruption("block checksum mismatch");
|
||||
s = Status::Corruption("block checksum mismatch", file->GetName());
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@ -119,13 +119,13 @@ Status ReadBlock(RandomAccessFile* file,
|
||||
size_t ulength = 0;
|
||||
if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) {
|
||||
delete[] buf;
|
||||
return Status::Corruption("corrupted compressed block contents");
|
||||
return Status::Corruption("corrupted compressed block contents", file->GetName());
|
||||
}
|
||||
char* ubuf = new char[ulength];
|
||||
if (!port::Snappy_Uncompress(data, n, ubuf)) {
|
||||
delete[] buf;
|
||||
delete[] ubuf;
|
||||
return Status::Corruption("corrupted compressed block contents");
|
||||
return Status::Corruption("corrupted compressed block contents", file->GetName());
|
||||
}
|
||||
delete[] buf;
|
||||
result->data = Slice(ubuf, ulength);
|
||||
@ -135,7 +135,7 @@ Status ReadBlock(RandomAccessFile* file,
|
||||
}
|
||||
default:
|
||||
delete[] buf;
|
||||
return Status::Corruption("bad block type");
|
||||
return Status::Corruption("bad block type", file->GetName());
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
|
@ -121,6 +121,8 @@ class PosixSequentialFile: public SequentialFile {
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual std::string GetName() const { return filename_; }
|
||||
};
|
||||
|
||||
// pread() based random-access
|
||||
@ -172,6 +174,8 @@ class PosixRandomAccessFile: public RandomAccessFile {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual std::string GetName() const { return filename_; }
|
||||
};
|
||||
|
||||
// mmap() based random-access
|
||||
@ -206,6 +210,8 @@ class PosixMmapReadableFile: public RandomAccessFile {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual std::string GetName() const { return filename_; }
|
||||
};
|
||||
|
||||
class PosixWritableFile : public WritableFile {
|
||||
@ -287,6 +293,8 @@ class PosixWritableFile : public WritableFile {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual std::string GetName() const { return filename_; }
|
||||
};
|
||||
|
||||
static int LockOrUnlock(int fd, bool lock) {
|
||||
|
@ -78,6 +78,7 @@ public:
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch);
|
||||
virtual Status Skip(uint64_t n);
|
||||
BOOL isEnable();
|
||||
virtual std::string GetName() const { return _filename; }
|
||||
private:
|
||||
BOOL _Init();
|
||||
void _CleanUp();
|
||||
@ -94,6 +95,7 @@ public:
|
||||
virtual ~Win32RandomAccessFile();
|
||||
virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const;
|
||||
BOOL isEnable();
|
||||
virtual std::string GetName() const { return _filename; }
|
||||
private:
|
||||
BOOL _Init(LPCWSTR path);
|
||||
void _CleanUp();
|
||||
@ -114,6 +116,7 @@ public:
|
||||
virtual Status Flush();
|
||||
virtual Status Sync();
|
||||
BOOL isEnable();
|
||||
virtual std::string GetName() const { return filename_; }
|
||||
private:
|
||||
std::string filename_;
|
||||
::HANDLE _hFile;
|
||||
|
20
src/sync.cpp
20
src/sync.cpp
@ -4,14 +4,16 @@
|
||||
|
||||
#include <sync.h>
|
||||
|
||||
#include <set>
|
||||
#include <util.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#ifdef DEBUG_LOCKCONTENTION
|
||||
#if !defined(HAVE_THREAD_LOCAL)
|
||||
static_assert(false, "thread_local is not supported");
|
||||
#endif
|
||||
void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
|
||||
{
|
||||
LogPrintf("LOCKCONTENTION: %s Locker: %s:%d\n", pszName, pszFile, nLine);
|
||||
@ -44,8 +46,8 @@ struct CLockLocation {
|
||||
return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : "");
|
||||
}
|
||||
|
||||
bool fTry;
|
||||
private:
|
||||
bool fTry;
|
||||
std::string mutexName;
|
||||
std::string sourceFile;
|
||||
int sourceLine;
|
||||
@ -66,10 +68,10 @@ struct LockData {
|
||||
|
||||
LockOrders lockorders;
|
||||
InvLockOrders invlockorders;
|
||||
boost::mutex dd_mutex;
|
||||
std::mutex dd_mutex;
|
||||
} static lockdata;
|
||||
|
||||
boost::thread_specific_ptr<LockStack> lockstack;
|
||||
static thread_local std::unique_ptr<LockStack> lockstack;
|
||||
|
||||
static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2)
|
||||
{
|
||||
@ -104,12 +106,12 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
|
||||
|
||||
static void push_lock(void* c, const CLockLocation& locklocation)
|
||||
{
|
||||
if (lockstack.get() == nullptr)
|
||||
if (!lockstack)
|
||||
lockstack.reset(new LockStack);
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex);
|
||||
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
||||
|
||||
(*lockstack).push_back(std::make_pair(c, locklocation));
|
||||
lockstack->push_back(std::make_pair(c, locklocation));
|
||||
|
||||
for (const std::pair<void*, CLockLocation> & i : (*lockstack)) {
|
||||
if (i.first == c)
|
||||
@ -175,7 +177,7 @@ void DeleteLock(void* cs)
|
||||
// We're already shutting down.
|
||||
return;
|
||||
}
|
||||
boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex);
|
||||
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
||||
std::pair<void*, void*> item = std::make_pair(cs, nullptr);
|
||||
LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
|
||||
while (it != lockdata.lockorders.end() && it->first.first == cs) {
|
||||
|
16
src/sync.h
16
src/sync.h
@ -8,8 +8,6 @@
|
||||
|
||||
#include <threadsafety.h>
|
||||
|
||||
#include <boost/thread/condition_variable.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
@ -196,8 +194,8 @@ public:
|
||||
class CSemaphore
|
||||
{
|
||||
private:
|
||||
boost::condition_variable condition;
|
||||
boost::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
std::mutex mutex;
|
||||
int value;
|
||||
|
||||
public:
|
||||
@ -205,16 +203,14 @@ public:
|
||||
|
||||
void wait()
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
while (value < 1) {
|
||||
condition.wait(lock);
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
condition.wait(lock, [&]() { return value >= 1; });
|
||||
value--;
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (value < 1)
|
||||
return false;
|
||||
value--;
|
||||
@ -224,7 +220,7 @@ public:
|
||||
void post()
|
||||
{
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
value++;
|
||||
}
|
||||
condition.notify_one();
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <boost/signals2/signal.hpp>
|
||||
#include <boost/thread/condition_variable.hpp> // for boost::thread_interrupted
|
||||
|
||||
// Debugging macros
|
||||
|
||||
|
@ -10,18 +10,16 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
extern std::unique_ptr<CWallet> pwalletMain;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup)
|
||||
|
||||
static void
|
||||
GetResults(std::map<CAmount, CAccountingEntry>& results)
|
||||
GetResults(CWallet *wallet, std::map<CAmount, CAccountingEntry>& results)
|
||||
{
|
||||
std::list<CAccountingEntry> aes;
|
||||
|
||||
results.clear();
|
||||
BOOST_CHECK(pwalletMain->ReorderTransactions() == DB_LOAD_OK);
|
||||
pwalletMain->ListAccountCreditDebit("", aes);
|
||||
BOOST_CHECK(wallet->ReorderTransactions() == DB_LOAD_OK);
|
||||
wallet->ListAccountCreditDebit("", aes);
|
||||
for (CAccountingEntry& ae : aes)
|
||||
{
|
||||
results[ae.nOrderPos] = ae;
|
||||
@ -54,7 +52,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||
ae.strOtherAccount = "c";
|
||||
pwalletMain->AddAccountingEntry(ae);
|
||||
|
||||
GetResults(results);
|
||||
GetResults(pwalletMain.get(), results);
|
||||
|
||||
BOOST_CHECK(pwalletMain->nOrderPosNext == 3);
|
||||
BOOST_CHECK(2 == results.size());
|
||||
@ -70,7 +68,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||
ae.nOrderPos = pwalletMain->IncOrderPosNext();
|
||||
pwalletMain->AddAccountingEntry(ae);
|
||||
|
||||
GetResults(results);
|
||||
GetResults(pwalletMain.get(), results);
|
||||
|
||||
BOOST_CHECK(results.size() == 3);
|
||||
BOOST_CHECK(pwalletMain->nOrderPosNext == 4);
|
||||
@ -102,7 +100,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||
vpwtx[2]->nTimeReceived = (unsigned int)1333333329;
|
||||
vpwtx[2]->nOrderPos = -1;
|
||||
|
||||
GetResults(results);
|
||||
GetResults(pwalletMain.get(), results);
|
||||
|
||||
BOOST_CHECK(results.size() == 3);
|
||||
BOOST_CHECK(pwalletMain->nOrderPosNext == 6);
|
||||
@ -120,7 +118,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||
ae.nOrderPos = -1;
|
||||
pwalletMain->AddAccountingEntry(ae);
|
||||
|
||||
GetResults(results);
|
||||
GetResults(pwalletMain.get(), results);
|
||||
|
||||
BOOST_CHECK(results.size() == 4);
|
||||
BOOST_CHECK(pwalletMain->nOrderPosNext == 7);
|
||||
|
@ -6,9 +6,6 @@
|
||||
|
||||
#include <rpc/server.h>
|
||||
#include <wallet/db.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
std::unique_ptr<CWallet> pwalletMain;
|
||||
|
||||
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
|
||||
TestingSetup(chainName)
|
||||
@ -27,7 +24,6 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
|
||||
WalletTestingSetup::~WalletTestingSetup()
|
||||
{
|
||||
UnregisterValidationInterface(pwalletMain.get());
|
||||
pwalletMain.reset();
|
||||
|
||||
bitdb.Flush(true);
|
||||
bitdb.Reset();
|
||||
|
@ -7,11 +7,15 @@
|
||||
|
||||
#include <test/test_dash.h>
|
||||
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
/** Testing setup and teardown for wallet.
|
||||
*/
|
||||
struct WalletTestingSetup: public TestingSetup {
|
||||
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
|
||||
~WalletTestingSetup();
|
||||
|
||||
std::unique_ptr<CWallet> pwalletMain;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -19,8 +19,6 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <univalue.h>
|
||||
|
||||
extern std::unique_ptr<CWallet> pwalletMain;
|
||||
|
||||
extern UniValue importmulti(const JSONRPCRequest& request);
|
||||
extern UniValue dumpwallet(const JSONRPCRequest& request);
|
||||
extern UniValue importwallet(const JSONRPCRequest& request);
|
||||
|
@ -5418,6 +5418,11 @@ int CMerkleTx::GetBlocksToMaturity() const
|
||||
|
||||
bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
|
||||
{
|
||||
// Quick check to avoid re-setting fInMempool to false
|
||||
if (mempool.exists(tx->GetHash())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We must set fInMempool here - while it will be re-set to true by the
|
||||
// entered-mempool callback, if we did not there would be a race where a
|
||||
// user could call sendmoney in a loop and hit spurious out of funds errors
|
||||
|
46
test/functional/feature_help.py
Executable file
46
test/functional/feature_help.py
Executable file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2018 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Verify that starting dashd with -h works as expected."""
|
||||
import subprocess
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
class HelpTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def setup_network(self):
|
||||
self.add_nodes(self.num_nodes)
|
||||
# Don't start the node
|
||||
|
||||
def run_test(self):
|
||||
self.log.info("Start dashd with -h for help text")
|
||||
self.nodes[0].start(extra_args=['-h'], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
# Node should exit immediately and output help to stdout.
|
||||
ret_code = self.nodes[0].process.wait(timeout=1)
|
||||
assert_equal(ret_code, 0)
|
||||
output = self.nodes[0].process.stdout.read()
|
||||
assert b'Options' in output
|
||||
self.log.info("Help text received: {} (...)".format(output[0:60]))
|
||||
self.nodes[0].running = False
|
||||
|
||||
self.log.info("Start dashd with -version for version information")
|
||||
self.nodes[0].start(extra_args=['-version'], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
# Node should exit immediately and output version to stdout.
|
||||
ret_code = self.nodes[0].process.wait(timeout=1)
|
||||
assert_equal(ret_code, 0)
|
||||
output = self.nodes[0].process.stdout.read()
|
||||
assert b'version' in output
|
||||
self.log.info("Version text received: {} (...)".format(output[0:60]))
|
||||
# Clean up TestNode state
|
||||
self.nodes[0].running = False
|
||||
self.nodes[0].process = None
|
||||
self.nodes[0].rpc_connected = False
|
||||
self.nodes[0].rpc = None
|
||||
|
||||
if __name__ == '__main__':
|
||||
HelpTest().main()
|
@ -57,21 +57,27 @@ class MempoolPersistTest(BitcoinTestFramework):
|
||||
self.log.debug("Send 5 transactions from node2 (to its own address)")
|
||||
for i in range(5):
|
||||
self.nodes[2].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("10"))
|
||||
node2_balance = self.nodes[2].getbalance()
|
||||
self.sync_all()
|
||||
|
||||
self.log.debug("Verify that node0 and node1 have 5 transactions in their mempools")
|
||||
assert_equal(len(self.nodes[0].getrawmempool()), 5)
|
||||
assert_equal(len(self.nodes[1].getrawmempool()), 5)
|
||||
|
||||
self.log.debug("Stop-start node0 and node1. Verify that node0 has the transactions in its mempool and node1 does not.")
|
||||
self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.")
|
||||
self.stop_nodes()
|
||||
self.start_node(0)
|
||||
self.start_node(1)
|
||||
self.start_node(2)
|
||||
# Give dashd a second to reload the mempool
|
||||
time.sleep(1)
|
||||
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
|
||||
wait_until(lambda: len(self.nodes[2].getrawmempool()) == 5)
|
||||
assert_equal(len(self.nodes[1].getrawmempool()), 0)
|
||||
|
||||
# Verify accounting of mempool transactions after restart is correct
|
||||
assert_equal(node2_balance, self.nodes[2].getbalance())
|
||||
|
||||
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
|
||||
self.stop_nodes()
|
||||
self.start_node(0, extra_args=["-persistmempool=0"])
|
||||
|
@ -7,8 +7,6 @@
|
||||
Tests correspond to code in rpc/net.cpp.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
@ -71,12 +69,8 @@ class NetTest(BitcoinTestFramework):
|
||||
|
||||
self.nodes[0].setnetworkactive(False)
|
||||
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], False)
|
||||
timeout = 3
|
||||
while self.nodes[0].getnetworkinfo()['connections'] != 0:
|
||||
# Wait a bit for all sockets to close
|
||||
assert timeout > 0, 'not all connections closed in time'
|
||||
timeout -= 0.1
|
||||
time.sleep(0.1)
|
||||
# Wait a bit for all sockets to close
|
||||
wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == 0, timeout=3)
|
||||
|
||||
self.nodes[0].setnetworkactive(True)
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
|
@ -11,7 +11,6 @@ This test takes 30 mins or more (up to 2 hours)
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
|
||||
@ -24,7 +23,7 @@ TIMESTAMP_WINDOW = 2 * 60 * 60
|
||||
|
||||
|
||||
def calc_usage(blockdir):
|
||||
return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f)) / (1024. * 1024.)
|
||||
return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(os.path.join(blockdir, f))) / (1024. * 1024.)
|
||||
|
||||
class PruneTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
@ -71,7 +70,7 @@ class PruneTest(BitcoinTestFramework):
|
||||
sync_blocks(self.nodes[0:5])
|
||||
|
||||
def test_height_min(self):
|
||||
if not os.path.isfile(self.prunedir+"blk00000.dat"):
|
||||
if not os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")):
|
||||
raise AssertionError("blk00000.dat is missing, pruning too early")
|
||||
self.log.info("Success")
|
||||
self.log.info("Though we're already using more than 550MiB, current usage: %d" % calc_usage(self.prunedir))
|
||||
@ -80,11 +79,8 @@ class PruneTest(BitcoinTestFramework):
|
||||
for i in range(25):
|
||||
mine_large_block(self.nodes[0], self.utxo_cache_0)
|
||||
|
||||
waitstart = time.time()
|
||||
while os.path.isfile(self.prunedir+"blk00000.dat"):
|
||||
time.sleep(0.1)
|
||||
if time.time() - waitstart > 30:
|
||||
raise AssertionError("blk00000.dat not pruned when it should be")
|
||||
# Wait for blk00000.dat to be pruned
|
||||
wait_until(lambda: not os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")), timeout=30)
|
||||
|
||||
self.log.info("Success")
|
||||
usage = calc_usage(self.prunedir)
|
||||
@ -219,11 +215,8 @@ class PruneTest(BitcoinTestFramework):
|
||||
goalbestheight = first_reorg_height + 1
|
||||
|
||||
self.log.info("Verify node 2 reorged back to the main chain, some blocks of which it had to redownload")
|
||||
waitstart = time.time()
|
||||
while self.nodes[2].getblockcount() < goalbestheight:
|
||||
time.sleep(0.1)
|
||||
if time.time() - waitstart > 900:
|
||||
raise AssertionError("Node 2 didn't reorg to proper height")
|
||||
# Wait for Node 2 to reorg to proper height
|
||||
wait_until(lambda: self.nodes[2].getblockcount() >= goalbestheight, timeout=900)
|
||||
assert(self.nodes[2].getbestblockhash() == goalbesthash)
|
||||
# Verify we can now have the data for a block previously pruned
|
||||
assert(self.nodes[2].getblock(self.forkhash)["height"] == self.forkheight)
|
||||
@ -263,7 +256,7 @@ class PruneTest(BitcoinTestFramework):
|
||||
assert_equal(ret, expected_ret)
|
||||
|
||||
def has_block(index):
|
||||
return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index))
|
||||
return os.path.isfile(os.path.join(self.nodes[node_number].datadir, "regtest", "blocks", "blk{:05}.dat".format(index)))
|
||||
|
||||
# should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000)
|
||||
assert_raises_rpc_error(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500))
|
||||
|
@ -10,8 +10,7 @@
|
||||
"""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
import time
|
||||
from test_framework.util import wait_until
|
||||
|
||||
class ReindexTest(BitcoinTestFramework):
|
||||
|
||||
@ -25,9 +24,7 @@ class ReindexTest(BitcoinTestFramework):
|
||||
self.stop_nodes()
|
||||
extra_args = [["-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]]
|
||||
self.start_nodes(extra_args)
|
||||
while self.nodes[0].getblockcount() < blockcount:
|
||||
time.sleep(0.1)
|
||||
assert_equal(self.nodes[0].getblockcount(), blockcount)
|
||||
wait_until(lambda: self.nodes[0].getblockcount() == blockcount)
|
||||
self.log.info("Success")
|
||||
|
||||
def run_test(self):
|
||||
|
@ -4,11 +4,13 @@
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test behavior of headers messages to announce blocks.
|
||||
|
||||
Setup:
|
||||
Setup:
|
||||
|
||||
- Two nodes, two p2p connections to node0. One p2p connection should only ever
|
||||
receive inv's (omitted from testing description below, this is our control).
|
||||
Second node is used for creating reorgs.
|
||||
- Two nodes:
|
||||
- node0 is the node-under-test. We create two p2p connections to it. The
|
||||
first p2p connection is a control and should only ever receive inv's. The
|
||||
second p2p connection tests the headers sending logic.
|
||||
- node1 is used to create reorgs.
|
||||
|
||||
test_null_locators
|
||||
==================
|
||||
@ -83,35 +85,44 @@ d. Announce 49 headers that don't connect.
|
||||
e. Announce one more that doesn't connect.
|
||||
Expect: disconnect.
|
||||
"""
|
||||
|
||||
from test_framework.mininode import *
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
from test_framework.blocktools import create_block, create_coinbase
|
||||
from test_framework.mininode import (
|
||||
CBlockHeader,
|
||||
CInv,
|
||||
network_thread_start,
|
||||
NodeConnCB,
|
||||
mininode_lock,
|
||||
msg_block,
|
||||
msg_getblocks,
|
||||
msg_getdata,
|
||||
msg_getheaders,
|
||||
msg_headers,
|
||||
msg_inv,
|
||||
msg_sendheaders,
|
||||
)
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
sync_blocks,
|
||||
wait_until,
|
||||
)
|
||||
|
||||
DIRECT_FETCH_RESPONSE_TIME = 0.05
|
||||
|
||||
direct_fetch_response_time = 0.05
|
||||
|
||||
class TestNode(NodeConnCB):
|
||||
class BaseNode(NodeConnCB):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.block_announced = False
|
||||
self.last_blockhash_announced = None
|
||||
|
||||
def clear_last_announcement(self):
|
||||
with mininode_lock:
|
||||
self.block_announced = False
|
||||
self.last_message.pop("inv", None)
|
||||
self.last_message.pop("headers", None)
|
||||
|
||||
# Request data for a list of block hashes
|
||||
def get_data(self, block_hashes):
|
||||
def send_get_data(self, block_hashes):
|
||||
"""Request data for a list of block hashes."""
|
||||
msg = msg_getdata()
|
||||
for x in block_hashes:
|
||||
msg.inv.append(CInv(2, x))
|
||||
self.connection.send_message(msg)
|
||||
|
||||
def get_headers(self, locator, hashstop):
|
||||
def send_get_headers(self, locator, hashstop):
|
||||
msg = msg_getheaders()
|
||||
msg.locator.vHave = locator
|
||||
msg.hashstop = hashstop
|
||||
@ -122,6 +133,27 @@ class TestNode(NodeConnCB):
|
||||
msg.inv = [CInv(2, blockhash)]
|
||||
self.connection.send_message(msg)
|
||||
|
||||
def send_header_for_blocks(self, new_blocks):
|
||||
headers_message = msg_headers()
|
||||
headers_message.headers = [CBlockHeader(b) for b in new_blocks]
|
||||
self.send_message(headers_message)
|
||||
|
||||
def send_getblocks(self, locator):
|
||||
getblocks_message = msg_getblocks()
|
||||
getblocks_message.locator.vHave = locator
|
||||
self.send_message(getblocks_message)
|
||||
|
||||
def wait_for_getdata(self, hash_list, timeout=60):
|
||||
if hash_list == []:
|
||||
return
|
||||
|
||||
test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list
|
||||
wait_until(test_function, timeout=timeout, lock=mininode_lock)
|
||||
|
||||
def wait_for_block_announcement(self, block_hash, timeout=60):
|
||||
test_function = lambda: self.last_blockhash_announced == block_hash
|
||||
wait_until(test_function, timeout=timeout, lock=mininode_lock)
|
||||
|
||||
def on_inv(self, conn, message):
|
||||
self.block_announced = True
|
||||
self.last_blockhash_announced = message.inv[-1].hash
|
||||
@ -132,95 +164,77 @@ class TestNode(NodeConnCB):
|
||||
message.headers[-1].calc_sha256()
|
||||
self.last_blockhash_announced = message.headers[-1].sha256
|
||||
|
||||
# Test whether the last announcement we received had the
|
||||
# right header or the right inv
|
||||
# inv and headers should be lists of block hashes
|
||||
def clear_last_announcement(self):
|
||||
with mininode_lock:
|
||||
self.block_announced = False
|
||||
self.last_message.pop("inv", None)
|
||||
self.last_message.pop("headers", None)
|
||||
|
||||
def check_last_announcement(self, headers=None, inv=None):
|
||||
expect_headers = headers if headers != None else []
|
||||
expect_inv = inv if inv != None else []
|
||||
"""Test whether the last announcement received had the right header or the right inv.
|
||||
|
||||
inv and headers should be lists of block hashes."""
|
||||
|
||||
test_function = lambda: self.block_announced
|
||||
wait_until(test_function, timeout=60, lock=mininode_lock)
|
||||
|
||||
with mininode_lock:
|
||||
self.block_announced = False
|
||||
|
||||
success = True
|
||||
compare_inv = []
|
||||
if "inv" in self.last_message:
|
||||
compare_inv = [x.hash for x in self.last_message["inv"].inv]
|
||||
if compare_inv != expect_inv:
|
||||
success = False
|
||||
if inv is not None:
|
||||
assert_equal(compare_inv, inv)
|
||||
|
||||
hash_headers = []
|
||||
compare_headers = []
|
||||
if "headers" in self.last_message:
|
||||
# treat headers as a list of block hashes
|
||||
hash_headers = [ x.sha256 for x in self.last_message["headers"].headers ]
|
||||
if hash_headers != expect_headers:
|
||||
success = False
|
||||
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("headers", None)
|
||||
return success
|
||||
|
||||
def wait_for_getdata(self, hash_list, timeout=60):
|
||||
if hash_list == []:
|
||||
return
|
||||
|
||||
test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list
|
||||
wait_until(test_function, timeout=timeout, lock=mininode_lock)
|
||||
return
|
||||
|
||||
def wait_for_block_announcement(self, block_hash, timeout=60):
|
||||
test_function = lambda: self.last_blockhash_announced == block_hash
|
||||
wait_until(test_function, timeout=timeout, lock=mininode_lock)
|
||||
return
|
||||
|
||||
def send_header_for_blocks(self, new_blocks):
|
||||
headers_message = msg_headers()
|
||||
headers_message.headers = [ CBlockHeader(b) for b in new_blocks ]
|
||||
self.send_message(headers_message)
|
||||
|
||||
def send_getblocks(self, locator):
|
||||
getblocks_message = msg_getblocks()
|
||||
getblocks_message.locator.vHave = locator
|
||||
self.send_message(getblocks_message)
|
||||
|
||||
class SendHeadersTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 2
|
||||
|
||||
# mine count blocks and return the new tip
|
||||
def mine_blocks(self, count):
|
||||
"""Mine count blocks and return the new tip."""
|
||||
|
||||
# Clear out last block announcement from each p2p listener
|
||||
[x.clear_last_announcement() for x in self.nodes[0].p2ps]
|
||||
self.nodes[0].generate(count)
|
||||
return int(self.nodes[0].getbestblockhash(), 16)
|
||||
|
||||
# mine a reorg that invalidates length blocks (replacing them with
|
||||
# length+1 blocks).
|
||||
# Note: we clear the state of our p2p connections after the
|
||||
# to-be-reorged-out blocks are mined, so that we don't break later tests.
|
||||
# return the list of block hashes newly mined
|
||||
def mine_reorg(self, length):
|
||||
self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
|
||||
"""Mine a reorg that invalidates length blocks (replacing them with # length+1 blocks).
|
||||
|
||||
Note: we clear the state of our p2p connections after the
|
||||
to-be-reorged-out blocks are mined, so that we don't break later tests.
|
||||
return the list of block hashes newly mined."""
|
||||
|
||||
self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
|
||||
sync_blocks(self.nodes, wait=0.1)
|
||||
for x in self.nodes[0].p2ps:
|
||||
x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16))
|
||||
x.clear_last_announcement()
|
||||
|
||||
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))
|
||||
self.nodes[1].invalidateblock(hash_to_invalidate)
|
||||
all_hashes = self.nodes[1].generate(length+1) # Must be longer than the orig chain
|
||||
all_hashes = self.nodes[1].generate(length + 1) # Must be longer than the orig chain
|
||||
sync_blocks(self.nodes, wait=0.1)
|
||||
return [int(x, 16) for x in all_hashes]
|
||||
|
||||
def run_test(self):
|
||||
# Setup the p2p connections and start up the network thread.
|
||||
inv_node = self.nodes[0].add_p2p_connection(TestNode())
|
||||
# Set nServices to 0 for test_node, so no block download will occur outside of
|
||||
# direct fetching
|
||||
test_node = self.nodes[0].add_p2p_connection(TestNode(), services=0)
|
||||
inv_node = self.nodes[0].add_p2p_connection(BaseNode())
|
||||
# Make sure NODE_NETWORK is not set for test_node, so no block download
|
||||
# will occur outside of direct fetching
|
||||
test_node = self.nodes[0].add_p2p_connection(BaseNode(), services=0)
|
||||
|
||||
network_thread_start()
|
||||
|
||||
@ -232,32 +246,32 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
inv_node.sync_with_ping()
|
||||
test_node.sync_with_ping()
|
||||
|
||||
self.test_null_locators(test_node)
|
||||
self.test_null_locators(test_node, inv_node)
|
||||
self.test_nonnull_locators(test_node, inv_node)
|
||||
|
||||
def test_null_locators(self, test_node):
|
||||
def test_null_locators(self, test_node, inv_node):
|
||||
tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0])
|
||||
tip_hash = int(tip["hash"], 16)
|
||||
|
||||
# TODO this partly fixes the same thing that is fixed by https://github.com/bitcoin/bitcoin/pull/13192
|
||||
# This will later conflict when backporting the actual fix. Just take everything from the Bitcoin fix as a
|
||||
# resolution
|
||||
assert_equal(test_node.check_last_announcement(headers=[], inv=[tip_hash]), True)
|
||||
inv_node.check_last_announcement(inv=[tip_hash], headers=[])
|
||||
test_node.check_last_announcement(inv=[tip_hash], headers=[])
|
||||
|
||||
self.log.info("Verify getheaders with null locator and valid hashstop returns headers.")
|
||||
test_node.clear_last_announcement()
|
||||
test_node.get_headers(locator=[], hashstop=tip_hash)
|
||||
assert_equal(test_node.check_last_announcement(headers=[tip_hash]), True)
|
||||
test_node.send_get_headers(locator=[], hashstop=tip_hash)
|
||||
test_node.check_last_announcement(headers=[tip_hash])
|
||||
|
||||
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.solve()
|
||||
test_node.send_header_for_blocks([block])
|
||||
test_node.clear_last_announcement()
|
||||
test_node.get_headers(locator=[], hashstop=int(block.hash, 16))
|
||||
test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16))
|
||||
test_node.sync_with_ping()
|
||||
assert_equal(test_node.block_announced, False)
|
||||
inv_node.clear_last_announcement()
|
||||
test_node.send_message(msg_block(block))
|
||||
inv_node.check_last_announcement(inv=[int(block.hash, 16)], headers=[])
|
||||
|
||||
def test_nonnull_locators(self, test_node, inv_node):
|
||||
tip = int(self.nodes[0].getbestblockhash(), 16)
|
||||
@ -268,30 +282,30 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
for i in range(4):
|
||||
old_tip = tip
|
||||
tip = self.mine_blocks(1)
|
||||
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
|
||||
assert_equal(test_node.check_last_announcement(inv=[tip]), True)
|
||||
inv_node.check_last_announcement(inv=[tip], headers=[])
|
||||
test_node.check_last_announcement(inv=[tip], headers=[])
|
||||
# Try a few different responses; none should affect next announcement
|
||||
if i == 0:
|
||||
# first request the block
|
||||
test_node.get_data([tip])
|
||||
test_node.send_get_data([tip])
|
||||
test_node.wait_for_block(tip)
|
||||
elif i == 1:
|
||||
# next try requesting header and block
|
||||
test_node.get_headers(locator=[old_tip], hashstop=tip)
|
||||
test_node.get_data([tip])
|
||||
test_node.send_get_headers(locator=[old_tip], hashstop=tip)
|
||||
test_node.send_get_data([tip])
|
||||
test_node.wait_for_block(tip)
|
||||
test_node.clear_last_announcement() # since we requested headers...
|
||||
test_node.clear_last_announcement() # since we requested headers...
|
||||
elif i == 2:
|
||||
# this time announce own block via headers
|
||||
height = self.nodes[0].getblockcount()
|
||||
last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time']
|
||||
block_time = last_time + 1
|
||||
new_block = create_block(tip, create_coinbase(height+1), block_time)
|
||||
new_block = create_block(tip, create_coinbase(height + 1), block_time)
|
||||
new_block.solve()
|
||||
test_node.send_header_for_blocks([new_block])
|
||||
test_node.wait_for_getdata([new_block.sha256])
|
||||
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()
|
||||
test_node.clear_last_announcement()
|
||||
|
||||
@ -302,15 +316,15 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
# commence and keep working.
|
||||
test_node.send_message(msg_sendheaders())
|
||||
prev_tip = int(self.nodes[0].getbestblockhash(), 16)
|
||||
test_node.get_headers(locator=[prev_tip], hashstop=0)
|
||||
test_node.send_get_headers(locator=[prev_tip], hashstop=0)
|
||||
test_node.sync_with_ping()
|
||||
|
||||
# Now that we've synced headers, headers announcements should work
|
||||
tip = self.mine_blocks(1)
|
||||
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
|
||||
assert_equal(test_node.check_last_announcement(headers=[tip]), True)
|
||||
inv_node.check_last_announcement(inv=[tip], headers=[])
|
||||
test_node.check_last_announcement(headers=[tip])
|
||||
|
||||
height = self.nodes[0].getblockcount()+1
|
||||
height = self.nodes[0].getblockcount() + 1
|
||||
block_time += 10 # Advance far enough ahead
|
||||
for i in range(10):
|
||||
# Mine i blocks, and alternate announcing either via
|
||||
@ -319,7 +333,7 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
# with block header, even though the blocks are never requested
|
||||
for j in range(2):
|
||||
blocks = []
|
||||
for b in range(i+1):
|
||||
for b in range(i + 1):
|
||||
blocks.append(create_block(tip, create_coinbase(height), block_time))
|
||||
blocks[-1].solve()
|
||||
tip = blocks[-1].sha256
|
||||
@ -333,7 +347,7 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
test_node.send_header_for_blocks(blocks)
|
||||
# Test that duplicate inv's won't result in duplicate
|
||||
# getdata requests, or duplicate headers announcements
|
||||
[ inv_node.send_block_inv(x.sha256) for x in blocks ]
|
||||
[inv_node.send_block_inv(x.sha256) for x in blocks]
|
||||
test_node.wait_for_getdata([x.sha256 for x in blocks])
|
||||
inv_node.sync_with_ping()
|
||||
else:
|
||||
@ -344,7 +358,7 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
# getdata requests (the check is further down)
|
||||
inv_node.send_header_for_blocks(blocks)
|
||||
inv_node.sync_with_ping()
|
||||
[ test_node.send_message(msg_block(x)) for x in blocks ]
|
||||
[test_node.send_message(msg_block(x)) for x in blocks]
|
||||
test_node.sync_with_ping()
|
||||
inv_node.sync_with_ping()
|
||||
# This block should not be announced to the inv node (since it also
|
||||
@ -352,8 +366,8 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
assert "inv" not in inv_node.last_message
|
||||
assert "headers" not in inv_node.last_message
|
||||
tip = self.mine_blocks(1)
|
||||
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
|
||||
assert_equal(test_node.check_last_announcement(headers=[tip]), True)
|
||||
inv_node.check_last_announcement(inv=[tip], headers=[])
|
||||
test_node.check_last_announcement(headers=[tip])
|
||||
height += 1
|
||||
block_time += 1
|
||||
|
||||
@ -367,16 +381,16 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
# First try mining a reorg that can propagate with header announcement
|
||||
new_block_hashes = self.mine_reorg(length=7)
|
||||
tip = new_block_hashes[-1]
|
||||
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
|
||||
assert_equal(test_node.check_last_announcement(headers=new_block_hashes), True)
|
||||
inv_node.check_last_announcement(inv=[tip], headers=[])
|
||||
test_node.check_last_announcement(headers=new_block_hashes)
|
||||
|
||||
block_time += 8
|
||||
block_time += 8
|
||||
|
||||
# Mine a too-large reorg, which should be announced with a single inv
|
||||
new_block_hashes = self.mine_reorg(length=8)
|
||||
tip = new_block_hashes[-1]
|
||||
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
|
||||
assert_equal(test_node.check_last_announcement(inv=[tip]), True)
|
||||
inv_node.check_last_announcement(inv=[tip], headers=[])
|
||||
test_node.check_last_announcement(inv=[tip], headers=[])
|
||||
|
||||
block_time += 9
|
||||
|
||||
@ -384,42 +398,42 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
fork_point = int(fork_point, 16)
|
||||
|
||||
# Use getblocks/getdata
|
||||
test_node.send_getblocks(locator = [fork_point])
|
||||
assert_equal(test_node.check_last_announcement(inv=new_block_hashes), True)
|
||||
test_node.get_data(new_block_hashes)
|
||||
test_node.send_getblocks(locator=[fork_point])
|
||||
test_node.check_last_announcement(inv=new_block_hashes, headers=[])
|
||||
test_node.send_get_data(new_block_hashes)
|
||||
test_node.wait_for_block(new_block_hashes[-1])
|
||||
|
||||
for i in range(3):
|
||||
# Mine another block, still should get only an inv
|
||||
tip = self.mine_blocks(1)
|
||||
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
|
||||
assert_equal(test_node.check_last_announcement(inv=[tip]), True)
|
||||
inv_node.check_last_announcement(inv=[tip], headers=[])
|
||||
test_node.check_last_announcement(inv=[tip], headers=[])
|
||||
if i == 0:
|
||||
# Just get the data -- shouldn't cause headers announcements to resume
|
||||
test_node.get_data([tip])
|
||||
test_node.send_get_data([tip])
|
||||
test_node.wait_for_block(tip)
|
||||
elif i == 1:
|
||||
# Send a getheaders message that shouldn't trigger headers announcements
|
||||
# to resume (best header sent will be too old)
|
||||
test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1])
|
||||
test_node.get_data([tip])
|
||||
test_node.send_get_headers(locator=[fork_point], hashstop=new_block_hashes[1])
|
||||
test_node.send_get_data([tip])
|
||||
test_node.wait_for_block(tip)
|
||||
elif i == 2:
|
||||
test_node.get_data([tip])
|
||||
test_node.send_get_data([tip])
|
||||
test_node.wait_for_block(tip)
|
||||
# This time, try sending either a getheaders to trigger resumption
|
||||
# of headers announcements, or mine a new block and inv it, also
|
||||
# of headers announcements, or mine a new block and inv it, also
|
||||
# triggering resumption of headers announcements.
|
||||
if j == 0:
|
||||
test_node.get_headers(locator=[tip], hashstop=0)
|
||||
test_node.send_get_headers(locator=[tip], hashstop=0)
|
||||
test_node.sync_with_ping()
|
||||
else:
|
||||
test_node.send_block_inv(tip)
|
||||
test_node.sync_with_ping()
|
||||
# New blocks should now be announced with header
|
||||
tip = self.mine_blocks(1)
|
||||
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
|
||||
assert_equal(test_node.check_last_announcement(headers=[tip]), True)
|
||||
inv_node.check_last_announcement(inv=[tip], headers=[])
|
||||
test_node.check_last_announcement(headers=[tip])
|
||||
|
||||
self.log.info("Part 3: success!")
|
||||
|
||||
@ -439,7 +453,7 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
height += 1
|
||||
inv_node.send_message(msg_block(blocks[-1]))
|
||||
|
||||
inv_node.sync_with_ping() # Make sure blocks are processed
|
||||
inv_node.sync_with_ping() # Make sure blocks are processed
|
||||
test_node.last_message.pop("getdata", None)
|
||||
test_node.send_header_for_blocks(blocks)
|
||||
test_node.sync_with_ping()
|
||||
@ -458,9 +472,9 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
|
||||
test_node.send_header_for_blocks(blocks)
|
||||
test_node.sync_with_ping()
|
||||
test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=direct_fetch_response_time)
|
||||
test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=DIRECT_FETCH_RESPONSE_TIME)
|
||||
|
||||
[ test_node.send_message(msg_block(x)) for x in blocks ]
|
||||
[test_node.send_message(msg_block(x)) for x in blocks]
|
||||
|
||||
test_node.sync_with_ping()
|
||||
|
||||
@ -489,13 +503,13 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
# both blocks (same work as tip)
|
||||
test_node.send_header_for_blocks(blocks[1:2])
|
||||
test_node.sync_with_ping()
|
||||
test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=direct_fetch_response_time)
|
||||
test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=DIRECT_FETCH_RESPONSE_TIME)
|
||||
|
||||
# Announcing 16 more headers should trigger direct fetch for 14 more
|
||||
# blocks
|
||||
test_node.send_header_for_blocks(blocks[2:18])
|
||||
test_node.sync_with_ping()
|
||||
test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=direct_fetch_response_time)
|
||||
test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=DIRECT_FETCH_RESPONSE_TIME)
|
||||
|
||||
# Announcing 1 more header should not trigger any response
|
||||
test_node.last_message.pop("getdata", None)
|
||||
@ -507,7 +521,7 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
self.log.info("Part 4: success!")
|
||||
|
||||
# Now deliver all those blocks we announced.
|
||||
[ test_node.send_message(msg_block(x)) for x in blocks ]
|
||||
[test_node.send_message(msg_block(x)) for x in blocks]
|
||||
|
||||
self.log.info("Part 5: Testing handling of unconnecting headers")
|
||||
# First we test that receipt of an unconnecting header doesn't prevent
|
||||
@ -529,7 +543,7 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
test_node.wait_for_getheaders()
|
||||
test_node.send_header_for_blocks(blocks)
|
||||
test_node.wait_for_getdata([x.sha256 for x in blocks])
|
||||
[ test_node.send_message(msg_block(x)) for x in blocks ]
|
||||
[test_node.send_message(msg_block(x)) for x in blocks]
|
||||
test_node.sync_with_ping()
|
||||
assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256)
|
||||
|
||||
@ -537,7 +551,7 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
# Now we test that if we repeatedly don't send connecting headers, we
|
||||
# don't go into an infinite loop trying to get them to connect.
|
||||
MAX_UNCONNECTING_HEADERS = 10
|
||||
for j in range(MAX_UNCONNECTING_HEADERS+1):
|
||||
for j in range(MAX_UNCONNECTING_HEADERS + 1):
|
||||
blocks.append(create_block(tip, create_coinbase(height), block_time))
|
||||
blocks[-1].solve()
|
||||
tip = blocks[-1].sha256
|
||||
@ -559,11 +573,11 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
|
||||
# Now try to see how many unconnecting headers we can send
|
||||
# before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS
|
||||
for i in range(5*MAX_UNCONNECTING_HEADERS - 1):
|
||||
for i in range(5 * MAX_UNCONNECTING_HEADERS - 1):
|
||||
# Send a header that doesn't connect, check that we get a getheaders.
|
||||
with mininode_lock:
|
||||
test_node.last_message.pop("getheaders", None)
|
||||
test_node.send_header_for_blocks([blocks[i%len(blocks)]])
|
||||
test_node.send_header_for_blocks([blocks[i % len(blocks)]])
|
||||
test_node.wait_for_getheaders()
|
||||
|
||||
# Eventually this stops working.
|
||||
|
@ -527,11 +527,9 @@ class CScript(bytes):
|
||||
yield CScriptOp(opcode)
|
||||
|
||||
def __repr__(self):
|
||||
# For Python3 compatibility add b before strings so testcases don't
|
||||
# need to change
|
||||
def _repr(o):
|
||||
if isinstance(o, bytes):
|
||||
return b"x('%s')" % hexlify(o).decode('ascii')
|
||||
return "x('%s')" % hexlify(o).decode('ascii')
|
||||
else:
|
||||
return repr(o)
|
||||
|
||||
|
@ -31,8 +31,8 @@ from .util import (
|
||||
copy_datadir,
|
||||
disconnect_nodes,
|
||||
force_finish_mnsync,
|
||||
get_datadir_path,
|
||||
initialize_datadir,
|
||||
log_filename,
|
||||
p2p_port,
|
||||
set_node_times,
|
||||
satoshi_round,
|
||||
@ -160,6 +160,8 @@ class BitcoinTestFramework():
|
||||
success = False
|
||||
self.log.exception("Unexpected exception caught during shutdown")
|
||||
else:
|
||||
for node in self.nodes:
|
||||
node.cleanup_on_exit = False
|
||||
self.log.info("Note: dashds were not stopped and may still be running")
|
||||
|
||||
if not self.options.nocleanup and not self.options.noshutdown and success != TestStatus.FAILED:
|
||||
@ -406,7 +408,7 @@ class BitcoinTestFramework():
|
||||
assert self.num_nodes <= MAX_NODES
|
||||
create_cache = False
|
||||
for i in range(MAX_NODES):
|
||||
if not os.path.isdir(os.path.join(self.options.cachedir, 'node' + str(i))):
|
||||
if not os.path.isdir(get_datadir_path(self.options.cachedir, i)):
|
||||
create_cache = True
|
||||
break
|
||||
|
||||
@ -415,8 +417,8 @@ class BitcoinTestFramework():
|
||||
|
||||
# find and delete old cache directories if any exist
|
||||
for i in range(MAX_NODES):
|
||||
if os.path.isdir(os.path.join(self.options.cachedir, "node" + str(i))):
|
||||
shutil.rmtree(os.path.join(self.options.cachedir, "node" + str(i)))
|
||||
if os.path.isdir(get_datadir_path(self.options.cachedir, i)):
|
||||
shutil.rmtree(get_datadir_path(self.options.cachedir, i))
|
||||
|
||||
# Create cache directories, run dashds:
|
||||
self.set_genesis_mocktime()
|
||||
@ -456,15 +458,18 @@ class BitcoinTestFramework():
|
||||
self.stop_nodes()
|
||||
self.nodes = []
|
||||
self.disable_mocktime()
|
||||
|
||||
def cache_path(n, *paths):
|
||||
return os.path.join(get_datadir_path(self.options.cachedir, n), "regtest", *paths)
|
||||
|
||||
for i in range(MAX_NODES):
|
||||
os.remove(log_filename(self.options.cachedir, i, "debug.log"))
|
||||
os.remove(log_filename(self.options.cachedir, i, "wallets/db.log"))
|
||||
os.remove(log_filename(self.options.cachedir, i, "peers.dat"))
|
||||
os.remove(log_filename(self.options.cachedir, i, "fee_estimates.dat"))
|
||||
for entry in os.listdir(cache_path(i)):
|
||||
if entry not in ['wallets', 'chainstate', 'blocks', 'evodb', 'llmq', 'backups']:
|
||||
os.remove(cache_path(i, entry))
|
||||
|
||||
for i in range(self.num_nodes):
|
||||
from_dir = os.path.join(self.options.cachedir, "node" + str(i))
|
||||
to_dir = os.path.join(self.options.tmpdir, "node" + str(i))
|
||||
from_dir = get_datadir_path(self.options.cachedir, i)
|
||||
to_dir = get_datadir_path(self.options.tmpdir, i)
|
||||
shutil.copytree(from_dir, to_dir)
|
||||
initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in dash.conf
|
||||
|
||||
|
@ -18,6 +18,7 @@ from .authproxy import JSONRPCException
|
||||
from .mininode import NodeConn
|
||||
from .util import (
|
||||
assert_equal,
|
||||
delete_cookie_file,
|
||||
get_rpc_proxy,
|
||||
rpc_url,
|
||||
wait_until,
|
||||
@ -74,9 +75,20 @@ class TestNode():
|
||||
self.rpc = None
|
||||
self.url = None
|
||||
self.log = logging.getLogger('TestFramework.node%d' % i)
|
||||
self.cleanup_on_exit = True # Whether to kill the node when this object goes away
|
||||
|
||||
self.p2ps = []
|
||||
|
||||
def __del__(self):
|
||||
# Ensure that we don't leave any bitcoind processes lying around after
|
||||
# the test ends
|
||||
if self.process and self.cleanup_on_exit:
|
||||
# Should only happen on test failure
|
||||
# Avoid using logger, as that may have already been shutdown when
|
||||
# this destructor is called.
|
||||
print("Cleaning up leftover process")
|
||||
self.process.kill()
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Dispatches any unrecognised messages to the RPC connection or a CLI instance."""
|
||||
if self.use_cli:
|
||||
@ -91,6 +103,10 @@ class TestNode():
|
||||
extra_args = self.extra_args
|
||||
if stderr is None:
|
||||
stderr = self.stderr
|
||||
# Delete any existing cookie file -- if such a file exists (eg due to
|
||||
# unclean shutdown), it will get overwritten anyway by bitcoind, and
|
||||
# potentially interfere with our attempt to authenticate
|
||||
delete_cookie_file(self.datadir)
|
||||
self.process = subprocess.Popen(self.args + extra_args, stderr=stderr, *args, **kwargs)
|
||||
self.running = True
|
||||
self.log.debug("dashd started, waiting for RPC to come up")
|
||||
|
@ -29,7 +29,7 @@ logger = logging.getLogger("TestFramework.utils")
|
||||
|
||||
def assert_fee_amount(fee, tx_size, fee_per_kB):
|
||||
"""Assert the fee was in range"""
|
||||
target_fee = tx_size * fee_per_kB / 1000
|
||||
target_fee = round(tx_size * fee_per_kB / 1000, 8)
|
||||
if fee < target_fee:
|
||||
raise AssertionError("Fee of %s DASH too low! (Should be %s DASH)" % (str(fee), str(target_fee)))
|
||||
# allow the wallet's estimation to be at most 2 bytes off
|
||||
@ -346,8 +346,11 @@ def copy_datadir(from_node, to_node, dirname):
|
||||
except:
|
||||
pass
|
||||
|
||||
def log_filename(dirname, n_node, logname):
|
||||
return os.path.join(dirname, "node" + str(n_node), "regtest", logname)
|
||||
# If a cookie file exists in the given datadir, delete it.
|
||||
def delete_cookie_file(datadir):
|
||||
if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
|
||||
logger.debug("Deleting leftover cookie file")
|
||||
os.remove(os.path.join(datadir, "regtest", ".cookie"))
|
||||
|
||||
def get_bip9_status(node, key):
|
||||
info = node.getblockchaininfo()
|
||||
@ -361,20 +364,15 @@ def disconnect_nodes(from_connection, node_num):
|
||||
for peer_id in [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']]:
|
||||
from_connection.disconnectnode(nodeid=peer_id)
|
||||
|
||||
for _ in range(50):
|
||||
if [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == []:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
raise AssertionError("timed out waiting for disconnect")
|
||||
# wait to disconnect
|
||||
wait_until(lambda: [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == [], timeout=5)
|
||||
|
||||
def connect_nodes(from_connection, node_num):
|
||||
ip_port = "127.0.0.1:" + str(p2p_port(node_num))
|
||||
from_connection.addnode(ip_port, "onetry")
|
||||
# poll until version handshake complete to avoid race conditions
|
||||
# with transaction relaying
|
||||
while any(peer['version'] == 0 for peer in from_connection.getpeerinfo()):
|
||||
time.sleep(0.1)
|
||||
wait_until(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
|
||||
|
||||
def connect_nodes_bi(nodes, a, b):
|
||||
connect_nodes(nodes[a], b)
|
||||
|
@ -147,6 +147,7 @@ BASE_SCRIPTS= [
|
||||
'feature_logging.py',
|
||||
'node_network_limited.py',
|
||||
'conf_args.py',
|
||||
'feature_help.py',
|
||||
]
|
||||
|
||||
EXTENDED_SCRIPTS = [
|
||||
|
@ -379,9 +379,9 @@ class WalletTest(BitcoinTestFramework):
|
||||
self.start_node(0, [m, "-limitancestorcount="+str(chainlimit)])
|
||||
self.start_node(1, [m, "-limitancestorcount="+str(chainlimit)])
|
||||
self.start_node(2, [m, "-limitancestorcount="+str(chainlimit)])
|
||||
while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]:
|
||||
if m == '-reindex':
|
||||
# reindex will leave rpc warm up "early"; Wait for it to finish
|
||||
time.sleep(0.1)
|
||||
wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
|
||||
assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])
|
||||
|
||||
# Exercise listsinceblock with the last two blocks
|
||||
|
Loading…
Reference in New Issue
Block a user