aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/httpserver.cpp58
-rw-r--r--src/httpserver.h10
-rw-r--r--src/indirectmap.h4
-rw-r--r--src/init.cpp2
-rw-r--r--src/key.h31
-rw-r--r--src/main.cpp50
-rw-r--r--src/main.h6
-rw-r--r--src/miner.cpp2
-rw-r--r--src/policy/policy.cpp19
-rw-r--r--src/policy/policy.h12
-rw-r--r--src/primitives/transaction.h8
-rw-r--r--src/pubkey.h11
-rw-r--r--src/qt/optionsmodel.cpp21
-rw-r--r--src/qt/optionsmodel.h4
-rw-r--r--src/script/bitcoinconsensus.h1
-rw-r--r--src/tinyformat.h700
-rw-r--r--src/txmempool.cpp2
-rw-r--r--src/util.h28
-rw-r--r--src/wallet/rpcdump.cpp29
-rw-r--r--src/wallet/rpcwallet.cpp24
-rw-r--r--src/wallet/wallet.cpp52
-rw-r--r--src/wallet/wallet.h7
22 files changed, 601 insertions, 480 deletions
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 812940eaf9..be7a6a1dde 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
+#include <future>
#include <event2/event.h>
#include <event2/http.h>
@@ -34,9 +35,6 @@
#endif
#endif
-#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
-#include <boost/foreach.hpp>
-
/** Maximum size of http request (request line + headers) */
static const size_t MAX_HEADERS_SIZE = 8192;
@@ -68,8 +66,8 @@ class WorkQueue
{
private:
/** Mutex protects entire object */
- CWaitableCriticalSection cs;
- CConditionVariable cond;
+ std::mutex cs;
+ std::condition_variable cond;
std::deque<std::unique_ptr<WorkItem>> queue;
bool running;
size_t maxDepth;
@@ -82,12 +80,12 @@ private:
WorkQueue &wq;
ThreadCounter(WorkQueue &w): wq(w)
{
- boost::lock_guard<boost::mutex> lock(wq.cs);
+ std::lock_guard<std::mutex> lock(wq.cs);
wq.numThreads += 1;
}
~ThreadCounter()
{
- boost::lock_guard<boost::mutex> lock(wq.cs);
+ std::lock_guard<std::mutex> lock(wq.cs);
wq.numThreads -= 1;
wq.cond.notify_all();
}
@@ -108,7 +106,7 @@ public:
/** Enqueue a work item */
bool Enqueue(WorkItem* item)
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
if (queue.size() >= maxDepth) {
return false;
}
@@ -123,7 +121,7 @@ public:
while (running) {
std::unique_ptr<WorkItem> i;
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
while (running && queue.empty())
cond.wait(lock);
if (!running)
@@ -137,14 +135,14 @@ public:
/** Interrupt and exit loops */
void Interrupt()
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
running = false;
cond.notify_all();
}
/** Wait for worker threads to exit */
void WaitExit()
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
while (numThreads > 0)
cond.wait(lock);
}
@@ -152,7 +150,7 @@ public:
/** Return current depth of queue */
size_t Depth()
{
- boost::unique_lock<boost::mutex> lock(cs);
+ std::unique_lock<std::mutex> lock(cs);
return queue.size();
}
};
@@ -189,7 +187,7 @@ static bool ClientAllowed(const CNetAddr& netaddr)
{
if (!netaddr.IsValid())
return false;
- BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets)
+ for(const CSubNet& subnet : rpc_allow_subnets)
if (subnet.Match(netaddr))
return true;
return false;
@@ -203,7 +201,7 @@ static bool InitHTTPAllowList()
rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost
if (mapMultiArgs.count("-rpcallowip")) {
const std::vector<std::string>& vAllow = mapMultiArgs["-rpcallowip"];
- BOOST_FOREACH (std::string strAllow, vAllow) {
+ for (std::string strAllow : vAllow) {
CSubNet subnet(strAllow);
if (!subnet.IsValid()) {
uiInterface.ThreadSafeMessageBox(
@@ -215,7 +213,7 @@ static bool InitHTTPAllowList()
}
}
std::string strAllowed;
- BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets)
+ for (const CSubNet& subnet : rpc_allow_subnets)
strAllowed += subnet.ToString() + " ";
LogPrint("http", "Allowing HTTP connections from: %s\n", strAllowed);
return true;
@@ -302,13 +300,14 @@ static void http_reject_request_cb(struct evhttp_request* req, void*)
}
/** Event dispatcher thread */
-static void ThreadHTTP(struct event_base* base, struct evhttp* http)
+static bool ThreadHTTP(struct event_base* base, struct evhttp* http)
{
RenameThread("bitcoin-http");
LogPrint("http", "Entering http event loop\n");
event_base_dispatch(base);
// Event loop will be interrupted by InterruptHTTPServer()
LogPrint("http", "Exited http event loop\n");
+ return event_base_got_break(base) == 0;
}
/** Bind HTTP server to specified addresses */
@@ -437,17 +436,22 @@ bool InitHTTPServer()
return true;
}
-boost::thread threadHTTP;
+std::thread threadHTTP;
+std::future<bool> threadResult;
bool StartHTTPServer()
{
LogPrint("http", "Starting HTTP server\n");
int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
- threadHTTP = boost::thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP));
+ std::packaged_task<bool(event_base*, evhttp*)> task(ThreadHTTP);
+ threadResult = task.get_future();
+ threadHTTP = std::thread(std::move(task), eventBase, eventHTTP);
- for (int i = 0; i < rpcThreads; i++)
- boost::thread(boost::bind(&HTTPWorkQueueRun, workQueue));
+ for (int i = 0; i < rpcThreads; i++) {
+ std::thread rpc_worker(HTTPWorkQueueRun, workQueue);
+ rpc_worker.detach();
+ }
return true;
}
@@ -456,7 +460,7 @@ void InterruptHTTPServer()
LogPrint("http", "Interrupting HTTP server\n");
if (eventHTTP) {
// Unlisten sockets
- BOOST_FOREACH (evhttp_bound_socket *socket, boundSockets) {
+ for (evhttp_bound_socket *socket : boundSockets) {
evhttp_del_accept_socket(eventHTTP, socket);
}
// Reject requests on current connections
@@ -482,15 +486,11 @@ void StopHTTPServer()
// master that appears to be solved, so in the future that solution
// could be used again (if desirable).
// (see discussion in https://github.com/bitcoin/bitcoin/pull/6990)
-#if BOOST_VERSION >= 105000
- if (!threadHTTP.try_join_for(boost::chrono::milliseconds(2000))) {
-#else
- if (!threadHTTP.timed_join(boost::posix_time::milliseconds(2000))) {
-#endif
+ if (threadResult.valid() && threadResult.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) {
LogPrintf("HTTP event loop did not exit within allotted time, sending loopbreak\n");
event_base_loopbreak(eventBase);
- threadHTTP.join();
}
+ threadHTTP.join();
}
if (eventHTTP) {
evhttp_free(eventHTTP);
@@ -517,7 +517,7 @@ static void httpevent_callback_fn(evutil_socket_t, short, void* data)
delete self;
}
-HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function<void(void)>& handler):
+HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void(void)>& handler):
deleteWhenTriggered(deleteWhenTriggered), handler(handler)
{
ev = event_new(base, -1, 0, httpevent_callback_fn, this);
@@ -599,7 +599,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size());
HTTPEvent* ev = new HTTPEvent(eventBase, true,
- boost::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL));
+ std::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL));
ev->trigger(0);
replySent = true;
req = 0; // transferred back to main thread
diff --git a/src/httpserver.h b/src/httpserver.h
index 20a119cc5c..0e30e666a6 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -7,9 +7,7 @@
#include <string>
#include <stdint.h>
-#include <boost/thread.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/function.hpp>
+#include <functional>
static const int DEFAULT_HTTP_THREADS=4;
static const int DEFAULT_HTTP_WORKQUEUE=16;
@@ -35,7 +33,7 @@ void InterruptHTTPServer();
void StopHTTPServer();
/** Handler for requests to a certain HTTP path */
-typedef boost::function<void(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
+typedef std::function<void(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
/** Register handler for prefix.
* If multiple handlers match a prefix, the first-registered one will
* be invoked.
@@ -132,7 +130,7 @@ public:
* deleteWhenTriggered deletes this event object after the event is triggered (and the handler called)
* handler is the handler to call when the event is triggered.
*/
- HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function<void(void)>& handler);
+ HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void(void)>& handler);
~HTTPEvent();
/** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after
@@ -141,7 +139,7 @@ public:
void trigger(struct timeval* tv);
bool deleteWhenTriggered;
- boost::function<void(void)> handler;
+ std::function<void(void)> handler;
private:
struct event* ev;
};
diff --git a/src/indirectmap.h b/src/indirectmap.h
index 28e1e8dedd..76da4a6bd5 100644
--- a/src/indirectmap.h
+++ b/src/indirectmap.h
@@ -1,3 +1,7 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
#ifndef BITCOIN_INDIRECTMAP_H
#define BITCOIN_INDIRECTMAP_H
diff --git a/src/init.cpp b/src/init.cpp
index 312dfe1699..8d4a2cafbf 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -446,7 +446,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("Node relay options:"));
if (showDebug)
strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !Params(CBaseChainParams::TESTNET).RequireStandard()));
- strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Minimum bytes per sigop in transactions we relay and mine (default: %u)"), DEFAULT_BYTES_PER_SIGOP));
+ strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP));
strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER));
strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY));
strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT));
diff --git a/src/key.h b/src/key.h
index b4f48d59f5..b589710bad 100644
--- a/src/key.h
+++ b/src/key.h
@@ -15,7 +15,7 @@
#include <vector>
-/**
+/**
* secp256k1:
* const unsigned int PRIVATE_KEY_SIZE = 279;
* const unsigned int PUBLIC_KEY_SIZE = 65;
@@ -45,6 +45,8 @@ private:
//! The actual byte data
unsigned char vch[32];
+ static_assert(sizeof(vch) == 32, "vch must be 32 bytes in length to not break serialization");
+
//! Check whether the 32-byte array pointed to be vch is valid keydata.
bool static Check(const unsigned char* vch);
@@ -70,20 +72,19 @@ public:
friend bool operator==(const CKey& a, const CKey& b)
{
- return a.fCompressed == b.fCompressed && a.size() == b.size() &&
- memcmp(&a.vch[0], &b.vch[0], a.size()) == 0;
+ return a.fCompressed == b.fCompressed &&
+ a.size() == b.size() &&
+ memcmp(&a.vch[0], &b.vch[0], a.size()) == 0;
}
//! Initialize using begin and end iterators to byte data.
template <typename T>
void Set(const T pbegin, const T pend, bool fCompressedIn)
{
- if (pend - pbegin != 32) {
+ if (pend - pbegin != sizeof(vch)) {
fValid = false;
- return;
- }
- if (Check(&pbegin[0])) {
- memcpy(vch, (unsigned char*)&pbegin[0], 32);
+ } else if (Check(&pbegin[0])) {
+ memcpy(vch, (unsigned char*)&pbegin[0], sizeof(vch));
fValid = true;
fCompressed = fCompressedIn;
} else {
@@ -92,7 +93,7 @@ public:
}
//! Simple read-only vector-like interface.
- unsigned int size() const { return (fValid ? 32 : 0); }
+ unsigned int size() const { return (fValid ? sizeof(vch) : 0); }
const unsigned char* begin() const { return vch; }
const unsigned char* end() const { return vch + size(); }
@@ -110,7 +111,7 @@ public:
/**
* Convert the private key to a CPrivKey (serialized OpenSSL private key data).
- * This is expensive.
+ * This is expensive.
*/
CPrivKey GetPrivKey() const;
@@ -146,9 +147,6 @@ public:
//! Load private key and check that public key matches.
bool Load(CPrivKey& privkey, CPubKey& vchPubKey, bool fSkipCheck);
-
- //! Check whether an element of a signature (r or s) is valid.
- static bool CheckSignatureElement(const unsigned char* vch, int len, bool half);
};
struct CExtKey {
@@ -160,8 +158,11 @@ struct CExtKey {
friend bool operator==(const CExtKey& a, const CExtKey& b)
{
- return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild &&
- a.chaincode == b.chaincode && a.key == b.key;
+ return a.nDepth == b.nDepth &&
+ memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 &&
+ a.nChild == b.nChild &&
+ a.chaincode == b.chaincode &&
+ a.key == b.key;
}
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
diff --git a/src/main.cpp b/src/main.cpp
index fe19895f5f..81f09515f3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -74,7 +74,6 @@ bool fHavePruned = false;
bool fPruneMode = false;
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
bool fRequireStandard = true;
-unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
size_t nCoinCacheUsage = 5000 * 300;
@@ -788,7 +787,7 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
return true;
if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime))
return true;
- BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ for (const auto& txin : tx.vin) {
if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL))
return false;
}
@@ -1002,11 +1001,11 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
unsigned int GetLegacySigOpCount(const CTransaction& tx)
{
unsigned int nSigOps = 0;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ for (const auto& txin : tx.vin)
{
nSigOps += txin.scriptSig.GetSigOpCount(false);
}
- BOOST_FOREACH(const CTxOut& txout, tx.vout)
+ for (const auto& txout : tx.vout)
{
nSigOps += txout.scriptPubKey.GetSigOpCount(false);
}
@@ -1064,7 +1063,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
// Check for negative or overflow output values
CAmount nValueOut = 0;
- BOOST_FOREACH(const CTxOut& txout, tx.vout)
+ for (const auto& txout : tx.vout)
{
if (txout.nValue < 0)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
@@ -1077,7 +1076,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
// Check for duplicate inputs
set<COutPoint> vInOutPoints;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ for (const auto& txin : tx.vin)
{
if (vInOutPoints.count(txin.prevout))
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
@@ -1091,7 +1090,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
}
else
{
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ for (const auto& txin : tx.vin)
if (txin.prevout.IsNull())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
}
@@ -1144,13 +1143,14 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
// Reject transactions with witness before segregated witness activates (override with -prematurewitness)
- if (!GetBoolArg("-prematurewitness",false) && !tx.wit.IsNull() && !IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus())) {
+ bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus());
+ if (!GetBoolArg("-prematurewitness",false) && !tx.wit.IsNull() && !witnessEnabled) {
return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true);
}
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason;
- if (fRequireStandard && !IsStandardTx(tx, reason))
+ if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled))
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
// Only accept nLockTime-using transactions that can be mined in the next
@@ -1296,7 +1296,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than
// MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
// merely non-standard transaction.
- if ((nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) || (nBytesPerSigOp && nSigOpsCost > nSize * WITNESS_SCALE_FACTOR / nBytesPerSigOp))
+ if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
strprintf("%d", nSigOpsCost));
@@ -3404,13 +3404,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
// Check transactions
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ for (const auto& tx : block.vtx)
if (!CheckTransaction(tx, state))
return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(),
strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage()));
unsigned int nSigOps = 0;
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ for (const auto& tx : block.vtx)
{
nSigOps += GetLegacySigOpCount(tx);
}
@@ -3502,7 +3502,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}
-bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev, int64_t nAdjustedTime)
+bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
{
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
@@ -3525,7 +3525,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
return true;
}
-bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev)
+bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindexPrev)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
const Consensus::Params& consensusParams = Params().GetConsensus();
@@ -3541,7 +3541,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
: block.GetBlockTime();
// Check that all transactions are finalized
- BOOST_FOREACH(const CTransaction& tx, block.vtx) {
+ for (const auto& tx : block.vtx) {
if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) {
return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction");
}
@@ -3567,7 +3567,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
// {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are
// multiple, the last one is used.
bool fHaveWitness = false;
- if (IsWitnessEnabled(pindexPrev, consensusParams)) {
+ if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == THRESHOLD_ACTIVE) {
int commitpos = GetWitnessCommitmentIndex(block);
if (commitpos != -1) {
bool malleated = false;
@@ -3576,11 +3576,11 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
// already does not permit it, it is impossible to trigger in the
// witness tree.
if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) {
- return state.DoS(100, error("%s : invalid witness nonce size", __func__), REJECT_INVALID, "bad-witness-nonce-size", true);
+ return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness nonce size", __func__));
}
CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) {
- return state.DoS(100, error("%s : witness merkle commitment mismatch", __func__), REJECT_INVALID, "bad-witness-merkle-match", true);
+ return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__));
}
fHaveWitness = true;
}
@@ -3590,7 +3590,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
if (!fHaveWitness) {
for (size_t i = 0; i < block.vtx.size(); i++) {
if (!block.vtx[i].wit.IsNull()) {
- return state.DoS(100, error("%s : unexpected witness data found", __func__), REJECT_INVALID, "unexpected-witness", true);
+ return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__));
}
}
}
@@ -3602,7 +3602,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
// the block hash, so we couldn't mark the block as permanently
// failed).
if (GetBlockWeight(block) > MAX_BLOCK_WEIGHT) {
- return state.DoS(100, error("ContextualCheckBlock(): weight limit failed"), REJECT_INVALID, "bad-blk-weight");
+ return state.DoS(100, false, REJECT_INVALID, "bad-blk-weight", false, strprintf("%s : weight limit failed", __func__));
}
return true;
@@ -4100,7 +4100,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
int nGoodTransactions = 0;
CValidationState state;
int reportDone = 0;
- LogPrintf("[0%]...");
+ LogPrintf("[0%%]...");
for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev)
{
boost::this_thread::interruption_point();
@@ -5343,7 +5343,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
BlockMap::iterator it = mapBlockIndex.find(req.blockhash);
if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) {
- Misbehaving(pfrom->GetId(), 100);
LogPrintf("Peer %d sent us a getblocktxn for a block we don't have", pfrom->id);
return true;
}
@@ -5627,8 +5626,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
std::vector<CInv> vInv(1);
vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash());
pfrom->PushMessage(NetMsgType::GETDATA, vInv);
- return true;
}
+ return true;
}
// If we're not close to tip yet, give up and let parallel block fetch work its magic
@@ -6146,6 +6145,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
}
+ else if (strCommand == NetMsgType::NOTFOUND) {
+ // We do not care about the NOTFOUND message, but logging an Unknown Command
+ // message would be undesirable as we transmit it ourselves.
+ }
+
else {
// Ignore unknown commands for extensibility
LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
diff --git a/src/main.h b/src/main.h
index 27121890f6..26ea6adc6b 100644
--- a/src/main.h
+++ b/src/main.h
@@ -124,7 +124,6 @@ static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
/** Default for -permitbaremultisig */
static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
-static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
@@ -165,7 +164,6 @@ extern int nScriptCheckThreads;
extern bool fTxIndex;
extern bool fIsBareMultisigStd;
extern bool fRequireStandard;
-extern unsigned int nBytesPerSigOp;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
extern size_t nCoinCacheUsage;
@@ -447,8 +445,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
/** Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock(). */
-bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex* pindexPrev, int64_t nAdjustedTime);
-bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev);
+bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime);
+bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindexPrev);
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
diff --git a/src/miner.cpp b/src/miner.cpp
index 25a5becf93..9575858840 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -187,7 +187,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nNonce = 0;
- pblocktemplate->vTxSigOpsCost[0] = GetLegacySigOpCount(pblock->vtx[0]);
+ pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(pblock->vtx[0]);
CValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 8617db00c8..48080abc77 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -31,7 +31,7 @@
* DUP CHECKSIG DROP ... repeated 100 times... OP_1
*/
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
+bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled)
{
std::vector<std::vector<unsigned char> > vSolutions;
if (!Solver(scriptPubKey, whichType, vSolutions))
@@ -50,10 +50,13 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
(!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes))
return false;
+ else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH))
+ return false;
+
return whichType != TX_NONSTANDARD;
}
-bool IsStandardTx(const CTransaction& tx, std::string& reason)
+bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled)
{
if (tx.nVersion > CTransaction::MAX_STANDARD_VERSION || tx.nVersion < 1) {
reason = "version";
@@ -92,7 +95,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
unsigned int nDataOut = 0;
txnouttype whichType;
BOOST_FOREACH(const CTxOut& txout, tx.vout) {
- if (!::IsStandard(txout.scriptPubKey, whichType)) {
+ if (!::IsStandard(txout.scriptPubKey, whichType, witnessEnabled)) {
reason = "scriptpubkey";
return false;
}
@@ -151,12 +154,14 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
return true;
}
-int64_t GetVirtualTransactionSize(int64_t nWeight)
+unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
+
+int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
{
- return (nWeight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
+ return (std::max(nWeight, nSigOpCost * nBytesPerSigOp) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
}
-int64_t GetVirtualTransactionSize(const CTransaction& tx)
+int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost)
{
- return GetVirtualTransactionSize(GetTransactionWeight(tx));
+ return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost);
}
diff --git a/src/policy/policy.h b/src/policy/policy.h
index f5f8652fb5..6bf5ca0ee5 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -28,6 +28,8 @@ static const unsigned int MAX_P2SH_SIGOPS = 15;
static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5;
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
+/** Default for -bytespersigop */
+static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
@@ -53,12 +55,12 @@ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
LOCKTIME_MEDIAN_TIME_PAST;
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
+bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled = false);
/**
* Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
-bool IsStandardTx(const CTransaction& tx, std::string& reason);
+bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled = false);
/**
* Check for standard transaction types
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
@@ -66,8 +68,10 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason);
*/
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
+extern unsigned int nBytesPerSigOp;
+
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
-int64_t GetVirtualTransactionSize(int64_t nWeight);
-int64_t GetVirtualTransactionSize(const CTransaction& tx);
+int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
+int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0);
#endif // BITCOIN_POLICY_POLICY_H
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index d37890667f..5689d15bf7 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -290,6 +290,8 @@ struct CMutableTransaction;
*/
template<typename Stream, typename Operation, typename TxType>
inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, int nType, int nVersion) {
+ const bool fAllowWitness = !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS);
+
READWRITE(*const_cast<int32_t*>(&tx.nVersion));
unsigned char flags = 0;
if (ser_action.ForRead()) {
@@ -298,7 +300,7 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in
const_cast<CTxWitness*>(&tx.wit)->SetNull();
/* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
READWRITE(*const_cast<std::vector<CTxIn>*>(&tx.vin));
- if (tx.vin.size() == 0 && !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) {
+ if (tx.vin.size() == 0 && fAllowWitness) {
/* We read a dummy or an empty vin. */
READWRITE(flags);
if (flags != 0) {
@@ -309,7 +311,7 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in
/* We read a non-empty vin. Assume a normal vout follows. */
READWRITE(*const_cast<std::vector<CTxOut>*>(&tx.vout));
}
- if ((flags & 1) && !(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) {
+ if ((flags & 1) && fAllowWitness) {
/* The witness flag is present, and we support witnesses. */
flags ^= 1;
const_cast<CTxWitness*>(&tx.wit)->vtxinwit.resize(tx.vin.size());
@@ -322,7 +324,7 @@ inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, in
} else {
// Consistency check
assert(tx.wit.vtxinwit.size() <= tx.vin.size());
- if (!(nVersion & SERIALIZE_TRANSACTION_NO_WITNESS)) {
+ if (fAllowWitness) {
/* Check whether witnesses need to be serialized. */
if (!tx.wit.IsNull()) {
flags |= 1;
diff --git a/src/pubkey.h b/src/pubkey.h
index db5444ea9d..aebfdbc826 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -13,7 +13,7 @@
#include <stdexcept>
#include <vector>
-/**
+/**
* secp256k1:
* const unsigned int PRIVATE_KEY_SIZE = 279;
* const unsigned int PUBLIC_KEY_SIZE = 65;
@@ -156,7 +156,7 @@ public:
/*
* Check syntactic correctness.
- *
+ *
* Note that this is consensus critical as CheckSig() calls it!
*/
bool IsValid() const
@@ -203,8 +203,11 @@ struct CExtPubKey {
friend bool operator==(const CExtPubKey &a, const CExtPubKey &b)
{
- return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild &&
- a.chaincode == b.chaincode && a.pubkey == b.pubkey;
+ return a.nDepth == b.nDepth &&
+ memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 &&
+ a.nChild == b.nChild &&
+ a.chaincode == b.chaincode &&
+ a.pubkey == b.pubkey;
}
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index cc2cbc0e66..684db71a8c 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -43,6 +43,8 @@ void OptionsModel::Init(bool resetSettings)
if (resetSettings)
Reset();
+ checkAndMigrate();
+
QSettings settings;
// Ensure restart flag is unset on client startup
@@ -429,3 +431,22 @@ bool OptionsModel::isRestartRequired()
QSettings settings;
return settings.value("fRestartRequired", false).toBool();
}
+
+void OptionsModel::checkAndMigrate()
+{
+ // Migration of default values
+ // Check if the QSettings container was already loaded with this client version
+ QSettings settings;
+ static const char strSettingsVersionKey[] = "nSettingsVersion";
+ int settingsVersion = settings.contains(strSettingsVersionKey) ? settings.value(strSettingsVersionKey).toInt() : 0;
+ if (settingsVersion < CLIENT_VERSION)
+ {
+ // -dbcache was bumped from 100 to 300 in 0.13
+ // see https://github.com/bitcoin/bitcoin/pull/8273
+ // force people to upgrade to the new value if they are using 100MB
+ if (settingsVersion < 130000 && settings.contains("nDatabaseCache") && settings.value("nDatabaseCache").toLongLong() == 100)
+ settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
+
+ settings.setValue(strSettingsVersionKey, CLIENT_VERSION);
+ }
+} \ No newline at end of file
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 3b491ceac2..b23b5f2607 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -84,9 +84,11 @@ private:
/* settings that were overriden by command-line */
QString strOverriddenByCommandLine;
- /// Add option to list of GUI options overridden through command line/config file
+ // Add option to list of GUI options overridden through command line/config file
void addOverriddenOption(const std::string &option);
+ // Check settings version and upgrade default values if required
+ void checkAndMigrate();
Q_SIGNALS:
void displayUnitChanged(int unit);
void coinControlFeaturesChanged(bool);
diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h
index 6f868d0d6d..f73a8e30bc 100644
--- a/src/script/bitcoinconsensus.h
+++ b/src/script/bitcoinconsensus.h
@@ -51,6 +51,7 @@ enum
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65)
+ bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112)
bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141)
};
diff --git a/src/tinyformat.h b/src/tinyformat.h
index c6ec0419b3..17f0360c42 100644
--- a/src/tinyformat.h
+++ b/src/tinyformat.h
@@ -67,7 +67,9 @@
// weekday, month, day, hour, min);
// std::cout << date;
//
-// These are the three primary interface functions.
+// These are the three primary interface functions. There is also a
+// convenience function printfln() which appends a newline to the usual result
+// of printf() for super simple logging.
//
//
// User defined format functions
@@ -86,6 +88,18 @@
// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an
// example, see the implementation of printf() at the end of the source file.
//
+// Sometimes it's useful to be able to pass a list of format arguments through
+// to a non-template function. The FormatList class is provided as a way to do
+// this by storing the argument list in a type-opaque way. Continuing the
+// example from above, we construct a FormatList using makeFormatList():
+//
+// FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min);
+//
+// The format list can now be passed into any non-template function and used
+// via a call to the vformat() function:
+//
+// tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList);
+//
//
// Additional API information
// --------------------------
@@ -118,6 +132,7 @@ namespace tfm = tinyformat;
//------------------------------------------------------------------------------
// Implementation details.
+#include <algorithm>
#include <cassert>
#include <iostream>
#include <sstream>
@@ -133,20 +148,20 @@ namespace tfm = tinyformat;
# endif
#endif
-#ifdef __GNUC__
-# define TINYFORMAT_NOINLINE __attribute__((noinline))
-#elif defined(_MSC_VER)
-# define TINYFORMAT_NOINLINE __declspec(noinline)
-#else
-# define TINYFORMAT_NOINLINE
-#endif
-
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201
// std::showpos is broken on old libstdc++ as provided with OSX. See
// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html
# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
#endif
+#ifdef __APPLE__
+// Workaround OSX linker warning: xcode uses different default symbol
+// visibilities for static libs vs executables (see issue #25)
+# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
+#else
+# define TINYFORMAT_HIDDEN
+#endif
+
namespace tinyformat {
//------------------------------------------------------------------------------
@@ -247,6 +262,29 @@ struct convertToInt<T,true>
static int invoke(const T& value) { return static_cast<int>(value); }
};
+// Format at most ntrunc characters to the given stream.
+template<typename T>
+inline void formatTruncated(std::ostream& out, const T& value, int ntrunc)
+{
+ std::ostringstream tmp;
+ tmp << value;
+ std::string result = tmp.str();
+ out.write(result.c_str(), (std::min)(ntrunc, static_cast<int>(result.size())));
+}
+#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \
+inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \
+{ \
+ std::streamsize len = 0; \
+ while(len < ntrunc && value[len] != 0) \
+ ++len; \
+ out.write(value, len); \
+}
+// Overload for const char* and char*. Could overload for signed & unsigned
+// char too, but these are technically unneeded for printf compatibility.
+TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char)
+TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char)
+#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR
+
} // namespace detail
@@ -255,18 +293,20 @@ struct convertToInt<T,true>
// desired.
-// Format a value into a stream. Called from format() for all types by default.
-//
-// Users may override this for their own types. When this function is called,
-// the stream flags will have been modified according to the format string.
-// The format specification is provided in the range [fmtBegin, fmtEnd).
-//
-// By default, formatValue() uses the usual stream insertion operator
-// operator<< to format the type T, with special cases for the %c and %p
-// conversions.
+/// Format a value into a stream, delegating to operator<< by default.
+///
+/// Users may override this for their own types. When this function is called,
+/// the stream flags will have been modified according to the format string.
+/// The format specification is provided in the range [fmtBegin, fmtEnd). For
+/// truncating conversions, ntrunc is set to the desired maximum number of
+/// characters, for example "%.7s" calls formatValue with ntrunc = 7.
+///
+/// By default, formatValue() uses the usual stream insertion operator
+/// operator<< to format the type T, with special cases for the %c and %p
+/// conversions.
template<typename T>
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
- const char* fmtEnd, const T& value)
+ const char* fmtEnd, int ntrunc, const T& value)
{
#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS
// Since we don't support printing of wchar_t using "%ls", make it fail at
@@ -288,6 +328,12 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
else if(detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/;
#endif
+ else if(ntrunc >= 0)
+ {
+ // Take care not to overread C strings in truncating conversions like
+ // "%.4s" where at most 4 characters may be read.
+ detail::formatTruncated(out, value, ntrunc);
+ }
else
out << value;
}
@@ -296,7 +342,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
// Overloaded version for char types to support printing as an integer
#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \
- const char* fmtEnd, charType value) \
+ const char* fmtEnd, int /**/, charType value) \
{ \
switch(*(fmtEnd-1)) \
{ \
@@ -435,225 +481,91 @@ cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' +
namespace detail {
-// Class holding current position in format string and an output stream into
-// which arguments are formatted.
-class FormatIterator
+// Type-opaque holder for an argument to format(), with associated actions on
+// the type held as explicit function pointers. This allows FormatArg's for
+// each argument to be allocated as a homogenous array inside FormatList
+// whereas a naive implementation based on inheritance does not.
+class FormatArg
{
public:
- // Flags for features not representable with standard stream state
- enum ExtraFormatFlags
- {
- Flag_None = 0,
- Flag_TruncateToPrecision = 1<<0, // truncate length to stream precision()
- Flag_SpacePadPositive = 1<<1, // pad positive values with spaces
- Flag_VariableWidth = 1<<2, // variable field width in arg list
- Flag_VariablePrecision = 1<<3 // variable field precision in arg list
- };
-
- // out is the output stream, fmt is the full format string
- FormatIterator(std::ostream& out, const char* fmt)
- : m_out(out),
- m_fmt(fmt),
- m_extraFlags(Flag_None),
- m_wantWidth(false),
- m_wantPrecision(false),
- m_variableWidth(0),
- m_variablePrecision(0),
- m_origWidth(out.width()),
- m_origPrecision(out.precision()),
- m_origFlags(out.flags()),
- m_origFill(out.fill())
+ FormatArg() {}
+
+ template<typename T>
+ FormatArg(const T& value)
+ : m_value(static_cast<const void*>(&value)),
+ m_formatImpl(&formatImpl<T>),
+ m_toIntImpl(&toIntImpl<T>)
{ }
- // Print remaining part of format string.
- void finish()
+ void format(std::ostream& out, const char* fmtBegin,
+ const char* fmtEnd, int ntrunc) const
{
- // It would be nice if we could do this from the destructor, but we
- // can't if TINFORMAT_ERROR is used to throw an exception!
- m_fmt = printFormatStringLiteral(m_out, m_fmt);
- if(*m_fmt != '\0')
- TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
+ m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
}
- ~FormatIterator()
+ int toInt() const
{
- // Restore stream state
- m_out.width(m_origWidth);
- m_out.precision(m_origPrecision);
- m_out.flags(m_origFlags);
- m_out.fill(m_origFill);
+ return m_toIntImpl(m_value);
}
- template<typename T>
- void accept(const T& value);
-
private:
- // Parse and return an integer from the string c, as atoi()
- // On return, c is set to one past the end of the integer.
- static int parseIntAndAdvance(const char*& c)
+ template<typename T>
+ TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin,
+ const char* fmtEnd, int ntrunc, const void* value)
{
- int i = 0;
- for(;*c >= '0' && *c <= '9'; ++c)
- i = 10*i + (*c - '0');
- return i;
+ formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast<const T*>(value));
}
- // Format at most truncLen characters of a C string to the given
- // stream. Return true if formatting proceeded (generic version always
- // returns false)
template<typename T>
- static bool formatCStringTruncate(std::ostream& /*out*/, const T& /*value*/,
- std::streamsize /*truncLen*/)
+ TINYFORMAT_HIDDEN static int toIntImpl(const void* value)
{
- return false;
- }
-# define TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(type) \
- static bool formatCStringTruncate(std::ostream& out, type* value, \
- std::streamsize truncLen) \
- { \
- std::streamsize len = 0; \
- while(len < truncLen && value[len] != 0) \
- ++len; \
- out.write(value, len); \
- return true; \
- }
- // Overload for const char* and char*. Could overload for signed &
- // unsigned char too, but these are technically unneeded for printf
- // compatibility.
- TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(const char)
- TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(char)
-# undef TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE
-
- // Print literal part of format string and return next format spec
- // position.
- //
- // Skips over any occurrences of '%%', printing a literal '%' to the
- // output. The position of the first % character of the next
- // nontrivial format spec is returned, or the end of string.
- static const char* printFormatStringLiteral(std::ostream& out,
- const char* fmt)
- {
- const char* c = fmt;
- for(; true; ++c)
- {
- switch(*c)
- {
- case '\0':
- out.write(fmt, static_cast<std::streamsize>(c - fmt));
- return c;
- case '%':
- out.write(fmt, static_cast<std::streamsize>(c - fmt));
- if(*(c+1) != '%')
- return c;
- // for "%%", tack trailing % onto next literal section.
- fmt = ++c;
- break;
- }
- }
+ return convertToInt<T>::invoke(*static_cast<const T*>(value));
}
- static const char* streamStateFromFormat(std::ostream& out,
- unsigned int& extraFlags,
- const char* fmtStart,
- int variableWidth,
- int variablePrecision);
-
- // Private copy & assign: Kill gcc warnings with -Weffc++
- FormatIterator(const FormatIterator&);
- FormatIterator& operator=(const FormatIterator&);
-
- // Stream, current format string & state
- std::ostream& m_out;
- const char* m_fmt;
- unsigned int m_extraFlags;
- // State machine info for handling of variable width & precision
- bool m_wantWidth;
- bool m_wantPrecision;
- int m_variableWidth;
- int m_variablePrecision;
- // Saved stream state
- std::streamsize m_origWidth;
- std::streamsize m_origPrecision;
- std::ios::fmtflags m_origFlags;
- char m_origFill;
+ const void* m_value;
+ void (*m_formatImpl)(std::ostream& out, const char* fmtBegin,
+ const char* fmtEnd, int ntrunc, const void* value);
+ int (*m_toIntImpl)(const void* value);
};
-// Accept a value for formatting into the internal stream.
-template<typename T>
-TINYFORMAT_NOINLINE // < greatly reduces bloat in optimized builds
-void FormatIterator::accept(const T& value)
+// Parse and return an integer from the string c, as atoi()
+// On return, c is set to one past the end of the integer.
+inline int parseIntAndAdvance(const char*& c)
{
- // Parse the format string
- const char* fmtEnd = 0;
- if(m_extraFlags == Flag_None && !m_wantWidth && !m_wantPrecision)
- {
- m_fmt = printFormatStringLiteral(m_out, m_fmt);
- fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt, 0, 0);
- m_wantWidth = (m_extraFlags & Flag_VariableWidth) != 0;
- m_wantPrecision = (m_extraFlags & Flag_VariablePrecision) != 0;
- }
- // Consume value as variable width and precision specifier if necessary
- if(m_extraFlags & (Flag_VariableWidth | Flag_VariablePrecision))
- {
- if(m_wantWidth || m_wantPrecision)
- {
- int v = convertToInt<T>::invoke(value);
- if(m_wantWidth)
- {
- m_variableWidth = v;
- m_wantWidth = false;
- }
- else if(m_wantPrecision)
- {
- m_variablePrecision = v;
- m_wantPrecision = false;
- }
- return;
- }
- // If we get here, we've set both the variable precision and width as
- // required and we need to rerun the stream state setup to insert these.
- fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt,
- m_variableWidth, m_variablePrecision);
- }
+ int i = 0;
+ for(;*c >= '0' && *c <= '9'; ++c)
+ i = 10*i + (*c - '0');
+ return i;
+}
- // Format the value into the stream.
- if(!(m_extraFlags & (Flag_SpacePadPositive | Flag_TruncateToPrecision)))
- formatValue(m_out, m_fmt, fmtEnd, value);
- else
+// Print literal part of format string and return next format spec
+// position.
+//
+// Skips over any occurrences of '%%', printing a literal '%' to the
+// output. The position of the first % character of the next
+// nontrivial format spec is returned, or the end of string.
+inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
+{
+ const char* c = fmt;
+ for(;; ++c)
{
- // The following are special cases where there's no direct
- // correspondence between stream formatting and the printf() behaviour.
- // Instead, we simulate the behaviour crudely by formatting into a
- // temporary string stream and munging the resulting string.
- std::ostringstream tmpStream;
- tmpStream.copyfmt(m_out);
- if(m_extraFlags & Flag_SpacePadPositive)
- tmpStream.setf(std::ios::showpos);
- // formatCStringTruncate is required for truncating conversions like
- // "%.4s" where at most 4 characters of the c-string should be read.
- // If we didn't include this special case, we might read off the end.
- if(!( (m_extraFlags & Flag_TruncateToPrecision) &&
- formatCStringTruncate(tmpStream, value, m_out.precision()) ))
- {
- // Not a truncated c-string; just format normally.
- formatValue(tmpStream, m_fmt, fmtEnd, value);
- }
- std::string result = tmpStream.str(); // allocates... yuck.
- if(m_extraFlags & Flag_SpacePadPositive)
+ switch(*c)
{
- for(size_t i = 0, iend = result.size(); i < iend; ++i)
- if(result[i] == '+')
- result[i] = ' ';
+ case '\0':
+ out.write(fmt, c - fmt);
+ return c;
+ case '%':
+ out.write(fmt, c - fmt);
+ if(*(c+1) != '%')
+ return c;
+ // for "%%", tack trailing % onto next literal section.
+ fmt = ++c;
+ break;
+ default:
+ break;
}
- if((m_extraFlags & Flag_TruncateToPrecision) &&
- (int)result.size() > (int)m_out.precision())
- m_out.write(result.c_str(), m_out.precision());
- else
- m_out << result;
}
- m_extraFlags = Flag_None;
- m_fmt = fmtEnd;
}
@@ -663,13 +575,14 @@ void FormatIterator::accept(const T& value)
// with the form "%[flags][width][.precision][length]type".
//
// Formatting options which can't be natively represented using the ostream
-// state are returned in the extraFlags parameter which is a bitwise
-// combination of values from the ExtraFormatFlags enum.
-inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
- unsigned int& extraFlags,
- const char* fmtStart,
- int variableWidth,
- int variablePrecision)
+// state are returned in spacePadPositive (for space padded positive numbers)
+// and ntrunc (for truncating conversions). argIndex is incremented if
+// necessary to pull out variable width and precision . The function returns a
+// pointer to the character after the end of the current format spec.
+inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive,
+ int& ntrunc, const char* fmtStart,
+ const detail::FormatArg* formatters,
+ int& argIndex, int numFormatters)
{
if(*fmtStart != '%')
{
@@ -684,9 +597,9 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
out.unsetf(std::ios::adjustfield | std::ios::basefield |
std::ios::floatfield | std::ios::showbase | std::ios::boolalpha |
std::ios::showpoint | std::ios::showpos | std::ios::uppercase);
- extraFlags = Flag_None;
bool precisionSet = false;
bool widthSet = false;
+ int widthExtra = 0;
const char* c = fmtStart + 1;
// 1) Parse flags
for(;; ++c)
@@ -713,12 +626,15 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
case ' ':
// overridden by show positive sign, '+' flag.
if(!(out.flags() & std::ios::showpos))
- extraFlags |= Flag_SpacePadPositive;
+ spacePadPositive = true;
continue;
case '+':
out.setf(std::ios::showpos);
- extraFlags &= ~Flag_SpacePadPositive;
+ spacePadPositive = false;
+ widthExtra = 1;
continue;
+ default:
+ break;
}
break;
}
@@ -731,15 +647,19 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
if(*c == '*')
{
widthSet = true;
- if(variableWidth < 0)
+ int width = 0;
+ if(argIndex < numFormatters)
+ width = formatters[argIndex++].toInt();
+ else
+ TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width");
+ if(width < 0)
{
// negative widths correspond to '-' flag set
out.fill(' ');
out.setf(std::ios::left, std::ios::adjustfield);
- variableWidth = -variableWidth;
+ width = -width;
}
- out.width(variableWidth);
- extraFlags |= Flag_VariableWidth;
+ out.width(width);
++c;
}
// 3) Parse precision
@@ -750,8 +670,10 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
if(*c == '*')
{
++c;
- extraFlags |= Flag_VariablePrecision;
- precision = variablePrecision;
+ if(argIndex < numFormatters)
+ precision = formatters[argIndex++].toInt();
+ else
+ TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision");
}
else
{
@@ -814,7 +736,7 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
break;
case 's':
if(precisionSet)
- extraFlags |= Flag_TruncateToPrecision;
+ ntrunc = static_cast<int>(out.precision());
// Make %s print booleans as "true" and "false"
out.setf(std::ios::boolalpha);
break;
@@ -826,6 +748,8 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly "
"terminated by end of string");
return c;
+ default:
+ break;
}
if(intConversion && precisionSet && !widthSet)
{
@@ -833,7 +757,7 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
// padded with zeros on the left). This isn't really supported by the
// iostreams, but we can approximately simulate it with the width if
// the width isn't otherwise used.
- out.width(out.precision());
+ out.width(out.precision() + widthExtra);
out.setf(std::ios::internal, std::ios::adjustfield);
out.fill('0');
}
@@ -841,170 +765,282 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
}
-
//------------------------------------------------------------------------------
-// Private format function on top of which the public interface is implemented.
-// We enforce a mimimum of one value to be formatted to prevent bugs looking like
-//
-// const char* myStr = "100% broken";
-// printf(myStr); // Parses % as a format specifier
-#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
-
-template<typename T1>
-void format(FormatIterator& fmtIter, const T1& value1)
+inline void formatImpl(std::ostream& out, const char* fmt,
+ const detail::FormatArg* formatters,
+ int numFormatters)
{
- fmtIter.accept(value1);
- fmtIter.finish();
+ // Saved stream state
+ std::streamsize origWidth = out.width();
+ std::streamsize origPrecision = out.precision();
+ std::ios::fmtflags origFlags = out.flags();
+ char origFill = out.fill();
+
+ for (int argIndex = 0; argIndex < numFormatters; ++argIndex)
+ {
+ // Parse the format string
+ fmt = printFormatStringLiteral(out, fmt);
+ bool spacePadPositive = false;
+ int ntrunc = -1;
+ const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt,
+ formatters, argIndex, numFormatters);
+ if (argIndex >= numFormatters)
+ {
+ // Check args remain after reading any variable width/precision
+ TINYFORMAT_ERROR("tinyformat: Not enough format arguments");
+ return;
+ }
+ const FormatArg& arg = formatters[argIndex];
+ // Format the arg into the stream.
+ if(!spacePadPositive)
+ arg.format(out, fmt, fmtEnd, ntrunc);
+ else
+ {
+ // The following is a special case with no direct correspondence
+ // between stream formatting and the printf() behaviour. Simulate
+ // it crudely by formatting into a temporary string stream and
+ // munging the resulting string.
+ std::ostringstream tmpStream;
+ tmpStream.copyfmt(out);
+ tmpStream.setf(std::ios::showpos);
+ arg.format(tmpStream, fmt, fmtEnd, ntrunc);
+ std::string result = tmpStream.str(); // allocates... yuck.
+ for(size_t i = 0, iend = result.size(); i < iend; ++i)
+ if(result[i] == '+') result[i] = ' ';
+ out << result;
+ }
+ fmt = fmtEnd;
+ }
+
+ // Print remaining part of format string.
+ fmt = printFormatStringLiteral(out, fmt);
+ if(*fmt != '\0')
+ TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
+
+ // Restore stream state
+ out.width(origWidth);
+ out.precision(origPrecision);
+ out.flags(origFlags);
+ out.fill(origFill);
}
-// General version for C++11
-template<typename T1, typename... Args>
-void format(FormatIterator& fmtIter, const T1& value1, const Args&... args)
+} // namespace detail
+
+
+/// List of template arguments format(), held in a type-opaque way.
+///
+/// A const reference to FormatList (typedef'd as FormatListRef) may be
+/// conveniently used to pass arguments to non-template functions: All type
+/// information has been stripped from the arguments, leaving just enough of a
+/// common interface to perform formatting as required.
+class FormatList
{
- fmtIter.accept(value1);
- format(fmtIter, args...);
-}
+ public:
+ FormatList(detail::FormatArg* formatters, int N)
+ : m_formatters(formatters), m_N(N) { }
-#else
+ friend void vformat(std::ostream& out, const char* fmt,
+ const FormatList& list);
-inline void format(FormatIterator& fmtIter)
+ private:
+ const detail::FormatArg* m_formatters;
+ int m_N;
+};
+
+/// Reference to type-opaque format list for passing to vformat()
+typedef const FormatList& FormatListRef;
+
+
+namespace detail {
+
+// Format list subclass with fixed storage to avoid dynamic allocation
+template<int N>
+class FormatListN : public FormatList
{
- fmtIter.finish();
-}
+ public:
+#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
+ template<typename... Args>
+ FormatListN(const Args&... args)
+ : FormatList(&m_formatterStore[0], N),
+ m_formatterStore { FormatArg(args)... }
+ { static_assert(sizeof...(args) == N, "Number of args must be N"); }
+#else // C++98 version
+ void init(int) {}
+# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \
+ \
+ template<TINYFORMAT_ARGTYPES(n)> \
+ FormatListN(TINYFORMAT_VARARGS(n)) \
+ : FormatList(&m_formatterStore[0], n) \
+ { assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \
+ \
+ template<TINYFORMAT_ARGTYPES(n)> \
+ void init(int i, TINYFORMAT_VARARGS(n)) \
+ { \
+ m_formatterStore[i] = FormatArg(v1); \
+ init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \
+ }
-// General version for C++98
-#define TINYFORMAT_MAKE_FORMAT_DETAIL(n) \
-template<TINYFORMAT_ARGTYPES(n)> \
-void format(detail::FormatIterator& fmtIter, TINYFORMAT_VARARGS(n)) \
-{ \
- fmtIter.accept(v1); \
- format(fmtIter TINYFORMAT_PASSARGS_TAIL(n)); \
-}
+ TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR)
+# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR
+#endif
-TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_DETAIL)
-#undef TINYFORMAT_MAKE_FORMAT_DETAIL
+ private:
+ FormatArg m_formatterStore[N];
+};
-#endif // End C++98 variadic template emulation for format()
+// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard
+template<> class FormatListN<0> : public FormatList
+{
+ public: FormatListN() : FormatList(0, 0) {}
+};
} // namespace detail
//------------------------------------------------------------------------------
-// Implement all the main interface functions here in terms of detail::format()
+// Primary API functions
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
-// C++11 - the simple case
-template<typename T1, typename... Args>
-void format(std::ostream& out, const char* fmt, const T1& v1, const Args&... args)
+/// Make type-agnostic format list from list of template arguments.
+///
+/// The exact return type of this function is an implementation detail and
+/// shouldn't be relied upon. Instead it should be stored as a FormatListRef:
+///
+/// FormatListRef formatList = makeFormatList( /*...*/ );
+template<typename... Args>
+detail::FormatListN<sizeof...(Args)> makeFormatList(const Args&... args)
{
- detail::FormatIterator fmtIter(out, fmt);
- format(fmtIter, v1, args...);
+ return detail::FormatListN<sizeof...(args)>(args...);
}
-template<typename T1, typename... Args>
-std::string format(const char* fmt, const T1& v1, const Args&... args)
+#else // C++98 version
+
+inline detail::FormatListN<0> makeFormatList()
+{
+ return detail::FormatListN<0>();
+}
+#define TINYFORMAT_MAKE_MAKEFORMATLIST(n) \
+template<TINYFORMAT_ARGTYPES(n)> \
+detail::FormatListN<n> makeFormatList(TINYFORMAT_VARARGS(n)) \
+{ \
+ return detail::FormatListN<n>(TINYFORMAT_PASSARGS(n)); \
+}
+TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST)
+#undef TINYFORMAT_MAKE_MAKEFORMATLIST
+
+#endif
+
+/// Format list of arguments to the stream according to the given format string.
+///
+/// The name vformat() is chosen for the semantic similarity to vprintf(): the
+/// list of format arguments is held in a single function argument.
+inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
+{
+ detail::formatImpl(out, fmt, list.m_formatters, list.m_N);
+}
+
+
+#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
+
+/// Format list of arguments to the stream according to given format string.
+template<typename... Args>
+void format(std::ostream& out, const char* fmt, const Args&... args)
+{
+ vformat(out, fmt, makeFormatList(args...));
+}
+
+/// Format list of arguments according to the given format string and return
+/// the result as a string.
+template<typename... Args>
+std::string format(const char* fmt, const Args&... args)
{
std::ostringstream oss;
- format(oss, fmt, v1, args...);
+ format(oss, fmt, args...);
return oss.str();
}
-template<typename T1, typename... Args>
-std::string format(const std::string &fmt, const T1& v1, const Args&... args)
+/// Format list of arguments to std::cout, according to the given format string
+template<typename... Args>
+void printf(const char* fmt, const Args&... args)
+{
+ format(std::cout, fmt, args...);
+}
+
+template<typename... Args>
+void printfln(const char* fmt, const Args&... args)
+{
+ format(std::cout, fmt, args...);
+ std::cout << '\n';
+}
+
+#else // C++98 version
+
+inline void format(std::ostream& out, const char* fmt)
+{
+ vformat(out, fmt, makeFormatList());
+}
+
+inline std::string format(const char* fmt)
{
std::ostringstream oss;
- format(oss, fmt.c_str(), v1, args...);
+ format(oss, fmt);
return oss.str();
}
-template<typename T1, typename... Args>
-void printf(const char* fmt, const T1& v1, const Args&... args)
+inline void printf(const char* fmt)
{
- format(std::cout, fmt, v1, args...);
+ format(std::cout, fmt);
}
-#else
+inline void printfln(const char* fmt)
+{
+ format(std::cout, fmt);
+ std::cout << '\n';
+}
-// C++98 - define the interface functions using the wrapping macros
#define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \
\
template<TINYFORMAT_ARGTYPES(n)> \
void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
- tinyformat::detail::FormatIterator fmtIter(out, fmt); \
- tinyformat::detail::format(fmtIter, TINYFORMAT_PASSARGS(n)); \
+ vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
std::ostringstream oss; \
- tinyformat::format(oss, fmt, TINYFORMAT_PASSARGS(n)); \
+ format(oss, fmt, TINYFORMAT_PASSARGS(n)); \
return oss.str(); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
-std::string format(const std::string &fmt, TINYFORMAT_VARARGS(n)) \
+void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
- std::ostringstream oss; \
- tinyformat::format(oss, fmt.c_str(), TINYFORMAT_PASSARGS(n)); \
- return oss.str(); \
+ format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
-void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \
+void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
- tinyformat::format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
+ format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
+ std::cout << '\n'; \
}
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS)
#undef TINYFORMAT_MAKE_FORMAT_FUNCS
-#endif
-
-//------------------------------------------------------------------------------
-// Define deprecated wrapping macro for backward compatibility in tinyformat
-// 1.x. Will be removed in version 2!
-#define TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS
-#define TINYFORMAT_WRAP_FORMAT_N(n, returnType, funcName, funcDeclSuffix, \
- bodyPrefix, streamName, bodySuffix) \
-template<TINYFORMAT_ARGTYPES(n)> \
-returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt, \
- TINYFORMAT_VARARGS(n)) funcDeclSuffix \
-{ \
- bodyPrefix \
- tinyformat::format(streamName, fmt, TINYFORMAT_PASSARGS(n)); \
- bodySuffix \
-} \
-
-#define TINYFORMAT_WRAP_FORMAT(returnType, funcName, funcDeclSuffix, \
- bodyPrefix, streamName, bodySuffix) \
-inline \
-returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt \
- ) funcDeclSuffix \
-{ \
- bodyPrefix \
- tinyformat::detail::FormatIterator(streamName, fmt).finish(); \
- bodySuffix \
-} \
-TINYFORMAT_WRAP_FORMAT_N(1 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(2 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(3 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(4 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(5 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(6 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(7 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(8 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(9 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(10, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(11, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(12, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(13, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(14, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(15, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
-TINYFORMAT_WRAP_FORMAT_N(16, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
+#endif
+// Added for Bitcoin Core
+template<typename... Args>
+std::string format(const std::string &fmt, const Args&... args)
+{
+ std::ostringstream oss;
+ format(oss, fmt.c_str(), args...);
+ return oss.str();
+}
} // namespace tinyformat
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 691baa6744..82827b8e4f 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -75,7 +75,7 @@ void CTxMemPoolEntry::UpdateLockPoints(const LockPoints& lp)
size_t CTxMemPoolEntry::GetTxSize() const
{
- return GetVirtualTransactionSize(nTxWeight);
+ return GetVirtualTransactionSize(nTxWeight, sigOpCost);
}
// Update the given tx for any in-mempool descendants.
diff --git a/src/util.h b/src/util.h
index ac4b947785..39328b51ef 100644
--- a/src/util.h
+++ b/src/util.h
@@ -77,33 +77,17 @@ int LogPrintStr(const std::string &str);
#define LogPrintf(...) LogPrint(NULL, __VA_ARGS__)
-template<typename T1, typename... Args>
-static inline int LogPrint(const char* category, const char* fmt, const T1& v1, const Args&... args)
+template<typename... Args>
+static inline int LogPrint(const char* category, const char* fmt, const Args&... args)
{
if(!LogAcceptCategory(category)) return 0; \
- return LogPrintStr(tfm::format(fmt, v1, args...));
+ return LogPrintStr(tfm::format(fmt, args...));
}
-template<typename T1, typename... Args>
-bool error(const char* fmt, const T1& v1, const Args&... args)
+template<typename... Args>
+bool error(const char* fmt, const Args&... args)
{
- LogPrintStr("ERROR: " + tfm::format(fmt, v1, args...) + "\n");
- return false;
-}
-
-/**
- * Zero-arg versions of logging and error, these are not covered by
- * the variadic templates above (and don't take format arguments but
- * bare strings).
- */
-static inline int LogPrint(const char* category, const char* s)
-{
- if(!LogAcceptCategory(category)) return 0;
- return LogPrintStr(s);
-}
-static inline bool error(const char* s)
-{
- LogPrintStr(std::string("ERROR: ") + s + "\n");
+ LogPrintStr("ERROR: " + tfm::format(fmt, args...) + "\n");
return false;
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index d55cc68dc0..6647d3297f 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -602,19 +602,42 @@ UniValue dumpwallet(const UniValue& params, bool fHelp)
file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
file << "\n";
+
+ // add the base58check encoded extended master if the wallet uses HD
+ CKeyID masterKeyID = pwalletMain->GetHDChain().masterKeyID;
+ if (!masterKeyID.IsNull())
+ {
+ CKey key;
+ if (pwalletMain->GetKey(masterKeyID, key))
+ {
+ CExtKey masterKey;
+ masterKey.SetMaster(key.begin(), key.size());
+
+ CBitcoinExtKey b58extkey;
+ b58extkey.SetKey(masterKey);
+
+ file << "# extended private masterkey: " << b58extkey.ToString() << "\n\n";
+ }
+ }
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second;
std::string strTime = EncodeDumpTime(it->first);
std::string strAddr = CBitcoinAddress(keyid).ToString();
CKey key;
if (pwalletMain->GetKey(keyid, key)) {
+ file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime);
if (pwalletMain->mapAddressBook.count(keyid)) {
- file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid].name), strAddr);
+ file << strprintf("label=%s", EncodeDumpString(pwalletMain->mapAddressBook[keyid].name));
+ } else if (keyid == masterKeyID) {
+ file << "hdmaster=1";
} else if (setKeyPool.count(keyid)) {
- file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
+ file << "reserve=1";
+ } else if (pwalletMain->mapKeyMetadata[keyid].hdKeypath == "m") {
+ file << "inactivehdmaster=1";
} else {
- file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr);
+ file << "change=1";
}
+ file << strprintf(" # addr=%s%s\n", strAddr, (pwalletMain->mapKeyMetadata[keyid].hdKeypath.size() > 0 ? " hdkeypath="+pwalletMain->mapKeyMetadata[keyid].hdKeypath : ""));
}
}
file << "\n";
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index b4831ad795..a90807e514 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2081,7 +2081,7 @@ UniValue encryptwallet(const UniValue& params, bool fHelp)
// slack space in .dat files; that is bad if the old data is
// unencrypted private keys. So:
StartShutdown();
- return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
+ return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
}
UniValue lockunspent(const UniValue& params, bool fHelp)
@@ -2260,16 +2260,16 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
"Returns an object containing various wallet state info.\n"
"\nResult:\n"
"{\n"
- " \"walletversion\": xxxxx, (numeric) the wallet version\n"
- " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
- " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
- " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
- " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
- " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
- " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
- " \"masterkeyid\": \"<hash160>\", (string) the Hash160 of the HD master pubkey\n"
+ " \"walletversion\": xxxxx, (numeric) the wallet version\n"
+ " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n"
+ " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
+ " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
+ " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
+ " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
+ " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
+ " \"hdmasterkeyid\": \"<hash160>\", (string) the Hash160 of the HD master pubkey\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getwalletinfo", "")
@@ -2291,7 +2291,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
CKeyID masterKeyID = pwalletMain->GetHDChain().masterKeyID;
if (!masterKeyID.IsNull())
- obj.push_back(Pair("masterkeyid", masterKeyID.GetHex()));
+ obj.push_back(Pair("hdmasterkeyid", masterKeyID.GetHex()));
return obj;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 5908dfeace..e5ee5063a5 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -626,6 +626,15 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
Lock();
Unlock(strWalletPassphrase);
+
+ // if we are using HD, replace the HD master key (seed) with a new one
+ if (!hdChain.masterKeyID.IsNull()) {
+ CKey key;
+ CPubKey masterPubKey = GenerateNewHDMasterKey();
+ if (!SetHDMasterKey(masterPubKey))
+ return false;
+ }
+
NewKeyPool();
Lock();
@@ -1166,20 +1175,43 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
return nChange;
}
-bool CWallet::SetHDMasterKey(const CKey& key)
+CPubKey CWallet::GenerateNewHDMasterKey()
+{
+ CKey key;
+ key.MakeNewKey(true);
+
+ int64_t nCreationTime = GetTime();
+ CKeyMetadata metadata(nCreationTime);
+
+ // calculate the pubkey
+ CPubKey pubkey = key.GetPubKey();
+ assert(key.VerifyPubKey(pubkey));
+
+ // set the hd keypath to "m" -> Master, refers the masterkeyid to itself
+ metadata.hdKeypath = "m";
+ metadata.hdMasterKeyID = pubkey.GetID();
+
+ {
+ LOCK(cs_wallet);
+
+ // mem store the metadata
+ mapKeyMetadata[pubkey.GetID()] = metadata;
+
+ // write the key&metadata to the database
+ if (!AddKeyPubKey(key, pubkey))
+ throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
+ }
+
+ return pubkey;
+}
+
+bool CWallet::SetHDMasterKey(const CPubKey& pubkey)
{
LOCK(cs_wallet);
// ensure this wallet.dat can only be opened by clients supporting HD
SetMinVersion(FEATURE_HD);
- // store the key as normal "key"/"ckey" object
- // in the database
- // key metadata is not required
- CPubKey pubkey = key.GetPubKey();
- if (!AddKeyPubKey(key, pubkey))
- throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed");
-
// store the keyid (hash160) together with
// the child index counter in the database
// as a hdchain object
@@ -3299,8 +3331,8 @@ bool CWallet::InitLoadWallet()
if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && walletInstance->hdChain.masterKeyID.IsNull()) {
// generate a new master key
CKey key;
- key.MakeNewKey(true);
- if (!walletInstance->SetHDMasterKey(key))
+ CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
+ if (!walletInstance->SetHDMasterKey(masterPubKey))
throw std::runtime_error("CWallet::GenerateNewKey(): Storing master key failed");
}
CPubKey newDefaultKey;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 3a3cb6d851..0c95fdf4b0 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -899,10 +899,13 @@ public:
/* Set the HD chain model (chain child index counters) */
bool SetHDChain(const CHDChain& chain, bool memonly);
+ const CHDChain& GetHDChain() { return hdChain; }
+ /* Generates a new HD master key (will not be activated) */
+ CPubKey GenerateNewHDMasterKey();
+
/* Set the current HD master key (will reset the chain child index counters) */
- bool SetHDMasterKey(const CKey& key);
- const CHDChain& GetHDChain() { return hdChain; }
+ bool SetHDMasterKey(const CPubKey& key);
};
/** A key allocated from the key pool. */