diff options
-rw-r--r-- | contrib/debian/examples/bitcoin.conf | 18 | ||||
-rwxr-xr-x | qa/rpc-tests/wallet.py | 21 | ||||
-rw-r--r-- | src/main.cpp | 2 | ||||
-rw-r--r-- | src/main.h | 1 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 18 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 2 | ||||
-rw-r--r-- | src/qt/peertablemodel.cpp | 2 | ||||
-rw-r--r-- | src/qt/rpcconsole.cpp | 1 | ||||
-rw-r--r-- | src/qt/transactiontablemodel.cpp | 4 | ||||
-rw-r--r-- | src/qt/walletview.cpp | 8 | ||||
-rw-r--r-- | src/qt/walletview.h | 2 | ||||
-rw-r--r-- | src/rpcserver.cpp | 3 | ||||
-rw-r--r-- | src/rpcserver.h | 1 | ||||
-rw-r--r-- | src/txdb.h | 2 | ||||
-rw-r--r-- | src/validationinterface.cpp | 4 | ||||
-rw-r--r-- | src/validationinterface.h | 6 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 22 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 57 | ||||
-rw-r--r-- | src/wallet/wallet.h | 5 |
19 files changed, 127 insertions, 52 deletions
diff --git a/contrib/debian/examples/bitcoin.conf b/contrib/debian/examples/bitcoin.conf index 31cca981e0..ade80d60ea 100644 --- a/contrib/debian/examples/bitcoin.conf +++ b/contrib/debian/examples/bitcoin.conf @@ -13,6 +13,12 @@ # Connect via a SOCKS5 proxy #proxy=127.0.0.1:9050 +# Bind to given address and always listen on it. Use [host]:port notation for IPv6 +#bind=<addr> + +# Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 +#whitebind=<addr> + ############################################################## ## Quick Primer on addnode vs connect ## ## Let's say for instance you use addnode=4.2.2.4 ## @@ -57,6 +63,10 @@ # server=1 tells Bitcoin-QT and bitcoind to accept JSON-RPC commands #server=0 +# Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. +# This option can be specified multiple times (default: bind to all interfaces) +#rpcbind=<addr> + # You must set rpcuser and rpcpassword to secure the JSON-RPC api #rpcuser=Ulysseys #rpcpassword=YourSuperGreatPasswordNumber_DO_NOT_USE_THIS_OR_YOU_WILL_GET_ROBBED_385593 @@ -94,6 +104,14 @@ #rpcsslcertificatechainfile=server.cert #rpcsslprivatekeyfile=server.pem +# Transaction Fee Changes in 0.10.0 + +# Send transactions as zero-fee transactions if possible (default: 0) +#sendfreetransactions=0 + +# Create transactions that have enough fees (or priority) so they are likely to begin confirmation within n blocks (default: 1). +# This setting is over-ridden by the -paytxfee option. +#txconfirmtarget=n # Miscellaneous options diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index dc4e0f77bd..01e9fa57b2 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -16,6 +16,7 @@ # h) node0 should now have 2 unspent outputs; send these to node2 via raw tx broadcast by node1 # i) have node1 mine a block # j) check balances - node0 should have 0, node2 should have 100 +# k) test ResendWalletTransactions - create transactions, startup fourth node, make sure it syncs # from test_framework import BitcoinTestFramework @@ -26,7 +27,7 @@ class WalletTest (BitcoinTestFramework): def setup_chain(self): print("Initializing test directory "+self.options.tmpdir) - initialize_chain_clean(self.options.tmpdir, 3) + initialize_chain_clean(self.options.tmpdir, 4) def setup_network(self, split=False): self.nodes = start_nodes(3, self.options.tmpdir) @@ -132,5 +133,23 @@ class WalletTest (BitcoinTestFramework): assert_equal(self.nodes[2].getbalance(), Decimal('59.99800000')) assert_equal(self.nodes[0].getbalance(), Decimal('39.99800000')) + # Test ResendWalletTransactions: + # Create a couple of transactions, then start up a fourth + # node (nodes[3]) and ask nodes[0] to rebroadcast. + # EXPECT: nodes[3] should have those transactions in its mempool. + txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) + txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1) + sync_mempools(self.nodes) + + self.nodes.append(start_node(3, self.options.tmpdir)) + connect_nodes_bi(self.nodes, 0, 3) + sync_blocks(self.nodes) + + relayed = self.nodes[0].resendwallettransactions() + assert_equal(set(relayed), set([txid1, txid2])) + sync_mempools(self.nodes) + + assert(txid1 in self.nodes[3].getrawmempool()) + if __name__ == '__main__': WalletTest ().main () diff --git a/src/main.cpp b/src/main.cpp index 1d78eedc1a..3ceacf32e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4475,7 +4475,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // transactions become unconfirmed and spams other nodes. if (!fReindex && !fImporting && !IsInitialBlockDownload()) { - GetMainSignals().Broadcast(); + GetMainSignals().Broadcast(nTimeBestReceived); } // diff --git a/src/main.h b/src/main.h index ac73b242f1..3ef042895d 100644 --- a/src/main.h +++ b/src/main.h @@ -115,7 +115,6 @@ extern BlockMap mapBlockIndex; extern uint64_t nLastBlockTx; extern uint64_t nLastBlockSize; extern const std::string strMessageMagic; -extern int64_t nTimeBestReceived; extern CWaitableCriticalSection csBestBlock; extern CConditionVariable cvBlockChange; extern bool fImporting; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 58c005ae75..198dd1fdf9 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -859,18 +859,18 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) } #ifdef ENABLE_WALLET -void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address) +void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label) { // On new transaction, make an info balloon + QString msg = tr("Date: %1\n").arg(date) + + tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(unit, amount, true)) + + tr("Type: %1\n").arg(type); + if (!label.isEmpty()) + msg += tr("Label: %1\n").arg(label); + else if (!address.isEmpty()) + msg += tr("Address: %1\n").arg(address); message((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"), - tr("Date: %1\n" - "Amount: %2\n" - "Type: %3\n" - "Address: %4\n") - .arg(date) - .arg(BitcoinUnits::formatWithUnit(unit, amount, true)) - .arg(type) - .arg(address), CClientUIInterface::MSG_INFORMATION); + msg, CClientUIInterface::MSG_INFORMATION); } #endif // ENABLE_WALLET diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 5a289a9046..494541f002 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -165,7 +165,7 @@ public slots: bool handlePaymentRequest(const SendCoinsRecipient& recipient); /** Show incoming transaction notification for new transactions. */ - void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address); + void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label); #endif // ENABLE_WALLET private slots: diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index dfb7a623af..220f273d02 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -115,7 +115,7 @@ PeerTableModel::PeerTableModel(ClientModel *parent) : clientModel(parent), timer(0) { - columns << tr("Address/Hostname") << tr("User Agent") << tr("Ping Time"); + columns << tr("Node/Service") << tr("User Agent") << tr("Ping Time"); priv = new PeerTablePriv(); // default to unsorted priv->sortColumn = -1; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index ccde44fb29..29c971ec79 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -357,7 +357,6 @@ void RPCConsole::clear() ui->messagesWidget->document()->setDefaultStyleSheet( "table { }" "td.time { color: #808080; padding-top: 3px; } " - "td.message { font-family: monospace; font-size: 12px; } " // Todo: Remove fixed font-size "td.cmd-request { color: #006060; } " "td.cmd-error { color: red; } " "b { color: #006060; } " diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index dff2676b10..34464b4075 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -227,7 +227,7 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren priv(new TransactionTablePriv(wallet, this)), fProcessingQueuedTransactions(false) { - columns << QString() << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); + columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); priv->refreshWallet(); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); @@ -626,7 +626,7 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat case Watchonly: return tr("Whether or not a watch-only address is involved in this transaction."); case ToAddress: - return tr("Destination address of transaction."); + return tr("User-defined intent/purpose of the transaction."); case Amount: return tr("Amount removed from or added to balance."); } diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 9d6a060194..956c8b8913 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -93,7 +93,7 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui) connect(this, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int))); // Pass through transaction notifications - connect(this, SIGNAL(incomingTransaction(QString,int,CAmount,QString,QString)), gui, SLOT(incomingTransaction(QString,int,CAmount,QString,QString))); + connect(this, SIGNAL(incomingTransaction(QString,int,CAmount,QString,QString,QString)), gui, SLOT(incomingTransaction(QString,int,CAmount,QString,QString,QString))); } } @@ -149,9 +149,11 @@ void WalletView::processNewTransaction(const QModelIndex& parent, int start, int QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong(); QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString(); - QString address = ttm->index(start, TransactionTableModel::ToAddress, parent).data().toString(); + QModelIndex index = ttm->index(start, 0, parent); + QString address = ttm->data(index, TransactionTableModel::AddressRole).toString(); + QString label = ttm->data(index, TransactionTableModel::LabelRole).toString(); - emit incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address); + emit incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label); } void WalletView::gotoOverviewPage() diff --git a/src/qt/walletview.h b/src/qt/walletview.h index f3d14c065c..1840e21e9c 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -113,7 +113,7 @@ signals: /** Encryption status of wallet changed */ void encryptionStatusChanged(int status); /** Notify that a new transaction appeared */ - void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address); + void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label); }; #endif // BITCOIN_QT_WALLETVIEW_H diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index ba71725222..d30fa32eb4 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -333,6 +333,9 @@ static const CRPCCommand vRPCCommands[] = { "hidden", "invalidateblock", &invalidateblock, true, false }, { "hidden", "reconsiderblock", &reconsiderblock, true, false }, { "hidden", "setmocktime", &setmocktime, true, false }, +#ifdef ENABLE_WALLET + { "hidden", "resendwallettransactions", &resendwallettransactions, true, true }, +#endif #ifdef ENABLE_WALLET /* Wallet */ diff --git a/src/rpcserver.h b/src/rpcserver.h index f63438ecb8..7011d41fc1 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -207,6 +207,7 @@ extern json_spirit::Value getwalletinfo(const json_spirit::Array& params, bool f extern json_spirit::Value getblockchaininfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getnetworkinfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value resendwallettransactions(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); diff --git a/src/txdb.h b/src/txdb.h index 1ce93969d8..86e1c5d831 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -16,7 +16,7 @@ class CBlockFileInfo; class CBlockIndex; -class CDiskTxPos; +struct CDiskTxPos; class uint256; //! -dbcache default (MiB) diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index ae4cd3c592..aa9aefb0de 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -18,13 +18,13 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); - g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); + g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); } void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); - g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); + g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); diff --git a/src/validationinterface.h b/src/validationinterface.h index b21b6e5782..b4c93d72ca 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -9,7 +9,7 @@ #include <boost/signals2/signal.hpp> class CBlock; -class CBlockLocator; +struct CBlockLocator; class CTransaction; class CValidationInterface; class CValidationState; @@ -33,7 +33,7 @@ protected: virtual void SetBestChain(const CBlockLocator &locator) {}; virtual void UpdatedTransaction(const uint256 &hash) {}; virtual void Inventory(const uint256 &hash) {}; - virtual void ResendWalletTransactions() {}; + virtual void ResendWalletTransactions(int64_t nBestBlockTime) {}; virtual void BlockChecked(const CBlock&, const CValidationState&) {}; friend void ::RegisterValidationInterface(CValidationInterface*); friend void ::UnregisterValidationInterface(CValidationInterface*); @@ -52,7 +52,7 @@ struct CMainSignals { /** Notifies listeners about an inventory item being seen on the network. */ boost::signals2::signal<void (const uint256 &)> Inventory; /** Tells listeners to broadcast their data. */ - boost::signals2::signal<void ()> Broadcast; + boost::signals2::signal<void (int64_t nBestBlockTime)> Broadcast; /** Notifies listeners of a block validation result */ boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked; }; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9318c1b2b1..29f3eda15d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2096,3 +2096,25 @@ Value getwalletinfo(const Array& params, bool fHelp) obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); return obj; } + +Value resendwallettransactions(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "resendwallettransactions\n" + "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n" + "Intended only for testing; the wallet code periodically re-broadcasts\n" + "automatically.\n" + "Returns array of transaction ids that were re-broadcast.\n" + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::vector<uint256> txids = pwalletMain->ResendWalletTransactionsBefore(GetTime()); + Array result; + BOOST_FOREACH(const uint256& txid, txids) + { + result.push_back(txid.ToString()); + } + return result; +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 09bcda577e..a10123002e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1114,15 +1114,17 @@ void CWallet::ReacceptWalletTransactions() } } -void CWalletTx::RelayWalletTransaction() +bool CWalletTx::RelayWalletTransaction() { if (!IsCoinBase()) { if (GetDepthInMainChain() == 0) { LogPrintf("Relaying wtx %s\n", GetHash().ToString()); RelayTransaction((CTransaction)*this); + return true; } } + return false; } set<uint256> CWalletTx::GetConflicts() const @@ -1324,7 +1326,31 @@ bool CWalletTx::IsTrusted() const return true; } -void CWallet::ResendWalletTransactions() +std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime) +{ + std::vector<uint256> result; + + LOCK(cs_wallet); + // Sort them in chronological order + multimap<unsigned int, CWalletTx*> mapSorted; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + // Don't rebroadcast if newer than nTime: + if (wtx.nTimeReceived > nTime) + continue; + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + if (wtx.RelayWalletTransaction()) + result.push_back(wtx.GetHash()); + } + return result; +} + +void CWallet::ResendWalletTransactions(int64_t nBestBlockTime) { // Do this infrequently and randomly to avoid giving away // that these are our transactions. @@ -1336,30 +1362,15 @@ void CWallet::ResendWalletTransactions() return; // Only do it if there's been a new block since last time - if (nTimeBestReceived < nLastResend) + if (nBestBlockTime < nLastResend) return; nLastResend = GetTime(); - // Rebroadcast any of our txes that aren't in a block yet - LogPrintf("ResendWalletTransactions()\n"); - { - LOCK(cs_wallet); - // Sort them in chronological order - multimap<unsigned int, CWalletTx*> mapSorted; - BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) - { - CWalletTx& wtx = item.second; - // Don't rebroadcast until it's had plenty of time that - // it should have gotten in already by now. - if (nTimeBestReceived - (int64_t)wtx.nTimeReceived > 5 * 60) - mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); - } - BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) - { - CWalletTx& wtx = *item.second; - wtx.RelayWalletTransaction(); - } - } + // Rebroadcast unconfirmed txes older than 5 minutes before the last + // block was found: + std::vector<uint256> relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60); + if (!relayed.empty()) + LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size()); } /** @} */ // end of mapWallet diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4a13f02195..6ae1c87b1d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -381,7 +381,7 @@ public: int64_t GetTxTime() const; int GetRequestCount() const; - void RelayWalletTransaction(); + bool RelayWalletTransaction(); std::set<uint256> GetConflicts() const; }; @@ -614,7 +614,8 @@ public: void EraseFromWallet(const uint256 &hash); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); - void ResendWalletTransactions(); + void ResendWalletTransactions(int64_t nBestBlockTime); + std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime); CAmount GetBalance() const; CAmount GetUnconfirmedBalance() const; CAmount GetImmatureBalance() const; |