- Extend CMerkleTx::GetDepthInMainChain with the concept of a "conflicted" transaction - a transaction generated by the wallet that is not in the main chain or in the mempool, and, therefore, will likely never be confirmed.
- Exclamation mark icon for conflicted transactions
- Show mouseover status for conflicted transactions as "conflicted"
- Don't show inactive transactions on overview page overview
vtxid.push_back((*mi).first);
}
-
-
-
+// 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 CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
{
- if (hashBlock == 0 || nIndex == -1)
- return 0;
+ bool fInMemPool = mempool.exists(GetHash());
+
+ if (hashBlock == 0 || nIndex == -1) {
+ return fInMemPool ? 0 : -1;
+ }
// Find the block it claims to be in
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
- if (mi == mapBlockIndex.end())
- return 0;
+ if (mi == mapBlockIndex.end()) {
+ return fInMemPool ? 0 : -1;
+ }
CBlockIndex* pindex = (*mi).second;
- if (!pindex || !pindex->IsInMainChain())
- return 0;
+ if (!pindex || !pindex->IsInMainChain()) {
+ return fInMemPool ? 0 : -1;
+ }
// Make sure the merkle branch connects to this block
if (!fMerkleVerified)
{
- if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot)
- return 0;
+ if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) {
+ return fInMemPool ? 0 : -1;
+ }
fMerkleVerified = true;
}
<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>
filter->setSourceModel(model->getTransactionTableModel());
filter->setLimit(NUM_ITEMS);
filter->setDynamicSortFilter(true);
-// filter->setSortRole(Qt::EditRole);
filter->setSortRole(TransactionTableModel::DateRole);
+ filter->setShowInactive(false);
filter->sort(TransactionTableModel::Status, Qt::DescendingOrder);
ui->listTransactions->setModel(filter);
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);
#include "transactionfilterproxy.h"
#include "transactiontablemodel.h"
+#include "transactionrecord.h"
#include <QDateTime>
addrPrefix(),
typeFilter(ALL_TYPES),
minAmount(0),
- limitRows(-1)
+ limitRows(-1),
+ showInactive(true)
{
}
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;
this->limitRows = limit;
}
+void TransactionFilterProxy::setShowInactive(bool showInactive)
+{
+ this->showInactive = showInactive;
+ invalidateFilter();
+}
+
int TransactionFilterProxy::rowCount(const QModelIndex &parent) const
{
if(limitRows != -1)
/** 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:
bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const;
quint32 typeFilter;
qint64 minAmount;
int limitRows;
+ bool showInactive;
signals:
if (!wtx.IsFinal())
{
- if (wtx.nLockTime < LOCKTIME_THRESHOLD)
- {
+ if (wtx.nLockTime < LOCKTIME_THRESHOLD) {
status.status = TransactionStatus::OpenUntilBlock;
status.open_for = nBestHeight - wtx.nLockTime;
}
- else
- {
+ else {
status.status = TransactionStatus::OpenUntilDate;
status.open_for = wtx.nLockTime;
}
}
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;
}
- else if (status.depth < NumConfirmations)
- {
+ else if (status.depth < NumConfirmations) {
status.status = TransactionStatus::Unconfirmed;
}
- else
- {
+ else {
status.status = TransactionStatus::HaveConfirmations;
}
}
OpenUntilBlock,
Offline,
Unconfirmed,
- HaveConfirmations
+ HaveConfirmations,
+ Conflicted
};
bool confirmed;
case TransactionStatus::HaveConfirmations:
status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth);
break;
+ case TransactionStatus::Conflicted:
+ status = tr("Conflicted");
+ break;
}
if(wtx->type == TransactionRecord::Generated)
};
case TransactionStatus::HaveConfirmations:
return QIcon(":/icons/transaction_confirmed");
+ case TransactionStatus::Conflicted:
+ return QIcon(":/icons/transaction_conflicted");
}
}
return QColor(0,0,0);
return rec->status.confirmed && !(rec->type == TransactionRecord::Generated && rec->status.maturity != TransactionStatus::Mature);
case FormattedAmountRole:
return formatTxAmount(rec, false);
+ case StatusRole:
+ return rec->status.status;
}
return QVariant();
}
/** Is transaction confirmed? */
ConfirmedRole,
/** Formatted amount, without brackets when unconfirmed */
- FormattedAmountRole
+ FormattedAmountRole,
+ /** Transaction status (TransactionRecord::Status) */
+ StatusRole
};
int rowCount(const QModelIndex &parent) const;
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);
}
}
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);
}
entry.push_back(Pair("confirmations", confirms));
if (wtx.IsCoinBase() || wtx.IsCoinStake())
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));
Object entry;
entry.push_back(Pair("account", strSentAccount));
entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString()));
- 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)
else
entry.push_back(Pair("category", "generate"));
}
- else
- entry.push_back(Pair("category", "receive"));
+ else {
+ 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);
Array details;
ListTransactions(pwalletMain->mapWallet[hash], "*", 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));
}
else
{
//
// database format versioning
//
-static const int DATABASE_VERSION = 70505;
+static const int DATABASE_VERSION = 70508;
//
// network protocol versioning
//
-static const int PROTOCOL_VERSION = 60011;
+static const int PROTOCOL_VERSION = 60013;
// earlier versions not supported as of Feb 2012, and are disconnected
static const int MIN_PROTO_VERSION = 209;
{
const CWalletTx* pcoin = &(*it).second;
- if (!pcoin->IsFinal())
+ if (!pcoin->IsFinal()) {
continue;
+ }
- if (fOnlyConfirmed && !pcoin->IsTrusted())
+ if (fOnlyConfirmed && !pcoin->IsTrusted()) {
continue;
+ }
- if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
+ if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) {
continue;
+ }
- if(pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0)
+ int nDepth = pcoin->GetDepthInMainChain();
+ if (nDepth < 0) {
continue;
+ }
- for (unsigned int i = 0; i < pcoin->vout.size(); i++)
+ if(pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0) {
+ continue;
+ }
+
+ for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue >= nMinimumInputValue &&
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
- vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain()));
-
+ vCoins.push_back(COutput(pcoin, i, nDepth));
+ }
}
}
}
void CWallet::AvailableCoinsMinConf(vector<COutput>& vCoins, int nConf) const
{
vCoins.clear();
-
{
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
// Quick answer in most cases
if (!IsFinal())
return false;
- if (GetDepthInMainChain() >= 1)
+ int nDepth = GetDepthInMainChain();
+ if (nDepth >= 1)
return true;
+ if (nDepth < 0)
+ return false;
if (fConfChange || !IsFromMe()) // using wtx's cached debit
return false;
if (!ptx->IsFinal())
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;