From 2ddaae4f87e44ebe0200c9319640965de0022879 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Fri, 21 Mar 2014 11:06:59 +0100 Subject: [PATCH 01/22] Implement name_update in createrawtransaction. Add support for name_update operations in the createrawtransaction RPC call with a new, optional third argument. --- src/bitcoinrpc.cpp | 19 +++++++++++--- src/namecoin.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++ src/namecoin.h | 5 ++++ 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index a78556a2e..26b85c58a 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -2746,17 +2746,23 @@ Value listunspent(const Array& params, bool fHelp) Value createrawtransaction(const Array& params, bool fHelp) { - if (fHelp || params.size() != 2) + if (fHelp || (params.size() != 2 && params.size() != 3)) throw runtime_error( "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n" + "optional third argument:\n" + " {\"op\":\"name_update\", \"name\":name, \"value\":value, \"address\":address}\n\n" "Create a transaction spending given inputs\n" "(array of objects containing transaction id and output number),\n" "sending to given address(es).\n" - "Returns hex-encoded raw transaction.\n" + "Optionally, a name_update operation can be performed.\n\n" + "Returns hex-encoded raw transaction.\n\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network."); - RPCTypeCheck(params, boost::assign::list_of(array_type)(obj_type)); + if (params.size() == 2) + RPCTypeCheck(params, boost::assign::list_of(array_type)(obj_type)); + else + RPCTypeCheck(params, boost::assign::list_of(array_type)(obj_type)(obj_type)); Array inputs = params[0].get_array(); Object sendTo = params[1].get_obj(); @@ -2799,6 +2805,12 @@ Value createrawtransaction(const Array& params, bool fHelp) rawTx.vout.push_back(out); } + if (params.size() == 3) + { + Object nameOp = params[2].get_obj(); + AddRawTxNameOperation(rawTx, nameOp); + } + CDataStream ss(SER_NETWORK, VERSION); ss << rawTx; return HexStr(ss.begin(), ss.end()); @@ -3839,6 +3851,7 @@ void RPCConvertValues(const std::string &strMethod, json_spirit::Array ¶ms) if (strMethod == "getrawtransaction" && n > 1) ConvertTo(params[1]); if (strMethod == "createrawtransaction" && n > 0) ConvertTo(params[0]); if (strMethod == "createrawtransaction" && n > 1) ConvertTo(params[1]); + if (strMethod == "createrawtransaction" && n > 2) ConvertTo(params[2]); if (strMethod == "signrawtransaction" && n > 1) ConvertTo(params[1], true); if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); diff --git a/src/namecoin.cpp b/src/namecoin.cpp index 3166a0051..0e3613d24 100644 --- a/src/namecoin.cpp +++ b/src/namecoin.cpp @@ -1256,6 +1256,70 @@ Value name_new(const Array& params, bool fHelp) return res; } +/* Implement name operations for createrawtransaction. */ +void +AddRawTxNameOperation (CTransaction& tx, const Object& obj) +{ + Value val = find_value (obj, "op"); + if (val.type () != str_type) + throw JSONRPCError (RPC_INVALID_PARAMETER, "Missing op key."); + const std::string op = val.get_str (); + + if (op != "name_update") + throw std::runtime_error ("Only name_update is implemented" + " for raw transactions at the moment."); + + val = find_value (obj, "name"); + if (val.type () != str_type) + throw JSONRPCError (RPC_INVALID_PARAMETER, "Missing name key."); + const std::string name = val.get_str (); + const std::vector vchName = vchFromString (name); + + val = find_value (obj, "value"); + if (val.type () != str_type) + throw JSONRPCError (RPC_INVALID_PARAMETER, "Missing value key."); + const std::string value = val.get_str (); + + val = find_value (obj, "address"); + if (val.type () != str_type) + throw JSONRPCError (RPC_INVALID_PARAMETER, "Missing address key."); + const std::string address = val.get_str (); + if (!IsValidBitcoinAddress (address)) + { + std::ostringstream msg; + msg << "Invalid Namecoin address: " << address; + throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, msg.str ()); + } + + /* Find the transaction input to add. */ + + CRITICAL_BLOCK(cs_main) + { + CNameDB dbName("r"); + CTransaction prevTx; + if (!GetTxOfName (dbName, vchName, prevTx)) + throw std::runtime_error ("could not find a coin with this name"); + const uint256 prevTxHash = prevTx.GetHash(); + const int nTxOut = IndexOfNameOutput (prevTx); + + CTxIn in(COutPoint(prevTxHash, nTxOut)); + tx.vin.push_back (in); + } + + /* Construct the transaction output. */ + + CScript scriptPubKeyOrig; + scriptPubKeyOrig.SetBitcoinAddress (address); + + CScript scriptPubKey; + scriptPubKey << OP_NAME_UPDATE << vchName << vchFromString (value) + << OP_2DROP << OP_DROP; + scriptPubKey += scriptPubKeyOrig; + + CTxOut out(MIN_AMOUNT, scriptPubKey); + tx.vout.push_back (out); +} + void UnspendInputs(CWalletTx& wtx) { set setCoins; diff --git a/src/namecoin.h b/src/namecoin.h index 4cd67b273..661ff8c32 100644 --- a/src/namecoin.h +++ b/src/namecoin.h @@ -1,6 +1,8 @@ #ifndef NAMECOIN_H #define NAMECOIN_H +#include "json/json_spirit.h" + class CNameDB : public CDB { protected: @@ -91,4 +93,7 @@ std::string SendMoneyWithInputTx(CScript scriptPubKey, int64 nValue, int64 nNetF bool CreateTransactionWithInputTx(const std::vector >& vecSend, CWalletTx& wtxIn, int nTxOut, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); int64 GetNetworkFee(int nHeight); +/* Handle the name operation part of the RPC call createrawtransaction. */ +void AddRawTxNameOperation(CTransaction& tx, const json_spirit::Object& obj); + #endif // NAMECOIN_H From 50f238f924c023f0b89aed05c321c39f77402f46 Mon Sep 17 00:00:00 2001 From: cryptopartyfr Date: Sat, 25 Jan 2014 05:32:40 +0100 Subject: [PATCH 02/22] Update FAQ.md typo: you wrote "namecoind first_update" instead of "namecoind name_firstupdate" (noticed by @bortzmeyer) --- FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index a076a72bd..77d3e3afc 100644 --- a/FAQ.md +++ b/FAQ.md @@ -50,7 +50,7 @@ This will output two values - a random number (short) and a transaction ID (long **wait 12 blocks**, then: -`namecoind first_update ` +`namecoind name_firstupdate ` **Q: How do I make my browser recognize the .bit top level domain?** From 3d545675552d8294e72845b045c8856e2fd52ec3 Mon Sep 17 00:00:00 2001 From: olgasanko Date: Mon, 3 Feb 2014 11:54:42 +0300 Subject: [PATCH 03/22] Update bitcoinrpc.cpp listsinceblock method added - allows to get all transactions in blocks since block [blockhash], or all transactions if omitted. This method is very important for monitoring incoming transactions mechanism. --- src/bitcoinrpc.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index d0a0d1de6..a78556a2e 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1503,6 +1503,69 @@ Value listaccounts(const Array& params, bool fHelp) return ret; } +Value listsinceblock(const Array& params, bool fHelp) +{ + if (fHelp) + throw runtime_error( + "listsinceblock [blockhash] [target-confirmations]\n" + "Get all transactions in blocks since block [blockhash], or all transactions if omitted"); + + CBlockIndex *pindex = NULL; + int target_confirms = 1; + + if (params.size() > 0) + { + uint256 blockId = 0; + + blockId.SetHex(params[0].get_str()); + pindex = CBlockLocator(blockId).GetBlockIndex(); + } + + if (params.size() > 1) + { + target_confirms = params[1].get_int(); + + if (target_confirms < 1) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + } + + int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; + + Array transactions; + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) + { + CWalletTx tx = (*it).second; + + if (depth == -1 || tx.GetDepthInMainChain() < depth) + ListTransactions(tx, "*", 0, true, transactions); + } + + uint256 lastblock; + + if (target_confirms == 1) + { + lastblock = hashBestChain; + } + else + { + int target_height = pindexBest->nHeight + 1 - target_confirms; + + CBlockIndex *block; + for (block = pindexBest; + block && block->nHeight > target_height; + block = block->pprev) { } + + lastblock = block ? block->GetBlockHash() : 0; + } + + Object ret; + ret.push_back(Pair("transactions", transactions)); + ret.push_back(Pair("lastblock", lastblock.GetHex())); + + return ret; +} + Value gettransaction(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -3081,6 +3144,7 @@ pair pCallTable[] = make_pair("verifymessage", &verifymessage), make_pair("listunspent", &listunspent), make_pair("listaddressgroupings", &listaddressgroupings), + make_pair("listsinceblock", &listsinceblock), make_pair("getrawtransaction", &getrawtransaction), make_pair("createrawtransaction", &createrawtransaction), make_pair("decoderawtransaction", &decoderawtransaction), @@ -3777,6 +3841,7 @@ void RPCConvertValues(const std::string &strMethod, json_spirit::Array ¶ms) if (strMethod == "createrawtransaction" && n > 1) ConvertTo(params[1]); if (strMethod == "signrawtransaction" && n > 1) ConvertTo(params[1], true); if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); + if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); } int CommandLineRPC(int argc, char *argv[]) From e3e9d356a5328fd99223a9b2a787dfc44f00f94e Mon Sep 17 00:00:00 2001 From: khalahan Date: Thu, 6 Feb 2014 17:36:54 +0100 Subject: [PATCH 04/22] Improve name_filter speed (x2) Removed some old checks that are not needed anymore --- src/namecoin.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/namecoin.cpp b/src/namecoin.cpp index 2ffb36b22..5db4286fc 100644 --- a/src/namecoin.cpp +++ b/src/namecoin.cpp @@ -916,12 +916,7 @@ Value name_filter(const Array& params, bool fHelp) Object oName; oName.push_back(Pair("name", name)); - CTransaction tx; - CDiskTxPos txPos = txName.txPos; - if ((nHeight + GetDisplayExpirationDepth(nHeight) - pindexBest->nHeight <= 0) - || txPos.IsNull() - || !tx.ReadFromDisk(txPos)) - //|| !GetValueOfNameTx(tx, vchValue)) + if (nHeight + GetDisplayExpirationDepth(nHeight) - pindexBest->nHeight <= 0) { oName.push_back(Pair("expired", 1)); } From 1e8ad5d4ccd646880f2056712a1a961f61f452b8 Mon Sep 17 00:00:00 2001 From: khalahan Date: Fri, 7 Feb 2014 19:37:28 +0100 Subject: [PATCH 05/22] Improve name_filter speed Regexp compilation moved outside of loop Small code optims --- src/namecoin.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/namecoin.cpp b/src/namecoin.cpp index 5db4286fc..3166a0051 100644 --- a/src/namecoin.cpp +++ b/src/namecoin.cpp @@ -890,15 +890,17 @@ Value name_filter(const Array& params, bool fHelp) if (!dbName.ScanNames(vchName, 100000000, nameScan)) throw JSONRPCError(RPC_WALLET_ERROR, "scan failed"); + // compile regex once + using namespace boost::xpressive; + smatch nameparts; + sregex cregex = sregex::compile(strRegexp); + pair, CNameIndex> pairScan; BOOST_FOREACH(pairScan, nameScan) { string name = stringFromVch(pairScan.first); // regexp - using namespace boost::xpressive; - smatch nameparts; - sregex cregex = sregex::compile(strRegexp); if(strRegexp != "" && !regex_search(name, nameparts, cregex)) continue; @@ -915,17 +917,19 @@ Value name_filter(const Array& params, bool fHelp) continue; Object oName; - oName.push_back(Pair("name", name)); - if (nHeight + GetDisplayExpirationDepth(nHeight) - pindexBest->nHeight <= 0) - { - oName.push_back(Pair("expired", 1)); - } - else - { - vector vchValue = txName.vValue; - string value = stringFromVch(vchValue); - oName.push_back(Pair("value", value)); - oName.push_back(Pair("expires_in", nHeight + GetDisplayExpirationDepth(nHeight) - pindexBest->nHeight)); + if (!fStat) { + oName.push_back(Pair("name", name)); + int nExpiresIn = nHeight + GetDisplayExpirationDepth(nHeight) - pindexBest->nHeight; + if (nExpiresIn <= 0) + { + oName.push_back(Pair("expired", 1)); + } + else + { + string value = stringFromVch(txName.vValue); + oName.push_back(Pair("value", value)); + oName.push_back(Pair("expires_in", nExpiresIn)); + } } oRes.push_back(oName); @@ -939,7 +943,7 @@ Value name_filter(const Array& params, bool fHelp) dbName.test(); } - if(fStat) + if (fStat) { Object oStat; oStat.push_back(Pair("blocks", (int)nBestHeight)); From 4523d97cce71c12a7f5c72c53785e7020cde8dda Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Fri, 7 Feb 2014 21:16:34 +0100 Subject: [PATCH 06/22] Add UI for configuring name as identity. --- src/qt/forms/configurenamedialog.ui | 60 ++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/qt/forms/configurenamedialog.ui b/src/qt/forms/configurenamedialog.ui index c03c03dd5..e0b39ac68 100644 --- a/src/qt/forms/configurenamedialog.ui +++ b/src/qt/forms/configurenamedialog.ui @@ -72,7 +72,7 @@ - &Data: + Data: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -272,6 +272,64 @@ ns0.xname.org + + + I&dentity Configuration + + + + + + &Real name: + + + nsTranslateEdit + + + + + + + Real name or pseudonym + + + + + + + &Email address: + + + nsTranslateEdit + + + + + + + Email address + + + + + + + Bit&message: + + + nsTranslateEdit + + + + + + + Bitmessage address + + + + + &Custom Configuration From c80d3d11878468267d0c54c977d8d9b2cabdd04b Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Fri, 7 Feb 2014 22:22:54 +0100 Subject: [PATCH 07/22] Implement logic for ID name config tab. --- src/qt/configurenamedialog.cpp | 55 ++++++++++++++++++++++++++++- src/qt/configurenamedialog.h | 4 +++ src/qt/forms/configurenamedialog.ui | 6 ++-- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/qt/configurenamedialog.cpp b/src/qt/configurenamedialog.cpp index 8cb83b77e..3a1dff146 100644 --- a/src/qt/configurenamedialog.cpp +++ b/src/qt/configurenamedialog.cpp @@ -57,6 +57,8 @@ ConfigureNameDialog::ConfigureNameDialog(const QString &name_, const QString &da // Empty data - select DNS for domains, custom for other if (name.startsWith("d/")) ui->tabWidget->setCurrentWidget(ui->tab_dns); + else if (name.startsWith("id/")) + ui->tabWidget->setCurrentWidget(ui->tab_id); else ui->tabWidget->setCurrentWidget(ui->tab_json); } @@ -124,6 +126,7 @@ ConfigureNameDialog::ConfigureNameDialog(const QString &name_, const QString &da else { // Check conformance to IP type + // FIXME: Allow string array for fingerprint. json_spirit::Object obj = val.get_obj(); QString ip, fingerprint; json_spirit::Value ipVal = json_spirit::find_value(obj, "ip"); @@ -164,7 +167,39 @@ ConfigureNameDialog::ConfigureNameDialog(const QString &name_, const QString &da ui->tabWidget->setCurrentWidget(ui->tab_ip); } else - ui->tabWidget->setCurrentWidget(ui->tab_json); + { + // Check conformance to ID type. + ok = false; + QString name, email, bm; + json_spirit::Value nameVal = json_spirit::find_value(obj, "name"); + json_spirit::Value emailVal = json_spirit::find_value(obj, "email"); + json_spirit::Value bmVal = json_spirit::find_value(obj, "bitmessage"); + if (nameVal.type() == json_spirit::str_type) + { + ok = true; + name = QString::fromStdString(nameVal.get_str()); + } + if (emailVal.type() == json_spirit::str_type) + { + ok = true; + email = QString::fromStdString(emailVal.get_str()); + } + if (bmVal.type() == json_spirit::str_type) + { + ok = true; + bm = QString::fromStdString(bmVal.get_str()); + } + + if (ok) + { + ui->idNameEdit->setText(name); + ui->idEmailEdit->setText(email); + ui->idBitmessageEdit->setText(bm); + ui->tabWidget->setCurrentWidget(ui->tab_id); + } + else + ui->tabWidget->setCurrentWidget(ui->tab_json); + } } } @@ -325,6 +360,24 @@ void ConfigureNameDialog::SetIP() ui->dataEdit->setText(QString::fromStdString(json_spirit::write_string(json_spirit::Value(data), false))); } +void ConfigureNameDialog::SetID() +{ + json_spirit::Object data; + + const QString name = ui->idNameEdit->text().trimmed(); + const QString email = ui->idEmailEdit->text().trimmed(); + const QString bm = ui->idBitmessageEdit->text().trimmed(); + + if (!name.isEmpty()) + data.push_back(json_spirit::Pair("name", name.toStdString())); + if (!email.isEmpty()) + data.push_back(json_spirit::Pair("email", email.toStdString())); + if (!bm.isEmpty()) + data.push_back(json_spirit::Pair("bitmessage", bm.toStdString())); + + ui->dataEdit->setText(QString::fromStdString(json_spirit::write_string(json_spirit::Value(data), false))); +} + void ConfigureNameDialog::on_dataEdit_textChanged(const QString &text) { json_spirit::Value val; diff --git a/src/qt/configurenamedialog.h b/src/qt/configurenamedialog.h index 3733ed35e..346288ead 100644 --- a/src/qt/configurenamedialog.h +++ b/src/qt/configurenamedialog.h @@ -34,6 +34,9 @@ public slots: void on_nsFingerprintEdit_textChanged() { if (initialized) SetDNS(); } void on_ipEdit_textChanged(const QString &text) { if (initialized) SetIP(); } void on_ipFingerprintEdit_textChanged() { if (initialized) SetIP(); } + void on_idNameEdit_textChanged() { if (initialized) SetID(); } + void on_idEmailEdit_textChanged() { if (initialized) SetID(); } + void on_idBitmessageEdit_textChanged() { if (initialized) SetID(); } void on_dataEdit_textChanged(const QString &text); private: @@ -46,6 +49,7 @@ public slots: void SetDNS(); void SetIP(); + void SetID(); }; #endif // CONFIGURENAMEDIALOG_H diff --git a/src/qt/forms/configurenamedialog.ui b/src/qt/forms/configurenamedialog.ui index e0b39ac68..0f0aaccfe 100644 --- a/src/qt/forms/configurenamedialog.ui +++ b/src/qt/forms/configurenamedialog.ui @@ -288,7 +288,7 @@ ns0.xname.org - + Real name or pseudonym @@ -305,7 +305,7 @@ ns0.xname.org - + Email address @@ -322,7 +322,7 @@ ns0.xname.org - + Bitmessage address From c5fa41eca158efddae613d86d22edbf94a763536 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 5 Mar 2014 10:31:09 +0100 Subject: [PATCH 08/22] Add script to run namecoin in valgrind. Add a small shell script to run namecoind in valgrind with memory leak checks and output tee'd to a log file. --- src/runValgrind.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 src/runValgrind.sh diff --git a/src/runValgrind.sh b/src/runValgrind.sh new file mode 100755 index 000000000..a36d4e55c --- /dev/null +++ b/src/runValgrind.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +valgrind --leak-check=full ./namecoind 2>&1 | tee valgrind.log From e19f0757cd7d0fc5349ed08bad88b427c1981bd7 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 5 Mar 2014 10:31:43 +0100 Subject: [PATCH 09/22] Free nodes in disconnected queue on shutdown. On shut down of the node, free CNode objects that are waiting in the "disconnected" queue. This prevents them being mentioned as "definitely lost" in valgrind reports. --- src/net.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index df7f59578..c072581dc 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -848,7 +848,7 @@ void ThreadSocketHandler2(void* parg) int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); vnThreadsRunning[0]++; if (fShutdown) - return; + goto shutdown; if (nSelect == SOCKET_ERROR) { int nErr = WSAGetLastError(); @@ -912,7 +912,7 @@ void ThreadSocketHandler2(void* parg) BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (fShutdown) - return; + goto shutdown; // // Receive @@ -1032,6 +1032,27 @@ void ThreadSocketHandler2(void* parg) Sleep(10); } + +shutdown: + + // Clean up disconnected nodes on shutdown. + printf ("Socket thread exited, freeing %d disconnected nodes...\n", + vNodesDisconnected.size ()); + while (!vNodesDisconnected.empty ()) + { + CNode* pnode = vNodesDisconnected.front (); + vNodesDisconnected.pop_front (); + + // Wait until the node is no longer used. + while (pnode->GetRefCount() > 0) + Sleep(10); + + CRITICAL_BLOCK(pnode->cs_vSend) + CRITICAL_BLOCK(pnode->cs_vRecv) + CRITICAL_BLOCK(pnode->cs_mapRequests) + CRITICAL_BLOCK(pnode->cs_inventory) + delete pnode; + } } From d6b600c4ad7c7a6e9c8cb7f1f6e45b4132445d5b Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 5 Mar 2014 10:57:21 +0100 Subject: [PATCH 10/22] Rewrite cleanup with RAII. Rewrite the goto shutdown hack by using the destructor of a local class to make the code cleaner. --- src/net.cpp | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index c072581dc..e2ac7f469 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -749,9 +749,32 @@ void ThreadSocketHandler(void* parg) void ThreadSocketHandler2(void* parg) { printf("ThreadSocketHandler started\n"); - list vNodesDisconnected; int nPrevNodeCount = 0; + class CleanupDisconnectedQueue : public list + { + public: + ~CleanupDisconnectedQueue () + { + printf ("Freeing %d disconnected nodes...\n", size()); + while (!empty ()) + { + CNode* pnode = front (); + pop_front (); + + // Wait until the node is no longer used. + while (pnode->GetRefCount() > 0) + Sleep(10); + + CRITICAL_BLOCK(pnode->cs_vSend) + CRITICAL_BLOCK(pnode->cs_vRecv) + CRITICAL_BLOCK(pnode->cs_mapRequests) + CRITICAL_BLOCK(pnode->cs_inventory) + delete pnode; + } + } + } vNodesDisconnected; + loop { // @@ -848,7 +871,7 @@ void ThreadSocketHandler2(void* parg) int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); vnThreadsRunning[0]++; if (fShutdown) - goto shutdown; + return; if (nSelect == SOCKET_ERROR) { int nErr = WSAGetLastError(); @@ -912,7 +935,7 @@ void ThreadSocketHandler2(void* parg) BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (fShutdown) - goto shutdown; + return; // // Receive @@ -1032,27 +1055,6 @@ void ThreadSocketHandler2(void* parg) Sleep(10); } - -shutdown: - - // Clean up disconnected nodes on shutdown. - printf ("Socket thread exited, freeing %d disconnected nodes...\n", - vNodesDisconnected.size ()); - while (!vNodesDisconnected.empty ()) - { - CNode* pnode = vNodesDisconnected.front (); - vNodesDisconnected.pop_front (); - - // Wait until the node is no longer used. - while (pnode->GetRefCount() > 0) - Sleep(10); - - CRITICAL_BLOCK(pnode->cs_vSend) - CRITICAL_BLOCK(pnode->cs_vRecv) - CRITICAL_BLOCK(pnode->cs_mapRequests) - CRITICAL_BLOCK(pnode->cs_inventory) - delete pnode; - } } From 8bd835098d4ee27f52c27b49330be9b7c51dd772 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 5 Mar 2014 11:57:35 +0100 Subject: [PATCH 11/22] Use new class CNodeList to free nodes on exit. Use a new "public" class CNodeList to hold lists of CNode* and to free them on program shutdown. --- src/net.cpp | 61 ++++++++++++++++++++++++----------------------------- src/net.h | 39 +++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index e2ac7f469..62b41db3a 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -54,7 +54,7 @@ uint64 nLocalHostNonce = 0; array vnThreadsRunning; SOCKET hListenSocket = INVALID_SOCKET; -vector vNodes; +CNodeList vNodes(true); CCriticalSection cs_vNodes; map, CAddress> mapAddresses; CCriticalSection cs_mapAddresses; @@ -87,6 +87,29 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); } +CNodeList::~CNodeList () +{ + printf ("Freeing %d nodes in CNodeList %p...\n", size(), this); + while (!empty ()) + { + CNode* pnode = back (); + pop_back (); + + if (fRefCounts) + pnode->Release (); + + // Wait until the node is no longer used. + while (pnode->GetRefCount () > 0) + Sleep (10); + + CRITICAL_BLOCK(pnode->cs_vSend) + CRITICAL_BLOCK(pnode->cs_vRecv) + CRITICAL_BLOCK(pnode->cs_mapRequests) + CRITICAL_BLOCK(pnode->cs_inventory) + delete pnode; + } +} + @@ -749,32 +772,9 @@ void ThreadSocketHandler(void* parg) void ThreadSocketHandler2(void* parg) { printf("ThreadSocketHandler started\n"); + CNodeList vNodesDisconnected(false); int nPrevNodeCount = 0; - class CleanupDisconnectedQueue : public list - { - public: - ~CleanupDisconnectedQueue () - { - printf ("Freeing %d disconnected nodes...\n", size()); - while (!empty ()) - { - CNode* pnode = front (); - pop_front (); - - // Wait until the node is no longer used. - while (pnode->GetRefCount() > 0) - Sleep(10); - - CRITICAL_BLOCK(pnode->cs_vSend) - CRITICAL_BLOCK(pnode->cs_vRecv) - CRITICAL_BLOCK(pnode->cs_mapRequests) - CRITICAL_BLOCK(pnode->cs_inventory) - delete pnode; - } - } - } vNodesDisconnected; - loop { // @@ -790,7 +790,7 @@ void ThreadSocketHandler2(void* parg) (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.empty())) { // remove from vNodes - vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); + vNodes.remove(pnode); // close socket and cleanup pnode->CloseSocketDisconnect(); @@ -798,14 +798,13 @@ void ThreadSocketHandler2(void* parg) // hold in disconnected pool until all refs are released pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 15 * 60); - if (pnode->fNetworkNode || pnode->fInbound) - pnode->Release(); + pnode->Release(); vNodesDisconnected.push_back(pnode); } } // Delete disconnected nodes - list vNodesDisconnectedCopy = vNodesDisconnected; + vector vNodesDisconnectedCopy = vNodesDisconnected; BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) { // wait until threads are done using it @@ -1744,10 +1743,6 @@ class CNetCleanup } ~CNetCleanup() { - // Close sockets - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->hSocket != INVALID_SOCKET) - closesocket(pnode->hSocket); if (hListenSocket != INVALID_SOCKET) if (closesocket(hListenSocket) == SOCKET_ERROR) printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); diff --git a/src/net.h b/src/net.h index 077f824ef..b17a97812 100644 --- a/src/net.h +++ b/src/net.h @@ -18,6 +18,7 @@ class CAddress; class CInv; class CRequestTracker; class CNode; +class CNodeList; class CBlockIndex; extern int nBestHeight; extern int nConnectTimeout; @@ -480,7 +481,7 @@ extern uint64 nLocalHostNonce; extern boost::array vnThreadsRunning; extern SOCKET hListenSocket; -extern std::vector vNodes; +extern CNodeList vNodes; extern CCriticalSection cs_vNodes; extern std::map, CAddress> mapAddresses; extern CCriticalSection cs_mapAddresses; @@ -966,6 +967,42 @@ class CNode void Cleanup(); }; +/* Small wrapper around list that ensures that the nodes + are deleted when the list goes out of scope. While the lists + usually live until program shutdown, this is still useful to + prevent "false positives" on memory leak checks. */ +class CNodeList : public std::vector +{ + +private: + + // Disallow default constructor. + CNodeList (); + + /* Set to true if the reference in this list counts towards the + nodes refcount. In other words, if fRefCounts is set, then + the node will be deleted already with refcount = 1 and we do not + wait for refcount = 0. */ + bool fRefCounts; + +public: + + explicit inline + CNodeList (bool fRefCountsIn) + : std::vector(), fRefCounts(fRefCountsIn) + {} + + ~CNodeList (); + + // Erase a given node from the list. + inline void + remove (CNode* pnode) + { + erase (std::remove (begin (), end (), pnode), end ()); + } + +}; + From c8933d442810992c66af27d553ce7b6684afb6c9 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 5 Mar 2014 12:18:21 +0100 Subject: [PATCH 12/22] Convert mapBlockIndex to class with RAII. Store mapBlockIndex global map into custom class that frees the memory on program shutdown using RAII. This prevents false positives in memory leak checks and is cleaner. --- src/main.cpp | 9 ++++++++- src/main.h | 20 +++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 26331adb5..f547053b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,7 @@ CCriticalSection cs_mapTransactions; unsigned int nTransactionsUpdated = 0; map mapNextTx; -map mapBlockIndex; +CMapBlockIndex mapBlockIndex; uint256 hashGenesisBlock("0x000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770"); CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download" @@ -3462,3 +3462,10 @@ std::string CBlockIndex::ToString() const (auxpow.get() != NULL) ? auxpow->GetParentBlockHash().ToString().substr(0,20).c_str() : "-" ); } + +CMapBlockIndex::~CMapBlockIndex () +{ + printf ("Freeing %d entries in CMapBlockIndex...\n", size ()); + for (iterator i = begin (); i != end (); ++i) + delete i->second; +} diff --git a/src/main.h b/src/main.h index 4fc6b7682..f488cb8c3 100644 --- a/src/main.h +++ b/src/main.h @@ -21,6 +21,7 @@ class CBlock; class CBlockIndex; +class CMapBlockIndex; class CWalletTx; class CWallet; class CKeyItem; @@ -59,7 +60,7 @@ static const int fHaveUPnP = false; extern CCriticalSection cs_main; -extern std::map mapBlockIndex; +extern CMapBlockIndex mapBlockIndex; extern uint256 hashGenesisBlock; extern CBigNum bnProofOfWorkLimit; extern CBlockIndex* pindexGenesisBlock; @@ -1302,6 +1303,23 @@ class CDiskBlockIndex : public CBlockIndex +/* Wrapper around std::map to ensure that the blocks + are freed properly. While the global map is only freed at program + shutdown, this is useful to prevent false positives when looking + for memory leaks. */ +class CMapBlockIndex : public std::map +{ + +public: + + ~CMapBlockIndex (); + +}; + + + + + From 2cf686acf4ea0599715d5b5f72120cb47b46a3f0 Mon Sep 17 00:00:00 2001 From: khalahan Date: Sat, 1 Mar 2014 23:54:51 +0100 Subject: [PATCH 13/22] Testnet difficulty calculation changes, to take effect Mar 15 2014 Allow mining of min-difficulty blocks if 20 minutes have gone by without mining a regular-difficulty block. Normal rules apply every 2016 blocks, though, so there may be a very-slow-to-confirm block at the difficulty-adjustment blocks. --- src/main.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f547053b4..bc013ca89 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -746,19 +746,40 @@ int64 static GetBlockValue(int nHeight, int64 nFees) return nSubsidy + nFees; } -unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast) +unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlock *pblock) { const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks const int64 nTargetSpacing = 10 * 60; const int64 nInterval = nTargetTimespan / nTargetSpacing; + unsigned int nProofOfWorkLimit = bnProofOfWorkLimit.GetCompact(); + // Genesis block if (pindexLast == NULL) - return bnProofOfWorkLimit.GetCompact(); + return nProofOfWorkLimit; // Only change once per interval if ((pindexLast->nHeight+1) % nInterval != 0) + { + // Special rules for testnet after 15 Mar 2014: + if (fTestNet && pblock->nTime > 1394838000) + { + // If the new block's timestamp is more than 2* 10 minutes + // then allow mining of a min-difficulty block. + if (pblock->nTime - pindexLast->nTime > nTargetSpacing*2) + return nProofOfWorkLimit; + else + { + // Return the last non-special-min-difficulty-rules-block + const CBlockIndex* pindex = pindexLast; + while (pindex->pprev && pindex->nHeight % nInterval != 0 && pindex->nBits == nProofOfWorkLimit) + pindex = pindex->pprev; + return pindex->nBits; + } + } + return pindexLast->nBits; + } // Go back the full period unless it's the first retarget after genesis. Code courtesy of ArtForz @@ -1526,7 +1547,7 @@ bool CBlock::AcceptBlock() int nHeight = pindexPrev->nHeight+1; // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev)) + if (nBits != GetNextWorkRequired(pindexPrev, this)) return error("AcceptBlock() : incorrect proof of work"); // Check timestamp against prev @@ -3125,7 +3146,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nBits = GetNextWorkRequired(pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); pblock->nNonce = 0; return pblock.release(); From be22e933e1cd822c8849b82fcd7f940e7936e330 Mon Sep 17 00:00:00 2001 From: khalahan Date: Sun, 2 Mar 2014 00:15:12 +0100 Subject: [PATCH 14/22] Update version to 0.3.73 --- src/serialize.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serialize.h b/src/serialize.h index 2a8e0ff80..a6c80d23e 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -35,7 +35,7 @@ class CDataStream; class CAutoFile; static const unsigned int MAX_SIZE = 0x02000000; -static const int VERSION = 37200; +static const int VERSION = 37300; static const char* pszSubVer = ""; static const bool VERSION_IS_BETA = false; From 929da856fd1fd383b8df57ed3a00b70b54d6f208 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 19 Mar 2014 13:47:10 +0100 Subject: [PATCH 15/22] Remove superfluous check. Remove a superfluous check for nValueIn > nValueOut in checking transacitons. The same is done right in the next line where it is verified that transaction fees are non-negative. --- src/main.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bc013ca89..3ae03bc8a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1093,9 +1093,6 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPoo if (!hooks->ConnectInputs(txdb, mapTestPool, *this, vTxPrev, vTxindex, pindexBlock, posThisTx, fBlock, fMiner)) return false; - if (nValueIn < GetValueOut()) - return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()); - // Tally transaction fees int64 nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) From 5da3bdc257579b80dff36e3714a4c06239836c38 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 26 Mar 2014 07:14:16 +0100 Subject: [PATCH 16/22] Update .gitignore. Update the top-level .gitignore to fit the generated files I see on a GNU/Linux system more closely. --- .gitignore | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 23241d271..f779df4f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ -src/*.exe *.pyc -src/bitcoin -src/bitcoind .*.swp *.*~* +qrc_bitcoin.cpp +namecoin-qt +namecoin-qt.exe src/namecoind +src/namecoind.exe +build +Makefile From 4fc01705c0271df58b3f151369ec2891ff577b49 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 26 Mar 2014 21:41:31 +0100 Subject: [PATCH 17/22] Update .gitignore further. Clarify with a comment that the ignored files are Qt-created, and exclude only toplevel Makefile. --- .gitignore | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f779df4f5..6984f5381 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,12 @@ *.pyc .*.swp *.*~* -qrc_bitcoin.cpp namecoin-qt namecoin-qt.exe src/namecoind src/namecoind.exe + +# Stuff created by the Qt build system. +qrc_bitcoin.cpp build -Makefile +/Makefile From 3aed36aaf46c791b5adb55208f7faf6e0f0a106a Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 19 Mar 2014 14:40:46 +0100 Subject: [PATCH 18/22] Show input amounts and fees in TxToJSON. In TxToJSON (decoderawtransaction / getrawtransaction), show input amounts of transactions as well as fees for better checking of correctness. --- src/bitcoinrpc.cpp | 73 +++++++++++++++++++++++++++++++++++----------- src/main.cpp | 2 +- src/main.h | 1 + 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 26b85c58a..57719a970 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -2579,12 +2579,45 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) entry.push_back(Pair("txid", tx.GetHash().GetHex())); entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime)); + + const CBlockIndex* pindex = NULL; + if (hashBlock != 0) + { + entry.push_back(Pair("blockhash", hashBlock.GetHex())); + map::const_iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) + { + pindex = (*mi).second; + if (pindex->IsInMainChain()) + { + entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); + entry.push_back(Pair("time", (boost::int64_t)pindex->nTime)); + entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime)); + } + else + entry.push_back(Pair("confirmations", 0)); + } + } + Array vin; + int64 nValueIn = 0; + bool fullValueIn = true; BOOST_FOREACH(const CTxIn& txin, tx.vin) { Object in; if (tx.IsCoinBase()) + { in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + + if (pindex) + { + const int64 val = GetBlockValue(pindex->nHeight, 0); + nValueIn += val; + in.push_back(Pair("value", ValueFromAmount(val))); + } + else + fullValueIn = false; + } else { in.push_back(Pair("txid", txin.prevout.hash.GetHex())); @@ -2593,15 +2626,36 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) o.push_back(Pair("asm", txin.scriptSig.ToString())); o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); in.push_back(Pair("scriptSig", o)); + + /* Try to retrieve previous transaction output to find + its value in order to calculate transaction fees. */ + CTransaction prevTx; + uint256 prevHashBlock = 0; + if (GetTransaction(txin.prevout.hash, prevTx, prevHashBlock)) + { + if (prevTx.vout.size () > txin.prevout.n) + { + const int64 val = prevTx.vout[txin.prevout.n].nValue; + nValueIn += val; + in.push_back(Pair("value", ValueFromAmount(val))); + } + else + fullValueIn = false; + } + else + fullValueIn = false; } in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence)); vin.push_back(in); } entry.push_back(Pair("vin", vin)); + Array vout; + int64 nValueOut = 0; for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& txout = tx.vout[i]; + nValueOut += txout.nValue; Object out; out.push_back(Pair("value", ValueFromAmount(txout.nValue))); out.push_back(Pair("n", (boost::int64_t)i)); @@ -2612,23 +2666,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) } entry.push_back(Pair("vout", vout)); - if (hashBlock != 0) - { - entry.push_back(Pair("blockhash", hashBlock.GetHex())); - map::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - { - entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); - entry.push_back(Pair("time", (boost::int64_t)pindex->nTime)); - entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime)); - } - else - entry.push_back(Pair("confirmations", 0)); - } - } + if (fullValueIn) + entry.push_back(Pair("fees", ValueFromAmount(nValueIn - nValueOut))); } Value getrawtransaction(const Array& params, bool fHelp) diff --git a/src/main.cpp b/src/main.cpp index 3ae03bc8a..cd8b8ac0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -736,7 +736,7 @@ uint256 static GetOrphanRoot(const CBlock* pblock) return pblock->GetHash(); } -int64 static GetBlockValue(int nHeight, int64 nFees) +int64 GetBlockValue(int nHeight, int64 nFees) { int64 nSubsidy = 50 * COIN; diff --git a/src/main.h b/src/main.h index f488cb8c3..e27158b88 100644 --- a/src/main.h +++ b/src/main.h @@ -109,6 +109,7 @@ CBlockIndex* FindBlockByHeight(int nHeight); bool ProcessMessages(CNode* pfrom); bool SendMessages(CNode* pto, bool fSendTrickle); void GenerateBitcoins(bool fGenerate, CWallet* pwallet); +int64 GetBlockValue(int nHeight, int64 nFees); CBlock* CreateNewBlock(CReserveKey& reservekey); void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime); void IncrementExtraNonceWithAux(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime, std::vector& vchAux); From 233eb3654d8206995eb276e23b60d69a570a60d1 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Thu, 20 Mar 2014 12:35:52 +0100 Subject: [PATCH 19/22] Decode name operations in TxToJSON. Decode name operations in TxToJSON for better results in decoderawtransaction and friends. --- src/bitcoinrpc.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++-- src/namecoin.cpp | 1 - src/namecoin.h | 1 + 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 57719a970..cbce254e5 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -2553,11 +2553,68 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out) string address; int nRequired = 1; - out.push_back(Pair("asm", scriptPubKey.ToString())); + /* If this is a name transaction, try to strip off the initial + script and decode the rest for better results. */ + std::string nameAsmPrefix = ""; + CScript script(scriptPubKey); + int op; + vector > vvch; + CScript::const_iterator pc = script.begin(); + if (DecodeNameScript(script, op, vvch, pc)) + { + Object nameOp; + + switch (op) + { + case OP_NAME_NEW: + { + assert(vvch.size() == 1); + const std::string rand = HexStr(vvch[0].begin(), vvch[0].end()); + nameOp.push_back(Pair("op", "name_new")); + nameOp.push_back(Pair("rand", rand)); + break; + } + + case OP_NAME_FIRSTUPDATE: + { + assert(vvch.size() == 3); + const std::string name(vvch[0].begin(), vvch[0].end()); + const std::string rand = HexStr(vvch[1].begin(), vvch[1].end()); + const std::string val(vvch[2].begin(), vvch[2].end()); + nameOp.push_back(Pair("op", "name_firstupdate")); + nameOp.push_back(Pair("name", name)); + nameOp.push_back(Pair("rand", rand)); + nameOp.push_back(Pair("value", val)); + break; + } + + case OP_NAME_UPDATE: + { + assert(vvch.size() == 2); + const std::string name(vvch[0].begin(), vvch[0].end()); + const std::string val(vvch[1].begin(), vvch[1].end()); + nameOp.push_back(Pair("op", "name_update")); + nameOp.push_back(Pair("name", name)); + nameOp.push_back(Pair("value", val)); + break; + } + + default: + nameOp.push_back(Pair("op", "unknown")); + break; + } + + out.push_back(Pair("nameOp", nameOp)); + nameAsmPrefix = "NAME_OPERATION "; + script = CScript(pc, script.end()); + } + + /* Write out the results. */ + out.push_back(Pair("asm", nameAsmPrefix + script.ToString())); out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); //if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) - if (!ExtractDestination(scriptPubKey, address)) + if (!ExtractDestination(script, address)) { out.push_back(Pair("type", "nonstandard" /*GetTxnOutputType(TX_NONSTANDARD)*/ )); return; diff --git a/src/namecoin.cpp b/src/namecoin.cpp index 0e3613d24..371045a5a 100644 --- a/src/namecoin.cpp +++ b/src/namecoin.cpp @@ -40,7 +40,6 @@ extern std::map > mapMyNameHashes; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); // forward decls -extern bool DecodeNameScript(const CScript& script, int& op, vector > &vvch, CScript::const_iterator& pc); extern bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet); extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType); extern bool IsConflictedTx(CTxDB& txdb, const CTransaction& tx, vector& name); diff --git a/src/namecoin.h b/src/namecoin.h index 661ff8c32..597bff92b 100644 --- a/src/namecoin.h +++ b/src/namecoin.h @@ -87,6 +87,7 @@ int GetDisplayExpirationDepth(int nHeight); bool GetNameOfTx(const CTransaction& tx, std::vector& name); bool GetValueOfNameTx(const CTransaction& tx, std::vector& value); bool DecodeNameTx(const CTransaction& tx, int& op, int& nOut, std::vector >& vvch, int nHeight); +bool DecodeNameScript(const CScript& script, int& op, std::vector > &vvch, CScript::const_iterator& pc); bool DecodeNameScript(const CScript& script, int& op, std::vector > &vvch); bool GetNameAddress(const CTransaction& tx, std::string& strAddress); std::string SendMoneyWithInputTx(CScript scriptPubKey, int64 nValue, int64 nNetFee, CWalletTx& wtxIn, CWalletTx& wtxNew, bool fAskFee); From 989d9c10ecddc92119b66ef24efc6b6b19048ee0 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Thu, 20 Mar 2014 13:04:43 +0100 Subject: [PATCH 20/22] Handle pay-to-pubkey in ExtractDestination. Handle OP_PUBKEY also (and not just OP_PUBKEYHASH) in ExtractDestination, so that those transactions are decoded correctly. --- src/bitcoinrpc.cpp | 4 ++++ src/script.cpp | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index cbce254e5..9e33f160e 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -2620,6 +2620,10 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out) return; } + /* Note: ExtractDestination handles both pubkey hash and pubkey, + but the code below prints pubkeyhash as type in both cases. That + is not so big a deal, presumably. */ + out.push_back(Pair("reqSigs", nRequired)); out.push_back(Pair("type", "pubkeyhash" /*GetTxnOutputType(type)*/ )); diff --git a/src/script.cpp b/src/script.cpp index 75ceb9c01..d58d84ae2 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1322,11 +1322,8 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig bool ExtractDestination(const CScript& scriptPubKey, std::string& addressRet) { - uint160 h; - if (!ExtractHash160(scriptPubKey, h)) - return false; - addressRet = Hash160ToAddress(h); - return true; + addressRet = scriptPubKey.GetBitcoinAddress(); + return (addressRet != ""); } static CScript PushAll(const vector& values) From df9fa2390d918e40e90b95ff7df1de43989ea065 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 26 Mar 2014 07:14:16 +0100 Subject: [PATCH 21/22] Update .gitignore. Update the top-level .gitignore to fit the generated files I see on a GNU/Linux system more closely. --- .gitignore | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 23241d271..f779df4f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ -src/*.exe *.pyc -src/bitcoin -src/bitcoind .*.swp *.*~* +qrc_bitcoin.cpp +namecoin-qt +namecoin-qt.exe src/namecoind +src/namecoind.exe +build +Makefile From cce8bf1d25d7c4d05f55fa0118f517f9ec25c04d Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 26 Mar 2014 21:41:31 +0100 Subject: [PATCH 22/22] Update .gitignore further. Clarify with a comment that the ignored files are Qt-created, and exclude only toplevel Makefile. --- .gitignore | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f779df4f5..6984f5381 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,12 @@ *.pyc .*.swp *.*~* -qrc_bitcoin.cpp namecoin-qt namecoin-qt.exe src/namecoind src/namecoind.exe + +# Stuff created by the Qt build system. +qrc_bitcoin.cpp build -Makefile +/Makefile