aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bitcoin-qt.pro33
-rw-r--r--doc/Tor.txt92
-rw-r--r--doc/assets-attribution.txt58
-rw-r--r--doc/files.txt19
-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/hash.cpp41
-rw-r--r--src/hash.h10
-rw-r--r--src/init.cpp3
-rw-r--r--src/key.cpp199
-rw-r--r--src/key.h46
-rw-r--r--src/main.cpp158
-rw-r--r--src/main.h2
-rw-r--r--src/rpcwallet.cpp17
-rw-r--r--src/script.cpp26
-rw-r--r--src/script.h11
-rw-r--r--src/test/bip32_tests.cpp116
-rw-r--r--src/test/canonical_tests.cpp4
-rw-r--r--src/test/data/script_valid.json9
-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
26 files changed, 857 insertions, 263 deletions
diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro
index 13c9d4cf24..32cf894b71 100644
--- a/bitcoin-qt.pro
+++ b/bitcoin-qt.pro
@@ -23,6 +23,21 @@ OBJECTS_DIR = build
MOC_DIR = build
UI_DIR = build
+contains(BITCOIN_QT_TEST, 1) {
+OBJECTS_DIR = build_test
+MOC_DIR = build_test
+UI_DIR = build_test
+
+SOURCES += src/qt/test/test_main.cpp \
+ src/qt/test/uritests.cpp
+HEADERS += src/qt/test/uritests.h
+DEPENDPATH += src/qt/test
+QT += testlib
+TARGET = bitcoin-qt_test
+DEFINES += BITCOIN_QT_TEST
+macx: CONFIG -= app_bundle
+}
+
# use: qmake "RELEASE=1"
contains(RELEASE, 1) {
# Mac: compile for maximum compatibility (10.5, 32-bit)
@@ -119,12 +134,12 @@ QMAKE_EXTRA_TARGETS += genleveldb
# Gross ugly hack that depends on qmake internals, unfortunately there is no other way to do it.
QMAKE_CLEAN += $$PWD/src/leveldb/libleveldb.a; cd $$PWD/src/leveldb ; $(MAKE) clean
-# regenerate src/build.h
+# regenerate build.h
!win32|contains(USE_BUILD_INFO, 1) {
genbuild.depends = FORCE
- genbuild.commands = cd $$PWD; /bin/sh share/genbuild.sh $$OUT_PWD/build/build.h
- genbuild.target = $$OUT_PWD/build/build.h
- PRE_TARGETDEPS += $$OUT_PWD/build/build.h
+ genbuild.commands = cd $$PWD; /bin/sh share/genbuild.sh $$OBJECTS_DIR/build.h
+ genbuild.target = $$OBJECTS_DIR/build.h
+ PRE_TARGETDEPS += $$OBJECTS_DIR/build.h
QMAKE_EXTRA_TARGETS += genbuild
DEFINES += HAVE_BUILD_INFO
}
@@ -311,16 +326,6 @@ SOURCES += src/qt/qrcodedialog.cpp
FORMS += src/qt/forms/qrcodedialog.ui
}
-contains(BITCOIN_QT_TEST, 1) {
-SOURCES += src/qt/test/test_main.cpp \
- src/qt/test/uritests.cpp
-HEADERS += src/qt/test/uritests.h
-DEPENDPATH += src/qt/test
-QT += testlib
-TARGET = bitcoin-qt_test
-DEFINES += BITCOIN_QT_TEST
- macx: CONFIG -= app_bundle
-}
# Todo: Remove this line when switching to Qt5, as that option was removed
CODECFORTR = UTF-8
diff --git a/doc/Tor.txt b/doc/Tor.txt
deleted file mode 100644
index 386e3b5466..0000000000
--- a/doc/Tor.txt
+++ /dev/null
@@ -1,92 +0,0 @@
-TOR SUPPORT IN BITCOIN
-======================
-
-It is possible to run Bitcoin as a Tor hidden service, and connect to such services.
-
-The following directions assume you have a Tor proxy running on port 9050. Many distributions
-default to having a SOCKS proxy listening on port 9050, but others may not.
-In particular, the Tor Browser Bundle defaults to listening on a random port. See
-https://www.torproject.org/docs/faq.html.en#TBBSocksPort for how to properly
-configure Tor.
-
-
-1. Run bitcoin behind a Tor proxy
----------------------------------
-
-The first step is running Bitcoin behind a Tor proxy. This will already make all
-outgoing connections be anonimized, but more is possible.
-
--socks=5 SOCKS5 supports connecting-to-hostname, which can be used instead
- of doing a (leaking) local DNS lookup. SOCKS5 is the default,
- but SOCKS4 does not support this. (SOCKS4a does, but isn't
- implemented).
-
--proxy=ip:port Set the proxy server. If SOCKS5 is selected (default), this proxy
- server will be used to try to reach .onion addresses as well.
-
--tor=ip:port Set the proxy server to use for tor hidden services. You do not
- need to set this if it's the same as -proxy. You can use -notor
- to explicitly disable access to hidden service.
-
--listen When using -proxy, listening is disabled by default. If you want
- to run a hidden service (see next section), you'll need to enable
- it explicitly.
-
--connect=X When behind a Tor proxy, you can specify .onion addresses instead
--addnode=X of IP addresses or hostnames in these parameters. It requires
--seednode=X SOCKS5. In Tor mode, such addresses can also be exchanged with
- other P2P nodes.
-
-In a typical situation, this suffices to run behind a Tor proxy:
-
- ./bitcoin -proxy=127.0.0.1:9050
-
-
-2. Run a bitcoin hidden server
-------------------------------
-
-If you configure your Tor system accordingly, it is possible to make your node also
-reachable from the Tor network. Add these lines to your /etc/tor/torrc (or equivalent
-config file):
-
- HiddenServiceDir /var/lib/tor/bitcoin-service/
- HiddenServicePort 8333 127.0.0.1:8333
-
-The directory can be different of course, but (both) port numbers should be equal to
-your bitcoind's P2P listen port (8333 by default).
-
--externalip=X You can tell bitcoin about its publicly reachable address using
- this option, and this can be a .onion address. Given the above
- configuration, you can find your onion address in
- /var/lib/tor/bitcoin-service/hostname. Onion addresses are given
- preference for your node to advertize itself with, for connections
- coming from unroutable addresses (such as 127.0.0.1, where the
- Tor proxy typically runs).
-
--listen You'll need to enable listening for incoming connections, as this
- is off by default behind a proxy.
-
--discover When -externalip is specified, no attempt is made to discover local
- IPv4 or IPv6 addresses. If you want to run a dual stack, reachable
- from both Tor and IPv4 (or IPv6), you'll need to either pass your
- other addresses using -externalip, or explicitly enable -discover.
- Note that both addresses of a dual-stack system may be easily
- linkable using traffic analysis.
-
-In a typical situation, where you're only reachable via Tor, this should suffice:
-
- ./bitcoind -proxy=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -listen
-
-(obviously, replace the Onion address with your own). If you don't care too much
-about hiding your node, and want to be reachable on IPv4 as well, additionally
-specify:
-
- ./bitcoind ... -discover
-
-and open port 8333 on your firewall (or use -upnp).
-
-If you only want to use Tor to reach onion addresses, but not use it as a proxy
-for normal IPv4/IPv6 communication, use:
-
- ./bitcoin -tor=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -discover
-
diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt
deleted file mode 100644
index 2069c5d6e0..0000000000
--- a/doc/assets-attribution.txt
+++ /dev/null
@@ -1,58 +0,0 @@
-Icon: src/qt/res/icons/clock*.png, src/qt/res/icons/tx*.png,
- src/qt/res/src/clock_green.svg, src/qt/res/src/clock1.svg,
- src/qt/res/src/clock2.svg, src/qt/res/src/clock3.svg,
- src/qt/res/src/clock4.svg, src/qt/res/src/clock5.svg,
- src/qt/res/src/inout.svg, src/qt/res/src/questionmark.svg
-Designer: Wladimir van der Laan
-License: MIT
-
-Icon: src/qt/res/icons/address-book.png, src/qt/res/icons/export.png,
- src/qt/res/icons/history.png, src/qt/res/icons/key.png,
- src/qt/res/icons/lock_*.png, src/qt/res/icons/overview.png,
- src/qt/res/icons/receive.png, src/qt/res/icons/send.png,
- src/qt/res/icons/synced.png, src/qt/res/icons/filesave.png
-Icon Pack: NUVOLA ICON THEME for KDE 3.x
-Designer: David Vignoni (david@icon-king.com)
- ICON KING - www.icon-king.com
-License: LGPL
-Site: http://www.icon-king.com/projects/nuvola/
-
-Icon: src/qt/res/icons/connect*.png
-Icon Pack: Human-O2
-Designer: schollidesign
-License: GNU/GPL
-Site: http://findicons.com/icon/93743/blocks_gnome_netstatus_0
-
-Icon: src/qt/res/icons/transaction*.png
-Designer: md2k7
-Site: https://bitcointalk.org/index.php?topic=15276.0
-License: You are free to do with these icons as you wish, including selling,
- copying, modifying etc.
-License: MIT
-
-Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png,
- src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png,
- src/qt/res/icons/add.png, src/qt/res/icons/edit.png,
- src/qt/res/icons/remove.png (edited)
-Designer: http://www.everaldo.com
-Icon Pack: Crystal SVG
-License: LGPL
-
-Icon: scripts/img/reload.xcf (modified), src/qt/res/movies/update_spinner.mng
-Icon Pack: Kids
-Designer: Everaldo (Everaldo Coelho)
-License: GNU/GPL
-Site: http://findicons.com/icon/17102/reload?id=17102
-
-Icon: src/qt/res/icons/debugwindow.png
-Designer: Vignoni David
-Site: http://www.oxygen-icons.org/
-License: Oxygen icon theme is dual licensed. You may copy it under the Creative Common Attribution-ShareAlike 3.0 License or the GNU Library General Public License.
-
-Icon: src/qt/res/icons/bitcoin.icns, src/qt/res/src/bitcoin.svg,
- src/qt/res/src/bitcoin.ico, src/qt/res/src/bitcoin.png,
- src/qt/res/src/bitcoin_testnet.png, docs/bitcoin_logo_doxygen.png,
- src/qt/res/icons/toolbar.png, src/qt/res/icons/toolbar_testnet.png,
- src/qt/res/images/splash.png, src/qt/res/images/splash_testnet.png
-Designer: Jonas Schnelli (based on the original bitcoin logo from Bitboy)
-License: MIT
diff --git a/doc/files.txt b/doc/files.txt
deleted file mode 100644
index 5d4cdabf8d..0000000000
--- a/doc/files.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Used in 0.8.0:
-* wallet.dat: personal wallet (BDB) with keys and transactions
-* peers.dat: peer IP address database (custom format); since 0.7.0
-* blocks/blk000??.dat: block data (custom, 128 MiB per file); since 0.8.0
-* blocks/rev000??.dat; block undo data (custom); since 0.8.0 (format changed since pre-0.8)
-* blocks/index/*; block index (LevelDB); since 0.8.0
-* chainstate/*; block chain state database (LevelDB); since 0.8.0
-* database/*: BDB database environment; only used for wallet since 0.8.0
-
-Only used in pre-0.8.0:
-* blktree/*; block chain index (LevelDB); since pre-0.8, replaced by blocks/index/* in 0.8.0
-* coins/*; unspent transaction output database (LevelDB); since pre-0.8, replaced by chainstate/* in 0.8.0
-
-Only used before 0.8.0:
-* blkindex.dat: block chain index database (BDB); replaced by {chainstate/*,blocks/index/*,blocks/rev000??.dat} in 0.8.0
-* blk000?.dat: block data (custom, 2 GiB per file); replaced by blocks/blk000??.dat in 0.8.0
-
-Only used before 0.7.0:
-* addr.dat: peer IP address database (BDB); replaced by peers.dat in 0.7.0
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/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 95d6cf1153..7182c14146 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);
+}
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 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/data/script_valid.json b/src/test/data/script_valid.json
index cc56a310ae..7aefc8e6e8 100644
--- a/src/test/data/script_valid.json
+++ b/src/test/data/script_valid.json
@@ -316,6 +316,15 @@
["2147483647", "1ADD 1"],
["-2147483647", "1ADD 1"],
+["1", "0x02 0x0100 EQUAL NOT", "Not the same byte array..."],
+["1", "0x02 0x0100 NUMEQUAL", "... but they are numerically equal"],
+["11", "0x4c 0x03 0x0b0000 NUMEQUAL"],
+["0", "0x01 0x80 EQUAL NOT"],
+["0", "0x01 0x80 NUMEQUAL", "Zero numerically equals negative zero"],
+["0", "0x02 0x0080 NUMEQUAL"],
+["0x03 0x000080", "0x04 0x00000080 NUMEQUAL"],
+["0x03 0x100080", "0x04 0x10000080 NUMEQUAL"],
+["0x03 0x100000", "0x04 0x10000000 NUMEQUAL"],
["NOP", "NOP 1", "The following tests check the if(stack.size() < N) tests in each opcode"],
["1", "IF 1 ENDIF", "They are here to catch copy-and-paste errors"],
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();
}