Skip to content

Commit 05d3ded

Browse files
committed
Merge pull request #3669 from gavinandresen/dead_txns
Handle "conflicted" transactions properly
2 parents e051e65 + 9a3d936 commit 05d3ded

18 files changed

+231
-17
lines changed

qa/rpc-tests/txnmall.sh

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/env bash
2+
3+
# Test block generation and basic wallet sending
4+
5+
if [ $# -lt 1 ]; then
6+
echo "Usage: $0 path_to_binaries"
7+
echo "e.g. $0 ../../src"
8+
exit 1
9+
fi
10+
11+
BITCOIND=${1}/bitcoind
12+
CLI=${1}/bitcoin-cli
13+
14+
DIR="${BASH_SOURCE%/*}"
15+
SENDANDWAIT="${DIR}/send.sh"
16+
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
17+
. "$DIR/util.sh"
18+
19+
D=$(mktemp -d test.XXXXX)
20+
21+
# Two nodes; one will play the part of merchant, the
22+
# other an evil transaction-mutating miner.
23+
24+
D1=${D}/node1
25+
CreateDataDir $D1 port=11000 rpcport=11001
26+
B1ARGS="-datadir=$D1 -debug"
27+
$BITCOIND $B1ARGS &
28+
B1PID=$!
29+
30+
D2=${D}/node2
31+
CreateDataDir $D2 port=11010 rpcport=11011
32+
B2ARGS="-datadir=$D2 -debug"
33+
$BITCOIND $B2ARGS &
34+
B2PID=$!
35+
36+
trap "kill -9 $B1PID $B2PID; rm -rf $D" EXIT
37+
38+
# Wait until all four nodes are at the same block number
39+
function WaitBlocks {
40+
while :
41+
do
42+
sleep 1
43+
BLOCKS1=$( GetBlocks $B1ARGS )
44+
BLOCKS2=$( GetBlocks $B2ARGS )
45+
if (( $BLOCKS1 == $BLOCKS2 ))
46+
then
47+
break
48+
fi
49+
done
50+
}
51+
52+
# Wait until node has $N peers
53+
function WaitPeers {
54+
while :
55+
do
56+
PEERS=$( $CLI $1 getconnectioncount )
57+
if (( "$PEERS" == $2 ))
58+
then
59+
break
60+
fi
61+
sleep 1
62+
done
63+
}
64+
65+
# Start with B2 connected to B1:
66+
$CLI $B2ARGS addnode 127.0.0.1:11000 onetry
67+
WaitPeers "$B1ARGS" 1
68+
69+
# 1 block, 50 XBT each == 50 XBT
70+
$CLI $B1ARGS setgenerate true 1
71+
72+
WaitBlocks
73+
# 100 blocks, 0 mature == 0 XBT
74+
$CLI $B2ARGS setgenerate true 100
75+
WaitBlocks
76+
77+
CheckBalance $B1ARGS 50
78+
CheckBalance $B2ARGS 0
79+
80+
# restart B2 with no connection
81+
$CLI $B2ARGS stop > /dev/null 2>&1
82+
wait $B2PID
83+
$BITCOIND $B2ARGS &
84+
B2PID=$!
85+
86+
B2ADDRESS=$( $CLI $B2ARGS getnewaddress )
87+
88+
# Have B1 create two transactions; second will
89+
# spend change from first, since B1 starts with only a single
90+
# 50 bitcoin output:
91+
TXID1=$( $CLI $B1ARGS sendtoaddress $B2ADDRESS 1.0 )
92+
TXID2=$( $CLI $B1ARGS sendtoaddress $B2ADDRESS 2.0 )
93+
94+
# Mutate TXID1 and add it to B2's memory pool:
95+
RAWTX1=$( $CLI $B1ARGS getrawtransaction $TXID1 )
96+
RAWTX2=$( $CLI $B1ARGS getrawtransaction $TXID2 )
97+
# ... mutate RAWTX1:
98+
# RAWTX1 is hex-encoded, serialized transaction. So each
99+
# byte is two characters; we'll prepend the first
100+
# "push" in the scriptsig with OP_PUSHDATA1 (0x4c),
101+
# and add one to the length of the signature.
102+
# Fields are fixed; from the beginning:
103+
# 4-byte version
104+
# 1-byte varint number-of inputs (one in this case)
105+
# 32-byte previous txid
106+
# 4-byte previous output
107+
# 1-byte varint length-of-scriptsig
108+
# 1-byte PUSH this many bytes onto stack
109+
# ... etc
110+
# So: to mutate, we want to get byte 41 (hex characters 82-83),
111+
# increment it, and insert 0x4c after it.
112+
L=${RAWTX1:82:2}
113+
NEWLEN=$( printf "%x" $(( 16#$L + 1 )) )
114+
MUTATEDTX1=${RAWTX1:0:82}${NEWLEN}4c${RAWTX1:84}
115+
# ... give mutated tx1 to B2:
116+
MUTATEDTXID=$( $CLI $B2ARGS sendrawtransaction $MUTATEDTX1 )
117+
118+
echo "TXID1: " $TXID1
119+
echo "Mutated: " $MUTATEDTXID
120+
121+
# Re-connect nodes, and have B2 mine a block
122+
$CLI $B2ARGS addnode 127.0.0.1:11000 onetry
123+
WaitPeers "$B1ARGS" 1
124+
125+
$CLI $B2ARGS setgenerate true 1
126+
WaitBlocks
127+
128+
$CLI $B2ARGS stop > /dev/null 2>&1
129+
wait $B2PID
130+
$CLI $B1ARGS stop > /dev/null 2>&1
131+
wait $B1PID
132+
133+
trap "" EXIT
134+
135+
echo "Done, bitcoind's shut down. To rerun/poke around:"
136+
echo "${1}/bitcoind -datadir=$D1 -daemon"
137+
echo "${1}/bitcoind -datadir=$D2 -daemon -connect=127.0.0.1:11000"
138+
echo "To cleanup:"
139+
echo "killall bitcoind; rm -rf test.*"
140+
exit 0
141+
142+
echo "Tests successful, cleaning up"
143+
rm -rf $D
144+
exit 0

src/main.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
872872
}
873873

874874

875-
int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
875+
int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const
876876
{
877877
if (hashBlock == 0 || nIndex == -1)
878878
return 0;
@@ -897,6 +897,14 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
897897
return chainActive.Height() - pindex->nHeight + 1;
898898
}
899899

900+
int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
901+
{
902+
int nResult = GetDepthInMainChainINTERNAL(pindexRet);
903+
if (nResult == 0 && !mempool.exists(GetHash()))
904+
return -1; // Not in chain, not in mempool
905+
906+
return nResult;
907+
}
900908

901909
int CMerkleTx::GetBlocksToMaturity() const
902910
{

src/main.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,8 @@ class CScriptCheck
423423
/** A transaction with a merkle branch linking it to the block chain. */
424424
class CMerkleTx : public CTransaction
425425
{
426+
private:
427+
int GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const;
426428
public:
427429
uint256 hashBlock;
428430
std::vector<uint256> vMerkleBranch;
@@ -461,9 +463,14 @@ class CMerkleTx : public CTransaction
461463

462464

463465
int SetMerkleBranch(const CBlock* pblock=NULL);
466+
467+
// Return depth of transaction in blockchain:
468+
// -1 : not in blockchain, and not in memory pool (conflicted transaction)
469+
// 0 : in memory pool, waiting to be included in a block
470+
// >=1 : this many blocks deep in the main chain
464471
int GetDepthInMainChain(CBlockIndex* &pindexRet) const;
465472
int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
466-
bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
473+
bool IsInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; }
467474
int GetBlocksToMaturity() const;
468475
bool AcceptToMemoryPool(bool fLimitFree=true);
469476
};

src/qt/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ RES_ICONS = \
243243
res/icons/toolbar_testnet.png \
244244
res/icons/transaction0.png \
245245
res/icons/transaction2.png \
246+
res/icons/transaction_conflicted.png \
246247
res/icons/tx_inout.png \
247248
res/icons/tx_input.png \
248249
res/icons/tx_output.png \

src/qt/bitcoin.qrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<file alias="connect_4">res/icons/connect4_16.png</file>
1313
<file alias="transaction_0">res/icons/transaction0.png</file>
1414
<file alias="transaction_confirmed">res/icons/transaction2.png</file>
15+
<file alias="transaction_conflicted">res/icons/transaction_conflicted.png</file>
1516
<file alias="transaction_1">res/icons/clock1.png</file>
1617
<file alias="transaction_2">res/icons/clock2.png</file>
1718
<file alias="transaction_3">res/icons/clock3.png</file>

src/qt/overviewpage.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ void OverviewPage::setWalletModel(WalletModel *model)
175175
filter->setLimit(NUM_ITEMS);
176176
filter->setDynamicSortFilter(true);
177177
filter->setSortRole(Qt::EditRole);
178+
filter->setShowInactive(false);
178179
filter->sort(TransactionTableModel::Status, Qt::DescendingOrder);
179180

180181
ui->listTransactions->setModel(filter);
474 Bytes
Loading

src/qt/transactiondesc.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
3030
else
3131
{
3232
int nDepth = wtx.GetDepthInMainChain();
33-
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
33+
if (nDepth < 0)
34+
return tr("conflicted");
35+
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
3436
return tr("%1/offline").arg(nDepth);
3537
else if (nDepth < 6)
3638
return tr("%1/unconfirmed").arg(nDepth);

src/qt/transactionfilterproxy.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "transactionfilterproxy.h"
66

77
#include "transactiontablemodel.h"
8+
#include "transactionrecord.h"
89

910
#include <cstdlib>
1011

@@ -22,7 +23,8 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) :
2223
addrPrefix(),
2324
typeFilter(ALL_TYPES),
2425
minAmount(0),
25-
limitRows(-1)
26+
limitRows(-1),
27+
showInactive(true)
2628
{
2729
}
2830

@@ -35,7 +37,10 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &
3537
QString address = index.data(TransactionTableModel::AddressRole).toString();
3638
QString label = index.data(TransactionTableModel::LabelRole).toString();
3739
qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong());
40+
int status = index.data(TransactionTableModel::StatusRole).toInt();
3841

42+
if(!showInactive && status == TransactionStatus::Conflicted)
43+
return false;
3944
if(!(TYPE(type) & typeFilter))
4045
return false;
4146
if(datetime < dateFrom || datetime > dateTo)
@@ -78,6 +83,12 @@ void TransactionFilterProxy::setLimit(int limit)
7883
this->limitRows = limit;
7984
}
8085

86+
void TransactionFilterProxy::setShowInactive(bool showInactive)
87+
{
88+
this->showInactive = showInactive;
89+
invalidateFilter();
90+
}
91+
8192
int TransactionFilterProxy::rowCount(const QModelIndex &parent) const
8293
{
8394
if(limitRows != -1)

src/qt/transactionfilterproxy.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ class TransactionFilterProxy : public QSortFilterProxyModel
3636
/** Set maximum number of rows returned, -1 if unlimited. */
3737
void setLimit(int limit);
3838

39+
/** Set whether to show conflicted transactions. */
40+
void setShowInactive(bool showInactive);
41+
3942
int rowCount(const QModelIndex &parent = QModelIndex()) const;
4043

4144
protected:
@@ -48,6 +51,7 @@ class TransactionFilterProxy : public QSortFilterProxyModel
4851
quint32 typeFilter;
4952
qint64 minAmount;
5053
int limitRows;
54+
bool showInactive;
5155
};
5256

5357
#endif // TRANSACTIONFILTERPROXY_H

0 commit comments

Comments
 (0)