aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bitcoind.cpp2
-rw-r--r--src/bitcoinrpc.cpp4
-rw-r--r--src/bitcoinrpc.h1
-rw-r--r--src/bloom.cpp2
-rw-r--r--src/chainparams.cpp5
-rw-r--r--src/checkpoints.cpp17
-rw-r--r--src/compat.h6
-rw-r--r--src/core.cpp294
-rw-r--r--src/core.h236
-rw-r--r--src/crypter.cpp3
-rw-r--r--src/db.cpp1
-rw-r--r--src/init.cpp42
-rw-r--r--src/init.h1
-rw-r--r--src/key.cpp5
-rw-r--r--src/leveldb/AUTHORS3
-rw-r--r--src/leveldb/Makefile6
-rw-r--r--src/leveldb/db/db_impl.cc41
-rw-r--r--src/leveldb/db/db_impl.h1
-rw-r--r--src/leveldb/db/db_test.cc85
-rw-r--r--src/leveldb/db/dbformat.cc2
-rw-r--r--src/leveldb/db/filename_test.cc2
-rw-r--r--src/leveldb/db/version_set.cc21
-rw-r--r--src/leveldb/include/leveldb/db.h2
-rw-r--r--src/leveldb/port/port_win.cc10
-rw-r--r--src/leveldb/table/block.cc13
-rw-r--r--src/leveldb/table/table.cc1
-rw-r--r--src/leveldb/table/table_test.cc30
-rw-r--r--src/leveldb/util/cache.cc5
-rw-r--r--src/leveldb/util/coding_test.cc2
-rw-r--r--src/leveldb/util/comparator.cc4
-rw-r--r--src/leveldb/util/env_posix.cc10
-rw-r--r--src/leveldb/util/hash.cc11
-rw-r--r--src/main.cpp1140
-rw-r--r--src/main.h231
-rw-r--r--src/makefile.linux-mingw1
-rw-r--r--src/makefile.mingw3
-rw-r--r--src/makefile.osx1
-rw-r--r--src/makefile.unix1
-rw-r--r--src/miner.cpp663
-rw-r--r--src/miner.h24
-rw-r--r--src/net.cpp21
-rw-r--r--src/qt/bitcoin.cpp103
-rw-r--r--src/qt/bitcoinamountfield.cpp13
-rw-r--r--src/qt/bitcoingui.cpp31
-rw-r--r--src/qt/bitcoingui.h4
-rw-r--r--src/qt/bitcoinstrings.cpp7
-rw-r--r--src/qt/bitcoinunits.cpp11
-rw-r--r--src/qt/bitcoinunits.h2
-rw-r--r--src/qt/forms/intro.ui266
-rw-r--r--src/qt/guiutil.cpp31
-rw-r--r--src/qt/guiutil.h5
-rw-r--r--src/qt/intro.cpp278
-rw-r--r--src/qt/intro.h68
-rw-r--r--src/qt/locale/bitcoin_en.ts345
-rw-r--r--src/qt/notificator.cpp2
-rw-r--r--src/qt/optionsmodel.cpp2
-rw-r--r--src/qt/paymentserver.cpp3
-rw-r--r--src/qt/rpcconsole.cpp2
-rw-r--r--src/qt/splashscreen.cpp3
-rw-r--r--src/rpcblockchain.cpp11
-rw-r--r--src/rpcmining.cpp2
-rw-r--r--src/rpcrawtransaction.cpp1
-rw-r--r--src/rpcwallet.cpp23
-rw-r--r--src/script.cpp2
-rw-r--r--src/sync.h42
-rw-r--r--src/test/DoS_tests.cpp29
-rw-r--r--src/test/checkblock_tests.cpp2
-rw-r--r--src/test/data/script_invalid.json16
-rw-r--r--src/test/data/script_valid.json15
-rw-r--r--src/test/miner_tests.cpp1
-rw-r--r--src/test/script_P2SH_tests.cpp8
-rw-r--r--src/test/transaction_tests.cpp9
-rw-r--r--src/test/util_tests.cpp34
-rw-r--r--src/util.cpp10
-rw-r--r--src/util.h7
-rw-r--r--src/version.cpp2
-rw-r--r--src/wallet.cpp25
-rw-r--r--src/wallet.h11
-rw-r--r--src/walletdb.cpp5
79 files changed, 2783 insertions, 1601 deletions
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index bc23cf5507..be18f9ae83 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -74,7 +74,7 @@ bool AppInit(int argc, char* argv[])
int ret = CommandLineRPC(argc, argv);
exit(ret);
}
-#if !defined(WIN32)
+#ifndef WIN32
fDaemon = GetBoolArg("-daemon", false);
if (fDaemon)
{
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 5908126200..a14f5e7060 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -195,6 +195,7 @@ static const CRPCCommand vRPCCommands[] =
{ "help", &help, true, true },
{ "stop", &stop, true, true },
{ "getblockcount", &getblockcount, true, false },
+ { "getbestblockhash", &getbestblockhash, true, false },
{ "getconnectioncount", &getconnectioncount, true, false },
{ "getpeerinfo", &getpeerinfo, true, false },
{ "addnode", &addnode, true, true },
@@ -413,7 +414,7 @@ int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
{
int nLen = 0;
- loop
+ while (true)
{
string str;
std::getline(stream, str);
@@ -1199,6 +1200,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]);
if (strMethod == "verifychain" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "verifychain" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
return params;
}
diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h
index 247c47adf9..4d5599be84 100644
--- a/src/bitcoinrpc.h
+++ b/src/bitcoinrpc.h
@@ -201,6 +201,7 @@ extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, b
extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getblockcount(const json_spirit::Array& params, bool fHelp); // in rpcblockchain.cpp
+extern json_spirit::Value getbestblockhash(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getdifficulty(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value settxfee(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getrawmempool(const json_spirit::Array& params, bool fHelp);
diff --git a/src/bloom.cpp b/src/bloom.cpp
index d9ec2efa81..b6799e143d 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -5,7 +5,7 @@
#include <stdlib.h>
#include "bloom.h"
-#include "main.h"
+#include "core.h"
#include "script.h"
#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 49873023ae..0795f09765 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -6,7 +6,8 @@
#include "assert.h"
#include "chainparams.h"
-#include "main.h"
+#include "core.h"
+#include "protocol.h"
#include "util.h"
#include <boost/assign/list_of.hpp>
@@ -201,7 +202,7 @@ public:
genesis.nTime = 1296688602;
genesis.nNonce = 414098458;
hashGenesisBlock = genesis.GetHash();
- assert(hashGenesisBlock == uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
+ assert(hashGenesisBlock == uint256("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
vFixedSeeds.clear();
vSeeds.clear();
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
index ba29e2463e..0716cfca31 100644
--- a/src/checkpoints.cpp
+++ b/src/checkpoints.cpp
@@ -67,11 +67,24 @@ namespace Checkpoints
300
};
+ static MapCheckpoints mapCheckpointsRegtest =
+ boost::assign::map_list_of
+ ( 0, uint256("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"))
+ ;
+ static const CCheckpointData dataRegtest = {
+ &mapCheckpointsRegtest,
+ 0,
+ 0,
+ 0
+ };
+
const CCheckpointData &Checkpoints() {
- if (TestNet())
+ if (Params().NetworkID() == CChainParams::TESTNET)
return dataTestnet;
- else
+ else if (Params().NetworkID() == CChainParams::MAIN)
return data;
+ else
+ return dataRegtest;
}
bool CheckBlock(int nHeight, const uint256& hash)
diff --git a/src/compat.h b/src/compat.h
index 706221692b..4e98b46c1c 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -3,7 +3,7 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef _BITCOIN_COMPAT_H
-#define _BITCOIN_COMPAT_H 1
+#define _BITCOIN_COMPAT_H
#ifdef WIN32
#define _WIN32_WINNT 0x0501
@@ -13,7 +13,6 @@
#endif
#define FD_SETSIZE 1024 // max number of fds in fd_set
#include <winsock2.h>
-#include <mswsock.h>
#include <ws2tcpip.h>
#else
#include <sys/types.h>
@@ -26,12 +25,11 @@
#include <ifaddrs.h>
#endif
-typedef u_int SOCKET;
#ifdef WIN32
#define MSG_NOSIGNAL 0
#define MSG_DONTWAIT 0
-typedef int socklen_t;
#else
+typedef u_int SOCKET;
#include "errno.h"
#define WSAGetLastError() errno
#define WSAEINVAL EINVAL
diff --git a/src/core.cpp b/src/core.cpp
index b12c90efe8..afba0959cf 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -4,4 +4,298 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "core.h"
+#include "util.h"
+std::string COutPoint::ToString() const
+{
+ return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10).c_str(), n);
+}
+
+void COutPoint::print() const
+{
+ printf("%s\n", ToString().c_str());
+}
+
+CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, unsigned int nSequenceIn)
+{
+ prevout = prevoutIn;
+ scriptSig = scriptSigIn;
+ nSequence = nSequenceIn;
+}
+
+CTxIn::CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn, unsigned int nSequenceIn)
+{
+ prevout = COutPoint(hashPrevTx, nOut);
+ scriptSig = scriptSigIn;
+ nSequence = nSequenceIn;
+}
+
+std::string CTxIn::ToString() const
+{
+ std::string str;
+ str += "CTxIn(";
+ str += prevout.ToString();
+ if (prevout.IsNull())
+ str += strprintf(", coinbase %s", HexStr(scriptSig).c_str());
+ else
+ str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str());
+ if (nSequence != std::numeric_limits<unsigned int>::max())
+ str += strprintf(", nSequence=%u", nSequence);
+ str += ")";
+ return str;
+}
+
+void CTxIn::print() const
+{
+ printf("%s\n", ToString().c_str());
+}
+
+CTxOut::CTxOut(int64 nValueIn, CScript scriptPubKeyIn)
+{
+ nValue = nValueIn;
+ scriptPubKey = scriptPubKeyIn;
+}
+
+uint256 CTxOut::GetHash() const
+{
+ return SerializeHash(*this);
+}
+
+std::string CTxOut::ToString() const
+{
+ if (scriptPubKey.size() < 6)
+ return "CTxOut(error)";
+ return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str());
+}
+
+void CTxOut::print() const
+{
+ printf("%s\n", ToString().c_str());
+}
+
+uint256 CTransaction::GetHash() const
+{
+ return SerializeHash(*this);
+}
+
+bool CTransaction::IsNewerThan(const CTransaction& old) const
+{
+ if (vin.size() != old.vin.size())
+ return false;
+ for (unsigned int i = 0; i < vin.size(); i++)
+ if (vin[i].prevout != old.vin[i].prevout)
+ return false;
+
+ bool fNewer = false;
+ unsigned int nLowest = std::numeric_limits<unsigned int>::max();
+ for (unsigned int i = 0; i < vin.size(); i++)
+ {
+ if (vin[i].nSequence != old.vin[i].nSequence)
+ {
+ if (vin[i].nSequence <= nLowest)
+ {
+ fNewer = false;
+ nLowest = vin[i].nSequence;
+ }
+ if (old.vin[i].nSequence < nLowest)
+ {
+ fNewer = true;
+ nLowest = old.vin[i].nSequence;
+ }
+ }
+ }
+ return fNewer;
+}
+
+std::string CTransaction::ToString() const
+{
+ std::string str;
+ str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n",
+ GetHash().ToString().substr(0,10).c_str(),
+ nVersion,
+ vin.size(),
+ vout.size(),
+ nLockTime);
+ for (unsigned int i = 0; i < vin.size(); i++)
+ str += " " + vin[i].ToString() + "\n";
+ for (unsigned int i = 0; i < vout.size(); i++)
+ str += " " + vout[i].ToString() + "\n";
+ return str;
+}
+
+void CTransaction::print() const
+{
+ printf("%s", ToString().c_str());
+}
+
+// Amount compression:
+// * If the amount is 0, output 0
+// * first, divide the amount (in base units) by the largest power of 10 possible; call the exponent e (e is max 9)
+// * if e<9, the last digit of the resulting number cannot be 0; store it as d, and drop it (divide by 10)
+// * call the result n
+// * output 1 + 10*(9*n + d - 1) + e
+// * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9
+// (this is decodable, as d is in [1-9] and e is in [0-9])
+
+uint64 CTxOutCompressor::CompressAmount(uint64 n)
+{
+ if (n == 0)
+ return 0;
+ int e = 0;
+ while (((n % 10) == 0) && e < 9) {
+ n /= 10;
+ e++;
+ }
+ if (e < 9) {
+ int d = (n % 10);
+ assert(d >= 1 && d <= 9);
+ n /= 10;
+ return 1 + (n*9 + d - 1)*10 + e;
+ } else {
+ return 1 + (n - 1)*10 + 9;
+ }
+}
+
+uint64 CTxOutCompressor::DecompressAmount(uint64 x)
+{
+ // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9
+ if (x == 0)
+ return 0;
+ x--;
+ // x = 10*(9*n + d - 1) + e
+ int e = x % 10;
+ x /= 10;
+ uint64 n = 0;
+ if (e < 9) {
+ // x = 9*n + d - 1
+ int d = (x % 9) + 1;
+ x /= 9;
+ // x = n
+ n = x*10 + d;
+ } else {
+ n = x+1;
+ }
+ while (e) {
+ n *= 10;
+ e--;
+ }
+ return n;
+}
+
+// calculate number of bytes for the bitmask, and its number of non-zero bytes
+// each bit in the bitmask represents the availability of one output, but the
+// availabilities of the first two outputs are encoded separately
+void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const {
+ unsigned int nLastUsedByte = 0;
+ for (unsigned int b = 0; 2+b*8 < vout.size(); b++) {
+ bool fZero = true;
+ for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) {
+ if (!vout[2+b*8+i].IsNull()) {
+ fZero = false;
+ continue;
+ }
+ }
+ if (!fZero) {
+ nLastUsedByte = b + 1;
+ nNonzeroBytes++;
+ }
+ }
+ nBytes += nLastUsedByte;
+}
+
+bool CCoins::Spend(const COutPoint &out, CTxInUndo &undo) {
+ if (out.n >= vout.size())
+ return false;
+ if (vout[out.n].IsNull())
+ return false;
+ undo = CTxInUndo(vout[out.n]);
+ vout[out.n].SetNull();
+ Cleanup();
+ if (vout.size() == 0) {
+ undo.nHeight = nHeight;
+ undo.fCoinBase = fCoinBase;
+ undo.nVersion = this->nVersion;
+ }
+ return true;
+}
+
+bool CCoins::Spend(int nPos) {
+ CTxInUndo undo;
+ COutPoint out(0, nPos);
+ return Spend(out, undo);
+}
+
+uint256 CBlockHeader::GetHash() const
+{
+ return Hash(BEGIN(nVersion), END(nNonce));
+}
+
+uint256 CBlock::BuildMerkleTree() const
+{
+ vMerkleTree.clear();
+ BOOST_FOREACH(const CTransaction& tx, vtx)
+ vMerkleTree.push_back(tx.GetHash());
+ int j = 0;
+ for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
+ {
+ for (int i = 0; i < nSize; i += 2)
+ {
+ int i2 = std::min(i+1, nSize-1);
+ vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]),
+ BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2])));
+ }
+ j += nSize;
+ }
+ return (vMerkleTree.empty() ? 0 : vMerkleTree.back());
+}
+
+std::vector<uint256> CBlock::GetMerkleBranch(int nIndex) const
+{
+ if (vMerkleTree.empty())
+ BuildMerkleTree();
+ std::vector<uint256> vMerkleBranch;
+ int j = 0;
+ for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
+ {
+ int i = std::min(nIndex^1, nSize-1);
+ vMerkleBranch.push_back(vMerkleTree[j+i]);
+ nIndex >>= 1;
+ j += nSize;
+ }
+ return vMerkleBranch;
+}
+
+uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
+{
+ if (nIndex == -1)
+ return 0;
+ BOOST_FOREACH(const uint256& otherside, vMerkleBranch)
+ {
+ if (nIndex & 1)
+ hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash));
+ else
+ hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside));
+ nIndex >>= 1;
+ }
+ return hash;
+}
+
+void CBlock::print() const
+{
+ printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n",
+ GetHash().ToString().c_str(),
+ nVersion,
+ hashPrevBlock.ToString().c_str(),
+ hashMerkleRoot.ToString().c_str(),
+ nTime, nBits, nNonce,
+ vtx.size());
+ for (unsigned int i = 0; i < vtx.size(); i++)
+ {
+ printf(" ");
+ vtx[i].print();
+ }
+ printf(" vMerkleTree: ");
+ for (unsigned int i = 0; i < vMerkleTree.size(); i++)
+ printf("%s ", vMerkleTree[i].ToString().c_str());
+ printf("\n");
+}
diff --git a/src/core.h b/src/core.h
index c568fd2ef9..1b9d4dd765 100644
--- a/src/core.h
+++ b/src/core.h
@@ -7,7 +7,6 @@
#include "uint256.h"
#include "serialize.h"
-#include "util.h"
#include "script.h"
#include <stdio.h>
@@ -42,15 +41,8 @@ public:
return !(a == b);
}
- std::string ToString() const
- {
- return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10).c_str(), n);
- }
-
- void print() const
- {
- printf("%s\n", ToString().c_str());
- }
+ std::string ToString() const;
+ void print() const;
};
/** An inpoint - a combination of a transaction and an index n into its vin */
@@ -82,19 +74,8 @@ public:
nSequence = std::numeric_limits<unsigned int>::max();
}
- explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max())
- {
- prevout = prevoutIn;
- scriptSig = scriptSigIn;
- nSequence = nSequenceIn;
- }
-
- CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max())
- {
- prevout = COutPoint(hashPrevTx, nOut);
- scriptSig = scriptSigIn;
- nSequence = nSequenceIn;
- }
+ explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max());
+ CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max());
IMPLEMENT_SERIALIZE
(
@@ -120,25 +101,8 @@ public:
return !(a == b);
}
- std::string ToString() const
- {
- std::string str;
- str += "CTxIn(";
- str += prevout.ToString();
- if (prevout.IsNull())
- str += strprintf(", coinbase %s", HexStr(scriptSig).c_str());
- else
- str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str());
- if (nSequence != std::numeric_limits<unsigned int>::max())
- str += strprintf(", nSequence=%u", nSequence);
- str += ")";
- return str;
- }
-
- void print() const
- {
- printf("%s\n", ToString().c_str());
- }
+ std::string ToString() const;
+ void print() const;
};
@@ -158,11 +122,7 @@ public:
SetNull();
}
- CTxOut(int64 nValueIn, CScript scriptPubKeyIn)
- {
- nValue = nValueIn;
- scriptPubKey = scriptPubKeyIn;
- }
+ CTxOut(int64 nValueIn, CScript scriptPubKeyIn);
IMPLEMENT_SERIALIZE
(
@@ -181,10 +141,7 @@ public:
return (nValue == -1);
}
- uint256 GetHash() const
- {
- return SerializeHash(*this);
- }
+ uint256 GetHash() const;
bool IsDust(int64 nMinRelayTxFee) const
{
@@ -210,17 +167,8 @@ public:
return !(a == b);
}
- std::string ToString() const
- {
- if (scriptPubKey.size() < 6)
- return "CTxOut(error)";
- return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str());
- }
-
- void print() const
- {
- printf("%s\n", ToString().c_str());
- }
+ std::string ToString() const;
+ void print() const;
};
@@ -265,39 +213,8 @@ public:
return (vin.empty() && vout.empty());
}
- uint256 GetHash() const
- {
- return SerializeHash(*this);
- }
-
- bool IsNewerThan(const CTransaction& old) const
- {
- if (vin.size() != old.vin.size())
- return false;
- for (unsigned int i = 0; i < vin.size(); i++)
- if (vin[i].prevout != old.vin[i].prevout)
- return false;
-
- bool fNewer = false;
- unsigned int nLowest = std::numeric_limits<unsigned int>::max();
- for (unsigned int i = 0; i < vin.size(); i++)
- {
- if (vin[i].nSequence != old.vin[i].nSequence)
- {
- if (vin[i].nSequence <= nLowest)
- {
- fNewer = false;
- nLowest = vin[i].nSequence;
- }
- if (old.vin[i].nSequence < nLowest)
- {
- fNewer = true;
- nLowest = old.vin[i].nSequence;
- }
- }
- }
- return fNewer;
- }
+ uint256 GetHash() const;
+ bool IsNewerThan(const CTransaction& old) const;
bool IsCoinBase() const
{
@@ -318,26 +235,8 @@ public:
}
- std::string ToString() const
- {
- std::string str;
- str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n",
- GetHash().ToString().substr(0,10).c_str(),
- nVersion,
- vin.size(),
- vout.size(),
- nLockTime);
- for (unsigned int i = 0; i < vin.size(); i++)
- str += " " + vin[i].ToString() + "\n";
- for (unsigned int i = 0; i < vout.size(); i++)
- str += " " + vout[i].ToString() + "\n";
- return str;
- }
-
- void print() const
- {
- printf("%s", ToString().c_str());
- }
+ std::string ToString() const;
+ void print() const;
};
/** wrapper for CTxOut that provides a more compact serialization */
@@ -521,26 +420,7 @@ public:
return !(a == b);
}
- // calculate number of bytes for the bitmask, and its number of non-zero bytes
- // each bit in the bitmask represents the availability of one output, but the
- // availabilities of the first two outputs are encoded separately
- void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const {
- unsigned int nLastUsedByte = 0;
- for (unsigned int b = 0; 2+b*8 < vout.size(); b++) {
- bool fZero = true;
- for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) {
- if (!vout[2+b*8+i].IsNull()) {
- fZero = false;
- continue;
- }
- }
- if (!fZero) {
- nLastUsedByte = b + 1;
- nNonzeroBytes++;
- }
- }
- nBytes += nLastUsedByte;
- }
+ void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const;
bool IsCoinBase() const {
return fCoinBase;
@@ -633,28 +513,10 @@ public:
}
// mark an outpoint spent, and construct undo information
- bool Spend(const COutPoint &out, CTxInUndo &undo) {
- if (out.n >= vout.size())
- return false;
- if (vout[out.n].IsNull())
- return false;
- undo = CTxInUndo(vout[out.n]);
- vout[out.n].SetNull();
- Cleanup();
- if (vout.size() == 0) {
- undo.nHeight = nHeight;
- undo.fCoinBase = fCoinBase;
- undo.nVersion = this->nVersion;
- }
- return true;
- }
+ bool Spend(const COutPoint &out, CTxInUndo &undo);
// mark a vout spent
- bool Spend(int nPos) {
- CTxInUndo undo;
- COutPoint out(0, nPos);
- return Spend(out, undo);
- }
+ bool Spend(int nPos);
// check whether a particular output is still available
bool IsAvailable(unsigned int nPos) const {
@@ -722,10 +584,7 @@ public:
return (nBits == 0);
}
- uint256 GetHash() const
- {
- return Hash(BEGIN(nVersion), END(nNonce));
- }
+ uint256 GetHash() const;
int64 GetBlockTime() const
{
@@ -733,4 +592,63 @@ public:
}
};
+
+class CBlock : public CBlockHeader
+{
+public:
+ // network and disk
+ std::vector<CTransaction> vtx;
+
+ // memory only
+ mutable std::vector<uint256> vMerkleTree;
+
+ CBlock()
+ {
+ SetNull();
+ }
+
+ CBlock(const CBlockHeader &header)
+ {
+ SetNull();
+ *((CBlockHeader*)this) = header;
+ }
+
+ IMPLEMENT_SERIALIZE
+ (
+ READWRITE(*(CBlockHeader*)this);
+ READWRITE(vtx);
+ )
+
+ void SetNull()
+ {
+ CBlockHeader::SetNull();
+ vtx.clear();
+ vMerkleTree.clear();
+ }
+
+ CBlockHeader GetBlockHeader() const
+ {
+ CBlockHeader block;
+ block.nVersion = nVersion;
+ block.hashPrevBlock = hashPrevBlock;
+ block.hashMerkleRoot = hashMerkleRoot;
+ block.nTime = nTime;
+ block.nBits = nBits;
+ block.nNonce = nNonce;
+ return block;
+ }
+
+ uint256 BuildMerkleTree() const;
+
+ const uint256 &GetTxHash(unsigned int nIndex) const {
+ assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first
+ assert(nIndex < vtx.size());
+ return vMerkleTree[nIndex];
+ }
+
+ std::vector<uint256> GetMerkleBranch(int nIndex) const;
+ static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex);
+ void print() const;
+};
+
#endif
diff --git a/src/crypter.cpp b/src/crypter.cpp
index 32baabd674..754de536a9 100644
--- a/src/crypter.cpp
+++ b/src/crypter.cpp
@@ -6,9 +6,6 @@
#include <openssl/evp.h>
#include <vector>
#include <string>
-#ifdef WIN32
-#include <windows.h>
-#endif
#include "crypter.h"
diff --git a/src/db.cpp b/src/db.cpp
index 93f3f5d8c4..03f46f3c26 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -8,7 +8,6 @@
#include "util.h"
#include "hash.h"
#include "addrman.h"
-#include <boost/version.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <openssl/rand.h>
diff --git a/src/init.cpp b/src/init.cpp
index 7b98253ead..09871c012c 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -12,6 +12,7 @@
#include "bitcoinrpc.h"
#include "net.h"
#include "util.h"
+#include "miner.h"
#include "ui_interface.h"
#include "checkpoints.h"
@@ -29,6 +30,7 @@
using namespace std;
using namespace boost;
+std::string strWalletFile;
CWallet* pwalletMain;
CClientUIInterface uiInterface;
@@ -119,7 +121,7 @@ void Shutdown()
}
bitdb.Flush(true);
boost::filesystem::remove(GetPidFile());
- UnregisterWallet(pwalletMain);
+ UnregisterAllWallets();
delete pwalletMain;
}
@@ -169,6 +171,7 @@ std::string HelpMessage()
strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n";
strUsage += " -gen " + _("Generate coins (default: 0)") + "\n";
strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n";
+ strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n";
strUsage += " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n";
strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n";
strUsage += " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n";
@@ -225,7 +228,7 @@ std::string HelpMessage()
strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n";
strUsage += " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n";
strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n";
- strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n";
+ strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n";
strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n";
strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n";
strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n";
@@ -237,12 +240,12 @@ std::string HelpMessage()
strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n";
strUsage += " -par=<n> " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n";
- strUsage += "\n"; _("Block creation options:") + "\n";
+ strUsage += "\n" + _("Block creation options:") + "\n";
strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n";
strUsage += " -blockmaxsize=<n> " + _("Set maximum block size in bytes (default: 250000)") + "\n";
strUsage += " -blockprioritysize=<n> " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n";
- strUsage += "\n"; _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n";
+ strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n";
strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n";
strUsage += " -rpcsslcertificatechainfile=<file.cert> " + _("Server certificate file (default: server.cert)") + "\n";
strUsage += " -rpcsslprivatekeyfile=<file.pem> " + _("Server private key (default: server.pem)") + "\n";
@@ -493,10 +496,16 @@ bool AppInit2(boost::thread_group& threadGroup)
InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction."));
}
+ strWalletFile = GetArg("-wallet", "wallet.dat");
+
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
std::string strDataDir = GetDataDir().string();
+ // Wallet file must be a plain filename without a directory
+ if (strWalletFile != boost::filesystem::basename(strWalletFile) + boost::filesystem::extension(strWalletFile))
+ return InitError(strprintf(_("Wallet %s resides outside data directory %s"), strWalletFile.c_str(), strDataDir.c_str()));
+
// Make sure only a single Bitcoin process is using the data directory.
boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist.
@@ -555,13 +564,13 @@ bool AppInit2(boost::thread_group& threadGroup)
if (GetBoolArg("-salvagewallet", false))
{
// Recover readable keypairs:
- if (!CWalletDB::Recover(bitdb, "wallet.dat", true))
+ if (!CWalletDB::Recover(bitdb, strWalletFile, true))
return false;
}
- if (filesystem::exists(GetDataDir() / "wallet.dat"))
+ if (filesystem::exists(GetDataDir() / strWalletFile))
{
- CDBEnv::VerifyResult r = bitdb.Verify("wallet.dat", CWalletDB::Recover);
+ CDBEnv::VerifyResult r = bitdb.Verify(strWalletFile, CWalletDB::Recover);
if (r == CDBEnv::RECOVER_OK)
{
string msg = strprintf(_("Warning: wallet.dat corrupt, data salvaged!"
@@ -749,18 +758,18 @@ bool AppInit2(boost::thread_group& threadGroup)
if (!mapBlockIndex.empty() && pindexGenesisBlock == NULL)
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
- // Check for changed -txindex state (only necessary if we are not reindexing anyway)
- if (!fReindex && fTxIndex != GetBoolArg("-txindex", false)) {
- strLoadError = _("You need to rebuild the database using -reindex to change -txindex");
- break;
- }
-
// Initialize the block index (no-op if non-empty database was already loaded)
if (!InitBlockIndex()) {
strLoadError = _("Error initializing block database");
break;
}
+ // Check for changed -txindex state
+ if (fTxIndex != GetBoolArg("-txindex", false)) {
+ strLoadError = _("You need to rebuild the database using -reindex to change -txindex");
+ break;
+ }
+
uiInterface.InitMessage(_("Verifying blocks..."));
if (!VerifyDB(GetArg("-checklevel", 3),
GetArg( "-checkblocks", 288))) {
@@ -785,6 +794,7 @@ bool AppInit2(boost::thread_group& threadGroup)
fReindex = true;
fRequestShutdown = false;
} else {
+ printf("Aborted block database rebuild. Exiting.\n");
return false;
}
} else {
@@ -820,7 +830,7 @@ bool AppInit2(boost::thread_group& threadGroup)
{
CBlockIndex* pindex = (*mi).second;
CBlock block;
- block.ReadFromDisk(pindex);
+ ReadBlockFromDisk(block, pindex);
block.BuildMerkleTree();
block.print();
printf("\n");
@@ -838,7 +848,7 @@ bool AppInit2(boost::thread_group& threadGroup)
nStart = GetTimeMillis();
bool fFirstRun = true;
- pwalletMain = new CWallet("wallet.dat");
+ pwalletMain = new CWallet(strWalletFile);
DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
@@ -903,7 +913,7 @@ bool AppInit2(boost::thread_group& threadGroup)
pindexRescan = pindexGenesisBlock;
else
{
- CWalletDB walletdb("wallet.dat");
+ CWalletDB walletdb(strWalletFile);
CBlockLocator locator;
if (walletdb.ReadBestBlock(locator))
pindexRescan = locator.GetBlockIndex();
diff --git a/src/init.h b/src/init.h
index 5927670c83..a4d5a67252 100644
--- a/src/init.h
+++ b/src/init.h
@@ -7,6 +7,7 @@
#include "wallet.h"
+extern std::string strWalletFile;
extern CWallet* pwalletMain;
void StartShutdown();
diff --git a/src/key.cpp b/src/key.cpp
index 7092b0e0c8..5d7ea93a0f 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -156,7 +156,8 @@ public:
BN_clear_free(&bn);
}
- void GetPrivKey(CPrivKey &privkey) {
+ void GetPrivKey(CPrivKey &privkey, bool fCompressed) {
+ EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
int nSize = i2d_ECPrivateKey(pkey, NULL);
assert(nSize);
privkey.resize(nSize);
@@ -356,7 +357,7 @@ CPrivKey CKey::GetPrivKey() const {
CECKey key;
key.SetSecretBytes(vch);
CPrivKey privkey;
- key.GetPrivKey(privkey);
+ key.GetPrivKey(privkey, fCompressed);
return privkey;
}
diff --git a/src/leveldb/AUTHORS b/src/leveldb/AUTHORS
index 27a9407e52..fc40194ab9 100644
--- a/src/leveldb/AUTHORS
+++ b/src/leveldb/AUTHORS
@@ -6,3 +6,6 @@ Google Inc.
# Initial version authors:
Jeffrey Dean <jeff@google.com>
Sanjay Ghemawat <sanjay@google.com>
+
+# Partial list of contributors:
+Kevin Regan <kevin.d.regan@gmail.com>
diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile
index 42c4952fec..38b9bf7729 100644
--- a/src/leveldb/Makefile
+++ b/src/leveldb/Makefile
@@ -42,6 +42,7 @@ TESTS = \
env_test \
filename_test \
filter_block_test \
+ issue178_test \
log_test \
memenv_test \
skiplist_test \
@@ -69,7 +70,7 @@ SHARED = $(SHARED1)
else
# Update db.h if you change these.
SHARED_MAJOR = 1
-SHARED_MINOR = 9
+SHARED_MINOR = 12
SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT)
SHARED2 = $(SHARED1).$(SHARED_MAJOR)
SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR)
@@ -146,6 +147,9 @@ filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
$(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS)
+ $(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
+
log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
$(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)
diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc
index c9de169f29..af02467b33 100644
--- a/src/leveldb/db/db_impl.cc
+++ b/src/leveldb/db/db_impl.cc
@@ -35,6 +35,8 @@
namespace leveldb {
+const int kNumNonTableCacheFiles = 10;
+
// Information kept for every waiting writer
struct DBImpl::Writer {
Status status;
@@ -92,9 +94,9 @@ Options SanitizeOptions(const std::string& dbname,
Options result = src;
result.comparator = icmp;
result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL;
- ClipToRange(&result.max_open_files, 20, 50000);
- ClipToRange(&result.write_buffer_size, 64<<10, 1<<30);
- ClipToRange(&result.block_size, 1<<10, 4<<20);
+ ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000);
+ ClipToRange(&result.write_buffer_size, 64<<10, 1<<30);
+ ClipToRange(&result.block_size, 1<<10, 4<<20);
if (result.info_log == NULL) {
// Open a log file in the same directory as the db
src.env->CreateDir(dbname); // In case it does not exist
@@ -130,12 +132,13 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname)
log_(NULL),
tmp_batch_(new WriteBatch),
bg_compaction_scheduled_(false),
- manual_compaction_(NULL) {
+ manual_compaction_(NULL),
+ consecutive_compaction_errors_(0) {
mem_->Ref();
has_imm_.Release_Store(NULL);
// Reserve ten files or so for other uses and give the rest to TableCache.
- const int table_cache_size = options.max_open_files - 10;
+ const int table_cache_size = options.max_open_files - kNumNonTableCacheFiles;
table_cache_ = new TableCache(dbname_, &options_, table_cache_size);
versions_ = new VersionSet(dbname_, &options_, table_cache_,
@@ -310,16 +313,24 @@ Status DBImpl::Recover(VersionEdit* edit) {
if (!s.ok()) {
return s;
}
+ std::set<uint64_t> expected;
+ versions_->AddLiveFiles(&expected);
uint64_t number;
FileType type;
std::vector<uint64_t> logs;
for (size_t i = 0; i < filenames.size(); i++) {
- if (ParseFileName(filenames[i], &number, &type)
- && type == kLogFile
- && ((number >= min_log) || (number == prev_log))) {
+ if (ParseFileName(filenames[i], &number, &type)) {
+ expected.erase(number);
+ if (type == kLogFile && ((number >= min_log) || (number == prev_log)))
logs.push_back(number);
}
}
+ if (!expected.empty()) {
+ char buf[50];
+ snprintf(buf, sizeof(buf), "%d missing files; e.g.",
+ static_cast<int>(expected.size()));
+ return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin())));
+ }
// Recover in the order in which the logs were generated
std::sort(logs.begin(), logs.end());
@@ -611,6 +622,7 @@ void DBImpl::BackgroundCall() {
Status s = BackgroundCompaction();
if (s.ok()) {
// Success
+ consecutive_compaction_errors_ = 0;
} else if (shutting_down_.Acquire_Load()) {
// Error most likely due to shutdown; do not wait
} else {
@@ -622,7 +634,12 @@ void DBImpl::BackgroundCall() {
Log(options_.info_log, "Waiting after background compaction error: %s",
s.ToString().c_str());
mutex_.Unlock();
- env_->SleepForMicroseconds(1000000);
+ ++consecutive_compaction_errors_;
+ int seconds_to_sleep = 1;
+ for (int i = 0; i < 3 && i < consecutive_compaction_errors_ - 1; ++i) {
+ seconds_to_sleep *= 2;
+ }
+ env_->SleepForMicroseconds(seconds_to_sleep * 1000000);
mutex_.Lock();
}
}
@@ -805,6 +822,9 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact,
(unsigned long long) output_number,
(unsigned long long) current_entries,
(unsigned long long) current_bytes);
+
+ // rate-limit compaction file creation with a 100ms pause
+ env_->SleepForMicroseconds(100000);
}
}
return s;
@@ -1268,10 +1288,11 @@ Status DBImpl::MakeRoomForWrite(bool force) {
} else if (imm_ != NULL) {
// We have filled up the current memtable, but the previous
// one is still being compacted, so we wait.
+ Log(options_.info_log, "Current memtable full; waiting...\n");
bg_cv_.Wait();
} else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) {
// There are too many level-0 files.
- Log(options_.info_log, "waiting...\n");
+ Log(options_.info_log, "Too many L0 files; waiting...\n");
bg_cv_.Wait();
} else {
// Attempt to switch to a new memtable and trigger compaction of old
diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h
index bd29dd8055..3c8d711ae0 100644
--- a/src/leveldb/db/db_impl.h
+++ b/src/leveldb/db/db_impl.h
@@ -163,6 +163,7 @@ class DBImpl : public DB {
// Have we encountered a background error in paranoid mode?
Status bg_error_;
+ int consecutive_compaction_errors_;
// Per level compaction stats. stats_[level] stores the stats for
// compactions that produced data for the specified "level".
diff --git a/src/leveldb/db/db_test.cc b/src/leveldb/db/db_test.cc
index 684ea3bdbc..49aae04dbd 100644
--- a/src/leveldb/db/db_test.cc
+++ b/src/leveldb/db/db_test.cc
@@ -33,8 +33,11 @@ class AtomicCounter {
public:
AtomicCounter() : count_(0) { }
void Increment() {
+ IncrementBy(1);
+ }
+ void IncrementBy(int count) {
MutexLock l(&mu_);
- count_++;
+ count_ += count;
}
int Read() {
MutexLock l(&mu_);
@@ -45,6 +48,10 @@ class AtomicCounter {
count_ = 0;
}
};
+
+void DelayMilliseconds(int millis) {
+ Env::Default()->SleepForMicroseconds(millis * 1000);
+}
}
// Special Env used to delay background operations
@@ -69,6 +76,7 @@ class SpecialEnv : public EnvWrapper {
AtomicCounter random_read_counter_;
AtomicCounter sleep_counter_;
+ AtomicCounter sleep_time_counter_;
explicit SpecialEnv(Env* base) : EnvWrapper(base) {
delay_sstable_sync_.Release_Store(NULL);
@@ -103,7 +111,7 @@ class SpecialEnv : public EnvWrapper {
Status Flush() { return base_->Flush(); }
Status Sync() {
while (env_->delay_sstable_sync_.Acquire_Load() != NULL) {
- env_->SleepForMicroseconds(100000);
+ DelayMilliseconds(100);
}
return base_->Sync();
}
@@ -174,8 +182,9 @@ class SpecialEnv : public EnvWrapper {
virtual void SleepForMicroseconds(int micros) {
sleep_counter_.Increment();
- target()->SleepForMicroseconds(micros);
+ sleep_time_counter_.IncrementBy(micros);
}
+
};
class DBTest {
@@ -461,6 +470,20 @@ class DBTest {
}
return result;
}
+
+ bool DeleteAnSSTFile() {
+ std::vector<std::string> filenames;
+ ASSERT_OK(env_->GetChildren(dbname_, &filenames));
+ uint64_t number;
+ FileType type;
+ for (size_t i = 0; i < filenames.size(); i++) {
+ if (ParseFileName(filenames[i], &number, &type) && type == kTableFile) {
+ ASSERT_OK(env_->DeleteFile(TableFileName(dbname_, number)));
+ return true;
+ }
+ }
+ return false;
+ }
};
TEST(DBTest, Empty) {
@@ -611,7 +634,7 @@ TEST(DBTest, GetEncountersEmptyLevel) {
}
// Step 4: Wait for compaction to finish
- env_->SleepForMicroseconds(1000000);
+ DelayMilliseconds(1000);
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
} while (ChangeOptions());
@@ -1295,7 +1318,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_a) {
Reopen();
Reopen();
ASSERT_EQ("(a->v)", Contents());
- env_->SleepForMicroseconds(1000000); // Wait for compaction to finish
+ DelayMilliseconds(1000); // Wait for compaction to finish
ASSERT_EQ("(a->v)", Contents());
}
@@ -1311,7 +1334,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_b) {
Put("","");
Reopen();
Put("","");
- env_->SleepForMicroseconds(1000000); // Wait for compaction to finish
+ DelayMilliseconds(1000); // Wait for compaction to finish
Reopen();
Put("d","dv");
Reopen();
@@ -1321,7 +1344,7 @@ TEST(DBTest, L0_CompactionBug_Issue44_b) {
Delete("b");
Reopen();
ASSERT_EQ("(->)(c->cv)", Contents());
- env_->SleepForMicroseconds(1000000); // Wait for compaction to finish
+ DelayMilliseconds(1000); // Wait for compaction to finish
ASSERT_EQ("(->)(c->cv)", Contents());
}
@@ -1506,6 +1529,30 @@ TEST(DBTest, NoSpace) {
ASSERT_GE(env_->sleep_counter_.Read(), 5);
}
+TEST(DBTest, ExponentialBackoff) {
+ Options options = CurrentOptions();
+ options.env = env_;
+ Reopen(&options);
+
+ ASSERT_OK(Put("foo", "v1"));
+ ASSERT_EQ("v1", Get("foo"));
+ Compact("a", "z");
+ env_->non_writable_.Release_Store(env_); // Force errors for new files
+ env_->sleep_counter_.Reset();
+ env_->sleep_time_counter_.Reset();
+ for (int i = 0; i < 5; i++) {
+ dbfull()->TEST_CompactRange(2, NULL, NULL);
+ }
+ env_->non_writable_.Release_Store(NULL);
+
+ // Wait for compaction to finish
+ DelayMilliseconds(1000);
+
+ ASSERT_GE(env_->sleep_counter_.Read(), 5);
+ ASSERT_LT(env_->sleep_counter_.Read(), 10);
+ ASSERT_GE(env_->sleep_time_counter_.Read(), 10e6);
+}
+
TEST(DBTest, NonWritableFileSystem) {
Options options = CurrentOptions();
options.write_buffer_size = 1000;
@@ -1519,7 +1566,7 @@ TEST(DBTest, NonWritableFileSystem) {
fprintf(stderr, "iter %d; errors %d\n", i, errors);
if (!Put("foo", big).ok()) {
errors++;
- env_->SleepForMicroseconds(100000);
+ DelayMilliseconds(100);
}
}
ASSERT_GT(errors, 0);
@@ -1567,6 +1614,24 @@ TEST(DBTest, ManifestWriteError) {
}
}
+TEST(DBTest, MissingSSTFile) {
+ ASSERT_OK(Put("foo", "bar"));
+ ASSERT_EQ("bar", Get("foo"));
+
+ // Dump the memtable to disk.
+ dbfull()->TEST_CompactMemTable();
+ ASSERT_EQ("bar", Get("foo"));
+
+ Close();
+ ASSERT_TRUE(DeleteAnSSTFile());
+ Options options = CurrentOptions();
+ options.paranoid_checks = true;
+ Status s = TryReopen(&options);
+ ASSERT_TRUE(!s.ok());
+ ASSERT_TRUE(s.ToString().find("issing") != std::string::npos)
+ << s.ToString();
+}
+
TEST(DBTest, FilesDeletedAfterCompaction) {
ASSERT_OK(Put("foo", "v2"));
Compact("a", "z");
@@ -1711,13 +1776,13 @@ TEST(DBTest, MultiThreaded) {
}
// Let them run for a while
- env_->SleepForMicroseconds(kTestSeconds * 1000000);
+ DelayMilliseconds(kTestSeconds * 1000);
// Stop the threads and wait for them to finish
mt.stop.Release_Store(&mt);
for (int id = 0; id < kNumThreads; id++) {
while (mt.thread_done[id].Acquire_Load() == NULL) {
- env_->SleepForMicroseconds(100000);
+ DelayMilliseconds(100);
}
}
} while (ChangeOptions());
diff --git a/src/leveldb/db/dbformat.cc b/src/leveldb/db/dbformat.cc
index 28e11b398d..20a7ca4462 100644
--- a/src/leveldb/db/dbformat.cc
+++ b/src/leveldb/db/dbformat.cc
@@ -26,7 +26,7 @@ std::string ParsedInternalKey::DebugString() const {
(unsigned long long) sequence,
int(type));
std::string result = "'";
- result += user_key.ToString();
+ result += EscapeString(user_key.ToString());
result += buf;
return result;
}
diff --git a/src/leveldb/db/filename_test.cc b/src/leveldb/db/filename_test.cc
index 47353d6c9a..5a26da4728 100644
--- a/src/leveldb/db/filename_test.cc
+++ b/src/leveldb/db/filename_test.cc
@@ -70,7 +70,7 @@ TEST(FileNameTest, Parse) {
for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) {
std::string f = errors[i];
ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f;
- };
+ }
}
TEST(FileNameTest, Construction) {
diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc
index 7d0a5de2b9..4fd1ddef21 100644
--- a/src/leveldb/db/version_set.cc
+++ b/src/leveldb/db/version_set.cc
@@ -1331,14 +1331,19 @@ Compaction* VersionSet::CompactRange(
}
// Avoid compacting too much in one shot in case the range is large.
- const uint64_t limit = MaxFileSizeForLevel(level);
- uint64_t total = 0;
- for (size_t i = 0; i < inputs.size(); i++) {
- uint64_t s = inputs[i]->file_size;
- total += s;
- if (total >= limit) {
- inputs.resize(i + 1);
- break;
+ // But we cannot do this for level-0 since level-0 files can overlap
+ // and we must not pick one file and drop another older file if the
+ // two files overlap.
+ if (level > 0) {
+ const uint64_t limit = MaxFileSizeForLevel(level);
+ uint64_t total = 0;
+ for (size_t i = 0; i < inputs.size(); i++) {
+ uint64_t s = inputs[i]->file_size;
+ total += s;
+ if (total >= limit) {
+ inputs.resize(i + 1);
+ break;
+ }
}
}
diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h
index 29d3674479..da8b11a8c0 100644
--- a/src/leveldb/include/leveldb/db.h
+++ b/src/leveldb/include/leveldb/db.h
@@ -14,7 +14,7 @@ namespace leveldb {
// Update Makefile if you change these
static const int kMajorVersion = 1;
-static const int kMinorVersion = 9;
+static const int kMinorVersion = 12;
struct Options;
struct ReadOptions;
diff --git a/src/leveldb/port/port_win.cc b/src/leveldb/port/port_win.cc
index 99c1d8e346..1b0f060a19 100644
--- a/src/leveldb/port/port_win.cc
+++ b/src/leveldb/port/port_win.cc
@@ -109,12 +109,10 @@ void CondVar::Signal() {
void CondVar::SignalAll() {
wait_mtx_.Lock();
- for(long i = 0; i < waiting_; ++i) {
- ::ReleaseSemaphore(sem1_, 1, NULL);
- while(waiting_ > 0) {
- --waiting_;
- ::WaitForSingleObject(sem2_, INFINITE);
- }
+ ::ReleaseSemaphore(sem1_, waiting_, NULL);
+ while(waiting_ > 0) {
+ --waiting_;
+ ::WaitForSingleObject(sem2_, INFINITE);
}
wait_mtx_.Unlock();
}
diff --git a/src/leveldb/table/block.cc b/src/leveldb/table/block.cc
index ab83c1112c..79ea9d9ee5 100644
--- a/src/leveldb/table/block.cc
+++ b/src/leveldb/table/block.cc
@@ -16,7 +16,7 @@
namespace leveldb {
inline uint32_t Block::NumRestarts() const {
- assert(size_ >= 2*sizeof(uint32_t));
+ assert(size_ >= sizeof(uint32_t));
return DecodeFixed32(data_ + size_ - sizeof(uint32_t));
}
@@ -27,11 +27,12 @@ Block::Block(const BlockContents& contents)
if (size_ < sizeof(uint32_t)) {
size_ = 0; // Error marker
} else {
- restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t);
- if (restart_offset_ > size_ - sizeof(uint32_t)) {
- // The size is too small for NumRestarts() and therefore
- // restart_offset_ wrapped around.
+ size_t max_restarts_allowed = (size_-sizeof(uint32_t)) / sizeof(uint32_t);
+ if (NumRestarts() > max_restarts_allowed) {
+ // The size is too small for NumRestarts()
size_ = 0;
+ } else {
+ restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t);
}
}
}
@@ -253,7 +254,7 @@ class Block::Iter : public Iterator {
};
Iterator* Block::NewIterator(const Comparator* cmp) {
- if (size_ < 2*sizeof(uint32_t)) {
+ if (size_ < sizeof(uint32_t)) {
return NewErrorIterator(Status::Corruption("bad block contents"));
}
const uint32_t num_restarts = NumRestarts();
diff --git a/src/leveldb/table/table.cc b/src/leveldb/table/table.cc
index dbd6d3a1bf..71c1756e5f 100644
--- a/src/leveldb/table/table.cc
+++ b/src/leveldb/table/table.cc
@@ -228,7 +228,6 @@ Status Table::InternalGet(const ReadOptions& options, const Slice& k,
!filter->KeyMayMatch(handle.offset(), k)) {
// Not found
} else {
- Slice handle = iiter->value();
Iterator* block_iter = BlockReader(this, options, iiter->value());
block_iter->Seek(k);
if (block_iter->Valid()) {
diff --git a/src/leveldb/table/table_test.cc b/src/leveldb/table/table_test.cc
index 57cea25334..c723bf84cf 100644
--- a/src/leveldb/table/table_test.cc
+++ b/src/leveldb/table/table_test.cc
@@ -644,6 +644,36 @@ class Harness {
Constructor* constructor_;
};
+// Test empty table/block.
+TEST(Harness, Empty) {
+ for (int i = 0; i < kNumTestArgs; i++) {
+ Init(kTestArgList[i]);
+ Random rnd(test::RandomSeed() + 1);
+ Test(&rnd);
+ }
+}
+
+// Special test for a block with no restart entries. The C++ leveldb
+// code never generates such blocks, but the Java version of leveldb
+// seems to.
+TEST(Harness, ZeroRestartPointsInBlock) {
+ char data[sizeof(uint32_t)];
+ memset(data, 0, sizeof(data));
+ BlockContents contents;
+ contents.data = Slice(data, sizeof(data));
+ contents.cachable = false;
+ contents.heap_allocated = false;
+ Block block(contents);
+ Iterator* iter = block.NewIterator(BytewiseComparator());
+ iter->SeekToFirst();
+ ASSERT_TRUE(!iter->Valid());
+ iter->SeekToLast();
+ ASSERT_TRUE(!iter->Valid());
+ iter->Seek("foo");
+ ASSERT_TRUE(!iter->Valid());
+ delete iter;
+}
+
// Test the empty key
TEST(Harness, SimpleEmptyKey) {
for (int i = 0; i < kNumTestArgs; i++) {
diff --git a/src/leveldb/util/cache.cc b/src/leveldb/util/cache.cc
index 24f1f63f4f..8b197bc02a 100644
--- a/src/leveldb/util/cache.cc
+++ b/src/leveldb/util/cache.cc
@@ -116,7 +116,6 @@ class HandleTable {
LRUHandle* h = list_[i];
while (h != NULL) {
LRUHandle* next = h->next_hash;
- Slice key = h->key();
uint32_t hash = h->hash;
LRUHandle** ptr = &new_list[hash & (new_length - 1)];
h->next_hash = *ptr;
@@ -160,7 +159,6 @@ class LRUCache {
// mutex_ protects the following state.
port::Mutex mutex_;
size_t usage_;
- uint64_t last_id_;
// Dummy head of LRU list.
// lru.prev is newest entry, lru.next is oldest entry.
@@ -170,8 +168,7 @@ class LRUCache {
};
LRUCache::LRUCache()
- : usage_(0),
- last_id_(0) {
+ : usage_(0) {
// Make empty circular linked list
lru_.next = &lru_;
lru_.prev = &lru_;
diff --git a/src/leveldb/util/coding_test.cc b/src/leveldb/util/coding_test.cc
index 2c52b17b60..fb5726e335 100644
--- a/src/leveldb/util/coding_test.cc
+++ b/src/leveldb/util/coding_test.cc
@@ -109,7 +109,7 @@ TEST(Coding, Varint64) {
values.push_back(power);
values.push_back(power-1);
values.push_back(power+1);
- };
+ }
std::string s;
for (int i = 0; i < values.size(); i++) {
diff --git a/src/leveldb/util/comparator.cc b/src/leveldb/util/comparator.cc
index 4b7b5724ef..6cc319242e 100644
--- a/src/leveldb/util/comparator.cc
+++ b/src/leveldb/util/comparator.cc
@@ -66,7 +66,7 @@ class BytewiseComparatorImpl : public Comparator {
};
} // namespace
-static port::OnceType once = LEVELDB_ONCE_INIT;
+static port::OnceType once_comparator = LEVELDB_ONCE_INIT;
static const Comparator* bytewise;
static void InitModule() {
@@ -74,7 +74,7 @@ static void InitModule() {
}
const Comparator* BytewiseComparator() {
- port::InitOnce(&once, InitModule);
+ port::InitOnce(&once_comparator, InitModule);
return bytewise;
}
diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc
index db81f56d11..6badfdc230 100644
--- a/src/leveldb/util/env_posix.cc
+++ b/src/leveldb/util/env_posix.cc
@@ -386,7 +386,7 @@ class PosixEnv : public Env {
PosixEnv();
virtual ~PosixEnv() {
fprintf(stderr, "Destroying Env::Default()\n");
- exit(1);
+ abort();
}
virtual Status NewSequentialFile(const std::string& fname,
@@ -467,7 +467,7 @@ class PosixEnv : public Env {
result = IOError(fname, errno);
}
return result;
- };
+ }
virtual Status CreateDir(const std::string& name) {
Status result;
@@ -475,7 +475,7 @@ class PosixEnv : public Env {
result = IOError(name, errno);
}
return result;
- };
+ }
virtual Status DeleteDir(const std::string& name) {
Status result;
@@ -483,7 +483,7 @@ class PosixEnv : public Env {
result = IOError(name, errno);
}
return result;
- };
+ }
virtual Status GetFileSize(const std::string& fname, uint64_t* size) {
Status s;
@@ -589,7 +589,7 @@ class PosixEnv : public Env {
void PthreadCall(const char* label, int result) {
if (result != 0) {
fprintf(stderr, "pthread %s: %s\n", label, strerror(result));
- exit(1);
+ abort();
}
}
diff --git a/src/leveldb/util/hash.cc b/src/leveldb/util/hash.cc
index ba1818082d..07cf022060 100644
--- a/src/leveldb/util/hash.cc
+++ b/src/leveldb/util/hash.cc
@@ -6,6 +6,13 @@
#include "util/coding.h"
#include "util/hash.h"
+// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
+// between switch labels. The real definition should be provided externally.
+// This one is a fallback version for unsupported compilers.
+#ifndef FALLTHROUGH_INTENDED
+#define FALLTHROUGH_INTENDED do { } while (0)
+#endif
+
namespace leveldb {
uint32_t Hash(const char* data, size_t n, uint32_t seed) {
@@ -28,10 +35,10 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) {
switch (limit - data) {
case 3:
h += data[2] << 16;
- // fall through
+ FALLTHROUGH_INTENDED;
case 2:
h += data[1] << 8;
- // fall through
+ FALLTHROUGH_INTENDED;
case 1:
h += data[0];
h *= m;
diff --git a/src/main.cpp b/src/main.cpp
index 5c9aa32024..e78e055147 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -59,8 +59,8 @@ CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
-map<uint256, CDataStream*> mapOrphanTransactions;
-map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
+map<uint256, CTransaction> mapOrphanTransactions;
+map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
@@ -99,9 +99,16 @@ void UnregisterWallet(CWallet* pwalletIn)
}
}
+void UnregisterAllWallets()
+{
+ LOCK(cs_setpwalletRegistered);
+ setpwalletRegistered.clear();
+}
+
// get the wallet transaction with the given hash (if it exists)
bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx)
{
+ LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
if (pwallet->GetTransaction(hashTx,wtx))
return true;
@@ -111,6 +118,7 @@ bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx)
// erases transaction with the given hash from all wallets
void static EraseFromWallets(uint256 hash)
{
+ LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->EraseFromWallet(hash);
}
@@ -118,6 +126,7 @@ void static EraseFromWallets(uint256 hash)
// make sure all wallets know about the given transaction, in the given block
void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate)
{
+ LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate);
}
@@ -125,6 +134,7 @@ void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock*
// notify wallets about a new best chain
void static SetBestChain(const CBlockLocator& loc)
{
+ LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->SetBestChain(loc);
}
@@ -132,6 +142,7 @@ void static SetBestChain(const CBlockLocator& loc)
// notify wallets about an updated transaction
void static UpdatedTransaction(const uint256& hashTx)
{
+ LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->UpdatedTransaction(hashTx);
}
@@ -139,6 +150,7 @@ void static UpdatedTransaction(const uint256& hashTx)
// dump all wallets
void static PrintWallets(const CBlock& block)
{
+ LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->PrintWallet(block);
}
@@ -146,6 +158,7 @@ void static PrintWallets(const CBlock& block)
// notify wallets about an incoming inventory (for request counts)
void static Inventory(const uint256& hash)
{
+ LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->Inventory(hash);
}
@@ -153,6 +166,7 @@ void static Inventory(const uint256& hash)
// ask wallets to resend their transactions
void static ResendWalletTransactions()
{
+ LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->ResendWalletTransactions();
}
@@ -385,16 +399,12 @@ CBlockTreeDB *pblocktree = NULL;
// mapOrphanTransactions
//
-bool AddOrphanTx(const CDataStream& vMsg)
+bool AddOrphanTx(const CTransaction& tx)
{
- CTransaction tx;
- CDataStream(vMsg) >> tx;
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
return false;
- CDataStream* pvMsg = new CDataStream(vMsg);
-
// Ignore big transactions, to avoid a
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
// large transaction with a missing parent then we assume
@@ -402,16 +412,16 @@ bool AddOrphanTx(const CDataStream& vMsg)
// have been mined or received.
// 10,000 orphans, each of which is at most 5,000 bytes big is
// at most 500 megabytes of orphans:
- if (pvMsg->size() > 5000)
+ unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
+ if (sz > 5000)
{
- printf("ignoring large orphan tx (size: %"PRIszu", hash: %s)\n", pvMsg->size(), hash.ToString().c_str());
- delete pvMsg;
+ printf("ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString().c_str());
return false;
}
- mapOrphanTransactions[hash] = pvMsg;
+ mapOrphanTransactions[hash] = tx;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
- mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg));
+ mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(),
mapOrphanTransactions.size());
@@ -422,16 +432,13 @@ void static EraseOrphanTx(uint256 hash)
{
if (!mapOrphanTransactions.count(hash))
return;
- const CDataStream* pvMsg = mapOrphanTransactions[hash];
- CTransaction tx;
- CDataStream(*pvMsg) >> tx;
+ const CTransaction& tx = mapOrphanTransactions[hash];
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
mapOrphanTransactionsByPrev.erase(txin.prevout.hash);
}
- delete pvMsg;
mapOrphanTransactions.erase(hash);
}
@@ -442,7 +449,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
{
// Evict a random orphan:
uint256 randomhash = GetRandHash();
- map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
+ map<uint256, CTransaction>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
EraseOrphanTx(it->first);
@@ -457,38 +464,53 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
-bool IsStandardTx(const CTransaction& tx)
+bool IsStandardTx(const CTransaction& tx, string& reason)
{
- if (tx.nVersion > CTransaction::CURRENT_VERSION)
+ if (tx.nVersion > CTransaction::CURRENT_VERSION) {
+ reason = "version";
return false;
+ }
- if (!IsFinalTx(tx))
+ if (!IsFinalTx(tx)) {
+ reason = "non-final";
return false;
+ }
// Extremely large transactions with lots of inputs can cost the network
// almost as much to process as they cost the sender in fees, because
// computing signature hashes is O(ninputs*txsize). Limiting transactions
// to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks.
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
- if (sz >= MAX_STANDARD_TX_SIZE)
+ if (sz >= MAX_STANDARD_TX_SIZE) {
+ reason = "tx-size";
return false;
+ }
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
// Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
// pay-to-script-hash, which is 3 ~80-byte signatures, 3
// ~65-byte public keys, plus a few script ops.
- if (txin.scriptSig.size() > 500)
+ if (txin.scriptSig.size() > 500) {
+ reason = "scriptsig-size";
return false;
- if (!txin.scriptSig.IsPushOnly())
+ }
+ if (!txin.scriptSig.IsPushOnly()) {
+ reason = "scriptsig-not-pushonly";
return false;
+ }
}
BOOST_FOREACH(const CTxOut& txout, tx.vout) {
- if (!::IsStandard(txout.scriptPubKey))
+ if (!::IsStandard(txout.scriptPubKey)) {
+ reason = "scriptpubkey";
return false;
- if (txout.IsDust(CTransaction::nMinRelayTxFee))
+ }
+ if (txout.IsDust(CTransaction::nMinRelayTxFee)) {
+ reason = "dust";
return false;
+ }
}
+
return true;
}
@@ -627,7 +649,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
if (pcoinsTip->GetCoins(GetHash(), coins)) {
CBlockIndex *pindex = FindBlockByHeight(coins.nHeight);
if (pindex) {
- if (!blockTmp.ReadFromDisk(pindex))
+ if (!ReadBlockFromDisk(blockTmp, pindex))
return 0;
pblock = &blockTmp;
}
@@ -764,7 +786,7 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
}
}
-bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFree,
+bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs)
{
if (pfMissingInputs)
@@ -781,9 +803,11 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet");
- // Rather not work on nonstandard transactions (unless -testnet)
- if (!TestNet() && !IsStandardTx(tx))
- return error("CTxMemPool::accept() : nonstandard transaction type");
+ // Rather not work on nonstandard transactions (unless -testnet/-regtest)
+ string reason;
+ if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason))
+ return error("CTxMemPool::accept() : nonstandard transaction: %s",
+ reason.c_str());
// is it already in the memory pool?
uint256 hash = tx.GetHash();
@@ -857,7 +881,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
}
// Check for non-standard pay-to-script-hash in inputs
- if (!TestNet() && !AreInputsStandard(tx, view))
+ if (Params().NetworkID() == CChainParams::MAIN && !AreInputsStandard(tx, view))
return error("CTxMemPool::accept() : nonstandard transaction input");
// Note: if you modify this code to accept non-standard transactions, then
@@ -929,7 +953,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
}
-bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
+bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx)
{
// Add to memory pool without checking anything. Don't call this directly,
// call CTxMemPool::accept to properly check the transaction first.
@@ -1114,7 +1138,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock
if (pindexSlow) {
CBlock block;
- if (block.ReadFromDisk(pindexSlow)) {
+ if (ReadBlockFromDisk(block, pindexSlow)) {
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
if (tx.GetHash() == hash) {
txOut = tx;
@@ -1146,12 +1170,62 @@ CBlockIndex* FindBlockByHeight(int nHeight)
return vBlockIndexByHeight[nHeight];
}
-bool CBlock::ReadFromDisk(const CBlockIndex* pindex)
+bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos)
+{
+ // Open history file to append
+ CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
+ if (!fileout)
+ return error("WriteBlockToDisk() : OpenBlockFile failed");
+
+ // Write index header
+ unsigned int nSize = fileout.GetSerializeSize(block);
+ fileout << FLATDATA(Params().MessageStart()) << nSize;
+
+ // Write block
+ long fileOutPos = ftell(fileout);
+ if (fileOutPos < 0)
+ return error("WriteBlockToDisk() : ftell failed");
+ pos.nPos = (unsigned int)fileOutPos;
+ fileout << block;
+
+ // Flush stdio buffers and commit to disk before returning
+ fflush(fileout);
+ if (!IsInitialBlockDownload())
+ FileCommit(fileout);
+
+ return true;
+}
+
+bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos)
{
- if (!ReadFromDisk(pindex->GetBlockPos()))
+ block.SetNull();
+
+ // Open history file to read
+ CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
+ if (!filein)
+ return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : OpenBlockFile failed");
+
+ // Read block
+ try {
+ filein >> block;
+ }
+ catch (std::exception &e) {
+ return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
+ }
+
+ // Check the header
+ if (!CheckProofOfWork(block.GetHash(), block.nBits))
+ return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : errors in block header");
+
+ return true;
+}
+
+bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex)
+{
+ if (!ReadBlockFromDisk(block, pindex->GetBlockPos()))
return false;
- if (GetHash() != pindex->GetBlockHash())
- return error("CBlock::ReadFromDisk() : GetHash() doesn't match index");
+ if (block.GetHash() != pindex->GetBlockHash())
+ return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index");
return true;
}
@@ -1163,7 +1237,7 @@ uint256 static GetOrphanRoot(const CBlockHeader* pblock)
return pblock->GetHash();
}
-int64 static GetBlockValue(int nHeight, int64 nFees)
+int64 GetBlockValue(int nHeight, int64 nFees)
{
int64 nSubsidy = 50 * COIN;
@@ -1203,7 +1277,7 @@ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
return bnResult.GetCompact();
}
-unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock)
+unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock)
{
unsigned int nProofOfWorkLimit = Params().ProofOfWorkLimit().GetCompact();
@@ -1302,6 +1376,82 @@ bool IsInitialBlockDownload()
pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60);
}
+bool fLargeWorkForkFound = false;
+bool fLargeWorkInvalidChainFound = false;
+CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL;
+
+void CheckForkWarningConditions()
+{
+ // If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it)
+ // of our head, drop it
+ if (pindexBestForkTip && nBestHeight - pindexBestForkTip->nHeight >= 72)
+ pindexBestForkTip = NULL;
+
+ if (pindexBestForkTip || nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256())
+ {
+ if (!fLargeWorkForkFound)
+ {
+ std::string strCmd = GetArg("-alertnotify", "");
+ if (!strCmd.empty())
+ {
+ std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") +
+ pindexBestForkBase->phashBlock->ToString() + std::string("'");
+ boost::replace_all(strCmd, "%s", warning);
+ boost::thread t(runCommand, strCmd); // thread runs free
+ }
+ }
+ if (pindexBestForkTip)
+ {
+ printf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n",
+ pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString().c_str(),
+ pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString().c_str());
+ fLargeWorkForkFound = true;
+ }
+ else
+ {
+ printf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n");
+ fLargeWorkInvalidChainFound = true;
+ }
+ }
+ else
+ {
+ fLargeWorkForkFound = false;
+ fLargeWorkInvalidChainFound = false;
+ }
+}
+
+void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
+{
+ // If we are on a fork that is sufficiently large, set a warning flag
+ CBlockIndex* pfork = pindexNewForkTip;
+ CBlockIndex* plonger = pindexBest;
+ while (pfork && pfork != plonger)
+ {
+ while (plonger && plonger->nHeight > pfork->nHeight)
+ plonger = plonger->pprev;
+ if (pfork == plonger)
+ break;
+ pfork = pfork->pprev;
+ }
+
+ // We define a condition which we should warn the user about as a fork of at least 7 blocks
+ // who's tip is within 72 blocks (+/- 12 hours if no one mines it) of ours
+ // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network
+ // hash rate operating on the fork.
+ // or a chain that is entirely longer than ours and invalid (note that this should be detected by both)
+ // We define it this way because it allows us to only store the highest fork tip (+ base) which meets
+ // the 7-block condition and from this always have the most-likely-to-cause-warning fork
+ if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) &&
+ pindexNewForkTip->nChainWork - pfork->nChainWork > (pfork->GetBlockWork() * 7).getuint256() &&
+ nBestHeight - pindexNewForkTip->nHeight < 72)
+ {
+ pindexBestForkTip = pindexNewForkTip;
+ pindexBestForkBase = pfork;
+ }
+
+ CheckForkWarningConditions();
+}
+
void static InvalidChainFound(CBlockIndex* pindexNew)
{
if (pindexNew->nChainWork > nBestInvalidWork)
@@ -1317,8 +1467,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
printf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n",
hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str());
- if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256())
- printf("InvalidChainFound: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n");
+ CheckForkWarningConditions();
}
void static InvalidBlockFound(CBlockIndex *pindex) {
@@ -1553,7 +1702,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach
-bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean)
+bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
{
assert(pindex == view.GetBestBlock());
@@ -1569,12 +1718,12 @@ bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoin
if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash()))
return error("DisconnectBlock() : failure reading undo data");
- if (blockUndo.vtxundo.size() + 1 != vtx.size())
+ if (blockUndo.vtxundo.size() + 1 != block.vtx.size())
return error("DisconnectBlock() : block and undo data inconsistent");
// undo transactions in reverse order
- for (int i = vtx.size() - 1; i >= 0; i--) {
- const CTransaction &tx = vtx[i];
+ for (int i = block.vtx.size() - 1; i >= 0; i--) {
+ const CTransaction &tx = block.vtx[i];
uint256 hash = tx.GetHash();
// check that all outputs are available
@@ -1667,10 +1816,10 @@ void ThreadScriptCheck() {
scriptcheckqueue.Thread();
}
-bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
+bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck)
{
// Check it again in case a previous version let a bad block in
- if (!CheckBlock(state, !fJustCheck, !fJustCheck))
+ if (!CheckBlock(block, state, !fJustCheck, !fJustCheck))
return false;
// verify that the view's current state corresponds to the previous block
@@ -1678,7 +1827,7 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi
// Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable)
- if (GetHash() == Params().HashGenesisBlock()) {
+ if (block.GetHash() == Params().HashGenesisBlock()) {
view.SetBestBlock(pindex);
pindexGenesisBlock = pindex;
return true;
@@ -1702,8 +1851,8 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi
!((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
(pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
if (fEnforceBIP30) {
- for (unsigned int i=0; i<vtx.size(); i++) {
- uint256 hash = GetTxHash(i);
+ for (unsigned int i = 0; i < block.vtx.size(); i++) {
+ uint256 hash = block.GetTxHash(i);
if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"));
}
@@ -1724,12 +1873,12 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi
int64 nFees = 0;
int nInputs = 0;
unsigned int nSigOps = 0;
- CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(vtx.size()));
+ CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
- vPos.reserve(vtx.size());
- for (unsigned int i=0; i<vtx.size(); i++)
+ vPos.reserve(block.vtx.size());
+ for (unsigned int i = 0; i < block.vtx.size(); i++)
{
- const CTransaction &tx = vtx[i];
+ const CTransaction &tx = block.vtx[i];
nInputs += tx.vin.size();
nSigOps += GetLegacySigOpCount(tx);
@@ -1760,19 +1909,19 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi
}
CTxUndo txundo;
- UpdateCoins(tx, state, view, txundo, pindex->nHeight, GetTxHash(i));
+ UpdateCoins(tx, state, view, txundo, pindex->nHeight, block.GetTxHash(i));
if (!tx.IsCoinBase())
blockundo.vtxundo.push_back(txundo);
- vPos.push_back(std::make_pair(GetTxHash(i), pos));
+ vPos.push_back(std::make_pair(block.GetTxHash(i), pos));
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}
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));
+ printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1));
- if (GetValueOut(vtx[0]) > GetBlockValue(pindex->nHeight, nFees))
- return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(vtx[0]), GetBlockValue(pindex->nHeight, nFees)));
+ if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees))
+ return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)));
if (!control.Wait())
return state.DoS(100, false);
@@ -1813,8 +1962,8 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi
assert(view.SetBestBlock(pindex));
// Watch for transactions paying to me
- for (unsigned int i=0; i<vtx.size(); i++)
- SyncWithWallets(GetTxHash(i), vtx[i], this, true);
+ for (unsigned int i = 0; i < block.vtx.size(); i++)
+ SyncWithWallets(block.GetTxHash(i), block.vtx[i], &block, true);
return true;
}
@@ -1857,13 +2006,13 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
}
// Disconnect shorter branch
- vector<CTransaction> vResurrect;
+ list<CTransaction> vResurrect;
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
CBlock block;
- if (!block.ReadFromDisk(pindex))
+ if (!ReadBlockFromDisk(block, pindex))
return state.Abort(_("Failed to read block"));
int64 nStart = GetTimeMicros();
- if (!block.DisconnectBlock(state, pindex, view))
+ if (!DisconnectBlock(block, state, pindex, view))
return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().c_str());
if (fBenchmark)
printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
@@ -1871,19 +2020,19 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
// Queue memory transactions to resurrect.
// We only do this for blocks after the last checkpoint (reorganisation before that
// point should only happen with -reindex/-loadblock, or a misbehaving peer.
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ BOOST_REVERSE_FOREACH(const CTransaction& tx, block.vtx)
if (!tx.IsCoinBase() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
- vResurrect.push_back(tx);
+ vResurrect.push_front(tx);
}
// Connect longer branch
vector<CTransaction> vDelete;
BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
CBlock block;
- if (!block.ReadFromDisk(pindex))
+ if (!ReadBlockFromDisk(block, pindex))
return state.Abort(_("Failed to read block"));
int64 nStart = GetTimeMicros();
- if (!block.ConnectBlock(state, pindex, view)) {
+ if (!ConnectBlock(block, state, pindex, view)) {
if (state.IsInvalid()) {
InvalidChainFound(pindexNew);
InvalidBlockFound(pindex);
@@ -1993,25 +2142,25 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
}
-bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos)
+bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos)
{
// Check for duplicate
- uint256 hash = GetHash();
+ uint256 hash = block.GetHash();
if (mapBlockIndex.count(hash))
return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString().c_str()));
// Construct new block index object
- CBlockIndex* pindexNew = new CBlockIndex(*this);
+ CBlockIndex* pindexNew = new CBlockIndex(block);
assert(pindexNew);
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
- map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
+ map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
}
- pindexNew->nTx = vtx.size();
+ pindexNew->nTx = block.vtx.size();
pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork().getuint256();
pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx;
pindexNew->nFile = pos.nFile;
@@ -2029,11 +2178,14 @@ bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos)
if (pindexNew == pindexBest)
{
+ // Clear fork warning if its no longer applicable
+ CheckForkWarningConditions();
// Notify UI to display prev block's coinbase if it was ours
static uint256 hashPrevBestCoinBase;
UpdatedTransaction(hashPrevBestCoinBase);
- hashPrevBestCoinBase = GetTxHash(0);
- }
+ hashPrevBestCoinBase = block.GetTxHash(0);
+ } else
+ CheckForkWarningConditionsOnNewFork(pindexNew);
if (!pblocktree->Flush())
return state.Abort(_("Failed to sync block index"));
@@ -2138,51 +2290,51 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
}
-bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const
+bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context
// that can be verified before saving an orphan block.
// Size limits
- if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
+ if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return state.DoS(100, error("CheckBlock() : size limits failed"));
// Check proof of work matches claimed amount
- if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits))
+ if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits))
return state.DoS(50, error("CheckBlock() : proof of work failed"));
// Check timestamp
- if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
+ if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
return state.Invalid(error("CheckBlock() : block timestamp too far in the future"));
// First transaction must be coinbase, the rest must not be
- if (vtx.empty() || !vtx[0].IsCoinBase())
+ if (block.vtx.empty() || !block.vtx[0].IsCoinBase())
return state.DoS(100, error("CheckBlock() : first tx is not coinbase"));
- for (unsigned int i = 1; i < vtx.size(); i++)
- if (vtx[i].IsCoinBase())
+ for (unsigned int i = 1; i < block.vtx.size(); i++)
+ if (block.vtx[i].IsCoinBase())
return state.DoS(100, error("CheckBlock() : more than one coinbase"));
// Check transactions
- BOOST_FOREACH(const CTransaction& tx, vtx)
+ BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!CheckTransaction(tx, state))
return error("CheckBlock() : CheckTransaction failed");
// Build the merkle tree already. We need it anyway later, and it makes the
// block cache the transaction hashes, which means they don't need to be
// recalculated many times during this block's validation.
- BuildMerkleTree();
+ block.BuildMerkleTree();
// Check for duplicate txids. This is caught by ConnectInputs(),
// but catching it earlier avoids a potential DoS attack:
set<uint256> uniqueTx;
- for (unsigned int i=0; i<vtx.size(); i++) {
- uniqueTx.insert(GetTxHash(i));
+ for (unsigned int i = 0; i < block.vtx.size(); i++) {
+ uniqueTx.insert(block.GetTxHash(i));
}
- if (uniqueTx.size() != vtx.size())
+ if (uniqueTx.size() != block.vtx.size())
return state.DoS(100, error("CheckBlock() : duplicate transaction"));
unsigned int nSigOps = 0;
- BOOST_FOREACH(const CTransaction& tx, vtx)
+ BOOST_FOREACH(const CTransaction& tx, block.vtx)
{
nSigOps += GetLegacySigOpCount(tx);
}
@@ -2190,16 +2342,16 @@ bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerk
return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
// Check merkle root
- if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree())
+ if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back())
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
return true;
}
-bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp)
+bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
{
// Check for duplicate
- uint256 hash = GetHash();
+ uint256 hash = block.GetHash();
if (mapBlockIndex.count(hash))
return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex"));
@@ -2207,23 +2359,23 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp)
CBlockIndex* pindexPrev = NULL;
int nHeight = 0;
if (hash != Params().HashGenesisBlock()) {
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock);
+ map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("AcceptBlock() : prev block not found"));
pindexPrev = (*mi).second;
nHeight = pindexPrev->nHeight+1;
// Check proof of work
- if (nBits != GetNextWorkRequired(pindexPrev, this))
+ if (block.nBits != GetNextWorkRequired(pindexPrev, &block))
return state.DoS(100, error("AcceptBlock() : incorrect proof of work"));
// Check timestamp against prev
- if (GetBlockTime() <= pindexPrev->GetMedianTimePast())
+ if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
return state.Invalid(error("AcceptBlock() : block's timestamp is too early"));
// Check that all transactions are finalized
- BOOST_FOREACH(const CTransaction& tx, vtx)
- if (!IsFinalTx(tx, nHeight, GetBlockTime()))
+ BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ if (!IsFinalTx(tx, nHeight, block.GetBlockTime()))
return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"));
// Check that the block chain matches the known block chain up to a checkpoint
@@ -2231,7 +2383,7 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp)
return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight));
// Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
- if (nVersion < 2)
+ if (block.nVersion < 2)
{
if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) ||
(TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100)))
@@ -2240,14 +2392,14 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp)
}
}
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
- if (nVersion >= 2)
+ if (block.nVersion >= 2)
{
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) ||
(TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100)))
{
CScript expect = CScript() << nHeight;
- if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin()))
+ if (!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin()))
return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"));
}
}
@@ -2255,16 +2407,16 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp)
// Write block to history file
try {
- unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION);
+ unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
if (dbp != NULL)
blockPos = *dbp;
- if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL))
+ if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.nTime, dbp != NULL))
return error("AcceptBlock() : FindBlockPos failed");
if (dbp == NULL)
- if (!WriteToDisk(blockPos))
+ if (!WriteBlockToDisk(block, blockPos))
return state.Abort(_("Failed to write block"));
- if (!AddToBlockIndex(state, blockPos))
+ if (!AddToBlockIndex(block, state, blockPos))
return error("AcceptBlock() : AddToBlockIndex failed");
} catch(std::runtime_error &e) {
return state.Abort(_("System error: ") + e.what());
@@ -2316,7 +2468,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString().c_str()));
// Preliminary checks
- if (!pblock->CheckBlock(state))
+ if (!CheckBlock(*pblock, state))
return error("ProcessBlock() : CheckBlock FAILED");
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
@@ -2357,7 +2509,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
}
// Store to disk
- if (!pblock->AcceptBlock(state, dbp))
+ if (!AcceptBlock(*pblock, state, dbp))
return error("ProcessBlock() : AcceptBlock FAILED");
// Recursively process any orphan blocks that depended on this one
@@ -2373,7 +2525,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
CBlock* pblockOrphan = (*mi).second;
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned)
CValidationState stateDummy;
- if (pblockOrphan->AcceptBlock(stateDummy))
+ if (AcceptBlock(*pblockOrphan, stateDummy))
vWorkQueue.push_back(pblockOrphan->GetHash());
mapOrphanBlocks.erase(pblockOrphan->GetHash());
delete pblockOrphan;
@@ -2710,10 +2862,10 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth)
break;
CBlock block;
// check level 0: read from disk
- if (!block.ReadFromDisk(pindex))
- return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
+ if (!ReadBlockFromDisk(block, pindex))
+ return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
// check level 1: verify block validity
- if (nCheckLevel >= 1 && !block.CheckBlock(state))
+ if (nCheckLevel >= 1 && !CheckBlock(block, state))
return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
@@ -2727,7 +2879,7 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth)
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) {
bool fClean = true;
- if (!block.DisconnectBlock(state, pindex, coins, &fClean))
+ if (!DisconnectBlock(block, state, pindex, coins, &fClean))
return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
pindexState = pindex->pprev;
if (!fClean) {
@@ -2747,9 +2899,9 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth)
boost::this_thread::interruption_point();
pindex = pindex->GetNextInMainChain();
CBlock block;
- if (!block.ReadFromDisk(pindex))
- return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
- if (!block.ConnectBlock(state, pindex, coins))
+ if (!ReadBlockFromDisk(block, pindex))
+ return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
+ if (!ConnectBlock(block, state, pindex, coins))
return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
}
}
@@ -2800,9 +2952,9 @@ bool InitBlockIndex() {
CValidationState state;
if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime))
return error("LoadBlockIndex() : FindBlockPos failed");
- if (!block.WriteToDisk(blockPos))
+ if (!WriteBlockToDisk(block, blockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
- if (!block.AddToBlockIndex(state, blockPos))
+ if (!AddToBlockIndex(block, state, blockPos))
return error("LoadBlockIndex() : genesis block not accepted");
} catch(std::runtime_error &e) {
return error("LoadBlockIndex() : failed to initialize block database: %s", e.what());
@@ -2858,7 +3010,7 @@ void PrintBlockTree()
// print item
CBlock block;
- block.ReadFromDisk(pindex);
+ ReadBlockFromDisk(block, pindex);
printf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"",
pindex->nHeight,
pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos,
@@ -2992,11 +3144,15 @@ string GetWarnings(string strFor)
strStatusBar = strMiscWarning;
}
- // Longer invalid proof-of-work chain
- if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256())
+ if (fLargeWorkForkFound)
{
nPriority = 2000;
- strStatusBar = strRPC = _("Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.");
+ strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
+ }
+ else if (fLargeWorkInvalidChainFound)
+ {
+ nPriority = 2000;
+ strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
}
// Alerts
@@ -3082,7 +3238,7 @@ void static ProcessGetData(CNode* pfrom)
if (mi != mapBlockIndex.end())
{
CBlock block;
- block.ReadFromDisk((*mi).second);
+ ReadBlockFromDisk(block, (*mi).second);
if (inv.type == MSG_BLOCK)
pfrom->PushMessage("block", block);
else // MSG_FILTERED_BLOCK)
@@ -3521,7 +3677,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CValidationState state;
if (mempool.accept(state, tx, true, &fMissingInputs))
{
- RelayTransaction(tx, inv.hash, vMsg);
+ RelayTransaction(tx, inv.hash);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
vEraseQueue.push_back(inv.hash);
@@ -3530,31 +3686,31 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
- for (map<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
+ for (set<uint256>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
mi != mapOrphanTransactionsByPrev[hashPrev].end();
++mi)
{
- const CDataStream& vMsg = *((*mi).second);
- CTransaction tx;
- CDataStream(vMsg) >> tx;
- CInv inv(MSG_TX, tx.GetHash());
+ const uint256& orphanHash = *mi;
+ const CTransaction& orphanTx = mapOrphanTransactions[orphanHash];
bool fMissingInputs2 = false;
- // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get anyone relaying LegitTxX banned)
+ // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
+ // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
+ // anyone relaying LegitTxX banned)
CValidationState stateDummy;
- if (mempool.accept(stateDummy, tx, true, &fMissingInputs2))
+ if (mempool.accept(stateDummy, orphanTx, true, &fMissingInputs2))
{
- printf(" accepted orphan tx %s\n", inv.hash.ToString().c_str());
- RelayTransaction(tx, inv.hash, vMsg);
- mapAlreadyAskedFor.erase(inv);
- vWorkQueue.push_back(inv.hash);
- vEraseQueue.push_back(inv.hash);
+ printf(" accepted orphan tx %s\n", orphanHash.ToString().c_str());
+ RelayTransaction(orphanTx, orphanHash);
+ mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash));
+ vWorkQueue.push_back(orphanHash);
+ vEraseQueue.push_back(orphanHash);
}
else if (!fMissingInputs2)
{
// invalid or too-little-fee orphan
- vEraseQueue.push_back(inv.hash);
- printf(" removed orphan tx %s\n", inv.hash.ToString().c_str());
+ vEraseQueue.push_back(orphanHash);
+ printf(" removed orphan tx %s\n", orphanHash.ToString().c_str());
}
}
}
@@ -3564,7 +3720,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
else if (fMissingInputs)
{
- AddOrphanTx(vMsg);
+ AddOrphanTx(tx);
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
@@ -4033,715 +4189,6 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
-
-
-
-
-
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// BitcoinMiner
-//
-
-int static FormatHashBlocks(void* pbuffer, unsigned int len)
-{
- unsigned char* pdata = (unsigned char*)pbuffer;
- unsigned int blocks = 1 + ((len + 8) / 64);
- unsigned char* pend = pdata + 64 * blocks;
- memset(pdata + len, 0, 64 * blocks - len);
- pdata[len] = 0x80;
- unsigned int bits = len * 8;
- pend[-1] = (bits >> 0) & 0xff;
- pend[-2] = (bits >> 8) & 0xff;
- pend[-3] = (bits >> 16) & 0xff;
- pend[-4] = (bits >> 24) & 0xff;
- return blocks;
-}
-
-static const unsigned int pSHA256InitState[8] =
-{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
-
-void SHA256Transform(void* pstate, void* pinput, const void* pinit)
-{
- SHA256_CTX ctx;
- unsigned char data[64];
-
- SHA256_Init(&ctx);
-
- for (int i = 0; i < 16; i++)
- ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]);
-
- for (int i = 0; i < 8; i++)
- ctx.h[i] = ((uint32_t*)pinit)[i];
-
- SHA256_Update(&ctx, data, sizeof(data));
- for (int i = 0; i < 8; i++)
- ((uint32_t*)pstate)[i] = ctx.h[i];
-}
-
-//
-// ScanHash scans nonces looking for a hash with at least some zero bits.
-// It operates on big endian data. Caller does the byte reversing.
-// All input buffers are 16-byte aligned. nNonce is usually preserved
-// between calls, but periodically or if nNonce is 0xffff0000 or above,
-// the block is rebuilt and nNonce starts over at zero.
-//
-unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone)
-{
- unsigned int& nNonce = *(unsigned int*)(pdata + 12);
- for (;;)
- {
- // Crypto++ SHA256
- // Hash pdata using pmidstate as the starting state into
- // pre-formatted buffer phash1, then hash phash1 into phash
- nNonce++;
- SHA256Transform(phash1, pdata, pmidstate);
- SHA256Transform(phash, phash1, pSHA256InitState);
-
- // Return the nonce if the hash has at least some zero bits,
- // caller will check if it has enough to reach the target
- if (((unsigned short*)phash)[14] == 0)
- return nNonce;
-
- // If nothing found after trying for a while, return -1
- if ((nNonce & 0xffff) == 0)
- {
- nHashesDone = 0xffff+1;
- return (unsigned int) -1;
- }
- if ((nNonce & 0xfff) == 0)
- boost::this_thread::interruption_point();
- }
-}
-
-// Some explaining would be appreciated
-class COrphan
-{
-public:
- CTransaction* ptx;
- set<uint256> setDependsOn;
- double dPriority;
- double dFeePerKb;
-
- COrphan(CTransaction* ptxIn)
- {
- ptx = ptxIn;
- dPriority = dFeePerKb = 0;
- }
-
- void print() const
- {
- printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n",
- ptx->GetHash().ToString().c_str(), dPriority, dFeePerKb);
- BOOST_FOREACH(uint256 hash, setDependsOn)
- printf(" setDependsOn %s\n", hash.ToString().c_str());
- }
-};
-
-
-uint64 nLastBlockTx = 0;
-uint64 nLastBlockSize = 0;
-
-// We want to sort transactions by priority and fee, so:
-typedef boost::tuple<double, double, CTransaction*> TxPriority;
-class TxPriorityCompare
-{
- bool byFee;
-public:
- TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
- bool operator()(const TxPriority& a, const TxPriority& b)
- {
- if (byFee)
- {
- if (a.get<1>() == b.get<1>())
- return a.get<0>() < b.get<0>();
- return a.get<1>() < b.get<1>();
- }
- else
- {
- if (a.get<0>() == b.get<0>())
- return a.get<1>() < b.get<1>();
- return a.get<0>() < b.get<0>();
- }
- }
-};
-
-CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
-{
- // Create new block
- auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
- if(!pblocktemplate.get())
- return NULL;
- CBlock *pblock = &pblocktemplate->block; // pointer for convenience
-
- // Create coinbase tx
- CTransaction txNew;
- txNew.vin.resize(1);
- txNew.vin[0].prevout.SetNull();
- txNew.vout.resize(1);
- CPubKey pubkey;
- if (!reservekey.GetReservedKey(pubkey))
- return NULL;
- txNew.vout[0].scriptPubKey << pubkey << OP_CHECKSIG;
-
- // Add our coinbase tx as first transaction
- pblock->vtx.push_back(txNew);
- pblocktemplate->vTxFees.push_back(-1); // updated at end
- pblocktemplate->vTxSigOps.push_back(-1); // updated at end
-
- // Largest block you're willing to create:
- unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2);
- // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
- nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize));
-
- // How much of the block should be dedicated to high-priority transactions,
- // included regardless of the fees they pay
- unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE);
- nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
-
- // Minimum block size you want to create; block will be filled with free transactions
- // until there are no more or the block reaches this size:
- unsigned int nBlockMinSize = GetArg("-blockminsize", 0);
- nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
-
- // Collect memory pool transactions into the block
- int64 nFees = 0;
- {
- LOCK2(cs_main, mempool.cs);
- CBlockIndex* pindexPrev = pindexBest;
- CCoinsViewCache view(*pcoinsTip, true);
-
- // Priority order to process transactions
- list<COrphan> vOrphan; // list memory doesn't move
- map<uint256, vector<COrphan*> > mapDependers;
- bool fPrintPriority = GetBoolArg("-printpriority", false);
-
- // This vector will be sorted into a priority queue:
- vector<TxPriority> vecPriority;
- vecPriority.reserve(mempool.mapTx.size());
- for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
- {
- CTransaction& tx = (*mi).second;
- if (tx.IsCoinBase() || !IsFinalTx(tx))
- continue;
-
- COrphan* porphan = NULL;
- double dPriority = 0;
- int64 nTotalIn = 0;
- bool fMissingInputs = false;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
- {
- // Read prev transaction
- if (!view.HaveCoins(txin.prevout.hash))
- {
- // This should never happen; all transactions in the memory
- // pool should connect to either transactions in the chain
- // or other transactions in the memory pool.
- if (!mempool.mapTx.count(txin.prevout.hash))
- {
- printf("ERROR: mempool transaction missing input\n");
- if (fDebug) assert("mempool transaction missing input" == 0);
- fMissingInputs = true;
- if (porphan)
- vOrphan.pop_back();
- break;
- }
-
- // Has to wait for dependencies
- if (!porphan)
- {
- // Use list for automatic deletion
- vOrphan.push_back(COrphan(&tx));
- porphan = &vOrphan.back();
- }
- mapDependers[txin.prevout.hash].push_back(porphan);
- porphan->setDependsOn.insert(txin.prevout.hash);
- nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue;
- continue;
- }
- const CCoins &coins = view.GetCoins(txin.prevout.hash);
-
- int64 nValueIn = coins.vout[txin.prevout.n].nValue;
- nTotalIn += nValueIn;
-
- int nConf = pindexPrev->nHeight - coins.nHeight + 1;
-
- dPriority += (double)nValueIn * nConf;
- }
- if (fMissingInputs) continue;
-
- // Priority is sum(valuein * age) / txsize
- unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
- dPriority /= nTxSize;
-
- // This is a more accurate fee-per-kilobyte than is used by the client code, because the
- // client code rounds up the size to the nearest 1K. That's good, because it gives an
- // incentive to create smaller transactions.
- double dFeePerKb = double(nTotalIn-GetValueOut(tx)) / (double(nTxSize)/1000.0);
-
- if (porphan)
- {
- porphan->dPriority = dPriority;
- porphan->dFeePerKb = dFeePerKb;
- }
- else
- vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second));
- }
-
- // Collect transactions into block
- uint64 nBlockSize = 1000;
- uint64 nBlockTx = 0;
- int nBlockSigOps = 100;
- bool fSortedByFee = (nBlockPrioritySize <= 0);
-
- TxPriorityCompare comparer(fSortedByFee);
- std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
-
- while (!vecPriority.empty())
- {
- // Take highest priority transaction off the priority queue:
- double dPriority = vecPriority.front().get<0>();
- double dFeePerKb = vecPriority.front().get<1>();
- CTransaction& tx = *(vecPriority.front().get<2>());
-
- std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
- vecPriority.pop_back();
-
- // Size limits
- unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
- if (nBlockSize + nTxSize >= nBlockMaxSize)
- continue;
-
- // Legacy limits on sigOps:
- unsigned int nTxSigOps = GetLegacySigOpCount(tx);
- if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
- continue;
-
- // Skip free transactions if we're past the minimum block size:
- if (fSortedByFee && (dFeePerKb < CTransaction::nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
- continue;
-
- // Prioritize by fee once past the priority size or we run out of high-priority
- // transactions:
- if (!fSortedByFee &&
- ((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
- {
- fSortedByFee = true;
- comparer = TxPriorityCompare(fSortedByFee);
- std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
- }
-
- if (!view.HaveInputs(tx))
- continue;
-
- int64 nTxFees = view.GetValueIn(tx)-GetValueOut(tx);
-
- nTxSigOps += GetP2SHSigOpCount(tx, view);
- if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
- continue;
-
- CValidationState state;
- if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH))
- continue;
-
- CTxUndo txundo;
- uint256 hash = tx.GetHash();
- UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1, hash);
-
- // Added
- pblock->vtx.push_back(tx);
- pblocktemplate->vTxFees.push_back(nTxFees);
- pblocktemplate->vTxSigOps.push_back(nTxSigOps);
- nBlockSize += nTxSize;
- ++nBlockTx;
- nBlockSigOps += nTxSigOps;
- nFees += nTxFees;
-
- if (fPrintPriority)
- {
- printf("priority %.1f feeperkb %.1f txid %s\n",
- dPriority, dFeePerKb, tx.GetHash().ToString().c_str());
- }
-
- // Add transactions that depend on this one to the priority queue
- if (mapDependers.count(hash))
- {
- BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
- {
- if (!porphan->setDependsOn.empty())
- {
- porphan->setDependsOn.erase(hash);
- if (porphan->setDependsOn.empty())
- {
- vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx));
- std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
- }
- }
- }
- }
- }
-
- nLastBlockTx = nBlockTx;
- nLastBlockSize = nBlockSize;
- printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize);
-
- pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
- pblocktemplate->vTxFees[0] = -nFees;
-
- // Fill in header
- pblock->hashPrevBlock = pindexPrev->GetBlockHash();
- UpdateTime(*pblock, pindexPrev);
- pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
- pblock->nNonce = 0;
- pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0;
- pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
-
- CBlockIndex indexDummy(*pblock);
- indexDummy.pprev = pindexPrev;
- indexDummy.nHeight = pindexPrev->nHeight + 1;
- CCoinsViewCache viewNew(*pcoinsTip, true);
- CValidationState state;
- if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true))
- throw std::runtime_error("CreateNewBlock() : ConnectBlock failed");
- }
-
- return pblocktemplate.release();
-}
-
-
-void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
-{
- // Update nExtraNonce
- static uint256 hashPrevBlock;
- if (hashPrevBlock != pblock->hashPrevBlock)
- {
- nExtraNonce = 0;
- hashPrevBlock = pblock->hashPrevBlock;
- }
- ++nExtraNonce;
- unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
- pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS;
- assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100);
-
- pblock->hashMerkleRoot = pblock->BuildMerkleTree();
-}
-
-
-void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1)
-{
- //
- // Pre-build hash buffers
- //
- struct
- {
- struct unnamed2
- {
- int nVersion;
- uint256 hashPrevBlock;
- uint256 hashMerkleRoot;
- unsigned int nTime;
- unsigned int nBits;
- unsigned int nNonce;
- }
- block;
- unsigned char pchPadding0[64];
- uint256 hash1;
- unsigned char pchPadding1[64];
- }
- tmp;
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.block.nVersion = pblock->nVersion;
- tmp.block.hashPrevBlock = pblock->hashPrevBlock;
- tmp.block.hashMerkleRoot = pblock->hashMerkleRoot;
- tmp.block.nTime = pblock->nTime;
- tmp.block.nBits = pblock->nBits;
- tmp.block.nNonce = pblock->nNonce;
-
- FormatHashBlocks(&tmp.block, sizeof(tmp.block));
- FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
-
- // Byte swap all the input buffer
- for (unsigned int i = 0; i < sizeof(tmp)/4; i++)
- ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
-
- // Precalc the first half of the first hash, which stays constant
- SHA256Transform(pmidstate, &tmp.block, pSHA256InitState);
-
- memcpy(pdata, &tmp.block, 128);
- memcpy(phash1, &tmp.hash1, 64);
-}
-
-
-bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
-{
- uint256 hash = pblock->GetHash();
- uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
-
- if (hash > hashTarget)
- return false;
-
- //// debug print
- printf("BitcoinMiner:\n");
- printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
- pblock->print();
- printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
-
- // Found a solution
- {
- LOCK(cs_main);
- if (pblock->hashPrevBlock != hashBestChain)
- return error("BitcoinMiner : generated block is stale");
-
- // Remove key from key pool
- reservekey.KeepKey();
-
- // Track how many getdata requests this block gets
- {
- LOCK(wallet.cs_wallet);
- wallet.mapRequestCount[pblock->GetHash()] = 0;
- }
-
- // Process this block the same as if we had received it from another node
- CValidationState state;
- if (!ProcessBlock(state, NULL, pblock))
- return error("BitcoinMiner : ProcessBlock, block not accepted");
- }
-
- return true;
-}
-
-void static BitcoinMiner(CWallet *pwallet)
-{
- printf("BitcoinMiner started\n");
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
- RenameThread("bitcoin-miner");
-
- // Each thread has its own key and counter
- CReserveKey reservekey(pwallet);
- unsigned int nExtraNonce = 0;
-
- try { loop {
- if (Params().NetworkID() != CChainParams::REGTEST) {
- // Busy-wait for the network to come online so we don't waste time mining
- // on an obsolete chain. In regtest mode we expect to fly solo.
- while (vNodes.empty())
- MilliSleep(1000);
- }
-
- //
- // Create new block
- //
- unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
- CBlockIndex* pindexPrev = pindexBest;
-
- auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(reservekey));
- if (!pblocktemplate.get())
- return;
- CBlock *pblock = &pblocktemplate->block;
- IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
-
- printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(),
- ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
-
- //
- // Pre-build hash buffers
- //
- char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf);
- char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf);
- char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf);
-
- FormatHashBuffers(pblock, pmidstate, pdata, phash1);
-
- unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4);
- unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8);
- unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12);
-
-
- //
- // Search
- //
- int64 nStart = GetTime();
- uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
- uint256 hashbuf[2];
- uint256& hash = *alignup<16>(hashbuf);
- loop
- {
- unsigned int nHashesDone = 0;
- unsigned int nNonceFound;
-
- // Crypto++ SHA256
- nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1,
- (char*)&hash, nHashesDone);
-
- // Check if something found
- if (nNonceFound != (unsigned int) -1)
- {
- for (unsigned int i = 0; i < sizeof(hash)/4; i++)
- ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]);
-
- if (hash <= hashTarget)
- {
- // Found a solution
- pblock->nNonce = ByteReverse(nNonceFound);
- assert(hash == pblock->GetHash());
-
- SetThreadPriority(THREAD_PRIORITY_NORMAL);
- CheckWork(pblock, *pwalletMain, reservekey);
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
-
- // In regression test mode, stop mining after a block is found. This
- // allows developers to controllably generate a block on demand.
- if (Params().NetworkID() == CChainParams::REGTEST)
- throw boost::thread_interrupted();
-
- break;
- }
- }
-
- // Meter hashes/sec
- static int64 nHashCounter;
- if (nHPSTimerStart == 0)
- {
- nHPSTimerStart = GetTimeMillis();
- nHashCounter = 0;
- }
- else
- nHashCounter += nHashesDone;
- if (GetTimeMillis() - nHPSTimerStart > 4000)
- {
- static CCriticalSection cs;
- {
- LOCK(cs);
- if (GetTimeMillis() - nHPSTimerStart > 4000)
- {
- dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart);
- nHPSTimerStart = GetTimeMillis();
- nHashCounter = 0;
- static int64 nLogTime;
- if (GetTime() - nLogTime > 30 * 60)
- {
- nLogTime = GetTime();
- printf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0);
- }
- }
- }
- }
-
- // Check for stop or if block needs to be rebuilt
- boost::this_thread::interruption_point();
- if (vNodes.empty() && Params().NetworkID() != CChainParams::REGTEST)
- break;
- if (nBlockNonce >= 0xffff0000)
- break;
- if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)
- break;
- if (pindexPrev != pindexBest)
- break;
-
- // Update nTime every few seconds
- UpdateTime(*pblock, pindexPrev);
- nBlockTime = ByteReverse(pblock->nTime);
- if (TestNet())
- {
- // Changing pblock->nTime can change work required on testnet:
- nBlockBits = ByteReverse(pblock->nBits);
- hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
- }
- }
- } }
- catch (boost::thread_interrupted)
- {
- printf("BitcoinMiner terminated\n");
- throw;
- }
-}
-
-void GenerateBitcoins(bool fGenerate, CWallet* pwallet)
-{
- static boost::thread_group* minerThreads = NULL;
-
- int nThreads = GetArg("-genproclimit", -1);
- if (nThreads < 0) {
- if (Params().NetworkID() == CChainParams::REGTEST)
- nThreads = 1;
- else
- nThreads = boost::thread::hardware_concurrency();
- }
-
- if (minerThreads != NULL)
- {
- minerThreads->interrupt_all();
- delete minerThreads;
- minerThreads = NULL;
- }
-
- if (nThreads == 0 || !fGenerate)
- return;
-
- minerThreads = new boost::thread_group();
- for (int i = 0; i < nThreads; i++)
- minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
-}
-
-// Amount compression:
-// * If the amount is 0, output 0
-// * first, divide the amount (in base units) by the largest power of 10 possible; call the exponent e (e is max 9)
-// * if e<9, the last digit of the resulting number cannot be 0; store it as d, and drop it (divide by 10)
-// * call the result n
-// * output 1 + 10*(9*n + d - 1) + e
-// * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9
-// (this is decodable, as d is in [1-9] and e is in [0-9])
-
-uint64 CTxOutCompressor::CompressAmount(uint64 n)
-{
- if (n == 0)
- return 0;
- int e = 0;
- while (((n % 10) == 0) && e < 9) {
- n /= 10;
- e++;
- }
- if (e < 9) {
- int d = (n % 10);
- assert(d >= 1 && d <= 9);
- n /= 10;
- return 1 + (n*9 + d - 1)*10 + e;
- } else {
- return 1 + (n - 1)*10 + 9;
- }
-}
-
-uint64 CTxOutCompressor::DecompressAmount(uint64 x)
-{
- // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9
- if (x == 0)
- return 0;
- x--;
- // x = 10*(9*n + d - 1) + e
- int e = x % 10;
- x /= 10;
- uint64 n = 0;
- if (e < 9) {
- // x = 9*n + d - 1
- int d = (x % 9) + 1;
- x /= 9;
- // x = n
- n = x*10 + d;
- } else {
- n = x+1;
- }
- while (e) {
- n *= 10;
- e--;
- }
- return n;
-}
-
-
class CMainCleanup
{
public:
@@ -4760,9 +4207,6 @@ public:
mapOrphanBlocks.clear();
// orphan transactions
- std::map<uint256, CDataStream*>::iterator it3 = mapOrphanTransactions.begin();
- for (; it3 != mapOrphanTransactions.end(); it3++)
- delete (*it3).second;
mapOrphanTransactions.clear();
}
} instance_of_cmaincleanup;
diff --git a/src/main.h b/src/main.h
index c5e5f2bfe6..ea86a2bcc0 100644
--- a/src/main.h
+++ b/src/main.h
@@ -119,6 +119,8 @@ struct CBlockTemplate;
void RegisterWallet(CWallet* pwalletIn);
/** Unregister a wallet from core */
void UnregisterWallet(CWallet* pwalletIn);
+/** Unregister all wallets from core */
+void UnregisterAllWallets();
/** Push an updated transaction to all registered wallets */
void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false);
@@ -157,16 +159,6 @@ bool ProcessMessages(CNode* pfrom);
bool SendMessages(CNode* pto, bool fSendTrickle);
/** Run an instance of the script checking thread */
void ThreadScriptCheck();
-/** Run the miner threads */
-void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
-/** Generate a new block, without valid proof-of-work */
-CBlockTemplate* CreateNewBlock(CReserveKey& reservekey);
-/** Modify the extranonce in a block */
-void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
-/** Do mining precalculation */
-void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
-/** Check mined block */
-bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
/** Calculate the minimum amount of work a received block needs, without knowing its direct parent */
@@ -183,6 +175,8 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, b
bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew);
/** Find the best known block, and make it the tip of the block chain */
bool ConnectBestBlock(CValidationState &state);
+int64 GetBlockValue(int nHeight, int64 nFees);
+unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock);
void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev);
@@ -316,7 +310,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach
std::vector<CScriptCheck> *pvChecks = NULL);
// Apply the effects of this transaction on the UTXO set represented by view
-bool UpdateCoins(const CTransaction& tx, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash);
+void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash);
// Context-independent validity checks
bool CheckTransaction(const CTransaction& tx, CValidationState& state);
@@ -324,7 +318,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state);
/** Check for standard transaction types
@return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
-bool IsStandardTx(const CTransaction& tx);
+bool IsStandardTx(const CTransaction& tx, std::string& reason);
bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64 nBlockTime = 0);
@@ -581,204 +575,33 @@ public:
};
-class CBlock : public CBlockHeader
-{
-public:
- // network and disk
- std::vector<CTransaction> vtx;
-
- // memory only
- mutable std::vector<uint256> vMerkleTree;
-
- CBlock()
- {
- SetNull();
- }
-
- CBlock(const CBlockHeader &header)
- {
- SetNull();
- *((CBlockHeader*)this) = header;
- }
-
- IMPLEMENT_SERIALIZE
- (
- READWRITE(*(CBlockHeader*)this);
- READWRITE(vtx);
- )
-
- void SetNull()
- {
- CBlockHeader::SetNull();
- vtx.clear();
- vMerkleTree.clear();
- }
-
- CBlockHeader GetBlockHeader() const
- {
- CBlockHeader block;
- block.nVersion = nVersion;
- block.hashPrevBlock = hashPrevBlock;
- block.hashMerkleRoot = hashMerkleRoot;
- block.nTime = nTime;
- block.nBits = nBits;
- block.nNonce = nNonce;
- return block;
- }
-
- uint256 BuildMerkleTree() const
- {
- vMerkleTree.clear();
- BOOST_FOREACH(const CTransaction& tx, vtx)
- vMerkleTree.push_back(tx.GetHash());
- int j = 0;
- for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
- {
- for (int i = 0; i < nSize; i += 2)
- {
- int i2 = std::min(i+1, nSize-1);
- vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]),
- BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2])));
- }
- j += nSize;
- }
- return (vMerkleTree.empty() ? 0 : vMerkleTree.back());
- }
-
- const uint256 &GetTxHash(unsigned int nIndex) const {
- assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first
- assert(nIndex < vtx.size());
- return vMerkleTree[nIndex];
- }
-
- std::vector<uint256> GetMerkleBranch(int nIndex) const
- {
- if (vMerkleTree.empty())
- BuildMerkleTree();
- std::vector<uint256> vMerkleBranch;
- int j = 0;
- for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
- {
- int i = std::min(nIndex^1, nSize-1);
- vMerkleBranch.push_back(vMerkleTree[j+i]);
- nIndex >>= 1;
- j += nSize;
- }
- return vMerkleBranch;
- }
-
- static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
- {
- if (nIndex == -1)
- return 0;
- BOOST_FOREACH(const uint256& otherside, vMerkleBranch)
- {
- if (nIndex & 1)
- hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash));
- else
- hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside));
- nIndex >>= 1;
- }
- return hash;
- }
-
- bool WriteToDisk(CDiskBlockPos &pos)
- {
- // Open history file to append
- CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
- if (!fileout)
- return error("CBlock::WriteToDisk() : OpenBlockFile failed");
-
- // Write index header
- unsigned int nSize = fileout.GetSerializeSize(*this);
- fileout << FLATDATA(Params().MessageStart()) << nSize;
-
- // Write block
- long fileOutPos = ftell(fileout);
- if (fileOutPos < 0)
- return error("CBlock::WriteToDisk() : ftell failed");
- pos.nPos = (unsigned int)fileOutPos;
- fileout << *this;
-
- // Flush stdio buffers and commit to disk before returning
- fflush(fileout);
- if (!IsInitialBlockDownload())
- FileCommit(fileout);
-
- return true;
- }
-
- bool ReadFromDisk(const CDiskBlockPos &pos)
- {
- SetNull();
-
- // Open history file to read
- CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
- if (!filein)
- return error("CBlock::ReadFromDisk() : OpenBlockFile failed");
- // Read block
- try {
- filein >> *this;
- }
- catch (std::exception &e) {
- return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
- }
+/** Functions for disk access for blocks */
+bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos);
+bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos);
+bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex);
- // Check the header
- if (!CheckProofOfWork(GetHash(), nBits))
- return error("CBlock::ReadFromDisk() : errors in block header");
- return true;
- }
+/** Functions for validating blocks and updating the block tree */
+/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
+ * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
+ * will be true if no problems were found. Otherwise, the return value will be false in case
+ * of problems. Note that in any case, coins may be modified. */
+bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL);
+// Apply the effects of this block (with given index) on the UTXO set represented by coins
+bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false);
- void print() const
- {
- printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n",
- GetHash().ToString().c_str(),
- nVersion,
- hashPrevBlock.ToString().c_str(),
- hashMerkleRoot.ToString().c_str(),
- nTime, nBits, nNonce,
- vtx.size());
- for (unsigned int i = 0; i < vtx.size(); i++)
- {
- printf(" ");
- vtx[i].print();
- }
- printf(" vMerkleTree: ");
- for (unsigned int i = 0; i < vMerkleTree.size(); i++)
- printf("%s ", vMerkleTree[i].ToString().c_str());
- printf("\n");
- }
-
-
- /** Undo the effects of this block (with given index) on the UTXO set represented by coins.
- * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
- * will be true if no problems were found. Otherwise, the return value will be false in case
- * of problems. Note that in any case, coins may be modified. */
- bool DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL);
-
- // Apply the effects of this block (with given index) on the UTXO set represented by coins
- bool ConnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false);
-
- // Read a block from disk
- bool ReadFromDisk(const CBlockIndex* pindex);
-
- // Add this block to the block index, and if necessary, switch the active block chain to this
- bool AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos);
-
- // Context-independent validity checks
- bool CheckBlock(CValidationState &state, bool fCheckPOW=true, bool fCheckMerkleRoot=true) const;
-
- // Store block on disk
- // if dbp is provided, the file is known to already reside on disk
- bool AcceptBlock(CValidationState &state, CDiskBlockPos *dbp = NULL);
-};
+// Add this block to the block index, and if necessary, switch the active block chain to this
+bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos);
+// Context-independent validity checks
+bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
+// Store block on disk
+// if dbp is provided, the file is known to already reside on disk
+bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp = NULL);
@@ -1258,8 +1081,8 @@ public:
std::map<uint256, CTransaction> mapTx;
std::map<COutPoint, CInPoint> mapNextTx;
- bool accept(CValidationState &state, CTransaction &tx, bool fLimitFree, bool* pfMissingInputs);
- bool addUnchecked(const uint256& hash, CTransaction &tx);
+ bool accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs);
+ bool addUnchecked(const uint256& hash, const CTransaction &tx);
bool remove(const CTransaction &tx, bool fRecursive = false);
bool removeConflicts(const CTransaction &tx);
void clear();
diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw
index 26d541664e..51830f2342 100644
--- a/src/makefile.linux-mingw
+++ b/src/makefile.linux-mingw
@@ -75,6 +75,7 @@ OBJS= \
obj/keystore.o \
obj/core.o \
obj/main.o \
+ obj/miner.o \
obj/net.o \
obj/protocol.o \
obj/bitcoinrpc.o \
diff --git a/src/makefile.mingw b/src/makefile.mingw
index 3659f52040..464f69b1c9 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -21,7 +21,7 @@ USE_UPNP:=-
USE_IPV6:=1
DEPSDIR?=/usr/local
-BOOST_SUFFIX?=-mgw46-mt-sd-1_52
+BOOST_SUFFIX?=-mgw46-mt-s-1_52
INCLUDEPATHS= \
-I"$(CURDIR)" \
@@ -83,6 +83,7 @@ OBJS= \
obj/keystore.o \
obj/core.o \
obj/main.o \
+ obj/miner.o \
obj/net.o \
obj/protocol.o \
obj/bitcoinrpc.o \
diff --git a/src/makefile.osx b/src/makefile.osx
index 269460c1ba..ee364f5c4a 100644
--- a/src/makefile.osx
+++ b/src/makefile.osx
@@ -86,6 +86,7 @@ OBJS= \
obj/keystore.o \
obj/core.o \
obj/main.o \
+ obj/miner.o \
obj/net.o \
obj/protocol.o \
obj/bitcoinrpc.o \
diff --git a/src/makefile.unix b/src/makefile.unix
index f17de05cb9..e00db09a33 100644
--- a/src/makefile.unix
+++ b/src/makefile.unix
@@ -125,6 +125,7 @@ OBJS= \
obj/keystore.o \
obj/core.o \
obj/main.o \
+ obj/miner.o \
obj/net.o \
obj/protocol.o \
obj/bitcoinrpc.o \
diff --git a/src/miner.cpp b/src/miner.cpp
new file mode 100644
index 0000000000..e50c0b576d
--- /dev/null
+++ b/src/miner.cpp
@@ -0,0 +1,663 @@
+// 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.
+
+#include "core.h"
+#include "wallet.h"
+#include "miner.h"
+#include "main.h"
+
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// BitcoinMiner
+//
+
+int static FormatHashBlocks(void* pbuffer, unsigned int len)
+{
+ unsigned char* pdata = (unsigned char*)pbuffer;
+ unsigned int blocks = 1 + ((len + 8) / 64);
+ unsigned char* pend = pdata + 64 * blocks;
+ memset(pdata + len, 0, 64 * blocks - len);
+ pdata[len] = 0x80;
+ unsigned int bits = len * 8;
+ pend[-1] = (bits >> 0) & 0xff;
+ pend[-2] = (bits >> 8) & 0xff;
+ pend[-3] = (bits >> 16) & 0xff;
+ pend[-4] = (bits >> 24) & 0xff;
+ return blocks;
+}
+
+static const unsigned int pSHA256InitState[8] =
+{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
+
+void SHA256Transform(void* pstate, void* pinput, const void* pinit)
+{
+ SHA256_CTX ctx;
+ unsigned char data[64];
+
+ SHA256_Init(&ctx);
+
+ for (int i = 0; i < 16; i++)
+ ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]);
+
+ for (int i = 0; i < 8; i++)
+ ctx.h[i] = ((uint32_t*)pinit)[i];
+
+ SHA256_Update(&ctx, data, sizeof(data));
+ for (int i = 0; i < 8; i++)
+ ((uint32_t*)pstate)[i] = ctx.h[i];
+}
+
+//
+// ScanHash scans nonces looking for a hash with at least some zero bits.
+// It operates on big endian data. Caller does the byte reversing.
+// All input buffers are 16-byte aligned. nNonce is usually preserved
+// between calls, but periodically or if nNonce is 0xffff0000 or above,
+// the block is rebuilt and nNonce starts over at zero.
+//
+unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone)
+{
+ unsigned int& nNonce = *(unsigned int*)(pdata + 12);
+ for (;;)
+ {
+ // Crypto++ SHA256
+ // Hash pdata using pmidstate as the starting state into
+ // pre-formatted buffer phash1, then hash phash1 into phash
+ nNonce++;
+ SHA256Transform(phash1, pdata, pmidstate);
+ SHA256Transform(phash, phash1, pSHA256InitState);
+
+ // Return the nonce if the hash has at least some zero bits,
+ // caller will check if it has enough to reach the target
+ if (((unsigned short*)phash)[14] == 0)
+ return nNonce;
+
+ // If nothing found after trying for a while, return -1
+ if ((nNonce & 0xffff) == 0)
+ {
+ nHashesDone = 0xffff+1;
+ return (unsigned int) -1;
+ }
+ if ((nNonce & 0xfff) == 0)
+ boost::this_thread::interruption_point();
+ }
+}
+
+// Some explaining would be appreciated
+class COrphan
+{
+public:
+ CTransaction* ptx;
+ set<uint256> setDependsOn;
+ double dPriority;
+ double dFeePerKb;
+
+ COrphan(CTransaction* ptxIn)
+ {
+ ptx = ptxIn;
+ dPriority = dFeePerKb = 0;
+ }
+
+ void print() const
+ {
+ printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n",
+ ptx->GetHash().ToString().c_str(), dPriority, dFeePerKb);
+ BOOST_FOREACH(uint256 hash, setDependsOn)
+ printf(" setDependsOn %s\n", hash.ToString().c_str());
+ }
+};
+
+
+uint64 nLastBlockTx = 0;
+uint64 nLastBlockSize = 0;
+
+// We want to sort transactions by priority and fee, so:
+typedef boost::tuple<double, double, CTransaction*> TxPriority;
+class TxPriorityCompare
+{
+ bool byFee;
+public:
+ TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
+ bool operator()(const TxPriority& a, const TxPriority& b)
+ {
+ if (byFee)
+ {
+ if (a.get<1>() == b.get<1>())
+ return a.get<0>() < b.get<0>();
+ return a.get<1>() < b.get<1>();
+ }
+ else
+ {
+ if (a.get<0>() == b.get<0>())
+ return a.get<1>() < b.get<1>();
+ return a.get<0>() < b.get<0>();
+ }
+ }
+};
+
+CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
+{
+ // Create new block
+ auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
+ if(!pblocktemplate.get())
+ return NULL;
+ CBlock *pblock = &pblocktemplate->block; // pointer for convenience
+
+ // Create coinbase tx
+ CTransaction txNew;
+ txNew.vin.resize(1);
+ txNew.vin[0].prevout.SetNull();
+ txNew.vout.resize(1);
+ CPubKey pubkey;
+ if (!reservekey.GetReservedKey(pubkey))
+ return NULL;
+ txNew.vout[0].scriptPubKey << pubkey << OP_CHECKSIG;
+
+ // Add our coinbase tx as first transaction
+ pblock->vtx.push_back(txNew);
+ pblocktemplate->vTxFees.push_back(-1); // updated at end
+ pblocktemplate->vTxSigOps.push_back(-1); // updated at end
+
+ // Largest block you're willing to create:
+ unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2);
+ // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
+ nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize));
+
+ // How much of the block should be dedicated to high-priority transactions,
+ // included regardless of the fees they pay
+ unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE);
+ nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
+
+ // Minimum block size you want to create; block will be filled with free transactions
+ // until there are no more or the block reaches this size:
+ unsigned int nBlockMinSize = GetArg("-blockminsize", 0);
+ nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
+
+ // Collect memory pool transactions into the block
+ int64 nFees = 0;
+ {
+ LOCK2(cs_main, mempool.cs);
+ CBlockIndex* pindexPrev = pindexBest;
+ CCoinsViewCache view(*pcoinsTip, true);
+
+ // Priority order to process transactions
+ list<COrphan> vOrphan; // list memory doesn't move
+ map<uint256, vector<COrphan*> > mapDependers;
+ bool fPrintPriority = GetBoolArg("-printpriority", false);
+
+ // This vector will be sorted into a priority queue:
+ vector<TxPriority> vecPriority;
+ vecPriority.reserve(mempool.mapTx.size());
+ for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
+ {
+ CTransaction& tx = (*mi).second;
+ if (tx.IsCoinBase() || !IsFinalTx(tx))
+ continue;
+
+ COrphan* porphan = NULL;
+ double dPriority = 0;
+ int64 nTotalIn = 0;
+ bool fMissingInputs = false;
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ // Read prev transaction
+ if (!view.HaveCoins(txin.prevout.hash))
+ {
+ // This should never happen; all transactions in the memory
+ // pool should connect to either transactions in the chain
+ // or other transactions in the memory pool.
+ if (!mempool.mapTx.count(txin.prevout.hash))
+ {
+ printf("ERROR: mempool transaction missing input\n");
+ if (fDebug) assert("mempool transaction missing input" == 0);
+ fMissingInputs = true;
+ if (porphan)
+ vOrphan.pop_back();
+ break;
+ }
+
+ // Has to wait for dependencies
+ if (!porphan)
+ {
+ // Use list for automatic deletion
+ vOrphan.push_back(COrphan(&tx));
+ porphan = &vOrphan.back();
+ }
+ mapDependers[txin.prevout.hash].push_back(porphan);
+ porphan->setDependsOn.insert(txin.prevout.hash);
+ nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue;
+ continue;
+ }
+ const CCoins &coins = view.GetCoins(txin.prevout.hash);
+
+ int64 nValueIn = coins.vout[txin.prevout.n].nValue;
+ nTotalIn += nValueIn;
+
+ int nConf = pindexPrev->nHeight - coins.nHeight + 1;
+
+ dPriority += (double)nValueIn * nConf;
+ }
+ if (fMissingInputs) continue;
+
+ // Priority is sum(valuein * age) / txsize
+ unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+ dPriority /= nTxSize;
+
+ // This is a more accurate fee-per-kilobyte than is used by the client code, because the
+ // client code rounds up the size to the nearest 1K. That's good, because it gives an
+ // incentive to create smaller transactions.
+ double dFeePerKb = double(nTotalIn-GetValueOut(tx)) / (double(nTxSize)/1000.0);
+
+ if (porphan)
+ {
+ porphan->dPriority = dPriority;
+ porphan->dFeePerKb = dFeePerKb;
+ }
+ else
+ vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second));
+ }
+
+ // Collect transactions into block
+ uint64 nBlockSize = 1000;
+ uint64 nBlockTx = 0;
+ int nBlockSigOps = 100;
+ bool fSortedByFee = (nBlockPrioritySize <= 0);
+
+ TxPriorityCompare comparer(fSortedByFee);
+ std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
+
+ while (!vecPriority.empty())
+ {
+ // Take highest priority transaction off the priority queue:
+ double dPriority = vecPriority.front().get<0>();
+ double dFeePerKb = vecPriority.front().get<1>();
+ CTransaction& tx = *(vecPriority.front().get<2>());
+
+ std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
+ vecPriority.pop_back();
+
+ // Size limits
+ unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+ if (nBlockSize + nTxSize >= nBlockMaxSize)
+ continue;
+
+ // Legacy limits on sigOps:
+ unsigned int nTxSigOps = GetLegacySigOpCount(tx);
+ if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
+ continue;
+
+ // Skip free transactions if we're past the minimum block size:
+ if (fSortedByFee && (dFeePerKb < CTransaction::nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
+ continue;
+
+ // Prioritize by fee once past the priority size or we run out of high-priority
+ // transactions:
+ if (!fSortedByFee &&
+ ((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
+ {
+ fSortedByFee = true;
+ comparer = TxPriorityCompare(fSortedByFee);
+ std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
+ }
+
+ if (!view.HaveInputs(tx))
+ continue;
+
+ int64 nTxFees = view.GetValueIn(tx)-GetValueOut(tx);
+
+ nTxSigOps += GetP2SHSigOpCount(tx, view);
+ if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
+ continue;
+
+ CValidationState state;
+ if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH))
+ continue;
+
+ CTxUndo txundo;
+ uint256 hash = tx.GetHash();
+ UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1, hash);
+
+ // Added
+ pblock->vtx.push_back(tx);
+ pblocktemplate->vTxFees.push_back(nTxFees);
+ pblocktemplate->vTxSigOps.push_back(nTxSigOps);
+ nBlockSize += nTxSize;
+ ++nBlockTx;
+ nBlockSigOps += nTxSigOps;
+ nFees += nTxFees;
+
+ if (fPrintPriority)
+ {
+ printf("priority %.1f feeperkb %.1f txid %s\n",
+ dPriority, dFeePerKb, tx.GetHash().ToString().c_str());
+ }
+
+ // Add transactions that depend on this one to the priority queue
+ if (mapDependers.count(hash))
+ {
+ BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
+ {
+ if (!porphan->setDependsOn.empty())
+ {
+ porphan->setDependsOn.erase(hash);
+ if (porphan->setDependsOn.empty())
+ {
+ vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx));
+ std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
+ }
+ }
+ }
+ }
+ }
+
+ nLastBlockTx = nBlockTx;
+ nLastBlockSize = nBlockSize;
+ printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize);
+
+ pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
+ pblocktemplate->vTxFees[0] = -nFees;
+
+ // Fill in header
+ pblock->hashPrevBlock = pindexPrev->GetBlockHash();
+ UpdateTime(*pblock, pindexPrev);
+ pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
+ pblock->nNonce = 0;
+ pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0;
+ pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
+
+ CBlockIndex indexDummy(*pblock);
+ indexDummy.pprev = pindexPrev;
+ indexDummy.nHeight = pindexPrev->nHeight + 1;
+ CCoinsViewCache viewNew(*pcoinsTip, true);
+ CValidationState state;
+ if (!ConnectBlock(*pblock, state, &indexDummy, viewNew, true))
+ throw std::runtime_error("CreateNewBlock() : ConnectBlock failed");
+ }
+
+ return pblocktemplate.release();
+}
+
+
+void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
+{
+ // Update nExtraNonce
+ static uint256 hashPrevBlock;
+ if (hashPrevBlock != pblock->hashPrevBlock)
+ {
+ nExtraNonce = 0;
+ hashPrevBlock = pblock->hashPrevBlock;
+ }
+ ++nExtraNonce;
+ unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
+ pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS;
+ assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100);
+
+ pblock->hashMerkleRoot = pblock->BuildMerkleTree();
+}
+
+
+void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1)
+{
+ //
+ // Pre-build hash buffers
+ //
+ struct
+ {
+ struct unnamed2
+ {
+ int nVersion;
+ uint256 hashPrevBlock;
+ uint256 hashMerkleRoot;
+ unsigned int nTime;
+ unsigned int nBits;
+ unsigned int nNonce;
+ }
+ block;
+ unsigned char pchPadding0[64];
+ uint256 hash1;
+ unsigned char pchPadding1[64];
+ }
+ tmp;
+ memset(&tmp, 0, sizeof(tmp));
+
+ tmp.block.nVersion = pblock->nVersion;
+ tmp.block.hashPrevBlock = pblock->hashPrevBlock;
+ tmp.block.hashMerkleRoot = pblock->hashMerkleRoot;
+ tmp.block.nTime = pblock->nTime;
+ tmp.block.nBits = pblock->nBits;
+ tmp.block.nNonce = pblock->nNonce;
+
+ FormatHashBlocks(&tmp.block, sizeof(tmp.block));
+ FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
+
+ // Byte swap all the input buffer
+ for (unsigned int i = 0; i < sizeof(tmp)/4; i++)
+ ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
+
+ // Precalc the first half of the first hash, which stays constant
+ SHA256Transform(pmidstate, &tmp.block, pSHA256InitState);
+
+ memcpy(pdata, &tmp.block, 128);
+ memcpy(phash1, &tmp.hash1, 64);
+}
+
+
+bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
+{
+ uint256 hash = pblock->GetHash();
+ uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
+
+ if (hash > hashTarget)
+ return false;
+
+ //// debug print
+ printf("BitcoinMiner:\n");
+ printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
+ pblock->print();
+ printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
+
+ // Found a solution
+ {
+ LOCK(cs_main);
+ if (pblock->hashPrevBlock != hashBestChain)
+ return error("BitcoinMiner : generated block is stale");
+
+ // Remove key from key pool
+ reservekey.KeepKey();
+
+ // Track how many getdata requests this block gets
+ {
+ LOCK(wallet.cs_wallet);
+ wallet.mapRequestCount[pblock->GetHash()] = 0;
+ }
+
+ // Process this block the same as if we had received it from another node
+ CValidationState state;
+ if (!ProcessBlock(state, NULL, pblock))
+ return error("BitcoinMiner : ProcessBlock, block not accepted");
+ }
+
+ return true;
+}
+
+void static BitcoinMiner(CWallet *pwallet)
+{
+ printf("BitcoinMiner started\n");
+ SetThreadPriority(THREAD_PRIORITY_LOWEST);
+ RenameThread("bitcoin-miner");
+
+ // Each thread has its own key and counter
+ CReserveKey reservekey(pwallet);
+ unsigned int nExtraNonce = 0;
+
+ try { while (true) {
+ if (Params().NetworkID() != CChainParams::REGTEST) {
+ // Busy-wait for the network to come online so we don't waste time mining
+ // on an obsolete chain. In regtest mode we expect to fly solo.
+ while (vNodes.empty())
+ MilliSleep(1000);
+ }
+
+ //
+ // Create new block
+ //
+ unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
+ CBlockIndex* pindexPrev = pindexBest;
+
+ auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(reservekey));
+ if (!pblocktemplate.get())
+ return;
+ CBlock *pblock = &pblocktemplate->block;
+ IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
+
+ printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(),
+ ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
+
+ //
+ // Pre-build hash buffers
+ //
+ char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf);
+ char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf);
+ char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf);
+
+ FormatHashBuffers(pblock, pmidstate, pdata, phash1);
+
+ unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4);
+ unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8);
+ unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12);
+
+
+ //
+ // Search
+ //
+ int64 nStart = GetTime();
+ uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
+ uint256 hashbuf[2];
+ uint256& hash = *alignup<16>(hashbuf);
+ while (true)
+ {
+ unsigned int nHashesDone = 0;
+ unsigned int nNonceFound;
+
+ // Crypto++ SHA256
+ nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1,
+ (char*)&hash, nHashesDone);
+
+ // Check if something found
+ if (nNonceFound != (unsigned int) -1)
+ {
+ for (unsigned int i = 0; i < sizeof(hash)/4; i++)
+ ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]);
+
+ if (hash <= hashTarget)
+ {
+ // Found a solution
+ pblock->nNonce = ByteReverse(nNonceFound);
+ assert(hash == pblock->GetHash());
+
+ SetThreadPriority(THREAD_PRIORITY_NORMAL);
+ CheckWork(pblock, *pwallet, reservekey);
+ SetThreadPriority(THREAD_PRIORITY_LOWEST);
+
+ // In regression test mode, stop mining after a block is found. This
+ // allows developers to controllably generate a block on demand.
+ if (Params().NetworkID() == CChainParams::REGTEST)
+ throw boost::thread_interrupted();
+
+ break;
+ }
+ }
+
+ // Meter hashes/sec
+ static int64 nHashCounter;
+ if (nHPSTimerStart == 0)
+ {
+ nHPSTimerStart = GetTimeMillis();
+ nHashCounter = 0;
+ }
+ else
+ nHashCounter += nHashesDone;
+ if (GetTimeMillis() - nHPSTimerStart > 4000)
+ {
+ static CCriticalSection cs;
+ {
+ LOCK(cs);
+ if (GetTimeMillis() - nHPSTimerStart > 4000)
+ {
+ dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart);
+ nHPSTimerStart = GetTimeMillis();
+ nHashCounter = 0;
+ static int64 nLogTime;
+ if (GetTime() - nLogTime > 30 * 60)
+ {
+ nLogTime = GetTime();
+ printf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0);
+ }
+ }
+ }
+ }
+
+ // Check for stop or if block needs to be rebuilt
+ boost::this_thread::interruption_point();
+ if (vNodes.empty() && Params().NetworkID() != CChainParams::REGTEST)
+ break;
+ if (nBlockNonce >= 0xffff0000)
+ break;
+ if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)
+ break;
+ if (pindexPrev != pindexBest)
+ break;
+
+ // Update nTime every few seconds
+ UpdateTime(*pblock, pindexPrev);
+ nBlockTime = ByteReverse(pblock->nTime);
+ if (TestNet())
+ {
+ // Changing pblock->nTime can change work required on testnet:
+ nBlockBits = ByteReverse(pblock->nBits);
+ hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
+ }
+ }
+ } }
+ catch (boost::thread_interrupted)
+ {
+ printf("BitcoinMiner terminated\n");
+ throw;
+ }
+}
+
+void GenerateBitcoins(bool fGenerate, CWallet* pwallet)
+{
+ static boost::thread_group* minerThreads = NULL;
+
+ int nThreads = GetArg("-genproclimit", -1);
+ if (nThreads < 0) {
+ if (Params().NetworkID() == CChainParams::REGTEST)
+ nThreads = 1;
+ else
+ nThreads = boost::thread::hardware_concurrency();
+ }
+
+ if (minerThreads != NULL)
+ {
+ minerThreads->interrupt_all();
+ delete minerThreads;
+ minerThreads = NULL;
+ }
+
+ if (nThreads == 0 || !fGenerate)
+ return;
+
+ minerThreads = new boost::thread_group();
+ for (int i = 0; i < nThreads; i++)
+ minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
+}
+
+
+
diff --git a/src/miner.h b/src/miner.h
new file mode 100644
index 0000000000..51d6a2e3ea
--- /dev/null
+++ b/src/miner.h
@@ -0,0 +1,24 @@
+// 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_MINER_H
+#define BITCOIN_MINER_H
+
+#include "core.h"
+#include "wallet.h"
+
+/** Run the miner threads */
+void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
+/** Generate a new block, without valid proof-of-work */
+CBlockTemplate* CreateNewBlock(CReserveKey& reservekey);
+/** Modify the extranonce in a block */
+void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
+/** Do mining precalculation */
+void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
+/** Check mined block */
+bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
+/** Base sha256 mining transform */
+void SHA256Transform(void* pstate, void* pinput, const void* pinit);
+
+#endif // BITCOIN_MINER_H
diff --git a/src/net.cpp b/src/net.cpp
index 0adf26ef0d..4ee1895a80 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -22,6 +22,9 @@
#include <miniupnpc/upnperrors.h>
#endif
+// Dump addresses to peers.dat every 15 minutes (900s)
+#define DUMP_ADDRESSES_INTERVAL 900
+
using namespace std;
using namespace boost;
@@ -126,7 +129,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer)
bool RecvLine(SOCKET hSocket, string& strLine)
{
strLine = "";
- loop
+ while (true)
{
char c;
int nBytes = recv(hSocket, &c, 1, 0);
@@ -298,7 +301,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha
{
if (strLine.empty()) // HTTP response is separated from headers by blank line
{
- loop
+ while (true)
{
if (!RecvLine(hSocket, strLine))
{
@@ -738,7 +741,7 @@ static list<CNode*> vNodesDisconnected;
void ThreadSocketHandler()
{
unsigned int nPrevNodeCount = 0;
- loop
+ while (true)
{
//
// Disconnect nodes
@@ -1102,7 +1105,7 @@ void ThreadMapPort()
string strDesc = "Bitcoin " + FormatFullVersion();
try {
- loop {
+ while (true) {
#ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
@@ -1265,7 +1268,7 @@ void ThreadOpenConnections()
// Initiate network connections
int64 nStart = GetTime();
- loop
+ while (true)
{
ProcessOneShot();
@@ -1306,7 +1309,7 @@ void ThreadOpenConnections()
int64 nANow = GetAdjustedTime();
int nTries = 0;
- loop
+ while (true)
{
// use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections)
CAddress addr = addrman.Select(10 + min(nOutbound,8)*10);
@@ -1595,8 +1598,12 @@ bool BindListenPort(const CService &addrBind, string& strError)
// and enable it by default or not. Try to enable it, if possible.
if (addrBind.IsIPv6()) {
#ifdef IPV6_V6ONLY
+#ifdef WIN32
+ setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int));
+#else
setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int));
#endif
+#endif
#ifdef WIN32
int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */;
int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */;
@@ -1730,7 +1737,7 @@ void StartNode(boost::thread_group& threadGroup)
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
// Dump network addresses
- threadGroup.create_thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 10000));
+ threadGroup.create_thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, DUMP_ADDRESSES_INTERVAL * 1000));
}
bool StopNode()
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 76e88b36a7..3b98334696 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -2,8 +2,6 @@
* W.J. van der Laan 2011-2012
*/
-#include <QApplication>
-
#include "bitcoingui.h"
#include "clientmodel.h"
#include "walletmodel.h"
@@ -15,7 +13,9 @@
#include "ui_interface.h"
#include "paymentserver.h"
#include "splashscreen.h"
+#include "intro.h"
+#include <QApplication>
#include <QMessageBox>
#if QT_VERSION < 0x050000
#include <QTextCodec>
@@ -24,6 +24,7 @@
#include <QTimer>
#include <QTranslator>
#include <QLibraryInfo>
+#include <QSettings>
#if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED)
#define _BITCOIN_QT_PLUGINS_INCLUDED
@@ -110,6 +111,46 @@ static void handleRunawayException(std::exception *e)
exit(1);
}
+/** Set up translations */
+static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator)
+{
+ QSettings settings;
+
+ // Get desired locale (e.g. "de_DE")
+ // 1) System default language
+ QString lang_territory = QLocale::system().name();
+ // 2) Language from QSettings
+ QString lang_territory_qsettings = settings.value("language", "").toString();
+ if(!lang_territory_qsettings.isEmpty())
+ lang_territory = lang_territory_qsettings;
+ // 3) -lang command line argument
+ lang_territory = QString::fromStdString(GetArg("-lang", lang_territory.toStdString()));
+
+ // Convert to "de" only by truncating "_DE"
+ QString lang = lang_territory;
+ lang.truncate(lang_territory.lastIndexOf('_'));
+
+ // Load language files for configured locale:
+ // - First load the translator for the base language, without territory
+ // - Then load the more specific locale translator
+
+ // Load e.g. qt_de.qm
+ if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
+ QApplication::installTranslator(&qtTranslatorBase);
+
+ // Load e.g. qt_de_DE.qm
+ if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
+ QApplication::installTranslator(&qtTranslator);
+
+ // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc)
+ if (translatorBase.load(lang, ":/translations/"))
+ QApplication::installTranslator(&translatorBase);
+
+ // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc)
+ if (translator.load(lang_territory, ":/translations/"))
+ QApplication::installTranslator(&translator);
+}
+
#ifndef BITCOIN_QT_TEST
int main(int argc, char *argv[])
{
@@ -130,6 +171,22 @@ int main(int argc, char *argv[])
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType< bool* >();
+ // Application identification (must be set before OptionsModel is initialized,
+ // as it is used to locate QSettings)
+ QApplication::setOrganizationName("Bitcoin");
+ QApplication::setOrganizationDomain("bitcoin.org");
+ if (GetBoolArg("-testnet", false)) // Separate UI settings for testnet
+ QApplication::setApplicationName("Bitcoin-Qt-testnet");
+ else
+ QApplication::setApplicationName("Bitcoin-Qt");
+
+ // Now that QSettings are accessible, initialize translations
+ QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
+ initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
+
+ // User language is set up: pick a data directory
+ Intro::pickDataDirectory();
+
// Do this early as we don't want to bother initializing if we are just calling IPC
// ... but do it after creating app, so QCoreApplication::arguments is initialized:
if (PaymentServer::ipcSendCommandLine())
@@ -142,53 +199,15 @@ int main(int argc, char *argv[])
// ... then bitcoin.conf:
if (!boost::filesystem::is_directory(GetDataDir(false)))
{
- // This message can not be translated, as translation is not initialized yet
- // (which not yet possible because lang=XX can be overridden in bitcoin.conf in the data directory)
- QMessageBox::critical(0, "Bitcoin",
- QString("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"])));
+ QMessageBox::critical(0, QObject::tr("Bitcoin"),
+ QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(mapArgs["-datadir"])));
return 1;
}
ReadConfigFile(mapArgs, mapMultiArgs);
- // Application identification (must be set before OptionsModel is initialized,
- // as it is used to locate QSettings)
- QApplication::setOrganizationName("Bitcoin");
- QApplication::setOrganizationDomain("bitcoin.org");
- if (GetBoolArg("-testnet", false)) // Separate UI settings for testnet
- QApplication::setApplicationName("Bitcoin-Qt-testnet");
- else
- QApplication::setApplicationName("Bitcoin-Qt");
-
// ... then GUI settings:
OptionsModel optionsModel;
- // Get desired locale (e.g. "de_DE") from command line or use system locale
- QString lang_territory = QString::fromStdString(GetArg("-lang", QLocale::system().name().toStdString()));
- QString lang = lang_territory;
- // Convert to "de" only by truncating "_DE"
- lang.truncate(lang_territory.lastIndexOf('_'));
-
- QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
- // Load language files for configured locale:
- // - First load the translator for the base language, without territory
- // - Then load the more specific locale translator
-
- // Load e.g. qt_de.qm
- if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
- app.installTranslator(&qtTranslatorBase);
-
- // Load e.g. qt_de_DE.qm
- if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
- app.installTranslator(&qtTranslator);
-
- // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in bitcoin.qrc)
- if (translatorBase.load(lang, ":/translations/"))
- app.installTranslator(&translatorBase);
-
- // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in bitcoin.qrc)
- if (translator.load(lang_territory, ":/translations/"))
- app.installTranslator(&translator);
-
// Subscribe to global signals from core
uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox);
uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee);
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index b12e296f99..eeb6fe89bb 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -4,10 +4,11 @@
#include "bitcoinunits.h"
#include "guiconstants.h"
+#include <QApplication>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QDoubleSpinBox>
-#include <QApplication>
+
#include <qmath.h> // for qPow()
BitcoinAmountField::BitcoinAmountField(QWidget *parent):
@@ -60,7 +61,9 @@ bool BitcoinAmountField::validate()
bool valid = true;
if (amount->value() == 0.0)
valid = false;
- if (valid && !BitcoinUnits::parse(currentUnit, text(), 0))
+ else if (!BitcoinUnits::parse(currentUnit, text(), 0))
+ valid = false;
+ else if (amount->value() > BitcoinUnits::maxAmount(currentUnit))
valid = false;
setValid(valid);
@@ -115,7 +118,7 @@ qint64 BitcoinAmountField::value(bool *valid_out) const
{
qint64 val_out = 0;
bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out);
- if(valid_out)
+ if (valid_out)
{
*valid_out = valid;
}
@@ -145,12 +148,12 @@ void BitcoinAmountField::unitChanged(int idx)
amount->setDecimals(BitcoinUnits::decimals(currentUnit));
amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals()));
- if(currentUnit == BitcoinUnits::uBTC)
+ if (currentUnit == BitcoinUnits::uBTC)
amount->setSingleStep(0.01);
else
amount->setSingleStep(0.001);
- if(valid)
+ if (valid)
{
// If value was valid, re-place it in the widget with the new unit
setValue(currentValue);
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 190da6caf8..8ec2f03fad 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -5,8 +5,6 @@
* The Bitcoin Developers 2011-2012
*/
-#include <QApplication>
-
#include "bitcoingui.h"
#include "transactiontablemodel.h"
@@ -30,6 +28,7 @@
#include "macdockiconhandler.h"
#endif
+#include <QApplication>
#include <QMenuBar>
#include <QMenu>
#include <QIcon>
@@ -49,8 +48,6 @@
#endif
#include <QMimeData>
#include <QStyle>
-#include <QSettings>
-#include <QDesktopWidget>
#include <QListWidget>
#include <iostream>
@@ -68,7 +65,7 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
rpcConsole(0),
prevBlocks(0)
{
- restoreWindowGeometry();
+ GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this);
#ifndef Q_OS_MAC
if (!fIsTestnet)
@@ -166,7 +163,7 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
BitcoinGUI::~BitcoinGUI()
{
- saveWindowGeometry();
+ GUIUtil::saveWindowGeometry("nWindow", this);
if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
trayIcon->hide();
#ifdef Q_OS_MAC
@@ -425,28 +422,6 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
}
#endif
-void BitcoinGUI::saveWindowGeometry()
-{
- QSettings settings;
- settings.setValue("nWindowPos", pos());
- settings.setValue("nWindowSize", size());
-}
-
-void BitcoinGUI::restoreWindowGeometry()
-{
- QSettings settings;
- QPoint pos = settings.value("nWindowPos").toPoint();
- QSize size = settings.value("nWindowSize", QSize(850, 550)).toSize();
- if (!pos.x() && !pos.y())
- {
- QRect screen = QApplication::desktop()->screenGeometry();
- pos.setX((screen.width()-size.width())/2);
- pos.setY((screen.height()-size.height())/2);
- }
- resize(size);
- move(pos);
-}
-
void BitcoinGUI::optionsClicked()
{
if(!clientModel || !clientModel->getOptionsModel())
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 685ce8b430..6b9161539c 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -122,10 +122,6 @@ private:
void createTrayIcon(bool fIsTestnet);
/** Create system tray menu (or setup the dock menu) */
void createTrayIconMenu();
- /** Save window size and position */
- void saveWindowGeometry();
- /** Restore window size and position */
- void restoreWindowGeometry();
public slots:
/** Set number of connections shown in the UI */
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index 1afce2eb7c..fc0e53887a 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -33,6 +33,10 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot obtain a lock on data directory %s. Bitcoin is probably already "
"running."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Enter regression test mode, which uses a special chain in which blocks can "
+"be solved instantly. This is intended for regression testing tools and app "
+"development."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error: The transaction was rejected! This might happen if some of the coins "
"in your wallet were already spent, such as if you used a copy of wallet.dat "
"and coins were spent in the copy but not marked as spent here."),
@@ -133,6 +137,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"),
QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 288, 0 = all)"),
QT_TRANSLATE_NOOP("bitcoin-core", "How thorough the block verification is (0-4, default: 3)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Information"),
QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"),
@@ -181,6 +186,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify configuration file (default: bitcoin.
QT_TRANSLATE_NOOP("bitcoin-core", "Specify connection timeout in milliseconds (default: 5000)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: bitcoind.pid)"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"),
QT_TRANSLATE_NOOP("bitcoin-core", "System error: "),
QT_TRANSLATE_NOOP("bitcoin-core", "This help message"),
@@ -202,6 +208,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network"),
QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Wallet %s resides outside data directory %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Bitcoin to complete"),
QT_TRANSLATE_NOOP("bitcoin-core", "Warning"),
QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade required!"),
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index d4715abaec..ae9791123d 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -63,6 +63,17 @@ qint64 BitcoinUnits::factor(int unit)
}
}
+qint64 BitcoinUnits::maxAmount(int unit)
+{
+ switch(unit)
+ {
+ case BTC: return Q_INT64_C(21000000);
+ case mBTC: return Q_INT64_C(21000000000);
+ case uBTC: return Q_INT64_C(21000000000000);
+ default: return 0;
+ }
+}
+
int BitcoinUnits::amountDigits(int unit)
{
switch(unit)
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index 6e96cef59d..f6fdf6c7be 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -38,6 +38,8 @@ public:
static QString description(int unit);
//! Number of Satoshis (1e-8) per unit
static qint64 factor(int unit);
+ //! Max amount per unit
+ static qint64 maxAmount(int unit);
//! Number of amount digits (to represent max number of coins)
static int amountDigits(int unit);
//! Number of decimals left
diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui
new file mode 100644
index 0000000000..0f6ae5a7d0
--- /dev/null
+++ b/src/qt/forms/intro.ui
@@ -0,0 +1,266 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Intro</class>
+ <widget class="QDialog" name="Intro">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>674</width>
+ <height>363</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Welcome</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="styleSheet">
+ <string notr="true">QLabel { font-style:italic; }</string>
+ </property>
+ <property name="text">
+ <string>Welcome to Bitcoin-Qt.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Minimum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>15</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="sizeWarningLabel">
+ <property name="text">
+ <string>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="dataDirDefault">
+ <property name="text">
+ <string>Use the default data directory</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="dataDirCustom">
+ <property name="text">
+ <string>Use a custom data directory:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>60</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="dataDirectory"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="ellipsisButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>30</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string notr="true">…</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="freeSpace">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="errorMessage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>Intro</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>Intro</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 88a6e7226e..32131bc39d 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -1,5 +1,3 @@
-#include <QApplication>
-
#include "guiutil.h"
#include "bitcoinaddressvalidator.h"
@@ -9,6 +7,7 @@
#include "util.h"
#include "init.h"
+#include <QApplication>
#include <QDateTime>
#include <QDoubleValidator>
#include <QFont>
@@ -24,6 +23,8 @@
#include <QFileDialog>
#include <QDesktopServices>
#include <QThread>
+#include <QSettings>
+#include <QDesktopWidget>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
@@ -487,6 +488,29 @@ bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
#endif
+void saveWindowGeometry(const QString& strSetting, QWidget *parent)
+{
+ QSettings settings;
+ settings.setValue(strSetting + "Pos", parent->pos());
+ settings.setValue(strSetting + "Size", parent->size());
+}
+
+void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent)
+{
+ QSettings settings;
+ QPoint pos = settings.value(strSetting + "Pos").toPoint();
+ QSize size = settings.value(strSetting + "Size", defaultSize).toSize();
+
+ if (!pos.x() && !pos.y()) {
+ QRect screen = QApplication::desktop()->screenGeometry();
+ pos.setX((screen.width() - size.width()) / 2);
+ pos.setY((screen.height() - size.height()) / 2);
+ }
+
+ parent->resize(size);
+ parent->move(pos);
+}
+
HelpMessageBox::HelpMessageBox(QWidget *parent) :
QMessageBox(parent)
{
@@ -500,7 +524,8 @@ HelpMessageBox::HelpMessageBox(QWidget *parent) :
uiOptions = tr("UI options") + ":\n" +
" -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
" -min " + tr("Start minimized") + "\n" +
- " -splash " + tr("Show splash screen on startup (default: 1)") + "\n";
+ " -splash " + tr("Show splash screen on startup (default: 1)") + "\n" +
+ " -choosedatadir " + tr("Choose data directory on startup (default: 0)") + "\n";
setWindowTitle(tr("Bitcoin-Qt"));
setTextFormat(Qt::PlainText);
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index e2c7d18aa2..ca3e7fe91d 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -96,6 +96,11 @@ namespace GUIUtil
bool GetStartOnSystemStartup();
bool SetStartOnSystemStartup(bool fAutoStart);
+ /** Save window size and position */
+ void saveWindowGeometry(const QString& strSetting, QWidget *parent);
+ /** Restore window size and position */
+ void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent);
+
/** Help message for Bitcoin-Qt, shown with --help. */
class HelpMessageBox : public QMessageBox
{
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
new file mode 100644
index 0000000000..99db141c94
--- /dev/null
+++ b/src/qt/intro.cpp
@@ -0,0 +1,278 @@
+#include "intro.h"
+#include "ui_intro.h"
+#include "util.h"
+
+#include <QFileDialog>
+#include <QSettings>
+#include <QMessageBox>
+
+#include <boost/filesystem.hpp>
+
+/* Minimum free space (in bytes) needed for data directory */
+static const uint64 GB_BYTES = 1000000000LL;
+static const uint64 BLOCK_CHAIN_SIZE = 10LL * GB_BYTES;
+
+/* Check free space asynchronously to prevent hanging the UI thread.
+
+ Up to one request to check a path is in flight to this thread; when the check()
+ function runs, the current path is requested from the associated Intro object.
+ The reply is sent back through a signal.
+
+ This ensures that no queue of checking requests is built up while the user is
+ still entering the path, and that always the most recently entered path is checked as
+ soon as the thread becomes available.
+*/
+class FreespaceChecker : public QObject
+{
+ Q_OBJECT
+
+public:
+ FreespaceChecker(Intro *intro);
+
+ enum Status {
+ ST_OK,
+ ST_ERROR
+ };
+
+public slots:
+ void check();
+
+signals:
+ void reply(int status, const QString &message, quint64 available);
+
+private:
+ Intro *intro;
+};
+
+#include "intro.moc"
+
+FreespaceChecker::FreespaceChecker(Intro *intro)
+{
+ this->intro = intro;
+}
+
+void FreespaceChecker::check()
+{
+ namespace fs = boost::filesystem;
+ QString dataDirStr = intro->getPathToCheck();
+ fs::path dataDir = fs::path(dataDirStr.toStdString());
+ uint64 freeBytesAvailable = 0;
+ int replyStatus = ST_OK;
+ QString replyMessage = tr("A new data directory will be created.");
+
+ /* Find first parent that exists, so that fs::space does not fail */
+ fs::path parentDir = dataDir;
+ fs::path parentDirOld = fs::path();
+ while(parentDir.has_parent_path() && !fs::exists(parentDir))
+ {
+ parentDir = parentDir.parent_path();
+
+ /* Check if we make any progress, break if not to prevent an infinite loop here */
+ if (parentDirOld == parentDir)
+ break;
+
+ parentDirOld = parentDir;
+ }
+
+ try {
+ freeBytesAvailable = fs::space(parentDir).available;
+ if(fs::exists(dataDir))
+ {
+ if(fs::is_directory(dataDir))
+ {
+ QString separator = "<code>" + QDir::toNativeSeparators("/") + tr("name") + "</code>";
+ replyStatus = ST_OK;
+ replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
+ } else {
+ replyStatus = ST_ERROR;
+ replyMessage = tr("Path already exists, and is not a directory.");
+ }
+ }
+ } catch(fs::filesystem_error &e)
+ {
+ /* Parent directory does not exist or is not accessible */
+ replyStatus = ST_ERROR;
+ replyMessage = tr("Cannot create data directory here.");
+ }
+ emit reply(replyStatus, replyMessage, freeBytesAvailable);
+}
+
+
+Intro::Intro(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::Intro),
+ thread(0),
+ signalled(false)
+{
+ ui->setupUi(this);
+ ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(BLOCK_CHAIN_SIZE/GB_BYTES));
+ startThread();
+}
+
+Intro::~Intro()
+{
+ delete ui;
+ /* Ensure thread is finished before it is deleted */
+ emit stopThread();
+ thread->wait();
+}
+
+QString Intro::getDataDirectory()
+{
+ return ui->dataDirectory->text();
+}
+
+void Intro::setDataDirectory(const QString &dataDir)
+{
+ ui->dataDirectory->setText(dataDir);
+ if(dataDir == getDefaultDataDirectory())
+ {
+ ui->dataDirDefault->setChecked(true);
+ ui->dataDirectory->setEnabled(false);
+ ui->ellipsisButton->setEnabled(false);
+ } else {
+ ui->dataDirCustom->setChecked(true);
+ ui->dataDirectory->setEnabled(true);
+ ui->ellipsisButton->setEnabled(true);
+ }
+}
+
+QString Intro::getDefaultDataDirectory()
+{
+ return QString::fromStdString(GetDefaultDataDir().string());
+}
+
+void Intro::pickDataDirectory()
+{
+ namespace fs = boost::filesystem;;
+ QSettings settings;
+ /* If data directory provided on command line, no need to look at settings
+ or show a picking dialog */
+ if(!GetArg("-datadir", "").empty())
+ return;
+ /* 1) Default data directory for operating system */
+ QString dataDir = getDefaultDataDirectory();
+ /* 2) Allow QSettings to override default dir */
+ dataDir = settings.value("strDataDir", dataDir).toString();
+
+ if(!fs::exists(dataDir.toStdString()) || GetBoolArg("-choosedatadir", false))
+ {
+ /* If current default data directory does not exist, let the user choose one */
+ Intro intro;
+ intro.setDataDirectory(dataDir);
+ while(true)
+ {
+ if(!intro.exec())
+ {
+ /* Cancel clicked */
+ exit(0);
+ }
+ dataDir = intro.getDataDirectory();
+ try {
+ fs::create_directory(dataDir.toStdString());
+ break;
+ } catch(fs::filesystem_error &e) {
+ QMessageBox::critical(0, QObject::tr("Bitcoin"),
+ QObject::tr("Error: Specified data directory \"%1\" can not be created.").arg(dataDir));
+ /* fall through, back to choosing screen */
+ }
+ }
+
+ settings.setValue("strDataDir", dataDir);
+ }
+ SoftSetArg("-datadir", dataDir.toStdString());
+}
+
+void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
+{
+ switch(status)
+ {
+ case FreespaceChecker::ST_OK:
+ ui->errorMessage->setText(message);
+ ui->errorMessage->setStyleSheet("");
+ break;
+ case FreespaceChecker::ST_ERROR:
+ ui->errorMessage->setText(tr("Error") + ": " + message);
+ ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
+ break;
+ }
+ /* Indicate number of bytes available */
+ if(status == FreespaceChecker::ST_ERROR)
+ {
+ ui->freeSpace->setText("");
+ } else {
+ QString freeString = QString::number(bytesAvailable/GB_BYTES) + tr("GB of free space available");
+ if(bytesAvailable < BLOCK_CHAIN_SIZE)
+ {
+ freeString += " " + tr("(of %1GB needed)").arg(BLOCK_CHAIN_SIZE/GB_BYTES);
+ ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
+ } else {
+ ui->freeSpace->setStyleSheet("");
+ }
+ ui->freeSpace->setText(freeString + ".");
+ }
+ /* Don't allow confirm in ERROR state */
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
+}
+
+void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
+{
+ /* Disable OK button until check result comes in */
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+ checkPath(dataDirStr);
+}
+
+void Intro::on_ellipsisButton_clicked()
+{
+ QString dir = QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text());
+ if(!dir.isEmpty())
+ ui->dataDirectory->setText(dir);
+}
+
+void Intro::on_dataDirDefault_clicked()
+{
+ setDataDirectory(getDefaultDataDirectory());
+}
+
+void Intro::on_dataDirCustom_clicked()
+{
+ ui->dataDirectory->setEnabled(true);
+ ui->ellipsisButton->setEnabled(true);
+}
+
+void Intro::startThread()
+{
+ thread = new QThread(this);
+ FreespaceChecker *executor = new FreespaceChecker(this);
+ executor->moveToThread(thread);
+
+ connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64)));
+ connect(this, SIGNAL(requestCheck()), executor, SLOT(check()));
+ /* make sure executor object is deleted in its own thread */
+ connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
+ connect(this, SIGNAL(stopThread()), thread, SLOT(quit()));
+
+ thread->start();
+}
+
+void Intro::checkPath(const QString &dataDir)
+{
+ mutex.lock();
+ pathToCheck = dataDir;
+ if(!signalled)
+ {
+ signalled = true;
+ emit requestCheck();
+ }
+ mutex.unlock();
+}
+
+QString Intro::getPathToCheck()
+{
+ QString retval;
+ mutex.lock();
+ retval = pathToCheck;
+ signalled = false; /* new request can be queued now */
+ mutex.unlock();
+ return retval;
+}
diff --git a/src/qt/intro.h b/src/qt/intro.h
new file mode 100644
index 0000000000..788799b7b0
--- /dev/null
+++ b/src/qt/intro.h
@@ -0,0 +1,68 @@
+#ifndef INTRO_H
+#define INTRO_H
+
+#include <QDialog>
+#include <QThread>
+#include <QMutex>
+
+namespace Ui {
+class Intro;
+}
+class FreespaceChecker;
+
+/** Introduction screen (pre-GUI startup).
+ Allows the user to choose a data directory,
+ in which the wallet and block chain will be stored.
+ */
+class Intro : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit Intro(QWidget *parent = 0);
+ ~Intro();
+
+ QString getDataDirectory();
+ void setDataDirectory(const QString &dataDir);
+
+ /**
+ * Determine data directory. Let the user choose if the current one doesn't exist.
+ *
+ * @note do NOT call global GetDataDir() before calling this function, this
+ * will cause the wrong path to be cached.
+ */
+ static void pickDataDirectory();
+
+ /**
+ * Determine default data directory for operating system.
+ */
+ static QString getDefaultDataDirectory();
+
+signals:
+ void requestCheck();
+ void stopThread();
+
+public slots:
+ void setStatus(int status, const QString &message, quint64 bytesAvailable);
+
+private slots:
+ void on_dataDirectory_textChanged(const QString &arg1);
+ void on_ellipsisButton_clicked();
+ void on_dataDirDefault_clicked();
+ void on_dataDirCustom_clicked();
+
+private:
+ Ui::Intro *ui;
+ QThread *thread;
+ QMutex mutex;
+ bool signalled;
+ QString pathToCheck;
+
+ void startThread();
+ void checkPath(const QString &dataDir);
+ QString getPathToCheck();
+
+ friend class FreespaceChecker;
+};
+
+#endif // INTRO_H
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index 2006a5296b..5ae067d9b8 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="en">
-<defaultcodec>UTF-8</defaultcodec>
<context>
<name>AboutDialog</name>
<message>
@@ -143,7 +142,7 @@ This product includes software developed by the OpenSSL Project for use in the O
<translation>Send &amp;Coins</translation>
</message>
<message>
- <location line="+260"/>
+ <location line="+265"/>
<source>Export Address Book Data</source>
<translation>Export Address Book Data</translation>
</message>
@@ -325,17 +324,17 @@ This product includes software developed by the OpenSSL Project for use in the O
<context>
<name>BitcoinGUI</name>
<message>
- <location filename="../bitcoingui.cpp" line="+233"/>
+ <location filename="../bitcoingui.cpp" line="+254"/>
<source>Sign &amp;message...</source>
<translation>Sign &amp;message...</translation>
</message>
<message>
- <location line="+280"/>
+ <location line="+246"/>
<source>Synchronizing with network...</source>
<translation>Synchronizing with network...</translation>
</message>
<message>
- <location line="-349"/>
+ <location line="-321"/>
<source>&amp;Overview</source>
<translation>&amp;Overview</translation>
</message>
@@ -375,7 +374,7 @@ This product includes software developed by the OpenSSL Project for use in the O
<translation>Quit application</translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+7"/>
<source>Show information about Bitcoin</source>
<translation>Show information about Bitcoin</translation>
</message>
@@ -395,7 +394,7 @@ This product includes software developed by the OpenSSL Project for use in the O
<translation>&amp;Options...</translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+9"/>
<source>&amp;Encrypt Wallet...</source>
<translation>&amp;Encrypt Wallet...</translation>
</message>
@@ -410,7 +409,7 @@ This product includes software developed by the OpenSSL Project for use in the O
<translation>&amp;Change Passphrase...</translation>
</message>
<message>
- <location line="+285"/>
+ <location line="+251"/>
<source>Importing blocks from disk...</source>
<translation>Importing blocks from disk...</translation>
</message>
@@ -420,17 +419,17 @@ This product includes software developed by the OpenSSL Project for use in the O
<translation>Reindexing blocks on disk...</translation>
</message>
<message>
- <location line="-347"/>
+ <location line="-319"/>
<source>Send coins to a Bitcoin address</source>
<translation>Send coins to a Bitcoin address</translation>
</message>
<message>
- <location line="+49"/>
+ <location line="+52"/>
<source>Modify configuration options for Bitcoin</source>
<translation>Modify configuration options for Bitcoin</translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+12"/>
<source>Backup wallet to another location</source>
<translation>Backup wallet to another location</translation>
</message>
@@ -455,18 +454,20 @@ This product includes software developed by the OpenSSL Project for use in the O
<translation>&amp;Verify message...</translation>
</message>
<message>
- <location line="-165"/>
- <location line="+530"/>
+ <location line="-183"/>
+ <location line="+6"/>
+ <location line="+508"/>
<source>Bitcoin</source>
<translation>Bitcoin</translation>
</message>
<message>
- <location line="-530"/>
+ <location line="-514"/>
+ <location line="+6"/>
<source>Wallet</source>
<translation>Wallet</translation>
</message>
<message>
- <location line="+101"/>
+ <location line="+107"/>
<source>&amp;Send</source>
<translation>&amp;Send</translation>
</message>
@@ -481,12 +482,14 @@ This product includes software developed by the OpenSSL Project for use in the O
<translation>&amp;Addresses</translation>
</message>
<message>
- <location line="+22"/>
+ <location line="+23"/>
+ <location line="+2"/>
<source>&amp;About Bitcoin</source>
<translation>&amp;About Bitcoin</translation>
</message>
<message>
- <location line="+9"/>
+ <location line="+10"/>
+ <location line="+2"/>
<source>&amp;Show / Hide</source>
<translation>&amp;Show / Hide</translation>
</message>
@@ -531,18 +534,19 @@ This product includes software developed by the OpenSSL Project for use in the O
<translation>Tabs toolbar</translation>
</message>
<message>
- <location line="+17"/>
- <location line="+10"/>
+ <location line="-228"/>
+ <location line="+288"/>
<source>[testnet]</source>
<translation>[testnet]</translation>
</message>
<message>
- <location line="+47"/>
+ <location line="-5"/>
+ <location line="+5"/>
<source>Bitcoin client</source>
<translation>Bitcoin client</translation>
</message>
<message numerus="yes">
- <location line="+141"/>
+ <location line="+121"/>
<source>%n active connection(s) to Bitcoin network</source>
<translation>
<numerusform>%n active connection to Bitcoin network</numerusform>
@@ -684,7 +688,7 @@ Address: %4
<translation>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</translation>
</message>
<message>
- <location filename="../bitcoin.cpp" line="+111"/>
+ <location filename="../bitcoin.cpp" line="+110"/>
<source>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</source>
<translation>A fatal error occurred. Bitcoin can no longer continue safely and will quit.</translation>
</message>
@@ -692,7 +696,7 @@ Address: %4
<context>
<name>ClientModel</name>
<message>
- <location filename="../clientmodel.cpp" line="+104"/>
+ <location filename="../clientmodel.cpp" line="+105"/>
<source>Network Alert</source>
<translation>Network Alert</translation>
</message>
@@ -766,15 +770,43 @@ Address: %4
</message>
</context>
<context>
+ <name>FreespaceChecker</name>
+ <message>
+ <location filename="../intro.cpp" line="+61"/>
+ <source>A new data directory will be created.</source>
+ <translation>A new data directory will be created.</translation>
+ </message>
+ <message>
+ <location line="+22"/>
+ <source>name</source>
+ <translation>name</translation>
+ </message>
+ <message>
+ <location line="+2"/>
+ <source>Directory already exists. Add %1 if you intend to create a new directory here.</source>
+ <translation>Directory already exists. Add %1 if you intend to create a new directory here.</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>Path already exists, and is not a directory.</source>
+ <translation>Path already exists, and is not a directory.</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Cannot create data directory here.</source>
+ <translation>Cannot create data directory here.</translation>
+ </message>
+</context>
+<context>
<name>GUIUtil::HelpMessageBox</name>
<message>
- <location filename="../guiutil.cpp" line="+424"/>
- <location line="+12"/>
+ <location filename="../guiutil.cpp" line="+517"/>
+ <location line="+13"/>
<source>Bitcoin-Qt</source>
<translation>Bitcoin-Qt</translation>
</message>
<message>
- <location line="-12"/>
+ <location line="-13"/>
<source>version</source>
<translation>version</translation>
</message>
@@ -808,6 +840,59 @@ Address: %4
<source>Show splash screen on startup (default: 1)</source>
<translation>Show splash screen on startup (default: 1)</translation>
</message>
+ <message>
+ <location line="+1"/>
+ <source>Choose data directory on startup (default: 0)</source>
+ <translation>Choose data directory on startup (default: 0)</translation>
+ </message>
+</context>
+<context>
+ <name>Intro</name>
+ <message>
+ <location filename="../forms/intro.ui" line="+14"/>
+ <source>Welcome</source>
+ <translation>Welcome</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>Welcome to Bitcoin-Qt.</source>
+ <translation>Welcome to Bitcoin-Qt.</translation>
+ </message>
+ <message>
+ <location line="+26"/>
+ <source>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</source>
+ <translation>As this is the first time the program is launched, you can choose where Bitcoin-Qt will store its data.</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</source>
+ <translation>Bitcoin-Qt will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</translation>
+ </message>
+ <message>
+ <location line="+10"/>
+ <source>Use the default data directory</source>
+ <translation>Use the default data directory</translation>
+ </message>
+ <message>
+ <location line="+7"/>
+ <source>Use a custom data directory:</source>
+ <translation>Use a custom data directory:</translation>
+ </message>
+ <message>
+ <location filename="../intro.cpp" line="+100"/>
+ <source>Error</source>
+ <translation>Error</translation>
+ </message>
+ <message>
+ <location line="+9"/>
+ <source>GB of free space available</source>
+ <translation>GB of free space available</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>(of %1GB needed)</source>
+ <translation>(of %1GB needed)</translation>
+ </message>
</context>
<context>
<name>OptionsDialog</name>
@@ -982,7 +1067,7 @@ Address: %4
<translation>&amp;Apply</translation>
</message>
<message>
- <location filename="../optionsdialog.cpp" line="+53"/>
+ <location filename="../optionsdialog.cpp" line="+54"/>
<source>default</source>
<translation>default</translation>
</message>
@@ -1028,17 +1113,12 @@ Address: %4
</message>
<message>
<location line="+50"/>
- <location line="+166"/>
+ <location line="+202"/>
<source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source>
<translation>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</translation>
</message>
<message>
- <location line="-124"/>
- <source>Balance:</source>
- <translation>Balance:</translation>
- </message>
- <message>
- <location line="+29"/>
+ <location line="-131"/>
<source>Unconfirmed:</source>
<translation>Unconfirmed:</translation>
</message>
@@ -1048,7 +1128,22 @@ Address: %4
<translation>Wallet</translation>
</message>
<message>
- <location line="+107"/>
+ <location line="+49"/>
+ <source>Confirmed:</source>
+ <translation>Confirmed:</translation>
+ </message>
+ <message>
+ <location line="+16"/>
+ <source>Your current spendable balance</source>
+ <translation>Your current spendable balance</translation>
+ </message>
+ <message>
+ <location line="+29"/>
+ <source>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</source>
+ <translation>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</translation>
+ </message>
+ <message>
+ <location line="+13"/>
<source>Immature:</source>
<translation>Immature:</translation>
</message>
@@ -1058,19 +1153,19 @@ Address: %4
<translation>Mined balance that has not yet matured</translation>
</message>
<message>
- <location line="+46"/>
- <source>&lt;b&gt;Recent transactions&lt;/b&gt;</source>
- <translation>&lt;b&gt;Recent transactions&lt;/b&gt;</translation>
+ <location line="+13"/>
+ <source>Total:</source>
+ <translation>Total:</translation>
</message>
<message>
- <location line="-101"/>
- <source>Your current balance</source>
- <translation>Your current balance</translation>
+ <location line="+16"/>
+ <source>Your current total balance</source>
+ <translation>Your current total balance</translation>
</message>
<message>
- <location line="+29"/>
- <source>Total of transactions that have yet to be confirmed, and do not yet count toward the current balance</source>
- <translation>Total of transactions that have yet to be confirmed, and do not yet count toward the current balance</translation>
+ <location line="+53"/>
+ <source>&lt;b&gt;Recent transactions&lt;/b&gt;</source>
+ <translation>&lt;b&gt;Recent transactions&lt;/b&gt;</translation>
</message>
<message>
<location filename="../overviewpage.cpp" line="+116"/>
@@ -1082,12 +1177,31 @@ Address: %4
<context>
<name>PaymentServer</name>
<message>
- <location filename="../paymentserver.cpp" line="+107"/>
+ <location filename="../paymentserver.cpp" line="+108"/>
<source>Cannot start bitcoin: click-to-pay handler</source>
<translation>Cannot start bitcoin: click-to-pay handler</translation>
</message>
</context>
<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../bitcoin.cpp" line="+92"/>
+ <location filename="../intro.cpp" line="-32"/>
+ <source>Bitcoin</source>
+ <translation>Bitcoin</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Error: Specified data directory &quot;%1&quot; does not exist.</source>
+ <translation>Error: Specified data directory &quot;%1&quot; does not exist.</translation>
+ </message>
+ <message>
+ <location filename="../intro.cpp" line="+1"/>
+ <source>Error: Specified data directory &quot;%1&quot; can not be created.</source>
+ <translation>Error: Specified data directory &quot;%1&quot; can not be created.</translation>
+ </message>
+</context>
+<context>
<name>QRCodeDialog</name>
<message>
<location filename="../forms/qrcodedialog.ui" line="+14"/>
@@ -1120,7 +1234,7 @@ Address: %4
<translation>&amp;Save As...</translation>
</message>
<message>
- <location filename="../qrcodedialog.cpp" line="+62"/>
+ <location filename="../qrcodedialog.cpp" line="+64"/>
<source>Error encoding URI into QR Code.</source>
<translation>Error encoding URI into QR Code.</translation>
</message>
@@ -1162,7 +1276,7 @@ Address: %4
<location line="+53"/>
<location line="+23"/>
<location line="+23"/>
- <location filename="../rpcconsole.cpp" line="+339"/>
+ <location filename="../rpcconsole.cpp" line="+345"/>
<source>N/A</source>
<translation>N/A</translation>
</message>
@@ -1296,7 +1410,7 @@ Address: %4
<name>SendCoinsDialog</name>
<message>
<location filename="../forms/sendcoinsdialog.ui" line="+14"/>
- <location filename="../sendcoinsdialog.cpp" line="+124"/>
+ <location filename="../sendcoinsdialog.cpp" line="+128"/>
<location line="+5"/>
<location line="+5"/>
<location line="+5"/>
@@ -1347,12 +1461,13 @@ Address: %4
<translation>S&amp;end</translation>
</message>
<message>
- <location filename="../sendcoinsdialog.cpp" line="-59"/>
+ <location filename="../sendcoinsdialog.cpp" line="-62"/>
+ <location line="+2"/>
<source>&lt;b&gt;%1&lt;/b&gt; to %2 (%3)</source>
<translation>&lt;b&gt;%1&lt;/b&gt; to %2 (%3)</translation>
</message>
<message>
- <location line="+5"/>
+ <location line="+6"/>
<source>Confirm send coins</source>
<translation>Confirm send coins</translation>
</message>
@@ -2096,7 +2211,7 @@ Address: %4
<translation>Show transaction details</translation>
</message>
<message>
- <location line="+139"/>
+ <location line="+143"/>
<source>Export Transaction Data</source>
<translation>Export Transaction Data</translation>
</message>
@@ -2172,7 +2287,7 @@ Address: %4
<context>
<name>WalletView</name>
<message>
- <location filename="../walletview.cpp" line="+42"/>
+ <location filename="../walletview.cpp" line="+46"/>
<source>&amp;Export</source>
<translation>&amp;Export</translation>
</message>
@@ -2182,7 +2297,7 @@ Address: %4
<translation>Export the data in the current tab to a file</translation>
</message>
<message>
- <location line="+193"/>
+ <location line="+197"/>
<source>Backup Wallet</source>
<translation>Backup Wallet</translation>
</message>
@@ -2215,17 +2330,17 @@ Address: %4
<context>
<name>bitcoin-core</name>
<message>
- <location filename="../bitcoinstrings.cpp" line="+94"/>
+ <location filename="../bitcoinstrings.cpp" line="+98"/>
<source>Bitcoin version</source>
<translation>Bitcoin version</translation>
</message>
<message>
- <location line="+102"/>
+ <location line="+104"/>
<source>Usage:</source>
<translation>Usage:</translation>
</message>
<message>
- <location line="-29"/>
+ <location line="-30"/>
<source>Send command to -server or bitcoind</source>
<translation>Send command to -server or bitcoind</translation>
</message>
@@ -2235,12 +2350,12 @@ Address: %4
<translation>List commands</translation>
</message>
<message>
- <location line="-12"/>
+ <location line="-13"/>
<source>Get help for a command</source>
<translation>Get help for a command</translation>
</message>
<message>
- <location line="+24"/>
+ <location line="+25"/>
<source>Options:</source>
<translation>Options:</translation>
</message>
@@ -2275,12 +2390,12 @@ Address: %4
<translation>Maintain at most &lt;n&gt; connections to peers (default: 125)</translation>
</message>
<message>
- <location line="-48"/>
+ <location line="-49"/>
<source>Connect to a node to retrieve peer addresses, and disconnect</source>
<translation>Connect to a node to retrieve peer addresses, and disconnect</translation>
</message>
<message>
- <location line="+82"/>
+ <location line="+84"/>
<source>Specify your own public address</source>
<translation>Specify your own public address</translation>
</message>
@@ -2290,17 +2405,17 @@ Address: %4
<translation>Threshold for disconnecting misbehaving peers (default: 100)</translation>
</message>
<message>
- <location line="-134"/>
+ <location line="-136"/>
<source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source>
<translation>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</translation>
</message>
<message>
- <location line="-29"/>
+ <location line="-33"/>
<source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source>
<translation>An error occurred while setting up the RPC port %u for listening on IPv4: %s</translation>
</message>
<message>
- <location line="+27"/>
+ <location line="+31"/>
<source>Listen for JSON-RPC connections on &lt;port&gt; (default: 8332 or testnet: 18332)</source>
<translation>Listen for JSON-RPC connections on &lt;port&gt; (default: 8332 or testnet: 18332)</translation>
</message>
@@ -2310,22 +2425,22 @@ Address: %4
<translation>Accept command line and JSON-RPC commands</translation>
</message>
<message>
- <location line="+76"/>
+ <location line="+77"/>
<source>Run in the background as a daemon and accept commands</source>
<translation>Run in the background as a daemon and accept commands</translation>
</message>
<message>
- <location line="+37"/>
+ <location line="+38"/>
<source>Use the test network</source>
<translation>Use the test network</translation>
</message>
<message>
- <location line="-112"/>
+ <location line="-114"/>
<source>Accept connections from outside (default: 1 if no -proxy or -connect)</source>
<translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation>
</message>
<message>
- <location line="-80"/>
+ <location line="-84"/>
<source>%s, you must set a rpcpassword in the configuration file:
%s
It is recommended you use the following random password:
@@ -2366,6 +2481,11 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
</message>
<message>
<location line="+3"/>
+ <source>Enter regression test mode, which uses a special chain in which blocks can be solved instantly. This is intended for regression testing tools and app development.</source>
+ <translation>Enter regression test mode, which uses a special chain in which blocks can be solved instantly. This is intended for regression testing tools and app development.</translation>
+ </message>
+ <message>
+ <location line="+4"/>
<source>Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source>
<translation>Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</translation>
</message>
@@ -2560,7 +2680,12 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>How thorough the block verification is (0-4, default: 3)</translation>
</message>
<message>
- <location line="+19"/>
+ <location line="+2"/>
+ <source>Incorrect or no genesis block found. Wrong datadir for network?</source>
+ <translation>Incorrect or no genesis block found. Wrong datadir for network?</translation>
+ </message>
+ <message>
+ <location line="+18"/>
<source>Not enough file descriptors available.</source>
<translation>Not enough file descriptors available.</translation>
</message>
@@ -2575,7 +2700,12 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Set the number of threads to service RPC calls (default: 4)</translation>
</message>
<message>
- <location line="+26"/>
+ <location line="+7"/>
+ <source>Specify wallet file (within data directory)</source>
+ <translation>Specify wallet file (within data directory)</translation>
+ </message>
+ <message>
+ <location line="+20"/>
<source>Verifying blocks...</source>
<translation>Verifying blocks...</translation>
</message>
@@ -2585,7 +2715,17 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Verifying wallet...</translation>
</message>
<message>
- <location line="-69"/>
+ <location line="+1"/>
+ <source>Wallet %s resides outside data directory %s</source>
+ <translation>Wallet %s resides outside data directory %s</translation>
+ </message>
+ <message>
+ <location line="+4"/>
+ <source>You need to rebuild the database using -reindex to change -txindex</source>
+ <translation>You need to rebuild the database using -reindex to change -txindex</translation>
+ </message>
+ <message>
+ <location line="-76"/>
<source>Imports blocks from external blk000??.dat file</source>
<translation>Imports blocks from external blk000??.dat file</translation>
</message>
@@ -2595,7 +2735,7 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Set the number of script verification threads (up to 16, 0 = auto, &lt;0 = leave that many cores free, default: 0)</translation>
</message>
<message>
- <location line="+77"/>
+ <location line="+78"/>
<source>Information</source>
<translation>Information</translation>
</message>
@@ -2700,7 +2840,7 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Specify connection timeout in milliseconds (default: 5000)</translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+5"/>
<source>System error: </source>
<translation>System error: </translation>
</message>
@@ -2740,7 +2880,7 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Username for JSON-RPC connections</translation>
</message>
<message>
- <location line="+4"/>
+ <location line="+5"/>
<source>Warning</source>
<translation>Warning</translation>
</message>
@@ -2750,42 +2890,37 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Warning: This version is obsolete, upgrade required!</translation>
</message>
<message>
- <location line="+1"/>
- <source>You need to rebuild the databases using -reindex to change -txindex</source>
- <translation>You need to rebuild the databases using -reindex to change -txindex</translation>
- </message>
- <message>
- <location line="+1"/>
+ <location line="+2"/>
<source>wallet.dat corrupt, salvage failed</source>
<translation>wallet.dat corrupt, salvage failed</translation>
</message>
<message>
- <location line="-50"/>
+ <location line="-52"/>
<source>Password for JSON-RPC connections</source>
<translation>Password for JSON-RPC connections</translation>
</message>
<message>
- <location line="-67"/>
+ <location line="-68"/>
<source>Allow JSON-RPC connections from specified IP address</source>
<translation>Allow JSON-RPC connections from specified IP address</translation>
</message>
<message>
- <location line="+76"/>
+ <location line="+77"/>
<source>Send commands to node running on &lt;ip&gt; (default: 127.0.0.1)</source>
<translation>Send commands to node running on &lt;ip&gt; (default: 127.0.0.1)</translation>
</message>
<message>
- <location line="-120"/>
+ <location line="-121"/>
<source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source>
<translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation>
</message>
<message>
- <location line="+147"/>
+ <location line="+149"/>
<source>Upgrade wallet to latest format</source>
<translation>Upgrade wallet to latest format</translation>
</message>
<message>
- <location line="-21"/>
+ <location line="-22"/>
<source>Set key pool size to &lt;n&gt; (default: 100)</source>
<translation>Set key pool size to &lt;n&gt; (default: 100)</translation>
</message>
@@ -2795,12 +2930,12 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Rescan the block chain for missing wallet transactions</translation>
</message>
<message>
- <location line="+35"/>
+ <location line="+36"/>
<source>Use OpenSSL (https) for JSON-RPC connections</source>
<translation>Use OpenSSL (https) for JSON-RPC connections</translation>
</message>
<message>
- <location line="-26"/>
+ <location line="-27"/>
<source>Server certificate file (default: server.cert)</source>
<translation>Server certificate file (default: server.cert)</translation>
</message>
@@ -2810,12 +2945,12 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Server private key (default: server.pem)</translation>
</message>
<message>
- <location line="-151"/>
+ <location line="-156"/>
<source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source>
<translation>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation>
</message>
<message>
- <location line="+165"/>
+ <location line="+171"/>
<source>This help message</source>
<translation>This help message</translation>
</message>
@@ -2825,7 +2960,7 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Unable to bind to %s on this computer (bind returned error %d, %s)</translation>
</message>
<message>
- <location line="-91"/>
+ <location line="-93"/>
<source>Connect through socks proxy</source>
<translation>Connect through socks proxy</translation>
</message>
@@ -2835,12 +2970,12 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Allow DNS lookups for -addnode, -seednode and -connect</translation>
</message>
<message>
- <location line="+55"/>
+ <location line="+56"/>
<source>Loading addresses...</source>
<translation>Loading addresses...</translation>
</message>
<message>
- <location line="-35"/>
+ <location line="-36"/>
<source>Error loading wallet.dat: Wallet corrupted</source>
<translation>Error loading wallet.dat: Wallet corrupted</translation>
</message>
@@ -2850,22 +2985,22 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Error loading wallet.dat: Wallet requires newer version of Bitcoin</translation>
</message>
<message>
- <location line="+93"/>
+ <location line="+96"/>
<source>Wallet needed to be rewritten: restart Bitcoin to complete</source>
<translation>Wallet needed to be rewritten: restart Bitcoin to complete</translation>
</message>
<message>
- <location line="-95"/>
+ <location line="-98"/>
<source>Error loading wallet.dat</source>
<translation>Error loading wallet.dat</translation>
</message>
<message>
- <location line="+28"/>
+ <location line="+29"/>
<source>Invalid -proxy address: &apos;%s&apos;</source>
<translation>Invalid -proxy address: &apos;%s&apos;</translation>
</message>
<message>
- <location line="+56"/>
+ <location line="+57"/>
<source>Unknown network specified in -onlynet: &apos;%s&apos;</source>
<translation>Unknown network specified in -onlynet: &apos;%s&apos;</translation>
</message>
@@ -2875,7 +3010,7 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Unknown -socks proxy version requested: %i</translation>
</message>
<message>
- <location line="-96"/>
+ <location line="-98"/>
<source>Cannot resolve -bind address: &apos;%s&apos;</source>
<translation>Cannot resolve -bind address: &apos;%s&apos;</translation>
</message>
@@ -2885,7 +3020,7 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Cannot resolve -externalip address: &apos;%s&apos;</translation>
</message>
<message>
- <location line="+44"/>
+ <location line="+45"/>
<source>Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos;</source>
<translation>Invalid amount for -paytxfee=&lt;amount&gt;: &apos;%s&apos;</translation>
</message>
@@ -2905,7 +3040,7 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Loading block index...</translation>
</message>
<message>
- <location line="-57"/>
+ <location line="-58"/>
<source>Add a node to connect to and attempt to keep the connection open</source>
<translation>Add a node to connect to and attempt to keep the connection open</translation>
</message>
@@ -2920,12 +3055,12 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Fee per KB to add to transactions you send</translation>
</message>
<message>
- <location line="+19"/>
+ <location line="+20"/>
<source>Loading wallet...</source>
<translation>Loading wallet...</translation>
</message>
<message>
- <location line="-52"/>
+ <location line="-53"/>
<source>Cannot downgrade wallet</source>
<translation>Cannot downgrade wallet</translation>
</message>
@@ -2935,22 +3070,22 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
<translation>Cannot write default address</translation>
</message>
<message>
- <location line="+64"/>
+ <location line="+65"/>
<source>Rescanning...</source>
<translation>Rescanning...</translation>
</message>
<message>
- <location line="-57"/>
+ <location line="-58"/>
<source>Done loading</source>
<translation>Done loading</translation>
</message>
<message>
- <location line="+82"/>
+ <location line="+84"/>
<source>To use the %s option</source>
<translation>To use the %s option</translation>
</message>
<message>
- <location line="-74"/>
+ <location line="-76"/>
<source>Error</source>
<translation>Error</translation>
</message>
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index 7cfaef6079..903c54b39d 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -1,9 +1,9 @@
#include "notificator.h"
+#include <QApplication>
#include <QMetaType>
#include <QVariant>
#include <QIcon>
-#include <QApplication>
#include <QStyle>
#include <QByteArray>
#include <QSystemTrayIcon>
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 6b1b4e3d8e..7ebe5b4755 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -89,7 +89,7 @@ bool OptionsModel::Upgrade()
settings.setValue("bImportFinished", true);
// Move settings from old wallet.dat (if any):
- CWalletDB walletdb("wallet.dat");
+ CWalletDB walletdb(strWalletFile);
QList<QString> intOptions;
intOptions << "nDisplayUnit" << "nTransactionFee";
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 0b0bce55bb..0d31f24a13 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -2,14 +2,13 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <QApplication>
-
#include "paymentserver.h"
#include "guiconstants.h"
#include "ui_interface.h"
#include "util.h"
+#include <QApplication>
#include <QByteArray>
#include <QDataStream>
#include <QDebug>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index ef72b17201..8953c36579 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -187,6 +187,7 @@ RPCConsole::RPCConsole(QWidget *parent) :
historyPtr(0)
{
ui->setupUi(this);
+ GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
#ifndef Q_OS_MAC
ui->openDebugLogfileButton->setIcon(QIcon(":/icons/export"));
@@ -209,6 +210,7 @@ RPCConsole::RPCConsole(QWidget *parent) :
RPCConsole::~RPCConsole()
{
+ GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
emit stopExecutor();
delete ui;
}
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index e400278123..22cf04486b 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -2,9 +2,8 @@
#include "clientversion.h"
#include "util.h"
-#include <QPainter>
-#undef loop /* ugh, remove this when the #define loop is gone from util.h */
#include <QApplication>
+#include <QPainter>
SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) :
QSplashScreen(pixmap, f)
diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp
index 6b2ef8315f..edaa732225 100644
--- a/src/rpcblockchain.cpp
+++ b/src/rpcblockchain.cpp
@@ -82,6 +82,15 @@ Value getblockcount(const Array& params, bool fHelp)
return nBestHeight;
}
+Value getbestblockhash(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 0)
+ throw runtime_error(
+ "getbestblockhash\n"
+ "Returns the hash of the best (tip) block in the longest block chain.");
+
+ return hashBestChain.GetHex();
+}
Value getdifficulty(const Array& params, bool fHelp)
{
@@ -163,7 +172,7 @@ Value getblock(const Array& params, bool fHelp)
CBlock block;
CBlockIndex* pblockindex = mapBlockIndex[hash];
- block.ReadFromDisk(pblockindex);
+ ReadBlockFromDisk(block, pblockindex);
if (!fVerbose)
{
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index d0b0a70be4..25111d3787 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -4,9 +4,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chainparams.h"
-#include "main.h"
#include "db.h"
#include "init.h"
+#include "miner.h"
#include "bitcoinrpc.h"
using namespace json_spirit;
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 61cb4e390e..f08688d386 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -9,7 +9,6 @@
#include "bitcoinrpc.h"
#include "db.h"
#include "init.h"
-#include "main.h"
#include "net.h"
#include "wallet.h"
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index 5388bd4d84..dca640323e 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -81,7 +81,7 @@ Value getinfo(const Array& params, bool fHelp)
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("testnet", TestNet()));
obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
- obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
+ obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize()));
obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
if (pwalletMain->IsCrypted())
obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
@@ -1253,17 +1253,24 @@ Value backupwallet(const Array& params, bool fHelp)
Value keypoolrefill(const Array& params, bool fHelp)
{
- if (fHelp || params.size() > 0)
+ if (fHelp || params.size() > 1)
throw runtime_error(
- "keypoolrefill\n"
+ "keypoolrefill [new-size]\n"
"Fills the keypool."
+ HelpRequiringPassphrase());
+ unsigned int kpSize = max(GetArg("-keypool", 100), 0LL);
+ if (params.size() > 0) {
+ if (params[0].get_int() < 0)
+ throw JSONRPCError(-8, "Invalid parameter, expected valid size");
+ kpSize = (unsigned int) params[0].get_int();
+ }
+
EnsureWalletIsUnlocked();
- pwalletMain->TopUpKeyPool();
+ pwalletMain->TopUpKeyPool(kpSize);
- if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100))
+ if (pwalletMain->GetKeyPoolSize() < kpSize)
throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
return Value::null;
@@ -1492,18 +1499,18 @@ Value lockunspent(const Array& params, bool fHelp)
BOOST_FOREACH(Value& output, outputs)
{
if (output.type() != obj_type)
- throw JSONRPCError(-8, "Invalid parameter, expected object");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "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");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "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");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
COutPoint outpt(uint256(txid), nOutput);
diff --git a/src/script.cpp b/src/script.cpp
index 14fe80e207..5699fbfb6a 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -1163,7 +1163,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
// Compare
CScript::const_iterator pc1 = script1.begin();
CScript::const_iterator pc2 = script2.begin();
- loop
+ while (true)
{
if (pc1 == script1.end() && pc2 == script2.end())
{
diff --git a/src/sync.h b/src/sync.h
index 930c9b2b80..64de7cc57c 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -11,6 +11,48 @@
#include <boost/thread/condition_variable.hpp>
#include "threadsafety.h"
+
+////////////////////////////////////////////////
+// //
+// THE SIMPLE DEFINITON, EXCLUDING DEBUG CODE //
+// //
+////////////////////////////////////////////////
+
+/*
+
+
+
+CCriticalSection mutex;
+ boost::recursive_mutex mutex;
+
+LOCK(mutex);
+ boost::unique_lock<boost::recursive_mutex> criticalblock(mutex);
+
+LOCK2(mutex1, mutex2);
+ boost::unique_lock<boost::recursive_mutex> criticalblock1(mutex1);
+ boost::unique_lock<boost::recursive_mutex> criticalblock2(mutex2);
+
+TRY_LOCK(mutex, name);
+ boost::unique_lock<boost::recursive_mutex> name(mutex, boost::try_to_lock_t);
+
+ENTER_CRITICAL_SECTION(mutex); // no RAII
+ mutex.lock();
+
+LEAVE_CRITICAL_SECTION(mutex); // no RAII
+ mutex.unlock();
+
+
+
+ */
+
+
+
+///////////////////////////////
+// //
+// THE ACTUAL IMPLEMENTATION //
+// //
+///////////////////////////////
+
// Template mixin that adds -Wthread-safety locking annotations to a
// subset of the mutex API.
template <typename PARENT>
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index ea0cc1bcef..c7f968da7c 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -17,10 +17,10 @@
#include <stdint.h>
// Tests this internal-to-main.cpp method:
-extern bool AddOrphanTx(const CDataStream& vMsg);
+extern bool AddOrphanTx(const CTransaction& tx);
extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
-extern std::map<uint256, CDataStream*> mapOrphanTransactions;
-extern std::map<uint256, std::map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
+extern std::map<uint256, CTransaction> mapOrphanTransactions;
+extern std::map<uint256, std::set<uint256> > mapOrphanTransactionsByPrev;
CService ip(uint32_t i)
{
@@ -134,14 +134,11 @@ BOOST_AUTO_TEST_CASE(DoS_checknbits)
CTransaction RandomOrphan()
{
- std::map<uint256, CDataStream*>::iterator it;
+ std::map<uint256, CTransaction>::iterator it;
it = mapOrphanTransactions.lower_bound(GetRandHash());
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
- const CDataStream* pvMsg = it->second;
- CTransaction tx;
- CDataStream(*pvMsg) >> tx;
- return tx;
+ return it->second;
}
BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
@@ -163,9 +160,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vout[0].nValue = 1*CENT;
tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
- CDataStream ds(SER_DISK, CLIENT_VERSION);
- ds << tx;
- AddOrphanTx(ds);
+ AddOrphanTx(tx);
}
// ... and 50 that depend on other orphans:
@@ -182,9 +177,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
SignSignature(keystore, txPrev, tx, 0);
- CDataStream ds(SER_DISK, CLIENT_VERSION);
- ds << tx;
- AddOrphanTx(ds);
+ AddOrphanTx(tx);
}
// This really-big orphan should be ignored:
@@ -208,9 +201,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
for (unsigned int j = 1; j < tx.vin.size(); j++)
tx.vin[j].scriptSig = tx.vin[0].scriptSig;
- CDataStream ds(SER_DISK, CLIENT_VERSION);
- ds << tx;
- BOOST_CHECK(!AddOrphanTx(ds));
+ BOOST_CHECK(!AddOrphanTx(tx));
}
// Test LimitOrphanTxSize() function:
@@ -247,9 +238,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig)
tx.vout[0].nValue = 1*CENT;
tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
- CDataStream ds(SER_DISK, CLIENT_VERSION);
- ds << tx;
- AddOrphanTx(ds);
+ AddOrphanTx(tx);
}
// Create a transaction that depends on orphans:
diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp
index d626f9a6f4..5675c40e76 100644
--- a/src/test/checkblock_tests.cpp
+++ b/src/test/checkblock_tests.cpp
@@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(May15)
// After May 15'th, big blocks are OK:
forkingBlock.nTime = tMay15; // Invalidates PoW
- BOOST_CHECK(forkingBlock.CheckBlock(state, false, false));
+ BOOST_CHECK(CheckBlock(forkingBlock, state, false, false));
}
SetMockTime(0);
diff --git a/src/test/data/script_invalid.json b/src/test/data/script_invalid.json
index 95664226c5..6964d16a7e 100644
--- a/src/test/data/script_invalid.json
+++ b/src/test/data/script_invalid.json
@@ -13,7 +13,9 @@
["0","NOP"],
["1", "IF VER ELSE 1 ENDIF", "VER non-functional"],
["0", "IF VERIF ELSE 1 ENDIF", "VERIF illegal everywhere"],
-["0", "IF VERNOTIF ELSE 1 ENDIF", "VERNOT illegal everywhere"],
+["0", "IF ELSE 1 ELSE VERIF ENDIF", "VERIF illegal everywhere"],
+["0", "IF VERNOTIF ELSE 1 ENDIF", "VERNOTIF illegal everywhere"],
+["0", "IF ELSE 1 ELSE VERNOTIF ENDIF", "VERNOTIF illegal everywhere"],
["1 IF", "1 ENDIF", "IF/ENDIF can't span scriptSig/scriptPubKey"],
["1 IF 0 ENDIF", "1 ENDIF"],
@@ -36,6 +38,18 @@
["1 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],
["0 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],
+["1", "IF RETURN ELSE ELSE 1 ENDIF", "Multiple ELSEs"],
+["1", "IF 1 ELSE ELSE RETURN ENDIF"],
+
+["1", "ENDIF", "Malformed IF/ELSE/ENDIF sequence"],
+["1", "ELSE ENDIF"],
+["1", "ENDIF ELSE"],
+["1", "ENDIF ELSE IF"],
+["1", "IF ELSE ENDIF ELSE"],
+["1", "IF ELSE ENDIF ELSE ENDIF"],
+["1", "IF ENDIF ENDIF"],
+["1", "IF ELSE ELSE ENDIF ENDIF"],
+
["1", "RETURN"],
["1", "DUP IF RETURN ENDIF"],
diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json
index 58682d3875..cc56a310ae 100644
--- a/src/test/data/script_valid.json
+++ b/src/test/data/script_valid.json
@@ -34,6 +34,21 @@
["1 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],
["0 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"],
+["0", "IF 0 ELSE 1 ELSE 0 ENDIF", "Multiple ELSE's are valid and executed inverts on each ELSE encountered"],
+["1", "IF 1 ELSE 0 ELSE ENDIF"],
+["1", "IF ELSE 0 ELSE 1 ENDIF"],
+["1", "IF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL"],
+["'' 1", "IF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL"],
+
+["1", "NOTIF 0 ELSE 1 ELSE 0 ENDIF", "Multiple ELSE's are valid and execution inverts on each ELSE encountered"],
+["0", "NOTIF 1 ELSE 0 ELSE ENDIF"],
+["0", "NOTIF ELSE 0 ELSE 1 ENDIF"],
+["0", "NOTIF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL"],
+["'' 0", "NOTIF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL"],
+
+["0", "IF 1 IF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 1 IF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL", "Nested ELSE ELSE"],
+["1", "NOTIF 0 NOTIF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 0 NOTIF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL"],
+
["0", "IF RETURN ENDIF 1", "RETURN only works if executed"],
["1 1", "VERIFY"],
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index af284653dd..bd1d998c4f 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -4,6 +4,7 @@
#include "main.h"
#include "uint256.h"
#include "util.h"
+#include "miner.h"
#include "wallet.h"
extern void SHA256Transform(void* pstate, void* pinput, const void* pinit);
diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp
index 23cb3a8e0a..3c666d2842 100644
--- a/src/test/script_P2SH_tests.cpp
+++ b/src/test/script_P2SH_tests.cpp
@@ -74,6 +74,7 @@ BOOST_AUTO_TEST_CASE(sign)
}
CTransaction txFrom; // Funding transaction:
+ string reason;
txFrom.vout.resize(8);
for (int i = 0; i < 4; i++)
{
@@ -82,7 +83,7 @@ BOOST_AUTO_TEST_CASE(sign)
txFrom.vout[i+4].scriptPubKey = standardScripts[i];
txFrom.vout[i+4].nValue = COIN;
}
- BOOST_CHECK(IsStandardTx(txFrom));
+ BOOST_CHECK(IsStandardTx(txFrom, reason));
CTransaction txTo[8]; // Spending transactions
for (int i = 0; i < 8; i++)
@@ -167,13 +168,14 @@ BOOST_AUTO_TEST_CASE(set)
}
CTransaction txFrom; // Funding transaction:
+ string reason;
txFrom.vout.resize(4);
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = outer[i];
txFrom.vout[i].nValue = CENT;
}
- BOOST_CHECK(IsStandardTx(txFrom));
+ BOOST_CHECK(IsStandardTx(txFrom, reason));
CTransaction txTo[4]; // Spending transactions
for (int i = 0; i < 4; i++)
@@ -189,7 +191,7 @@ BOOST_AUTO_TEST_CASE(set)
for (int i = 0; i < 4; i++)
{
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
- BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i]), strprintf("txTo[%d].IsStandard", i));
+ BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i));
}
}
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 53d1307b69..0c7475b4f2 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -260,16 +260,17 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
key.MakeNewKey(true);
t.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
- BOOST_CHECK(IsStandardTx(t));
+ string reason;
+ BOOST_CHECK(IsStandardTx(t, reason));
t.vout[0].nValue = 5011; // dust
- BOOST_CHECK(!IsStandardTx(t));
+ BOOST_CHECK(!IsStandardTx(t, reason));
t.vout[0].nValue = 6011; // not dust
- BOOST_CHECK(IsStandardTx(t));
+ BOOST_CHECK(IsStandardTx(t, reason));
t.vout[0].scriptPubKey = CScript() << OP_1;
- BOOST_CHECK(!IsStandardTx(t));
+ BOOST_CHECK(!IsStandardTx(t, reason));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 64bd3a1b28..fd936517fd 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -263,28 +263,10 @@ BOOST_AUTO_TEST_CASE(util_IsHex)
BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
{
- // Expected results for the determinstic seed.
- const uint32_t exp_vals[11] = { 91632771U,1889679809U,3842137544U,3256031132U,
- 1761911779U, 489223532U,2692793790U,2737472863U,
- 2796262275U,1309899767U,840571781U};
- // Expected 0s in rand()%(idx+2) for the determinstic seed.
- const int exp_count[9] = {5013,3346,2415,1972,1644,1386,1176,1096,1009};
int i;
int count=0;
- seed_insecure_rand();
-
- //Does the non-determistic rand give us results that look too like the determinstic one?
- for (i=0;i<10;i++)
- {
- int match = 0;
- uint32_t rval = insecure_rand();
- for (int j=0;j<11;j++)match |= rval==exp_vals[j];
- count += match;
- }
- // sum(binomial(10,i)*(11/(2^32))^i*(1-(11/(2^32)))^(10-i),i,0,4) ~= 1-1/2^134.73
- // So _very_ unlikely to throw a false failure here.
- BOOST_CHECK(count<=4);
+ seed_insecure_rand(true);
for (int mod=2;mod<11;mod++)
{
@@ -307,20 +289,6 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
BOOST_CHECK(count<=10000/mod+err);
BOOST_CHECK(count>=10000/mod-err);
}
-
- seed_insecure_rand(true);
-
- for (i=0;i<11;i++)
- {
- BOOST_CHECK_EQUAL(insecure_rand(),exp_vals[i]);
- }
-
- for (int mod=2;mod<11;mod++)
- {
- count = 0;
- for (i=0;i<10000;i++) count += insecure_rand()%mod==0;
- BOOST_CHECK_EQUAL(count,exp_count[mod-2]);
- }
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util.cpp b/src/util.cpp
index bfb6d75838..049e55b7d6 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -311,7 +311,7 @@ string vstrprintf(const char *format, va_list ap)
char* p = buffer;
int limit = sizeof(buffer);
int ret;
- loop
+ while (true)
{
va_list arg_ptr;
va_copy(arg_ptr, ap);
@@ -371,7 +371,7 @@ void ParseString(const string& str, char c, vector<string>& v)
return;
string::size_type i1 = 0;
string::size_type i2;
- loop
+ while (true)
{
i2 = str.find(c, i1);
if (i2 == str.npos)
@@ -487,7 +487,7 @@ vector<unsigned char> ParseHex(const char* psz)
{
// convert hex dump to vector
vector<unsigned char> vch;
- loop
+ while (true)
{
while (isspace(*psz))
psz++;
@@ -941,7 +941,7 @@ string DecodeBase32(const string& str)
bool WildcardMatch(const char* psz, const char* mask)
{
- loop
+ while (true)
{
switch (*mask)
{
@@ -1118,6 +1118,7 @@ boost::filesystem::path GetPidFile()
return pathPidFile;
}
+#ifndef WIN32
void CreatePidFile(const boost::filesystem::path &path, pid_t pid)
{
FILE* file = fopen(path.string().c_str(), "w");
@@ -1127,6 +1128,7 @@ void CreatePidFile(const boost::filesystem::path &path, pid_t pid)
fclose(file);
}
}
+#endif
bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest)
{
diff --git a/src/util.h b/src/util.h
index 86a38ad331..9aea564406 100644
--- a/src/util.h
+++ b/src/util.h
@@ -35,7 +35,6 @@ typedef unsigned long long uint64;
static const int64 COIN = 100000000;
static const int64 CENT = 1000000;
-#define loop for (;;)
#define BEGIN(a) ((char*)&(a))
#define END(a) ((char*)&((&(a))[1]))
#define UBEGIN(a) ((unsigned char*)&(a))
@@ -208,7 +207,9 @@ boost::filesystem::path GetDefaultDataDir();
const boost::filesystem::path &GetDataDir(bool fNetSpecific = true);
boost::filesystem::path GetConfigFile();
boost::filesystem::path GetPidFile();
+#ifndef WIN32
void CreatePidFile(const boost::filesystem::path &path, pid_t pid);
+#endif
void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet, std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet);
#ifdef WIN32
boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
@@ -527,7 +528,7 @@ inline uint32_t ByteReverse(uint32_t value)
// Standard wrapper for do-something-forever thread functions.
// "Forever" really means until the thread is interrupted.
// Use it like:
-// new boost::thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 10000));
+// new boost::thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 900000));
// or maybe:
// boost::function<void()> f = boost::bind(&FunctionWithArg, argument);
// threadGroup.create_thread(boost::bind(&LoopForever<boost::function<void()> >, "nothing", f, milliseconds));
@@ -540,8 +541,8 @@ template <typename Callable> void LoopForever(const char* name, Callable func,
{
while (1)
{
- func();
MilliSleep(msecs);
+ func();
}
}
catch (boost::thread_interrupted)
diff --git a/src/version.cpp b/src/version.cpp
index 8af406feab..d9d6724a02 100644
--- a/src/version.cpp
+++ b/src/version.cpp
@@ -36,7 +36,7 @@ const std::string CLIENT_NAME("Satoshi");
// git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$
#ifdef GIT_ARCHIVE
# define GIT_COMMIT_ID "$Format:%h$"
-# define GIT_COMMIT_DATE "$Format:%cD"
+# define GIT_COMMIT_DATE "$Format:%cD$"
#endif
#define BUILD_DESC_FROM_COMMIT(maj,min,rev,build,commit) \
diff --git a/src/wallet.cpp b/src/wallet.cpp
index b09f949802..8b9f3d34e1 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -804,7 +804,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
}
CBlock block;
- block.ReadFromDisk(pindex);
+ ReadBlockFromDisk(block, pindex);
BOOST_FOREACH(CTransaction& tx, block.vtx)
{
if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate))
@@ -892,19 +892,17 @@ void CWallet::ResendWalletTransactions()
{
// Do this infrequently and randomly to avoid giving away
// that these are our transactions.
- static int64 nNextTime;
- if (GetTime() < nNextTime)
+ if (GetTime() < nNextResend)
return;
- bool fFirst = (nNextTime == 0);
- nNextTime = GetTime() + GetRand(30 * 60);
+ bool fFirst = (nNextResend == 0);
+ nNextResend = GetTime() + GetRand(30 * 60);
if (fFirst)
return;
// Only do it if there's been a new block since last time
- static int64 nLastTime;
- if (nTimeBestReceived < nLastTime)
+ if (nTimeBestReceived < nLastResend)
return;
- nLastTime = GetTime();
+ nLastResend = GetTime();
// Rebroadcast any of our txes that aren't in a block yet
printf("ResendWalletTransactions()\n");
@@ -1196,7 +1194,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend,
LOCK2(cs_main, cs_wallet);
{
nFeeRet = nTransactionFee;
- loop
+ while (true)
{
wtxNew.vin.clear();
wtxNew.vout.clear();
@@ -1553,7 +1551,7 @@ bool CWallet::NewKeyPool()
return true;
}
-bool CWallet::TopUpKeyPool()
+bool CWallet::TopUpKeyPool(unsigned int kpSize)
{
{
LOCK(cs_wallet);
@@ -1564,7 +1562,12 @@ bool CWallet::TopUpKeyPool()
CWalletDB walletdb(strWalletFile);
// Top up key pool
- unsigned int nTargetSize = max(GetArg("-keypool", 100), 0LL);
+ unsigned int nTargetSize;
+ if (kpSize > 0)
+ nTargetSize = kpSize;
+ else
+ nTargetSize = max(GetArg("-keypool", 100), 0LL);
+
while (setKeyPool.size() < (nTargetSize + 1))
{
int64 nEnd = 1;
diff --git a/src/wallet.h b/src/wallet.h
index 36b3608fb0..664a032912 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -80,6 +80,9 @@ private:
// the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded
int nWalletMaxVersion;
+ int64 nNextResend;
+ int64 nLastResend;
+
public:
mutable CCriticalSection cs_wallet;
@@ -101,6 +104,8 @@ public:
nMasterKeyMaxID = 0;
pwalletdbEncryption = NULL;
nOrderPosNext = 0;
+ nNextResend = 0;
+ nLastResend = 0;
}
CWallet(std::string strWalletFileIn)
{
@@ -111,6 +116,8 @@ public:
nMasterKeyMaxID = 0;
pwalletdbEncryption = NULL;
nOrderPosNext = 0;
+ nNextResend = 0;
+ nLastResend = 0;
}
std::map<uint256, CWalletTx> mapWallet;
@@ -195,7 +202,7 @@ public:
std::string SendMoneyToDestination(const CTxDestination &address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
bool NewKeyPool();
- bool TopUpKeyPool();
+ bool TopUpKeyPool(unsigned int kpSize = 0);
int64 AddReserveKey(const CKeyPool& keypool);
void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool);
void KeepKey(int64 nIndex);
@@ -292,7 +299,7 @@ public:
}
}
- int GetKeyPoolSize()
+ unsigned int GetKeyPoolSize()
{
return setKeyPool.size();
}
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
index 702e219a5b..014d8cbe29 100644
--- a/src/walletdb.cpp
+++ b/src/walletdb.cpp
@@ -5,6 +5,7 @@
#include "walletdb.h"
#include "wallet.h"
+#include <boost/version.hpp>
#include <boost/filesystem.hpp>
using namespace std;
@@ -72,7 +73,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountin
if (!pcursor)
throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
unsigned int fFlags = DB_SET_RANGE;
- loop
+ while (true)
{
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
@@ -436,7 +437,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
return DB_CORRUPT;
}
- loop
+ while (true)
{
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);