diff options
-rw-r--r-- | bitcoin-qt.pro | 87 | ||||
-rw-r--r-- | src/bitcoinrpc.cpp | 215 | ||||
-rw-r--r-- | src/key.h | 140 | ||||
-rw-r--r-- | src/main.cpp | 18 | ||||
-rw-r--r-- | src/main.h | 1 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 2 | ||||
-rw-r--r-- | src/qt/bitcoin.qrc | 5 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 5 | ||||
-rw-r--r-- | src/qt/forms/sendcoinsdialog.ui | 48 | ||||
-rw-r--r-- | src/qt/notificator.cpp | 12 | ||||
-rw-r--r-- | src/qt/notificator.h | 4 | ||||
-rw-r--r-- | src/qt/optionsdialog.cpp | 4 | ||||
-rw-r--r-- | src/qt/overviewpage.cpp | 3 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 11 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.h | 1 | ||||
-rw-r--r-- | src/qt/transactiondesc.h | 1 | ||||
-rw-r--r-- | src/test/base58_tests.cpp | 87 | ||||
-rw-r--r-- | src/test/base64_tests.cpp | 20 | ||||
-rw-r--r-- | src/test/test_bitcoin.cpp | 3 | ||||
-rw-r--r-- | src/test/util_tests.cpp | 214 | ||||
-rw-r--r-- | src/util.cpp | 142 | ||||
-rw-r--r-- | src/util.h | 49 |
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]); @@ -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; + } + } +}; + |