Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 561 Lines • ▼ Show 20 Lines | std::set<uint256> CWallet::GetConflicts(const uint256 &txid) const { | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
bool CWallet::HasWalletSpend(const uint256 &txid) const { | bool CWallet::HasWalletSpend(const uint256 &txid) const { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); | auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); | ||||
return (iter != mapTxSpends.end() && iter->first.hash == txid); | return (iter != mapTxSpends.end() && iter->first.GetTxId() == txid); | ||||
} | } | ||||
void CWallet::Flush(bool shutdown) { | void CWallet::Flush(bool shutdown) { | ||||
dbw->Flush(shutdown); | dbw->Flush(shutdown); | ||||
} | } | ||||
bool CWallet::Verify(const CChainParams &chainParams) { | bool CWallet::Verify(const CChainParams &chainParams) { | ||||
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { | if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { | ||||
▲ Show 20 Lines • Show All 510 Lines • ▼ Show 20 Lines | bool CWallet::LoadToWallet(const CWalletTx &wtxIn) { | ||||
uint256 txid = wtxIn.GetId(); | uint256 txid = wtxIn.GetId(); | ||||
mapWallet[txid] = wtxIn; | mapWallet[txid] = wtxIn; | ||||
CWalletTx &wtx = mapWallet[txid]; | CWalletTx &wtx = mapWallet[txid]; | ||||
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.hash)) { | if (mapWallet.count(txin.prevout.GetTxId())) { | ||||
CWalletTx &prevtx = mapWallet[txin.prevout.hash]; | CWalletTx &prevtx = mapWallet[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 All 23 Lines | if (pIndex != nullptr) { | ||||
range = mapTxSpends.equal_range(txin.prevout); | range = mapTxSpends.equal_range(txin.prevout); | ||||
while (range.first != range.second) { | while (range.first != range.second) { | ||||
if (range.first->second != tx.GetId()) { | if (range.first->second != tx.GetId()) { | ||||
LogPrintf("Transaction %s (in block %s) conflicts with " | LogPrintf("Transaction %s (in block %s) conflicts with " | ||||
"wallet transaction %s (both spend %s:%i)\n", | "wallet transaction %s (both spend %s:%i)\n", | ||||
tx.GetId().ToString(), | tx.GetId().ToString(), | ||||
pIndex->GetBlockHash().ToString(), | pIndex->GetBlockHash().ToString(), | ||||
range.first->second.ToString(), | range.first->second.ToString(), | ||||
range.first->first.hash.ToString(), | range.first->first.GetTxId().ToString(), | ||||
range.first->first.n); | range.first->first.GetN()); | ||||
MarkConflicted(pIndex->GetBlockHash(), range.first->second); | MarkConflicted(pIndex->GetBlockHash(), range.first->second); | ||||
} | } | ||||
range.first++; | range.first++; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool fExisted = mapWallet.count(tx.GetId()) != 0; | bool fExisted = mapWallet.count(tx.GetId()) != 0; | ||||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | while (!todo.empty()) { | ||||
wtx.setAbandoned(); | wtx.setAbandoned(); | ||||
wtx.MarkDirty(); | wtx.MarkDirty(); | ||||
walletdb.WriteTx(wtx); | walletdb.WriteTx(wtx); | ||||
NotifyTransactionChanged(this, wtx.GetId(), CT_UPDATED); | NotifyTransactionChanged(this, wtx.GetId(), CT_UPDATED); | ||||
// 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 abandoned too. | // that spend them abandoned too. | ||||
TxSpends::const_iterator iter = | TxSpends::const_iterator iter = | ||||
mapTxSpends.lower_bound(COutPoint(hashTx, 0)); | mapTxSpends.lower_bound(COutPoint(hashTx, 0)); | ||||
while (iter != mapTxSpends.end() && iter->first.hash == 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.hash)) { | if (mapWallet.count(txin.prevout.GetTxId())) { | ||||
mapWallet[txin.prevout.hash].MarkDirty(); | mapWallet[txin.prevout.GetTxId()].MarkDirty(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
Show All 36 Lines | while (!todo.empty()) { | ||||
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.hash == 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.hash)) { | if (mapWallet.count(txin.prevout.GetTxId())) { | ||||
mapWallet[txin.prevout.hash].MarkDirty(); | mapWallet[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.hash)) { | if (mapWallet.count(txin.prevout.GetTxId())) { | ||||
mapWallet[txin.prevout.hash].MarkDirty(); | mapWallet[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 All 24 Lines | void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) { | ||||
for (const CTransactionRef &ptx : pblock->vtx) { | for (const CTransactionRef &ptx : pblock->vtx) { | ||||
SyncTransaction(ptx); | SyncTransaction(ptx); | ||||
} | } | ||||
} | } | ||||
isminetype CWallet::IsMine(const CTxIn &txin) const { | isminetype CWallet::IsMine(const CTxIn &txin) const { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::map<uint256, CWalletTx>::const_iterator mi = | std::map<uint256, CWalletTx>::const_iterator mi = | ||||
mapWallet.find(txin.prevout.hash); | mapWallet.find(txin.prevout.GetTxId()); | ||||
if (mi != mapWallet.end()) { | if (mi != mapWallet.end()) { | ||||
const CWalletTx &prev = (*mi).second; | const CWalletTx &prev = (*mi).second; | ||||
if (txin.prevout.n < prev.tx->vout.size()) { | if (txin.prevout.GetN() < prev.tx->vout.size()) { | ||||
return IsMine(prev.tx->vout[txin.prevout.n]); | return IsMine(prev.tx->vout[txin.prevout.GetN()]); | ||||
} | } | ||||
} | } | ||||
return ISMINE_NO; | return ISMINE_NO; | ||||
} | } | ||||
// Note that this function doesn't distinguish between a 0-valued input, and a | // Note that this function doesn't distinguish between a 0-valued input, and a | ||||
// not-"is mine" (according to the filter) input. | // not-"is mine" (according to the filter) input. | ||||
Amount CWallet::GetDebit(const CTxIn &txin, const isminefilter &filter) const { | Amount CWallet::GetDebit(const CTxIn &txin, const isminefilter &filter) const { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::map<uint256, CWalletTx>::const_iterator mi = | std::map<uint256, CWalletTx>::const_iterator mi = | ||||
mapWallet.find(txin.prevout.hash); | mapWallet.find(txin.prevout.GetTxId()); | ||||
if (mi != mapWallet.end()) { | if (mi != mapWallet.end()) { | ||||
const CWalletTx &prev = (*mi).second; | const CWalletTx &prev = (*mi).second; | ||||
if (txin.prevout.n < prev.tx->vout.size()) { | if (txin.prevout.GetN() < prev.tx->vout.size()) { | ||||
if (IsMine(prev.tx->vout[txin.prevout.n]) & filter) { | if (IsMine(prev.tx->vout[txin.prevout.GetN()]) & filter) { | ||||
return prev.tx->vout[txin.prevout.n].nValue; | return prev.tx->vout[txin.prevout.GetN()].nValue; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return Amount(0); | return Amount(0); | ||||
} | } | ||||
isminetype CWallet::IsMine(const CTxOut &txout) const { | isminetype CWallet::IsMine(const CTxOut &txout) const { | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | Amount CWallet::GetDebit(const CTransaction &tx, | ||||
return nDebit; | return nDebit; | ||||
} | } | ||||
bool CWallet::IsAllFromMe(const CTransaction &tx, | bool CWallet::IsAllFromMe(const CTransaction &tx, | ||||
const isminefilter &filter) const { | const isminefilter &filter) const { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
auto mi = mapWallet.find(txin.prevout.hash); | auto mi = mapWallet.find(txin.prevout.GetTxId()); | ||||
if (mi == mapWallet.end()) { | if (mi == mapWallet.end()) { | ||||
// Any unknown inputs can't be from us. | // Any unknown inputs can't be from us. | ||||
return false; | return false; | ||||
} | } | ||||
const CWalletTx &prev = (*mi).second; | const CWalletTx &prev = (*mi).second; | ||||
if (txin.prevout.n >= prev.tx->vout.size()) { | if (txin.prevout.GetN() >= prev.tx->vout.size()) { | ||||
// Invalid input! | // Invalid input! | ||||
return false; | return false; | ||||
} | } | ||||
if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) { | if (!(IsMine(prev.tx->vout[txin.prevout.GetN()]) & filter)) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
Amount CWallet::GetCredit(const CTransaction &tx, | Amount CWallet::GetCredit(const CTransaction &tx, | ||||
▲ Show 20 Lines • Show All 520 Lines • ▼ Show 20 Lines | bool CWalletTx::IsTrusted() const { | ||||
// mempool. | // mempool. | ||||
if (!InMempool()) { | if (!InMempool()) { | ||||
return false; | return false; | ||||
} | } | ||||
// Trusted if all inputs are from us and are in the mempool: | // Trusted if all inputs are from us and are in the mempool: | ||||
for (const CTxIn &txin : tx->vin) { | for (const CTxIn &txin : tx->vin) { | ||||
// Transactions not sent by us: not trusted | // Transactions not sent by us: not trusted | ||||
const CWalletTx *parent = pwallet->GetWalletTx(txin.prevout.hash); | const CWalletTx *parent = pwallet->GetWalletTx(txin.prevout.GetTxId()); | ||||
if (parent == nullptr) { | if (parent == nullptr) { | ||||
return false; | return false; | ||||
} | } | ||||
const CTxOut &parentOut = parent->tx->vout[txin.prevout.n]; | const CTxOut &parentOut = parent->tx->vout[txin.prevout.GetN()]; | ||||
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) { | if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 475 Lines • ▼ Show 20 Lines | bool CWallet::SelectCoins( | ||||
std::vector<COutPoint> vPresetInputs; | std::vector<COutPoint> vPresetInputs; | ||||
if (coinControl) { | if (coinControl) { | ||||
coinControl->ListSelected(vPresetInputs); | coinControl->ListSelected(vPresetInputs); | ||||
} | } | ||||
for (const COutPoint &outpoint : vPresetInputs) { | for (const COutPoint &outpoint : vPresetInputs) { | ||||
std::map<uint256, CWalletTx>::const_iterator it = | std::map<uint256, CWalletTx>::const_iterator it = | ||||
mapWallet.find(outpoint.hash); | mapWallet.find(outpoint.GetTxId()); | ||||
if (it == mapWallet.end()) { | if (it == mapWallet.end()) { | ||||
// TODO: Allow non-wallet inputs | // TODO: Allow non-wallet inputs | ||||
return false; | return false; | ||||
} | } | ||||
const CWalletTx *pcoin = &it->second; | const CWalletTx *pcoin = &it->second; | ||||
// Clearly invalid input, fail. | // Clearly invalid input, fail. | ||||
if (pcoin->tx->vout.size() <= outpoint.n) { | if (pcoin->tx->vout.size() <= outpoint.GetN()) { | ||||
return false; | return false; | ||||
} | } | ||||
nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; | nValueFromPresetInputs += pcoin->tx->vout[outpoint.GetN()].nValue; | ||||
setPresetCoins.insert(std::make_pair(pcoin, outpoint.n)); | setPresetCoins.insert(std::make_pair(pcoin, outpoint.GetN())); | ||||
} | } | ||||
// Remove preset inputs from vCoins. | // Remove preset inputs from vCoins. | ||||
for (std::vector<COutput>::iterator it = vCoins.begin(); | for (std::vector<COutput>::iterator it = vCoins.begin(); | ||||
it != vCoins.end() && coinControl && coinControl->HasSelected();) { | it != vCoins.end() && coinControl && coinControl->HasSelected();) { | ||||
if (setPresetCoins.count(std::make_pair(it->tx, it->i))) { | if (setPresetCoins.count(std::make_pair(it->tx, it->i))) { | ||||
it = vCoins.erase(it); | it = vCoins.erase(it); | ||||
} else { | } else { | ||||
▲ Show 20 Lines • Show All 168 Lines • ▼ Show 20 Lines | bool CWallet::CreateTransaction(const std::vector<CRecipient> &vecSend, | ||||
if (GetRandInt(10) == 0) { | if (GetRandInt(10) == 0) { | ||||
txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | ||||
} | } | ||||
assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); | assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); | ||||
assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
{ | { | ||||
std::set<std::pair<const CWalletTx *, unsigned int>> setCoins; | std::set<std::pair<const CWalletTx *, uint32_t>> setCoins; | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
std::vector<COutput> vAvailableCoins; | std::vector<COutput> vAvailableCoins; | ||||
AvailableCoins(vAvailableCoins, true, coinControl); | AvailableCoins(vAvailableCoins, true, coinControl); | ||||
nFeeRet = Amount(0); | nFeeRet = Amount(0); | ||||
// Start with no fee and loop until there is enough fee. | // Start with no fee and loop until there is enough fee. | ||||
while (true) { | while (true) { | ||||
▲ Show 20 Lines • Show All 160 Lines • ▼ Show 20 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
// Fill vin | // Fill vin | ||||
// | // | ||||
// Note how the sequence number is set to non-maxint so that the | // Note how the sequence number is set to non-maxint so that the | ||||
// nLockTime set above actually works. | // nLockTime set above actually works. | ||||
for (const auto &coin : setCoins) { | for (const auto &coin : setCoins) { | ||||
txNew.vin.push_back( | txNew.vin.push_back( | ||||
CTxIn(coin.first->GetId(), coin.second, CScript(), | CTxIn(coin.first->GetId(), coin.second, CScript(), | ||||
std::numeric_limits<unsigned int>::max() - 1)); | std::numeric_limits<uint32_t>::max() - 1)); | ||||
} | } | ||||
// Fill in dummy signatures for fee calculation. | // Fill in dummy signatures for fee calculation. | ||||
if (!DummySignTx(txNew, setCoins)) { | if (!DummySignTx(txNew, setCoins)) { | ||||
strFailReason = _("Signing transaction failed"); | strFailReason = _("Signing transaction failed"); | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 155 Lines • ▼ Show 20 Lines | bool CWallet::CommitTransaction(CWalletTx &wtxNew, CReserveKey &reservekey, | ||||
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.hash]; | CWalletTx &coin = mapWallet[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; | ||||
if (fBroadcastTransactions) { | if (fBroadcastTransactions) { | ||||
▲ Show 20 Lines • Show All 510 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.hash] | if (!ExtractDestination(mapWallet[txin.prevout.GetTxId()] | ||||
.tx->vout[txin.prevout.n] | .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 939 Lines • Show Last 20 Lines |