aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/init.cpp17
-rw-r--r--src/key.cpp23
-rw-r--r--src/key.h3
-rw-r--r--src/qt/clientmodel.cpp4
-rw-r--r--src/script.cpp146
-rw-r--r--src/test/Makefile.am2
-rw-r--r--src/test/multisig_tests.cpp2
-rw-r--r--src/test/script_tests.cpp2
-rw-r--r--src/test/sighash_tests.cpp120
-rw-r--r--src/walletdb.cpp38
-rw-r--r--src/walletdb.h10
11 files changed, 302 insertions, 65 deletions
diff --git a/src/init.cpp b/src/init.cpp
index fce5992255..28ec409bae 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -185,7 +185,7 @@ std::string HelpMessage()
strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n";
strUsage += " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n";
strUsage += " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n";
- strUsage += " -tor=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n";
+ strUsage += " -onion=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n";
strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n";
strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n";
strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n";
@@ -642,15 +642,20 @@ bool AppInit2(boost::thread_group& threadGroup)
fProxy = true;
}
- // -tor can override normal proxy, -notor disables tor entirely
- if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) {
+ // -onion can override normal proxy, -noonion disables tor entirely
+ // -tor here is a temporary backwards compatibility measure
+ if (mapArgs.count("-tor"))
+ printf("Notice: option -tor has been replaced with -onion and will be removed in a later version.\n");
+ if (!(mapArgs.count("-onion") && mapArgs["-onion"] == "0") &&
+ !(mapArgs.count("-tor") && mapArgs["-tor"] == "0") &&
+ (fProxy || mapArgs.count("-onion") || mapArgs.count("-tor"))) {
CService addrOnion;
- if (!mapArgs.count("-tor"))
+ if (!mapArgs.count("-onion") && !mapArgs.count("-tor"))
addrOnion = addrProxy;
else
- addrOnion = CService(mapArgs["-tor"], 9050);
+ addrOnion = mapArgs.count("-onion")?CService(mapArgs["-onion"], 9050):CService(mapArgs["-tor"], 9050);
if (!addrOnion.IsValid())
- return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"].c_str()));
+ return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs.count("-onion")?mapArgs["-onion"].c_str():mapArgs["-tor"].c_str()));
SetProxy(NET_TOR, addrOnion, 5);
SetReachable(NET_TOR);
}
diff --git a/src/key.cpp b/src/key.cpp
index 8ef1c414c4..414845a9da 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -166,9 +166,12 @@ public:
assert(nSize == nSize2);
}
- bool SetPrivKey(const CPrivKey &privkey) {
+ bool SetPrivKey(const CPrivKey &privkey, bool fSkipCheck=false) {
const unsigned char* pbegin = &privkey[0];
if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) {
+ if(fSkipCheck)
+ return true;
+
// d2i_ECPrivateKey returns true if parsing succeeds.
// This doesn't necessarily mean the key is valid.
if (EC_KEY_check_key(pkey))
@@ -411,6 +414,24 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
return true;
}
+bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) {
+ CECKey key;
+ if (!key.SetPrivKey(privkey, fSkipCheck))
+ return false;
+
+ key.GetSecretBytes(vch);
+ fCompressed = vchPubKey.IsCompressed();
+ fValid = true;
+
+ if (fSkipCheck)
+ return true;
+
+ if (GetPubKey() != vchPubKey)
+ return false;
+
+ return true;
+}
+
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
if (!IsValid())
return false;
diff --git a/src/key.h b/src/key.h
index 75431e944f..ac050356f2 100644
--- a/src/key.h
+++ b/src/key.h
@@ -261,6 +261,9 @@ public:
// Derive BIP32 child key.
bool Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const;
+
+ // Load private key and check that public key matches.
+ bool Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck);
};
struct CExtPubKey {
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 2bab488135..212fa6974a 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -65,10 +65,8 @@ QDateTime ClientModel::getLastBlockDate() const
{
if (chainActive.Tip())
return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
- else if(!isTestNet())
- return QDateTime::fromTime_t(1231006505); // Genesis block's time
else
- return QDateTime::fromTime_t(1296688602); // Genesis block's time (testnet)
+ return QDateTime::fromTime_t(Params().GenesisBlock().nTime); // Genesis block's time of current network
}
double ClientModel::getVerificationProgress() const
diff --git a/src/script.cpp b/src/script.cpp
index 0fe2953548..ad2d35d94c 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -971,62 +971,118 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
+namespace {
+/** Wrapper that serializes like CTransaction, but with the modifications
+ * required for the signature hash done in-place
+ */
+class CTransactionSignatureSerializer {
+private:
+ const CTransaction &txTo; // reference to the spending transaction (the one being serialized)
+ const CScript &scriptCode; // output script being consumed
+ const unsigned int nIn; // input index of txTo being signed
+ const bool fAnyoneCanPay; // whether the hashtype has the SIGHASH_ANYONECANPAY flag set
+ const bool fHashSingle; // whether the hashtype is SIGHASH_SINGLE
+ const bool fHashNone; // whether the hashtype is SIGHASH_NONE
-
-uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
-{
- if (nIn >= txTo.vin.size())
- {
- LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn);
- return 1;
+public:
+ CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
+ txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn),
+ fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)),
+ fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE),
+ fHashNone((nHashTypeIn & 0x1f) == SIGHASH_NONE) {}
+
+ /** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */
+ template<typename S>
+ void SerializeScriptCode(S &s, int nType, int nVersion) const {
+ CScript::const_iterator it = scriptCode.begin();
+ CScript::const_iterator itBegin = it;
+ opcodetype opcode;
+ unsigned int nCodeSeparators = 0;
+ while (scriptCode.GetOp(it, opcode)) {
+ if (opcode == OP_CODESEPARATOR)
+ nCodeSeparators++;
+ }
+ ::WriteCompactSize(s, scriptCode.size() - nCodeSeparators);
+ it = itBegin;
+ while (scriptCode.GetOp(it, opcode)) {
+ if (opcode == OP_CODESEPARATOR) {
+ s.write((char*)&itBegin[0], it-itBegin-1);
+ itBegin = it;
+ }
+ }
+ s.write((char*)&itBegin[0], it-itBegin);
}
- CTransaction txTmp(txTo);
- // In case concatenating two scripts ends up with two codeseparators,
- // or an extra one at the end, this prevents all those possible incompatibilities.
- scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));
+ /** Serialize an input of txTo */
+ template<typename S>
+ void SerializeInput(S &s, unsigned int nInput, int nType, int nVersion) const {
+ // In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized
+ if (fAnyoneCanPay)
+ nInput = nIn;
+ // Serialize the prevout
+ ::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion);
+ // Serialize the script
+ if (nInput != nIn)
+ // Blank out other inputs' signatures
+ ::Serialize(s, CScript(), nType, nVersion);
+ else
+ SerializeScriptCode(s, nType, nVersion);
+ // Serialize the nSequence
+ if (nInput != nIn && (fHashSingle || fHashNone))
+ // let the others update at will
+ ::Serialize(s, (int)0, nType, nVersion);
+ else
+ ::Serialize(s, txTo.vin[nInput].nSequence, nType, nVersion);
+ }
- // Blank out other inputs' signatures
- for (unsigned int i = 0; i < txTmp.vin.size(); i++)
- txTmp.vin[i].scriptSig = CScript();
- txTmp.vin[nIn].scriptSig = scriptCode;
+ /** Serialize an output of txTo */
+ template<typename S>
+ void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const {
+ if (fHashSingle && nOutput != nIn)
+ // Do not lock-in the txout payee at other indices as txin
+ ::Serialize(s, CTxOut(), nType, nVersion);
+ else
+ ::Serialize(s, txTo.vout[nOutput], nType, nVersion);
+ }
- // Blank out some of the outputs
- if ((nHashType & 0x1f) == SIGHASH_NONE)
- {
- // Wildcard payee
- txTmp.vout.clear();
+ /** Serialize txTo */
+ template<typename S>
+ void Serialize(S &s, int nType, int nVersion) const {
+ // Serialize nVersion
+ ::Serialize(s, txTo.nVersion, nType, nVersion);
+ // Serialize vin
+ unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size();
+ ::WriteCompactSize(s, nInputs);
+ for (unsigned int nInput = 0; nInput < nInputs; nInput++)
+ SerializeInput(s, nInput, nType, nVersion);
+ // Serialize vout
+ unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size());
+ ::WriteCompactSize(s, nOutputs);
+ for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++)
+ SerializeOutput(s, nOutput, nType, nVersion);
+ // Serialie nLockTime
+ ::Serialize(s, txTo.nLockTime, nType, nVersion);
+ }
+};
+}
- // Let the others update at will
- for (unsigned int i = 0; i < txTmp.vin.size(); i++)
- if (i != nIn)
- txTmp.vin[i].nSequence = 0;
+uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
+{
+ if (nIn >= txTo.vin.size()) {
+ LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn);
+ return 1;
}
- else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
- {
- // Only lock-in the txout payee at same index as txin
- unsigned int nOut = nIn;
- if (nOut >= txTmp.vout.size())
- {
- LogPrintf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut);
+
+ // Check for invalid use of SIGHASH_SINGLE
+ if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
+ if (nIn >= txTo.vout.size()) {
+ LogPrintf("ERROR: SignatureHash() : nOut=%d out of range\n", nIn);
return 1;
}
- txTmp.vout.resize(nOut+1);
- for (unsigned int i = 0; i < nOut; i++)
- txTmp.vout[i].SetNull();
-
- // Let the others update at will
- for (unsigned int i = 0; i < txTmp.vin.size(); i++)
- if (i != nIn)
- txTmp.vin[i].nSequence = 0;
}
- // Blank out other inputs completely, not recommended for open transactions
- if (nHashType & SIGHASH_ANYONECANPAY)
- {
- txTmp.vin[0] = txTmp.vin[nIn];
- txTmp.vin.resize(1);
- }
+ // Wrapper to serialize only the necessary parts of the transaction being signed
+ CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType);
// Serialize and hash
CHashWriter ss(SER_GETHASH, 0);
diff --git a/src/test/Makefile.am b/src/test/Makefile.am
index a859eb1de8..c3495095d9 100644
--- a/src/test/Makefile.am
+++ b/src/test/Makefile.am
@@ -34,7 +34,7 @@ test_bitcoin_SOURCES = accounting_tests.cpp alert_tests.cpp \
netbase_tests.cpp pmt_tests.cpp rpc_tests.cpp script_P2SH_tests.cpp \
script_tests.cpp serialize_tests.cpp sigopcount_tests.cpp test_bitcoin.cpp \
transaction_tests.cpp uint160_tests.cpp uint256_tests.cpp util_tests.cpp \
- wallet_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES)
+ wallet_tests.cpp sighash_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES)
nodist_test_bitcoin_SOURCES = $(BUILT_SOURCES)
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 9ef932b5b4..7f6f141c62 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -19,7 +19,7 @@ using namespace boost::assign;
typedef vector<unsigned char> valtype;
-extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
+extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
BOOST_AUTO_TEST_SUITE(multisig_tests)
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index dfa5529b87..32be914414 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -21,7 +21,7 @@ using namespace std;
using namespace json_spirit;
using namespace boost::algorithm;
-extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
+extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
new file mode 100644
index 0000000000..f098d46186
--- /dev/null
+++ b/src/test/sighash_tests.cpp
@@ -0,0 +1,120 @@
+#include <boost/test/unit_test.hpp>
+
+#include "main.h"
+#include "util.h"
+
+extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
+
+// Old script.cpp SignatureHash function
+uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
+{
+ if (nIn >= txTo.vin.size())
+ {
+ printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn);
+ return 1;
+ }
+ CTransaction txTmp(txTo);
+
+ // In case concatenating two scripts ends up with two codeseparators,
+ // or an extra one at the end, this prevents all those possible incompatibilities.
+ scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));
+
+ // Blank out other inputs' signatures
+ for (unsigned int i = 0; i < txTmp.vin.size(); i++)
+ txTmp.vin[i].scriptSig = CScript();
+ txTmp.vin[nIn].scriptSig = scriptCode;
+
+ // Blank out some of the outputs
+ if ((nHashType & 0x1f) == SIGHASH_NONE)
+ {
+ // Wildcard payee
+ txTmp.vout.clear();
+
+ // Let the others update at will
+ for (unsigned int i = 0; i < txTmp.vin.size(); i++)
+ if (i != nIn)
+ txTmp.vin[i].nSequence = 0;
+ }
+ else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
+ {
+ // Only lock-in the txout payee at same index as txin
+ unsigned int nOut = nIn;
+ if (nOut >= txTmp.vout.size())
+ {
+ printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut);
+ return 1;
+ }
+ txTmp.vout.resize(nOut+1);
+ for (unsigned int i = 0; i < nOut; i++)
+ txTmp.vout[i].SetNull();
+
+ // Let the others update at will
+ for (unsigned int i = 0; i < txTmp.vin.size(); i++)
+ if (i != nIn)
+ txTmp.vin[i].nSequence = 0;
+ }
+
+ // Blank out other inputs completely, not recommended for open transactions
+ if (nHashType & SIGHASH_ANYONECANPAY)
+ {
+ txTmp.vin[0] = txTmp.vin[nIn];
+ txTmp.vin.resize(1);
+ }
+
+ // Serialize and hash
+ CHashWriter ss(SER_GETHASH, 0);
+ ss << txTmp << nHashType;
+ return ss.GetHash();
+}
+
+void static RandomScript(CScript &script) {
+ static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR};
+ script = CScript();
+ int ops = (insecure_rand() % 10);
+ for (int i=0; i<ops; i++)
+ script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))];
+}
+
+void static RandomTransaction(CTransaction &tx, bool fSingle) {
+ tx.nVersion = insecure_rand();
+ tx.vin.clear();
+ tx.vout.clear();
+ tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0;
+ int ins = (insecure_rand() % 4) + 1;
+ int outs = fSingle ? ins : (insecure_rand() % 4) + 1;
+ for (int in = 0; in < ins; in++) {
+ tx.vin.push_back(CTxIn());
+ CTxIn &txin = tx.vin.back();
+ txin.prevout.hash = GetRandHash();
+ txin.prevout.n = insecure_rand() % 4;
+ RandomScript(txin.scriptSig);
+ txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1;
+ }
+ for (int out = 0; out < outs; out++) {
+ tx.vout.push_back(CTxOut());
+ CTxOut &txout = tx.vout.back();
+ txout.nValue = insecure_rand() % 100000000;
+ RandomScript(txout.scriptPubKey);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE(sighash_tests)
+
+BOOST_AUTO_TEST_CASE(sighash_test)
+{
+ seed_insecure_rand(false);
+
+ for (int i=0; i<50000; i++) {
+ int nHashType = insecure_rand();
+ CTransaction txTo;
+ RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE);
+ CScript scriptCode;
+ RandomScript(scriptCode);
+ int nIn = insecure_rand() % txTo.vin.size();
+ BOOST_CHECK(SignatureHash(scriptCode, txTo, nIn, nHashType) ==
+ SignatureHashOld(scriptCode, txTo, nIn, nHashType));
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
index 635fda1b42..efcd59d5f1 100644
--- a/src/walletdb.cpp
+++ b/src/walletdb.cpp
@@ -306,6 +306,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}
CKey key;
CPrivKey pkey;
+ uint256 hash = 0;
+
if (strType == "key")
{
wss.nKeys++;
@@ -315,14 +317,40 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssValue >> wkey;
pkey = wkey.vchPrivKey;
}
- if (!key.SetPrivKey(pkey, vchPubKey.IsCompressed()))
+
+ // Old wallets store keys as "key" [pubkey] => [privkey]
+ // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
+ // using EC operations as a checksum.
+ // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
+ // remaining backwards-compatible.
+ try
{
- strErr = "Error reading wallet database: CPrivKey corrupt";
- return false;
+ ssValue >> hash;
+ }
+ catch(...){}
+
+ bool fSkipCheck = false;
+
+ if (hash != 0)
+ {
+ // hash pubkey/privkey to accelerate wallet load
+ std::vector<unsigned char> vchKey;
+ vchKey.reserve(vchPubKey.size() + pkey.size());
+ vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
+ vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
+
+ if (Hash(vchKey.begin(), vchKey.end()) != hash)
+ {
+ strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
+ return false;
+ }
+
+ fSkipCheck = true;
}
- if (key.GetPubKey() != vchPubKey)
+
+ if (!key.Load(pkey, vchPubKey, fSkipCheck))
{
- strErr = "Error reading wallet database: CPrivKey pubkey inconsistency";
+ strErr = "Error reading wallet database: CPrivKey corrupt";
return false;
}
if (!pwallet->LoadKey(key, vchPubKey))
diff --git a/src/walletdb.h b/src/walletdb.h
index 09ebebe5ac..2d01a5cf74 100644
--- a/src/walletdb.h
+++ b/src/walletdb.h
@@ -93,8 +93,14 @@ public:
if (!Write(std::make_pair(std::string("keymeta"), vchPubKey),
keyMeta))
return false;
-
- return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false);
+
+ // hash pubkey/privkey to accelerate wallet load
+ std::vector<unsigned char> vchKey;
+ vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
+ vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
+ vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
+
+ return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
}
bool WriteCryptedKey(const CPubKey& vchPubKey,