aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/base58.h72
-rw-r--r--src/bitcoinrpc.cpp3
-rw-r--r--src/chainparams.cpp27
-rw-r--r--src/chainparams.h10
-rw-r--r--src/crypter.cpp3
-rw-r--r--src/hash.cpp41
-rw-r--r--src/hash.h10
-rw-r--r--src/init.cpp5
-rw-r--r--src/key.cpp199
-rw-r--r--src/key.h46
-rw-r--r--src/main.cpp234
-rw-r--r--src/main.h6
-rw-r--r--src/qt/bitcoinstrings.cpp2
-rw-r--r--src/qt/locale/bitcoin_en.ts6
-rw-r--r--src/rpcwallet.cpp23
-rw-r--r--src/script.cpp26
-rw-r--r--src/script.h11
-rw-r--r--src/test/DoS_tests.cpp29
-rw-r--r--src/test/bip32_tests.cpp116
-rw-r--r--src/test/canonical_tests.cpp4
-rw-r--r--src/test/hmac_tests.cpp125
-rw-r--r--src/test/util_tests.cpp11
-rw-r--r--src/util.h15
-rw-r--r--src/wallet.cpp9
-rw-r--r--src/wallet.h4
25 files changed, 875 insertions, 162 deletions
diff --git a/src/base58.h b/src/base58.h
index 630d6fe9aa..aabae8de88 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -177,8 +177,8 @@ inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>
class CBase58Data
{
protected:
- // the version byte
- unsigned char nVersion;
+ // the version byte(s)
+ std::vector<unsigned char> vchVersion;
// the actually encoded data
typedef std::vector<unsigned char, zero_after_free_allocator<unsigned char> > vector_uchar;
@@ -186,38 +186,38 @@ protected:
CBase58Data()
{
- nVersion = 0;
+ vchVersion.clear();
vchData.clear();
}
- void SetData(int nVersionIn, const void* pdata, size_t nSize)
+ void SetData(const std::vector<unsigned char> &vchVersionIn, const void* pdata, size_t nSize)
{
- nVersion = nVersionIn;
+ vchVersion = vchVersionIn;
vchData.resize(nSize);
if (!vchData.empty())
memcpy(&vchData[0], pdata, nSize);
}
- void SetData(int nVersionIn, const unsigned char *pbegin, const unsigned char *pend)
+ void SetData(const std::vector<unsigned char> &vchVersionIn, const unsigned char *pbegin, const unsigned char *pend)
{
- SetData(nVersionIn, (void*)pbegin, pend - pbegin);
+ SetData(vchVersionIn, (void*)pbegin, pend - pbegin);
}
public:
- bool SetString(const char* psz)
+ bool SetString(const char* psz, unsigned int nVersionBytes = 1)
{
std::vector<unsigned char> vchTemp;
DecodeBase58Check(psz, vchTemp);
- if (vchTemp.empty())
+ if (vchTemp.size() < nVersionBytes)
{
vchData.clear();
- nVersion = 0;
+ vchVersion.clear();
return false;
}
- nVersion = vchTemp[0];
- vchData.resize(vchTemp.size() - 1);
+ vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes);
+ vchData.resize(vchTemp.size() - nVersionBytes);
if (!vchData.empty())
- memcpy(&vchData[0], &vchTemp[1], vchData.size());
+ memcpy(&vchData[0], &vchTemp[nVersionBytes], vchData.size());
OPENSSL_cleanse(&vchTemp[0], vchData.size());
return true;
}
@@ -229,15 +229,15 @@ public:
std::string ToString() const
{
- std::vector<unsigned char> vch(1, nVersion);
+ std::vector<unsigned char> vch = vchVersion;
vch.insert(vch.end(), vchData.begin(), vchData.end());
return EncodeBase58Check(vch);
}
int CompareTo(const CBase58Data& b58) const
{
- if (nVersion < b58.nVersion) return -1;
- if (nVersion > b58.nVersion) return 1;
+ if (vchVersion < b58.vchVersion) return -1;
+ if (vchVersion > b58.vchVersion) return 1;
if (vchData < b58.vchData) return -1;
if (vchData > b58.vchData) return 1;
return 0;
@@ -289,8 +289,8 @@ public:
bool IsValid() const
{
bool fCorrectSize = vchData.size() == 20;
- bool fKnownVersion = nVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
- nVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
+ bool fKnownVersion = vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
+ vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
return fCorrectSize && fKnownVersion;
}
@@ -318,16 +318,16 @@ public:
return CNoDestination();
uint160 id;
memcpy(&id, &vchData[0], 20);
- if (nVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
+ if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
return CKeyID(id);
- else if (nVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS))
+ else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS))
return CScriptID(id);
else
return CNoDestination();
}
bool GetKeyID(CKeyID &keyID) const {
- if (!IsValid() || nVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
+ if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
return false;
uint160 id;
memcpy(&id, &vchData[0], 20);
@@ -336,7 +336,7 @@ public:
}
bool IsScript() const {
- return IsValid() && nVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
+ return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
}
};
@@ -366,7 +366,7 @@ public:
bool IsValid() const
{
bool fExpectedFormat = vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1);
- bool fCorrectVersion = nVersion == Params().Base58Prefix(CChainParams::SECRET_KEY);
+ bool fCorrectVersion = vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY);
return fExpectedFormat && fCorrectVersion;
}
@@ -390,4 +390,30 @@ public:
}
};
+
+template<typename K, int Size, CChainParams::Base58Type Type> class CBitcoinExtKeyBase : public CBase58Data
+{
+public:
+ void SetKey(const K &key) {
+ unsigned char vch[Size];
+ key.Encode(vch);
+ SetData(Params().Base58Prefix(Type), vch, vch+Size);
+ }
+
+ K GetKey() {
+ K ret;
+ ret.Decode(&vchData[0], &vchData[Size]);
+ return ret;
+ }
+
+ CBitcoinExtKeyBase(const K &key) {
+ SetKey(key);
+ }
+
+ CBitcoinExtKeyBase() {}
+};
+
+typedef CBitcoinExtKeyBase<CExtKey, 74, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey;
+typedef CBitcoinExtKeyBase<CExtPubKey, 74, CChainParams::EXT_PUBLIC_KEY> CBitcoinExtPubKey;
+
#endif // BITCOIN_BASE58_H
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 7a3e6560ab..d22809ce69 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -476,7 +476,7 @@ bool HTTPAuthorized(map<string, string>& mapHeaders)
return false;
string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
string strUserPass = DecodeBase64(strUserPass64);
- return strUserPass == strRPCUserColonPass;
+ return TimingResistantEqual(strUserPass, strRPCUserColonPass);
}
//
@@ -1200,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/chainparams.cpp b/src/chainparams.cpp
index 3bb62fb793..0795f09765 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -10,6 +10,10 @@
#include "protocol.h"
#include "util.h"
+#include <boost/assign/list_of.hpp>
+
+using namespace boost::assign;
+
//
// Main network
//
@@ -141,9 +145,11 @@ public:
vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org"));
vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org"));
- base58Prefixes[PUBKEY_ADDRESS] = 0;
- base58Prefixes[SCRIPT_ADDRESS] = 5;
- base58Prefixes[SECRET_KEY] = 128;
+ base58Prefixes[PUBKEY_ADDRESS] = list_of(0);
+ base58Prefixes[SCRIPT_ADDRESS] = list_of(5);
+ base58Prefixes[SECRET_KEY] = list_of(128);
+ base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x88)(0xB2)(0x1E);
+ base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x88)(0xAD)(0xE4);
// Convert the pnSeeds array into usable address objects.
for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
@@ -196,17 +202,18 @@ public:
genesis.nTime = 1296688602;
genesis.nNonce = 414098458;
hashGenesisBlock = genesis.GetHash();
- assert(hashGenesisBlock == uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
+ assert(hashGenesisBlock == uint256("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
vFixedSeeds.clear();
vSeeds.clear();
vSeeds.push_back(CDNSSeedData("bitcoin.petertodd.org", "testnet-seed.bitcoin.petertodd.org"));
vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me"));
- base58Prefixes[PUBKEY_ADDRESS] = 111;
- base58Prefixes[SCRIPT_ADDRESS] = 196;
- base58Prefixes[SECRET_KEY] = 239;
-
+ base58Prefixes[PUBKEY_ADDRESS] = list_of(111);
+ base58Prefixes[SCRIPT_ADDRESS] = list_of(196);
+ base58Prefixes[SECRET_KEY] = list_of(239);
+ base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x35)(0x87)(0xCF);
+ base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x35)(0x83)(0x94);
}
virtual Network NetworkID() const { return CChainParams::TESTNET; }
};
@@ -234,10 +241,6 @@ public:
assert(hashGenesisBlock == uint256("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"));
vSeeds.clear(); // Regtest mode doesn't have any DNS seeds.
-
- base58Prefixes[PUBKEY_ADDRESS] = 0;
- base58Prefixes[SCRIPT_ADDRESS] = 5;
- base58Prefixes[SECRET_KEY] = 128;
}
virtual bool RequireRPCPassword() const { return false; }
diff --git a/src/chainparams.h b/src/chainparams.h
index 572712b589..0dac79aed4 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -45,6 +45,8 @@ public:
PUBKEY_ADDRESS,
SCRIPT_ADDRESS,
SECRET_KEY,
+ EXT_PUBLIC_KEY,
+ EXT_SECRET_KEY,
MAX_BASE58_TYPES
};
@@ -60,7 +62,7 @@ public:
const string& DataDir() const { return strDataDir; }
virtual Network NetworkID() const = 0;
const vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
- int Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
+ const std::vector<unsigned char> &Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
virtual const vector<CAddress>& FixedSeeds() const = 0;
int RPCPort() const { return nRPCPort; }
protected:
@@ -76,7 +78,7 @@ protected:
int nSubsidyHalvingInterval;
string strDataDir;
vector<CDNSSeedData> vSeeds;
- int base58Prefixes[MAX_BASE58_TYPES];
+ std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
};
/**
@@ -99,4 +101,8 @@ inline bool TestNet() {
return Params().NetworkID() == CChainParams::TESTNET;
}
+inline bool RegTest() {
+ return Params().NetworkID() == CChainParams::REGTEST;
+}
+
#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/hash.cpp b/src/hash.cpp
index bddd8abf38..7b054bd154 100644
--- a/src/hash.cpp
+++ b/src/hash.cpp
@@ -56,3 +56,44 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char
return h1;
}
+
+int HMAC_SHA512_Init(HMAC_SHA512_CTX *pctx, const void *pkey, size_t len)
+{
+ unsigned char key[128];
+ if (len <= 128)
+ {
+ memcpy(key, pkey, len);
+ memset(key + len, 0, 128-len);
+ }
+ else
+ {
+ SHA512_CTX ctxKey;
+ SHA512_Init(&ctxKey);
+ SHA512_Update(&ctxKey, pkey, len);
+ SHA512_Final(key, &ctxKey);
+ memset(key + 64, 0, 64);
+ }
+
+ for (int n=0; n<128; n++)
+ key[n] ^= 0x5c;
+ SHA512_Init(&pctx->ctxOuter);
+ SHA512_Update(&pctx->ctxOuter, key, 128);
+
+ for (int n=0; n<128; n++)
+ key[n] ^= 0x5c ^ 0x36;
+ SHA512_Init(&pctx->ctxInner);
+ return SHA512_Update(&pctx->ctxInner, key, 128);
+}
+
+int HMAC_SHA512_Update(HMAC_SHA512_CTX *pctx, const void *pdata, size_t len)
+{
+ return SHA512_Update(&pctx->ctxInner, pdata, len);
+}
+
+int HMAC_SHA512_Final(unsigned char *pmd, HMAC_SHA512_CTX *pctx)
+{
+ unsigned char buf[64];
+ SHA512_Final(buf, &pctx->ctxInner);
+ SHA512_Update(&pctx->ctxOuter, buf, 64);
+ return SHA512_Final(pmd, &pctx->ctxOuter);
+}
diff --git a/src/hash.h b/src/hash.h
index 536ab71165..880468a2d2 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -123,4 +123,14 @@ inline uint160 Hash160(const std::vector<unsigned char>& vch)
unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char>& vDataToHash);
+typedef struct
+{
+ SHA512_CTX ctxInner;
+ SHA512_CTX ctxOuter;
+} HMAC_SHA512_CTX;
+
+int HMAC_SHA512_Init(HMAC_SHA512_CTX *pctx, const void *pkey, size_t len);
+int HMAC_SHA512_Update(HMAC_SHA512_CTX *pctx, const void *pdata, size_t len);
+int HMAC_SHA512_Final(unsigned char *pmd, HMAC_SHA512_CTX *pctx);
+
#endif
diff --git a/src/init.cpp b/src/init.cpp
index 49d6e1fde4..9f3d526414 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -228,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";
@@ -424,6 +424,7 @@ bool AppInit2(boost::thread_group& threadGroup)
fDebug = GetBoolArg("-debug", false);
fBenchmark = GetBoolArg("-benchmark", false);
+ mempool.fChecks = GetBoolArg("-checkmempool", RegTest());
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = GetArg("-par", 0);
@@ -504,7 +505,7 @@ bool AppInit2(boost::thread_group& threadGroup)
// 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\n"), strWalletFile.c_str(), strDataDir.c_str()));
+ 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";
diff --git a/src/key.cpp b/src/key.cpp
index 1ab4c62ebf..85dc9cda2b 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <openssl/bn.h>
#include <openssl/ecdsa.h>
#include <openssl/rand.h>
#include <openssl/obj_mac.h>
@@ -194,9 +195,26 @@ public:
}
bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) {
+ vchSig.clear();
+ ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
+ if (sig == NULL)
+ return false;
+ if (BN_is_odd(sig->s)) {
+ // enforce even S values, by negating the value (modulo the order) if odd
+ BN_CTX *ctx = BN_CTX_new();
+ BN_CTX_start(ctx);
+ const EC_GROUP *group = EC_KEY_get0_group(pkey);
+ BIGNUM *order = BN_CTX_get(ctx);
+ EC_GROUP_get_order(group, order, ctx);
+ BN_sub(sig->s, order, sig->s);
+ BN_CTX_end(ctx);
+ BN_CTX_free(ctx);
+ }
unsigned int nSize = ECDSA_size(pkey);
vchSig.resize(nSize); // Make sure it is big enough
- assert(ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey));
+ unsigned char *pos = &vchSig[0];
+ nSize = i2d_ECDSA_SIG(sig, &pos);
+ ECDSA_SIG_free(sig);
vchSig.resize(nSize); // Shrink to fit actual size
return true;
}
@@ -254,6 +272,57 @@ public:
ECDSA_SIG_free(sig);
return ret;
}
+
+ static bool TweakSecret(unsigned char vchSecretOut[32], const unsigned char vchSecretIn[32], const unsigned char vchTweak[32])
+ {
+ bool ret = true;
+ BN_CTX *ctx = BN_CTX_new();
+ BN_CTX_start(ctx);
+ BIGNUM *bnSecret = BN_CTX_get(ctx);
+ BIGNUM *bnTweak = BN_CTX_get(ctx);
+ BIGNUM *bnOrder = BN_CTX_get(ctx);
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1);
+ EC_GROUP_get_order(group, bnOrder, ctx); // what a grossly inefficient way to get the (constant) group order...
+ BN_bin2bn(vchTweak, 32, bnTweak);
+ if (BN_cmp(bnTweak, bnOrder) >= 0)
+ ret = false; // extremely unlikely
+ BN_bin2bn(vchSecretIn, 32, bnSecret);
+ BN_add(bnSecret, bnSecret, bnTweak);
+ BN_nnmod(bnSecret, bnSecret, bnOrder, ctx);
+ if (BN_is_zero(bnSecret))
+ ret = false; // ridiculously unlikely
+ int nBits = BN_num_bits(bnSecret);
+ memset(vchSecretOut, 0, 32);
+ BN_bn2bin(bnSecret, &vchSecretOut[32-(nBits+7)/8]);
+ EC_GROUP_free(group);
+ BN_CTX_end(ctx);
+ BN_CTX_free(ctx);
+ return ret;
+ }
+
+ bool TweakPublic(const unsigned char vchTweak[32]) {
+ bool ret = true;
+ BN_CTX *ctx = BN_CTX_new();
+ BN_CTX_start(ctx);
+ BIGNUM *bnTweak = BN_CTX_get(ctx);
+ BIGNUM *bnOrder = BN_CTX_get(ctx);
+ BIGNUM *bnOne = BN_CTX_get(ctx);
+ const EC_GROUP *group = EC_KEY_get0_group(pkey);
+ EC_GROUP_get_order(group, bnOrder, ctx); // what a grossly inefficient way to get the (constant) group order...
+ BN_bin2bn(vchTweak, 32, bnTweak);
+ if (BN_cmp(bnTweak, bnOrder) >= 0)
+ ret = false; // extremely unlikely
+ EC_POINT *point = EC_POINT_dup(EC_KEY_get0_public_key(pkey), group);
+ BN_one(bnOne);
+ EC_POINT_mul(group, point, bnTweak, point, bnOne, ctx);
+ if (EC_POINT_is_at_infinity(group, point))
+ ret = false; // ridiculously unlikely
+ EC_KEY_set_public_key(pkey, point);
+ EC_POINT_free(point);
+ BN_CTX_end(ctx);
+ BN_CTX_free(ctx);
+ return ret;
+ }
};
}; // end of anonymous namespace
@@ -394,3 +463,131 @@ bool CPubKey::Decompress() {
key.GetPubKey(*this, false);
return true;
}
+
+void static BIP32Hash(const unsigned char chainCode[32], unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]) {
+ unsigned char num[4];
+ num[0] = (nChild >> 24) & 0xFF;
+ num[1] = (nChild >> 16) & 0xFF;
+ num[2] = (nChild >> 8) & 0xFF;
+ num[3] = (nChild >> 0) & 0xFF;
+ HMAC_SHA512_CTX ctx;
+ HMAC_SHA512_Init(&ctx, chainCode, 32);
+ HMAC_SHA512_Update(&ctx, &header, 1);
+ HMAC_SHA512_Update(&ctx, data, 32);
+ HMAC_SHA512_Update(&ctx, num, 4);
+ HMAC_SHA512_Final(output, &ctx);
+}
+
+bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const {
+ assert(IsValid());
+ assert(IsCompressed());
+ unsigned char out[64];
+ LockObject(out);
+ if ((nChild >> 31) == 0) {
+ CPubKey pubkey = GetPubKey();
+ assert(pubkey.begin() + 33 == pubkey.end());
+ BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, out);
+ } else {
+ assert(begin() + 32 == end());
+ BIP32Hash(cc, nChild, 0, begin(), out);
+ }
+ memcpy(ccChild, out+32, 32);
+ bool ret = CECKey::TweakSecret((unsigned char*)keyChild.begin(), begin(), out);
+ UnlockObject(out);
+ keyChild.fCompressed = true;
+ keyChild.fValid = ret;
+ return ret;
+}
+
+bool CPubKey::Derive(CPubKey& pubkeyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const {
+ assert(IsValid());
+ assert((nChild >> 31) == 0);
+ assert(begin() + 33 == end());
+ unsigned char out[64];
+ BIP32Hash(cc, nChild, *begin(), begin()+1, out);
+ memcpy(ccChild, out+32, 32);
+ CECKey key;
+ bool ret = key.SetPubKey(*this);
+ ret &= key.TweakPublic(out);
+ key.GetPubKey(pubkeyChild, true);
+ return ret;
+}
+
+bool CExtKey::Derive(CExtKey &out, unsigned int nChild) const {
+ out.nDepth = nDepth + 1;
+ CKeyID id = key.GetPubKey().GetID();
+ memcpy(&out.vchFingerprint[0], &id, 4);
+ out.nChild = nChild;
+ return key.Derive(out.key, out.vchChainCode, nChild, vchChainCode);
+}
+
+void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) {
+ static const char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
+ HMAC_SHA512_CTX ctx;
+ HMAC_SHA512_Init(&ctx, hashkey, sizeof(hashkey));
+ HMAC_SHA512_Update(&ctx, seed, nSeedLen);
+ unsigned char out[64];
+ LockObject(out);
+ HMAC_SHA512_Final(out, &ctx);
+ key.Set(&out[0], &out[32], true);
+ memcpy(vchChainCode, &out[32], 32);
+ UnlockObject(out);
+ nDepth = 0;
+ nChild = 0;
+ memset(vchFingerprint, 0, sizeof(vchFingerprint));
+}
+
+CExtPubKey CExtKey::Neuter() const {
+ CExtPubKey ret;
+ ret.nDepth = nDepth;
+ memcpy(&ret.vchFingerprint[0], &vchFingerprint[0], 4);
+ ret.nChild = nChild;
+ ret.pubkey = key.GetPubKey();
+ memcpy(&ret.vchChainCode[0], &vchChainCode[0], 32);
+ return ret;
+}
+
+void CExtKey::Encode(unsigned char code[74]) const {
+ code[0] = nDepth;
+ memcpy(code+1, vchFingerprint, 4);
+ code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
+ code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
+ memcpy(code+9, vchChainCode, 32);
+ code[41] = 0;
+ assert(key.size() == 32);
+ memcpy(code+42, key.begin(), 32);
+}
+
+void CExtKey::Decode(const unsigned char code[74]) {
+ nDepth = code[0];
+ memcpy(vchFingerprint, code+1, 4);
+ nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
+ memcpy(vchChainCode, code+9, 32);
+ key.Set(code+42, code+74, true);
+}
+
+void CExtPubKey::Encode(unsigned char code[74]) const {
+ code[0] = nDepth;
+ memcpy(code+1, vchFingerprint, 4);
+ code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
+ code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
+ memcpy(code+9, vchChainCode, 32);
+ assert(pubkey.size() == 33);
+ memcpy(code+41, pubkey.begin(), 33);
+}
+
+void CExtPubKey::Decode(const unsigned char code[74]) {
+ nDepth = code[0];
+ memcpy(vchFingerprint, code+1, 4);
+ nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
+ memcpy(vchChainCode, code+9, 32);
+ pubkey.Set(code+41, code+74);
+}
+
+bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const {
+ out.nDepth = nDepth + 1;
+ CKeyID id = pubkey.GetID();
+ memcpy(&out.vchFingerprint[0], &id, 4);
+ out.nChild = nChild;
+ return pubkey.Derive(out.pubkey, out.vchChainCode, nChild, vchChainCode);
+}
diff --git a/src/key.h b/src/key.h
index ce469ad298..75431e944f 100644
--- a/src/key.h
+++ b/src/key.h
@@ -161,6 +161,9 @@ public:
// Turn this public key into an uncompressed public key.
bool Decompress();
+
+ // Derive BIP32 child pubkey.
+ bool Derive(CPubKey& pubkeyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const;
};
@@ -201,6 +204,10 @@ public:
UnlockObject(vch);
}
+ friend bool operator==(const CKey &a, const CKey &b) {
+ return a.fCompressed == b.fCompressed && memcmp(&a.vch[0], &b.vch[0], 32);
+ }
+
// Initialize using begin and end iterators to byte data.
template<typename T>
void Set(const T pbegin, const T pend, bool fCompressedIn) {
@@ -251,6 +258,45 @@ public:
// 0x1D = second key with even y, 0x1E = second key with odd y,
// add 0x04 for compressed keys.
bool SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const;
+
+ // Derive BIP32 child key.
+ bool Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const;
+};
+
+struct CExtPubKey {
+ unsigned char nDepth;
+ unsigned char vchFingerprint[4];
+ unsigned int nChild;
+ unsigned char vchChainCode[32];
+ CPubKey pubkey;
+
+ friend bool operator==(const CExtPubKey &a, const CExtPubKey &b) {
+ return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild &&
+ memcmp(&a.vchChainCode[0], &b.vchChainCode[0], 32) == 0 && a.pubkey == b.pubkey;
+ }
+
+ void Encode(unsigned char code[74]) const;
+ void Decode(const unsigned char code[74]);
+ bool Derive(CExtPubKey &out, unsigned int nChild) const;
+};
+
+struct CExtKey {
+ unsigned char nDepth;
+ unsigned char vchFingerprint[4];
+ unsigned int nChild;
+ unsigned char vchChainCode[32];
+ CKey key;
+
+ friend bool operator==(const CExtKey &a, const CExtKey &b) {
+ return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild &&
+ memcmp(&a.vchChainCode[0], &b.vchChainCode[0], 32) == 0 && a.key == b.key;
+ }
+
+ void Encode(unsigned char code[74]) const;
+ void Decode(const unsigned char code[74]);
+ bool Derive(CExtKey &out, unsigned int nChild) const;
+ CExtPubKey Neuter() const;
+ void SetMaster(const unsigned char *seed, unsigned int nSeedLen);
};
#endif
diff --git a/src/main.cpp b/src/main.cpp
index 50cff7f51d..2ccd5131d1 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;
@@ -399,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
@@ -416,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());
@@ -436,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);
}
@@ -456,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);
@@ -793,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)
@@ -960,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.
@@ -980,15 +973,15 @@ bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive)
{
LOCK(cs);
uint256 hash = tx.GetHash();
+ if (fRecursive) {
+ for (unsigned int i = 0; i < tx.vout.size(); i++) {
+ std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i));
+ if (it != mapNextTx.end())
+ remove(*it->second.ptx, true);
+ }
+ }
if (mapTx.count(hash))
{
- if (fRecursive) {
- for (unsigned int i = 0; i < tx.vout.size(); i++) {
- std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i));
- if (it != mapNextTx.end())
- remove(*it->second.ptx, true);
- }
- }
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapNextTx.erase(txin.prevout);
mapTx.erase(hash);
@@ -1021,6 +1014,45 @@ void CTxMemPool::clear()
++nTransactionsUpdated;
}
+bool CTxMemPool::fChecks = false;
+
+void CTxMemPool::check(CCoinsViewCache *pcoins) const
+{
+ if (!fChecks)
+ return;
+
+ printf("Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());
+
+ LOCK(cs);
+ for (std::map<uint256, CTransaction>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
+ unsigned int i = 0;
+ BOOST_FOREACH(const CTxIn &txin, it->second.vin) {
+ // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
+ std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(txin.prevout.hash);
+ if (it2 != mapTx.end()) {
+ assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull());
+ } else {
+ CCoins &coins = pcoins->GetCoins(txin.prevout.hash);
+ assert(coins.IsAvailable(txin.prevout.n));
+ }
+ // Check whether its inputs are marked in mapNextTx.
+ std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout);
+ assert(it3 != mapNextTx.end());
+ assert(it3->second.ptx == &it->second);
+ assert(it3->second.n == i);
+ i++;
+ }
+ }
+ for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) {
+ uint256 hash = it->second.ptx->GetHash();
+ std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(hash);
+ assert(it2 != mapTx.end());
+ assert(&it2->second == it->second.ptx);
+ assert(it2->second.vin.size() > it->second.n);
+ assert(it->first == it->second.ptx->vin[it->second.n].prevout);
+ }
+}
+
void CTxMemPool::queryHashes(std::vector<uint256>& vtxid)
{
vtxid.clear();
@@ -1383,6 +1415,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)
@@ -1398,8 +1506,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) {
@@ -1902,6 +2009,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
{
+ mempool.check(pcoinsTip);
+
// All modifications to the coin state will be done in this cache.
// Only when all have succeeded, we push it to pcoinsTip.
CCoinsViewCache view(*pcoinsTip, true);
@@ -2015,7 +2124,8 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
BOOST_FOREACH(CTransaction& tx, vResurrect) {
// ignore validation errors in resurrected transactions
CValidationState stateDummy;
- mempool.accept(stateDummy, tx, false, NULL);
+ if (!mempool.accept(stateDummy, tx, false, NULL))
+ mempool.remove(tx, true);
}
// Delete redundant memory transactions that are in the connected branch
@@ -2024,6 +2134,8 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
mempool.removeConflicts(tx);
}
+ mempool.check(pcoinsTip);
+
// Update best block in wallet (so we can detect restored wallets)
if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0))
{
@@ -2110,11 +2222,14 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos
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 = block.GetTxHash(0);
- }
+ } else
+ CheckForkWarningConditionsOnNewFork(pindexNew);
if (!pblocktree->Flush())
return state.Abort(_("Failed to sync block index"));
@@ -2271,7 +2386,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
// Check merkle root
- if (fCheckMerkleRoot && block.hashMerkleRoot != block.BuildMerkleTree())
+ if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back())
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
return true;
@@ -3073,11 +3188,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: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
+ }
+ else if (fLargeWorkInvalidChainFound)
{
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: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
}
// Alerts
@@ -3598,21 +3717,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CInv inv(MSG_TX, tx.GetHash());
pfrom->AddInventoryKnown(inv);
- // Truncate messages to the size of the tx in them
- unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
- unsigned int oldSize = vMsg.size();
- if (nSize < oldSize) {
- vMsg.resize(nSize);
- printf("truncating oversized TX %s (%u -> %u)\n",
- tx.GetHash().ToString().c_str(),
- oldSize, nSize);
- }
-
bool fMissingInputs = false;
CValidationState state;
if (mempool.accept(state, tx, true, &fMissingInputs))
{
- RelayTransaction(tx, inv.hash, vMsg);
+ mempool.check(pcoinsTip);
+ RelayTransaction(tx, inv.hash);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
vEraseQueue.push_back(inv.hash);
@@ -3621,32 +3731,33 @@ 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());
}
+ mempool.check(pcoinsTip);
}
}
@@ -3655,7 +3766,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);
@@ -4142,9 +4253,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 cb0ee1aaa8..a690a2bc9c 100644
--- a/src/main.h
+++ b/src/main.h
@@ -1077,17 +1077,19 @@ public:
class CTxMemPool
{
public:
+ static bool fChecks;
mutable CCriticalSection cs;
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();
void queryHashes(std::vector<uint256>& vtxid);
void pruneSpent(const uint256& hash, CCoins &coins);
+ void check(CCoinsViewCache *pcoins) const;
unsigned long size()
{
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index 534e4f3d46..fc0e53887a 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -208,7 +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\n"),
+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/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index d7d792a458..5ae067d9b8 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -2716,10 +2716,8 @@ for example: alertnotify=echo %%s | mail -s &quot;Bitcoin Alert&quot; admin@foo.
</message>
<message>
<location line="+1"/>
- <source>Wallet %s resides outside data directory %s
-</source>
- <translation>Wallet %s resides outside data directory %s
-</translation>
+ <source>Wallet %s resides outside data directory %s</source>
+ <translation>Wallet %s resides outside data directory %s</translation>
</message>
<message>
<location line="+4"/>
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 5699fbfb6a..2df2e9f0d5 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -227,7 +227,10 @@ const char* GetOpName(opcodetype opcode)
}
}
-bool IsCanonicalPubKey(const valtype &vchPubKey) {
+bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) {
+ if (!(flags & SCRIPT_VERIFY_STRICTENC))
+ return true;
+
if (vchPubKey.size() < 33)
return error("Non-canonical public key: too short");
if (vchPubKey[0] == 0x04) {
@@ -242,7 +245,10 @@ bool IsCanonicalPubKey(const valtype &vchPubKey) {
return true;
}
-bool IsCanonicalSignature(const valtype &vchSig) {
+bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) {
+ if (!(flags & SCRIPT_VERIFY_STRICTENC))
+ return true;
+
// See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
// A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
// Where R and S are not negative (their first byte has its highest bit not set), and not
@@ -286,6 +292,11 @@ bool IsCanonicalSignature(const valtype &vchSig) {
if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80))
return error("Non-canonical signature: S value excessively padded");
+ if (flags & SCRIPT_VERIFY_EVEN_S) {
+ if (S[nLenS-1] & 1)
+ return error("Non-canonical signature: S value odd");
+ }
+
return true;
}
@@ -302,7 +313,6 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
if (script.size() > 10000)
return false;
int nOpCount = 0;
- bool fStrictEncodings = flags & SCRIPT_VERIFY_STRICTENC;
try
{
@@ -841,9 +851,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig));
- bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey)));
- if (fSuccess)
- fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags);
+ bool fSuccess = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) &&
+ CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags);
popstack(stack);
popstack(stack);
@@ -903,9 +912,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
valtype& vchPubKey = stacktop(-ikey);
// Check signature
- bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey)));
- if (fOk)
- fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags);
+ bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) &&
+ CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags);
if (fOk) {
isig++;
diff --git a/src/script.h b/src/script.h
index 03afe8b652..fd5b12921e 100644
--- a/src/script.h
+++ b/src/script.h
@@ -32,9 +32,10 @@ enum
enum
{
SCRIPT_VERIFY_NONE = 0,
- SCRIPT_VERIFY_P2SH = (1U << 0),
- SCRIPT_VERIFY_STRICTENC = (1U << 1),
- SCRIPT_VERIFY_NOCACHE = (1U << 2),
+ SCRIPT_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts
+ SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys
+ SCRIPT_VERIFY_EVEN_S = (1U << 2), // enforce even S values in signatures (depends on STRICTENC)
+ SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it)
};
enum txnouttype
@@ -665,8 +666,8 @@ public:
}
};
-bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey);
-bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig);
+bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey, unsigned int flags);
+bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig, unsigned int flags);
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
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/bip32_tests.cpp b/src/test/bip32_tests.cpp
new file mode 100644
index 0000000000..66d2330fbf
--- /dev/null
+++ b/src/test/bip32_tests.cpp
@@ -0,0 +1,116 @@
+#include <boost/test/unit_test.hpp>
+
+#include <string>
+#include <vector>
+
+#include "key.h"
+#include "base58.h"
+#include "uint256.h"
+#include "util.h"
+
+struct TestDerivation {
+ std::string pub;
+ std::string prv;
+ unsigned int nChild;
+};
+
+struct TestVector {
+ std::string strHexMaster;
+ std::vector<TestDerivation> vDerive;
+
+ TestVector(std::string strHexMasterIn) : strHexMaster(strHexMasterIn) {}
+
+ TestVector& operator()(std::string pub, std::string prv, unsigned int nChild) {
+ vDerive.push_back(TestDerivation());
+ TestDerivation &der = vDerive.back();
+ der.pub = pub;
+ der.prv = prv;
+ der.nChild = nChild;
+ return *this;
+ }
+};
+
+TestVector test1 =
+ TestVector("000102030405060708090a0b0c0d0e0f")
+ ("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
+ "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
+ 0x80000000)
+ ("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",
+ "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
+ 1)
+ ("xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",
+ "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
+ 0x80000002)
+ ("xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
+ "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
+ 2)
+ ("xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV",
+ "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
+ 1000000000)
+ ("xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy",
+ "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
+ 0);
+
+TestVector test2 =
+ TestVector("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542")
+ ("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB",
+ "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
+ 0)
+ ("xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
+ "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
+ 0xFFFFFFFF)
+ ("xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a",
+ "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
+ 1)
+ ("xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon",
+ "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
+ 0xFFFFFFFE)
+ ("xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
+ "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
+ 2)
+ ("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt",
+ "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
+ 0);
+
+void RunTest(const TestVector &test) {
+ std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
+ CExtKey key;
+ CExtPubKey pubkey;
+ key.SetMaster(&seed[0], seed.size());
+ pubkey = key.Neuter();
+ BOOST_FOREACH(const TestDerivation &derive, test.vDerive) {
+ unsigned char data[74];
+ key.Encode(data);
+ pubkey.Encode(data);
+ // Test private key
+ CBitcoinExtKey b58key; b58key.SetKey(key);
+ BOOST_CHECK(b58key.ToString() == derive.prv);
+ // Test public key
+ CBitcoinExtPubKey b58pubkey; b58pubkey.SetKey(pubkey);
+ BOOST_CHECK(b58pubkey.ToString() == derive.pub);
+ // Derive new keys
+ CExtKey keyNew;
+ BOOST_CHECK(key.Derive(keyNew, derive.nChild));
+ CExtPubKey pubkeyNew = keyNew.Neuter();
+ if (!(derive.nChild & 0x80000000)) {
+ // Compare with public derivation
+ CExtPubKey pubkeyNew2;
+ BOOST_CHECK(pubkey.Derive(pubkeyNew2, derive.nChild));
+ BOOST_CHECK(pubkeyNew == pubkeyNew2);
+ }
+ key = keyNew;
+ pubkey = pubkeyNew;
+ }
+}
+
+BOOST_AUTO_TEST_SUITE(bip32_tests)
+
+BOOST_AUTO_TEST_CASE(bip32_test1) {
+ RunTest(test1);
+}
+
+BOOST_AUTO_TEST_CASE(bip32_test2) {
+ RunTest(test2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/canonical_tests.cpp b/src/test/canonical_tests.cpp
index 42d21f8ac5..09988da259 100644
--- a/src/test/canonical_tests.cpp
+++ b/src/test/canonical_tests.cpp
@@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(script_canon)
string test = tv.get_str();
if (IsHex(test)) {
std::vector<unsigned char> sig = ParseHex(test);
- BOOST_CHECK_MESSAGE(IsCanonicalSignature(sig), test);
+ BOOST_CHECK_MESSAGE(IsCanonicalSignature(sig, SCRIPT_VERIFY_STRICTENC), test);
BOOST_CHECK_MESSAGE(IsCanonicalSignature_OpenSSL(sig), test);
}
}
@@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(script_noncanon)
string test = tv.get_str();
if (IsHex(test)) {
std::vector<unsigned char> sig = ParseHex(test);
- BOOST_CHECK_MESSAGE(!IsCanonicalSignature(sig), test);
+ BOOST_CHECK_MESSAGE(!IsCanonicalSignature(sig, SCRIPT_VERIFY_STRICTENC), test);
BOOST_CHECK_MESSAGE(!IsCanonicalSignature_OpenSSL(sig), test);
}
}
diff --git a/src/test/hmac_tests.cpp b/src/test/hmac_tests.cpp
new file mode 100644
index 0000000000..92ca5e6aff
--- /dev/null
+++ b/src/test/hmac_tests.cpp
@@ -0,0 +1,125 @@
+#include <boost/test/unit_test.hpp>
+
+#include "hash.h"
+#include "util.h"
+
+using namespace std;
+
+BOOST_AUTO_TEST_SUITE(hmac_tests)
+
+typedef struct {
+ const char *pszKey;
+ const char *pszData;
+ const char *pszMAC;
+} testvec_t;
+
+// test cases 1, 2, 3, 4, 6 and 7 of RFC 4231
+static const testvec_t vtest[] = {
+ {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"
+ "0b0b0b0b",
+ "4869205468657265",
+ "87aa7cdea5ef619d4ff0b4241a1d6cb0"
+ "2379f4e2ce4ec2787ad0b30545e17cde"
+ "daa833b7d6b8a702038b274eaea3f4e4"
+ "be9d914eeb61f1702e696c203a126854"
+ },
+ {
+ "4a656665",
+ "7768617420646f2079612077616e7420"
+ "666f72206e6f7468696e673f",
+ "164b7a7bfcf819e2e395fbe73b56e0a3"
+ "87bd64222e831fd610270cd7ea250554"
+ "9758bf75c05a994a6d034f65f8f0e6fd"
+ "caeab1a34d4a6b4b636e070a38bce737"
+ },
+ {
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaa",
+ "dddddddddddddddddddddddddddddddd"
+ "dddddddddddddddddddddddddddddddd"
+ "dddddddddddddddddddddddddddddddd"
+ "dddd",
+ "fa73b0089d56a284efb0f0756c890be9"
+ "b1b5dbdd8ee81a3655f83e33b2279d39"
+ "bf3e848279a722c806b485a47e67c807"
+ "b946a337bee8942674278859e13292fb"
+ },
+ {
+ "0102030405060708090a0b0c0d0e0f10"
+ "111213141516171819",
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+ "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
+ "cdcd",
+ "b0ba465637458c6990e5a8c5f61d4af7"
+ "e576d97ff94b872de76f8050361ee3db"
+ "a91ca5c11aa25eb4d679275cc5788063"
+ "a5f19741120c4f2de2adebeb10a298dd"
+ },
+ {
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaa",
+ "54657374205573696e67204c61726765"
+ "72205468616e20426c6f636b2d53697a"
+ "65204b6579202d2048617368204b6579"
+ "204669727374",
+ "80b24263c7c1a3ebb71493c1dd7be8b4"
+ "9b46d1f41b4aeec1121b013783f8f352"
+ "6b56d037e05f2598bd0fd2215d6a1e52"
+ "95e64f73f63f0aec8b915a985d786598"
+ },
+ {
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaa",
+ "54686973206973206120746573742075"
+ "73696e672061206c6172676572207468"
+ "616e20626c6f636b2d73697a65206b65"
+ "7920616e642061206c61726765722074"
+ "68616e20626c6f636b2d73697a652064"
+ "6174612e20546865206b6579206e6565"
+ "647320746f2062652068617368656420"
+ "6265666f7265206265696e6720757365"
+ "642062792074686520484d414320616c"
+ "676f726974686d2e",
+ "e37b6a775dc87dbaa4dfa9f96e5e3ffd"
+ "debd71f8867289865df5a32d20cdc944"
+ "b6022cac3c4982b10d5eeb55c3e4de15"
+ "134676fb6de0446065c97440fa8c6a58"
+ }
+};
+
+BOOST_AUTO_TEST_CASE(hmacsha512_testvectors)
+{
+ for (unsigned int n=0; n<sizeof(vtest)/sizeof(vtest[0]); n++)
+ {
+ vector<unsigned char> vchKey = ParseHex(vtest[n].pszKey);
+ vector<unsigned char> vchData = ParseHex(vtest[n].pszData);
+ vector<unsigned char> vchMAC = ParseHex(vtest[n].pszMAC);
+ unsigned char vchTemp[64];
+
+ HMAC_SHA512_CTX ctx;
+ HMAC_SHA512_Init(&ctx, &vchKey[0], vchKey.size());
+ HMAC_SHA512_Update(&ctx, &vchData[0], vchData.size());
+ HMAC_SHA512_Final(&vchTemp[0], &ctx);
+
+ BOOST_CHECK(memcmp(&vchTemp[0], &vchMAC[0], 64) == 0);
+
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index fd936517fd..abfd882655 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -291,4 +291,15 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
}
}
+BOOST_AUTO_TEST_CASE(util_TimingResistantEqual)
+{
+ BOOST_CHECK(TimingResistantEqual(std::string(""), std::string("")));
+ BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("")));
+ BOOST_CHECK(!TimingResistantEqual(std::string(""), std::string("abc")));
+ BOOST_CHECK(!TimingResistantEqual(std::string("a"), std::string("aa")));
+ BOOST_CHECK(!TimingResistantEqual(std::string("aa"), std::string("a")));
+ BOOST_CHECK(TimingResistantEqual(std::string("abc"), std::string("abc")));
+ BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("aba")));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util.h b/src/util.h
index 9aea564406..c9614d3055 100644
--- a/src/util.h
+++ b/src/util.h
@@ -437,6 +437,21 @@ static inline uint32_t insecure_rand(void)
*/
void seed_insecure_rand(bool fDeterministic=false);
+/**
+ * Timing-attack-resistant comparison.
+ * Takes time proportional to length
+ * of first argument.
+ */
+template <typename T>
+bool TimingResistantEqual(const T& a, const T& b)
+{
+ if (b.size() == 0) return a.size() == 0;
+ size_t accumulator = a.size() ^ b.size();
+ for (size_t i = 0; i < a.size(); i++)
+ accumulator |= a[i] ^ b[i%b.size()];
+ return accumulator == 0;
+}
+
/** Median filter over a stream of values.
* Returns the median of the last N numbers
*/
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 88b07c9d76..8b9f3d34e1 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -1551,7 +1551,7 @@ bool CWallet::NewKeyPool()
return true;
}
-bool CWallet::TopUpKeyPool()
+bool CWallet::TopUpKeyPool(unsigned int kpSize)
{
{
LOCK(cs_wallet);
@@ -1562,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 3474045e32..664a032912 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -202,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);
@@ -299,7 +299,7 @@ public:
}
}
- int GetKeyPoolSize()
+ unsigned int GetKeyPoolSize()
{
return setKeyPool.size();
}