Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 579 Lines • ▼ Show 20 Lines | void CWallet::SyncMetaData( | ||||
std::pair<TxSpends::iterator, TxSpends::iterator> range) { | std::pair<TxSpends::iterator, TxSpends::iterator> range) { | ||||
// We want all the wallet transactions in range to have the same metadata as | // We want all the wallet transactions in range to have the same metadata as | ||||
// the oldest (smallest nOrderPos). | // the oldest (smallest nOrderPos). | ||||
// So: find smallest nOrderPos: | // So: find smallest nOrderPos: | ||||
int nMinOrderPos = std::numeric_limits<int>::max(); | int nMinOrderPos = std::numeric_limits<int>::max(); | ||||
const CWalletTx *copyFrom = nullptr; | const CWalletTx *copyFrom = nullptr; | ||||
for (TxSpends::iterator it = range.first; it != range.second; ++it) { | for (TxSpends::iterator it = range.first; it != range.second; ++it) { | ||||
const CWalletTx *wtx = &mapWallet[it->second]; | const CWalletTx *wtx = &mapWallet.at(it->second); | ||||
if (wtx->nOrderPos < nMinOrderPos) { | if (wtx->nOrderPos < nMinOrderPos) { | ||||
nMinOrderPos = wtx->nOrderPos; | nMinOrderPos = wtx->nOrderPos; | ||||
copyFrom = wtx; | copyFrom = wtx; | ||||
} | } | ||||
} | } | ||||
// Now copy data from copyFrom to rest: | // Now copy data from copyFrom to rest: | ||||
for (TxSpends::iterator it = range.first; it != range.second; ++it) { | for (TxSpends::iterator it = range.first; it != range.second; ++it) { | ||||
const TxId &txid = it->second; | const TxId &txid = it->second; | ||||
CWalletTx *copyTo = &mapWallet[txid]; | CWalletTx *copyTo = &mapWallet.at(txid); | ||||
if (copyFrom == copyTo) { | if (copyFrom == copyTo) { | ||||
continue; | continue; | ||||
} | } | ||||
assert( | assert( | ||||
copyFrom && | copyFrom && | ||||
"Oldest wallet transaction in range assumed to have been found."); | "Oldest wallet transaction in range assumed to have been found."); | ||||
Show All 40 Lines | void CWallet::AddToSpends(const COutPoint &outpoint, const TxId &wtxid) { | ||||
std::pair<TxSpends::iterator, TxSpends::iterator> range; | std::pair<TxSpends::iterator, TxSpends::iterator> range; | ||||
range = mapTxSpends.equal_range(outpoint); | range = mapTxSpends.equal_range(outpoint); | ||||
SyncMetaData(range); | SyncMetaData(range); | ||||
} | } | ||||
void CWallet::AddToSpends(const TxId &wtxid) { | void CWallet::AddToSpends(const TxId &wtxid) { | ||||
assert(mapWallet.count(wtxid)); | assert(mapWallet.count(wtxid)); | ||||
CWalletTx &thisTx = mapWallet[wtxid]; | CWalletTx &thisTx = mapWallet.at(wtxid); | ||||
// Coinbases don't spend anything! | // Coinbases don't spend anything! | ||||
if (thisTx.IsCoinBase()) { | if (thisTx.IsCoinBase()) { | ||||
return; | return; | ||||
} | } | ||||
for (const CTxIn &txin : thisTx.tx->vin) { | for (const CTxIn &txin : thisTx.tx->vin) { | ||||
AddToSpends(txin.prevout, wtxid); | AddToSpends(txin.prevout, wtxid); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 356 Lines • ▼ Show 20 Lines | |||||
bool CWallet::LoadToWallet(const CWalletTx &wtxIn) { | bool CWallet::LoadToWallet(const CWalletTx &wtxIn) { | ||||
const TxId &txid = wtxIn.GetId(); | const TxId &txid = wtxIn.GetId(); | ||||
CWalletTx &wtx = mapWallet.emplace(txid, wtxIn).first->second; | CWalletTx &wtx = mapWallet.emplace(txid, wtxIn).first->second; | ||||
wtx.BindWallet(this); | wtx.BindWallet(this); | ||||
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); | wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); | ||||
AddToSpends(txid); | AddToSpends(txid); | ||||
for (const CTxIn &txin : wtx.tx->vin) { | for (const CTxIn &txin : wtx.tx->vin) { | ||||
if (mapWallet.count(txin.prevout.GetTxId())) { | if (mapWallet.count(txin.prevout.GetTxId())) { | ||||
CWalletTx &prevtx = mapWallet[txin.prevout.GetTxId()]; | CWalletTx &prevtx = mapWallet.at(txin.prevout.GetTxId()); | ||||
if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { | if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { | ||||
MarkConflicted(prevtx.hashBlock, wtx.GetId()); | MarkConflicted(prevtx.hashBlock, wtx.GetId()); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | bool CWallet::AbandonTransaction(const TxId &txid) { | ||||
CWalletDB walletdb(*dbw, "r+"); | CWalletDB walletdb(*dbw, "r+"); | ||||
std::set<TxId> todo; | std::set<TxId> todo; | ||||
std::set<TxId> done; | std::set<TxId> done; | ||||
// Can't mark abandoned if confirmed or in mempool. | // Can't mark abandoned if confirmed or in mempool. | ||||
assert(mapWallet.count(txid)); | assert(mapWallet.count(txid)); | ||||
CWalletTx &origtx = mapWallet[txid]; | CWalletTx &origtx = mapWallet.at(txid); | ||||
jasonbcox: Just noting for other reviewers that a few of these changes are not in the original PR, but are… | |||||
if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { | if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { | ||||
return false; | return false; | ||||
} | } | ||||
todo.insert(txid); | todo.insert(txid); | ||||
while (!todo.empty()) { | while (!todo.empty()) { | ||||
const TxId now = *todo.begin(); | const TxId now = *todo.begin(); | ||||
todo.erase(now); | todo.erase(now); | ||||
done.insert(now); | done.insert(now); | ||||
assert(mapWallet.count(now)); | assert(mapWallet.count(now)); | ||||
CWalletTx &wtx = mapWallet[now]; | CWalletTx &wtx = mapWallet.at(now); | ||||
int currentconfirm = wtx.GetDepthInMainChain(); | int currentconfirm = wtx.GetDepthInMainChain(); | ||||
// If the orig tx was not in block, none of its spends can be. | // If the orig tx was not in block, none of its spends can be. | ||||
assert(currentconfirm <= 0); | assert(currentconfirm <= 0); | ||||
// If (currentconfirm < 0) {Tx and spends are already conflicted, no | // If (currentconfirm < 0) {Tx and spends are already conflicted, no | ||||
// need to abandon} | // need to abandon} | ||||
if (currentconfirm == 0 && !wtx.isAbandoned()) { | if (currentconfirm == 0 && !wtx.isAbandoned()) { | ||||
// If the orig tx was not in block/mempool, none of its spends can | // If the orig tx was not in block/mempool, none of its spends can | ||||
// be in mempool. | // be in mempool. | ||||
Show All 14 Lines | while (!todo.empty()) { | ||||
iter++; | iter++; | ||||
} | } | ||||
// If a transaction changes 'conflicted' state, that changes the | // If a transaction changes 'conflicted' state, that changes the | ||||
// balance available of the outputs it spends. So force those to be | // balance available of the outputs it spends. So force those to be | ||||
// recomputed. | // recomputed. | ||||
for (const CTxIn &txin : wtx.tx->vin) { | for (const CTxIn &txin : wtx.tx->vin) { | ||||
if (mapWallet.count(txin.prevout.GetTxId())) { | if (mapWallet.count(txin.prevout.GetTxId())) { | ||||
mapWallet[txin.prevout.GetTxId()].MarkDirty(); | mapWallet.at(txin.prevout.GetTxId()).MarkDirty(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
Show All 23 Lines | void CWallet::MarkConflicted(const uint256 &hashBlock, const TxId &txid) { | ||||
todo.insert(txid); | todo.insert(txid); | ||||
while (!todo.empty()) { | while (!todo.empty()) { | ||||
const TxId now = *todo.begin(); | const TxId now = *todo.begin(); | ||||
todo.erase(now); | todo.erase(now); | ||||
done.insert(now); | done.insert(now); | ||||
assert(mapWallet.count(now)); | assert(mapWallet.count(now)); | ||||
CWalletTx &wtx = mapWallet[now]; | CWalletTx &wtx = mapWallet.at(now); | ||||
int currentconfirm = wtx.GetDepthInMainChain(); | int currentconfirm = wtx.GetDepthInMainChain(); | ||||
if (conflictconfirms < currentconfirm) { | if (conflictconfirms < currentconfirm) { | ||||
// Block is 'more conflicted' than current confirm; update. | // Block is 'more conflicted' than current confirm; update. | ||||
// Mark transaction as conflicted with this block. | // Mark transaction as conflicted with this block. | ||||
wtx.nIndex = -1; | wtx.nIndex = -1; | ||||
wtx.hashBlock = hashBlock; | wtx.hashBlock = hashBlock; | ||||
wtx.MarkDirty(); | wtx.MarkDirty(); | ||||
walletdb.WriteTx(wtx); | walletdb.WriteTx(wtx); | ||||
// Iterate over all its outputs, and mark transactions in the wallet | // Iterate over all its outputs, and mark transactions in the wallet | ||||
// that spend them conflicted too. | // that spend them conflicted too. | ||||
TxSpends::const_iterator iter = | TxSpends::const_iterator iter = | ||||
mapTxSpends.lower_bound(COutPoint(now, 0)); | mapTxSpends.lower_bound(COutPoint(now, 0)); | ||||
while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) { | while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) { | ||||
if (!done.count(iter->second)) { | if (!done.count(iter->second)) { | ||||
todo.insert(iter->second); | todo.insert(iter->second); | ||||
} | } | ||||
iter++; | iter++; | ||||
} | } | ||||
// If a transaction changes 'conflicted' state, that changes the | // If a transaction changes 'conflicted' state, that changes the | ||||
// balance available of the outputs it spends. So force those to be | // balance available of the outputs it spends. So force those to be | ||||
// recomputed. | // recomputed. | ||||
for (const CTxIn &txin : wtx.tx->vin) { | for (const CTxIn &txin : wtx.tx->vin) { | ||||
if (mapWallet.count(txin.prevout.GetTxId())) { | if (mapWallet.count(txin.prevout.GetTxId())) { | ||||
mapWallet[txin.prevout.GetTxId()].MarkDirty(); | mapWallet.at(txin.prevout.GetTxId()).MarkDirty(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void CWallet::SyncTransaction(const CTransactionRef &ptx, | void CWallet::SyncTransaction(const CTransactionRef &ptx, | ||||
const CBlockIndex *pindex, int posInBlock) { | const CBlockIndex *pindex, int posInBlock) { | ||||
const CTransaction &tx = *ptx; | const CTransaction &tx = *ptx; | ||||
if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true)) { | if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true)) { | ||||
// Not one of ours | // Not one of ours | ||||
return; | return; | ||||
} | } | ||||
// If a transaction changes 'conflicted' state, that changes the balance | // If a transaction changes 'conflicted' state, that changes the balance | ||||
// available of the outputs it spends. So force those to be recomputed, | // available of the outputs it spends. So force those to be recomputed, | ||||
// also: | // also: | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
if (mapWallet.count(txin.prevout.GetTxId())) { | if (mapWallet.count(txin.prevout.GetTxId())) { | ||||
mapWallet[txin.prevout.GetTxId()].MarkDirty(); | mapWallet.at(txin.prevout.GetTxId()).MarkDirty(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void CWallet::TransactionAddedToMempool(const CTransactionRef &ptx) { | void CWallet::TransactionAddedToMempool(const CTransactionRef &ptx) { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
SyncTransaction(ptx); | SyncTransaction(ptx); | ||||
▲ Show 20 Lines • Show All 1,964 Lines • ▼ Show 20 Lines | bool CWallet::CommitTransaction( | ||||
reservekey.KeepKey(); | reservekey.KeepKey(); | ||||
// 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(wtxNew); | ||||
// Notify that old coins are spent. | // Notify that old coins are spent. | ||||
for (const CTxIn &txin : wtxNew.tx->vin) { | for (const CTxIn &txin : wtxNew.tx->vin) { | ||||
CWalletTx &coin = mapWallet[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); | ||||
} | } | ||||
// Track how many getdata requests our transaction gets. | // Track how many getdata requests our transaction gets. | ||||
mapRequestCount[wtxNew.GetId()] = 0; | mapRequestCount[wtxNew.GetId()] = 0; | ||||
// 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[wtxNew.GetId()]; | CWalletTx &wtx = mapWallet.at(wtxNew.GetId()); | ||||
if (fBroadcastTransactions) { | if (fBroadcastTransactions) { | ||||
// Broadcast | // Broadcast | ||||
if (!wtx.AcceptToMemoryPool(maxTxFee, state)) { | if (!wtx.AcceptToMemoryPool(maxTxFee, state)) { | ||||
LogPrintf("CommitTransaction(): Transaction cannot be broadcast " | LogPrintf("CommitTransaction(): Transaction cannot be broadcast " | ||||
"immediately, %s\n", | "immediately, %s\n", | ||||
state.GetRejectReason()); | state.GetRejectReason()); | ||||
// TODO: if we expect the failure to be long term or permanent, | // TODO: if we expect the failure to be long term or permanent, | ||||
▲ Show 20 Lines • Show All 466 Lines • ▼ Show 20 Lines | for (std::pair<uint256, CWalletTx> walletEntry : mapWallet) { | ||||
// Group all input addresses with each other. | // Group all input addresses with each other. | ||||
for (CTxIn txin : pcoin->tx->vin) { | for (CTxIn txin : pcoin->tx->vin) { | ||||
CTxDestination address; | CTxDestination address; | ||||
// If this input isn't mine, ignore it. | // If this input isn't mine, ignore it. | ||||
if (!IsMine(txin)) { | if (!IsMine(txin)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!ExtractDestination(mapWallet[txin.prevout.GetTxId()] | if (!ExtractDestination(mapWallet.at(txin.prevout.GetTxId()) | ||||
.tx->vout[txin.prevout.GetN()] | .tx->vout[txin.prevout.GetN()] | ||||
.scriptPubKey, | .scriptPubKey, | ||||
address)) { | address)) { | ||||
continue; | continue; | ||||
} | } | ||||
grouping.insert(address); | grouping.insert(address); | ||||
any_mine = true; | any_mine = true; | ||||
▲ Show 20 Lines • Show All 703 Lines • Show Last 20 Lines |
Just noting for other reviewers that a few of these changes are not in the original PR, but are noted in the summary.