Somewhat confident now, tested on GNOME+KDE, with all types of transactions. Next...
[novacoin.git] / src / qt / transactionrecord.cpp
1 #include "transactionrecord.h"
2
3
4 /* Return positive answer if transaction should be shown in list.
5  */
6 bool TransactionRecord::showTransaction(const CWalletTx &wtx)
7 {
8     if (wtx.IsCoinBase())
9     {
10         // Don't show generated coin until confirmed by at least one block after it
11         // so we don't get the user's hopes up until it looks like it's probably accepted.
12         //
13         // It is not an error when generated blocks are not accepted.  By design,
14         // some percentage of blocks, like 10% or more, will end up not accepted.
15         // This is the normal mechanism by which the network copes with latency.
16         //
17         // We display regular transactions right away before any confirmation
18         // because they can always get into some block eventually.  Generated coins
19         // are special because if their block is not accepted, they are not valid.
20         //
21         if (wtx.GetDepthInMainChain() < 2)
22         {
23             return false;
24         }
25     }
26     return true;
27 }
28
29 /* Decompose CWallet transaction to model transaction records.
30  */
31 QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWalletTx &wtx)
32 {
33     QList<TransactionRecord> parts;
34     int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
35     int64 nCredit = wtx.GetCredit(true);
36     int64 nDebit = wtx.GetDebit();
37     int64 nNet = nCredit - nDebit;
38     uint256 hash = wtx.GetHash();
39     std::map<std::string, std::string> mapValue = wtx.mapValue;
40
41     if (showTransaction(wtx))
42     {
43         if (nNet > 0 || wtx.IsCoinBase())
44         {
45             //
46             // Credit
47             //
48             TransactionRecord sub(hash, nTime);
49
50             sub.credit = nNet;
51
52             if (wtx.IsCoinBase())
53             {
54                 // Generated
55                 sub.type = TransactionRecord::Generated;
56
57                 if (nCredit == 0)
58                 {
59                     int64 nUnmatured = 0;
60                     BOOST_FOREACH(const CTxOut& txout, wtx.vout)
61                         nUnmatured += txout.GetCredit();
62                     sub.credit = nUnmatured;
63                 }
64             }
65             else if (!mapValue["from"].empty() || !mapValue["message"].empty())
66             {
67                 // Received by IP connection
68                 sub.type = TransactionRecord::RecvFromIP;
69                 if (!mapValue["from"].empty())
70                     sub.address = mapValue["from"];
71             }
72             else
73             {
74                 // Received by Bitcoin Address
75                 sub.type = TransactionRecord::RecvWithAddress;
76                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
77                 {
78                     if (txout.IsMine())
79                     {
80                         std::vector<unsigned char> vchPubKey;
81                         if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
82                         {
83                             sub.address = PubKeyToAddress(vchPubKey);
84                         }
85                         break;
86                     }
87                 }
88             }
89             parts.append(sub);
90         }
91         else
92         {
93             bool fAllFromMe = true;
94             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
95                 fAllFromMe = fAllFromMe && txin.IsMine();
96
97             bool fAllToMe = true;
98             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
99                 fAllToMe = fAllToMe && txout.IsMine();
100
101             if (fAllFromMe && fAllToMe)
102             {
103                 // Payment to self
104                 int64 nChange = wtx.GetChange();
105
106                 parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
107                                 -(nDebit - nChange), nCredit - nChange));
108             }
109             else if (fAllFromMe)
110             {
111                 //
112                 // Debit
113                 //
114                 int64 nTxFee = nDebit - wtx.GetValueOut();
115
116                 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
117                 {
118                     const CTxOut& txout = wtx.vout[nOut];
119                     TransactionRecord sub(hash, nTime);
120                     sub.idx = parts.size();
121
122                     if (txout.IsMine())
123                     {
124                         // Ignore parts sent to self, as this is usually the change
125                         // from a transaction sent back to our own address.
126                         continue;
127                     }
128                     else if (!mapValue["to"].empty())
129                     {
130                         // Sent to IP
131                         sub.type = TransactionRecord::SendToIP;
132                         sub.address = mapValue["to"];
133                     }
134                     else
135                     {
136                         // Sent to Bitcoin Address
137                         sub.type = TransactionRecord::SendToAddress;
138                         uint160 hash160;
139                         if (ExtractHash160(txout.scriptPubKey, hash160))
140                             sub.address = Hash160ToAddress(hash160);
141                     }
142
143                     int64 nValue = txout.nValue;
144                     /* Add fee to first output */
145                     if (nTxFee > 0)
146                     {
147                         nValue += nTxFee;
148                         nTxFee = 0;
149                     }
150                     sub.debit = -nValue;
151
152                     parts.append(sub);
153                 }
154             }
155             else
156             {
157                 //
158                 // Mixed debit transaction, can't break down payees
159                 //
160                 bool fAllMine = true;
161                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
162                     fAllMine = fAllMine && txout.IsMine();
163                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
164                     fAllMine = fAllMine && txin.IsMine();
165
166                 parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
167             }
168         }
169     }
170
171     return parts;
172 }
173
174 void TransactionRecord::updateStatus(const CWalletTx &wtx)
175 {
176     // Determine transaction status
177
178     // Find the block the tx is in
179     CBlockIndex* pindex = NULL;
180     std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
181     if (mi != mapBlockIndex.end())
182         pindex = (*mi).second;
183
184     // Sort order, unrecorded transactions sort to the top
185     status.sortKey = strprintf("%010d-%01d-%010u-%03d",
186         (pindex ? pindex->nHeight : INT_MAX),
187         (wtx.IsCoinBase() ? 1 : 0),
188         wtx.nTimeReceived,
189         idx);
190     status.confirmed = wtx.IsConfirmed();
191     status.depth = wtx.GetDepthInMainChain();
192     status.cur_num_blocks = nBestHeight;
193
194     if (!wtx.IsFinal())
195     {
196         if (wtx.nLockTime < 500000000)
197         {
198             status.status = TransactionStatus::OpenUntilBlock;
199             status.open_for = nBestHeight - wtx.nLockTime;
200         }
201         else
202         {
203             status.status = TransactionStatus::OpenUntilDate;
204             status.open_for = wtx.nLockTime;
205         }
206     }
207     else
208     {
209         if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
210         {
211             status.status = TransactionStatus::Offline;
212         }
213         else if (status.depth < 6)
214         {
215             status.status = TransactionStatus::Unconfirmed;
216         }
217         else
218         {
219             status.status = TransactionStatus::HaveConfirmations;
220         }
221     }
222
223     // For generated transactions, determine maturity
224     if(type == TransactionRecord::Generated)
225     {
226         int64 nCredit = wtx.GetCredit(true);
227         if (nCredit == 0)
228         {
229             status.maturity = TransactionStatus::Immature;
230
231             if (wtx.IsInMainChain())
232             {
233                 status.matures_in = wtx.GetBlocksToMaturity();
234
235                 // Check if the block was requested by anyone
236                 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
237                     status.maturity = TransactionStatus::MaturesWarning;
238             }
239             else
240             {
241                 status.maturity = TransactionStatus::NotAccepted;
242             }
243         }
244         else
245         {
246             status.maturity = TransactionStatus::Mature;
247         }
248     }
249 }
250
251 bool TransactionRecord::statusUpdateNeeded()
252 {
253     return status.cur_num_blocks != nBestHeight;
254 }