aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/init.cpp23
-rw-r--r--src/main.cpp14
-rw-r--r--src/main.h9
-rw-r--r--src/qt/Makefile.am1
-rw-r--r--src/qt/bitcoin.cpp3
-rw-r--r--src/qt/bitcoin.qrc1
-rw-r--r--src/qt/forms/optionsdialog.ui129
-rw-r--r--src/qt/optionsdialog.cpp5
-rw-r--r--src/qt/optionsmodel.cpp76
-rw-r--r--src/qt/optionsmodel.h4
-rw-r--r--src/qt/overviewpage.cpp1
-rw-r--r--src/qt/res/icons/transaction_conflicted.pngbin0 -> 474 bytes
-rw-r--r--src/qt/transactiondesc.cpp4
-rw-r--r--src/qt/transactionfilterproxy.cpp13
-rw-r--r--src/qt/transactionfilterproxy.h4
-rw-r--r--src/qt/transactionrecord.cpp8
-rw-r--r--src/qt/transactionrecord.h3
-rw-r--r--src/qt/transactiontablemodel.cpp10
-rw-r--r--src/qt/transactiontablemodel.h4
-rw-r--r--src/qt/walletmodel.cpp8
-rw-r--r--src/rpcblockchain.cpp4
-rw-r--r--src/rpcnet.cpp2
-rw-r--r--src/rpcwallet.cpp37
-rw-r--r--src/script.cpp45
-rw-r--r--src/script.h23
-rw-r--r--src/test/script_tests.cpp18
-rw-r--r--src/wallet.cpp158
-rw-r--r--src/wallet.h30
-rw-r--r--src/walletdb.cpp88
-rw-r--r--src/walletdb.h18
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 &amp;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&amp;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 &amp;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>&amp;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
new file mode 100644
index 0000000000..51fff649ab
--- /dev/null
+++ b/src/qt/res/icons/transaction_conflicted.png
Binary files differ
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);
};