Implement IsMine filter
[novacoin.git] / src / qt / transactionrecord.cpp
index 34f30d5..bd6155b 100644 (file)
@@ -1,5 +1,7 @@
 #include "transactionrecord.h"
 
+#include "wallet.h"
+#include "base58.h"
 
 /* Return positive answer if transaction should be shown in list.
  */
@@ -7,18 +9,8 @@ bool TransactionRecord::showTransaction(const CWalletTx &wtx)
 {
     if (wtx.IsCoinBase())
     {
-        // Don't show generated coin until confirmed by at least one block after it
-        // so we don't get the user's hopes up until it looks like it's probably accepted.
-        //
-        // It is not an error when generated blocks are not accepted.  By design,
-        // some percentage of blocks, like 10% or more, will end up not accepted.
-        // This is the normal mechanism by which the network copes with latency.
-        //
-        // We display regular transactions right away before any confirmation
-        // because they can always get into some block eventually.  Generated coins
-        // are special because if their block is not accepted, they are not valid.
-        //
-        if (wtx.GetDepthInMainChain() < 2)
+        // Ensures we show generated coins / mined transactions at depth 1
+        if (!wtx.IsInMainChain())
         {
             return false;
         }
@@ -29,144 +21,133 @@ bool TransactionRecord::showTransaction(const CWalletTx &wtx)
 /*
  * Decompose CWallet transaction to model transaction records.
  */
-QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWalletTx &wtx)
+QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
 {
     QList<TransactionRecord> parts;
-    int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
+    int64 nTime = wtx.GetTxTime();
     int64 nCredit = wtx.GetCredit(true);
-    int64 nDebit = wtx.GetDebit();
+    int64 nDebit = wtx.GetDebit(MINE_ALL);
     int64 nNet = nCredit - nDebit;
-    uint256 hash = wtx.GetHash();
+    uint256 hash = wtx.GetHash(), hashPrev = 0;
     std::map<std::string, std::string> mapValue = wtx.mapValue;
 
-    if (showTransaction(wtx))
+    if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake())
     {
-        if (nNet > 0 || wtx.IsCoinBase())
+        //
+        // Credit
+        //
+        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
         {
-            //
-            // Credit
-            //
-            TransactionRecord sub(hash, nTime);
-
-            sub.credit = nNet;
-
-            if (wtx.IsCoinBase())
+            if(wallet->IsMine(txout))
             {
-                // Generated
-                sub.type = TransactionRecord::Generated;
-
-                if (nCredit == 0)
+                TransactionRecord sub(hash, nTime);
+                CTxDestination address;
+                sub.idx = parts.size(); // sequence number
+                sub.credit = txout.nValue;
+                if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
                 {
-                    int64 nUnmatured = 0;
-                    BOOST_FOREACH(const CTxOut& txout, wtx.vout)
-                        nUnmatured += txout.GetCredit();
-                    sub.credit = nUnmatured;
+                    // Received by Bitcoin Address
+                    sub.type = TransactionRecord::RecvWithAddress;
+                    sub.address = CBitcoinAddress(address).ToString();
                 }
-            }
-            else if (!mapValue["from"].empty() || !mapValue["message"].empty())
-            {
-                // Received by IP connection
-                sub.type = TransactionRecord::RecvFromIP;
-                if (!mapValue["from"].empty())
+                else
+                {
+                    // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
+                    sub.type = TransactionRecord::RecvFromOther;
                     sub.address = mapValue["from"];
-            }
-            else
-            {
-                // Received by Bitcoin Address
-                sub.type = TransactionRecord::RecvWithAddress;
-                BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+                }
+                if (wtx.IsCoinBase())
+                {
+                    // Generated (proof-of-work)
+                    sub.type = TransactionRecord::Generated;
+                }
+                if (wtx.IsCoinStake())
                 {
-                    if (txout.IsMine())
-                    {
-                        std::vector<unsigned char> vchPubKey;
-                        if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
-                        {
-                            sub.address = PubKeyToAddress(vchPubKey);
-                        }
-                        break;
-                    }
+                    // Generated (proof-of-stake)
+
+                    if (hashPrev == hash)
+                        continue; // last coinstake output
+
+                    sub.type = TransactionRecord::Generated;
+                    sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit;
+                    hashPrev = hash;
                 }
+
+                parts.append(sub);
             }
-            parts.append(sub);
         }
-        else
+    }
+    else
+    {
+        bool fAllFromMe = true;
+        BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+            fAllFromMe = fAllFromMe && wallet->IsMine(txin);
+
+        bool fAllToMe = true;
+        BOOST_FOREACH(const CTxOut& txout, wtx.vout)
+            fAllToMe = fAllToMe && wallet->IsMine(txout);
+
+        if (fAllFromMe && fAllToMe)
         {
-            bool fAllFromMe = true;
-            BOOST_FOREACH(const CTxIn& txin, wtx.vin)
-                fAllFromMe = fAllFromMe && txin.IsMine();
+            // Payment to self
+            int64 nChange = wtx.GetChange();
 
-            bool fAllToMe = true;
-            BOOST_FOREACH(const CTxOut& txout, wtx.vout)
-                fAllToMe = fAllToMe && txout.IsMine();
+            parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
+                            -(nDebit - nChange), nCredit - nChange));
+        }
+        else if (fAllFromMe)
+        {
+            //
+            // Debit
+            //
+            int64 nTxFee = nDebit - wtx.GetValueOut();
 
-            if (fAllFromMe && fAllToMe)
+            for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
             {
-                // Payment to self
-                int64 nChange = wtx.GetChange();
+                const CTxOut& txout = wtx.vout[nOut];
+                TransactionRecord sub(hash, nTime);
+                sub.idx = parts.size();
 
-                parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
-                                -(nDebit - nChange), nCredit - nChange));
-            }
-            else if (fAllFromMe)
-            {
-                //
-                // Debit
-                //
-                int64 nTxFee = nDebit - wtx.GetValueOut();
+                if(wallet->IsMine(txout))
+                {
+                    // Ignore parts sent to self, as this is usually the change
+                    // from a transaction sent back to our own address.
+                    continue;
+                }
 
-                for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
+                CTxDestination address;
+                if (ExtractDestination(txout.scriptPubKey, address))
                 {
-                    const CTxOut& txout = wtx.vout[nOut];
-                    TransactionRecord sub(hash, nTime);
-                    sub.idx = parts.size();
-
-                    if (txout.IsMine())
-                    {
-                        // Ignore parts sent to self, as this is usually the change
-                        // from a transaction sent back to our own address.
-                        continue;
-                    }
-                    else if (!mapValue["to"].empty())
-                    {
-                        // Sent to IP
-                        sub.type = TransactionRecord::SendToIP;
-                        sub.address = mapValue["to"];
-                    }
-                    else
-                    {
-                        // Sent to Bitcoin Address
-                        sub.type = TransactionRecord::SendToAddress;
-                        uint160 hash160;
-                        if (ExtractHash160(txout.scriptPubKey, hash160))
-                            sub.address = Hash160ToAddress(hash160);
-                    }
-
-                    int64 nValue = txout.nValue;
-                    /* Add fee to first output */
-                    if (nTxFee > 0)
-                    {
-                        nValue += nTxFee;
-                        nTxFee = 0;
-                    }
-                    sub.debit = -nValue;
-
-                    parts.append(sub);
+                    // Sent to Bitcoin Address
+                    sub.type = TransactionRecord::SendToAddress;
+                    sub.address = CBitcoinAddress(address).ToString();
                 }
-            }
-            else
-            {
-                //
-                // Mixed debit transaction, can't break down payees
-                //
-                bool fAllMine = true;
-                BOOST_FOREACH(const CTxOut& txout, wtx.vout)
-                    fAllMine = fAllMine && txout.IsMine();
-                BOOST_FOREACH(const CTxIn& txin, wtx.vin)
-                    fAllMine = fAllMine && txin.IsMine();
-
-                parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
+                else
+                {
+                    // Sent to IP, or other non-address transaction like OP_EVAL
+                    sub.type = TransactionRecord::SendToOther;
+                    sub.address = mapValue["to"];
+                }
+
+                int64 nValue = txout.nValue;
+                /* Add fee to first output */
+                if (nTxFee > 0)
+                {
+                    nValue += nTxFee;
+                    nTxFee = 0;
+                }
+                sub.debit = -nValue;
+
+                parts.append(sub);
             }
         }
+        else
+        {
+            //
+            // Mixed debit transaction, can't break down payees
+            //
+            parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
+        }
     }
 
     return parts;
@@ -184,17 +165,17 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
 
     // Sort order, unrecorded transactions sort to the top
     status.sortKey = strprintf("%010d-%01d-%010u-%03d",
-        (pindex ? pindex->nHeight : INT_MAX),
+        (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
         (wtx.IsCoinBase() ? 1 : 0),
         wtx.nTimeReceived,
         idx);
-    status.confirmed = wtx.IsConfirmed();
+    status.confirmed = wtx.IsTrusted();
     status.depth = wtx.GetDepthInMainChain();
     status.cur_num_blocks = nBestHeight;
 
     if (!wtx.IsFinal())
     {
-        if (wtx.nLockTime < 500000000)
+        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
         {
             status.status = TransactionStatus::OpenUntilBlock;
             status.open_for = nBestHeight - wtx.nLockTime;
@@ -211,7 +192,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
         {
             status.status = TransactionStatus::Offline;
         }
-        else if (status.depth < 6)
+        else if (status.depth < NumConfirmations)
         {
             status.status = TransactionStatus::Unconfirmed;
         }
@@ -224,6 +205,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
     // For generated transactions, determine maturity
     if(type == TransactionRecord::Generated)
     {
+/*
         int64 nCredit = wtx.GetCredit(true);
         if (nCredit == 0)
         {
@@ -246,6 +228,29 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
         {
             status.maturity = TransactionStatus::Mature;
         }
+*/
+
+        if (wtx.GetBlocksToMaturity() > 0)
+        {
+            status.maturity = TransactionStatus::Immature;
+
+            if (wtx.IsInMainChain())
+            {
+                status.matures_in = wtx.GetBlocksToMaturity();
+
+                // Check if the block was requested by anyone
+                if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
+                    status.maturity = TransactionStatus::MaturesWarning;
+            }
+            else
+            {
+                status.maturity = TransactionStatus::NotAccepted;
+            }
+        }
+        else
+        {
+            status.maturity = TransactionStatus::Mature;
+        }
     }
 }
 
@@ -253,3 +258,9 @@ bool TransactionRecord::statusUpdateNeeded()
 {
     return status.cur_num_blocks != nBestHeight;
 }
+
+std::string TransactionRecord::getTxID()
+{
+    return hash.ToString() + strprintf("-%03d", idx);
+}
+