df6d458b85
* 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}
333 lines
11 KiB
C++
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));
|
|
}
|
|
|