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