neobytes/src/qt/transactionrecord.cpp
Oleg Girko df6d458b85 Backport Bitcoin PR#9260: Mrs Peacock in The Library with The Candlestick (killed main.{h,cpp}) (#1566)
* Remove orphan state wipe from UnloadBlockIndex.

As orphan state is now "network state", like in
d6ea737be19a0001e69e4e854eb1cef21523ea7a,

UnloadBlockIndex is only used during init if we end up reindexing
to clear our block state so that we can start over. However, at
that time no connections have been brought up as CConnman hasn't
been started yet, so all of the network processing state logic is
empty when its called.

* Move network-msg-processing code out of main to its own file

* Rename the remaining main.{h,cpp} to validation.{h,cpp}
2017-08-09 03:19:06 +03:00

333 lines
11 KiB
C++

// Copyright (c) 2011-2015 The Bitcoin Core developers
// Copyright (c) 2014-2017 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "transactionrecord.h"
#include "base58.h"
#include "consensus/consensus.h"
#include "validation.h"
#include "timedata.h"
#include "wallet/wallet.h"
#include "instantx.h"
#include "privatesend.h"
#include <stdint.h>
#include <boost/foreach.hpp>
/* Return positive answer if transaction should be shown in list.
*/
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
if (wtx.IsCoinBase())
{
// Ensures we show generated coins / mined transactions at depth 1
if (!wtx.IsInMainChain())
{
return false;
}
}
return true;
}
/*
* Decompose CWallet transaction to model transaction records.
*/
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
QList<TransactionRecord> parts;
int64_t nTime = wtx.GetTxTime();
CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
CAmount nNet = nCredit - nDebit;
uint256 hash = wtx.GetHash();
std::map<std::string, std::string> mapValue = wtx.mapValue;
if (nNet > 0 || wtx.IsCoinBase())
{
//
// Credit
//
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
isminetype mine = wallet->IsMine(txout);
if(mine)
{
TransactionRecord sub(hash, nTime);
CTxDestination address;
sub.idx = parts.size(); // sequence number
sub.credit = txout.nValue;
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
{
// Received by Dash Address
sub.type = TransactionRecord::RecvWithAddress;
sub.address = CBitcoinAddress(address).ToString();
}
else
{
// Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
sub.type = TransactionRecord::RecvFromOther;
sub.address = mapValue["from"];
}
if (wtx.IsCoinBase())
{
// Generated
sub.type = TransactionRecord::Generated;
}
parts.append(sub);
}
}
}
else
{
bool fAllFromMeDenom = true;
int nFromMe = 0;
bool involvesWatchAddress = false;
isminetype fAllFromMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
{
if(wallet->IsMine(txin)) {
fAllFromMeDenom = fAllFromMeDenom && wallet->IsDenominated(txin);
nFromMe++;
}
isminetype mine = wallet->IsMine(txin);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllFromMe > mine) fAllFromMe = mine;
}
isminetype fAllToMe = ISMINE_SPENDABLE;
bool fAllToMeDenom = true;
int nToMe = 0;
BOOST_FOREACH(const CTxOut& txout, wtx.vout) {
if(wallet->IsMine(txout)) {
fAllToMeDenom = fAllToMeDenom && wallet->IsDenominatedAmount(txout.nValue);
nToMe++;
}
isminetype mine = wallet->IsMine(txout);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllToMe > mine) fAllToMe = mine;
}
if(fAllFromMeDenom && fAllToMeDenom && nFromMe * nToMe) {
parts.append(TransactionRecord(hash, nTime, TransactionRecord::PrivateSendDenominate, "", -nDebit, nCredit));
parts.last().involvesWatchAddress = false; // maybe pass to TransactionRecord as constructor argument
}
else if (fAllFromMe && fAllToMe)
{
// Payment to self
// TODO: this section still not accurate but covers most cases,
// might need some additional work however
TransactionRecord sub(hash, nTime);
// Payment to self by default
sub.type = TransactionRecord::SendToSelf;
sub.address = "";
if(mapValue["DS"] == "1")
{
sub.type = TransactionRecord::PrivateSend;
CTxDestination address;
if (ExtractDestination(wtx.vout[0].scriptPubKey, address))
{
// Sent to Dash Address
sub.address = CBitcoinAddress(address).ToString();
}
else
{
// Sent to IP, or other non-address transaction like OP_EVAL
sub.address = mapValue["to"];
}
}
else
{
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
{
const CTxOut& txout = wtx.vout[nOut];
sub.idx = parts.size();
if(wallet->IsCollateralAmount(txout.nValue)) sub.type = TransactionRecord::PrivateSendMakeCollaterals;
if(wallet->IsDenominatedAmount(txout.nValue)) sub.type = TransactionRecord::PrivateSendCreateDenominations;
if(nDebit - wtx.GetValueOut() == CPrivateSend::GetCollateralAmount()) sub.type = TransactionRecord::PrivateSendCollateralPayment;
}
}
CAmount nChange = wtx.GetChange();
sub.debit = -(nDebit - nChange);
sub.credit = nCredit - nChange;
parts.append(sub);
parts.last().involvesWatchAddress = involvesWatchAddress; // maybe pass to TransactionRecord as constructor argument
}
else if (fAllFromMe)
{
//
// Debit
//
CAmount nTxFee = nDebit - wtx.GetValueOut();
for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
{
const CTxOut& txout = wtx.vout[nOut];
TransactionRecord sub(hash, nTime);
sub.idx = parts.size();
sub.involvesWatchAddress = involvesWatchAddress;
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;
}
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address))
{
// Sent to Dash Address
sub.type = TransactionRecord::SendToAddress;
sub.address = CBitcoinAddress(address).ToString();
}
else
{
// Sent to IP, or other non-address transaction like OP_EVAL
sub.type = TransactionRecord::SendToOther;
sub.address = mapValue["to"];
}
if(mapValue["DS"] == "1")
{
sub.type = TransactionRecord::PrivateSend;
}
CAmount 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));
parts.last().involvesWatchAddress = involvesWatchAddress;
}
}
return parts;
}
void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
AssertLockHeld(cs_main);
// Determine transaction status
// Find the block the tx is in
CBlockIndex* pindex = NULL;
BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock);
if (mi != mapBlockIndex.end())
pindex = (*mi).second;
// Sort order, unrecorded transactions sort to the top
status.sortKey = strprintf("%010d-%01d-%010u-%03d",
(pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
(wtx.IsCoinBase() ? 1 : 0),
wtx.nTimeReceived,
idx);
status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
status.depth = wtx.GetDepthInMainChain();
status.cur_num_blocks = chainActive.Height();
status.cur_num_ix_locks = nCompleteTXLocks;
if (!CheckFinalTx(wtx))
{
if (wtx.nLockTime < LOCKTIME_THRESHOLD)
{
status.status = TransactionStatus::OpenUntilBlock;
status.open_for = wtx.nLockTime - chainActive.Height();
}
else
{
status.status = TransactionStatus::OpenUntilDate;
status.open_for = wtx.nLockTime;
}
}
// For generated transactions, determine maturity
else if(type == TransactionRecord::Generated)
{
if (wtx.GetBlocksToMaturity() > 0)
{
status.status = 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.status = TransactionStatus::MaturesWarning;
}
else
{
status.status = TransactionStatus::NotAccepted;
}
}
else
{
status.status = TransactionStatus::Confirmed;
}
}
else
{
if (status.depth < 0)
{
status.status = TransactionStatus::Conflicted;
}
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
{
status.status = TransactionStatus::Offline;
}
else if (status.depth == 0)
{
status.status = TransactionStatus::Unconfirmed;
}
else if (status.depth < RecommendedNumConfirmations)
{
status.status = TransactionStatus::Confirming;
}
else
{
status.status = TransactionStatus::Confirmed;
}
}
}
bool TransactionRecord::statusUpdateNeeded()
{
AssertLockHeld(cs_main);
return status.cur_num_blocks != chainActive.Height() || status.cur_num_ix_locks != nCompleteTXLocks;
}
QString TransactionRecord::getTxID() const
{
return formatSubTxId(hash, idx);
}
QString TransactionRecord::formatSubTxId(const uint256 &hash, int vout)
{
return QString::fromStdString(hash.ToString() + strprintf("-%03d", vout));
}