aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bignum.h68
-rw-r--r--src/bitcoinrpc.cpp4
-rw-r--r--src/bitcoinrpc.h2
-rw-r--r--src/checkpoints.cpp9
-rw-r--r--src/init.cpp12
-rwxr-xr-xsrc/leveldb/build_detect_platform10
-rw-r--r--src/main.cpp51
-rw-r--r--src/main.h1
-rw-r--r--src/makefile.mingw2
-rw-r--r--src/net.h22
-rw-r--r--src/qt/bitcoingui.cpp60
-rw-r--r--src/qt/bitcoingui.h2
-rw-r--r--src/qt/optionsmodel.cpp4
-rw-r--r--src/rpcdump.cpp18
-rw-r--r--src/rpcwallet.cpp77
-rw-r--r--src/sync.h25
-rw-r--r--src/test/bignum_tests.cpp53
-rw-r--r--src/threadsafety.h53
-rw-r--r--src/util.cpp2
-rw-r--r--src/util.h8
-rw-r--r--src/wallet.cpp38
-rw-r--r--src/wallet.h7
22 files changed, 434 insertions, 94 deletions
diff --git a/src/bignum.h b/src/bignum.h
index 96b1b2e6ae..ad98613f5f 100644
--- a/src/bignum.h
+++ b/src/bignum.h
@@ -262,28 +262,68 @@ public:
return vch;
}
+ // The "compact" format is a representation of a whole
+ // number N using an unsigned 32bit number similar to a
+ // floating point format.
+ // The most significant 8 bits are the unsigned exponent of base 256.
+ // This exponent can be thought of as "number of bytes of N".
+ // The lower 23 bits are the mantissa.
+ // Bit number 24 (0x800000) represents the sign of N.
+ // N = (-1^sign) * mantissa * 256^(exponent-3)
+ //
+ // Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn().
+ // MPI uses the most significant bit of the first byte as sign.
+ // Thus 0x1234560000 is compact (0x05123456)
+ // and 0xc0de000000 is compact (0x0600c0de)
+ // (0x05c0de00) would be -0x40de000000
+ //
+ // Bitcoin only uses this "compact" format for encoding difficulty
+ // targets, which are unsigned 256bit quantities. Thus, all the
+ // complexities of the sign bit and using base 256 are probably an
+ // implementation accident.
+ //
+ // This implementation directly uses shifts instead of going
+ // through an intermediate MPI representation.
CBigNum& SetCompact(unsigned int nCompact)
{
unsigned int nSize = nCompact >> 24;
- std::vector<unsigned char> vch(4 + nSize);
- vch[3] = nSize;
- if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff;
- if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff;
- if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff;
- BN_mpi2bn(&vch[0], vch.size(), this);
+ bool fNegative =(nCompact & 0x00800000) != 0;
+ unsigned int nWord = nCompact & 0x007fffff;
+ if (nSize <= 3)
+ {
+ nWord >>= 8*(3-nSize);
+ BN_set_word(this, nWord);
+ }
+ else
+ {
+ BN_set_word(this, nWord);
+ BN_lshift(this, this, 8*(nSize-3));
+ }
+ BN_set_negative(this, fNegative);
return *this;
}
unsigned int GetCompact() const
{
- unsigned int nSize = BN_bn2mpi(this, NULL);
- std::vector<unsigned char> vch(nSize);
- nSize -= 4;
- BN_bn2mpi(this, &vch[0]);
- unsigned int nCompact = nSize << 24;
- if (nSize >= 1) nCompact |= (vch[4] << 16);
- if (nSize >= 2) nCompact |= (vch[5] << 8);
- if (nSize >= 3) nCompact |= (vch[6] << 0);
+ unsigned int nSize = BN_num_bytes(this);
+ unsigned int nCompact = 0;
+ if (nSize <= 3)
+ nCompact = BN_get_word(this) << 8*(3-nSize);
+ else
+ {
+ CBigNum bn;
+ BN_rshift(&bn, this, 8*(nSize-3));
+ nCompact = BN_get_word(&bn);
+ }
+ // The 0x00800000 bit denotes the sign.
+ // Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
+ if (nCompact & 0x00800000)
+ {
+ nCompact >>= 8;
+ nSize++;
+ }
+ nCompact |= nSize << 24;
+ nCompact |= (BN_is_negative(this) ? 0x00800000 : 0);
return nCompact;
}
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 481ceb9f40..bfb696da3d 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -254,6 +254,8 @@ static const CRPCCommand vRPCCommands[] =
{ "sendrawtransaction", &sendrawtransaction, false, false },
{ "gettxoutsetinfo", &gettxoutsetinfo, true, false },
{ "gettxout", &gettxout, true, false },
+ { "lockunspent", &lockunspent, false, false },
+ { "listlockunspent", &listlockunspent, false, false },
};
CRPCTable::CRPCTable()
@@ -1215,6 +1217,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]);
+ if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]);
+ if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]);
return params;
}
diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h
index dc4dc303a8..44050ae1bb 100644
--- a/src/bitcoinrpc.h
+++ b/src/bitcoinrpc.h
@@ -177,6 +177,8 @@ extern json_spirit::Value getinfo(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp
extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value lockunspent(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value createrawtransaction(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value decoderawtransaction(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, bool fHelp);
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
index 8208854962..279003072e 100644
--- a/src/checkpoints.cpp
+++ b/src/checkpoints.cpp
@@ -39,6 +39,9 @@ namespace Checkpoints
bool CheckBlock(int nHeight, const uint256& hash)
{
+ if (!GetBoolArg("-checkpoints", true))
+ return true;
+
MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
MapCheckpoints::const_iterator i = checkpoints.find(nHeight);
@@ -48,6 +51,9 @@ namespace Checkpoints
int GetTotalBlocksEstimate()
{
+ if (!GetBoolArg("-checkpoints", true))
+ return 0;
+
MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
return checkpoints.rbegin()->first;
@@ -55,6 +61,9 @@ namespace Checkpoints
CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
{
+ if (!GetBoolArg("-checkpoints", true))
+ return NULL;
+
MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints)
diff --git a/src/init.cpp b/src/init.cpp
index 39259c5b7a..74533e49e1 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -258,6 +258,7 @@ std::string HelpMessage()
" -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n" +
" -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" +
" -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" +
+ " -checkpoints " + _("Lock in block chain with compiled-in checkpoints (default: 1)") + "\n" +
" -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" +
" -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n" +
" -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n" +
@@ -345,7 +346,7 @@ void ThreadImport(void *data) {
if (fReindex) {
CImportingNow imp;
int nFile = 0;
- while (!fShutdown) {
+ while (!fRequestShutdown) {
CDiskBlockPos pos(nFile, 0);
FILE *file = OpenBlockFile(pos, true);
if (!file)
@@ -354,7 +355,7 @@ void ThreadImport(void *data) {
LoadExternalBlockFile(file, &pos);
nFile++;
}
- if (!fShutdown) {
+ if (!fRequestShutdown) {
pblocktree->WriteReindexing(false);
fReindex = false;
printf("Reindexing finished\n");
@@ -363,7 +364,7 @@ void ThreadImport(void *data) {
// hardcoded $DATADIR/bootstrap.dat
filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat";
- if (filesystem::exists(pathBootstrap) && !fShutdown) {
+ if (filesystem::exists(pathBootstrap) && !fRequestShutdown) {
FILE *file = fopen(pathBootstrap.string().c_str(), "rb");
if (file) {
CImportingNow imp;
@@ -376,7 +377,7 @@ void ThreadImport(void *data) {
// -loadblock=
BOOST_FOREACH(boost::filesystem::path &path, import->vFiles) {
- if (fShutdown)
+ if (fRequestShutdown)
break;
FILE *file = fopen(path.string().c_str(), "rb");
if (file) {
@@ -481,6 +482,7 @@ bool AppInit2()
// ********************************************************* Step 3: parameter-to-internal-flags
fDebug = GetBoolArg("-debug");
+ fBenchmark = GetBoolArg("-benchmark");
// -debug implies fDebug*
if (fDebug)
@@ -865,7 +867,7 @@ bool AppInit2()
if (walletdb.ReadBestBlock(locator))
pindexRescan = locator.GetBlockIndex();
}
- if (pindexBest != pindexRescan)
+ if (pindexBest && pindexBest != pindexRescan)
{
uiInterface.InitMessage(_("Rescanning..."));
printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform
index dd982236fd..566d1f83a4 100755
--- a/src/leveldb/build_detect_platform
+++ b/src/leveldb/build_detect_platform
@@ -119,6 +119,16 @@ case "$TARGET_OS" in
PLATFORM_EXTRALIBS="-lboost_system-mt-s -lboost_filesystem-mt-s -lboost_thread_win32-mt-s"
CROSS_COMPILE=true
;;
+ NATIVE_WINDOWS)
+ PLATFORM=OS_WINDOWS
+ COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DBOOST_THREAD_USE_LIB"
+ PLATFORM_CXXFLAGS=""
+ PLATFORM_LDFLAGS=""
+ PLATFORM_SHARED_CFLAGS=""
+ PLATFORM_SOURCES="port/port_win.cc util/env_boost.cc util/win_logger.cc"
+ PLATFORM_EXTRALIBS="-lboost_system-mgw45-mt-s-1_50 -lboost_filesystem-mgw45-mt-s-1_50 -lboost_thread-mgw45-mt-s-1_50 -lboost_chrono-mgw45-mt-s-1_50"
+ CROSS_COMPILE=true
+ ;;
*)
echo "Unknown platform!" >&2
exit 1
diff --git a/src/main.cpp b/src/main.cpp
index db12c63932..96bdabd586 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -42,6 +42,7 @@ set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain
int64 nTimeBestReceived = 0;
bool fImporting = false;
bool fReindex = false;
+bool fBenchmark = false;
unsigned int nCoinCacheSize = 5000;
CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
@@ -1240,9 +1241,12 @@ bool ConnectBestBlock() {
if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) {
reverse(vAttach.begin(), vAttach.end());
- BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach)
+ BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) {
+ if (fRequestShutdown)
+ break;
if (!SetBestChain(pindexSwitch))
return false;
+ }
return true;
}
pindexTest = pindexTest->pprev;
@@ -1590,12 +1594,16 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
CBlockUndo blockundo;
+ int64 nStart = GetTimeMicros();
int64 nFees = 0;
+ int nInputs = 0;
unsigned int nSigOps = 0;
for (unsigned int i=0; i<vtx.size(); i++)
{
+
const CTransaction &tx = vtx[i];
+ nInputs += tx.vin.size();
nSigOps += tx.GetLegacySigOpCount();
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
@@ -1626,7 +1634,11 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
return error("ConnectBlock() : UpdateInputs failed");
if (!tx.IsCoinBase())
blockundo.vtxundo.push_back(txundo);
+
}
+ int64 nTime = GetTimeMicros() - nStart;
+ if (fBenchmark)
+ printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1));
if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
return error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees));
@@ -1691,7 +1703,7 @@ bool SetBestChain(CBlockIndex* pindexNew)
// Find the fork (typically, there is none)
CBlockIndex* pfork = view.GetBestBlock();
CBlockIndex* plonger = pindexNew;
- while (pfork != plonger)
+ while (pfork && pfork != plonger)
{
while (plonger->nHeight > pfork->nHeight)
if (!(plonger = plonger->pprev))
@@ -1724,8 +1736,11 @@ bool SetBestChain(CBlockIndex* pindexNew)
CBlock block;
if (!block.ReadFromDisk(pindex))
return error("SetBestBlock() : ReadFromDisk for disconnect failed");
+ int64 nStart = GetTimeMicros();
if (!block.DisconnectBlock(pindex, view))
return error("SetBestBlock() : DisconnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str());
+ if (fBenchmark)
+ printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Queue memory transactions to resurrect.
// We only do this for blocks after the last checkpoint (reorganisation before that
@@ -1741,11 +1756,14 @@ bool SetBestChain(CBlockIndex* pindexNew)
CBlock block;
if (!block.ReadFromDisk(pindex))
return error("SetBestBlock() : ReadFromDisk for connect failed");
+ int64 nStart = GetTimeMicros();
if (!block.ConnectBlock(pindex, view)) {
InvalidChainFound(pindexNew);
InvalidBlockFound(pindex);
return error("SetBestBlock() : ConnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str());
}
+ if (fBenchmark)
+ printf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Queue memory transactions to delete
BOOST_FOREACH(const CTransaction& tx, block.vtx)
@@ -1753,8 +1771,13 @@ bool SetBestChain(CBlockIndex* pindexNew)
}
// Flush changes to global coin state
+ int64 nStart = GetTimeMicros();
+ int nModified = view.GetCacheSize();
if (!view.Flush())
return error("SetBestBlock() : unable to modify coin state");
+ int64 nTime = GetTimeMicros() - nStart;
+ if (fBenchmark)
+ printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified);
// Make sure it's successfully written to disk before changing memory structure
bool fIsInitialDownload = IsInitialBlockDownload();
@@ -1897,6 +1920,7 @@ bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeigh
nLastBlockFile = pos.nFile;
infoLastBlockFile.SetNull();
pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile);
+ fUpdatedLast = true;
}
} else {
while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
@@ -2326,13 +2350,18 @@ bool static LoadBlockIndexDB()
if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str());
+ // Load bnBestInvalidWork, OK if it doesn't exist
+ pblocktree->ReadBestInvalidWork(bnBestInvalidWork);
+
+ // Check whether we need to continue reindexing
+ bool fReindexing = false;
+ pblocktree->ReadReindexing(fReindexing);
+ fReindex |= fReindexing;
+
// Load hashBestChain pointer to end of best chain
pindexBest = pcoinsTip->GetBestBlock();
if (pindexBest == NULL)
- {
- if (pindexGenesisBlock == NULL)
- return true;
- }
+ return true;
hashBestChain = pindexBest->GetBlockHash();
nBestHeight = pindexBest->nHeight;
bnBestChainWork = pindexBest->bnChainWork;
@@ -2348,14 +2377,6 @@ bool static LoadBlockIndexDB()
BlockHashStr(hashBestChain).c_str(), nBestHeight,
DateTimeStrFormat("%Y-%m-%dT%H:%M:%S", pindexBest->GetBlockTime()).c_str());
- // Load bnBestInvalidWork, OK if it doesn't exist
- pblocktree->ReadBestInvalidWork(bnBestInvalidWork);
-
- // Check whether we need to continue reindexing
- bool fReindexing = false;
- pblocktree->ReadReindexing(fReindexing);
- fReindex |= fReindexing;
-
// Verify blocks in the best chain
int nCheckLevel = GetArg("-checklevel", 1);
int nCheckDepth = GetArg( "-checkblocks", 2500);
@@ -2556,7 +2577,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
}
}
uint64 nRewind = blkdat.GetPos();
- while (blkdat.good() && !blkdat.eof() && !fShutdown) {
+ while (blkdat.good() && !blkdat.eof() && !fRequestShutdown) {
blkdat.SetPos(nRewind);
nRewind++; // start one byte further next time, in case of failure
blkdat.SetLimit(); // remove former limit
diff --git a/src/main.h b/src/main.h
index fbd68127a4..fdaec3469e 100644
--- a/src/main.h
+++ b/src/main.h
@@ -89,6 +89,7 @@ extern std::set<CWallet*> setpwalletRegistered;
extern unsigned char pchMessageStart[4];
extern bool fImporting;
extern bool fReindex;
+extern bool fBenchmark;
extern unsigned int nCoinCacheSize;
// Settings
diff --git a/src/makefile.mingw b/src/makefile.mingw
index fd61d44709..22d65d6703 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -97,7 +97,7 @@ DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
# TODO: If this fails, try adding a ranlib libleveldb.a && ranlib libmemenv.a
leveldb/libleveldb.a:
cd leveldb && $(MAKE) libleveldb.a libmemenv.a && cd ..
-obj/leveldb.o: leveldb/libleveldb.lib
+obj/leveldb.o: leveldb/libleveldb.a
obj/%.o: %.cpp $(HEADERS)
g++ -c $(CFLAGS) -o $@ $<
diff --git a/src/net.h b/src/net.h
index c43e438d5a..ace119f954 100644
--- a/src/net.h
+++ b/src/net.h
@@ -305,7 +305,8 @@ public:
- void BeginMessage(const char* pszCommand)
+ // TODO: Document the postcondition of this function. Is cs_vSend locked?
+ void BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend)
{
ENTER_CRITICAL_SECTION(cs_vSend);
if (nHeaderStart != -1)
@@ -317,7 +318,8 @@ public:
printf("sending: %s ", pszCommand);
}
- void AbortMessage()
+ // TODO: Document the precondition of this function. Is cs_vSend locked?
+ void AbortMessage() UNLOCK_FUNCTION(cs_vSend)
{
if (nHeaderStart < 0)
return;
@@ -330,7 +332,8 @@ public:
printf("(aborted)\n");
}
- void EndMessage()
+ // TODO: Document the precondition of this function. Is cs_vSend locked?
+ void EndMessage() UNLOCK_FUNCTION(cs_vSend)
{
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
@@ -362,19 +365,6 @@ public:
LEAVE_CRITICAL_SECTION(cs_vSend);
}
- void EndMessageAbortIfEmpty()
- {
- if (nHeaderStart < 0)
- return;
- int nSize = vSend.size() - nMessageStart;
- if (nSize > 0)
- EndMessage();
- else
- AbortMessage();
- }
-
-
-
void PushVersion();
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 3fe86501f6..9cd22ed297 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -697,39 +697,33 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee)
*payFee = (retval == QMessageBox::Yes);
}
-void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end)
+void BitcoinGUI::incomingTransaction(const QModelIndex& parent, int start, int /*end*/)
{
- if(!walletModel || !clientModel)
+ // Prevent balloon-spam when initial block download is in progress
+ if(!walletModel || !clientModel || clientModel->inInitialBlockDownload())
return;
+
TransactionTableModel *ttm = walletModel->getTransactionTableModel();
+
+ QString date = ttm->index(start, TransactionTableModel::Date, parent)
+ .data().toString();
qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent)
- .data(Qt::EditRole).toULongLong();
- if(!clientModel->inInitialBlockDownload())
- {
- // On new transaction, make an info balloon
- // Unless the initial block download is in progress, to prevent balloon-spam
- QString date = ttm->index(start, TransactionTableModel::Date, parent)
- .data().toString();
- QString type = ttm->index(start, TransactionTableModel::Type, parent)
+ .data(Qt::EditRole).toULongLong();
+ QString type = ttm->index(start, TransactionTableModel::Type, parent)
+ .data().toString();
+ QString address = ttm->index(start, TransactionTableModel::ToAddress, parent)
.data().toString();
- QString address = ttm->index(start, TransactionTableModel::ToAddress, parent)
- .data().toString();
- QIcon icon = qvariant_cast<QIcon>(ttm->index(start,
- TransactionTableModel::ToAddress, parent)
- .data(Qt::DecorationRole));
- notificator->notify(Notificator::Information,
- (amount)<0 ? tr("Sent transaction") :
- tr("Incoming transaction"),
- tr("Date: %1\n"
- "Amount: %2\n"
- "Type: %3\n"
- "Address: %4\n")
- .arg(date)
- .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true))
- .arg(type)
- .arg(address), icon);
- }
+ // On new transaction, make an info balloon
+ message((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"),
+ tr("Date: %1\n"
+ "Amount: %2\n"
+ "Type: %3\n"
+ "Address: %4\n")
+ .arg(date)
+ .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true))
+ .arg(type)
+ .arg(address), CClientUIInterface::MSG_INFORMATION);
}
void BitcoinGUI::gotoOverviewPage()
@@ -821,7 +815,8 @@ void BitcoinGUI::dropEvent(QDropEvent *event)
if (nValidUrisFound)
gotoSendCoinsPage();
else
- notificator->notify(Notificator::Warning, tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."));
+ message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
+ CClientUIInterface::ICON_WARNING);
}
event->acceptProposedAction();
@@ -848,7 +843,8 @@ void BitcoinGUI::handleURI(QString strURI)
gotoSendCoinsPage();
}
else
- notificator->notify(Notificator::Warning, tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."));
+ message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
+ CClientUIInterface::ICON_WARNING);
}
void BitcoinGUI::setEncryptionStatus(int status)
@@ -898,8 +894,12 @@ void BitcoinGUI::backupWallet()
QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)"));
if(!filename.isEmpty()) {
if(!walletModel->backupWallet(filename)) {
- QMessageBox::warning(this, tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location."));
+ message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location."),
+ CClientUIInterface::MSG_ERROR);
}
+ else
+ message(tr("Backup Successful"), tr("The wallet data was successfully saved to the new location."),
+ CClientUIInterface::MSG_INFORMATION);
}
}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 3faf6d948c..b7afdb1c8c 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -168,7 +168,7 @@ private slots:
The new items are those between start and end inclusive, under the given parent item.
*/
- void incomingTransaction(const QModelIndex & parent, int start, int end);
+ void incomingTransaction(const QModelIndex& parent, int start, int /*end*/);
/** Encrypt the wallet */
void encryptWallet(bool status);
/** Backup the wallet */
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index e3c9413f1b..5dac5a6c45 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -137,7 +137,11 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
case MinimizeToTray:
return QVariant(fMinimizeToTray);
case MapPortUPnP:
+#ifdef USE_UPNP
return settings.value("fUseUPnP", GetBoolArg("-upnp", true));
+#else
+ return QVariant(false);
+#endif
case MinimizeOnClose:
return QVariant(fMinimizeOnClose);
case ProxyUse: {
diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp
index 55d5d79e23..77cef02736 100644
--- a/src/rpcdump.cpp
+++ b/src/rpcdump.cpp
@@ -34,15 +34,21 @@ public:
Value importprivkey(const Array& params, bool fHelp)
{
- if (fHelp || params.size() < 1 || params.size() > 2)
+ if (fHelp || params.size() < 1 || params.size() > 3)
throw runtime_error(
- "importprivkey <bitcoinprivkey> [label]\n"
+ "importprivkey <bitcoinprivkey> [label] [rescan=true]\n"
"Adds a private key (as returned by dumpprivkey) to your wallet.");
string strSecret = params[0].get_str();
string strLabel = "";
if (params.size() > 1)
strLabel = params[1].get_str();
+
+ // Whether to perform rescan after import
+ bool fRescan = true;
+ if (params.size() > 2)
+ fRescan = params[2].get_bool();
+
CBitcoinSecret vchSecret;
bool fGood = vchSecret.SetString(strSecret);
@@ -61,9 +67,11 @@ Value importprivkey(const Array& params, bool fHelp)
if (!pwalletMain->AddKey(key))
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
-
- pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
- pwalletMain->ReacceptWalletTransactions();
+
+ if (fRescan) {
+ pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
+ pwalletMain->ReacceptWalletTransactions();
+ }
}
return Value::null;
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index d597e5e3f7..90a68f560a 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -3,14 +3,18 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <boost/assign/list_of.hpp>
+
#include "wallet.h"
#include "walletdb.h"
#include "bitcoinrpc.h"
#include "init.h"
#include "base58.h"
-using namespace json_spirit;
using namespace std;
+using namespace boost;
+using namespace boost::assign;
+using namespace json_spirit;
int64 nWalletUnlockTime;
static CCriticalSection cs_nWalletUnlockTime;
@@ -1497,3 +1501,74 @@ Value validateaddress(const Array& params, bool fHelp)
return ret;
}
+Value lockunspent(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 2)
+ throw runtime_error(
+ "lockunspent unlock? [array-of-Objects]\n"
+ "Updates list of temporarily unspendable outputs.");
+
+ if (params.size() == 1)
+ RPCTypeCheck(params, list_of(bool_type));
+ else
+ RPCTypeCheck(params, list_of(bool_type)(array_type));
+
+ bool fUnlock = params[0].get_bool();
+
+ if (params.size() == 1) {
+ if (fUnlock)
+ pwalletMain->UnlockAllCoins();
+ return true;
+ }
+
+ Array outputs = params[1].get_array();
+ BOOST_FOREACH(Value& output, outputs)
+ {
+ if (output.type() != obj_type)
+ throw JSONRPCError(-8, "Invalid parameter, expected object");
+ const Object& o = output.get_obj();
+
+ RPCTypeCheck(o, map_list_of("txid", str_type)("vout", int_type));
+
+ string txid = find_value(o, "txid").get_str();
+ if (!IsHex(txid))
+ throw JSONRPCError(-8, "Invalid parameter, expected hex txid");
+
+ int nOutput = find_value(o, "vout").get_int();
+ if (nOutput < 0)
+ throw JSONRPCError(-8, "Invalid parameter, vout must be positive");
+
+ COutPoint outpt(uint256(txid), nOutput);
+
+ if (fUnlock)
+ pwalletMain->UnlockCoin(outpt);
+ else
+ pwalletMain->LockCoin(outpt);
+ }
+
+ return true;
+}
+
+Value listlockunspent(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 0)
+ throw runtime_error(
+ "listlockunspent\n"
+ "Returns list of temporarily unspendable outputs.");
+
+ vector<COutPoint> vOutpts;
+ pwalletMain->ListLockedCoins(vOutpts);
+
+ Array ret;
+
+ BOOST_FOREACH(COutPoint &outpt, vOutpts) {
+ Object o;
+
+ o.push_back(Pair("txid", outpt.hash.GetHex()));
+ o.push_back(Pair("vout", (int)outpt.n));
+ ret.push_back(o);
+ }
+
+ return ret;
+}
+
diff --git a/src/sync.h b/src/sync.h
index 9dfc6697c6..930c9b2b80 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -9,15 +9,36 @@
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/condition_variable.hpp>
+#include "threadsafety.h"
+// Template mixin that adds -Wthread-safety locking annotations to a
+// subset of the mutex API.
+template <typename PARENT>
+class LOCKABLE AnnotatedMixin : public PARENT
+{
+public:
+ void lock() EXCLUSIVE_LOCK_FUNCTION()
+ {
+ PARENT::lock();
+ }
+ void unlock() UNLOCK_FUNCTION()
+ {
+ PARENT::unlock();
+ }
+ bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
+ {
+ return PARENT::try_lock();
+ }
+};
/** Wrapped boost mutex: supports recursive locking, but no waiting */
-typedef boost::recursive_mutex CCriticalSection;
+// TODO: We should move away from using the recursive lock by default.
+typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection;
/** Wrapped boost mutex: supports waiting but not recursive locking */
-typedef boost::mutex CWaitableCriticalSection;
+typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection;
#ifdef DEBUG_LOCKORDER
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
diff --git a/src/test/bignum_tests.cpp b/src/test/bignum_tests.cpp
index 744681871f..5b898d1499 100644
--- a/src/test/bignum_tests.cpp
+++ b/src/test/bignum_tests.cpp
@@ -122,4 +122,57 @@ BOOST_AUTO_TEST_CASE(bignum_setint64)
}
}
+
+BOOST_AUTO_TEST_CASE(bignum_SetCompact)
+{
+ CBigNum num;
+ num.SetCompact(0);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0);
+
+ num.SetCompact(0x00123456);
+ BOOST_CHECK_EQUAL(num.GetHex(), "0");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0);
+
+ num.SetCompact(0x01123456);
+ BOOST_CHECK_EQUAL(num.GetHex(), "12");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x01120000);
+
+ // Make sure that we don't generate compacts with the 0x00800000 bit set
+ num = 0x80;
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x02008000);
+
+ num.SetCompact(0x01fedcba);
+ BOOST_CHECK_EQUAL(num.GetHex(), "-7e");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x01fe0000);
+
+ num.SetCompact(0x02123456);
+ BOOST_CHECK_EQUAL(num.GetHex(), "1234");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x02123400);
+
+ num.SetCompact(0x03123456);
+ BOOST_CHECK_EQUAL(num.GetHex(), "123456");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x03123456);
+
+ num.SetCompact(0x04123456);
+ BOOST_CHECK_EQUAL(num.GetHex(), "12345600");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x04123456);
+
+ num.SetCompact(0x04923456);
+ BOOST_CHECK_EQUAL(num.GetHex(), "-12345600");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x04923456);
+
+ num.SetCompact(0x05009234);
+ BOOST_CHECK_EQUAL(num.GetHex(), "92340000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x05009234);
+
+ num.SetCompact(0x20123456);
+ BOOST_CHECK_EQUAL(num.GetHex(), "1234560000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0x20123456);
+
+ num.SetCompact(0xff123456);
+ BOOST_CHECK_EQUAL(num.GetHex(), "123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(num.GetCompact(), 0xff123456);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/threadsafety.h b/src/threadsafety.h
new file mode 100644
index 0000000000..3d3d526fd6
--- /dev/null
+++ b/src/threadsafety.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2012 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifndef BITCOIN_THREADSAFETY_H
+#define BITCOIN_THREADSAFETY_H
+
+#ifdef __clang__
+// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
+// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
+//
+// See http://clang.llvm.org/docs/LanguageExtensions.html#threadsafety
+// for documentation. The clang compiler can do advanced static analysis
+// of locking when given the -Wthread-safety option.
+#define LOCKABLE __attribute__ ((lockable))
+#define SCOPED_LOCKABLE __attribute__ ((scoped_lockable))
+#define GUARDED_BY(x) __attribute__ ((guarded_by(x)))
+#define GUARDED_VAR __attribute__ ((guarded_var))
+#define PT_GUARDED_BY(x) __attribute__ ((pt_guarded_by(x)))
+#define PT_GUARDED_VAR __attribute__ ((pt_guarded_var))
+#define ACQUIRED_AFTER(...) __attribute__ ((acquired_after(__VA_ARGS__)))
+#define ACQUIRED_BEFORE(...) __attribute__ ((acquired_before(__VA_ARGS__)))
+#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__ ((exclusive_lock_function(__VA_ARGS__)))
+#define SHARED_LOCK_FUNCTION(...) __attribute__ ((shared_lock_function(__VA_ARGS__)))
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__ ((exclusive_trylock_function(__VA_ARGS__)))
+#define SHARED_TRYLOCK_FUNCTION(...) __attribute__ ((shared_trylock_function(__VA_ARGS__)))
+#define UNLOCK_FUNCTION(...) __attribute__ ((unlock_function(__VA_ARGS__)))
+#define LOCK_RETURNED(x) __attribute__ ((lock_returned(x)))
+#define LOCKS_EXCLUDED(...) __attribute__ ((locks_excluded(__VA_ARGS__)))
+#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__ ((exclusive_locks_required(__VA_ARGS__)))
+#define SHARED_LOCKS_REQUIRED(...) __attribute__ ((shared_locks_required(__VA_ARGS__)))
+#define NO_THREAD_SAFETY_ANALYSIS __attribute__ ((no_thread_safety_analysis))
+#else
+#define LOCKABLE
+#define SCOPED_LOCKABLE
+#define GUARDED_BY(x)
+#define GUARDED_VAR
+#define PT_GUARDED_BY(x)
+#define PT_GUARDED_VAR
+#define ACQUIRED_AFTER(...)
+#define ACQUIRED_BEFORE(...)
+#define EXCLUSIVE_LOCK_FUNCTION(...)
+#define SHARED_LOCK_FUNCTION(...)
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
+#define SHARED_TRYLOCK_FUNCTION(...)
+#define UNLOCK_FUNCTION(...)
+#define LOCK_RETURNED(x)
+#define LOCKS_EXCLUDED(...)
+#define EXCLUSIVE_LOCKS_REQUIRED(...)
+#define SHARED_LOCKS_REQUIRED(...)
+#define NO_THREAD_SAFETY_ANALYSIS
+#endif // __GNUC__
+#endif // BITCOIN_THREADSAFETY_H
diff --git a/src/util.cpp b/src/util.cpp
index 194218590d..806f3ebcf6 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -64,7 +64,7 @@ bool fDebug = false;
bool fDebugNet = false;
bool fPrintToConsole = false;
bool fPrintToDebugger = false;
-bool fRequestShutdown = false;
+volatile bool fRequestShutdown = false;
bool fShutdown = false;
bool fDaemon = false;
bool fServer = false;
diff --git a/src/util.h b/src/util.h
index ada0dd3790..ab921e6f05 100644
--- a/src/util.h
+++ b/src/util.h
@@ -132,7 +132,7 @@ extern bool fDebug;
extern bool fDebugNet;
extern bool fPrintToConsole;
extern bool fPrintToDebugger;
-extern bool fRequestShutdown;
+extern volatile bool fRequestShutdown;
extern bool fShutdown;
extern bool fDaemon;
extern bool fServer;
@@ -330,6 +330,12 @@ inline int64 GetTimeMillis()
boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds();
}
+inline int64 GetTimeMicros()
+{
+ return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) -
+ boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds();
+}
+
inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime)
{
time_t n = nTime;
diff --git a/src/wallet.cpp b/src/wallet.cpp
index cedda9e9e0..c07adff6c6 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -957,9 +957,11 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed) const
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
continue;
- for (unsigned int i = 0; i < pcoin->vout.size(); i++)
- if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0)
+ for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
+ if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) &&
+ !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0)
vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain()));
+ }
}
}
}
@@ -1770,3 +1772,35 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx)
NotifyTransactionChanged(this, hashTx, CT_UPDATED);
}
}
+
+void CWallet::LockCoin(COutPoint& output)
+{
+ setLockedCoins.insert(output);
+}
+
+void CWallet::UnlockCoin(COutPoint& output)
+{
+ setLockedCoins.erase(output);
+}
+
+void CWallet::UnlockAllCoins()
+{
+ setLockedCoins.clear();
+}
+
+bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
+{
+ COutPoint outpt(hash, n);
+
+ return (setLockedCoins.count(outpt) > 0);
+}
+
+void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts)
+{
+ for (std::set<COutPoint>::iterator it = setLockedCoins.begin();
+ it != setLockedCoins.end(); it++) {
+ COutPoint outpt = (*it);
+ vOutpts.push_back(outpt);
+ }
+}
+
diff --git a/src/wallet.h b/src/wallet.h
index 05d60056f4..ecfdafe2eb 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -119,11 +119,18 @@ public:
CPubKey vchDefaultKey;
+ std::set<COutPoint> setLockedCoins;
+
// check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; }
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true) const;
bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
+ bool IsLockedCoin(uint256 hash, unsigned int n) const;
+ void LockCoin(COutPoint& output);
+ void UnlockCoin(COutPoint& output);
+ void UnlockAllCoins();
+ void ListLockedCoins(std::vector<COutPoint>& vOutpts);
// keystore implementation
// Generate a new key