Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 848 Lines • ▼ Show 20 Lines | if (srctx) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) { | CWalletTx *CWallet::AddToWallet(CTransactionRef tx, | ||||
const CWalletTx::Confirmation &confirm, | |||||
const UpdateWalletTxFn &update_wtx, | |||||
bool fFlushOnClose) { | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
WalletBatch batch(*database, "r+", fFlushOnClose); | WalletBatch batch(*database, "r+", fFlushOnClose); | ||||
const TxId &txid = wtxIn.GetId(); | const TxId &txid = tx->GetId(); | ||||
if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) { | if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) { | ||||
// Mark used destinations | // Mark used destinations | ||||
std::set<CTxDestination> tx_destinations; | std::set<CTxDestination> tx_destinations; | ||||
for (const CTxIn &txin : wtxIn.tx->vin) { | for (const CTxIn &txin : tx->vin) { | ||||
const COutPoint &op = txin.prevout; | const COutPoint &op = txin.prevout; | ||||
SetSpentKeyState(batch, op.GetTxId(), op.GetN(), true, | SetSpentKeyState(batch, op.GetTxId(), op.GetN(), true, | ||||
tx_destinations); | tx_destinations); | ||||
} | } | ||||
MarkDestinationsDirty(tx_destinations); | MarkDestinationsDirty(tx_destinations); | ||||
} | } | ||||
// Inserts only if not already there, returns tx inserted or tx found. | // Inserts only if not already there, returns tx inserted or tx found. | ||||
std::pair<std::map<TxId, CWalletTx>::iterator, bool> ret = | auto ret = | ||||
mapWallet.insert(std::make_pair(txid, wtxIn)); | mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), | ||||
std::forward_as_tuple(this, tx)); | |||||
CWalletTx &wtx = (*ret.first).second; | CWalletTx &wtx = (*ret.first).second; | ||||
wtx.BindWallet(this); | wtx.BindWallet(this); | ||||
bool fInsertedNew = ret.second; | bool fInsertedNew = ret.second; | ||||
bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew); | |||||
if (fInsertedNew) { | if (fInsertedNew) { | ||||
wtx.m_confirm = confirm; | |||||
wtx.nTimeReceived = chain().getAdjustedTime(); | wtx.nTimeReceived = chain().getAdjustedTime(); | ||||
wtx.nOrderPos = IncOrderPosNext(&batch); | wtx.nOrderPos = IncOrderPosNext(&batch); | ||||
wtx.m_it_wtxOrdered = | wtx.m_it_wtxOrdered = | ||||
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); | wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); | ||||
wtx.nTimeSmart = ComputeTimeSmart(wtx); | wtx.nTimeSmart = ComputeTimeSmart(wtx); | ||||
AddToSpends(txid); | AddToSpends(txid); | ||||
} | } | ||||
bool fUpdated = false; | |||||
if (!fInsertedNew) { | if (!fInsertedNew) { | ||||
if (wtxIn.m_confirm.status != wtx.m_confirm.status) { | if (confirm.status != wtx.m_confirm.status) { | ||||
wtx.m_confirm.status = wtxIn.m_confirm.status; | wtx.m_confirm.status = confirm.status; | ||||
wtx.m_confirm.nIndex = wtxIn.m_confirm.nIndex; | wtx.m_confirm.nIndex = confirm.nIndex; | ||||
wtx.m_confirm.hashBlock = wtxIn.m_confirm.hashBlock; | wtx.m_confirm.hashBlock = confirm.hashBlock; | ||||
wtx.m_confirm.block_height = wtxIn.m_confirm.block_height; | wtx.m_confirm.block_height = confirm.block_height; | ||||
fUpdated = true; | fUpdated = true; | ||||
} else { | } else { | ||||
assert(wtx.m_confirm.nIndex == wtxIn.m_confirm.nIndex); | assert(wtx.m_confirm.nIndex == confirm.nIndex); | ||||
assert(wtx.m_confirm.hashBlock == wtxIn.m_confirm.hashBlock); | assert(wtx.m_confirm.hashBlock == confirm.hashBlock); | ||||
assert(wtx.m_confirm.block_height == wtxIn.m_confirm.block_height); | assert(wtx.m_confirm.block_height == confirm.block_height); | ||||
} | |||||
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) { | |||||
wtx.fFromMe = wtxIn.fFromMe; | |||||
fUpdated = true; | |||||
} | } | ||||
} | } | ||||
//// debug print | //// debug print | ||||
WalletLogPrintf("AddToWallet %s %s%s\n", wtxIn.GetId().ToString(), | WalletLogPrintf("AddToWallet %s %s%s\n", txid.ToString(), | ||||
(fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); | (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); | ||||
// Write to disk | // Write to disk | ||||
if ((fInsertedNew || fUpdated) && !batch.WriteTx(wtx)) { | if ((fInsertedNew || fUpdated) && !batch.WriteTx(wtx)) { | ||||
return false; | return nullptr; | ||||
} | } | ||||
// Break debit/credit balance caches: | // Break debit/credit balance caches: | ||||
wtx.MarkDirty(); | wtx.MarkDirty(); | ||||
// Notify UI of new or updated transaction. | // Notify UI of new or updated transaction. | ||||
NotifyTransactionChanged(this, txid, fInsertedNew ? CT_NEW : CT_UPDATED); | NotifyTransactionChanged(this, txid, fInsertedNew ? CT_NEW : CT_UPDATED); | ||||
#if defined(HAVE_SYSTEM) | #if defined(HAVE_SYSTEM) | ||||
// Notify an external script when a wallet transaction comes in or is | // Notify an external script when a wallet transaction comes in or is | ||||
// updated. | // updated. | ||||
std::string strCmd = gArgs.GetArg("-walletnotify", ""); | std::string strCmd = gArgs.GetArg("-walletnotify", ""); | ||||
if (!strCmd.empty()) { | if (!strCmd.empty()) { | ||||
boost::replace_all(strCmd, "%s", wtxIn.GetId().GetHex()); | boost::replace_all(strCmd, "%s", txid.GetHex()); | ||||
#ifndef WIN32 | #ifndef WIN32 | ||||
// Substituting the wallet name isn't currently supported on windows | // Substituting the wallet name isn't currently supported on windows | ||||
// because windows shell escaping has not been implemented yet: | // because windows shell escaping has not been implemented yet: | ||||
// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875 | // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875 | ||||
// A few ways it could be implemented in the future are described in: | // A few ways it could be implemented in the future are described in: | ||||
// https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094 | // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094 | ||||
boost::replace_all(strCmd, "%w", ShellEscape(GetName())); | boost::replace_all(strCmd, "%w", ShellEscape(GetName())); | ||||
#endif | #endif | ||||
std::thread t(runCommand, strCmd); | std::thread t(runCommand, strCmd); | ||||
// Thread runs free. | // Thread runs free. | ||||
t.detach(); | t.detach(); | ||||
} | } | ||||
#endif | #endif | ||||
return true; | return &wtx; | ||||
} | } | ||||
void CWallet::LoadToWallet(CWalletTx &wtxIn) { | void CWallet::LoadToWallet(CWalletTx &wtxIn) { | ||||
// If wallet doesn't have a chain (e.g wallet-tool), don't bother to update | // If wallet doesn't have a chain (e.g wallet-tool), don't bother to update | ||||
// txn. | // txn. | ||||
if (HaveChain()) { | if (HaveChain()) { | ||||
std::optional<int> block_height = | std::optional<int> block_height = | ||||
chain().getBlockHeight(wtxIn.m_confirm.hashBlock); | chain().getBlockHeight(wtxIn.m_confirm.hashBlock); | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | if (fExisted || IsMine(tx) || IsFromMe(tx)) { | ||||
// loop though all outputs | // loop though all outputs | ||||
for (const CTxOut &txout : tx.vout) { | for (const CTxOut &txout : tx.vout) { | ||||
for (const auto &spk_man_pair : m_spk_managers) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
spk_man_pair.second->MarkUnusedAddresses(txout.scriptPubKey); | spk_man_pair.second->MarkUnusedAddresses(txout.scriptPubKey); | ||||
} | } | ||||
} | } | ||||
CWalletTx wtx(this, ptx); | |||||
// Block disconnection override an abandoned tx as unconfirmed | // Block disconnection override an abandoned tx as unconfirmed | ||||
// which means user may have to call abandontransaction again | // which means user may have to call abandontransaction again | ||||
wtx.m_confirm = confirm; | return AddToWallet(MakeTransactionRef(tx), confirm, | ||||
/* update_wtx= */ nullptr, | |||||
return AddToWallet(wtx, false); | /* fFlushOnClose= */ false); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool CWallet::TransactionCanBeAbandoned(const TxId &txid) const { | bool CWallet::TransactionCanBeAbandoned(const TxId &txid) const { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
const CWalletTx *wtx = GetWalletTx(txid); | const CWalletTx *wtx = GetWalletTx(txid); | ||||
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && | return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && | ||||
!wtx->InMempool(); | !wtx->InMempool(); | ||||
▲ Show 20 Lines • Show All 2,346 Lines • ▼ Show 20 Lines | bool CWallet::CreateTransaction(const std::vector<CRecipient> &vecSend, | ||||
return res; | return res; | ||||
} | } | ||||
void CWallet::CommitTransaction( | void CWallet::CommitTransaction( | ||||
CTransactionRef tx, mapValue_t mapValue, | CTransactionRef tx, mapValue_t mapValue, | ||||
std::vector<std::pair<std::string, std::string>> orderForm) { | std::vector<std::pair<std::string, std::string>> orderForm) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CWalletTx wtxNew(this, std::move(tx)); | WalletLogPrintfToBeContinued("CommitTransaction:\n%s", tx->ToString()); | ||||
wtxNew.mapValue = std::move(mapValue); | |||||
wtxNew.vOrderForm = std::move(orderForm); | |||||
wtxNew.fTimeReceivedIsTxTime = true; | |||||
wtxNew.fFromMe = true; | |||||
WalletLogPrintfToBeContinued("CommitTransaction:\n%s", | |||||
wtxNew.tx->ToString()); | |||||
// Add tx to wallet, because if it has change it's also ours, otherwise just | // Add tx to wallet, because if it has change it's also ours, otherwise just | ||||
// for transaction history. | // for transaction history. | ||||
AddToWallet(wtxNew); | AddToWallet(tx, {}, [&](CWalletTx &wtx, bool new_tx) { | ||||
CHECK_NONFATAL(wtx.mapValue.empty()); | |||||
CHECK_NONFATAL(wtx.vOrderForm.empty()); | |||||
wtx.mapValue = std::move(mapValue); | |||||
wtx.vOrderForm = std::move(orderForm); | |||||
wtx.fTimeReceivedIsTxTime = true; | |||||
wtx.fFromMe = true; | |||||
return true; | |||||
}); | |||||
// Notify that old coins are spent. | // Notify that old coins are spent. | ||||
for (const CTxIn &txin : wtxNew.tx->vin) { | for (const CTxIn &txin : tx->vin) { | ||||
CWalletTx &coin = mapWallet.at(txin.prevout.GetTxId()); | CWalletTx &coin = mapWallet.at(txin.prevout.GetTxId()); | ||||
coin.BindWallet(this); | coin.BindWallet(this); | ||||
NotifyTransactionChanged(this, coin.GetId(), CT_UPDATED); | NotifyTransactionChanged(this, coin.GetId(), CT_UPDATED); | ||||
} | } | ||||
// Get the inserted-CWalletTx from mapWallet so that the | // Get the inserted-CWalletTx from mapWallet so that the | ||||
// fInMempool flag is cached properly | // fInMempool flag is cached properly | ||||
CWalletTx &wtx = mapWallet.at(wtxNew.GetId()); | CWalletTx &wtx = mapWallet.at(tx->GetId()); | ||||
if (!fBroadcastTransactions) { | if (!fBroadcastTransactions) { | ||||
// Don't submit tx to the mempool | // Don't submit tx to the mempool | ||||
return; | return; | ||||
} | } | ||||
std::string err_string; | std::string err_string; | ||||
if (!wtx.SubmitMemoryPoolAndRelay(err_string, true)) { | if (!wtx.SubmitMemoryPoolAndRelay(err_string, true)) { | ||||
▲ Show 20 Lines • Show All 1,604 Lines • Show Last 20 Lines |