Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | bool operator()( | ||||
&t1, | &t1, | ||||
const std::pair<CAmount, std::pair<const CWalletTx *, unsigned int>> | const std::pair<CAmount, std::pair<const CWalletTx *, unsigned int>> | ||||
&t2) const { | &t2) const { | ||||
return t1.first < t2.first; | return t1.first < t2.first; | ||||
} | } | ||||
}; | }; | ||||
std::string COutput::ToString() const { | std::string COutput::ToString() const { | ||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i, | return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, | ||||
nDepth, FormatMoney(tx->tx->vout[i].nValue.GetSatoshis())); | nDepth, FormatMoney(tx->tx->vout[i].nValue.GetSatoshis())); | ||||
} | } | ||||
const CWalletTx *CWallet::GetWalletTx(const uint256 &hash) const { | const CWalletTx *CWallet::GetWalletTx(const unspentid_t &unspentid) const { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash); | auto wtx_iter = std::find_if( | ||||
mapWallet.begin(), mapWallet.end(), | |||||
[&unspentid](const std::pair<txhash_t, CWalletTx> &t) -> bool { | |||||
return t.second.tx->Getunspentid() == unspentid; | |||||
}); | |||||
if (wtx_iter == mapWallet.end()) { | |||||
return nullptr; | |||||
} | |||||
return &(wtx_iter->second); | |||||
} | |||||
const CWalletTx *CWallet::GetWalletTx(const txhash_t &txhash) const { | |||||
LOCK(cs_wallet); | |||||
std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.find(txhash); | |||||
if (it == mapWallet.end()) { | if (it == mapWallet.end()) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return &(it->second); | return &(it->second); | ||||
} | } | ||||
CPubKey CWallet::GenerateNewKey() { | CPubKey CWallet::GenerateNewKey() { | ||||
▲ Show 20 Lines • Show All 386 Lines • ▼ Show 20 Lines | if (nWalletVersion > nVersion) { | ||||
return false; | return false; | ||||
} | } | ||||
nWalletMaxVersion = nVersion; | nWalletMaxVersion = nVersion; | ||||
return true; | return true; | ||||
} | } | ||||
std::set<uint256> CWallet::GetConflicts(const uint256 &txid) const { | std::set<txhash_t> CWallet::GetConflicts(const txhash_t &txhash) const { | ||||
std::set<uint256> result; | std::set<txhash_t> result; | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid); | std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.find(txhash); | ||||
if (it == mapWallet.end()) { | if (it == mapWallet.end()) { | ||||
return result; | return result; | ||||
} | } | ||||
const CWalletTx &wtx = it->second; | const CWalletTx &wtx = it->second; | ||||
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; | std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; | ||||
for (const CTxIn &txin : wtx.tx->vin) { | for (const CTxIn &txin : wtx.tx->vin) { | ||||
if (mapTxSpends.count(txin.prevout) <= 1) { | if (mapTxSpends.count(txin.prevout) <= 1) { | ||||
// No conflict if zero or one spends. | // No conflict if zero or one spends. | ||||
continue; | continue; | ||||
} | } | ||||
range = mapTxSpends.equal_range(txin.prevout); | range = mapTxSpends.equal_range(txin.prevout); | ||||
for (TxSpends::const_iterator _it = range.first; _it != range.second; | for (TxSpends::const_iterator _it = range.first; _it != range.second; | ||||
++_it) { | ++_it) { | ||||
result.insert(_it->second); | result.insert(_it->second); | ||||
} | } | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
bool CWallet::HasWalletSpend(const uint256 &txid) const { | |||||
AssertLockHeld(cs_wallet); | |||||
auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); | |||||
return (iter != mapTxSpends.end() && iter->first.hash == txid); | |||||
} | |||||
void CWallet::Flush(bool shutdown) { | void CWallet::Flush(bool shutdown) { | ||||
bitdb.Flush(shutdown); | bitdb.Flush(shutdown); | ||||
} | } | ||||
bool CWallet::Verify() { | bool CWallet::Verify() { | ||||
if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { | if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 66 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 uint256 &hash = it->second; | const txhash_t &hash = it->second; | ||||
int n = mapWallet[hash].nOrderPos; | int n = mapWallet[hash].nOrderPos; | ||||
if (n < nMinOrderPos) { | if (n < nMinOrderPos) { | ||||
nMinOrderPos = n; | nMinOrderPos = n; | ||||
copyFrom = &mapWallet[hash]; | copyFrom = &mapWallet[hash]; | ||||
} | } | ||||
} | } | ||||
// 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 uint256 &hash = it->second; | const txhash_t &hash = it->second; | ||||
CWalletTx *copyTo = &mapWallet[hash]; | CWalletTx *copyTo = &mapWallet[hash]; | ||||
if (copyFrom == copyTo) { | if (copyFrom == copyTo) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!copyFrom->IsEquivalentTo(*copyTo)) { | if (!copyFrom->IsEquivalentTo(*copyTo)) { | ||||
continue; | continue; | ||||
} | } | ||||
copyTo->mapValue = copyFrom->mapValue; | copyTo->mapValue = copyFrom->mapValue; | ||||
copyTo->vOrderForm = copyFrom->vOrderForm; | copyTo->vOrderForm = copyFrom->vOrderForm; | ||||
// fTimeReceivedIsTxTime not copied on purpose nTimeReceived not copied | // fTimeReceivedIsTxTime not copied on purpose nTimeReceived not copied | ||||
// on purpose. | // on purpose. | ||||
copyTo->nTimeSmart = copyFrom->nTimeSmart; | copyTo->nTimeSmart = copyFrom->nTimeSmart; | ||||
copyTo->fFromMe = copyFrom->fFromMe; | copyTo->fFromMe = copyFrom->fFromMe; | ||||
copyTo->strFromAccount = copyFrom->strFromAccount; | copyTo->strFromAccount = copyFrom->strFromAccount; | ||||
// nOrderPos not copied on purpose cached members not copied on purpose. | // nOrderPos not copied on purpose cached members not copied on purpose. | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Outpoint is spent if any non-conflicted transaction, spends it: | * Outpoint is spent if any non-conflicted transaction, spends it: | ||||
*/ | */ | ||||
bool CWallet::IsSpent(const uint256 &hash, unsigned int n) const { | bool CWallet::IsSpent(const COutPoint &outpoint) const { | ||||
const COutPoint outpoint(hash, n); | |||||
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; | std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; | ||||
range = mapTxSpends.equal_range(outpoint); | range = mapTxSpends.equal_range(outpoint); | ||||
for (TxSpends::const_iterator it = range.first; it != range.second; ++it) { | for (TxSpends::const_iterator it = range.first; it != range.second; ++it) { | ||||
const uint256 &wtxid = it->second; | const txhash_t &txhash = it->second; | ||||
std::map<uint256, CWalletTx>::const_iterator mit = | std::map<txhash_t, CWalletTx>::const_iterator mit = | ||||
mapWallet.find(wtxid); | mapWallet.find(txhash); | ||||
if (mit != mapWallet.end()) { | if (mit != mapWallet.end()) { | ||||
int depth = mit->second.GetDepthInMainChain(); | int depth = mit->second.GetDepthInMainChain(); | ||||
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) { | if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) { | ||||
// Spent | // Spent | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
void CWallet::AddToSpends(const COutPoint &outpoint, const uint256 &wtxid) { | void CWallet::AddToSpends(const COutPoint &outpoint, const txhash_t &wtxhash) { | ||||
mapTxSpends.insert(std::make_pair(outpoint, wtxid)); | mapTxSpends.insert(std::make_pair(outpoint, wtxhash)); | ||||
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 uint256 &wtxid) { | void CWallet::AddToSpends(const txhash_t &wtxhash) { | ||||
assert(mapWallet.count(wtxid)); | assert(mapWallet.count(wtxhash)); | ||||
CWalletTx &thisTx = mapWallet[wtxid]; | CWalletTx &thisTx = mapWallet[wtxhash]; | ||||
// 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, wtxhash); | ||||
} | } | ||||
} | } | ||||
bool CWallet::EncryptWallet(const SecureString &strWalletPassphrase) { | bool CWallet::EncryptWallet(const SecureString &strWalletPassphrase) { | ||||
if (IsCrypted()) { | if (IsCrypted()) { | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | DBErrors CWallet::ReorderTransactions() { | ||||
// bad idea to change the output of this. | // bad idea to change the output of this. | ||||
// First: get all CWalletTx and CAccountingEntry into a sorted-by-time | // First: get all CWalletTx and CAccountingEntry into a sorted-by-time | ||||
// multimap. | // multimap. | ||||
typedef std::pair<CWalletTx *, CAccountingEntry *> TxPair; | typedef std::pair<CWalletTx *, CAccountingEntry *> TxPair; | ||||
typedef std::multimap<int64_t, TxPair> TxItems; | typedef std::multimap<int64_t, TxPair> TxItems; | ||||
TxItems txByTime; | TxItems txByTime; | ||||
for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); ++it) { | it != mapWallet.end(); ++it) { | ||||
CWalletTx *wtx = &((*it).second); | CWalletTx *wtx = &((*it).second); | ||||
txByTime.insert( | txByTime.insert( | ||||
std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); | std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); | ||||
} | } | ||||
std::list<CAccountingEntry> acentries; | std::list<CAccountingEntry> acentries; | ||||
walletdb.ListAccountCreditDebit("", acentries); | walletdb.ListAccountCreditDebit("", acentries); | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | DBErrors CWallet::ReorderTransactions() { | ||||
walletdb.WriteOrderPosNext(nOrderPosNext); | walletdb.WriteOrderPosNext(nOrderPosNext); | ||||
return DB_LOAD_OK; | return DB_LOAD_OK; | ||||
} | } | ||||
int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { | int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { | ||||
// nOrderPosNext | // nOrderPosNext | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
int64_t nRet = nOrderPosNext++; | const int64_t nRet = nOrderPosNext++; | ||||
if (pwalletdb) { | if (pwalletdb) { | ||||
pwalletdb->WriteOrderPosNext(nOrderPosNext); | pwalletdb->WriteOrderPosNext(nOrderPosNext); | ||||
} else { | } else { | ||||
CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); | CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); | ||||
} | } | ||||
return nRet; | return nRet; | ||||
} | } | ||||
bool CWallet::AccountMove(std::string strFrom, std::string strTo, | bool CWallet::AccountMove(std::string strFrom, std::string strTo, | ||||
CAmount nAmount, std::string strComment) { | CAmount nAmount, std::string strComment) { | ||||
CWalletDB walletdb(strWalletFile); | CWalletDB walletdb(strWalletFile); | ||||
if (!walletdb.TxnBegin()) { | if (!walletdb.TxnBegin()) { | ||||
return false; | return false; | ||||
} | } | ||||
int64_t nNow = GetAdjustedTime(); | const int64_t nNow = GetAdjustedTime(); | ||||
// Debit | // Debit | ||||
CAccountingEntry debit; | CAccountingEntry debit; | ||||
debit.nOrderPos = IncOrderPosNext(&walletdb); | debit.nOrderPos = IncOrderPosNext(&walletdb); | ||||
debit.strAccount = strFrom; | debit.strAccount = strFrom; | ||||
debit.nCreditDebit = -nAmount; | debit.nCreditDebit = -nAmount; | ||||
debit.nTime = nNow; | debit.nTime = nNow; | ||||
debit.strOtherAccount = strTo; | debit.strOtherAccount = strTo; | ||||
Show All 26 Lines | bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, | ||||
if (!bForceNew) { | if (!bForceNew) { | ||||
if (!account.vchPubKey.IsValid()) { | if (!account.vchPubKey.IsValid()) { | ||||
bForceNew = true; | bForceNew = true; | ||||
} else { | } else { | ||||
// Check if the current key has been used. | // Check if the current key has been used. | ||||
CScript scriptPubKey = | CScript scriptPubKey = | ||||
GetScriptForDestination(account.vchPubKey.GetID()); | GetScriptForDestination(account.vchPubKey.GetID()); | ||||
for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::iterator it = mapWallet.begin(); | ||||
it != mapWallet.end() && account.vchPubKey.IsValid(); ++it) { | it != mapWallet.end() && account.vchPubKey.IsValid(); ++it) { | ||||
for (const CTxOut &txout : (*it).second.tx->vout) { | for (const CTxOut &txout : (*it).second.tx->vout) { | ||||
if (txout.scriptPubKey == scriptPubKey) { | if (txout.scriptPubKey == scriptPubKey) { | ||||
bForceNew = true; | bForceNew = true; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Show All 12 Lines | bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, | ||||
pubKey = account.vchPubKey; | pubKey = account.vchPubKey; | ||||
return true; | return true; | ||||
} | } | ||||
void CWallet::MarkDirty() { | void CWallet::MarkDirty() { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
for (std::pair<const uint256, CWalletTx> &item : mapWallet) { | for (std::pair<const txhash_t, CWalletTx> &item : mapWallet) { | ||||
item.second.MarkDirty(); | item.second.MarkDirty(); | ||||
} | } | ||||
} | } | ||||
bool CWallet::MarkReplaced(const uint256 &originalHash, | void CWallet::MarkDirty(const COutPoint &outpoint) { | ||||
const uint256 &newHash) { | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
const CWalletTx *pwtx = GetWalletTx(outpoint.unspentid); | |||||
auto mi = mapWallet.find(originalHash); | if (pwtx) { | ||||
// Get non-const | |||||
// There is a bug if MarkReplaced is not called on an existing wallet | CWalletTx &wtx = mapWallet[pwtx->GetHash()]; | ||||
// transaction. | wtx.MarkDirty(); | ||||
assert(mi != mapWallet.end()); | |||||
CWalletTx &wtx = (*mi).second; | |||||
// Ensure for now that we're not overwriting data. | |||||
assert(wtx.mapValue.count("replaced_by_txid") == 0); | |||||
wtx.mapValue["replaced_by_txid"] = newHash.ToString(); | |||||
CWalletDB walletdb(strWalletFile, "r+"); | |||||
bool success = true; | |||||
if (!walletdb.WriteTx(wtx)) { | |||||
LogPrintf("%s: Updating walletdb tx %s failed", __func__, | |||||
wtx.GetId().ToString()); | |||||
success = false; | |||||
} | } | ||||
NotifyTransactionChanged(this, originalHash, CT_UPDATED); | |||||
return success; | |||||
} | } | ||||
bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) { | bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); | CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); | ||||
uint256 hash = wtxIn.GetId(); | const txhash_t hash = wtxIn.GetHash(); | ||||
// 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<uint256, CWalletTx>::iterator, bool> ret = | std::pair<std::map<txhash_t, CWalletTx>::iterator, bool> ret = | ||||
mapWallet.insert(std::make_pair(hash, wtxIn)); | mapWallet.insert(std::make_pair(hash, wtxIn)); | ||||
CWalletTx &wtx = (*ret.first).second; | CWalletTx &wtx = (*ret.first).second; | ||||
wtx.BindWallet(this); | wtx.BindWallet(this); | ||||
bool fInsertedNew = ret.second; | bool fInsertedNew = ret.second; | ||||
if (fInsertedNew) { | if (fInsertedNew) { | ||||
wtx.nTimeReceived = GetAdjustedTime(); | wtx.nTimeReceived = GetAdjustedTime(); | ||||
wtx.nOrderPos = IncOrderPosNext(&walletdb); | wtx.nOrderPos = IncOrderPosNext(&walletdb); | ||||
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); | wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); | ||||
Show All 39 Lines | if (fInsertedNew) { | ||||
} | } | ||||
int64_t blocktime = | int64_t blocktime = | ||||
mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); | mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); | ||||
wtx.nTimeSmart = | wtx.nTimeSmart = | ||||
std::max(latestEntry, std::min(blocktime, latestNow)); | std::max(latestEntry, std::min(blocktime, latestNow)); | ||||
} else { | } else { | ||||
LogPrintf("AddToWallet(): found %s in block %s not in index\n", | LogPrintf("AddToWallet(): found %s in block %s not in index\n", | ||||
wtxIn.GetId().ToString(), wtxIn.hashBlock.ToString()); | wtxIn.GetHash().ToString(), | ||||
wtxIn.hashBlock.ToString()); | |||||
} | } | ||||
} | } | ||||
AddToSpends(hash); | AddToSpends(hash); | ||||
} | } | ||||
bool fUpdated = false; | bool fUpdated = false; | ||||
if (!fInsertedNew) { | if (!fInsertedNew) { | ||||
Show All 16 Lines | if (!fInsertedNew) { | ||||
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) { | if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) { | ||||
wtx.fFromMe = wtxIn.fFromMe; | wtx.fFromMe = wtxIn.fFromMe; | ||||
fUpdated = true; | fUpdated = true; | ||||
} | } | ||||
} | } | ||||
//// debug print | //// debug print | ||||
LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetId().ToString(), | LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), | ||||
(fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); | (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); | ||||
// Write to disk | // Write to disk | ||||
if ((fInsertedNew || fUpdated) && !walletdb.WriteTx(wtx)) { | if ((fInsertedNew || fUpdated) && !walletdb.WriteTx(wtx)) { | ||||
return false; | return false; | ||||
} | } | ||||
// 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, hash, fInsertedNew ? CT_NEW : CT_UPDATED); | NotifyTransactionChanged(this, wtx.GetHash(), | ||||
fInsertedNew ? CT_NEW : CT_UPDATED); | |||||
// 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 = GetArg("-walletnotify", ""); | std::string strCmd = GetArg("-walletnotify", ""); | ||||
if (!strCmd.empty()) { | if (!strCmd.empty()) { | ||||
boost::replace_all(strCmd, "%s", wtxIn.GetId().GetHex()); | boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); | ||||
// Thread runs free. | // Thread runs free. | ||||
boost::thread t(runCommand, strCmd); | boost::thread t(runCommand, strCmd); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CWallet::LoadToWallet(const CWalletTx &wtxIn) { | bool CWallet::LoadToWallet(const CWalletTx &wtxIn) { | ||||
uint256 txid = wtxIn.GetId(); | const txhash_t txhash = wtxIn.GetHash(); | ||||
mapWallet[txid] = wtxIn; | mapWallet[txhash] = wtxIn; | ||||
CWalletTx &wtx = mapWallet[txid]; | CWalletTx &wtx = mapWallet[txhash]; | ||||
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(txhash); | ||||
for (const CTxIn &txin : wtx.tx->vin) { | for (const CTxIn &txin : wtx.tx->vin) { | ||||
if (mapWallet.count(txin.prevout.hash)) { | const CWalletTx *prevtx = GetWalletTx(txin.prevout.unspentid); | ||||
CWalletTx &prevtx = mapWallet[txin.prevout.hash]; | if (prevtx && prevtx->nIndex == -1 && !prevtx->hashUnset()) { | ||||
if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { | MarkConflicted(prevtx->hashBlock, wtx.GetHash()); | ||||
MarkConflicted(prevtx.hashBlock, wtx.GetId()); | |||||
} | |||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Add a transaction to the wallet, or update it. pIndex and posInBlock should | * Add a transaction to the wallet, or update it. pIndex and posInBlock should | ||||
Show All 13 Lines | bool CWallet::AddToWalletIfInvolvingMe(const CTransaction &tx, | ||||
int posInBlock, bool fUpdate) { | int posInBlock, bool fUpdate) { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
if (posInBlock != -1) { | if (posInBlock != -1) { | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> | std::pair<TxSpends::const_iterator, TxSpends::const_iterator> | ||||
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.Getunspentid()) { | ||||
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.GetHash().ToString(), | ||||
pIndex->GetBlockHash().ToString(), | pIndex->GetBlockHash().ToString(), | ||||
range.first->second.ToString(), | range.first->second.ToString(), | ||||
range.first->first.hash.ToString(), | range.first->first.unspentid.ToString(), | ||||
range.first->first.n); | range.first->first.n); | ||||
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.GetHash()) != 0; | ||||
if (fExisted && !fUpdate) { | if (fExisted && !fUpdate) { | ||||
return false; | return false; | ||||
} | } | ||||
if (fExisted || IsMine(tx) || IsFromMe(tx)) { | if (fExisted || IsMine(tx) || IsFromMe(tx)) { | ||||
CWalletTx wtx(this, MakeTransactionRef(tx)); | CWalletTx wtx(this, MakeTransactionRef(tx)); | ||||
// Get merkle branch if transaction was found in a block. | // Get merkle branch if transaction was found in a block. | ||||
if (posInBlock != -1) { | if (posInBlock != -1) { | ||||
wtx.SetMerkleBranch(pIndex, posInBlock); | wtx.SetMerkleBranch(pIndex, posInBlock); | ||||
} | } | ||||
return AddToWallet(wtx, false); | return AddToWallet(wtx, false); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool CWallet::AbandonTransaction(const uint256 &hashTx) { | bool CWallet::AbandonTransaction(const txhash_t &hashTx) { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
CWalletDB walletdb(strWalletFile, "r+"); | CWalletDB walletdb(strWalletFile, "r+"); | ||||
std::set<uint256> todo; | std::set<txhash_t> todo; | ||||
std::set<uint256> done; | std::set<txhash_t> done; | ||||
// Can't mark abandoned if confirmed or in mempool. | // Can't mark abandoned if confirmed or in mempool. | ||||
assert(mapWallet.count(hashTx)); | const CWalletTx *origtx = GetWalletTx(hashTx); | ||||
CWalletTx &origtx = mapWallet[hashTx]; | const unspentid_t origunspentid = origtx->tx->Getunspentid(); | ||||
if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { | assert(origtx); | ||||
if (origtx->GetDepthInMainChain() > 0 || origtx->InMempool()) { | |||||
return false; | return false; | ||||
} | } | ||||
todo.insert(hashTx); | todo.insert(hashTx); | ||||
while (!todo.empty()) { | while (!todo.empty()) { | ||||
uint256 now = *todo.begin(); | txhash_t 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[now]; | ||||
unspentid_t unspentid = wtx.tx->Getunspentid(); | |||||
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. | ||||
assert(!wtx.InMempool()); | assert(!wtx.InMempool()); | ||||
wtx.nIndex = -1; | wtx.nIndex = -1; | ||||
wtx.setAbandoned(); | wtx.setAbandoned(); | ||||
wtx.MarkDirty(); | wtx.MarkDirty(); | ||||
walletdb.WriteTx(wtx); | walletdb.WriteTx(wtx); | ||||
NotifyTransactionChanged(this, wtx.GetId(), CT_UPDATED); | NotifyTransactionChanged(this, now, 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(origunspentid, 0)); | ||||
while (iter != mapTxSpends.end() && iter->first.hash == now) { | while (iter != mapTxSpends.end() && | ||||
iter->first.unspentid == unspentid) { | |||||
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)) | MarkDirty(txin.prevout); | ||||
mapWallet[txin.prevout.hash].MarkDirty(); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CWallet::MarkConflicted(const uint256 &hashBlock, const uint256 &hashTx) { | void CWallet::MarkConflicted(const uint256 &hashBlock, const txhash_t &hashTx) { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
int conflictconfirms = 0; | int conflictconfirms = 0; | ||||
if (mapBlockIndex.count(hashBlock)) { | if (mapBlockIndex.count(hashBlock)) { | ||||
CBlockIndex *pindex = mapBlockIndex[hashBlock]; | CBlockIndex *pindex = mapBlockIndex[hashBlock]; | ||||
if (chainActive.Contains(pindex)) { | if (chainActive.Contains(pindex)) { | ||||
conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); | conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); | ||||
} | } | ||||
} | } | ||||
// If number of conflict confirms cannot be determined, this means that the | // If number of conflict confirms cannot be determined, this means that the | ||||
// block is still unknown or not yet part of the main chain, for example | // block is still unknown or not yet part of the main chain, for example | ||||
// when loading the wallet during a reindex. Do nothing in that case. | // when loading the wallet during a reindex. Do nothing in that case. | ||||
if (conflictconfirms >= 0) { | if (conflictconfirms >= 0) { | ||||
return; | return; | ||||
} | } | ||||
// Do not flush the wallet here for performance reasons | // Do not flush the wallet here for performance reasons | ||||
CWalletDB walletdb(strWalletFile, "r+", false); | CWalletDB walletdb(strWalletFile, "r+", false); | ||||
std::set<uint256> todo; | std::set<txhash_t> todo; | ||||
std::set<uint256> done; | std::set<txhash_t> done; | ||||
todo.insert(hashTx); | todo.insert(hashTx); | ||||
while (!todo.empty()) { | while (!todo.empty()) { | ||||
uint256 now = *todo.begin(); | txhash_t 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[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); | ||||
unspentid_t unspentid = wtx.tx->Getunspentid(); | |||||
// 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(unspentid, 0)); | ||||
while (iter != mapTxSpends.end() && iter->first.hash == now) { | while (iter != mapTxSpends.end() && | ||||
iter->first.unspentid == unspentid) { | |||||
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)) { | MarkDirty(txin.prevout); | ||||
mapWallet[txin.prevout.hash].MarkDirty(); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void CWallet::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, | void CWallet::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, | ||||
int posInBlock) { | int posInBlock) { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true)) { | if (!AddToWalletIfInvolvingMe(tx, 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)) | MarkDirty(txin.prevout); | ||||
mapWallet[txin.prevout.hash].MarkDirty(); | |||||
} | } | ||||
} | } | ||||
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 = | const CWalletTx *wtx = GetWalletTx(txin.prevout.unspentid); | ||||
mapWallet.find(txin.prevout.hash); | if (wtx) { | ||||
if (mi != mapWallet.end()) { | if (txin.prevout.n < wtx->tx->vout.size()) { | ||||
const CWalletTx &prev = (*mi).second; | return IsMine(wtx->tx->vout[txin.prevout.n]); | ||||
if (txin.prevout.n < prev.tx->vout.size()) { | |||||
return IsMine(prev.tx->vout[txin.prevout.n]); | |||||
} | } | ||||
} | } | ||||
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. | ||||
CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter &filter) const { | CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter &filter) const { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::map<uint256, CWalletTx>::const_iterator mi = | const CWalletTx *wtx = GetWalletTx(txin.prevout.unspentid); | ||||
mapWallet.find(txin.prevout.hash); | if (wtx) { | ||||
if (mi != mapWallet.end()) { | if (txin.prevout.n < wtx->tx->vout.size()) { | ||||
const CWalletTx &prev = (*mi).second; | if (IsMine(wtx->tx->vout[txin.prevout.n]) & filter) { | ||||
if (txin.prevout.n < prev.tx->vout.size()) { | return wtx->tx->vout[txin.prevout.n].nValue.GetSatoshis(); | ||||
if (IsMine(prev.tx->vout[txin.prevout.n]) & filter) { | |||||
return prev.tx->vout[txin.prevout.n].nValue.GetSatoshis(); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
return 0; | return 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 | CAmount 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); | |||||
if (mi == mapWallet.end()) { | const CWalletTx *prev = GetWalletTx(txin.prevout.unspentid); | ||||
if (!prev) { | |||||
// 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; | if (txin.prevout.n >= prev->tx->vout.size()) { | ||||
if (txin.prevout.n >= 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.n]) & filter)) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
CAmount CWallet::GetCredit(const CTransaction &tx, | CAmount CWallet::GetCredit(const CTransaction &tx, | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | if (IsCoinBase()) { | ||||
pwallet->mapRequestCount.find(hashBlock); | pwallet->mapRequestCount.find(hashBlock); | ||||
if (mi != pwallet->mapRequestCount.end()) { | if (mi != pwallet->mapRequestCount.end()) { | ||||
nRequests = (*mi).second; | nRequests = (*mi).second; | ||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
// Did anyone request this transaction? | // Did anyone request this transaction? | ||||
std::map<uint256, int>::const_iterator mi = | std::map<uint256, int>::const_iterator mi = | ||||
pwallet->mapRequestCount.find(GetId()); | pwallet->mapRequestCount.find(GetHash()); | ||||
if (mi != pwallet->mapRequestCount.end()) { | if (mi != pwallet->mapRequestCount.end()) { | ||||
nRequests = (*mi).second; | nRequests = (*mi).second; | ||||
// How about the block it's in? | // How about the block it's in? | ||||
if (nRequests == 0 && !hashUnset()) { | if (nRequests == 0 && !hashUnset()) { | ||||
std::map<uint256, int>::const_iterator _mi = | std::map<uint256, int>::const_iterator _mi = | ||||
pwallet->mapRequestCount.find(hashBlock); | pwallet->mapRequestCount.find(hashBlock); | ||||
if (_mi != pwallet->mapRequestCount.end()) { | if (_mi != pwallet->mapRequestCount.end()) { | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | for (unsigned int i = 0; i < tx->vout.size(); ++i) { | ||||
} | } | ||||
// In either case, we need to get the destination address. | // In either case, we need to get the destination address. | ||||
CTxDestination address; | CTxDestination address; | ||||
if (!ExtractDestination(txout.scriptPubKey, address) && | if (!ExtractDestination(txout.scriptPubKey, address) && | ||||
!txout.scriptPubKey.IsUnspendable()) { | !txout.scriptPubKey.IsUnspendable()) { | ||||
LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, " | LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, " | ||||
"txid %s\n", | "txhash %s\n", | ||||
this->GetId().ToString()); | this->GetHash().ToString()); | ||||
address = CNoDestination(); | address = CNoDestination(); | ||||
} | } | ||||
COutputEntry output = {address, txout.nValue.GetSatoshis(), (int)i}; | COutputEntry output = {address, txout.nValue.GetSatoshis(), (int)i}; | ||||
// If we are debited by the transaction, add the output as a "sent" | // If we are debited by the transaction, add the output as a "sent" | ||||
// entry. | // entry. | ||||
if (nDebit > 0) { | if (nDebit > 0) { | ||||
▲ Show 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | if (!fBroadcastTransactions) { | ||||
return; | return; | ||||
} | } | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
std::map<int64_t, CWalletTx *> mapSorted; | std::map<int64_t, CWalletTx *> mapSorted; | ||||
// Sort pending wallet transactions based on their initial wallet insertion | // Sort pending wallet transactions based on their initial wallet insertion | ||||
// order. | // order. | ||||
for (std::pair<const uint256, CWalletTx> &item : mapWallet) { | for (std::pair<const txhash_t, CWalletTx> &item : mapWallet) { | ||||
const uint256 &wtxid = item.first; | const uint256 &wtxhash = item.first; | ||||
CWalletTx &wtx = item.second; | CWalletTx &wtx = item.second; | ||||
assert(wtx.GetId() == wtxid); | assert(wtx.GetHash() == wtxhash); | ||||
int nDepth = wtx.GetDepthInMainChain(); | int nDepth = wtx.GetDepthInMainChain(); | ||||
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) { | if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) { | ||||
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); | mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); | ||||
} | } | ||||
} | } | ||||
Show All 11 Lines | bool CWalletTx::RelayWalletTransaction(CConnman *connman) { | ||||
assert(pwallet->GetBroadcastTransactions()); | assert(pwallet->GetBroadcastTransactions()); | ||||
if (IsCoinBase() || isAbandoned() || GetDepthInMainChain() != 0) { | if (IsCoinBase() || isAbandoned() || GetDepthInMainChain() != 0) { | ||||
return false; | return false; | ||||
} | } | ||||
CValidationState state; | CValidationState state; | ||||
// GetDepthInMainChain already catches known conflicts. | // GetDepthInMainChain already catches known conflicts. | ||||
if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) { | if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) { | ||||
LogPrintf("Relaying wtx %s\n", GetId().ToString()); | LogPrintf("Relaying wtx %s\n", GetHash().ToString()); | ||||
if (connman) { | if (connman) { | ||||
CInv inv(MSG_TX, GetId()); | CInv inv(MSG_TX, GetHash()); | ||||
connman->ForEachNode( | connman->ForEachNode( | ||||
[&inv](CNode *pnode) { pnode->PushInventory(inv); }); | [&inv](CNode *pnode) { pnode->PushInventory(inv); }); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
std::set<uint256> CWalletTx::GetConflicts() const { | std::set<txhash_t> CWalletTx::GetConflicts() const { | ||||
std::set<uint256> result; | std::set<txhash_t> result; | ||||
if (pwallet != nullptr) { | if (pwallet != nullptr) { | ||||
uint256 myHash = GetId(); | txhash_t myHash = tx->GetHash(); | ||||
result = pwallet->GetConflicts(myHash); | result = pwallet->GetConflicts(myHash); | ||||
result.erase(myHash); | result.erase(myHash); | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
CAmount CWalletTx::GetDebit(const isminefilter &filter) const { | CAmount CWalletTx::GetDebit(const isminefilter &filter) const { | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | if (IsCoinBase() && GetBlocksToMaturity() > 0) { | ||||
return 0; | return 0; | ||||
} | } | ||||
if (fUseCache && fAvailableCreditCached) { | if (fUseCache && fAvailableCreditCached) { | ||||
return nAvailableCreditCached; | return nAvailableCreditCached; | ||||
} | } | ||||
CAmount nCredit = 0; | CAmount nCredit = 0; | ||||
uint256 hashTx = GetId(); | unspentid_t unspentid = tx->Getunspentid(); | ||||
for (unsigned int i = 0; i < tx->vout.size(); i++) { | for (unsigned int i = 0; i < tx->vout.size(); i++) { | ||||
if (!pwallet->IsSpent(hashTx, i)) { | if (!pwallet->IsSpent(COutPoint(unspentid, i))) { | ||||
const CTxOut &txout = tx->vout[i]; | const CTxOut &txout = tx->vout[i]; | ||||
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); | nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); | ||||
if (!MoneyRange(nCredit)) { | if (!MoneyRange(nCredit)) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"CWalletTx::GetAvailableCredit() : value out of range"); | "CWalletTx::GetAvailableCredit() : value out of range"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Show All 29 Lines | if (IsCoinBase() && GetBlocksToMaturity() > 0) { | ||||
return 0; | return 0; | ||||
} | } | ||||
if (fUseCache && fAvailableWatchCreditCached) { | if (fUseCache && fAvailableWatchCreditCached) { | ||||
return nAvailableWatchCreditCached; | return nAvailableWatchCreditCached; | ||||
} | } | ||||
CAmount nCredit = 0; | CAmount nCredit = 0; | ||||
unspentid_t unspentid = tx->Getunspentid(); | |||||
for (unsigned int i = 0; i < tx->vout.size(); i++) { | for (unsigned int i = 0; i < tx->vout.size(); i++) { | ||||
if (!pwallet->IsSpent(GetId(), i)) { | if (!pwallet->IsSpent(COutPoint(unspentid, i))) { | ||||
const CTxOut &txout = tx->vout[i]; | const CTxOut &txout = tx->vout[i]; | ||||
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); | nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); | ||||
if (!MoneyRange(nCredit)) { | if (!MoneyRange(nCredit)) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"CWalletTx::GetAvailableCredit() : value out of range"); | "CWalletTx::GetAvailableCredit() : value out of range"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Show All 10 Lines | CAmount CWalletTx::GetChange() const { | ||||
nChangeCached = pwallet->GetChange(*this); | nChangeCached = pwallet->GetChange(*this); | ||||
fChangeCached = true; | fChangeCached = true; | ||||
return nChangeCached; | return nChangeCached; | ||||
} | } | ||||
bool CWalletTx::InMempool() const { | bool CWalletTx::InMempool() const { | ||||
LOCK(mempool.cs); | LOCK(mempool.cs); | ||||
if (mempool.exists(GetId())) { | if (mempool.exists(GetHash())) { | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool CWalletTx::IsTrusted() const { | bool CWalletTx::IsTrusted() const { | ||||
// Quick answer in most cases | // Quick answer in most cases | ||||
Show All 19 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.unspentid); | ||||
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.n]; | ||||
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) { | if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) { | ||||
return false; | return false; | ||||
} | } | ||||
Show All 11 Lines | bool CWalletTx::IsEquivalentTo(const CWalletTx &_tx) const { | ||||
for (unsigned int i = 0; i < tx2.vin.size(); i++) { | for (unsigned int i = 0; i < tx2.vin.size(); i++) { | ||||
tx2.vin[i].scriptSig = CScript(); | tx2.vin[i].scriptSig = CScript(); | ||||
} | } | ||||
return CTransaction(tx1) == CTransaction(tx2); | return CTransaction(tx1) == CTransaction(tx2); | ||||
} | } | ||||
std::vector<uint256> | std::vector<txhash_t> | ||||
CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman *connman) { | CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman *connman) { | ||||
std::vector<uint256> result; | std::vector<txhash_t> result; | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
// Sort them in chronological order | // Sort them in chronological order | ||||
std::multimap<unsigned int, CWalletTx *> mapSorted; | std::multimap<unsigned int, CWalletTx *> mapSorted; | ||||
for (std::pair<const uint256, CWalletTx> &item : mapWallet) { | for (std::pair<const txhash_t, CWalletTx> &item : mapWallet) { | ||||
CWalletTx &wtx = item.second; | CWalletTx &wtx = item.second; | ||||
// Don't rebroadcast if newer than nTime: | // Don't rebroadcast if newer than nTime: | ||||
if (wtx.nTimeReceived > nTime) { | if (wtx.nTimeReceived > nTime) { | ||||
continue; | continue; | ||||
} | } | ||||
mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx)); | mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx)); | ||||
} | } | ||||
for (std::pair<const unsigned int, CWalletTx *> &item : mapSorted) { | for (std::pair<const unsigned int, CWalletTx *> &item : mapSorted) { | ||||
CWalletTx &wtx = *item.second; | CWalletTx &wtx = *item.second; | ||||
if (wtx.RelayWalletTransaction(connman)) { | if (wtx.RelayWalletTransaction(connman)) { | ||||
result.push_back(wtx.GetId()); | result.push_back(wtx.GetHash()); | ||||
} | } | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, | void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, | ||||
CConnman *connman) { | CConnman *connman) { | ||||
Show All 13 Lines | void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, | ||||
if (nBestBlockTime < nLastResend) { | if (nBestBlockTime < nLastResend) { | ||||
return; | return; | ||||
} | } | ||||
nLastResend = GetTime(); | nLastResend = GetTime(); | ||||
// Rebroadcast unconfirmed txes older than 5 minutes before the last block | // Rebroadcast unconfirmed txes older than 5 minutes before the last block | ||||
// was found: | // was found: | ||||
std::vector<uint256> relayed = | std::vector<txhash_t> relayed = | ||||
ResendWalletTransactionsBefore(nBestBlockTime - 5 * 60, connman); | ResendWalletTransactionsBefore(nBestBlockTime - 5 * 60, connman); | ||||
if (!relayed.empty()) { | if (!relayed.empty()) { | ||||
LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, | LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, | ||||
relayed.size()); | relayed.size()); | ||||
} | } | ||||
} | } | ||||
/** @} */ // end of mapWallet | /** @} */ // end of mapWallet | ||||
/** | /** | ||||
* @defgroup Actions | * @defgroup Actions | ||||
* | * | ||||
* @{ | * @{ | ||||
*/ | */ | ||||
CAmount CWallet::GetBalance() const { | CAmount CWallet::GetBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
CAmount nTotal = 0; | CAmount nTotal = 0; | ||||
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); ++it) { | it != mapWallet.end(); ++it) { | ||||
const CWalletTx *pcoin = &(*it).second; | const CWalletTx *pcoin = &(*it).second; | ||||
if (pcoin->IsTrusted()) { | if (pcoin->IsTrusted()) { | ||||
nTotal += pcoin->GetAvailableCredit(); | nTotal += pcoin->GetAvailableCredit(); | ||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
CAmount CWallet::GetUnconfirmedBalance() const { | CAmount CWallet::GetUnconfirmedBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
CAmount nTotal = 0; | CAmount nTotal = 0; | ||||
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); ++it) { | it != mapWallet.end(); ++it) { | ||||
const CWalletTx *pcoin = &(*it).second; | const CWalletTx *pcoin = &(*it).second; | ||||
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && | if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && | ||||
pcoin->InMempool()) { | pcoin->InMempool()) { | ||||
nTotal += pcoin->GetAvailableCredit(); | nTotal += pcoin->GetAvailableCredit(); | ||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
CAmount CWallet::GetImmatureBalance() const { | CAmount CWallet::GetImmatureBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
CAmount nTotal = 0; | CAmount nTotal = 0; | ||||
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); ++it) { | it != mapWallet.end(); ++it) { | ||||
const CWalletTx *pcoin = &(*it).second; | const CWalletTx *pcoin = &(*it).second; | ||||
nTotal += pcoin->GetImmatureCredit(); | nTotal += pcoin->GetImmatureCredit(); | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
CAmount CWallet::GetWatchOnlyBalance() const { | CAmount CWallet::GetWatchOnlyBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
CAmount nTotal = 0; | CAmount nTotal = 0; | ||||
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); ++it) { | it != mapWallet.end(); ++it) { | ||||
const CWalletTx *pcoin = &(*it).second; | const CWalletTx *pcoin = &(*it).second; | ||||
if (pcoin->IsTrusted()) { | if (pcoin->IsTrusted()) { | ||||
nTotal += pcoin->GetAvailableWatchOnlyCredit(); | nTotal += pcoin->GetAvailableWatchOnlyCredit(); | ||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const { | CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
CAmount nTotal = 0; | CAmount nTotal = 0; | ||||
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); ++it) { | it != mapWallet.end(); ++it) { | ||||
const CWalletTx *pcoin = &(*it).second; | const CWalletTx *pcoin = &(*it).second; | ||||
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && | if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && | ||||
pcoin->InMempool()) { | pcoin->InMempool()) { | ||||
nTotal += pcoin->GetAvailableWatchOnlyCredit(); | nTotal += pcoin->GetAvailableWatchOnlyCredit(); | ||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
CAmount CWallet::GetImmatureWatchOnlyBalance() const { | CAmount CWallet::GetImmatureWatchOnlyBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
CAmount nTotal = 0; | CAmount nTotal = 0; | ||||
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); ++it) { | it != mapWallet.end(); ++it) { | ||||
const CWalletTx *pcoin = &(*it).second; | const CWalletTx *pcoin = &(*it).second; | ||||
nTotal += pcoin->GetImmatureWatchOnlyCredit(); | nTotal += pcoin->GetImmatureWatchOnlyCredit(); | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlyConfirmed, | void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlyConfirmed, | ||||
const CCoinControl *coinControl, | const CCoinControl *coinControl, | ||||
bool fIncludeZeroValue) const { | bool fIncludeZeroValue) const { | ||||
vCoins.clear(); | vCoins.clear(); | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); ++it) { | it != mapWallet.end(); ++it) { | ||||
const uint256 &wtxid = it->first; | |||||
const CWalletTx *pcoin = &(*it).second; | const CWalletTx *pcoin = &(*it).second; | ||||
const unspentid_t &unspentid = pcoin->tx->Getunspentid(); | |||||
if (!CheckFinalTx(*pcoin)) { | if (!CheckFinalTx(*pcoin)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (fOnlyConfirmed && !pcoin->IsTrusted()) { | if (fOnlyConfirmed && !pcoin->IsTrusted()) { | ||||
continue; | continue; | ||||
} | } | ||||
Show All 11 Lines | for (std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
// It's possible for these to be conflicted via ancestors which we may | // It's possible for these to be conflicted via ancestors which we may | ||||
// never be able to detect. | // never be able to detect. | ||||
if (nDepth == 0 && !pcoin->InMempool()) { | if (nDepth == 0 && !pcoin->InMempool()) { | ||||
continue; | continue; | ||||
} | } | ||||
// Bitcoin-ABC: Removed check that prevents consideration of coins from | // Bitcoin-ABC: Removed check that prevents consideration of coins from | ||||
// transactions that are replacing other transactions. This check based | // transactions that are replacing other transactions. This check based | ||||
// on pcoin->mapValue.count("replaces_txid") which was not being set | // on pcoin->mapValue.count("replaces_txhash") which was not being set | ||||
// anywhere. | // anywhere. | ||||
// Similarly, we should not consider coins from transactions that have | // Similarly, we should not consider coins from transactions that have | ||||
// been replaced. In the example above, we would want to prevent | // been replaced. In the example above, we would want to prevent | ||||
// creation of a transaction A' spending an output of A, because if | // creation of a transaction A' spending an output of A, because if | ||||
// transaction B were initially confirmed, conflicting with A and A', we | // transaction B were initially confirmed, conflicting with A and A', we | ||||
// wouldn't want to the user to create a transaction D intending to | // wouldn't want to the user to create a transaction D intending to | ||||
// replace A', but potentially resulting in a scenario where A, A', and | // replace A', but potentially resulting in a scenario where A, A', and | ||||
// D could all be accepted (instead of just B and D, or just A and A' | // D could all be accepted (instead of just B and D, or just A and A' | ||||
// like the user would want). | // like the user would want). | ||||
// Bitcoin-ABC: retained this check as 'replaced_by_txid' is still set | // Bitcoin-ABC: retained this check as 'replaced_by_txhash' is still set | ||||
// in the wallet code. | // in the wallet code. | ||||
if (nDepth == 0 && fOnlyConfirmed && | if (nDepth == 0 && fOnlyConfirmed && | ||||
pcoin->mapValue.count("replaced_by_txid")) { | pcoin->mapValue.count("replaced_by_txhash")) { | ||||
continue; | continue; | ||||
} | } | ||||
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { | for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { | ||||
isminetype mine = IsMine(pcoin->tx->vout[i]); | isminetype mine = IsMine(pcoin->tx->vout[i]); | ||||
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && | if (!(IsSpent(COutPoint(unspentid, i))) && mine != ISMINE_NO && | ||||
!IsLockedCoin((*it).first, i) && | !IsLockedCoin(COutPoint(unspentid, i)) && | ||||
(pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && | (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && | ||||
(!coinControl || !coinControl->HasSelected() || | (!coinControl || !coinControl->HasSelected() || | ||||
coinControl->fAllowOtherInputs || | coinControl->fAllowOtherInputs || | ||||
coinControl->IsSelected(COutPoint((*it).first, i)))) { | coinControl->IsSelected(COutPoint(unspentid, i)))) { | ||||
vCoins.push_back(COutput( | vCoins.push_back(COutput( | ||||
pcoin, i, nDepth, | pcoin, i, nDepth, | ||||
((mine & ISMINE_SPENDABLE) != ISMINE_NO) || | ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || | ||||
(coinControl && coinControl->fAllowWatchOnly && | (coinControl && coinControl->fAllowWatchOnly && | ||||
(mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), | (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), | ||||
(mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != | (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != | ||||
ISMINE_NO)); | ISMINE_NO)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | for (const COutput &output : vCoins) { | ||||
const CWalletTx *pcoin = output.tx; | const CWalletTx *pcoin = output.tx; | ||||
if (output.nDepth < | if (output.nDepth < | ||||
(pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) { | (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!mempool.TransactionWithinChainLimit(pcoin->GetId(), | if (!mempool.TransactionWithinChainLimit(pcoin->GetHash(), | ||||
nMaxAncestors)) { | nMaxAncestors)) { | ||||
continue; | continue; | ||||
} | } | ||||
int i = output.i; | int i = output.i; | ||||
CAmount n = pcoin->tx->vout[i].nValue.GetSatoshis(); | CAmount n = pcoin->tx->vout[i].nValue.GetSatoshis(); | ||||
std::pair<CAmount, std::pair<const CWalletTx *, unsigned int>> coin = | std::pair<CAmount, std::pair<const CWalletTx *, unsigned int>> coin = | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | bool CWallet::SelectCoins( | ||||
CAmount nValueFromPresetInputs = 0; | CAmount nValueFromPresetInputs = 0; | ||||
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 = | |||||
mapWallet.find(outpoint.hash); | const CWalletTx *pcoin = GetWalletTx(outpoint.unspentid); | ||||
if (it == mapWallet.end()) { | if (!pcoin) { | ||||
// TODO: Allow non-wallet inputs | // TODO: Allow non-wallet inputs | ||||
return false; | return false; | ||||
} | } | ||||
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.n) { | ||||
return false; | return false; | ||||
} | } | ||||
nValueFromPresetInputs += | nValueFromPresetInputs += | ||||
pcoin->tx->vout[outpoint.n].nValue.GetSatoshis(); | pcoin->tx->vout[outpoint.n].nValue.GetSatoshis(); | ||||
setPresetCoins.insert(std::make_pair(pcoin, outpoint.n)); | setPresetCoins.insert(std::make_pair(pcoin, outpoint.n)); | ||||
▲ Show 20 Lines • Show All 351 Lines • ▼ Show 20 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
reservekey.ReturnKey(); | reservekey.ReturnKey(); | ||||
} | } | ||||
// 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( | ||||
CTxIn(coin.first->GetId(), coin.second, CScript(), | coin.first->tx->Getunspentid(), coin.second, CScript(), | ||||
std::numeric_limits<unsigned int>::max() - 1)); | std::numeric_limits<unsigned int>::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 165 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]; | const CWalletTx *prev = GetWalletTx(txin.prevout.unspentid); | ||||
assert(prev); | |||||
CWalletTx &coin = mapWallet[prev->GetHash()]; | |||||
coin.BindWallet(this); | coin.BindWallet(this); | ||||
NotifyTransactionChanged(this, coin.GetId(), CT_UPDATED); | NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); | ||||
} | } | ||||
// Track how many getdata requests our transaction gets. | // Track how many getdata requests our transaction gets. | ||||
mapRequestCount[wtxNew.GetId()] = 0; | mapRequestCount[wtxNew.GetHash()] = 0; | ||||
if (fBroadcastTransactions) { | if (fBroadcastTransactions) { | ||||
// Broadcast | // Broadcast | ||||
if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { | if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { | ||||
LogPrintf("CommitTransaction(): Transaction cannot be " | LogPrintf("CommitTransaction(): Transaction cannot be " | ||||
"broadcast immediately, %s\n", | "broadcast 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 95 Lines • ▼ Show 20 Lines | DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | ||||
fFirstRunRet = !vchDefaultKey.IsValid(); | fFirstRunRet = !vchDefaultKey.IsValid(); | ||||
uiInterface.LoadWallet(this); | uiInterface.LoadWallet(this); | ||||
return DB_LOAD_OK; | return DB_LOAD_OK; | ||||
} | } | ||||
DBErrors CWallet::ZapSelectTx(std::vector<uint256> &vHashIn, | DBErrors CWallet::ZapSelectTx(std::vector<txhash_t> &vHashIn, | ||||
std::vector<uint256> &vHashOut) { | std::vector<txhash_t> &vHashOut) { | ||||
if (!fFileBacked) { | if (!fFileBacked) { | ||||
return DB_LOAD_OK; | return DB_LOAD_OK; | ||||
} | } | ||||
DBErrors nZapSelectTxRet = | DBErrors nZapSelectTxRet = | ||||
CWalletDB(strWalletFile, "cr+").ZapSelectTx(this, vHashIn, vHashOut); | CWalletDB(strWalletFile, "cr+").ZapSelectTx(this, vHashIn, vHashOut); | ||||
if (nZapSelectTxRet == DB_NEED_REWRITE) { | if (nZapSelectTxRet == DB_NEED_REWRITE) { | ||||
if (CDB::Rewrite(strWalletFile, "\x04pool")) { | if (CDB::Rewrite(strWalletFile, "\x04pool")) { | ||||
▲ Show 20 Lines • Show All 266 Lines • ▼ Show 20 Lines | int64_t CWallet::GetOldestKeyPoolTime() { | ||||
assert(keypool.vchPubKey.IsValid()); | assert(keypool.vchPubKey.IsValid()); | ||||
return keypool.nTime; | return keypool.nTime; | ||||
} | } | ||||
std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() { | std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() { | ||||
std::map<CTxDestination, CAmount> balances; | std::map<CTxDestination, CAmount> balances; | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
for (std::pair<uint256, CWalletTx> walletEntry : mapWallet) { | for (std::pair<txhash_t, CWalletTx> walletEntry : mapWallet) { | ||||
CWalletTx *pcoin = &walletEntry.second; | CWalletTx *pcoin = &walletEntry.second; | ||||
const unspentid_t &unspentid = pcoin->tx->Getunspentid(); | |||||
if (!pcoin->IsTrusted()) { | if (!pcoin->IsTrusted()) { | ||||
continue; | continue; | ||||
} | } | ||||
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) { | if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) { | ||||
continue; | continue; | ||||
} | } | ||||
int nDepth = pcoin->GetDepthInMainChain(); | int nDepth = pcoin->GetDepthInMainChain(); | ||||
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) { | if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) { | ||||
continue; | continue; | ||||
} | } | ||||
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { | for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { | ||||
CTxDestination addr; | CTxDestination addr; | ||||
if (!IsMine(pcoin->tx->vout[i])) { | if (!IsMine(pcoin->tx->vout[i])) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) { | if (!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) { | ||||
continue; | continue; | ||||
} | } | ||||
Amount n = | Amount n = IsSpent(COutPoint(unspentid, i)) | ||||
IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue; | ? 0 | ||||
: pcoin->tx->vout[i].nValue; | |||||
if (!balances.count(addr)) balances[addr] = 0; | if (!balances.count(addr)) balances[addr] = 0; | ||||
balances[addr] += n.GetSatoshis(); | balances[addr] += n.GetSatoshis(); | ||||
} | } | ||||
} | } | ||||
return balances; | return balances; | ||||
} | } | ||||
Show All 12 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] | const CWalletTx *wtx = GetWalletTx(txin.prevout.unspentid); | ||||
.tx->vout[txin.prevout.n] | assert(wtx); | ||||
.scriptPubKey, | if (!ExtractDestination( | ||||
address)) { | wtx->tx->vout[txin.prevout.n].scriptPubKey, address)) { | ||||
continue; | continue; | ||||
} | } | ||||
grouping.insert(address); | grouping.insert(address); | ||||
any_mine = true; | any_mine = true; | ||||
} | } | ||||
// Group change with input addresses. | // Group change with input addresses. | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
CAmount CWallet::GetAccountBalance(CWalletDB &walletdb, | CAmount CWallet::GetAccountBalance(CWalletDB &walletdb, | ||||
const std::string &strAccount, int nMinDepth, | const std::string &strAccount, int nMinDepth, | ||||
const isminefilter &filter) { | const isminefilter &filter) { | ||||
CAmount nBalance = 0; | CAmount nBalance = 0; | ||||
// Tally wallet transactions. | // Tally wallet transactions. | ||||
for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); ++it) { | it != mapWallet.end(); ++it) { | ||||
const CWalletTx &wtx = (*it).second; | const CWalletTx &wtx = (*it).second; | ||||
if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || | if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || | ||||
wtx.GetDepthInMainChain() < 0) { | wtx.GetDepthInMainChain() < 0) { | ||||
continue; | continue; | ||||
} | } | ||||
CAmount nReceived, nSent, nFee; | CAmount nReceived, nSent, nFee; | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | for (const int64_t &id : setKeyPool) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": unknown key in key pool"); | ": unknown key in key pool"); | ||||
} | } | ||||
setAddress.insert(keyID); | setAddress.insert(keyID); | ||||
} | } | ||||
} | } | ||||
void CWallet::UpdatedTransaction(const uint256 &hashTx) { | void CWallet::UpdatedTransaction(const txhash_t &hashTx) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
// Only notify UI if this transaction is in this wallet. | // Only notify UI if this transaction is in this wallet. | ||||
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx); | const CWalletTx *wtx = GetWalletTx(hashTx); | ||||
if (mi != mapWallet.end()) { | if (wtx) { | ||||
NotifyTransactionChanged(this, hashTx, CT_UPDATED); | NotifyTransactionChanged(this, hashTx, CT_UPDATED); | ||||
} | } | ||||
} | } | ||||
void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script) { | void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script) { | ||||
std::shared_ptr<CReserveKey> rKey(new CReserveKey(this)); | std::shared_ptr<CReserveKey> rKey(new CReserveKey(this)); | ||||
CPubKey pubkey; | CPubKey pubkey; | ||||
if (!rKey->GetReservedKey(pubkey)) { | if (!rKey->GetReservedKey(pubkey)) { | ||||
Show All 17 Lines | |||||
} | } | ||||
void CWallet::UnlockAllCoins() { | void CWallet::UnlockAllCoins() { | ||||
// setLockedCoins | // setLockedCoins | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
setLockedCoins.clear(); | setLockedCoins.clear(); | ||||
} | } | ||||
bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const { | bool CWallet::IsLockedCoin(const COutPoint &outpoint) const { | ||||
// setLockedCoins | // setLockedCoins | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
COutPoint outpt(hash, n); | |||||
return setLockedCoins.count(outpt) > 0; | return setLockedCoins.count(outpoint) > 0; | ||||
} | } | ||||
void CWallet::ListLockedCoins(std::vector<COutPoint> &vOutpts) { | void CWallet::ListLockedCoins(std::vector<COutPoint> &vOutpts) { | ||||
// setLockedCoins | // setLockedCoins | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
for (std::set<COutPoint>::iterator it = setLockedCoins.begin(); | for (std::set<COutPoint>::iterator it = setLockedCoins.begin(); | ||||
it != setLockedCoins.end(); it++) { | it != setLockedCoins.end(); it++) { | ||||
COutPoint outpt = (*it); | COutPoint outpt = (*it); | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | void CWallet::GetKeyBirthTimes( | ||||
// If there are no such keys, we're done. | // If there are no such keys, we're done. | ||||
if (mapKeyFirstBlock.empty()) { | if (mapKeyFirstBlock.empty()) { | ||||
return; | return; | ||||
} | } | ||||
// Find first block that affects those keys, if there are any left. | // Find first block that affects those keys, if there are any left. | ||||
std::vector<CKeyID> vAffected; | std::vector<CKeyID> vAffected; | ||||
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); | for (std::map<txhash_t, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
it != mapWallet.end(); it++) { | it != mapWallet.end(); it++) { | ||||
// Iterate over all wallet transactions... | // Iterate over all wallet transactions... | ||||
const CWalletTx &wtx = (*it).second; | const CWalletTx &wtx = (*it).second; | ||||
BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); | BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); | ||||
if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { | if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { | ||||
// ... which are already in a block. | // ... which are already in a block. | ||||
int nHeight = blit->second->nHeight; | int nHeight = blit->second->nHeight; | ||||
for (const CTxOut &txout : wtx.tx->vout) { | for (const CTxOut &txout : wtx.tx->vout) { | ||||
▲ Show 20 Lines • Show All 136 Lines • ▼ Show 20 Lines | strUsage += | ||||
_("Specify wallet file (within data directory)") + " " + | _("Specify wallet file (within data directory)") + " " + | ||||
strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); | strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); | ||||
strUsage += HelpMessageOpt( | strUsage += HelpMessageOpt( | ||||
"-walletbroadcast", | "-walletbroadcast", | ||||
_("Make the wallet broadcast transactions") + " " + | _("Make the wallet broadcast transactions") + " " + | ||||
strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); | strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); | ||||
strUsage += HelpMessageOpt("-walletnotify=<cmd>", | strUsage += HelpMessageOpt("-walletnotify=<cmd>", | ||||
_("Execute command when a wallet transaction " | _("Execute command when a wallet transaction " | ||||
"changes (%s in cmd is replaced by TxID)")); | "changes (%s in cmd is replaced by txhash)")); | ||||
strUsage += HelpMessageOpt( | strUsage += HelpMessageOpt( | ||||
"-zapwallettxes=<mode>", | "-zapwallettxes=<mode>", | ||||
_("Delete all wallet transactions and only recover those parts of the " | _("Delete all wallet transactions and only recover those parts of the " | ||||
"blockchain through -rescan on startup") + | "blockchain through -rescan on startup") + | ||||
" " + _("(1 = keep tx meta data e.g. account owner and payment " | " " + _("(1 = keep tx meta data e.g. account owner and payment " | ||||
"request information, 2 = drop tx meta data)")); | "request information, 2 = drop tx meta data)")); | ||||
if (showDebug) { | if (showDebug) { | ||||
▲ Show 20 Lines • Show All 185 Lines • ▼ Show 20 Lines | if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { | ||||
CWalletDB::IncrementUpdateCounter(); | CWalletDB::IncrementUpdateCounter(); | ||||
// Restore wallet transaction metadata after -zapwallettxes=1 | // Restore wallet transaction metadata after -zapwallettxes=1 | ||||
if (GetBoolArg("-zapwallettxes", false) && | if (GetBoolArg("-zapwallettxes", false) && | ||||
GetArg("-zapwallettxes", "1") != "2") { | GetArg("-zapwallettxes", "1") != "2") { | ||||
CWalletDB walletdb(walletFile); | CWalletDB walletdb(walletFile); | ||||
for (const CWalletTx &wtxOld : vWtx) { | for (const CWalletTx &wtxOld : vWtx) { | ||||
uint256 txid = wtxOld.GetId(); | txhash_t txhash = wtxOld.GetHash(); | ||||
std::map<uint256, CWalletTx>::iterator mi = | std::map<txhash_t, CWalletTx>::iterator mi = | ||||
walletInstance->mapWallet.find(txid); | walletInstance->mapWallet.find(txhash); | ||||
if (mi != walletInstance->mapWallet.end()) { | if (mi != walletInstance->mapWallet.end()) { | ||||
const CWalletTx *copyFrom = &wtxOld; | const CWalletTx *copyFrom = &wtxOld; | ||||
CWalletTx *copyTo = &mi->second; | CWalletTx *copyTo = &mi->second; | ||||
copyTo->mapValue = copyFrom->mapValue; | copyTo->mapValue = copyFrom->mapValue; | ||||
copyTo->vOrderForm = copyFrom->vOrderForm; | copyTo->vOrderForm = copyFrom->vOrderForm; | ||||
copyTo->nTimeReceived = copyFrom->nTimeReceived; | copyTo->nTimeReceived = copyFrom->nTimeReceived; | ||||
copyTo->nTimeSmart = copyFrom->nTimeSmart; | copyTo->nTimeSmart = copyFrom->nTimeSmart; | ||||
copyTo->fFromMe = copyFrom->fFromMe; | copyTo->fFromMe = copyFrom->fFromMe; | ||||
▲ Show 20 Lines • Show All 298 Lines • Show Last 20 Lines |