aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLuke Dashjr <luke-jr+git@utopios.org>2012-05-13 04:43:24 +0000
committerWladimir J. van der Laan <laanwj@gmail.com>2014-07-11 14:48:02 +0200
commitff6a7af154f2151c93a06b7ee86c167603c5ac55 (patch)
tree5042feb9576de0a4857ac41e444774a87f79194f /src
parentf0fc81a0832a8fa93c5d7e619e25f739d72b49f3 (diff)
getblocktemplate: longpolling support
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp5
-rw-r--r--src/main.h2
-rw-r--r--src/rpcmining.cpp60
-rw-r--r--src/rpcserver.cpp11
-rw-r--r--src/rpcserver.h2
-rw-r--r--src/sync.h3
6 files changed, 82 insertions, 1 deletions
diff --git a/src/main.cpp b/src/main.cpp
index a9c080ffae..9e97a91682 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -41,6 +41,8 @@ CCriticalSection cs_main;
map<uint256, CBlockIndex*> mapBlockIndex;
CChain chainActive;
int64_t nTimeBestReceived = 0;
+CWaitableCriticalSection csBestBlock;
+CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
@@ -1944,11 +1946,14 @@ void static UpdateTip(CBlockIndex *pindexNew) {
// New best block
nTimeBestReceived = GetTime();
mempool.AddTransactionsUpdated(1);
+
LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
Checkpoints::GuessVerificationProgress(chainActive.Tip()));
+ cvBlockChange.notify_all();
+
// Check the version of the last 100 blocks to see if we need to upgrade:
if (!fIsInitialDownload)
{
diff --git a/src/main.h b/src/main.h
index f6bac889be..a683412571 100644
--- a/src/main.h
+++ b/src/main.h
@@ -87,6 +87,8 @@ extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockSize;
extern const std::string strMessageMagic;
extern int64_t nTimeBestReceived;
+extern CWaitableCriticalSection csBestBlock;
+extern CConditionVariable cvBlockChange;
extern bool fImporting;
extern bool fReindex;
extern bool fBenchmark;
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index c7621dc137..6f72ea7404 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -324,6 +324,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
);
std::string strMode = "template";
+ Value lpval = Value::null;
if (params.size() > 0)
{
const Object& oparam = params[0].get_obj();
@@ -336,6 +337,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
}
else
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
+ lpval = find_value(oparam, "longpollid");
}
if (strMode != "template")
@@ -347,8 +349,63 @@ Value getblocktemplate(const Array& params, bool fHelp)
if (IsInitialBlockDownload())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks...");
- // Update block
static unsigned int nTransactionsUpdatedLast;
+
+ if (lpval.type() != null_type)
+ {
+ // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
+ uint256 hashWatchedChain;
+ boost::system_time checktxtime;
+ unsigned int nTransactionsUpdatedLastLP;
+
+ if (lpval.type() == str_type)
+ {
+ // Format: <hashBestChain><nTransactionsUpdatedLast>
+ std::string lpstr = lpval.get_str();
+
+ hashWatchedChain.SetHex(lpstr.substr(0, 64));
+ nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
+ }
+ else
+ {
+ // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
+ hashWatchedChain = chainActive.Tip()->GetBlockHash();
+ nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
+ }
+
+ // Release the wallet and main lock while waiting
+#ifdef ENABLE_WALLET
+ if(pwalletMain)
+ LEAVE_CRITICAL_SECTION(pwalletMain->cs_wallet);
+#endif
+ LEAVE_CRITICAL_SECTION(cs_main);
+ {
+ checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
+
+ boost::unique_lock<boost::mutex> lock(csBestBlock);
+ while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
+ {
+ if (!cvBlockChange.timed_wait(lock, checktxtime))
+ {
+ // Timeout: Check transactions for update
+ if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
+ break;
+ checktxtime += boost::posix_time::seconds(10);
+ }
+ }
+ }
+ ENTER_CRITICAL_SECTION(cs_main);
+#ifdef ENABLE_WALLET
+ if(pwalletMain)
+ ENTER_CRITICAL_SECTION(pwalletMain->cs_wallet);
+#endif
+
+ if (!IsRPCRunning())
+ throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
+ // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
+ }
+
+ // Update block
static CBlockIndex* pindexPrev;
static int64_t nStart;
static CBlockTemplate* pblocktemplate;
@@ -436,6 +493,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
result.push_back(Pair("transactions", transactions));
result.push_back(Pair("coinbaseaux", aux));
result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
+ result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
result.push_back(Pair("target", hashTarget.GetHex()));
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
result.push_back(Pair("mutable", aMutable));
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index 18fa075101..bb8e56c785 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -32,6 +32,7 @@ using namespace std;
static std::string strRPCUserColonPass;
+static bool fRPCRunning = false;
// These are created by StartRPCThreads, destroyed in StopRPCThreads
static asio::io_service* rpc_io_service = NULL;
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
@@ -659,6 +660,7 @@ void StartRPCThreads()
rpc_worker_group = new boost::thread_group();
for (int i = 0; i < GetArg("-rpcthreads", 4); i++)
rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
+ fRPCRunning = true;
}
void StartDummyRPCThread()
@@ -671,12 +673,15 @@ void StartDummyRPCThread()
rpc_dummy_work = new asio::io_service::work(*rpc_io_service);
rpc_worker_group = new boost::thread_group();
rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
+ fRPCRunning = true;
}
}
void StopRPCThreads()
{
if (rpc_io_service == NULL) return;
+ // Set this to false first, so that longpolling loops will exit when woken up
+ fRPCRunning = false;
// First, cancel all timers and acceptors
// This is not done automatically by ->stop(), and in some cases the destructor of
@@ -698,6 +703,7 @@ void StopRPCThreads()
deadlineTimers.clear();
rpc_io_service->stop();
+ cvBlockChange.notify_all();
if (rpc_worker_group != NULL)
rpc_worker_group->join_all();
delete rpc_dummy_work; rpc_dummy_work = NULL;
@@ -706,6 +712,11 @@ void StopRPCThreads()
delete rpc_io_service; rpc_io_service = NULL;
}
+bool IsRPCRunning()
+{
+ return fRPCRunning;
+}
+
void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
{
if (!err)
diff --git a/src/rpcserver.h b/src/rpcserver.h
index e32eb975a1..31badadd6d 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -40,6 +40,8 @@ void StartRPCThreads();
void StartDummyRPCThread();
/* Stop RPC threads */
void StopRPCThreads();
+/* Query whether RPC is running */
+bool IsRPCRunning();
/*
Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
diff --git a/src/sync.h b/src/sync.h
index 077ed59b89..cd319e0171 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -84,6 +84,9 @@ typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection;
/** Wrapped boost mutex: supports waiting but not recursive locking */
typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection;
+/** Just a typedef for boost::condition_variable, can be wrapped later if desired */
+typedef boost::condition_variable CConditionVariable;
+
#ifdef DEBUG_LOCKORDER
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
void LeaveCritical();