diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/base58.h | 72 | ||||
-rw-r--r-- | src/bitcoinrpc.cpp | 1 | ||||
-rw-r--r-- | src/chainparams.cpp | 27 | ||||
-rw-r--r-- | src/chainparams.h | 10 | ||||
-rw-r--r-- | src/hash.cpp | 41 | ||||
-rw-r--r-- | src/hash.h | 10 | ||||
-rw-r--r-- | src/init.cpp | 3 | ||||
-rw-r--r-- | src/key.cpp | 199 | ||||
-rw-r--r-- | src/key.h | 46 | ||||
-rw-r--r-- | src/main.cpp | 158 | ||||
-rw-r--r-- | src/main.h | 2 | ||||
-rw-r--r-- | src/rpcwallet.cpp | 17 | ||||
-rw-r--r-- | src/script.cpp | 26 | ||||
-rw-r--r-- | src/script.h | 11 | ||||
-rw-r--r-- | src/test/bip32_tests.cpp | 116 | ||||
-rw-r--r-- | src/test/canonical_tests.cpp | 4 | ||||
-rw-r--r-- | src/test/hmac_tests.cpp | 125 | ||||
-rw-r--r-- | src/wallet.cpp | 9 | ||||
-rw-r--r-- | src/wallet.h | 4 |
19 files changed, 802 insertions, 79 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 dfce9789a5..d22809ce69 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -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/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 20d334019d..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); 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); +} @@ -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 9de895374e..2ccd5131d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -973,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); @@ -1014,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(); @@ -1376,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) @@ -1391,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) { @@ -1895,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); @@ -2008,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 @@ -2017,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)) { @@ -2103,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")); @@ -2264,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; @@ -3066,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 @@ -3595,6 +3721,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CValidationState state; if (mempool.accept(state, tx, true, &fMissingInputs)) { + mempool.check(pcoinsTip); RelayTransaction(tx, inv.hash); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -3630,6 +3757,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vEraseQueue.push_back(orphanHash); printf(" removed orphan tx %s\n", orphanHash.ToString().c_str()); } + mempool.check(pcoinsTip); } } diff --git a/src/main.h b/src/main.h index ea86a2bcc0..a690a2bc9c 100644 --- a/src/main.h +++ b/src/main.h @@ -1077,6 +1077,7 @@ public: class CTxMemPool { public: + static bool fChecks; mutable CCriticalSection cs; std::map<uint256, CTransaction> mapTx; std::map<COutPoint, CInPoint> mapNextTx; @@ -1088,6 +1089,7 @@ public: 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/rpcwallet.cpp b/src/rpcwallet.cpp index c4f14b4921..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; 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/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/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(); } |