aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bitcoin-qt.pro87
-rw-r--r--src/bitcoinrpc.cpp215
-rw-r--r--src/key.h140
-rw-r--r--src/main.cpp18
-rw-r--r--src/main.h1
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/bitcoin.qrc5
-rw-r--r--src/qt/bitcoingui.cpp5
-rw-r--r--src/qt/forms/sendcoinsdialog.ui48
-rw-r--r--src/qt/notificator.cpp12
-rw-r--r--src/qt/notificator.h4
-rw-r--r--src/qt/optionsdialog.cpp4
-rw-r--r--src/qt/overviewpage.cpp3
-rw-r--r--src/qt/sendcoinsdialog.cpp11
-rw-r--r--src/qt/sendcoinsdialog.h1
-rw-r--r--src/qt/transactiondesc.h1
-rw-r--r--src/test/base58_tests.cpp87
-rw-r--r--src/test/base64_tests.cpp20
-rw-r--r--src/test/test_bitcoin.cpp3
-rw-r--r--src/test/util_tests.cpp214
-rw-r--r--src/util.cpp142
-rw-r--r--src/util.h49
22 files changed, 963 insertions, 109 deletions
diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro
index b9fecb848e..2d9cdc7ef5 100644
--- a/bitcoin-qt.pro
+++ b/bitcoin-qt.pro
@@ -2,23 +2,21 @@ TEMPLATE = app
TARGET =
INCLUDEPATH += src src/json src/cryptopp src/qt
DEFINES += QT_GUI
-# DEFINES += SSL
CONFIG += no_include_pwd
+# for boost 1.37, add -mt to the boost libraries
+# use: qmake BOOST_LIB_SUFFIX=-mt
+# or when linking against a specific BerkelyDB version: BDB_LIB_SUFFIX=-4.8
+
+# Dependency library locations can be customized with BOOST_INCLUDE_PATH,
+# BOOST_LIB_PATH, BDB_INCLUDE_PATH, BDB_LIB_PATH
+# OPENSSL_INCLUDE_PATH and OPENSSL_LIB_PATH respectively
+
OBJECTS_DIR = build
MOC_DIR = build
UI_DIR = build
-# for boost 1.37, add -mt to the boost libraries
-LIBS += -lssl -lcrypto -ldb_cxx
-unix:!macx:LIBS += -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread
-macx:LIBS += -lboost_system-mt -lboost_filesystem-mt -lboost_program_options-mt -lboost_thread-mt
-macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3
-windows:LIBS += -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -lboost_program_options-mgw44-mt-1_43 -lboost_thread-mgw44-mt-1_43 -lws2_32 -lgdi32
-windows:DEFINES += __WXMSW__
-windows:RC_FILE = src/qt/res/bitcoin-qt.rc
-
-# use: qmake "USE_UPNP=1"
+# use: qmake "USE_UPNP=0" (disable by default) or "USE_UPNP=1" (enable by default)
# miniupnpc (http://miniupnp.free.fr/files/) must be installed
count(USE_UPNP, 1) {
message(Building with UPNP support)
@@ -26,12 +24,19 @@ count(USE_UPNP, 1) {
LIBS += -lminiupnpc
}
-count(USE_DBUS, 1) {
+# use: qmake "USE_DBUS=1"
+contains(USE_DBUS, 1) {
message(Building with DBUS (Freedesktop notifications) support)
- DEFINES += QT_DBUS
+ DEFINES += USE_DBUS
QT += dbus
}
+# use: qmake "USE_SSL=1"
+contains(USE_DBUS, 1) {
+ message(Building with SSL support for RPC)
+ DEFINES += USE_SSL
+}
+
# for extra security against potential buffer overflows
QMAKE_CXXFLAGS += -fstack-protector
QMAKE_LFLAGS += -fstack-protector
@@ -173,17 +178,63 @@ FORMS += \
src/qt/forms/askpassphrasedialog.ui
CODECFORTR = UTF-8
+
# for lrelease/lupdate
TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts \
src/qt/locale/bitcoin_ru.ts
+isEmpty(QMAKE_LRELEASE) {
+ win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\lrelease.exe
+ else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
+}
+isEmpty(TS_DIR):TS_DIR = src/qt/locale
+# automatically build translations, so they can be included in resource file
+TSQM.name = lrelease ${QMAKE_FILE_IN}
+TSQM.input = TRANSLATIONS
+TSQM.output = $$TS_DIR/${QMAKE_FILE_BASE}.qm
+TSQM.commands = $$QMAKE_LRELEASE ${QMAKE_FILE_IN}
+TSQM.CONFIG = no_link
+QMAKE_EXTRA_COMPILERS += TSQM
+PRE_TARGETDEPS += compiler_TSQM_make_all
+
+# "Other files" to show in Qt Creator
OTHER_FILES += \
- README.rst
+ doc/*.rst doc/*.txt doc/README README.md
+
+# platform specific defaults, if not overridden on command line
+isEmpty(BOOST_LIB_SUFFIX) {
+ macx:BOOST_LIB_SUFFIX = -mt
+ windows:BOOST_LIB_SUFFIX = -mgw44-mt-1_43
+}
-# For use with MacPorts
-macx:INCLUDEPATH += /opt/local/include /opt/local/include/db48
-macx:LIBS += -L/opt/local/lib -L/opt/local/lib/db48
+isEmpty(BDB_LIB_PATH) {
+ macx:BDB_LIB_PATH = /opt/local/lib/db48
+}
+
+isEmpty(BDB_INCLUDE_PATH) {
+ macx:BDB_INCLUDE_PATH = /opt/local/include/db48
+}
+
+isEmpty(BOOST_LIB_PATH) {
+ macx:BOOST_LIB_PATH = /opt/local/lib
+}
+
+isEmpty(BOOST_INCLUDE_PATH) {
+ macx:BOOST_INCLUDE_PATH = /opt/local/include
+}
-# Additional Mac options
+windows:LIBS += -lws2_32 -lgdi32
+windows:DEFINES += __WXMSW__
+windows:RC_FILE = src/qt/res/bitcoin-qt.rc
+
+macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3
macx:ICON = src/qt/res/icons/bitcoin.icns
macx:TARGET = "Bitcoin Qt"
+
+# Set libraries and includes at end, to use platform-defined defaults if not overridden
+INCLUDEPATH += $$BOOST_INCLUDE_PATH $$BDB_INCLUDE_PATH $$OPENSSL_INCLUDE_PATH
+LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,)
+LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX
+LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_LIB_SUFFIX
+
+system($$QMAKE_LRELEASE -silent $$_PRO_FILE_)
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 5a1fab6943..cd546abde8 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -526,6 +526,72 @@ Value sendtoaddress(const Array& params, bool fHelp)
return wtx.GetHash().GetHex();
}
+static const string strMessageMagic = "Bitcoin Signed Message:\n";
+
+Value signmessage(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 2)
+ throw runtime_error(
+ "signmessage <bitcoinaddress> <message>\n"
+ "Sign a message with the private key of an address");
+
+ if (pwalletMain->IsLocked())
+ throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
+
+ string strAddress = params[0].get_str();
+ string strMessage = params[1].get_str();
+
+ CBitcoinAddress addr(strAddress);
+ if (!addr.IsValid())
+ throw JSONRPCError(-3, "Invalid address");
+
+ CKey key;
+ if (!pwalletMain->GetKey(addr, key))
+ throw JSONRPCError(-4, "Private key not available");
+
+ CDataStream ss(SER_GETHASH);
+ ss << strMessageMagic;
+ ss << strMessage;
+
+ vector<unsigned char> vchSig;
+ if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig))
+ throw JSONRPCError(-5, "Sign failed");
+
+ return EncodeBase64(&vchSig[0], vchSig.size());
+}
+
+Value verifymessage(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 3)
+ throw runtime_error(
+ "verifymessage <bitcoinaddress> <signature> <message>\n"
+ "Verify a signed message");
+
+ string strAddress = params[0].get_str();
+ string strSign = params[1].get_str();
+ string strMessage = params[2].get_str();
+
+ CBitcoinAddress addr(strAddress);
+ if (!addr.IsValid())
+ throw JSONRPCError(-3, "Invalid address");
+
+ bool fInvalid = false;
+ vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
+
+ if (fInvalid)
+ throw JSONRPCError(-5, "Malformed base64 encoding");
+
+ CDataStream ss(SER_GETHASH);
+ ss << strMessageMagic;
+ ss << strMessage;
+
+ CKey key;
+ if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
+ return false;
+
+ return (key.GetAddress() == addr);
+}
+
Value getreceivedbyaddress(const Array& params, bool fHelp)
{
@@ -937,7 +1003,6 @@ Value ListReceived(const Array& params, bool fByAccounts)
Object obj;
obj.push_back(Pair("address", address.ToString()));
obj.push_back(Pair("account", strAccount));
- obj.push_back(Pair("label", strAccount)); // deprecated
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
ret.push_back(obj);
@@ -952,7 +1017,6 @@ Value ListReceived(const Array& params, bool fByAccounts)
int nConf = (*it).second.nConf;
Object obj;
obj.push_back(Pair("account", (*it).first));
- obj.push_back(Pair("label", (*it).first)); // deprecated
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
ret.push_back(obj);
@@ -1482,9 +1546,9 @@ Value getwork(const Array& params, bool fHelp)
throw runtime_error(
"getwork [data]\n"
"If [data] is not specified, returns formatted hash data to work on:\n"
- " \"midstate\" : precomputed hash state after hashing the first half of the data\n"
+ " \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
" \"data\" : block data\n"
- " \"hash1\" : formatted hash buffer for second hash\n"
+ " \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
" \"target\" : little endian hash target\n"
"If [data] is specified, tries to solve the block and returns true if it was successful.");
@@ -1548,9 +1612,9 @@ Value getwork(const Array& params, bool fHelp)
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
Object result;
- result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate))));
+ result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
- result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1))));
+ result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
return result;
}
@@ -1581,6 +1645,86 @@ Value getwork(const Array& params, bool fHelp)
}
+Value getmemorypool(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 1)
+ throw runtime_error(
+ "getmemorypool [data]\n"
+ "If [data] is not specified, returns data needed to construct a block to work on:\n"
+ " \"version\" : block version\n"
+ " \"previousblockhash\" : hash of current highest block\n"
+ " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
+ " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
+ " \"time\" : timestamp appropriate for next block\n"
+ " \"bits\" : compressed target of next block\n"
+ "If [data] is specified, tries to solve the block and returns true if it was successful.");
+
+ if (params.size() == 0)
+ {
+ if (vNodes.empty())
+ throw JSONRPCError(-9, "Bitcoin is not connected!");
+
+ if (IsInitialBlockDownload())
+ throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
+
+ static CReserveKey reservekey(pwalletMain);
+
+ // Update block
+ static unsigned int nTransactionsUpdatedLast;
+ static CBlockIndex* pindexPrev;
+ static int64 nStart;
+ static CBlock* pblock;
+ if (pindexPrev != pindexBest ||
+ (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
+ {
+ nTransactionsUpdatedLast = nTransactionsUpdated;
+ pindexPrev = pindexBest;
+ nStart = GetTime();
+
+ // Create new block
+ if(pblock)
+ delete pblock;
+ pblock = CreateNewBlock(reservekey);
+ if (!pblock)
+ throw JSONRPCError(-7, "Out of memory");
+ }
+
+ // Update nTime
+ pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+ pblock->nNonce = 0;
+
+ Array transactions;
+ BOOST_FOREACH(CTransaction tx, pblock->vtx) {
+ if(tx.IsCoinBase())
+ continue;
+
+ CDataStream ssTx;
+ ssTx << tx;
+
+ transactions.push_back(HexStr(ssTx.begin(), ssTx.end()));
+ }
+
+ Object result;
+ result.push_back(Pair("version", pblock->nVersion));
+ result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
+ result.push_back(Pair("transactions", transactions));
+ result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
+ result.push_back(Pair("time", (int64_t)pblock->nTime));
+ result.push_back(Pair("bits", (int64_t)pblock->nBits));
+ return result;
+ }
+ else
+ {
+ // Parse parameters
+ CDataStream ssBlock(ParseHex(params[0].get_str()));
+ CBlock pblock;
+ ssBlock >> pblock;
+
+ return ProcessBlock(NULL, &pblock);
+ }
+}
+
+
@@ -1609,20 +1753,13 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("getnewaddress", &getnewaddress),
make_pair("getaccountaddress", &getaccountaddress),
make_pair("setaccount", &setaccount),
- make_pair("setlabel", &setaccount), // deprecated
make_pair("getaccount", &getaccount),
- make_pair("getlabel", &getaccount), // deprecated
make_pair("getaddressesbyaccount", &getaddressesbyaccount),
- make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated
make_pair("sendtoaddress", &sendtoaddress),
- make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress
- make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress
make_pair("getreceivedbyaddress", &getreceivedbyaddress),
make_pair("getreceivedbyaccount", &getreceivedbyaccount),
- make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated
make_pair("listreceivedbyaddress", &listreceivedbyaddress),
make_pair("listreceivedbyaccount", &listreceivedbyaccount),
- make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated
make_pair("backupwallet", &backupwallet),
make_pair("keypoolrefill", &keypoolrefill),
make_pair("walletpassphrase", &walletpassphrase),
@@ -1636,9 +1773,12 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("sendmany", &sendmany),
make_pair("gettransaction", &gettransaction),
make_pair("listtransactions", &listtransactions),
+ make_pair("signmessage", &signmessage),
+ make_pair("verifymessage", &verifymessage),
make_pair("getwork", &getwork),
make_pair("listaccounts", &listaccounts),
make_pair("settxfee", &settxfee),
+ make_pair("getmemorypool", &getmemorypool),
};
map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
@@ -1656,17 +1796,15 @@ string pAllowInSafeMode[] =
"getinfo",
"getnewaddress",
"getaccountaddress",
- "setlabel", // deprecated
"getaccount",
- "getlabel", // deprecated
"getaddressesbyaccount",
- "getaddressesbylabel", // deprecated
"backupwallet",
"keypoolrefill",
"walletpassphrase",
"walletlock",
"validateaddress",
"getwork",
+ "getmemorypool",
};
set<string> setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0]));
@@ -1811,43 +1949,6 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe
return nStatus;
}
-string EncodeBase64(string s)
-{
- BIO *b64, *bmem;
- BUF_MEM *bptr;
-
- b64 = BIO_new(BIO_f_base64());
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- bmem = BIO_new(BIO_s_mem());
- b64 = BIO_push(b64, bmem);
- BIO_write(b64, s.c_str(), s.size());
- BIO_flush(b64);
- BIO_get_mem_ptr(b64, &bptr);
-
- string result(bptr->data, bptr->length);
- BIO_free_all(b64);
-
- return result;
-}
-
-string DecodeBase64(string s)
-{
- BIO *b64, *bmem;
-
- char* buffer = static_cast<char*>(calloc(s.size(), sizeof(char)));
-
- b64 = BIO_new(BIO_f_base64());
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- bmem = BIO_new_mem_buf(const_cast<char*>(s.c_str()), s.size());
- bmem = BIO_push(b64, bmem);
- BIO_read(bmem, buffer, s.size());
- BIO_free_all(bmem);
-
- string result(buffer);
- free(buffer);
- return result;
-}
-
bool HTTPAuthorized(map<string, string>& mapHeaders)
{
string strAuth = mapHeaders["authorization"];
@@ -2119,7 +2220,7 @@ void ThreadRPCServer2(void* parg)
if (valMethod.type() != str_type)
throw JSONRPCError(-32600, "Method must be a string");
string strMethod = valMethod.get_str();
- if (strMethod != "getwork")
+ if (strMethod != "getwork" && strMethod != "getmemorypool")
printf("ThreadRPCServer method=%s\n", strMethod.c_str());
// Parse params
@@ -2285,18 +2386,12 @@ int CommandLineRPC(int argc, char *argv[])
if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
- if (strMethod == "getamountreceived" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
- if (strMethod == "getallreceived" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
- if (strMethod == "getallreceived" && n > 1) ConvertTo<bool>(params[1]); // deprecated
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
- if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
- if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo<bool>(params[1]); // deprecated
if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
diff --git a/src/key.h b/src/key.h
index d2e6689456..5ffd7b9ccc 100644
--- a/src/key.h
+++ b/src/key.h
@@ -75,6 +75,76 @@ err:
return(ok);
}
+int static inline ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
+{
+ if (!eckey) return 0;
+
+ int ret = 0;
+ BN_CTX *ctx = NULL;
+
+ BIGNUM *x = NULL;
+ BIGNUM *e = NULL;
+ BIGNUM *order = NULL;
+ BIGNUM *sor = NULL;
+ BIGNUM *eor = NULL;
+ BIGNUM *field = NULL;
+ EC_POINT *R = NULL;
+ EC_POINT *O = NULL;
+ EC_POINT *Q = NULL;
+ BIGNUM *rr = NULL;
+ BIGNUM *zero = NULL;
+ int n = 0;
+ int i = recid / 2;
+
+ const EC_GROUP *group = EC_KEY_get0_group(eckey);
+ if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
+ BN_CTX_start(ctx);
+ order = BN_CTX_get(ctx);
+ if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
+ x = BN_CTX_get(ctx);
+ if (!BN_copy(x, order)) { ret=-1; goto err; }
+ if (!BN_mul_word(x, i)) { ret=-1; goto err; }
+ if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
+ field = BN_CTX_get(ctx);
+ if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
+ if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
+ if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+ if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
+ if (check)
+ {
+ if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+ if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
+ if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
+ }
+ if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
+ n = EC_GROUP_get_degree(group);
+ e = BN_CTX_get(ctx);
+ if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
+ if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
+ zero = BN_CTX_get(ctx);
+ if (!BN_zero(zero)) { ret=-1; goto err; }
+ if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
+ rr = BN_CTX_get(ctx);
+ if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
+ sor = BN_CTX_get(ctx);
+ if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
+ eor = BN_CTX_get(ctx);
+ if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
+ if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
+ if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
+
+ ret = 1;
+
+err:
+ if (ctx) {
+ BN_CTX_end(ctx);
+ BN_CTX_free(ctx);
+ }
+ if (R != NULL) EC_POINT_free(R);
+ if (O != NULL) EC_POINT_free(O);
+ if (Q != NULL) EC_POINT_free(Q);
+ return ret;
+}
class key_error : public std::runtime_error
{
@@ -221,6 +291,66 @@ public:
return true;
}
+ // create a compact signature (65 bytes), which allows reconstructing the used public key
+ bool SignCompact(uint256 hash, std::vector<unsigned char>& vchSig)
+ {
+ bool fOk = false;
+ ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
+ if (sig==NULL)
+ return false;
+ vchSig.clear();
+ vchSig.resize(65,0);
+ int nBitsR = BN_num_bits(sig->r);
+ int nBitsS = BN_num_bits(sig->s);
+ if (nBitsR <= 256 && nBitsS <= 256)
+ {
+ int nRecId = -1;
+ for (int i=0; i<4; i++)
+ {
+ CKey keyRec;
+ keyRec.fSet = true;
+ if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1)
+ if (keyRec.GetPubKey() == this->GetPubKey())
+ {
+ nRecId = i;
+ break;
+ }
+ }
+
+ if (nRecId == -1)
+ throw key_error("CKEy::SignCompact() : unable to construct recoverable key");
+
+ vchSig[0] = nRecId+27;
+ BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]);
+ BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]);
+ fOk = true;
+ }
+ ECDSA_SIG_free(sig);
+ return fOk;
+ }
+
+ // reconstruct public key from a compact signature
+ bool SetCompactSignature(uint256 hash, const std::vector<unsigned char>& vchSig)
+ {
+ if (vchSig.size() != 65)
+ return false;
+ if (vchSig[0]<27 || vchSig[0]>=31)
+ return false;
+ ECDSA_SIG *sig = ECDSA_SIG_new();
+ BN_bin2bn(&vchSig[1],32,sig->r);
+ BN_bin2bn(&vchSig[33],32,sig->s);
+
+ EC_KEY_free(pkey);
+ pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
+ if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), vchSig[0] - 27, 0) == 1)
+ {
+ fSet = true;
+ ECDSA_SIG_free(sig);
+ return true;
+ }
+ return false;
+ }
+
bool Verify(uint256 hash, const std::vector<unsigned char>& vchSig)
{
// -1 = error, 0 = bad sig, 1 = good
@@ -229,6 +359,16 @@ public:
return true;
}
+ bool VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig)
+ {
+ CKey key;
+ if (!key.SetCompactSignature(hash, vchSig))
+ return false;
+ if (GetPubKey() != key.GetPubKey())
+ return false;
+ return true;
+ }
+
CBitcoinAddress GetAddress() const
{
return CBitcoinAddress(GetPubKey());
diff --git a/src/main.cpp b/src/main.cpp
index f129d7c7b1..750290ccc5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -32,7 +32,6 @@ uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3
static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
const int nTotalBlocksEstimate = 140700; // Conservative estimate of total nr of blocks on main chain
const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download"
-int nMaxBlocksOfPeers = 0; // Amount of blocks that other nodes claim to have
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
CBigNum bnBestChainWork = 0;
@@ -41,6 +40,8 @@ uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
+CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
+
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
@@ -65,11 +66,6 @@ int fUseUPnP = false;
#endif
-
-
-
-
-
//////////////////////////////////////////////////////////////////////////////
//
// dispatching functions
@@ -730,7 +726,7 @@ int GetTotalBlocksEstimate()
// Return maximum amount of blocks that other nodes claim to have
int GetNumBlocksOfPeers()
{
- return std::max(nMaxBlocksOfPeers, GetTotalBlocksEstimate());
+ return std::max(cPeerBlockCounts.median(), GetTotalBlocksEstimate());
}
bool IsInitialBlockDownload()
@@ -1342,7 +1338,7 @@ bool CBlock::AcceptBlock()
return true;
}
-bool static ProcessBlock(CNode* pfrom, CBlock* pblock)
+bool ProcessBlock(CNode* pfrom, CBlock* pblock)
{
// Check for duplicate
uint256 hash = pblock->GetHash();
@@ -1859,10 +1855,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->fSuccessfullyConnected = true;
printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight);
- if(pfrom->nStartingHeight > nMaxBlocksOfPeers)
- {
- nMaxBlocksOfPeers = pfrom->nStartingHeight;
- }
+
+ cPeerBlockCounts.input(pfrom->nStartingHeight);
}
diff --git a/src/main.h b/src/main.h
index 1106bb9785..329035dc5a 100644
--- a/src/main.h
+++ b/src/main.h
@@ -85,6 +85,7 @@ class CTxIndex;
void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
+bool ProcessBlock(CNode* pfrom, CBlock* pblock);
bool CheckDiskSpace(uint64 nAdditionalBytes=0);
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb");
FILE* AppendBlockFile(unsigned int& nFileRet);
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index daba512adc..60a9074204 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -120,7 +120,7 @@ int main(int argc, char *argv[])
// Load language file for system locale
QString locale = QLocale::system().name();
QTranslator translator;
- translator.load("bitcoin_"+locale);
+ translator.load(":/translations/"+locale);
app.installTranslator(&translator);
QSplashScreen splash(QPixmap(":/images/splash"), 0);
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index be0e4dce61..2985bb60ef 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -45,4 +45,9 @@
<qresource prefix="/movies">
<file alias="update_spinner">res/movies/update_spinner.mng</file>
</qresource>
+ <qresource prefix="/translations">
+ <file alias="de_DE">locale/bitcoin_de.qm</file>
+ <file alias="nl_NL">locale/bitcoin_nl.qm</file>
+ <file alias="ru_RU">locale/bitcoin_ru.qm</file>
+ </qresource>
</RCC>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 3e6b547006..be10b97c0f 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -522,11 +522,6 @@ void BitcoinGUI::gotoReceiveCoinsPage()
void BitcoinGUI::gotoSendCoinsPage()
{
sendCoinsAction->setChecked(true);
- if(centralWidget->currentWidget() != sendCoinsPage)
- {
- // Clear the current contents if we arrived from another tab
- sendCoinsPage->clear();
- }
centralWidget->setCurrentWidget(sendCoinsPage);
exportAction->setEnabled(false);
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 5b30d99e56..f9dd02fef5 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -25,7 +25,7 @@
<x>0</x>
<y>0</y>
<width>666</width>
- <height>162</height>
+ <height>165</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@@ -59,7 +59,7 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
- <number>12</number>
+ <number>6</number>
</property>
<item>
<widget class="QPushButton" name="addButton">
@@ -76,6 +76,50 @@
</widget>
</item>
<item>
+ <widget class="QPushButton" name="clearButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Clear all</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../bitcoin.qrc">
+ <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
+ </property>
+ <property name="autoRepeatDelay">
+ <number>300</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Balance:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelBalance">
+ <property name="text">
+ <string>123.456 BTC</string>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp
index 86ccfc8541..cf0c0a3901 100644
--- a/src/qt/notificator.cpp
+++ b/src/qt/notificator.cpp
@@ -9,7 +9,7 @@
#include <QSystemTrayIcon>
#include <QMessageBox>
-#ifdef QT_DBUS
+#ifdef USE_DBUS
#include <QtDBus/QtDBus>
#include <stdint.h>
#endif
@@ -23,7 +23,7 @@ Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon,
programName(programName),
mode(None),
trayIcon(trayicon)
-#ifdef QT_DBUS
+#ifdef USE_DBUS
,interface(0)
#endif
{
@@ -31,7 +31,7 @@ Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon,
{
mode = QSystemTray;
}
-#ifdef QT_DBUS
+#ifdef USE_DBUS
interface = new QDBusInterface("org.freedesktop.Notifications",
"/org/freedesktop/Notifications", "org.freedesktop.Notifications");
if(interface->isValid())
@@ -43,12 +43,12 @@ Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon,
Notificator::~Notificator()
{
-#ifdef QT_DBUS
+#ifdef USE_DBUS
delete interface;
#endif
}
-#ifdef QT_DBUS
+#ifdef USE_DBUS
// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html
class FreedesktopImage
@@ -205,7 +205,7 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c
{
switch(mode)
{
-#ifdef QT_DBUS
+#ifdef USE_DBUS
case Freedesktop:
notifyDBus(cls, title, text, icon, millisTimeout);
break;
diff --git a/src/qt/notificator.h b/src/qt/notificator.h
index 13f6a908da..4217f7e06f 100644
--- a/src/qt/notificator.h
+++ b/src/qt/notificator.h
@@ -6,7 +6,7 @@
QT_BEGIN_NAMESPACE
class QSystemTrayIcon;
-#ifdef QT_DBUS
+#ifdef USE_DBUS
class QDBusInterface;
#endif
QT_END_NAMESPACE
@@ -52,7 +52,7 @@ private:
QString programName;
Mode mode;
QSystemTrayIcon *trayIcon;
-#ifdef QT_DBUS
+#ifdef USE_DBUS
QDBusInterface *interface;
void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 0eeb6f86a5..7267e3d103 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -23,6 +23,7 @@
/* First page of options */
class MainOptionsPage : public QWidget
{
+ Q_OBJECT
public:
explicit MainOptionsPage(QWidget *parent=0);
@@ -45,6 +46,7 @@ public slots:
class DisplayOptionsPage : public QWidget
{
+ Q_OBJECT
public:
explicit DisplayOptionsPage(QWidget *parent=0);
@@ -58,6 +60,8 @@ public slots:
};
+#include "optionsdialog.moc"
+
OptionsDialog::OptionsDialog(QWidget *parent):
QDialog(parent), contents_widget(0), pages_widget(0),
model(0), main_page(0), display_page(0)
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 7bade9a755..f84a79fe30 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -17,7 +17,7 @@
class TxViewDelegate : public QAbstractItemDelegate
{
- //Q_OBJECT
+ Q_OBJECT
public:
TxViewDelegate(): QAbstractItemDelegate(), unit(BitcoinUnits::BTC)
{
@@ -87,6 +87,7 @@ public:
int unit;
};
+#include "overviewpage.moc"
OverviewPage::OverviewPage(QWidget *parent) :
QWidget(parent),
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 852d789805..58eb5c21f7 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -22,6 +22,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
addEntry();
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
+ connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
}
void SendCoinsDialog::setModel(WalletModel *model)
@@ -36,6 +37,9 @@ void SendCoinsDialog::setModel(WalletModel *model)
entry->setModel(model);
}
}
+
+ setBalance(model->getBalance(), model->getUnconfirmedBalance());
+ connect(model, SIGNAL(balanceChanged(qint64, qint64)), this, SLOT(setBalance(qint64, qint64)));
}
SendCoinsDialog::~SendCoinsDialog()
@@ -241,3 +245,10 @@ void SendCoinsDialog::handleURL(const QUrl *url)
}
pasteEntry(rv);
}
+
+void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance)
+{
+ Q_UNUSED(unconfirmedBalance);
+ int unit = model->getOptionsModel()->getDisplayUnit();
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance));
+}
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 9c56e51811..a14f99e8b2 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -37,6 +37,7 @@ public slots:
void accept();
SendCoinsEntry *addEntry();
void updateRemoveEnabled();
+ void setBalance(qint64 balance, qint64 unconfirmedBalance);
private:
Ui::SendCoinsDialog *ui;
diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h
index 257b2cbb89..484bb1230e 100644
--- a/src/qt/transactiondesc.h
+++ b/src/qt/transactiondesc.h
@@ -10,6 +10,7 @@ class CWalletTx;
class TransactionDesc: public QObject
{
+ Q_OBJECT
public:
// Provide human-readable extended HTML description of a transaction
static QString toHTML(CWallet *wallet, CWalletTx &wtx);
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
new file mode 100644
index 0000000000..c7fa74e96d
--- /dev/null
+++ b/src/test/base58_tests.cpp
@@ -0,0 +1,87 @@
+#include <boost/test/unit_test.hpp>
+
+#include "../util.h"
+
+BOOST_AUTO_TEST_SUITE(base58_tests)
+
+// TODO:
+// EncodeBase58Check
+// DecodeBase58Check
+// CBase58Data
+// bool SetString(const char* psz)
+ // bool SetString(const std::string& str)
+ // std::string ToString() const
+ // int CompareTo(const CBase58Data& b58) const
+ // bool operator==(const CBase58Data& b58) const
+ // bool operator<=(const CBase58Data& b58) const
+ // bool operator>=(const CBase58Data& b58) const
+ // bool operator< (const CBase58Data& b58) const
+ // bool operator> (const CBase58Data& b58) const
+
+// CBitcoinAddress
+ // bool SetHash160(const uint160& hash160)
+ // bool SetPubKey(const std::vector<unsigned char>& vchPubKey)
+ // bool IsValid() const
+ // CBitcoinAddress()
+ // CBitcoinAddress(uint160 hash160In)
+ // CBitcoinAddress(const std::vector<unsigned char>& vchPubKey)
+ // CBitcoinAddress(const std::string& strAddress)
+ // CBitcoinAddress(const char* pszAddress)
+ // uint160 GetHash160() const
+
+#define U(x) (reinterpret_cast<const unsigned char*>(x))
+static struct {
+ const unsigned char *data;
+ int size;
+} vstrIn[] = {
+{U(""), 0},
+{U("\x61"), 1},
+{U("\x62\x62\x62"), 3},
+{U("\x63\x63\x63"), 3},
+{U("\x73\x69\x6d\x70\x6c\x79\x20\x61\x20\x6c\x6f\x6e\x67\x20\x73\x74\x72\x69\x6e\x67"), 20},
+{U("\x00\xeb\x15\x23\x1d\xfc\xeb\x60\x92\x58\x86\xb6\x7d\x06\x52\x99\x92\x59\x15\xae\xb1\x72\xc0\x66\x47"), 25},
+{U("\x51\x6b\x6f\xcd\x0f"), 5},
+{U("\xbf\x4f\x89\x00\x1e\x67\x02\x74\xdd"), 9},
+{U("\x57\x2e\x47\x94"), 4},
+{U("\xec\xac\x89\xca\xd9\x39\x23\xc0\x23\x21"), 10},
+{U("\x10\xc8\x51\x1e"), 4},
+{U("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), 10},
+};
+
+const char *vstrOut[] = {
+"",
+"2g",
+"a3gV",
+"aPEr",
+"2cFupjhnEsSn59qHXstmK2ffpLv2",
+"1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L",
+"ABnLTmg",
+"3SEo3LWLoPntC",
+"3EFU7m",
+"EJDM8drfXA6uyA",
+"Rt5zm",
+"1111111111"
+};
+
+BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
+{
+ for (int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
+ {
+ BOOST_CHECK_EQUAL(EncodeBase58(vstrIn[i].data, vstrIn[i].data + vstrIn[i].size), vstrOut[i]);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
+{
+ std::vector<unsigned char> result;
+ for (int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
+ {
+ std::vector<unsigned char> expected(vstrIn[i].data, vstrIn[i].data + vstrIn[i].size);
+ BOOST_CHECK(DecodeBase58(vstrOut[i], result));
+ BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
+ }
+ BOOST_CHECK(!DecodeBase58("invalid", result));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp
new file mode 100644
index 0000000000..f30f7f8936
--- /dev/null
+++ b/src/test/base64_tests.cpp
@@ -0,0 +1,20 @@
+#include <boost/test/unit_test.hpp>
+
+#include "../util.h"
+
+BOOST_AUTO_TEST_SUITE(base64_tests)
+
+BOOST_AUTO_TEST_CASE(base64_testvectors)
+{
+ static const string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"};
+ static const string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"};
+ for (int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
+ {
+ string strEnc = EncodeBase64(vstrIn[i]);
+ BOOST_CHECK(strEnc == vstrOut[i]);
+ string strDec = DecodeBase64(strEnc);
+ BOOST_CHECK(strDec == vstrIn[i]);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index c6f6d94b1e..4f98a93c19 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -9,6 +9,9 @@
#include "script_tests.cpp"
#include "transaction_tests.cpp"
#include "DoS_tests.cpp"
+#include "base64_tests.cpp"
+#include "util_tests.cpp"
+#include "base58_tests.cpp"
CWallet* pwalletMain;
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
new file mode 100644
index 0000000000..8afc85c507
--- /dev/null
+++ b/src/test/util_tests.cpp
@@ -0,0 +1,214 @@
+#include <vector>
+#include <boost/test/unit_test.hpp>
+#include <boost/foreach.hpp>
+
+#include "../util.h"
+
+using namespace std;
+
+BOOST_AUTO_TEST_SUITE(util_tests)
+
+BOOST_AUTO_TEST_CASE(util_MedianFilter)
+{
+ CMedianFilter<int> filter(5, 15);
+
+ BOOST_CHECK_EQUAL(filter.median(), 15);
+
+ filter.input(20); // [15 20]
+ BOOST_CHECK_EQUAL(filter.median(), 17);
+
+ filter.input(30); // [15 20 30]
+ BOOST_CHECK_EQUAL(filter.median(), 20);
+
+ filter.input(3); // [3 15 20 30]
+ BOOST_CHECK_EQUAL(filter.median(), 17);
+
+ filter.input(7); // [3 7 15 20 30]
+ BOOST_CHECK_EQUAL(filter.median(), 15);
+
+ filter.input(18); // [3 7 18 20 30]
+ BOOST_CHECK_EQUAL(filter.median(), 18);
+
+ filter.input(0); // [0 3 7 18 30]
+ BOOST_CHECK_EQUAL(filter.median(), 7);
+}
+
+static const unsigned char ParseHex_expected[65] = {
+ 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7,
+ 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde,
+ 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12,
+ 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d,
+ 0x5f
+};
+BOOST_AUTO_TEST_CASE(util_ParseHex)
+{
+ std::vector<unsigned char> result;
+ std::vector<unsigned char> expected(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected));
+ // Basic test vector
+ result = ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
+ BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
+
+ // Spaces between bytes must be supported
+ result = ParseHex("12 34 56 78");
+ BOOST_CHECK(result.size() == 4 && result[0] == 0x12 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78);
+
+ // Stop parsing at invalid value
+ result = ParseHex("1234 invalid 1234");
+ BOOST_CHECK(result.size() == 2 && result[0] == 0x12 && result[1] == 0x34);
+}
+
+BOOST_AUTO_TEST_CASE(util_HexStr)
+{
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected)),
+ "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
+
+ BOOST_CHECK_EQUAL(
+ HexStr(ParseHex_expected, ParseHex_expected + 5, true),
+ "04 67 8a fd b0");
+}
+
+BOOST_AUTO_TEST_CASE(util_DateTimeStrFormat)
+{
+ BOOST_CHECK_EQUAL(DateTimeStrFormat("%x %H:%M:%S", 0), "01/01/70 00:00:00");
+ BOOST_CHECK_EQUAL(DateTimeStrFormat("%x %H:%M:%S", 0x7FFFFFFF), "01/19/38 03:14:07");
+ // Formats used within bitcoin
+ BOOST_CHECK_EQUAL(DateTimeStrFormat("%x %H:%M:%S", 1317425777), "09/30/11 23:36:17");
+ BOOST_CHECK_EQUAL(DateTimeStrFormat("%x %H:%M", 1317425777), "09/30/11 23:36");
+}
+
+BOOST_AUTO_TEST_CASE(util_ParseParameters)
+{
+ const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"};
+
+ ParseParameters(0, (char**)argv_test);
+ BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty());
+
+ ParseParameters(1, (char**)argv_test);
+ BOOST_CHECK(mapArgs.empty() && mapMultiArgs.empty());
+
+ ParseParameters(5, (char**)argv_test);
+ // expectation: -ignored is ignored (program name argument),
+ // -a, -b and -ccc end up in map, -d ignored because it is after
+ // a non-option argument (non-GNU option parsing)
+ BOOST_CHECK(mapArgs.size() == 3 && mapMultiArgs.size() == 3);
+ BOOST_CHECK(mapArgs.count("-a") && mapArgs.count("-b") && mapArgs.count("-ccc")
+ && !mapArgs.count("f") && !mapArgs.count("-d"));
+ BOOST_CHECK(mapMultiArgs.count("-a") && mapMultiArgs.count("-b") && mapMultiArgs.count("-ccc")
+ && !mapMultiArgs.count("f") && !mapMultiArgs.count("-d"));
+
+ BOOST_CHECK(mapArgs["-a"] == "" && mapArgs["-ccc"] == "multiple");
+ BOOST_CHECK(mapMultiArgs["-ccc"].size() == 2);
+}
+
+BOOST_AUTO_TEST_CASE(util_GetArg)
+{
+ mapArgs.clear();
+ mapArgs["strtest1"] = "string...";
+ // strtest2 undefined on purpose
+ mapArgs["inttest1"] = "12345";
+ mapArgs["inttest2"] = "81985529216486895";
+ // inttest3 undefined on purpose
+ mapArgs["booltest1"] = "";
+ // booltest2 undefined on purpose
+ mapArgs["booltest3"] = "0";
+ mapArgs["booltest4"] = "1";
+
+ BOOST_CHECK_EQUAL(GetArg("strtest1", "default"), "string...");
+ BOOST_CHECK_EQUAL(GetArg("strtest2", "default"), "default");
+ BOOST_CHECK_EQUAL(GetArg("inttest1", -1), 12345);
+ BOOST_CHECK_EQUAL(GetArg("inttest2", -1), 81985529216486895LL);
+ BOOST_CHECK_EQUAL(GetArg("inttest3", -1), -1);
+ BOOST_CHECK_EQUAL(GetBoolArg("booltest1"), true);
+ BOOST_CHECK_EQUAL(GetBoolArg("booltest2"), false);
+ BOOST_CHECK_EQUAL(GetBoolArg("booltest3"), false);
+ BOOST_CHECK_EQUAL(GetBoolArg("booltest4"), true);
+}
+
+BOOST_AUTO_TEST_CASE(util_WildcardMatch)
+{
+ BOOST_CHECK(WildcardMatch("127.0.0.1", "*"));
+ BOOST_CHECK(WildcardMatch("127.0.0.1", "127.*"));
+ BOOST_CHECK(WildcardMatch("abcdef", "a?cde?"));
+ BOOST_CHECK(!WildcardMatch("abcdef", "a?cde??"));
+ BOOST_CHECK(WildcardMatch("abcdef", "a*f"));
+ BOOST_CHECK(!WildcardMatch("abcdef", "a*x"));
+ BOOST_CHECK(WildcardMatch("", "*"));
+}
+
+BOOST_AUTO_TEST_CASE(util_FormatMoney)
+{
+ BOOST_CHECK_EQUAL(FormatMoney(0, false), "0.00");
+ BOOST_CHECK_EQUAL(FormatMoney((COIN/10000)*123456789, false), "12345.6789");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN, true), "+1.00");
+ BOOST_CHECK_EQUAL(FormatMoney(-COIN, false), "-1.00");
+ BOOST_CHECK_EQUAL(FormatMoney(-COIN, true), "-1.00");
+
+ BOOST_CHECK_EQUAL(FormatMoney(COIN*100000000, false), "100000000.00");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN*10000000, false), "10000000.00");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN*1000000, false), "1000000.00");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN*100000, false), "100000.00");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN*10000, false), "10000.00");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN*1000, false), "1000.00");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN*100, false), "100.00");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN*10, false), "10.00");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN, false), "1.00");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN/10, false), "0.10");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN/100, false), "0.01");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN/1000, false), "0.001");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN/10000, false), "0.0001");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN/100000, false), "0.00001");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN/1000000, false), "0.000001");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN/10000000, false), "0.0000001");
+ BOOST_CHECK_EQUAL(FormatMoney(COIN/100000000, false), "0.00000001");
+}
+
+BOOST_AUTO_TEST_CASE(util_ParseMoney)
+{
+ int64 ret = 0;
+ BOOST_CHECK(ParseMoney("0.0", ret));
+ BOOST_CHECK_EQUAL(ret, 0);
+
+ BOOST_CHECK(ParseMoney("12345.6789", ret));
+ BOOST_CHECK_EQUAL(ret, (COIN/10000)*123456789);
+
+ BOOST_CHECK(ParseMoney("100000000.00", ret));
+ BOOST_CHECK_EQUAL(ret, COIN*100000000);
+ BOOST_CHECK(ParseMoney("10000000.00", ret));
+ BOOST_CHECK_EQUAL(ret, COIN*10000000);
+ BOOST_CHECK(ParseMoney("1000000.00", ret));
+ BOOST_CHECK_EQUAL(ret, COIN*1000000);
+ BOOST_CHECK(ParseMoney("100000.00", ret));
+ BOOST_CHECK_EQUAL(ret, COIN*100000);
+ BOOST_CHECK(ParseMoney("10000.00", ret));
+ BOOST_CHECK_EQUAL(ret, COIN*10000);
+ BOOST_CHECK(ParseMoney("1000.00", ret));
+ BOOST_CHECK_EQUAL(ret, COIN*1000);
+ BOOST_CHECK(ParseMoney("100.00", ret));
+ BOOST_CHECK_EQUAL(ret, COIN*100);
+ BOOST_CHECK(ParseMoney("10.00", ret));
+ BOOST_CHECK_EQUAL(ret, COIN*10);
+ BOOST_CHECK(ParseMoney("1.00", ret));
+ BOOST_CHECK_EQUAL(ret, COIN);
+ BOOST_CHECK(ParseMoney("0.1", ret));
+ BOOST_CHECK_EQUAL(ret, COIN/10);
+ BOOST_CHECK(ParseMoney("0.01", ret));
+ BOOST_CHECK_EQUAL(ret, COIN/100);
+ BOOST_CHECK(ParseMoney("0.001", ret));
+ BOOST_CHECK_EQUAL(ret, COIN/1000);
+ BOOST_CHECK(ParseMoney("0.0001", ret));
+ BOOST_CHECK_EQUAL(ret, COIN/10000);
+ BOOST_CHECK(ParseMoney("0.00001", ret));
+ BOOST_CHECK_EQUAL(ret, COIN/100000);
+ BOOST_CHECK(ParseMoney("0.000001", ret));
+ BOOST_CHECK_EQUAL(ret, COIN/1000000);
+ BOOST_CHECK(ParseMoney("0.0000001", ret));
+ BOOST_CHECK_EQUAL(ret, COIN/10000000);
+ BOOST_CHECK(ParseMoney("0.00000001", ret));
+ BOOST_CHECK_EQUAL(ret, COIN/100000000);
+
+ // Attempted 63 bit overflow should fail
+ BOOST_CHECK(!ParseMoney("92233720368.54775808", ret));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util.cpp b/src/util.cpp
index a5e3d30995..02e2d21491 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -387,7 +387,7 @@ bool ParseMoney(const char* pszIn, int64& nRet)
for (; *p; p++)
if (!isspace(*p))
return false;
- if (strWhole.size() > 14)
+ if (strWhole.size() > 10) // guard against 63 bit overflow
return false;
if (nUnits < 0 || nUnits > COIN)
return false;
@@ -443,7 +443,6 @@ vector<unsigned char> ParseHex(const string& str)
return ParseHex(str.c_str());
}
-
void ParseParameters(int argc, char* argv[])
{
mapArgs.clear();
@@ -470,6 +469,145 @@ void ParseParameters(int argc, char* argv[])
}
}
+string EncodeBase64(const unsigned char* pch, size_t len)
+{
+ static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ string strRet="";
+ strRet.reserve((len+2)/3*4);
+
+ int mode=0, left=0;
+ const unsigned char *pchEnd = pch+len;
+
+ while (pch<pchEnd)
+ {
+ int enc = *(pch++);
+ switch (mode)
+ {
+ case 0: // we have no bits
+ strRet += pbase64[enc >> 2];
+ left = (enc & 3) << 4;
+ mode = 1;
+ break;
+
+ case 1: // we have two bits
+ strRet += pbase64[left | (enc >> 4)];
+ left = (enc & 15) << 2;
+ mode = 2;
+ break;
+
+ case 2: // we have four bits
+ strRet += pbase64[left | (enc >> 6)];
+ strRet += pbase64[enc & 63];
+ mode = 0;
+ break;
+ }
+ }
+
+ if (mode)
+ {
+ strRet += pbase64[left];
+ strRet += '=';
+ if (mode == 1)
+ strRet += '=';
+ }
+
+ return strRet;
+}
+
+string EncodeBase64(const string& str)
+{
+ return EncodeBase64((const unsigned char*)str.c_str(), str.size());
+}
+
+vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
+{
+ static const int decode64_table[256] =
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
+ -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ };
+
+ if (pfInvalid)
+ *pfInvalid = false;
+
+ vector<unsigned char> vchRet;
+ vchRet.reserve(strlen(p)*3/4);
+
+ int mode = 0;
+ int left = 0;
+
+ while (1)
+ {
+ int dec = decode64_table[*p];
+ if (dec == -1) break;
+ p++;
+ switch (mode)
+ {
+ case 0: // we have no bits and get 6
+ left = dec;
+ mode = 1;
+ break;
+
+ case 1: // we have 6 bits and keep 4
+ vchRet.push_back((left<<2) | (dec>>4));
+ left = dec & 15;
+ mode = 2;
+ break;
+
+ case 2: // we have 4 bits and get 6, we keep 2
+ vchRet.push_back((left<<4) | (dec>>2));
+ left = dec & 3;
+ mode = 3;
+ break;
+
+ case 3: // we have 2 bits and get 6
+ vchRet.push_back((left<<6) | dec);
+ mode = 0;
+ break;
+ }
+ }
+
+ if (pfInvalid)
+ switch (mode)
+ {
+ case 0: // 4n base64 characters processed: ok
+ break;
+
+ case 1: // 4n+1 base64 character processed: impossible
+ *pfInvalid = true;
+ break;
+
+ case 2: // 4n+2 base64 characters processed: require '=='
+ if (left || p[0] != '=' || p[1] != '=' || decode64_table[p[2]] != -1)
+ *pfInvalid = true;
+ break;
+
+ case 3: // 4n+3 base64 characters processed: require '='
+ if (left || p[0] != '=' || decode64_table[p[1]] != -1)
+ *pfInvalid = true;
+ break;
+ }
+
+ return vchRet;
+}
+
+string DecodeBase64(const string& str)
+{
+ vector<unsigned char> vchRet = DecodeBase64(str.c_str());
+ return string((const char*)&vchRet[0], vchRet.size());
+}
bool WildcardMatch(const char* psz, const char* mask)
diff --git a/src/util.h b/src/util.h
index 33013a2f81..f0b2f4a71c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -178,6 +178,10 @@ bool ParseMoney(const std::string& str, int64& nRet);
bool ParseMoney(const char* pszIn, int64& nRet);
std::vector<unsigned char> ParseHex(const char* psz);
std::vector<unsigned char> ParseHex(const std::string& str);
+std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = NULL);
+std::string DecodeBase64(const std::string& str);
+std::string EncodeBase64(const unsigned char* pch, size_t len);
+std::string EncodeBase64(const std::string& str);
void ParseParameters(int argc, char* argv[]);
const char* wxGetTranslation(const char* psz);
bool WildcardMatch(const char* psz, const char* mask);
@@ -566,6 +570,51 @@ inline uint160 Hash160(const std::vector<unsigned char>& vch)
}
+// Median filter over a stream of values
+// Returns the median of the last N numbers
+template <typename T> class CMedianFilter
+{
+private:
+ std::vector<T> vValues;
+ std::vector<T> vSorted;
+ int nSize;
+public:
+ CMedianFilter(int size, T initial_value):
+ nSize(size)
+ {
+ vValues.reserve(size);
+ vValues.push_back(initial_value);
+ vSorted = vValues;
+ }
+
+ void input(T value)
+ {
+ if(vValues.size() == nSize)
+ {
+ vValues.erase(vValues.begin());
+ }
+ vValues.push_back(value);
+
+ vSorted.resize(vValues.size());
+ std::copy(vValues.begin(), vValues.end(), vSorted.begin());
+ std::sort(vSorted.begin(), vSorted.end());
+ }
+
+ T median() const
+ {
+ int size = vSorted.size();
+ assert(size>0);
+ if(size & 1) // Odd number of elements
+ {
+ return vSorted[size/2];
+ }
+ else // Even number of elements
+ {
+ return (vSorted[size/2-1] + vSorted[size/2]) / 2;
+ }
+ }
+};
+