diff --git a/src/bignum.h b/src/bignum.h index abcc052efec..9f75d7c7899 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -122,16 +122,22 @@ public: return (n > std::numeric_limits::max() ? std::numeric_limits::min() : -(int)n); } - void setint64(int64 n) + void setint64(int64 sn) { - unsigned char pch[sizeof(n) + 6]; + unsigned char pch[sizeof(sn) + 6]; unsigned char* p = pch + 4; - bool fNegative = false; - if (n < (int64)0) + bool fNegative; + uint64 n; + + if (sn < (int64)0) { - n = -n; + n = -sn; fNegative = true; + } else { + n = sn; + fNegative = false; } + bool fLeadingZeroes = true; for (int i = 0; i < 8; i++) { diff --git a/src/main.cpp b/src/main.cpp index a4b0a1d8cc5..19510945843 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -51,7 +51,7 @@ map mapOrphanBlocks; multimap mapOrphanBlocksByPrev; map mapOrphanTransactions; -multimap mapOrphanTransactionsByPrev; +map > mapOrphanTransactionsByPrev; // Constant stuff for coinbase transactions we create: CScript COINBASE_FLAGS; @@ -168,17 +168,37 @@ void static ResendWalletTransactions() // mapOrphanTransactions // -void AddOrphanTx(const CDataStream& vMsg) +bool AddOrphanTx(const CDataStream& vMsg) { CTransaction tx; CDataStream(vMsg) >> tx; uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) - return; + return false; - CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg); + CDataStream* pvMsg = new CDataStream(vMsg); + + // Ignore big transactions, to avoid a + // send-big-orphans memory exhaustion attack. If a peer has a legitimate + // large transaction with a missing parent then we assume + // it will rebroadcast it later, after the parent transaction(s) + // have been mined or received. + // 10,000 orphans, each of which is at most 5,000 bytes big is + // at most 500 megabytes of orphans: + if (pvMsg->size() > 5000) + { + printf("ignoring large orphan tx (size: %u, hash: %s)\n", pvMsg->size(), hash.ToString().substr(0,10).c_str()); + delete pvMsg; + return false; + } + + mapOrphanTransactions[hash] = pvMsg; BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); + mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg)); + + printf("stored orphan tx %s (mapsz %u)\n", hash.ToString().substr(0,10).c_str(), + mapOrphanTransactions.size()); + return true; } void static EraseOrphanTx(uint256 hash) @@ -190,14 +210,9 @@ void static EraseOrphanTx(uint256 hash) CDataStream(*pvMsg) >> tx; BOOST_FOREACH(const CTxIn& txin, tx.vin) { - for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(txin.prevout.hash); - mi != mapOrphanTransactionsByPrev.upper_bound(txin.prevout.hash);) - { - if ((*mi).second == pvMsg) - mapOrphanTransactionsByPrev.erase(mi++); - else - mi++; - } + mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); + if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) + mapOrphanTransactionsByPrev.erase(txin.prevout.hash); } delete pvMsg; mapOrphanTransactions.erase(hash); @@ -1138,17 +1153,28 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); + // Check for negative or overflow input values + nValueIn += txPrev.vout[prevout.n].nValue; + if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return DoS(100, error("ConnectInputs() : txin values out of range")); + + } + // The first loop above does all the inexpensive checks. + // Only if ALL inputs pass do we perform expensive ECDSA signature checks. + // Helps prevent CPU exhaustion attacks. + for (unsigned int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + assert(inputs.count(prevout.hash) > 0); + CTxIndex& txindex = inputs[prevout.hash].first; + CTransaction& txPrev = inputs[prevout.hash].second; + // Check for conflicts (double-spend) // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!txindex.vSpent[prevout.n].IsNull()) return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); - // Check for negative or overflow input values - nValueIn += txPrev.vout[prevout.n].nValue; - if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return DoS(100, error("ConnectInputs() : txin values out of range")); - // Skip ECDSA signature verification when connecting blocks (fBlock=true) // before the last blockchain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. @@ -2572,6 +2598,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "tx") { vector vWorkQueue; + vector vEraseQueue; CDataStream vMsg(vRecv); CTransaction tx; vRecv >> tx; @@ -2586,37 +2613,45 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); + vEraseQueue.push_back(inv.hash); // Recursively process any orphan transactions that depended on this one for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; - for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev); - mi != mapOrphanTransactionsByPrev.upper_bound(hashPrev); + for (map::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); + mi != mapOrphanTransactionsByPrev[hashPrev].end(); ++mi) { const CDataStream& vMsg = *((*mi).second); CTransaction tx; CDataStream(vMsg) >> tx; CInv inv(MSG_TX, tx.GetHash()); + bool fMissingInputs2 = false; - if (tx.AcceptToMemoryPool(true)) + if (tx.AcceptToMemoryPool(true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); + vEraseQueue.push_back(inv.hash); + } + else if (!fMissingInputs2) + { + // invalid orphan + vEraseQueue.push_back(inv.hash); + printf(" removed invalid orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); } } } - BOOST_FOREACH(uint256 hash, vWorkQueue) + BOOST_FOREACH(uint256 hash, vEraseQueue) EraseOrphanTx(hash); } else if (fMissingInputs) { - printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); AddOrphanTx(vMsg); // DoS prevention: do not allow mapOrphanTransactions to grow unbounded diff --git a/src/main.h b/src/main.h index 48877f976ad..fd248186474 100644 --- a/src/main.h +++ b/src/main.h @@ -26,7 +26,7 @@ class CInv; class CRequestTracker; class CNode; -static const int CLIENT_VERSION = 60008; +static const int CLIENT_VERSION = 60009; static const bool VERSION_IS_BETA = true; extern const std::string CLIENT_NAME; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 5b5a8f5271e..2c3d8a49e3a 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -143,7 +143,7 @@ void OverviewPage::setNumTransactions(int count) void OverviewPage::setModel(WalletModel *model) { this->model = model; - if(model) + if(model && model->getOptionsModel()) { // Set up transaction list TransactionFilterProxy *filter = new TransactionFilterProxy(); @@ -163,17 +163,23 @@ void OverviewPage::setModel(WalletModel *model) setNumTransactions(model->getNumTransactions()); connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); - connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(displayUnitChanged())); + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + } + + // update the display unit, to not use the default ("BTC") + updateDisplayUnit(); +} + +void OverviewPage::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + if(currentBalance != -1) + setBalance(currentBalance, currentUnconfirmedBalance); + + // Update txdelegate->unit with the current unit + txdelegate->unit = model->getOptionsModel()->getDisplayUnit(); + + ui->listTransactions->update(); } } - -void OverviewPage::displayUnitChanged() -{ - if(!model || !model->getOptionsModel()) - return; - if(currentBalance != -1) - setBalance(currentBalance, currentUnconfirmedBalance); - - txdelegate->unit = model->getOptionsModel()->getDisplayUnit(); - ui->listTransactions->update(); -} diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 11992271688..99fe4864948 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -40,7 +40,7 @@ private: TxViewDelegate *txdelegate; private slots: - void displayUnitChanged(); + void updateDisplayUnit(); }; #endif // OVERVIEWPAGE_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index b4029aa0d22..33a18e4d662 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -46,10 +46,11 @@ void SendCoinsDialog::setModel(WalletModel *model) entry->setModel(model); } } - if(model) + if(model && model->getOptionsModel()) { setBalance(model->getBalance(), model->getUnconfirmedBalance()); connect(model, SIGNAL(balanceChanged(qint64, qint64)), this, SLOT(setBalance(qint64, qint64))); + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); } } @@ -202,7 +203,7 @@ SendCoinsEntry *SendCoinsDialog::addEntry() ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint()); QCoreApplication::instance()->processEvents(); QScrollBar* bar = ui->scrollArea->verticalScrollBar(); - if (bar) + if(bar) bar->setSliderPosition(bar->maximum()); return entry; } @@ -286,3 +287,12 @@ void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance) int unit = model->getOptionsModel()->getDisplayUnit(); ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); } + +void SendCoinsDialog::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + // Update labelBalance with the current balance and the current unit + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance())); + } +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 5dcbfbeb619..2498a9a71ec 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -47,8 +47,8 @@ private: private slots: void on_sendButton_clicked(); - void removeEntry(SendCoinsEntry* entry); + void updateDisplayUnit(); }; #endif // SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index c8242d8352f..599a804c46c 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -68,6 +68,10 @@ void SendCoinsEntry::on_payTo_textChanged(const QString &address) void SendCoinsEntry::setModel(WalletModel *model) { this->model = model; + + if(model && model->getOptionsModel()) + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + clear(); } @@ -82,10 +86,8 @@ void SendCoinsEntry::clear() ui->addAsLabel->clear(); ui->payAmount->clear(); ui->payTo->setFocus(); - if(model && model->getOptionsModel()) - { - ui->payAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); - } + // update the display unit, to not use the default ("BTC") + updateDisplayUnit(); } void SendCoinsEntry::on_deleteButton_clicked() @@ -160,3 +162,11 @@ void SendCoinsEntry::setFocus() ui->payTo->setFocus(); } +void SendCoinsEntry::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + // Update payAmount with the current unit + ui->payAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); + } +} diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index cdbf8932646..db6cba0d80c 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -45,6 +45,7 @@ private slots: void on_payTo_textChanged(const QString &address); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); + void updateDisplayUnit(); private: Ui::SendCoinsEntry *ui; diff --git a/src/util.h b/src/util.h index e0821cce19b..9910c338af8 100644 --- a/src/util.h +++ b/src/util.h @@ -354,7 +354,7 @@ inline int64 GetPerformanceCounter() #else timeval t; gettimeofday(&t, NULL); - nCounter = t.tv_sec * 1000000 + t.tv_usec; + nCounter = (int64) t.tv_sec * 1000000 + t.tv_usec; #endif return nCounter; }