Remove direct bitcoin calls from qt transaction table files

This commit is contained in:
Russell Yanofsky 2017-04-18 16:42:30 -04:00 committed by John Newbery
parent 3cab2ce5f9
commit 58845587e1
16 changed files with 410 additions and 210 deletions

View File

@ -60,6 +60,7 @@ class NodeImpl : public Node
void initLogging() override { InitLogging(); } void initLogging() override { InitLogging(); }
void initParameterInteraction() override { InitParameterInteraction(); } void initParameterInteraction() override { InitParameterInteraction(); }
std::string getWarnings(const std::string& type) override { return GetWarnings(type); } std::string getWarnings(const std::string& type) override { return GetWarnings(type); }
uint32_t getLogCategories() override { return ::logCategories; }
bool baseInitialize() override bool baseInitialize() override
{ {
return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() && return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
@ -227,6 +228,11 @@ class NodeImpl : public Node
std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); } std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); }
void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); } void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); }
void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); } void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); }
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{
LOCK(::cs_main);
return ::pcoinsTip->GetCoin(output, coin);
}
std::vector<std::unique_ptr<Wallet>> getWallets() override std::vector<std::unique_ptr<Wallet>> getWallets() override
{ {
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET

View File

@ -22,6 +22,7 @@
class CCoinControl; class CCoinControl;
class CFeeRate; class CFeeRate;
class CNodeStats; class CNodeStats;
class Coin;
class RPCTimerInterface; class RPCTimerInterface;
class UniValue; class UniValue;
class proxyType; class proxyType;
@ -66,6 +67,9 @@ public:
//! Get warnings. //! Get warnings.
virtual std::string getWarnings(const std::string& type) = 0; virtual std::string getWarnings(const std::string& type) = 0;
// Get log flags.
virtual uint32_t getLogCategories() = 0;
//! Initialize app dependencies. //! Initialize app dependencies.
virtual bool baseInitialize() = 0; virtual bool baseInitialize() = 0;
@ -184,6 +188,9 @@ public:
//! Unset RPC timer interface. //! Unset RPC timer interface.
virtual void rpcUnsetTimerInterface(RPCTimerInterface* iface) = 0; virtual void rpcUnsetTimerInterface(RPCTimerInterface* iface) = 0;
//! Get unspent outputs associated with a transaction.
virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0;
//! Return interfaces for accessing wallets (if any). //! Return interfaces for accessing wallets (if any).
virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0; virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;

View File

@ -15,6 +15,7 @@
#include <script/standard.h> #include <script/standard.h>
#include <support/allocators/secure.h> #include <support/allocators/secure.h>
#include <sync.h> #include <sync.h>
#include <timedata.h>
#include <ui_interface.h> #include <ui_interface.h>
#include <uint256.h> #include <uint256.h>
#include <validation.h> #include <validation.h>
@ -54,6 +55,54 @@ public:
CReserveKey m_key; CReserveKey m_key;
}; };
//! Construct wallet tx struct.
WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
{
WalletTx result;
result.tx = wtx.tx;
result.txin_is_mine.reserve(wtx.tx->vin.size());
for (const auto& txin : wtx.tx->vin) {
result.txin_is_mine.emplace_back(wallet.IsMine(txin));
}
result.txout_is_mine.reserve(wtx.tx->vout.size());
result.txout_address.reserve(wtx.tx->vout.size());
result.txout_address_is_mine.reserve(wtx.tx->vout.size());
for (const auto& txout : wtx.tx->vout) {
result.txout_is_mine.emplace_back(wallet.IsMine(txout));
result.txout_address.emplace_back();
result.txout_address_is_mine.emplace_back(ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ?
IsMine(wallet, result.txout_address.back()) :
ISMINE_NO);
}
result.credit = wtx.GetCredit(ISMINE_ALL);
result.debit = wtx.GetDebit(ISMINE_ALL);
result.change = wtx.GetChange();
result.time = wtx.GetTxTime();
result.value_map = wtx.mapValue;
result.is_coinbase = wtx.IsCoinBase();
return result;
}
//! Construct wallet tx status struct.
WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx)
{
WalletTxStatus result;
auto mi = ::mapBlockIndex.find(wtx.hashBlock);
CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr;
result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max()),
result.blocks_to_maturity = wtx.GetBlocksToMaturity();
result.depth_in_main_chain = wtx.GetDepthInMainChain();
result.request_count = wtx.GetRequestCount();
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
result.is_final = CheckFinalTx(*wtx.tx);
result.is_trusted = wtx.IsTrusted();
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
result.is_in_main_chain = wtx.IsInMainChain();
return result;
}
//! Construct wallet TxOut struct. //! Construct wallet TxOut struct.
WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth) WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth)
{ {
@ -205,6 +254,75 @@ public:
return feebumper::CommitTransaction(&m_wallet, txid, std::move(mtx), errors, bumped_txid) == return feebumper::CommitTransaction(&m_wallet, txid, std::move(mtx), errors, bumped_txid) ==
feebumper::Result::OK; feebumper::Result::OK;
} }
CTransactionRef getTx(const uint256& txid) override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
auto mi = m_wallet.mapWallet.find(txid);
if (mi != m_wallet.mapWallet.end()) {
return mi->second.tx;
}
return {};
}
WalletTx getWalletTx(const uint256& txid) override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
auto mi = m_wallet.mapWallet.find(txid);
if (mi != m_wallet.mapWallet.end()) {
return MakeWalletTx(m_wallet, mi->second);
}
return {};
}
std::vector<WalletTx> getWalletTxs() override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
std::vector<WalletTx> result;
result.reserve(m_wallet.mapWallet.size());
for (const auto& entry : m_wallet.mapWallet) {
result.emplace_back(MakeWalletTx(m_wallet, entry.second));
}
return result;
}
bool tryGetTxStatus(const uint256& txid,
interface::WalletTxStatus& tx_status,
int& num_blocks,
int64_t& adjusted_time) override
{
TRY_LOCK(::cs_main, locked_chain);
if (!locked_chain) {
return false;
}
TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
if (!locked_wallet) {
return false;
}
auto mi = m_wallet.mapWallet.find(txid);
if (mi == m_wallet.mapWallet.end()) {
return false;
}
num_blocks = ::chainActive.Height();
adjusted_time = GetAdjustedTime();
tx_status = MakeWalletTxStatus(mi->second);
return true;
}
WalletTx getWalletTxDetails(const uint256& txid,
WalletTxStatus& tx_status,
WalletOrderForm& order_form,
bool& in_mempool,
int& num_blocks,
int64_t& adjusted_time) override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
auto mi = m_wallet.mapWallet.find(txid);
if (mi != m_wallet.mapWallet.end()) {
num_blocks = ::chainActive.Height();
adjusted_time = GetAdjustedTime();
in_mempool = mi->second.InMempool();
order_form = mi->second.vOrderForm;
tx_status = MakeWalletTxStatus(mi->second);
return MakeWalletTx(m_wallet, mi->second);
}
return {};
}
WalletBalances getBalances() override WalletBalances getBalances() override
{ {
WalletBalances result; WalletBalances result;
@ -236,6 +354,26 @@ public:
{ {
return m_wallet.GetAvailableBalance(&coin_control); return m_wallet.GetAvailableBalance(&coin_control);
} }
isminetype txinIsMine(const CTxIn& txin) override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
return m_wallet.IsMine(txin);
}
isminetype txoutIsMine(const CTxOut& txout) override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
return m_wallet.IsMine(txout);
}
CAmount getDebit(const CTxIn& txin, isminefilter filter) override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
return m_wallet.GetDebit(txin, filter);
}
CAmount getCredit(const CTxOut& txout, isminefilter filter) override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
return m_wallet.GetCredit(txout, filter);
}
CoinsList listCoins() override CoinsList listCoins() override
{ {
LOCK2(::cs_main, m_wallet.cs_wallet); LOCK2(::cs_main, m_wallet.cs_wallet);

View File

@ -33,7 +33,9 @@ class Handler;
class PendingWalletTx; class PendingWalletTx;
struct WalletAddress; struct WalletAddress;
struct WalletBalances; struct WalletBalances;
struct WalletTx;
struct WalletTxOut; struct WalletTxOut;
struct WalletTxStatus;
using WalletOrderForm = std::vector<std::pair<std::string, std::string>>; using WalletOrderForm = std::vector<std::pair<std::string, std::string>>;
using WalletValueMap = std::map<std::string, std::string>; using WalletValueMap = std::map<std::string, std::string>;
@ -158,6 +160,29 @@ public:
std::vector<std::string>& errors, std::vector<std::string>& errors,
uint256& bumped_txid) = 0; uint256& bumped_txid) = 0;
//! Get a transaction.
virtual CTransactionRef getTx(const uint256& txid) = 0;
//! Get transaction information.
virtual WalletTx getWalletTx(const uint256& txid) = 0;
//! Get list of all wallet transactions.
virtual std::vector<WalletTx> getWalletTxs() = 0;
//! Try to get updated status for a particular transaction, if possible without blocking.
virtual bool tryGetTxStatus(const uint256& txid,
WalletTxStatus& tx_status,
int& num_blocks,
int64_t& adjusted_time) = 0;
//! Get transaction details.
virtual WalletTx getWalletTxDetails(const uint256& txid,
WalletTxStatus& tx_status,
WalletOrderForm& order_form,
bool& in_mempool,
int& num_blocks,
int64_t& adjusted_time) = 0;
//! Get balances. //! Get balances.
virtual WalletBalances getBalances() = 0; virtual WalletBalances getBalances() = 0;
@ -170,6 +195,18 @@ public:
//! Get available balance. //! Get available balance.
virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0; virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0;
//! Return whether transaction input belongs to wallet.
virtual isminetype txinIsMine(const CTxIn& txin) = 0;
//! Return whether transaction output belongs to wallet.
virtual isminetype txoutIsMine(const CTxOut& txout) = 0;
//! Return debit amount if transaction input belongs to wallet.
virtual CAmount getDebit(const CTxIn& txin, isminefilter filter) = 0;
//! Return credit amount if transaction input belongs to wallet.
virtual CAmount getCredit(const CTxOut& txout, isminefilter filter) = 0;
//! Return AvailableCoins + LockedCoins grouped by wallet address. //! Return AvailableCoins + LockedCoins grouped by wallet address.
//! (put change in one group with wallet address) //! (put change in one group with wallet address)
using CoinsList = std::map<CTxDestination, std::vector<std::tuple<COutPoint, WalletTxOut>>>; using CoinsList = std::map<CTxDestination, std::vector<std::tuple<COutPoint, WalletTxOut>>>;
@ -265,6 +302,38 @@ struct WalletBalances
} }
}; };
// Wallet transaction information.
struct WalletTx
{
CTransactionRef tx;
std::vector<isminetype> txin_is_mine;
std::vector<isminetype> txout_is_mine;
std::vector<CTxDestination> txout_address;
std::vector<isminetype> txout_address_is_mine;
CAmount credit;
CAmount debit;
CAmount change;
int64_t time;
std::map<std::string, std::string> value_map;
bool is_coinbase;
};
//! Updated transaction status.
struct WalletTxStatus
{
int block_height;
int blocks_to_maturity;
int depth_in_main_chain;
int request_count;
unsigned int time_received;
uint32_t lock_time;
bool is_final;
bool is_trusted;
bool is_abandoned;
bool is_coinbase;
bool is_in_main_chain;
};
//! Wallet transaction output. //! Wallet transaction output.
struct WalletTxOut struct WalletTxOut
{ {

View File

@ -37,7 +37,6 @@
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
#include <wallet/init.h> #include <wallet/init.h>
#include <wallet/wallet.h>
#endif #endif
#include <walletinitinterface.h> #include <walletinitinterface.h>
@ -466,9 +465,8 @@ void BitcoinApplication::initializeResult(bool success)
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
bool fFirstWallet = true; bool fFirstWallet = true;
auto wallets = m_node.getWallets(); auto wallets = m_node.getWallets();
auto cwallet = ::vpwallets.begin();
for (auto& wallet : wallets) { for (auto& wallet : wallets) {
WalletModel * const walletModel = new WalletModel(std::move(wallet), m_node, platformStyle, *cwallet++, optionsModel); WalletModel * const walletModel = new WalletModel(std::move(wallet), m_node, platformStyle, optionsModel);
window->addWallet(walletModel); window->addWallet(walletModel);
if (fFirstWallet) { if (fFirstWallet) {

View File

@ -12,10 +12,9 @@
#include <streams.h> #include <streams.h>
RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) : RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) :
QAbstractTableModel(parent), walletModel(parent) QAbstractTableModel(parent), walletModel(parent)
{ {
Q_UNUSED(wallet);
nReceiveRequestsMaxId = 0; nReceiveRequestsMaxId = 0;
// Load entries from wallet // Load entries from wallet

View File

@ -11,8 +11,6 @@
#include <QStringList> #include <QStringList>
#include <QDateTime> #include <QDateTime>
class CWallet;
class RecentRequestEntry class RecentRequestEntry
{ {
public: public:
@ -60,7 +58,7 @@ class RecentRequestsTableModel: public QAbstractTableModel
Q_OBJECT Q_OBJECT
public: public:
explicit RecentRequestsTableModel(CWallet *wallet, WalletModel *parent); explicit RecentRequestsTableModel(WalletModel *parent);
~RecentRequestsTableModel(); ~RecentRequestsTableModel();
enum ColumnIndex { enum ColumnIndex {

View File

@ -179,7 +179,7 @@ void TestGUI()
auto node = interface::MakeNode(); auto node = interface::MakeNode();
OptionsModel optionsModel(*node); OptionsModel optionsModel(*node);
vpwallets.insert(vpwallets.begin(), &wallet); vpwallets.insert(vpwallets.begin(), &wallet);
WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &wallet, &optionsModel); WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &optionsModel);
vpwallets.erase(vpwallets.begin()); vpwallets.erase(vpwallets.begin());
sendCoinsDialog.setModel(&walletModel); sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel); transactionView.setModel(&walletModel);

View File

@ -10,6 +10,7 @@
#include <qt/transactionrecord.h> #include <qt/transactionrecord.h>
#include <consensus/consensus.h> #include <consensus/consensus.h>
#include <interface/node.h>
#include <key_io.h> #include <key_io.h>
#include <validation.h> #include <validation.h>
#include <script/script.h> #include <script/script.h>
@ -22,25 +23,24 @@
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) QString TransactionDesc::FormatTxStatus(const interface::WalletTx& wtx, const interface::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime)
{ {
AssertLockHeld(cs_main); if (!status.is_final)
if (!CheckFinalTx(*wtx.tx))
{ {
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height()); return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - numBlocks);
else else
return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime)); return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime));
} }
else else
{ {
int nDepth = wtx.GetDepthInMainChain(); int nDepth = status.depth_in_main_chain;
if (nDepth < 0) if (nDepth < 0)
return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth); return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth);
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) else if (adjustedTime - status.time_received > 2 * 60 && status.request_count == 0)
return tr("%1/offline").arg(nDepth); return tr("%1/offline").arg(nDepth);
else if (nDepth == 0) else if (nDepth == 0)
return tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx.isAbandoned() ? ", "+tr("abandoned") : ""); return tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + (status.is_abandoned ? ", "+tr("abandoned") : "");
else if (nDepth < 6) else if (nDepth < 6)
return tr("%1/unconfirmed").arg(nDepth); return tr("%1/unconfirmed").arg(nDepth);
else else
@ -48,21 +48,27 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
} }
} }
QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit) QString TransactionDesc::toHTML(interface::Node& node, interface::Wallet& wallet, TransactionRecord *rec, int unit)
{ {
int numBlocks;
int64_t adjustedTime;
interface::WalletTxStatus status;
interface::WalletOrderForm orderForm;
bool inMempool;
interface::WalletTx wtx = wallet.getWalletTxDetails(rec->hash, status, orderForm, inMempool, numBlocks, adjustedTime);
QString strHTML; QString strHTML;
LOCK2(cs_main, wallet->cs_wallet);
strHTML.reserve(4000); strHTML.reserve(4000);
strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>"; strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
int64_t nTime = wtx.GetTxTime(); int64_t nTime = wtx.time;
CAmount nCredit = wtx.GetCredit(ISMINE_ALL); CAmount nCredit = wtx.credit;
CAmount nDebit = wtx.GetDebit(ISMINE_ALL); CAmount nDebit = wtx.debit;
CAmount nNet = nCredit - nDebit; CAmount nNet = nCredit - nDebit;
strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx); strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks, adjustedTime);
int nRequests = wtx.GetRequestCount(); int nRequests = status.request_count;
if (nRequests != -1) if (nRequests != -1)
{ {
if (nRequests == 0) if (nRequests == 0)
@ -77,14 +83,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// //
// From // From
// //
if (wtx.IsCoinBase()) if (wtx.is_coinbase)
{ {
strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>"; strHTML += "<b>" + tr("Source") + ":</b> " + tr("Generated") + "<br>";
} }
else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) else if (wtx.value_map.count("from") && !wtx.value_map["from"].empty())
{ {
// Online transaction // Online transaction
strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "<br>"; strHTML += "<b>" + tr("From") + ":</b> " + GUIUtil::HtmlEscape(wtx.value_map["from"]) + "<br>";
} }
else else
{ {
@ -94,14 +100,16 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Credit // Credit
CTxDestination address = DecodeDestination(rec->address); CTxDestination address = DecodeDestination(rec->address);
if (IsValidDestination(address)) { if (IsValidDestination(address)) {
if (wallet->mapAddressBook.count(address)) std::string name;
isminetype ismine;
if (wallet.getAddress(address, &name, &ismine))
{ {
strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
strHTML += "<b>" + tr("To") + ":</b> "; strHTML += "<b>" + tr("To") + ":</b> ";
strHTML += GUIUtil::HtmlEscape(rec->address); strHTML += GUIUtil::HtmlEscape(rec->address);
QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only"); QString addressOwned = ismine == ISMINE_SPENDABLE ? tr("own address") : tr("watch-only");
if (!wallet->mapAddressBook[address].name.empty()) if (!name.empty())
strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")"; strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(name) + ")";
else else
strHTML += " (" + addressOwned + ")"; strHTML += " (" + addressOwned + ")";
strHTML += "<br>"; strHTML += "<br>";
@ -113,31 +121,32 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// //
// To // To
// //
if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty()) if (wtx.value_map.count("to") && !wtx.value_map["to"].empty())
{ {
// Online transaction // Online transaction
std::string strAddress = wtx.mapValue["to"]; std::string strAddress = wtx.value_map["to"];
strHTML += "<b>" + tr("To") + ":</b> "; strHTML += "<b>" + tr("To") + ":</b> ";
CTxDestination dest = DecodeDestination(strAddress); CTxDestination dest = DecodeDestination(strAddress);
if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty()) std::string name;
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " "; if (wallet.getAddress(dest, &name) && !name.empty())
strHTML += GUIUtil::HtmlEscape(name) + " ";
strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>"; strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
} }
// //
// Amount // Amount
// //
if (wtx.IsCoinBase() && nCredit == 0) if (wtx.is_coinbase && nCredit == 0)
{ {
// //
// Coinbase // Coinbase
// //
CAmount nUnmatured = 0; CAmount nUnmatured = 0;
for (const CTxOut& txout : wtx.tx->vout) for (const CTxOut& txout : wtx.tx->vout)
nUnmatured += wallet->GetCredit(txout, ISMINE_ALL); nUnmatured += wallet.getCredit(txout, ISMINE_ALL);
strHTML += "<b>" + tr("Credit") + ":</b> "; strHTML += "<b>" + tr("Credit") + ":</b> ";
if (wtx.IsInMainChain()) if (status.is_in_main_chain)
strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", status.blocks_to_maturity) + ")";
else else
strHTML += "(" + tr("not accepted") + ")"; strHTML += "(" + tr("not accepted") + ")";
strHTML += "<br>"; strHTML += "<br>";
@ -152,16 +161,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
else else
{ {
isminetype fAllFromMe = ISMINE_SPENDABLE; isminetype fAllFromMe = ISMINE_SPENDABLE;
for (const CTxIn& txin : wtx.tx->vin) for (isminetype mine : wtx.txin_is_mine)
{ {
isminetype mine = wallet->IsMine(txin);
if(fAllFromMe > mine) fAllFromMe = mine; if(fAllFromMe > mine) fAllFromMe = mine;
} }
isminetype fAllToMe = ISMINE_SPENDABLE; isminetype fAllToMe = ISMINE_SPENDABLE;
for (const CTxOut& txout : wtx.tx->vout) for (isminetype mine : wtx.txout_is_mine)
{ {
isminetype mine = wallet->IsMine(txout);
if(fAllToMe > mine) fAllToMe = mine; if(fAllToMe > mine) fAllToMe = mine;
} }
@ -173,22 +180,24 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// //
// Debit // Debit
// //
auto mine = wtx.txout_is_mine.begin();
for (const CTxOut& txout : wtx.tx->vout) for (const CTxOut& txout : wtx.tx->vout)
{ {
// Ignore change // Ignore change
isminetype toSelf = wallet->IsMine(txout); isminetype toSelf = *(mine++);
if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE)) if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE))
continue; continue;
if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty()) if (!wtx.value_map.count("to") || wtx.value_map["to"].empty())
{ {
// Offline transaction // Offline transaction
CTxDestination address; CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address)) if (ExtractDestination(txout.scriptPubKey, address))
{ {
strHTML += "<b>" + tr("To") + ":</b> "; strHTML += "<b>" + tr("To") + ":</b> ";
if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) std::string name;
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; if (wallet.getAddress(address, &name) && !name.empty())
strHTML += GUIUtil::HtmlEscape(name) + " ";
strHTML += GUIUtil::HtmlEscape(EncodeDestination(address)); strHTML += GUIUtil::HtmlEscape(EncodeDestination(address));
if(toSelf == ISMINE_SPENDABLE) if(toSelf == ISMINE_SPENDABLE)
strHTML += " (own address)"; strHTML += " (own address)";
@ -206,7 +215,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
if (fAllToMe) if (fAllToMe)
{ {
// Payment to self // Payment to self
CAmount nChange = wtx.GetChange(); CAmount nChange = wtx.change;
CAmount nValue = nCredit - nChange; CAmount nValue = nCredit - nChange;
strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "<br>"; strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "<br>";
strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>"; strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
@ -221,12 +230,18 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// //
// Mixed debit transaction // Mixed debit transaction
// //
for (const CTxIn& txin : wtx.tx->vin) auto mine = wtx.txin_is_mine.begin();
if (wallet->IsMine(txin)) for (const CTxIn& txin : wtx.tx->vin) {
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; if (*(mine++)) {
for (const CTxOut& txout : wtx.tx->vout) strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin, ISMINE_ALL)) + "<br>";
if (wallet->IsMine(txout)) }
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; }
mine = wtx.txout_is_mine.begin();
for (const CTxOut& txout : wtx.tx->vout) {
if (*(mine++)) {
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout, ISMINE_ALL)) + "<br>";
}
}
} }
} }
@ -235,10 +250,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// //
// Message // Message
// //
if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty()) if (wtx.value_map.count("message") && !wtx.value_map["message"].empty())
strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "<br>"; strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.value_map["message"], true) + "<br>";
if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty()) if (wtx.value_map.count("comment") && !wtx.value_map["comment"].empty())
strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "<br>"; strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.value_map["comment"], true) + "<br>";
strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxHash() + "<br>"; strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxHash() + "<br>";
strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->GetTotalSize()) + " bytes<br>"; strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->GetTotalSize()) + " bytes<br>";
@ -246,14 +261,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>"; strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>";
// Message from normal bitcoin:URI (bitcoin:123...?message=example) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
for (const std::pair<std::string, std::string>& r : wtx.vOrderForm) for (const std::pair<std::string, std::string>& r : orderForm)
if (r.first == "Message") if (r.first == "Message")
strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r.second, true) + "<br>"; strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r.second, true) + "<br>";
// //
// PaymentRequest info: // PaymentRequest info:
// //
for (const std::pair<std::string, std::string>& r : wtx.vOrderForm) for (const std::pair<std::string, std::string>& r : orderForm)
{ {
if (r.first == "PaymentRequest") if (r.first == "PaymentRequest")
{ {
@ -265,7 +280,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
} }
} }
if (wtx.IsCoinBase()) if (wtx.is_coinbase)
{ {
quint32 numBlocksToMaturity = COINBASE_MATURITY + 1; quint32 numBlocksToMaturity = COINBASE_MATURITY + 1;
strHTML += "<br>" + tr("Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "<br>"; strHTML += "<br>" + tr("Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "<br>";
@ -274,15 +289,15 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// //
// Debug view // Debug view
// //
if (logCategories != BCLog::NONE) if (node.getLogCategories() != BCLog::NONE)
{ {
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>"; strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
for (const CTxIn& txin : wtx.tx->vin) for (const CTxIn& txin : wtx.tx->vin)
if(wallet->IsMine(txin)) if(wallet.txinIsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin, ISMINE_ALL)) + "<br>";
for (const CTxOut& txout : wtx.tx->vout) for (const CTxOut& txout : wtx.tx->vout)
if(wallet->IsMine(txout)) if(wallet.txoutIsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout, ISMINE_ALL)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>"; strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true); strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true);
@ -295,7 +310,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
COutPoint prevout = txin.prevout; COutPoint prevout = txin.prevout;
Coin prev; Coin prev;
if(pcoinsTip->GetCoin(prevout, prev)) if(node.getUnspentOutput(prevout, prev))
{ {
{ {
strHTML += "<li>"; strHTML += "<li>";
@ -303,13 +318,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
CTxDestination address; CTxDestination address;
if (ExtractDestination(vout.scriptPubKey, address)) if (ExtractDestination(vout.scriptPubKey, address))
{ {
if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) std::string name;
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; if (wallet.getAddress(address, &name) && !name.empty())
strHTML += GUIUtil::HtmlEscape(name) + " ";
strHTML += QString::fromStdString(EncodeDestination(address)); strHTML += QString::fromStdString(EncodeDestination(address));
} }
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>"; strHTML = strHTML + " IsMine=" + (wallet.txoutIsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>"; strHTML = strHTML + " IsWatchOnly=" + (wallet.txoutIsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
} }
} }
} }

View File

@ -10,8 +10,12 @@
class TransactionRecord; class TransactionRecord;
class CWallet; namespace interface {
class CWalletTx; class Node;
class Wallet;
struct WalletTx;
struct WalletTxStatus;
}
/** Provide a human-readable extended HTML description of a transaction. /** Provide a human-readable extended HTML description of a transaction.
*/ */
@ -20,12 +24,12 @@ class TransactionDesc: public QObject
Q_OBJECT Q_OBJECT
public: public:
static QString toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit); static QString toHTML(interface::Node& node, interface::Wallet& wallet, TransactionRecord *rec, int unit);
private: private:
TransactionDesc() {} TransactionDesc() {}
static QString FormatTxStatus(const CWalletTx& wtx); static QString FormatTxStatus(const interface::WalletTx& wtx, const interface::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime);
}; };
#endif // BITCOIN_QT_TRANSACTIONDESC_H #endif // BITCOIN_QT_TRANSACTIONDESC_H

View File

@ -5,17 +5,17 @@
#include <qt/transactionrecord.h> #include <qt/transactionrecord.h>
#include <consensus/consensus.h> #include <consensus/consensus.h>
#include <interface/wallet.h>
#include <key_io.h> #include <key_io.h>
#include <validation.h>
#include <timedata.h> #include <timedata.h>
#include <wallet/wallet.h> #include <validation.h>
#include <stdint.h> #include <stdint.h>
/* Return positive answer if transaction should be shown in list. /* Return positive answer if transaction should be shown in list.
*/ */
bool TransactionRecord::showTransaction(const CWalletTx &wtx) bool TransactionRecord::showTransaction()
{ {
// There are currently no cases where we hide transactions, but // There are currently no cases where we hide transactions, but
// we may want to use this in the future for things like RBF. // we may want to use this in the future for things like RBF.
@ -25,17 +25,17 @@ bool TransactionRecord::showTransaction(const CWalletTx &wtx)
/* /*
* Decompose CWallet transaction to model transaction records. * Decompose CWallet transaction to model transaction records.
*/ */
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface::WalletTx& wtx)
{ {
QList<TransactionRecord> parts; QList<TransactionRecord> parts;
int64_t nTime = wtx.GetTxTime(); int64_t nTime = wtx.time;
CAmount nCredit = wtx.GetCredit(ISMINE_ALL); CAmount nCredit = wtx.credit;
CAmount nDebit = wtx.GetDebit(ISMINE_ALL); CAmount nDebit = wtx.debit;
CAmount nNet = nCredit - nDebit; CAmount nNet = nCredit - nDebit;
uint256 hash = wtx.GetHash(); uint256 hash = wtx.tx->GetHash();
std::map<std::string, std::string> mapValue = wtx.mapValue; std::map<std::string, std::string> mapValue = wtx.value_map;
if (nNet > 0 || wtx.IsCoinBase()) if (nNet > 0 || wtx.is_coinbase)
{ {
// //
// Credit // Credit
@ -43,7 +43,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++) for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{ {
const CTxOut& txout = wtx.tx->vout[i]; const CTxOut& txout = wtx.tx->vout[i];
isminetype mine = wallet->IsMine(txout); isminetype mine = wtx.txout_is_mine[i];
if(mine) if(mine)
{ {
TransactionRecord sub(hash, nTime); TransactionRecord sub(hash, nTime);
@ -51,11 +51,11 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
sub.idx = i; // vout index sub.idx = i; // vout index
sub.credit = txout.nValue; sub.credit = txout.nValue;
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY; sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) if (wtx.txout_address_is_mine[i])
{ {
// Received by Bitcoin Address // Received by Bitcoin Address
sub.type = TransactionRecord::RecvWithAddress; sub.type = TransactionRecord::RecvWithAddress;
sub.address = EncodeDestination(address); sub.address = EncodeDestination(wtx.txout_address[i]);
} }
else else
{ {
@ -63,7 +63,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
sub.type = TransactionRecord::RecvFromOther; sub.type = TransactionRecord::RecvFromOther;
sub.address = mapValue["from"]; sub.address = mapValue["from"];
} }
if (wtx.IsCoinBase()) if (wtx.is_coinbase)
{ {
// Generated // Generated
sub.type = TransactionRecord::Generated; sub.type = TransactionRecord::Generated;
@ -77,17 +77,15 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{ {
bool involvesWatchAddress = false; bool involvesWatchAddress = false;
isminetype fAllFromMe = ISMINE_SPENDABLE; isminetype fAllFromMe = ISMINE_SPENDABLE;
for (const CTxIn& txin : wtx.tx->vin) for (isminetype mine : wtx.txin_is_mine)
{ {
isminetype mine = wallet->IsMine(txin);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllFromMe > mine) fAllFromMe = mine; if(fAllFromMe > mine) fAllFromMe = mine;
} }
isminetype fAllToMe = ISMINE_SPENDABLE; isminetype fAllToMe = ISMINE_SPENDABLE;
for (const CTxOut& txout : wtx.tx->vout) for (isminetype mine : wtx.txout_is_mine)
{ {
isminetype mine = wallet->IsMine(txout);
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllToMe > mine) fAllToMe = mine; if(fAllToMe > mine) fAllToMe = mine;
} }
@ -95,7 +93,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
if (fAllFromMe && fAllToMe) if (fAllFromMe && fAllToMe)
{ {
// Payment to self // Payment to self
CAmount nChange = wtx.GetChange(); CAmount nChange = wtx.change;
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
-(nDebit - nChange), nCredit - nChange)); -(nDebit - nChange), nCredit - nChange));
@ -115,19 +113,18 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
sub.idx = nOut; sub.idx = nOut;
sub.involvesWatchAddress = involvesWatchAddress; sub.involvesWatchAddress = involvesWatchAddress;
if(wallet->IsMine(txout)) if(wtx.txout_is_mine[nOut])
{ {
// Ignore parts sent to self, as this is usually the change // Ignore parts sent to self, as this is usually the change
// from a transaction sent back to our own address. // from a transaction sent back to our own address.
continue; continue;
} }
CTxDestination address; if (!boost::get<CNoDestination>(&wtx.txout_address[nOut]))
if (ExtractDestination(txout.scriptPubKey, address))
{ {
// Sent to Bitcoin Address // Sent to Bitcoin Address
sub.type = TransactionRecord::SendToAddress; sub.type = TransactionRecord::SendToAddress;
sub.address = EncodeDestination(address); sub.address = EncodeDestination(wtx.txout_address[nOut]);
} }
else else
{ {
@ -161,50 +158,46 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
return parts; return parts;
} }
void TransactionRecord::updateStatus(const CWalletTx &wtx) void TransactionRecord::updateStatus(const interface::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime)
{ {
AssertLockHeld(cs_main);
// Determine transaction status // Determine transaction status
// Find the block the tx is in
const CBlockIndex* pindex = LookupBlockIndex(wtx.hashBlock);
// Sort order, unrecorded transactions sort to the top // Sort order, unrecorded transactions sort to the top
status.sortKey = strprintf("%010d-%01d-%010u-%03d", status.sortKey = strprintf("%010d-%01d-%010u-%03d",
(pindex ? pindex->nHeight : std::numeric_limits<int>::max()), wtx.block_height,
(wtx.IsCoinBase() ? 1 : 0), wtx.is_coinbase ? 1 : 0,
wtx.nTimeReceived, wtx.time_received,
idx); idx);
status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0); status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
status.depth = wtx.GetDepthInMainChain(); status.depth = wtx.depth_in_main_chain;
status.cur_num_blocks = chainActive.Height(); status.cur_num_blocks = numBlocks;
if (!CheckFinalTx(*wtx.tx)) if (!wtx.is_final)
{ {
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) if (wtx.lock_time < LOCKTIME_THRESHOLD)
{ {
status.status = TransactionStatus::OpenUntilBlock; status.status = TransactionStatus::OpenUntilBlock;
status.open_for = wtx.tx->nLockTime - chainActive.Height(); status.open_for = wtx.lock_time - numBlocks;
} }
else else
{ {
status.status = TransactionStatus::OpenUntilDate; status.status = TransactionStatus::OpenUntilDate;
status.open_for = wtx.tx->nLockTime; status.open_for = wtx.lock_time;
} }
} }
// For generated transactions, determine maturity // For generated transactions, determine maturity
else if(type == TransactionRecord::Generated) else if(type == TransactionRecord::Generated)
{ {
if (wtx.GetBlocksToMaturity() > 0) if (wtx.blocks_to_maturity > 0)
{ {
status.status = TransactionStatus::Immature; status.status = TransactionStatus::Immature;
if (wtx.IsInMainChain()) if (wtx.is_in_main_chain)
{ {
status.matures_in = wtx.GetBlocksToMaturity(); status.matures_in = wtx.blocks_to_maturity;
// Check if the block was requested by anyone // Check if the block was requested by anyone
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0)
status.status = TransactionStatus::MaturesWarning; status.status = TransactionStatus::MaturesWarning;
} }
else else
@ -223,14 +216,14 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
{ {
status.status = TransactionStatus::Conflicted; status.status = TransactionStatus::Conflicted;
} }
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) else if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0)
{ {
status.status = TransactionStatus::Offline; status.status = TransactionStatus::Offline;
} }
else if (status.depth == 0) else if (status.depth == 0)
{ {
status.status = TransactionStatus::Unconfirmed; status.status = TransactionStatus::Unconfirmed;
if (wtx.isAbandoned()) if (wtx.is_abandoned)
status.status = TransactionStatus::Abandoned; status.status = TransactionStatus::Abandoned;
} }
else if (status.depth < RecommendedNumConfirmations) else if (status.depth < RecommendedNumConfirmations)
@ -245,10 +238,9 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
status.needsUpdate = false; status.needsUpdate = false;
} }
bool TransactionRecord::statusUpdateNeeded() const bool TransactionRecord::statusUpdateNeeded(int numBlocks) const
{ {
AssertLockHeld(cs_main); return status.cur_num_blocks != numBlocks || status.needsUpdate;
return status.cur_num_blocks != chainActive.Height() || status.needsUpdate;
} }
QString TransactionRecord::getTxHash() const QString TransactionRecord::getTxHash() const

View File

@ -11,8 +11,12 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
class CWallet; namespace interface {
class CWalletTx; class Node;
class Wallet;
struct WalletTx;
struct WalletTxStatus;
}
/** UI model for transaction status. The transaction status is the part of a transaction that will change over time. /** UI model for transaction status. The transaction status is the part of a transaction that will change over time.
*/ */
@ -106,8 +110,8 @@ public:
/** Decompose CWallet transaction to model transaction records. /** Decompose CWallet transaction to model transaction records.
*/ */
static bool showTransaction(const CWalletTx &wtx); static bool showTransaction();
static QList<TransactionRecord> decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx); static QList<TransactionRecord> decomposeTransaction(const interface::WalletTx& wtx);
/** @name Immutable transaction attributes /** @name Immutable transaction attributes
@{*/ @{*/
@ -136,11 +140,11 @@ public:
/** Update status from core wallet tx. /** Update status from core wallet tx.
*/ */
void updateStatus(const CWalletTx &wtx); void updateStatus(const interface::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime);
/** Return whether a status update is needed. /** Return whether a status update is needed.
*/ */
bool statusUpdateNeeded() const; bool statusUpdateNeeded(int numBlocks) const;
}; };
#endif // BITCOIN_QT_TRANSACTIONRECORD_H #endif // BITCOIN_QT_TRANSACTIONRECORD_H

View File

@ -14,11 +14,12 @@
#include <qt/walletmodel.h> #include <qt/walletmodel.h>
#include <core_io.h> #include <core_io.h>
#include <interface/handler.h>
#include <interface/node.h>
#include <validation.h> #include <validation.h>
#include <sync.h> #include <sync.h>
#include <uint256.h> #include <uint256.h>
#include <util.h> #include <util.h>
#include <wallet/wallet.h>
#include <QColor> #include <QColor>
#include <QDateTime> #include <QDateTime>
@ -57,13 +58,11 @@ struct TxLessThan
class TransactionTablePriv class TransactionTablePriv
{ {
public: public:
TransactionTablePriv(CWallet *_wallet, TransactionTableModel *_parent) : TransactionTablePriv(TransactionTableModel *_parent) :
wallet(_wallet),
parent(_parent) parent(_parent)
{ {
} }
CWallet *wallet;
TransactionTableModel *parent; TransactionTableModel *parent;
/* Local cache of wallet. /* Local cache of wallet.
@ -74,16 +73,15 @@ public:
/* Query entire wallet anew from core. /* Query entire wallet anew from core.
*/ */
void refreshWallet() void refreshWallet(interface::Wallet& wallet)
{ {
qDebug() << "TransactionTablePriv::refreshWallet"; qDebug() << "TransactionTablePriv::refreshWallet";
cachedWallet.clear(); cachedWallet.clear();
{ {
LOCK2(cs_main, wallet->cs_wallet); for (const auto& wtx : wallet.getWalletTxs()) {
for (const auto& entry : wallet->mapWallet) if (TransactionRecord::showTransaction()) {
{ cachedWallet.append(TransactionRecord::decomposeTransaction(wtx));
if (TransactionRecord::showTransaction(entry.second)) }
cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, entry.second));
} }
} }
} }
@ -93,7 +91,7 @@ public:
Call with transaction that was added, removed or changed. Call with transaction that was added, removed or changed.
*/ */
void updateWallet(const uint256 &hash, int status, bool showTransaction) void updateWallet(interface::Wallet& wallet, const uint256 &hash, int status, bool showTransaction)
{ {
qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status); qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);
@ -128,17 +126,16 @@ public:
} }
if(showTransaction) if(showTransaction)
{ {
LOCK2(cs_main, wallet->cs_wallet);
// Find transaction in wallet // Find transaction in wallet
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash); interface::WalletTx wtx = wallet.getWalletTx(hash);
if(mi == wallet->mapWallet.end()) if(!wtx.tx)
{ {
qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet"; qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet";
break; break;
} }
// Added -- insert at the right position // Added -- insert at the right position
QList<TransactionRecord> toInsert = QList<TransactionRecord> toInsert =
TransactionRecord::decomposeTransaction(wallet, mi->second); TransactionRecord::decomposeTransaction(wtx);
if(!toInsert.isEmpty()) /* only if something to insert */ if(!toInsert.isEmpty()) /* only if something to insert */
{ {
parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
@ -179,7 +176,7 @@ public:
return cachedWallet.size(); return cachedWallet.size();
} }
TransactionRecord *index(int idx) TransactionRecord *index(interface::Wallet& wallet, int idx)
{ {
if(idx >= 0 && idx < cachedWallet.size()) if(idx >= 0 && idx < cachedWallet.size())
{ {
@ -192,61 +189,42 @@ public:
// If a status update is needed (blocks came in since last check), // If a status update is needed (blocks came in since last check),
// update the status of this transaction from the wallet. Otherwise, // update the status of this transaction from the wallet. Otherwise,
// simply re-use the cached status. // simply re-use the cached status.
TRY_LOCK(cs_main, lockMain); interface::WalletTxStatus wtx;
if(lockMain) int numBlocks;
{ int64_t adjustedTime;
TRY_LOCK(wallet->cs_wallet, lockWallet); if (wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, adjustedTime) && rec->statusUpdateNeeded(numBlocks)) {
if(lockWallet && rec->statusUpdateNeeded()) rec->updateStatus(wtx, numBlocks, adjustedTime);
{
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
if(mi != wallet->mapWallet.end())
{
rec->updateStatus(mi->second);
}
}
} }
return rec; return rec;
} }
return 0; return 0;
} }
QString describe(TransactionRecord *rec, int unit) QString describe(interface::Node& node, interface::Wallet& wallet, TransactionRecord *rec, int unit)
{ {
{ return TransactionDesc::toHTML(node, wallet, rec, unit);
LOCK2(cs_main, wallet->cs_wallet);
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
if(mi != wallet->mapWallet.end())
{
return TransactionDesc::toHTML(wallet, mi->second, rec, unit);
}
}
return QString();
} }
QString getTxHex(TransactionRecord *rec) QString getTxHex(interface::Wallet& wallet, TransactionRecord *rec)
{ {
LOCK2(cs_main, wallet->cs_wallet); auto tx = wallet.getTx(rec->hash);
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash); if (tx) {
if(mi != wallet->mapWallet.end()) std::string strHex = EncodeHexTx(*tx);
{
std::string strHex = EncodeHexTx(*mi->second.tx);
return QString::fromStdString(strHex); return QString::fromStdString(strHex);
} }
return QString(); return QString();
} }
}; };
TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, CWallet* _wallet, WalletModel *parent): TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, WalletModel *parent):
QAbstractTableModel(parent), QAbstractTableModel(parent),
wallet(_wallet),
walletModel(parent), walletModel(parent),
priv(new TransactionTablePriv(_wallet, this)), priv(new TransactionTablePriv(this)),
fProcessingQueuedTransactions(false), fProcessingQueuedTransactions(false),
platformStyle(_platformStyle) platformStyle(_platformStyle)
{ {
columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
priv->refreshWallet(); priv->refreshWallet(walletModel->wallet());
connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
@ -271,7 +249,7 @@ void TransactionTableModel::updateTransaction(const QString &hash, int status, b
uint256 updated; uint256 updated;
updated.SetHex(hash.toStdString()); updated.SetHex(hash.toStdString());
priv->updateWallet(updated, status, showTransaction); priv->updateWallet(walletModel->wallet(), updated, status, showTransaction);
} }
void TransactionTableModel::updateConfirmations() void TransactionTableModel::updateConfirmations()
@ -608,7 +586,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case WatchonlyDecorationRole: case WatchonlyDecorationRole:
return txWatchonlyDecoration(rec); return txWatchonlyDecoration(rec);
case LongDescriptionRole: case LongDescriptionRole:
return priv->describe(rec, walletModel->getOptionsModel()->getDisplayUnit()); return priv->describe(walletModel->node(), walletModel->wallet(), rec, walletModel->getOptionsModel()->getDisplayUnit());
case AddressRole: case AddressRole:
return QString::fromStdString(rec->address); return QString::fromStdString(rec->address);
case LabelRole: case LabelRole:
@ -618,7 +596,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case TxHashRole: case TxHashRole:
return rec->getTxHash(); return rec->getTxHash();
case TxHexRole: case TxHexRole:
return priv->getTxHex(rec); return priv->getTxHex(walletModel->wallet(), rec);
case TxPlainTextRole: case TxPlainTextRole:
{ {
QString details; QString details;
@ -694,10 +672,10 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat
QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
TransactionRecord *data = priv->index(row); TransactionRecord *data = priv->index(walletModel->wallet(), row);
if(data) if(data)
{ {
return createIndex(row, column, priv->index(row)); return createIndex(row, column, priv->index(walletModel->wallet(), row));
} }
return QModelIndex(); return QModelIndex();
} }
@ -735,13 +713,11 @@ private:
static bool fQueueNotifications = false; static bool fQueueNotifications = false;
static std::vector< TransactionNotification > vQueueNotifications; static std::vector< TransactionNotification > vQueueNotifications;
static void NotifyTransactionChanged(TransactionTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status) static void NotifyTransactionChanged(TransactionTableModel *ttm, const uint256 &hash, ChangeType status)
{ {
// Find transaction in wallet // Find transaction in wallet
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
// Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread) // Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread)
bool inWallet = mi != wallet->mapWallet.end(); bool showTransaction = TransactionRecord::showTransaction();
bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second));
TransactionNotification notification(hash, status, showTransaction); TransactionNotification notification(hash, status, showTransaction);
@ -777,13 +753,13 @@ static void ShowProgress(TransactionTableModel *ttm, const std::string &title, i
void TransactionTableModel::subscribeToCoreSignals() void TransactionTableModel::subscribeToCoreSignals()
{ {
// Connect signals to wallet // Connect signals to wallet
wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); m_handler_transaction_changed = walletModel->wallet().handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2));
wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); m_handler_show_progress = walletModel->wallet().handleShowProgress(boost::bind(ShowProgress, this, _1, _2));
} }
void TransactionTableModel::unsubscribeFromCoreSignals() void TransactionTableModel::unsubscribeFromCoreSignals()
{ {
// Disconnect signals from wallet // Disconnect signals from wallet
wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); m_handler_transaction_changed->disconnect();
wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); m_handler_show_progress->disconnect();
} }

View File

@ -10,13 +10,17 @@
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QStringList> #include <QStringList>
#include <memory>
namespace interface {
class Handler;
}
class PlatformStyle; class PlatformStyle;
class TransactionRecord; class TransactionRecord;
class TransactionTablePriv; class TransactionTablePriv;
class WalletModel; class WalletModel;
class CWallet;
/** UI model for the transaction table of a wallet. /** UI model for the transaction table of a wallet.
*/ */
class TransactionTableModel : public QAbstractTableModel class TransactionTableModel : public QAbstractTableModel
@ -24,7 +28,7 @@ class TransactionTableModel : public QAbstractTableModel
Q_OBJECT Q_OBJECT
public: public:
explicit TransactionTableModel(const PlatformStyle *platformStyle, CWallet* wallet, WalletModel *parent = 0); explicit TransactionTableModel(const PlatformStyle *platformStyle, WalletModel *parent = 0);
~TransactionTableModel(); ~TransactionTableModel();
enum ColumnIndex { enum ColumnIndex {
@ -80,8 +84,9 @@ public:
bool processingQueuedTransactions() const { return fProcessingQueuedTransactions; } bool processingQueuedTransactions() const { return fProcessingQueuedTransactions; }
private: private:
CWallet* wallet;
WalletModel *walletModel; WalletModel *walletModel;
std::unique_ptr<interface::Handler> m_handler_transaction_changed;
std::unique_ptr<interface::Handler> m_handler_show_progress;
QStringList columns; QStringList columns;
TransactionTablePriv *priv; TransactionTablePriv *priv;
bool fProcessingQueuedTransactions; bool fProcessingQueuedTransactions;

View File

@ -5,31 +5,20 @@
#include <qt/walletmodel.h> #include <qt/walletmodel.h>
#include <qt/addresstablemodel.h> #include <qt/addresstablemodel.h>
#include <consensus/validation.h>
#include <qt/guiconstants.h> #include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h> #include <qt/optionsmodel.h>
#include <qt/paymentserver.h> #include <qt/paymentserver.h>
#include <qt/recentrequeststablemodel.h> #include <qt/recentrequeststablemodel.h>
#include <qt/sendcoinsdialog.h> #include <qt/sendcoinsdialog.h>
#include <qt/transactiontablemodel.h> #include <qt/transactiontablemodel.h>
#include <chain.h>
#include <interface/handler.h> #include <interface/handler.h>
#include <interface/node.h> #include <interface/node.h>
#include <key_io.h> #include <key_io.h>
#include <keystore.h>
#include <validation.h>
#include <net.h> // for g_connman
#include <policy/fees.h>
#include <policy/rbf.h>
#include <sync.h>
#include <ui_interface.h> #include <ui_interface.h>
#include <util.h> // for GetBoolArg #include <util.h> // for GetBoolArg
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/walletdb.h> // for BackupWallet
#include <stdint.h> #include <stdint.h>
@ -39,8 +28,8 @@
#include <QTimer> #include <QTimer>
WalletModel::WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) : WalletModel::WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, OptionsModel *_optionsModel, QObject *parent) :
QObject(parent), m_wallet(std::move(wallet)), m_node(node), cwallet(_wallet), optionsModel(_optionsModel), addressTableModel(0), QObject(parent), m_wallet(std::move(wallet)), m_node(node), optionsModel(_optionsModel), addressTableModel(0),
transactionTableModel(0), transactionTableModel(0),
recentRequestsTableModel(0), recentRequestsTableModel(0),
cachedEncryptionStatus(Unencrypted), cachedEncryptionStatus(Unencrypted),
@ -50,8 +39,8 @@ WalletModel::WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::N
fForceCheckBalanceChanged = false; fForceCheckBalanceChanged = false;
addressTableModel = new AddressTableModel(this); addressTableModel = new AddressTableModel(this);
transactionTableModel = new TransactionTableModel(platformStyle, cwallet, this); transactionTableModel = new TransactionTableModel(platformStyle, this);
recentRequestsTableModel = new RecentRequestsTableModel(cwallet, this); recentRequestsTableModel = new RecentRequestsTableModel(this);
// This timer will be fired repeatedly to update the balance // This timer will be fired repeatedly to update the balance
pollTimer = new QTimer(this); pollTimer = new QTimer(this);

View File

@ -111,7 +111,7 @@ class WalletModel : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, CWallet *cwallet, OptionsModel *optionsModel, QObject *parent = 0); explicit WalletModel(std::unique_ptr<interface::Wallet> wallet, interface::Node& node, const PlatformStyle *platformStyle, OptionsModel *optionsModel, QObject *parent = 0);
~WalletModel(); ~WalletModel();
enum StatusCode // Returned by sendCoins enum StatusCode // Returned by sendCoins
@ -213,7 +213,6 @@ private:
std::unique_ptr<interface::Handler> m_handler_watch_only_changed; std::unique_ptr<interface::Handler> m_handler_watch_only_changed;
interface::Node& m_node; interface::Node& m_node;
CWallet *cwallet;
bool fHaveWatchOnly; bool fHaveWatchOnly;
bool fForceCheckBalanceChanged; bool fForceCheckBalanceChanged;