diff options
Diffstat (limited to 'src')
30 files changed, 532 insertions, 211 deletions
diff --git a/src/init.cpp b/src/init.cpp index 91d0964365..a2c07c9f66 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -270,10 +270,12 @@ std::string HelpMessage(HelpMessageMode hmm) strUsage += " -disablewallet " + _("Do not load the wallet and disable wallet RPC calls") + "\n"; strUsage += " -paytxfee=<amt> " + _("Fee per kB to add to transactions you send") + "\n"; strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n"; + strUsage += " -zapwallettxes " + _("Clear list of wallet transactions (diagnostic tool; implies -rescan)") + "\n"; strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n"; strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n"; strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n"; strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; + strUsage += " -spendzeroconfchange " + _("Spend unconfirmed change when sending transactions (default: 1)") + "\n"; #endif strUsage += "\n" + _("Block creation options:") + "\n"; strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; @@ -453,6 +455,12 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf("AppInit2 : parameter interaction: -salvagewallet=1 -> setting -rescan=1\n"); } + // -zapwallettx implies a rescan + if (GetBoolArg("-zapwallettxes", false)) { + if (SoftSetBoolArg("-rescan", true)) + LogPrintf("AppInit2 : parameter interaction: -zapwallettxes=1 -> setting -rescan=1\n"); + } + // Make sure enough file descriptors are available int nBind = std::max((int)mapArgs.count("-bind"), 1); nMaxConnections = GetArg("-maxconnections", 125); @@ -539,6 +547,7 @@ bool AppInit2(boost::thread_group& threadGroup) if (nTransactionFee > 0.25 * COIN) InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); } + bSpendZeroConfChange = GetArg("-spendzeroconfchange", true); strWalletFile = GetArg("-wallet", "wallet.dat"); #endif @@ -897,6 +906,20 @@ bool AppInit2(boost::thread_group& threadGroup) pwalletMain = NULL; LogPrintf("Wallet disabled!\n"); } else { + if (GetBoolArg("-zapwallettxes", false)) { + uiInterface.InitMessage(_("Zapping all transactions from wallet...")); + + pwalletMain = new CWallet(strWalletFile); + DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(); + if (nZapWalletRet != DB_LOAD_OK) { + uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted")); + return false; + } + + delete pwalletMain; + pwalletMain = NULL; + } + uiInterface.InitMessage(_("Loading wallet...")); nStart = GetTimeMillis(); diff --git a/src/main.cpp b/src/main.cpp index 0509674cf8..f61b282865 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -442,6 +442,10 @@ bool IsStandardTx(const CTransaction& tx, string& reason) reason = "scriptsig-not-pushonly"; return false; } + if (!txin.scriptSig.HasCanonicalPushes()) { + reason = "non-canonical-push"; + return false; + } } unsigned int nDataOut = 0; @@ -868,7 +872,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } -int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const +int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const { if (hashBlock == 0 || nIndex == -1) return 0; @@ -893,6 +897,14 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const return chainActive.Height() - pindex->nHeight + 1; } +int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const +{ + int nResult = GetDepthInMainChainINTERNAL(pindexRet); + if (nResult == 0 && !mempool.exists(GetHash())) + return -1; // Not in chain, not in mempool + + return nResult; +} int CMerkleTx::GetBlocksToMaturity() const { diff --git a/src/main.h b/src/main.h index c9e72f05f7..42b96abb37 100644 --- a/src/main.h +++ b/src/main.h @@ -423,6 +423,8 @@ public: /** A transaction with a merkle branch linking it to the block chain. */ class CMerkleTx : public CTransaction { +private: + int GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const; public: uint256 hashBlock; std::vector<uint256> vMerkleBranch; @@ -461,9 +463,14 @@ public: int SetMerkleBranch(const CBlock* pblock=NULL); + + // Return depth of transaction in blockchain: + // -1 : not in blockchain, and not in memory pool (conflicted transaction) + // 0 : in memory pool, waiting to be included in a block + // >=1 : this many blocks deep in the main chain int GetDepthInMainChain(CBlockIndex* &pindexRet) const; int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } - bool IsInMainChain() const { return GetDepthInMainChain() > 0; } + bool IsInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; } int GetBlocksToMaturity() const; bool AcceptToMemoryPool(bool fLimitFree=true); }; diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index 7de47291cc..030804db6e 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -243,6 +243,7 @@ RES_ICONS = \ res/icons/toolbar_testnet.png \ res/icons/transaction0.png \ res/icons/transaction2.png \ + res/icons/transaction_conflicted.png \ res/icons/tx_inout.png \ res/icons/tx_input.png \ res/icons/tx_output.png \ diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index ae510114f0..162009f5de 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -376,9 +376,6 @@ void BitcoinApplication::initializeResult(int retval) returnValue = retval ? 0 : 1; if(retval) { - // Miscellaneous initialization after core is initialized - optionsModel->Upgrade(); // Must be done after AppInit2 - #ifdef ENABLE_WALLET PaymentServer::LoadRootCAs(); paymentServer->setOptionsModel(optionsModel); diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 746b5a4092..7c3a7756b7 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -12,6 +12,7 @@ <file alias="connect_4">res/icons/connect4_16.png</file> <file alias="transaction_0">res/icons/transaction0.png</file> <file alias="transaction_confirmed">res/icons/transaction2.png</file> + <file alias="transaction_conflicted">res/icons/transaction_conflicted.png</file> <file alias="transaction_1">res/icons/clock1.png</file> <file alias="transaction_2">res/icons/clock2.png</file> <file alias="transaction_3">res/icons/clock3.png</file> diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index e0d99aac39..b4a9f1f580 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -28,52 +28,6 @@ </attribute> <layout class="QVBoxLayout" name="verticalLayout_Main"> <item> - <widget class="QLabel" name="transactionFeeInfoLabel"> - <property name="text"> - <string>Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB.</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_1_Main"> - <item> - <widget class="QLabel" name="transactionFeeLabel"> - <property name="text"> - <string>Pay transaction &fee</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="buddy"> - <cstring>transactionFee</cstring> - </property> - </widget> - </item> - <item> - <widget class="BitcoinAmountField" name="transactionFee"/> - </item> - <item> - <spacer name="horizontalSpacer_1_Main"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> <widget class="QCheckBox" name="bitcoinAtStartup"> <property name="toolTip"> <string>Automatically start Bitcoin after logging in to the system.</string> @@ -194,6 +148,89 @@ </item> </layout> </widget> + <widget class="QWidget" name="tabWallet"> + <attribute name="title"> + <string>W&allet</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_Network"> + <item> + <widget class="QLabel" name="transactionFeeInfoLabel"> + <property name="text"> + <string>Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB.</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_1_Main"> + <item> + <widget class="QLabel" name="transactionFeeLabel"> + <property name="text"> + <string>Pay transaction &fee</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="buddy"> + <cstring>transactionFee</cstring> + </property> + </widget> + </item> + <item> + <widget class="BitcoinAmountField" name="transactionFee"/> + </item> + <item> + <spacer name="horizontalSpacer_1_Main"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="spendZeroConfChange"> + <property name="text"> + <string>Spend unconfirmed change (experts only)</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> <widget class="QWidget" name="tabNetwork"> <attribute name="title"> <string>&Network</string> diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index f61bb3ed2c..317e1db7d3 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -148,11 +148,14 @@ void OptionsDialog::setModel(OptionsModel *model) void OptionsDialog::setMapper() { /* Main */ - mapper->addMapping(ui->transactionFee, OptionsModel::Fee); mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup); mapper->addMapping(ui->threadsScriptVerif, OptionsModel::ThreadsScriptVerif); mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache); + /* Wallet */ + mapper->addMapping(ui->transactionFee, OptionsModel::Fee); + mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange); + /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 5b4a54d54d..eff73b7702 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -76,6 +76,11 @@ void OptionsModel::Init() nTransactionFee = settings.value("nTransactionFee").toLongLong(); // if -paytxfee is set, this will be overridden later in init.cpp if (mapArgs.count("-paytxfee")) strOverriddenByCommandLine += "-paytxfee "; + + if (!settings.contains("bSpendZeroConfChange")) + settings.setValue("bSpendZeroConfChange", true); + if (!SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) + strOverriddenByCommandLine += "-spendzeroconfchange "; #endif if (!settings.contains("nDatabaseCache")) @@ -130,69 +135,6 @@ void OptionsModel::Reset() // default setting for OptionsModel::StartAtStartup - disabled if (GUIUtil::GetStartOnSystemStartup()) GUIUtil::SetStartOnSystemStartup(false); - - // Ensure Upgrade() is not running again by setting the bImportFinished flag - settings.setValue("bImportFinished", true); -} - -void OptionsModel::Upgrade() -{ - QSettings settings; - - // Already upgraded - if (settings.contains("bImportFinished")) - return; - - settings.setValue("bImportFinished", true); - -#ifdef ENABLE_WALLET - // Move settings from old wallet.dat (if any): - CWalletDB walletdb(strWalletFile); - - QList<QString> intOptions; - intOptions << "nDisplayUnit" << "nTransactionFee"; - foreach(QString key, intOptions) - { - int value = 0; - if (walletdb.ReadSetting(key.toStdString(), value)) - { - settings.setValue(key, value); - walletdb.EraseSetting(key.toStdString()); - } - } - QList<QString> boolOptions; - boolOptions << "bDisplayAddresses" << "fMinimizeToTray" << "fMinimizeOnClose" << "fUseProxy" << "fUseUPnP"; - foreach(QString key, boolOptions) - { - bool value = false; - if (walletdb.ReadSetting(key.toStdString(), value)) - { - settings.setValue(key, value); - walletdb.EraseSetting(key.toStdString()); - } - } - try - { - CAddress addrProxyAddress; - if (walletdb.ReadSetting("addrProxy", addrProxyAddress)) - { - settings.setValue("addrProxy", addrProxyAddress.ToStringIPPort().c_str()); - walletdb.EraseSetting("addrProxy"); - } - } - catch (std::ios_base::failure &e) - { - // 0.6.0rc1 saved this as a CService, which causes failure when parsing as a CAddress - CService addrProxy; - if (walletdb.ReadSetting("addrProxy", addrProxy)) - { - settings.setValue("addrProxy", addrProxy.ToStringIPPort().c_str()); - walletdb.EraseSetting("addrProxy"); - } - } -#endif - - Init(); } int OptionsModel::rowCount(const QModelIndex & parent) const @@ -247,6 +189,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const // Todo: Consider to revert back to use just nTransactionFee here, if we don't want // -paytxfee to update our QSettings! return settings.value("nTransactionFee"); + case SpendZeroConfChange: + return settings.value("bSpendZeroConfChange"); #endif case DisplayUnit: return nDisplayUnit; @@ -337,6 +281,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in settings.setValue("nTransactionFee", (qint64)nTransactionFee); emit transactionFeeChanged(nTransactionFee); break; + case SpendZeroConfChange: + if (settings.value("bSpendZeroConfChange") != value) { + settings.setValue("bSpendZeroConfChange", value); + setRestartRequired(true); + } + break; #endif case DisplayUnit: nDisplayUnit = value.toInt(); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 7a71b772b2..a3487ddd2e 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -40,15 +40,13 @@ public: CoinControlFeatures, // bool ThreadsScriptVerif, // int DatabaseCache, // int + SpendZeroConfChange, // bool OptionIDRowCount, }; void Init(); void Reset(); - /* Migrate settings from wallet.dat after app initialization */ - void Upgrade(); - int rowCount(const QModelIndex & parent = QModelIndex()) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 016097c5a0..1a9d1de571 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -175,6 +175,7 @@ void OverviewPage::setWalletModel(WalletModel *model) filter->setLimit(NUM_ITEMS); filter->setDynamicSortFilter(true); filter->setSortRole(Qt::EditRole); + filter->setShowInactive(false); filter->sort(TransactionTableModel::Status, Qt::DescendingOrder); ui->listTransactions->setModel(filter); diff --git a/src/qt/res/icons/transaction_conflicted.png b/src/qt/res/icons/transaction_conflicted.png Binary files differnew file mode 100644 index 0000000000..51fff649ab --- /dev/null +++ b/src/qt/res/icons/transaction_conflicted.png diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index c33733604a..4aebaa1e7f 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -30,7 +30,9 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) else { int nDepth = wtx.GetDepthInMainChain(); - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + if (nDepth < 0) + return tr("conflicted"); + else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) return tr("%1/offline").arg(nDepth); else if (nDepth < 6) return tr("%1/unconfirmed").arg(nDepth); diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index a14e74a469..f9546fddb5 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -5,6 +5,7 @@ #include "transactionfilterproxy.h" #include "transactiontablemodel.h" +#include "transactionrecord.h" #include <cstdlib> @@ -22,7 +23,8 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : addrPrefix(), typeFilter(ALL_TYPES), minAmount(0), - limitRows(-1) + limitRows(-1), + showInactive(true) { } @@ -35,7 +37,10 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & QString address = index.data(TransactionTableModel::AddressRole).toString(); QString label = index.data(TransactionTableModel::LabelRole).toString(); qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); + int status = index.data(TransactionTableModel::StatusRole).toInt(); + if(!showInactive && status == TransactionStatus::Conflicted) + return false; if(!(TYPE(type) & typeFilter)) return false; if(datetime < dateFrom || datetime > dateTo) @@ -78,6 +83,12 @@ void TransactionFilterProxy::setLimit(int limit) this->limitRows = limit; } +void TransactionFilterProxy::setShowInactive(bool showInactive) +{ + this->showInactive = showInactive; + invalidateFilter(); +} + int TransactionFilterProxy::rowCount(const QModelIndex &parent) const { if(limitRows != -1) diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index 6d1644d48d..9919bc3fd6 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -36,6 +36,9 @@ public: /** Set maximum number of rows returned, -1 if unlimited. */ void setLimit(int limit); + /** Set whether to show conflicted transactions. */ + void setShowInactive(bool showInactive); + int rowCount(const QModelIndex &parent = QModelIndex()) const; protected: @@ -48,6 +51,7 @@ private: quint32 typeFilter; qint64 minAmount; int limitRows; + bool showInactive; }; #endif // TRANSACTIONFILTERPROXY_H diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index ceb8189b8c..8cfaed27c7 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -164,7 +164,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) (wtx.IsCoinBase() ? 1 : 0), wtx.nTimeReceived, idx); - status.confirmed = wtx.IsConfirmed(); + status.confirmed = wtx.IsTrusted(); status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = chainActive.Height(); @@ -183,7 +183,11 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) } else { - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + if (status.depth < 0) + { + status.status = TransactionStatus::Conflicted; + } + else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) { status.status = TransactionStatus::Offline; } diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 8a7c9044e3..d7be0bc438 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -36,7 +36,8 @@ public: OpenUntilBlock, Offline, Unconfirmed, - HaveConfirmations + HaveConfirmations, + Conflicted }; bool confirmed; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index ca60c18f60..7d76204ba4 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -312,7 +312,7 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for)); break; case TransactionStatus::Offline: - status = tr("Offline (%1 confirmations)").arg(wtx->status.depth); + status = tr("Offline"); break; case TransactionStatus::Unconfirmed: status = tr("Unconfirmed (%1 of %2 confirmations)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations); @@ -320,6 +320,9 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons case TransactionStatus::HaveConfirmations: status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth); break; + case TransactionStatus::Conflicted: + status = tr("Conflicted"); + break; } } @@ -471,7 +474,6 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) case TransactionStatus::OpenUntilBlock: case TransactionStatus::OpenUntilDate: return QColor(64,64,255); - break; case TransactionStatus::Offline: return QColor(192,192,192); case TransactionStatus::Unconfirmed: @@ -486,6 +488,8 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) }; case TransactionStatus::HaveConfirmations: return QIcon(":/icons/transaction_confirmed"); + case TransactionStatus::Conflicted: + return QIcon(":/icons/transaction_conflicted"); } } return QColor(0,0,0); @@ -587,6 +591,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const rec->status.maturity != TransactionStatus::Mature); case FormattedAmountRole: return formatTxAmount(rec, false); + case StatusRole: + return rec->status.status; } return QVariant(); } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index c23c606c31..7b9cf09cbe 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -53,7 +53,9 @@ public: /** Is transaction confirmed? */ ConfirmedRole, /** Formatted amount, without brackets when unconfirmed */ - FormattedAmountRole + FormattedAmountRole, + /** Transaction status (TransactionRecord::Status) */ + StatusRole }; int rowCount(const QModelIndex &parent) const; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index c2ac3f7abe..3549cd49f0 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -494,7 +494,9 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect BOOST_FOREACH(const COutPoint& outpoint, vOutpoints) { if (!wallet->mapWallet.count(outpoint.hash)) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain()); + int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); + if (nDepth < 0) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth); vOutputs.push_back(out); } } @@ -513,7 +515,9 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins) { if (!wallet->mapWallet.count(outpoint.hash)) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain()); + int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); + if (nDepth < 0) continue; + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth); vCoins.push_back(out); } diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 661deffb19..78a92ce1e9 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -410,8 +410,8 @@ Value verifychain(const Array& params, bool fHelp) "verifychain ( checklevel numblocks )\n" "\nVerifies blockchain database.\n" "\nArguments:\n" - "1. checklevel (numeric, optional, default=3) The level\n" - "2. numblocks (numeric, optional, 288) The number of blocks\n" + "1. checklevel (numeric, optional, 0-4, default=3) How thorough the block verification is.\n" + "2. numblocks (numeric, optional, default=288, 0=all) The number of blocks to check.\n" "\nResult:\n" "true|false (boolean) Verified or not\n" "\nExamples:\n" diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 3d84f41ba0..b764349338 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -47,7 +47,7 @@ Value ping(const Array& params, bool fHelp) "ping\n" "\nRequests that a ping be sent to all other nodes, to measure ping time.\n" "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" - "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping." + "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n" "\nExamples:\n" + HelpExampleCli("ping", "") + HelpExampleRpc("ping", "") diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 730b439135..c9152d7759 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -45,13 +45,18 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) entry.push_back(Pair("confirmations", confirms)); if (wtx.IsCoinBase()) entry.push_back(Pair("generated", true)); - if (confirms) + if (confirms > 0) { entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); entry.push_back(Pair("blockindex", wtx.nIndex)); entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime))); } - entry.push_back(Pair("txid", wtx.GetHash().GetHex())); + uint256 hash = wtx.GetHash(); + entry.push_back(Pair("txid", hash.GetHex())); + Array conflicts; + BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) + conflicts.push_back(conflict.GetHex()); + entry.push_back(Pair("walletconflicts", conflicts)); entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived)); BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) @@ -621,7 +626,7 @@ Value getbalance(const Array& params, bool fHelp) for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!wtx.IsConfirmed()) + if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0) continue; int64_t allFee; @@ -1109,7 +1114,10 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe Object entry; entry.push_back(Pair("account", strSentAccount)); MaybePushAddress(entry, s.first); - entry.push_back(Pair("category", "send")); + if (wtx.GetDepthInMainChain() < 0) + entry.push_back(Pair("category", "conflicted")); + else + entry.push_back(Pair("category", "send")); entry.push_back(Pair("amount", ValueFromAmount(-s.second))); entry.push_back(Pair("fee", ValueFromAmount(-nFee))); if (fLong) @@ -1141,7 +1149,12 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe entry.push_back(Pair("category", "generate")); } else - entry.push_back(Pair("category", "receive")); + { + if (wtx.GetDepthInMainChain() < 0) + entry.push_back(Pair("category", "conflicted")); + else + entry.push_back(Pair("category", "receive")); + } entry.push_back(Pair("amount", ValueFromAmount(r.second))); if (fLong) WalletTxToJSON(wtx, entry); @@ -1317,6 +1330,8 @@ Value listaccounts(const Array& params, bool fHelp) string strSentAccount; list<pair<CTxDestination, int64_t> > listReceived; list<pair<CTxDestination, int64_t> > listSent; + if (wtx.GetBlocksToMaturity() > 0) + continue; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); mapAccountBalances[strSentAccount] -= nFee; BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) @@ -1448,7 +1463,8 @@ Value gettransaction(const Array& params, bool fHelp) " \"amount\" : x.xxx (numeric) The amount in btc\n" " }\n" " ,...\n" - " ]\n" + " ],\n" + " \"hex\" : \"data\" (string) Raw data for transaction\n" "}\n" "\nbExamples\n" @@ -1479,6 +1495,11 @@ Value gettransaction(const Array& params, bool fHelp) ListTransactions(wtx, "*", 0, false, details); entry.push_back(Pair("details", details)); + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << wtx; + string strHex = HexStr(ssTx.begin(), ssTx.end()); + entry.push_back(Pair("hex", strHex)); + return entry; } @@ -1850,9 +1871,9 @@ Value settxfee(const Array& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 1) throw runtime_error( "settxfee amount\n" - "\nSet the transaction fee per KB.\n" + "\nSet the transaction fee per kB.\n" "\nArguments:\n" - "1. amount (numeric, required) The transaction fee in BTC/KB rounded to the nearest 0.00000001\n" + "1. amount (numeric, required) The transaction fee in BTC/kB rounded to the nearest 0.00000001\n" "\nResult\n" "true|false (boolean) Returns true if successful\n" "\nExamples:\n" diff --git a/src/script.cpp b/src/script.cpp index 2b66bc73d6..83fc91956c 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1863,6 +1863,51 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } +bool CScript::IsPushOnly() const +{ + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + return false; + // Note that IsPushOnly() *does* consider OP_RESERVED to be a + // push-type opcode, however execution of OP_RESERVED fails, so + // it's not relevant to P2SH as the scriptSig would fail prior to + // the P2SH special validation code being executed. + if (opcode > OP_16) + return false; + } + return true; +} + +bool CScript::HasCanonicalPushes() const +{ + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + std::vector<unsigned char> data; + if (!GetOp(pc, opcode, data)) + return false; + if (opcode > OP_16) + continue; + if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16)) + // Could have used an OP_n code, rather than a 1-byte push. + return false; + if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1) + // Could have used a normal n-byte push, rather than OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF) + // Could have used an OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF) + // Could have used an OP_PUSHDATA2. + return false; + } + return true; +} + class CScriptVisitor : public boost::static_visitor<bool> { private: diff --git a/src/script.h b/src/script.h index bd120cc07d..335ddfb1b2 100644 --- a/src/script.h +++ b/src/script.h @@ -541,24 +541,11 @@ public: bool IsPayToScriptHash() const; - // Called by IsStandardTx - bool IsPushOnly() const - { - const_iterator pc = begin(); - while (pc < end()) - { - opcodetype opcode; - if (!GetOp(pc, opcode)) - return false; - // Note that IsPushOnly() *does* consider OP_RESERVED to be a - // push-type opcode, however execution of OP_RESERVED fails, so - // it's not relevant to P2SH as the scriptSig would fail prior to - // the P2SH special validation code being executed. - if (opcode > OP_16) - return false; - } - return true; - } + // Called by IsStandardTx and P2SH VerifyScript (which makes it consensus-critical). + bool IsPushOnly() const; + + // Called by IsStandardTx. + bool HasCanonicalPushes() const; // Returns whether the script is guaranteed to fail at execution, // regardless of the initial stack. This allows outputs to be pruned diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index dee0f110ad..dd1b613047 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -438,4 +438,22 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) BOOST_CHECK(combined == partial3c); } +BOOST_AUTO_TEST_CASE(script_standard_push) +{ + for (int i=0; i<1000; i++) { + CScript script; + script << i; + BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Number " << i << " is not pure push."); + BOOST_CHECK_MESSAGE(script.HasCanonicalPushes(), "Number " << i << " push is not canonical."); + } + + for (int i=0; i<1000; i++) { + std::vector<unsigned char> data(i, '\111'); + CScript script; + script << data; + BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Length " << i << " is not pure push."); + BOOST_CHECK_MESSAGE(script.HasCanonicalPushes(), "Length " << i << " push is not canonical."); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet.cpp b/src/wallet.cpp index ac8001f72b..823c96949f 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -18,6 +18,7 @@ using namespace std; // Settings int64_t nTransactionFee = 0; +bool bSpendZeroConfChange = true; ////////////////////////////////////////////////////////////////////////////// // @@ -191,18 +192,6 @@ void CWallet::SetBestChain(const CBlockLocator& loc) walletdb.WriteBestBlock(loc); } -// This class implements an addrIncoming entry that causes pre-0.4 -// clients to crash on startup if reading a private-key-encrypted wallet. -class CCorruptAddress -{ -public: - IMPLEMENT_SERIALIZE - ( - if (nType & SER_DISK) - READWRITE(nVersion); - ) -}; - bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) { AssertLockHeld(cs_wallet); // nWalletVersion @@ -221,13 +210,6 @@ bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, if (fFileBacked) { CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); - if (nWalletVersion >= 40000) - { - // Versions prior to 0.4.0 did not support the "minversion" record. - // Use a CCorruptAddress to make them crash instead. - CCorruptAddress corruptAddress; - pwalletdb->WriteSetting("addrIncoming", corruptAddress); - } if (nWalletVersion > 40000) pwalletdb->WriteMinVersion(nWalletVersion); if (!pwalletdbIn) @@ -249,6 +231,82 @@ bool CWallet::SetMaxVersion(int nVersion) return true; } +set<uint256> CWallet::GetConflicts(const uint256& txid) const +{ + set<uint256> result; + AssertLockHeld(cs_wallet); + + std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid); + if (it == mapWallet.end()) + return result; + const CWalletTx& wtx = it->second; + + std::pair<TxConflicts::const_iterator, TxConflicts::const_iterator> range; + + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + range = mapTxConflicts.equal_range(txin.prevout); + for (TxConflicts::const_iterator it = range.first; it != range.second; ++it) + result.insert(it->second); + } + return result; +} + +void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> range) +{ + // We want all the wallet transactions in range to have the same metadata as + // the oldest (smallest nOrderPos). + // So: find smallest nOrderPos: + + int nMinOrderPos = std::numeric_limits<int>::max(); + const CWalletTx* copyFrom = NULL; + for (TxConflicts::iterator it = range.first; it != range.second; ++it) + { + const uint256& hash = it->second; + int n = mapWallet[hash].nOrderPos; + if (n < nMinOrderPos) + { + nMinOrderPos = n; + copyFrom = &mapWallet[hash]; + } + } + // Now copy data from copyFrom to rest: + for (TxConflicts::iterator it = range.first; it != range.second; ++it) + { + const uint256& hash = it->second; + CWalletTx* copyTo = &mapWallet[hash]; + if (copyFrom == copyTo) continue; + copyTo->mapValue = copyFrom->mapValue; + copyTo->vOrderForm = copyFrom->vOrderForm; + // fTimeReceivedIsTxTime not copied on purpose + // nTimeReceived not copied on purpose + copyTo->nTimeSmart = copyFrom->nTimeSmart; + copyTo->fFromMe = copyFrom->fFromMe; + copyTo->strFromAccount = copyFrom->strFromAccount; + // vfSpent not copied on purpose + // nOrderPos not copied on purpose + // cached members not copied on purpose + } +} + +void CWallet::AddToConflicts(const uint256& wtxhash) +{ + assert(mapWallet.count(wtxhash)); + CWalletTx& thisTx = mapWallet[wtxhash]; + if (thisTx.IsCoinBase()) + return; + + BOOST_FOREACH(const CTxIn& txin, thisTx.vin) + { + mapTxConflicts.insert(make_pair(txin.prevout, wtxhash)); + + pair<TxConflicts::iterator, TxConflicts::iterator> range; + range = mapTxConflicts.equal_range(txin.prevout); + if (range.first != range.second) + SyncMetaData(range); + } +} + bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) @@ -403,9 +461,16 @@ void CWallet::MarkDirty() } } -bool CWallet::AddToWallet(const CWalletTx& wtxIn) +bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) { uint256 hash = wtxIn.GetHash(); + + if (fFromLoadWallet) + { + mapWallet[hash] = wtxIn; + AddToConflicts(hash); + } + else { LOCK(cs_wallet); // Inserts only if not already there, returns tx inserted or tx found @@ -463,6 +528,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) wtxIn.GetHash().ToString(), wtxIn.hashBlock.ToString()); } + AddToConflicts(hash); } bool fUpdated = false; @@ -925,6 +991,18 @@ void CWalletTx::RelayWalletTransaction() } } +set<uint256> CWalletTx::GetConflicts() const +{ + set<uint256> result; + if (pwallet != NULL) + { + uint256 myHash = GetHash(); + result = pwallet->GetConflicts(myHash); + result.erase(myHash); + } + return result; +} + void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away @@ -982,7 +1060,7 @@ int64_t CWallet::GetBalance() const for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (pcoin->IsConfirmed()) + if (pcoin->IsTrusted()) nTotal += pcoin->GetAvailableCredit(); } } @@ -998,7 +1076,7 @@ int64_t CWallet::GetUnconfirmedBalance() const for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!IsFinalTx(*pcoin) || !pcoin->IsConfirmed()) + if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) nTotal += pcoin->GetAvailableCredit(); } } @@ -1033,17 +1111,21 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const if (!IsFinalTx(*pcoin)) continue; - if (fOnlyConfirmed && !pcoin->IsConfirmed()) + if (fOnlyConfirmed && !pcoin->IsTrusted()) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) continue; + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 && (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) - vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); + vCoins.push_back(COutput(pcoin, i, nDepth)); } } } @@ -1211,7 +1293,7 @@ bool CWallet::SelectCoins(int64_t nTargetValue, set<pair<const CWalletTx*,unsign return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)); + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet))); } @@ -1515,6 +1597,30 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) } +DBErrors CWallet::ZapWalletTx() +{ + if (!fFileBacked) + return DB_LOAD_OK; + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this); + if (nZapWalletTxRet == DB_NEED_REWRITE) + { + if (CDB::Rewrite(strWalletFile, "\x04pool")) + { + LOCK(cs_wallet); + setKeyPool.clear(); + // Note: can't top-up keypool here, because wallet is locked. + // User will be prompted to unlock wallet the next operation + // the requires a new key. + } + } + + if (nZapWalletTxRet != DB_LOAD_OK) + return nZapWalletTxRet; + + return DB_LOAD_OK; +} + + bool CWallet::SetAddressBook(const CTxDestination& address, const string& strName, const string& strPurpose) { AssertLockHeld(cs_wallet); // mapAddressBook @@ -1728,7 +1834,7 @@ std::map<CTxDestination, int64_t> CWallet::GetAddressBalances() { CWalletTx *pcoin = &walletEntry.second; - if (!IsFinalTx(*pcoin) || !pcoin->IsConfirmed()) + if (!IsFinalTx(*pcoin) || !pcoin->IsTrusted()) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) diff --git a/src/wallet.h b/src/wallet.h index dc8c007ac8..95fb0ee9de 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -25,6 +25,7 @@ // Settings extern int64_t nTransactionFee; +extern bool bSpendZeroConfChange; class CAccountingEntry; class CCoinControl; @@ -107,6 +108,12 @@ private: int64_t nNextResend; int64_t nLastResend; + // Used to detect and report conflicted transactions: + typedef std::multimap<COutPoint, uint256> TxConflicts; + TxConflicts mapTxConflicts; + void AddToConflicts(const uint256& wtxhash); + void SyncMetaData(std::pair<TxConflicts::iterator, TxConflicts::iterator>); + public: /// Main wallet lock. /// This lock protects all the fields added by CWallet @@ -150,6 +157,7 @@ public: } std::map<uint256, CWalletTx> mapWallet; + int64_t nOrderPosNext; std::map<uint256, int> mapRequestCount; @@ -222,7 +230,7 @@ public: TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = ""); void MarkDirty(); - bool AddToWallet(const CWalletTx& wtxIn); + bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet=false); void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock); bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate); void EraseFromWallet(const uint256 &hash); @@ -322,6 +330,7 @@ public: void SetBestChain(const CBlockLocator& loc); DBErrors LoadWallet(bool& fFirstRunRet); + DBErrors ZapWalletTx(); bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); @@ -356,6 +365,9 @@ public: // get the current wallet format (the oldest client version guaranteed to understand this wallet) int GetVersion() { AssertLockHeld(cs_wallet); return nWalletVersion; } + // Get wallet transactions that conflict with given transaction (spend same outputs) + std::set<uint256> GetConflicts(const uint256& txid) const; + /** Address book entry changed. * @note called with lock cs_wallet held. */ @@ -694,14 +706,17 @@ public: return (GetDebit() > 0); } - bool IsConfirmed() const + bool IsTrusted() const { // Quick answer in most cases if (!IsFinalTx(*this)) return false; - if (GetDepthInMainChain() >= 1) + int nDepth = GetDepthInMainChain(); + if (nDepth >= 1) return true; - if (!IsFromMe()) // using wtx's cached debit + if (nDepth < 0) + return false; + if (!bSpendZeroConfChange || !IsFromMe()) // using wtx's cached debit return false; // If no confirmations but it's from us, we can still @@ -716,8 +731,11 @@ public: if (!IsFinalTx(*ptx)) return false; - if (ptx->GetDepthInMainChain() >= 1) + int nPDepth = ptx->GetDepthInMainChain(); + if (nPDepth >= 1) continue; + if (nPDepth < 0) + return false; if (!pwallet->IsFromMe(*ptx)) return false; @@ -745,6 +763,8 @@ public: void AddSupportingTransactions(); bool AcceptWalletTransaction(); void RelayWalletTransaction(); + + std::set<uint256> GetConflicts() const; }; diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 4d5c1d8bdc..b3816a54b6 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -154,12 +154,6 @@ bool CWalletDB::ErasePool(int64_t nPool) return Erase(std::make_pair(std::string("pool"), nPool)); } -bool CWalletDB::EraseSetting(const std::string& strKey) -{ - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("setting"), strKey)); -} - bool CWalletDB::WriteMinVersion(int nVersion) { return Write(std::string("minversion"), nVersion); @@ -388,7 +382,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, if (wtx.nOrderPos == -1) wss.fAnyUnordered = true; - pwallet->mapWallet[hash] = wtx; + pwallet->AddToWallet(wtx, true); //// debug print //LogPrintf("LoadWallet %s\n", wtx.GetHash().ToString()); //LogPrintf(" %12"PRId64" %s %s %s\n", @@ -690,6 +684,86 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) return result; } +DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash) +{ + pwallet->vchDefaultKey = CPubKey(); + CWalletScanState wss; + bool fNoncriticalErrors = false; + DBErrors result = DB_LOAD_OK; + + try { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string)"minversion", nMinVersion)) + { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + { + LogPrintf("Error getting wallet database cursor\n"); + return DB_CORRUPT; + } + + while (true) + { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + { + LogPrintf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } + + string strType; + ssKey >> strType; + if (strType == "tx") { + uint256 hash; + ssKey >> hash; + + vTxHash.push_back(hash); + } + } + pcursor->close(); + } + catch (boost::thread_interrupted) { + throw; + } + catch (...) { + result = DB_CORRUPT; + } + + if (fNoncriticalErrors && result == DB_LOAD_OK) + result = DB_NONCRITICAL_ERROR; + + return result; +} + +DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet) +{ + // build list of wallet TXs + vector<uint256> vTxHash; + DBErrors err = FindWalletTx(pwallet, vTxHash); + if (err != DB_LOAD_OK) + return err; + + // erase each wallet TX + BOOST_FOREACH (uint256& hash, vTxHash) { + if (!EraseTx(hash)) + return DB_CORRUPT; + } + + return DB_LOAD_OK; +} + void ThreadFlushWalletDB(const string& strFile) { // Make this thread recognisable as the wallet flushing thread diff --git a/src/walletdb.h b/src/walletdb.h index 15af287245..3bfb436050 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -104,22 +104,6 @@ public: bool WritePool(int64_t nPool, const CKeyPool& keypool); bool ErasePool(int64_t nPool); - // Settings are no longer stored in wallet.dat; these are - // used only for backwards compatibility: - template<typename T> - bool ReadSetting(const std::string& strKey, T& value) - { - return Read(std::make_pair(std::string("setting"), strKey), value); - } - template<typename T> - bool WriteSetting(const std::string& strKey, const T& value) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("setting"), strKey), value); - } - - bool EraseSetting(const std::string& strKey); - bool WriteMinVersion(int nVersion); bool ReadAccount(const std::string& strAccount, CAccount& account); @@ -138,6 +122,8 @@ public: DBErrors ReorderTransactions(CWallet*); DBErrors LoadWallet(CWallet* pwallet); + DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash); + DBErrors ZapWalletTx(CWallet* pwallet); static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, std::string filename); }; |