Bugfix: Unspendable inputs handling
[novacoin.git] / src / qt / transactionrecord.cpp
1 #include "transactionrecord.h"
2
3 #include "wallet.h"
4 #include "base58.h"
5
6 /* Return positive answer if transaction should be shown in list.
7  */
8 bool TransactionRecord::showTransaction(const CWalletTx &wtx)
9 {
10     if (wtx.IsCoinBase())
11     {
12         // Ensures we show generated coins / mined transactions at depth 1
13         if (!wtx.IsInMainChain())
14         {
15             return false;
16         }
17     }
18     return true;
19 }
20
21 /*
22  * Decompose CWallet transaction to model transaction records.
23  */
24 QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
25 {
26     QList<TransactionRecord> parts;
27     int64 nTime = wtx.GetTxTime();
28     int64 nCredit = wtx.GetCredit(true);
29     int64 nDebit = wtx.GetDebit();
30     int64 nNet = nCredit - nDebit;
31     uint256 hash = wtx.GetHash(), hashPrev = 0;
32     std::map<std::string, std::string> mapValue = wtx.mapValue;
33
34     if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake())
35     {
36         //
37         // Credit
38         //
39         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
40         {
41             if(wallet->IsMine(txout))
42             {
43                 TransactionRecord sub(hash, nTime);
44                 CTxDestination address;
45                 sub.idx = parts.size(); // sequence number
46                 sub.credit = txout.nValue;
47                 if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
48                 {
49                     // Received by Bitcoin Address
50                     sub.type = TransactionRecord::RecvWithAddress;
51                     sub.address = CBitcoinAddress(address).ToString();
52                 }
53                 else
54                 {
55                     // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
56                     sub.type = TransactionRecord::RecvFromOther;
57                     sub.address = mapValue["from"];
58                 }
59                 if (wtx.IsCoinBase())
60                 {
61                     // Generated (proof-of-work)
62                     sub.type = TransactionRecord::Generated;
63                 }
64                 if (wtx.IsCoinStake())
65                 {
66                     // Generated (proof-of-stake)
67
68                     if (hashPrev == hash)
69                         continue; // last coinstake output
70
71                     sub.type = TransactionRecord::Generated;
72                     sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit;
73                     hashPrev = hash;
74                 }
75
76                 parts.append(sub);
77             }
78         }
79     }
80     else
81     {
82         bool fAllFromMe = true;
83         BOOST_FOREACH(const CTxIn& txin, wtx.vin)
84             fAllFromMe = fAllFromMe && wallet->IsMine(txin);
85
86         bool fAllToMe = true;
87         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
88             fAllToMe = fAllToMe && wallet->IsMine(txout);
89
90         if (fAllFromMe && fAllToMe)
91         {
92             // Payment to self
93             int64 nChange = wtx.GetChange();
94
95             parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
96                             -(nDebit - nChange), nCredit - nChange));
97         }
98         else if (fAllFromMe)
99         {
100             //
101             // Debit
102             //
103             int64 nTxFee = nDebit - wtx.GetValueOut();
104
105             for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
106             {
107                 const CTxOut& txout = wtx.vout[nOut];
108                 TransactionRecord sub(hash, nTime);
109                 sub.idx = parts.size();
110
111                 if(wallet->IsMine(txout))
112                 {
113                     // Ignore parts sent to self, as this is usually the change
114                     // from a transaction sent back to our own address.
115                     continue;
116                 }
117
118                 CTxDestination address;
119                 if (ExtractDestination(txout.scriptPubKey, address))
120                 {
121                     // Sent to Bitcoin Address
122                     sub.type = TransactionRecord::SendToAddress;
123                     sub.address = CBitcoinAddress(address).ToString();
124                 }
125                 else
126                 {
127                     // Sent to IP, or other non-address transaction like OP_EVAL
128                     sub.type = TransactionRecord::SendToOther;
129                     sub.address = mapValue["to"];
130                 }
131
132                 int64 nValue = txout.nValue;
133                 /* Add fee to first output */
134                 if (nTxFee > 0)
135                 {
136                     nValue += nTxFee;
137                     nTxFee = 0;
138                 }
139                 sub.debit = -nValue;
140
141                 parts.append(sub);
142             }
143         }
144         else
145         {
146             //
147             // Mixed debit transaction, can't break down payees
148             //
149             parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
150         }
151     }
152
153     return parts;
154 }
155
156 void TransactionRecord::updateStatus(const CWalletTx &wtx)
157 {
158     // Determine transaction status
159
160     // Find the block the tx is in
161     CBlockIndex* pindex = NULL;
162     std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
163     if (mi != mapBlockIndex.end())
164         pindex = (*mi).second;
165
166     // Sort order, unrecorded transactions sort to the top
167     status.sortKey = strprintf("%010d-%01d-%010u-%03d",
168         (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
169         (wtx.IsCoinBase() ? 1 : 0),
170         wtx.nTimeReceived,
171         idx);
172     status.confirmed = wtx.IsTrusted();
173     status.depth = wtx.GetDepthInMainChain();
174     status.cur_num_blocks = nBestHeight;
175
176     if (!wtx.IsFinal())
177     {
178         if (wtx.nLockTime < LOCKTIME_THRESHOLD)
179         {
180             status.status = TransactionStatus::OpenUntilBlock;
181             status.open_for = nBestHeight - wtx.nLockTime;
182         }
183         else
184         {
185             status.status = TransactionStatus::OpenUntilDate;
186             status.open_for = wtx.nLockTime;
187         }
188     }
189     else
190     {
191         if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
192         {
193             status.status = TransactionStatus::Offline;
194         }
195         else if (status.depth < NumConfirmations)
196         {
197             status.status = TransactionStatus::Unconfirmed;
198         }
199         else
200         {
201             status.status = TransactionStatus::HaveConfirmations;
202         }
203     }
204
205     // For generated transactions, determine maturity
206     if(type == TransactionRecord::Generated)
207     {
208         int64 nCredit = wtx.GetCredit(true);
209         if (nCredit == 0)
210         {
211             status.maturity = TransactionStatus::Immature;
212
213             if (wtx.IsInMainChain())
214             {
215                 status.matures_in = wtx.GetBlocksToMaturity();
216
217                 // Check if the block was requested by anyone
218                 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
219                     status.maturity = TransactionStatus::MaturesWarning;
220             }
221             else
222             {
223                 status.maturity = TransactionStatus::NotAccepted;
224             }
225         }
226         else
227         {
228             status.maturity = TransactionStatus::Mature;
229         }
230     }
231 }
232
233 bool TransactionRecord::statusUpdateNeeded()
234 {
235     return status.cur_num_blocks != nBestHeight;
236 }
237
238 std::string TransactionRecord::getTxID()
239 {
240     return hash.ToString() + strprintf("-%03d", idx);
241 }
242