Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | for (const std::shared_ptr<CWallet> &wallet : vpwallets) { | ||||
return wallet; | return wallet; | ||||
} | } | ||||
} | } | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
// Custom deleter for shared_ptr<CWallet>. | // Custom deleter for shared_ptr<CWallet>. | ||||
static void ReleaseWallet(CWallet *wallet) { | static void ReleaseWallet(CWallet *wallet) { | ||||
LogPrintf("Releasing wallet %s\n", wallet->GetName()); | wallet->WalletLogPrintf("Releasing wallet\n"); | ||||
wallet->BlockUntilSyncedToCurrentChain(); | wallet->BlockUntilSyncedToCurrentChain(); | ||||
wallet->Flush(); | wallet->Flush(); | ||||
delete wallet; | delete wallet; | ||||
} | } | ||||
static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10; | static const size_t OUTPUT_GROUP_MAX_ENTRIES = 10; | ||||
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; | const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; | ||||
▲ Show 20 Lines • Show All 273 Lines • ▼ Show 20 Lines | bool CWallet::LoadCScript(const CScript &redeemScript) { | ||||
/** | /** | ||||
* A sanity check was added in pull #3843 to avoid adding redeemScripts that | * A sanity check was added in pull #3843 to avoid adding redeemScripts that | ||||
* never can be redeemed. However, old wallets may still contain these. Do | * never can be redeemed. However, old wallets may still contain these. Do | ||||
* not add them to the wallet and warn. | * not add them to the wallet and warn. | ||||
*/ | */ | ||||
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { | if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { | ||||
std::string strAddr = | std::string strAddr = | ||||
EncodeDestination(CScriptID(redeemScript), GetConfig()); | EncodeDestination(CScriptID(redeemScript), GetConfig()); | ||||
LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i " | WalletLogPrintf("%s: Warning: This wallet contains a redeemScript " | ||||
"which exceeds maximum size %i thus can never be redeemed. " | "of size %i which exceeds maximum size %i thus can " | ||||
"Do not use address %s.\n", | "never be redeemed. Do not use address %s.\n", | ||||
__func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, | __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, | ||||
strAddr); | strAddr); | ||||
return true; | return true; | ||||
} | } | ||||
return CCryptoKeyStore::AddCScript(redeemScript); | return CCryptoKeyStore::AddCScript(redeemScript); | ||||
} | } | ||||
bool CWallet::AddWatchOnly(const CScript &dest) { | bool CWallet::AddWatchOnly(const CScript &dest) { | ||||
if (!CCryptoKeyStore::AddWatchOnly(dest)) { | if (!CCryptoKeyStore::AddWatchOnly(dest)) { | ||||
▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | for (MasterKeyMap::value_type &pMasterKey : mapMasterKeys) { | ||||
pMasterKey.second.nDeriveIterations * 100 / | pMasterKey.second.nDeriveIterations * 100 / | ||||
double(GetTimeMillis() - nStartTime))) / | double(GetTimeMillis() - nStartTime))) / | ||||
2; | 2; | ||||
if (pMasterKey.second.nDeriveIterations < 25000) { | if (pMasterKey.second.nDeriveIterations < 25000) { | ||||
pMasterKey.second.nDeriveIterations = 25000; | pMasterKey.second.nDeriveIterations = 25000; | ||||
} | } | ||||
LogPrintf( | WalletLogPrintf( | ||||
"Wallet passphrase changed to an nDeriveIterations of %i\n", | "Wallet passphrase changed to an nDeriveIterations of %i\n", | ||||
pMasterKey.second.nDeriveIterations); | pMasterKey.second.nDeriveIterations); | ||||
if (!crypter.SetKeyFromPassphrase( | if (!crypter.SetKeyFromPassphrase( | ||||
strNewWalletPassphrase, pMasterKey.second.vchSalt, | strNewWalletPassphrase, pMasterKey.second.vchSalt, | ||||
pMasterKey.second.nDeriveIterations, | pMasterKey.second.nDeriveIterations, | ||||
pMasterKey.second.nDerivationMethod)) { | pMasterKey.second.nDerivationMethod)) { | ||||
return false; | return false; | ||||
▲ Show 20 Lines • Show All 222 Lines • ▼ Show 20 Lines | kMasterKey.nDeriveIterations = | ||||
static_cast<unsigned int>(kMasterKey.nDeriveIterations * 100 / | static_cast<unsigned int>(kMasterKey.nDeriveIterations * 100 / | ||||
double(GetTimeMillis() - nStartTime))) / | double(GetTimeMillis() - nStartTime))) / | ||||
2; | 2; | ||||
if (kMasterKey.nDeriveIterations < 25000) { | if (kMasterKey.nDeriveIterations < 25000) { | ||||
kMasterKey.nDeriveIterations = 25000; | kMasterKey.nDeriveIterations = 25000; | ||||
} | } | ||||
LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", | WalletLogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", | ||||
kMasterKey.nDeriveIterations); | kMasterKey.nDeriveIterations); | ||||
if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, | if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, | ||||
kMasterKey.nDeriveIterations, | kMasterKey.nDeriveIterations, | ||||
kMasterKey.nDerivationMethod)) { | kMasterKey.nDerivationMethod)) { | ||||
return false; | return false; | ||||
} | } | ||||
if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey)) { | if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey)) { | ||||
▲ Show 20 Lines • Show All 269 Lines • ▼ Show 20 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(), | WalletLogPrintf("AddToWallet %s %s%s\n", wtxIn.GetId().ToString(), | ||||
(fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); | (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); | ||||
// Write to disk | // Write to disk | ||||
if ((fInsertedNew || fUpdated) && !batch.WriteTx(wtx)) { | if ((fInsertedNew || fUpdated) && !batch.WriteTx(wtx)) { | ||||
return false; | return false; | ||||
} | } | ||||
// Break debit/credit balance caches: | // Break debit/credit balance caches: | ||||
wtx.MarkDirty(); | wtx.MarkDirty(); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef &ptx, | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
if (pIndex != nullptr) { | if (pIndex != nullptr) { | ||||
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.GetId()) { | ||||
LogPrintf("Transaction %s (in block %s) conflicts with " | WalletLogPrintf( | ||||
"wallet transaction %s (both spend %s:%i)\n", | "Transaction %s (in block %s) conflicts with 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.GetTxId().ToString(), | range.first->first.GetTxId().ToString(), | ||||
range.first->first.GetN()); | 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 All 14 Lines | if (fExisted || IsMine(tx) || IsFromMe(tx)) { | ||||
// extract addresses and check if they match with an unused keypool | // extract addresses and check if they match with an unused keypool | ||||
// key | // key | ||||
std::vector<CKeyID> vAffected; | std::vector<CKeyID> vAffected; | ||||
CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); | CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); | ||||
for (const CKeyID &keyid : vAffected) { | for (const CKeyID &keyid : vAffected) { | ||||
std::map<CKeyID, int64_t>::const_iterator mi = | std::map<CKeyID, int64_t>::const_iterator mi = | ||||
m_pool_key_to_index.find(keyid); | m_pool_key_to_index.find(keyid); | ||||
if (mi != m_pool_key_to_index.end()) { | if (mi != m_pool_key_to_index.end()) { | ||||
LogPrintf("%s: Detected a used keypool key, mark all " | WalletLogPrintf("%s: Detected a used keypool key, mark all " | ||||
"keypool key up to this key as used\n", | "keypool key up to this key as used\n", | ||||
__func__); | __func__); | ||||
MarkReserveKeysAsUsed(mi->second); | MarkReserveKeysAsUsed(mi->second); | ||||
if (!TopUpKeyPool()) { | if (!TopUpKeyPool()) { | ||||
LogPrintf( | WalletLogPrintf( | ||||
"%s: Topping up keypool failed (locked wallet)\n", | "%s: Topping up keypool failed (locked wallet)\n", | ||||
__func__); | __func__); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
CWalletTx wtx(this, ptx); | CWalletTx wtx(this, ptx); | ||||
▲ Show 20 Lines • Show All 599 Lines • ▼ Show 20 Lines | for (unsigned int i = 0; i < tx->vout.size(); ++i) { | ||||
continue; | continue; | ||||
} | } | ||||
// 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, " | pwallet->WalletLogPrintf("CWalletTx::GetAmounts: Unknown " | ||||
"txid %s\n", | "transaction type found, txid %s\n", | ||||
this->GetId().ToString()); | this->GetId().ToString()); | ||||
address = CNoDestination(); | address = CNoDestination(); | ||||
} | } | ||||
COutputEntry output = {address, txout.nValue, (int)i}; | COutputEntry output = {address, txout.nValue, (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 > Amount::zero()) { | if (nDebit > Amount::zero()) { | ||||
Show All 21 Lines | int64_t CWallet::RescanFromTime(int64_t startTime, | ||||
// Find starting block. May be null if nCreateTime is greater than the | // Find starting block. May be null if nCreateTime is greater than the | ||||
// highest blockchain timestamp, in which case there is nothing that needs | // highest blockchain timestamp, in which case there is nothing that needs | ||||
// to be scanned. | // to be scanned. | ||||
CBlockIndex *startBlock = nullptr; | CBlockIndex *startBlock = nullptr; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
startBlock = | startBlock = | ||||
chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); | chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); | ||||
LogPrintf("%s: Rescanning last %i blocks\n", __func__, | WalletLogPrintf( | ||||
startBlock ? chainActive.Height() - startBlock->nHeight + 1 | "%s: Rescanning last %i blocks\n", __func__, | ||||
: 0); | startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); | ||||
} | } | ||||
if (startBlock) { | if (startBlock) { | ||||
const CBlockIndex *const failedBlock = | const CBlockIndex *const failedBlock = | ||||
ScanForWalletTransactions(startBlock, nullptr, reserver, update); | ScanForWalletTransactions(startBlock, nullptr, reserver, update); | ||||
if (failedBlock) { | if (failedBlock) { | ||||
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; | return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; | ||||
} | } | ||||
Show All 26 Lines | CBlockIndex *CWallet::ScanForWalletTransactions( | ||||
if (pindexStop) { | if (pindexStop) { | ||||
assert(pindexStop->nHeight >= pindexStart->nHeight); | assert(pindexStop->nHeight >= pindexStart->nHeight); | ||||
} | } | ||||
CBlockIndex *pindex = pindexStart; | CBlockIndex *pindex = pindexStart; | ||||
CBlockIndex *ret = nullptr; | CBlockIndex *ret = nullptr; | ||||
if (pindex) { | if (pindex) { | ||||
LogPrintf("Rescan started from block %d...\n", pindex->nHeight); | WalletLogPrintf("Rescan started from block %d...\n", pindex->nHeight); | ||||
} | } | ||||
{ | { | ||||
fAbortRescan = false; | fAbortRescan = false; | ||||
// Show rescan progress in GUI as dialog or on splashscreen, if -rescan | // Show rescan progress in GUI as dialog or on splashscreen, if -rescan | ||||
// on startup. | // on startup. | ||||
ShowProgress(_("Rescanning..."), 0); | ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), | ||||
0); | |||||
CBlockIndex *tip = nullptr; | CBlockIndex *tip = nullptr; | ||||
double progress_begin; | double progress_begin; | ||||
double progress_end; | double progress_end; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
progress_begin = | progress_begin = | ||||
GuessVerificationProgress(chainParams.TxData(), pindex); | GuessVerificationProgress(chainParams.TxData(), pindex); | ||||
if (pindexStop == nullptr) { | if (pindexStop == nullptr) { | ||||
tip = chainActive.Tip(); | tip = chainActive.Tip(); | ||||
progress_end = | progress_end = | ||||
GuessVerificationProgress(chainParams.TxData(), tip); | GuessVerificationProgress(chainParams.TxData(), tip); | ||||
} else { | } else { | ||||
progress_end = | progress_end = | ||||
GuessVerificationProgress(chainParams.TxData(), pindexStop); | GuessVerificationProgress(chainParams.TxData(), pindexStop); | ||||
} | } | ||||
} | } | ||||
double progress_current = progress_begin; | double progress_current = progress_begin; | ||||
while (pindex && !fAbortRescan && !ShutdownRequested()) { | while (pindex && !fAbortRescan && !ShutdownRequested()) { | ||||
if (pindex->nHeight % 100 == 0 && | if (pindex->nHeight % 100 == 0 && | ||||
progress_end - progress_begin > 0.0) { | progress_end - progress_begin > 0.0) { | ||||
ShowProgress( | ShowProgress( | ||||
_("Rescanning..."), | strprintf("%s " + _("Rescanning..."), GetDisplayName()), | ||||
std::max( | std::max( | ||||
1, | 1, | ||||
std::min(99, (int)((progress_current - progress_begin) / | std::min(99, (int)((progress_current - progress_begin) / | ||||
(progress_end - progress_begin) * | (progress_end - progress_begin) * | ||||
100)))); | 100)))); | ||||
} | } | ||||
if (GetTime() >= nNow + 60) { | if (GetTime() >= nNow + 60) { | ||||
nNow = GetTime(); | nNow = GetTime(); | ||||
LogPrintf("Still rescanning. At block %d. Progress=%f\n", | WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", | ||||
pindex->nHeight, progress_current); | pindex->nHeight, progress_current); | ||||
} | } | ||||
CBlock block; | CBlock block; | ||||
if (ReadBlockFromDisk(block, pindex, chainParams.GetConsensus())) { | if (ReadBlockFromDisk(block, pindex, chainParams.GetConsensus())) { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
if (pindex && !chainActive.Contains(pindex)) { | if (pindex && !chainActive.Contains(pindex)) { | ||||
// Abort scan if current block is no longer active, to | // Abort scan if current block is no longer active, to | ||||
// prevent marking transactions as coming from the wrong | // prevent marking transactions as coming from the wrong | ||||
Show All 22 Lines | if (pindex) { | ||||
// in case the tip has changed, update progress max | // in case the tip has changed, update progress max | ||||
progress_end = | progress_end = | ||||
GuessVerificationProgress(chainParams.TxData(), tip); | GuessVerificationProgress(chainParams.TxData(), tip); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (pindex && fAbortRescan) { | if (pindex && fAbortRescan) { | ||||
LogPrintf("Rescan aborted at block %d. Progress=%f\n", | WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", | ||||
pindex->nHeight, progress_current); | pindex->nHeight, progress_current); | ||||
} else if (pindex && ShutdownRequested()) { | } else if (pindex && ShutdownRequested()) { | ||||
LogPrintf("Rescan interrupted by shutdown request at block %d. " | WalletLogPrintf("Rescan interrupted by shutdown request at block " | ||||
"Progress=%f\n", | "%d. Progress=%f\n", | ||||
pindex->nHeight, progress_current); | pindex->nHeight, progress_current); | ||||
} | } | ||||
// Hide progress dialog in GUI. | // Hide progress dialog in GUI. | ||||
ShowProgress(_("Rescanning..."), 100); | ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), | ||||
100); | |||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
void CWallet::ReacceptWalletTransactions() { | void CWallet::ReacceptWalletTransactions() { | ||||
// If transactions aren't being broadcasted, don't let them into local | // If transactions aren't being broadcasted, don't let them into local | ||||
// mempool either. | // mempool either. | ||||
if (!fBroadcastTransactions) { | if (!fBroadcastTransactions) { | ||||
Show All 29 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()); | pwallet->WalletLogPrintf("Relaying wtx %s\n", GetId().ToString()); | ||||
if (connman) { | if (connman) { | ||||
CInv inv(MSG_TX, GetId()); | CInv inv(MSG_TX, GetId()); | ||||
connman->ForEachNode( | connman->ForEachNode( | ||||
[&inv](CNode *pnode) { pnode->PushInventory(inv); }); | [&inv](CNode *pnode) { pnode->PushInventory(inv); }); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 267 Lines • ▼ Show 20 Lines | void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, | ||||
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<uint256> 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__, | WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", | ||||
relayed.size()); | __func__, relayed.size()); | ||||
} | } | ||||
} | } | ||||
/** @} */ // end of mapWallet | /** @} */ // end of mapWallet | ||||
/** | /** | ||||
* @defgroup Actions | * @defgroup Actions | ||||
* | * | ||||
▲ Show 20 Lines • Show All 1,074 Lines • ▼ Show 20 Lines | bool CWallet::CommitTransaction( | ||||
CWalletTx wtxNew(this, std::move(tx)); | CWalletTx wtxNew(this, std::move(tx)); | ||||
wtxNew.mapValue = std::move(mapValue); | wtxNew.mapValue = std::move(mapValue); | ||||
wtxNew.vOrderForm = std::move(orderForm); | wtxNew.vOrderForm = std::move(orderForm); | ||||
wtxNew.strFromAccount = std::move(fromAccount); | wtxNew.strFromAccount = std::move(fromAccount); | ||||
wtxNew.fTimeReceivedIsTxTime = true; | wtxNew.fTimeReceivedIsTxTime = true; | ||||
wtxNew.fFromMe = true; | wtxNew.fFromMe = true; | ||||
LogPrintfToBeContinued("CommitTransaction:\n%s", wtxNew.tx->ToString()); | WalletLogPrintfToBeContinued("CommitTransaction:\n%s", | ||||
wtxNew.tx->ToString()); | |||||
// Take key pair from key pool so it won't be used again. | // Take key pair from key pool so it won't be used again. | ||||
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.at(txin.prevout.GetTxId()); | CWalletTx &coin = mapWallet.at(txin.prevout.GetTxId()); | ||||
coin.BindWallet(this); | coin.BindWallet(this); | ||||
NotifyTransactionChanged(this, coin.GetId(), CT_UPDATED); | NotifyTransactionChanged(this, coin.GetId(), CT_UPDATED); | ||||
} | } | ||||
// Get the inserted-CWalletTx from mapWallet so that the | // Get the inserted-CWalletTx from mapWallet so that the | ||||
// fInMempool flag is cached properly | // fInMempool flag is cached properly | ||||
CWalletTx &wtx = mapWallet.at(wtxNew.GetId()); | CWalletTx &wtx = mapWallet.at(wtxNew.GetId()); | ||||
if (fBroadcastTransactions) { | if (fBroadcastTransactions) { | ||||
// Broadcast | // Broadcast | ||||
if (!wtx.AcceptToMemoryPool(maxTxFee, state)) { | if (!wtx.AcceptToMemoryPool(maxTxFee, state)) { | ||||
LogPrintf("CommitTransaction(): Transaction cannot be broadcast " | WalletLogPrintf("CommitTransaction(): Transaction cannot be " | ||||
"immediately, %s\n", | "broadcast immediately, %s\n", | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
// TODO: if we expect the failure to be long term or permanent, | // TODO: if we expect the failure to be long term or permanent, | ||||
// instead delete wtx from the wallet and return failure. | // instead delete wtx from the wallet and return failure. | ||||
} else { | } else { | ||||
wtx.RelayWalletTransaction(connman); | wtx.RelayWalletTransaction(connman); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 199 Lines • ▼ Show 20 Lines | bool CWallet::NewKeyPool() { | ||||
set_pre_split_keypool.clear(); | set_pre_split_keypool.clear(); | ||||
m_pool_key_to_index.clear(); | m_pool_key_to_index.clear(); | ||||
if (!TopUpKeyPool()) { | if (!TopUpKeyPool()) { | ||||
return false; | return false; | ||||
} | } | ||||
LogPrintf("CWallet::NewKeyPool rewrote keypool\n"); | WalletLogPrintf("CWallet::NewKeyPool rewrote keypool\n"); | ||||
return true; | return true; | ||||
} | } | ||||
size_t CWallet::KeypoolCountExternalKeys() { | size_t CWallet::KeypoolCountExternalKeys() { | ||||
// setExternalKeyPool | // setExternalKeyPool | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
return setExternalKeyPool.size() + set_pre_split_keypool.size(); | return setExternalKeyPool.size() + set_pre_split_keypool.size(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | for (int64_t i = missingInternal + missingExternal; i--;) { | ||||
if (internal) { | if (internal) { | ||||
setInternalKeyPool.insert(index); | setInternalKeyPool.insert(index); | ||||
} else { | } else { | ||||
setExternalKeyPool.insert(index); | setExternalKeyPool.insert(index); | ||||
} | } | ||||
m_pool_key_to_index[pubkey.GetID()] = index; | m_pool_key_to_index[pubkey.GetID()] = index; | ||||
} | } | ||||
if (missingInternal + missingExternal > 0) { | if (missingInternal + missingExternal > 0) { | ||||
LogPrintf( | WalletLogPrintf( | ||||
"keypool added %d keys (%d internal), size=%u (%u internal)\n", | "keypool added %d keys (%d internal), size=%u (%u internal)\n", | ||||
missingInternal + missingExternal, missingInternal, | missingInternal + missingExternal, missingInternal, | ||||
setInternalKeyPool.size() + setExternalKeyPool.size() + | setInternalKeyPool.size() + setExternalKeyPool.size() + | ||||
set_pre_split_keypool.size(), | set_pre_split_keypool.size(), | ||||
setInternalKeyPool.size()); | setInternalKeyPool.size()); | ||||
} | } | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | if (use_split_keypool && keypool.fInternal != fReturningInternal) { | ||||
": keypool entry misclassified"); | ": keypool entry misclassified"); | ||||
} | } | ||||
if (!keypool.vchPubKey.IsValid()) { | if (!keypool.vchPubKey.IsValid()) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": keypool entry invalid"); | ": keypool entry invalid"); | ||||
} | } | ||||
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | ||||
LogPrintf("keypool reserve %d\n", nIndex); | WalletLogPrintf("keypool reserve %d\n", nIndex); | ||||
return true; | return true; | ||||
} | } | ||||
void CWallet::KeepKey(int64_t nIndex) { | void CWallet::KeepKey(int64_t nIndex) { | ||||
// Remove from key pool. | // Remove from key pool. | ||||
WalletBatch batch(*database); | WalletBatch batch(*database); | ||||
batch.ErasePool(nIndex); | batch.ErasePool(nIndex); | ||||
LogPrintf("keypool keep %d\n", nIndex); | WalletLogPrintf("keypool keep %d\n", nIndex); | ||||
} | } | ||||
void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) { | void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) { | ||||
// Return to key pool | // Return to key pool | ||||
{ | { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (fInternal) { | if (fInternal) { | ||||
setInternalKeyPool.insert(nIndex); | setInternalKeyPool.insert(nIndex); | ||||
} else if (!set_pre_split_keypool.empty()) { | } else if (!set_pre_split_keypool.empty()) { | ||||
set_pre_split_keypool.insert(nIndex); | set_pre_split_keypool.insert(nIndex); | ||||
} else { | } else { | ||||
setExternalKeyPool.insert(nIndex); | setExternalKeyPool.insert(nIndex); | ||||
} | } | ||||
m_pool_key_to_index[pubkey.GetID()] = nIndex; | m_pool_key_to_index[pubkey.GetID()] = nIndex; | ||||
} | } | ||||
LogPrintf("keypool return %d\n", nIndex); | WalletLogPrintf("keypool return %d\n", nIndex); | ||||
} | } | ||||
bool CWallet::GetKeyFromPool(CPubKey &result, bool internal) { | bool CWallet::GetKeyFromPool(CPubKey &result, bool internal) { | ||||
if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | if (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | ||||
return false; | return false; | ||||
} | } | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
▲ Show 20 Lines • Show All 278 Lines • ▼ Show 20 Lines | while (it != std::end(*setKeyPool)) { | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
if (batch.ReadPool(index, keypool)) { | if (batch.ReadPool(index, keypool)) { | ||||
// TODO: This should be unnecessary | // TODO: This should be unnecessary | ||||
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | ||||
} | } | ||||
LearnAllRelatedScripts(keypool.vchPubKey); | LearnAllRelatedScripts(keypool.vchPubKey); | ||||
batch.ErasePool(index); | batch.ErasePool(index); | ||||
LogPrintf("keypool index %d removed\n", index); | WalletLogPrintf("keypool index %d removed\n", index); | ||||
it = setKeyPool->erase(it); | it = setKeyPool->erase(it); | ||||
} | } | ||||
} | } | ||||
void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script) { | void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script) { | ||||
std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this); | std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this); | ||||
CPubKey pubkey; | CPubKey pubkey; | ||||
if (!rKey->GetReservedKey(pubkey)) { | if (!rKey->GetReservedKey(pubkey)) { | ||||
▲ Show 20 Lines • Show All 157 Lines • ▼ Show 20 Lines | if (!wtx.hashUnset()) { | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
int64_t blocktime = pindex->GetBlockTime(); | int64_t blocktime = pindex->GetBlockTime(); | ||||
nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); | nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); | ||||
} else { | } else { | ||||
LogPrintf("%s: found %s in block %s not in index\n", __func__, | WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, | ||||
wtx.GetId().ToString(), wtx.hashBlock.ToString()); | wtx.GetId().ToString(), wtx.hashBlock.ToString()); | ||||
} | } | ||||
} | } | ||||
return nTimeSmart; | return nTimeSmart; | ||||
} | } | ||||
bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, | bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, | ||||
const std::string &value) { | const std::string &value) { | ||||
if (boost::get<CNoDestination>(&dest)) { | if (boost::get<CNoDestination>(&dest)) { | ||||
▲ Show 20 Lines • Show All 194 Lines • ▼ Show 20 Lines | if (nLoadWalletRet != DBErrors::LOAD_OK) { | ||||
} | } | ||||
} | } | ||||
int prev_version = walletInstance->nWalletVersion; | int prev_version = walletInstance->nWalletVersion; | ||||
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { | if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { | ||||
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0); | int nMaxVersion = gArgs.GetArg("-upgradewallet", 0); | ||||
// The -upgradewallet without argument case | // The -upgradewallet without argument case | ||||
if (nMaxVersion == 0) { | if (nMaxVersion == 0) { | ||||
LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); | walletInstance->WalletLogPrintf("Performing wallet upgrade to %i\n", | ||||
FEATURE_LATEST); | |||||
nMaxVersion = FEATURE_LATEST; | nMaxVersion = FEATURE_LATEST; | ||||
// permanently upgrade the wallet immediately | // permanently upgrade the wallet immediately | ||||
walletInstance->SetMinVersion(FEATURE_LATEST); | walletInstance->SetMinVersion(FEATURE_LATEST); | ||||
} else { | } else { | ||||
LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); | walletInstance->WalletLogPrintf( | ||||
"Allowing wallet upgrade up to %i\n", nMaxVersion); | |||||
} | } | ||||
if (nMaxVersion < walletInstance->GetVersion()) { | if (nMaxVersion < walletInstance->GetVersion()) { | ||||
InitError(_("Cannot downgrade wallet")); | InitError(_("Cannot downgrade wallet")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
walletInstance->SetMaxVersion(nMaxVersion); | walletInstance->SetMaxVersion(nMaxVersion); | ||||
Show All 15 Lines | if (gArgs.GetBoolArg("-upgradewallet", false)) { | ||||
"or -upgradewallet with no version specified.")); | "or -upgradewallet with no version specified.")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
bool hd_upgrade = false; | bool hd_upgrade = false; | ||||
bool split_upgrade = false; | bool split_upgrade = false; | ||||
if (walletInstance->CanSupportFeature(FEATURE_HD) && | if (walletInstance->CanSupportFeature(FEATURE_HD) && | ||||
!walletInstance->IsHDEnabled()) { | !walletInstance->IsHDEnabled()) { | ||||
LogPrintf("Upgrading wallet to HD\n"); | walletInstance->WalletLogPrintf("Upgrading wallet to HD\n"); | ||||
walletInstance->SetMinVersion(FEATURE_HD); | walletInstance->SetMinVersion(FEATURE_HD); | ||||
// generate a new master key | // generate a new master key | ||||
CPubKey masterPubKey = walletInstance->GenerateNewSeed(); | CPubKey masterPubKey = walletInstance->GenerateNewSeed(); | ||||
walletInstance->SetHDSeed(masterPubKey); | walletInstance->SetHDSeed(masterPubKey); | ||||
hd_upgrade = true; | hd_upgrade = true; | ||||
} | } | ||||
// Upgrade to HD chain split if necessary | // Upgrade to HD chain split if necessary | ||||
if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) { | if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) { | ||||
LogPrintf("Upgrading wallet to use HD chain split\n"); | walletInstance->WalletLogPrintf( | ||||
"Upgrading wallet to use HD chain split\n"); | |||||
walletInstance->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL); | walletInstance->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL); | ||||
split_upgrade = FEATURE_HD_SPLIT > prev_version; | split_upgrade = FEATURE_HD_SPLIT > prev_version; | ||||
} | } | ||||
// Mark all keys currently in the keypool as pre-split | // Mark all keys currently in the keypool as pre-split | ||||
if (split_upgrade) { | if (split_upgrade) { | ||||
walletInstance->MarkPreSplitKeys(); | walletInstance->MarkPreSplitKeys(); | ||||
} | } | ||||
// Regenerate the keypool if upgraded to HD | // Regenerate the keypool if upgraded to HD | ||||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | if (gArgs.IsArgSet("-paytxfee")) { | ||||
} | } | ||||
} | } | ||||
walletInstance->m_spend_zero_conf_change = | walletInstance->m_spend_zero_conf_change = | ||||
gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); | gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); | ||||
walletInstance->m_default_address_type = DEFAULT_ADDRESS_TYPE; | walletInstance->m_default_address_type = DEFAULT_ADDRESS_TYPE; | ||||
walletInstance->m_default_change_type = DEFAULT_CHANGE_TYPE; | walletInstance->m_default_change_type = DEFAULT_CHANGE_TYPE; | ||||
LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); | walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", | ||||
GetTimeMillis() - nStart); | |||||
// Try to top up keypool. No-op if the wallet is locked. | // Try to top up keypool. No-op if the wallet is locked. | ||||
walletInstance->TopUpKeyPool(); | walletInstance->TopUpKeyPool(); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CBlockIndex *pindexRescan = chainActive.Genesis(); | CBlockIndex *pindexRescan = chainActive.Genesis(); | ||||
if (!gArgs.GetBoolArg("-rescan", false)) { | if (!gArgs.GetBoolArg("-rescan", false)) { | ||||
Show All 22 Lines | if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { | ||||
InitError(_("Prune: last wallet synchronisation goes beyond " | InitError(_("Prune: last wallet synchronisation goes beyond " | ||||
"pruned data. You need to -reindex (download the " | "pruned data. You need to -reindex (download the " | ||||
"whole blockchain again in case of pruned node)")); | "whole blockchain again in case of pruned node)")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
uiInterface.InitMessage(_("Rescanning...")); | uiInterface.InitMessage(_("Rescanning...")); | ||||
LogPrintf("Rescanning last %i blocks (from block %i)...\n", | walletInstance->WalletLogPrintf( | ||||
"Rescanning last %i blocks (from block %i)...\n", | |||||
chainActive.Height() - pindexRescan->nHeight, | chainActive.Height() - pindexRescan->nHeight, | ||||
pindexRescan->nHeight); | pindexRescan->nHeight); | ||||
// No need to read and scan block if block was created before our wallet | // No need to read and scan block if block was created before our wallet | ||||
// birthday (as adjusted for block time variability) | // birthday (as adjusted for block time variability) | ||||
while (pindexRescan && walletInstance->nTimeFirstKey && | while (pindexRescan && walletInstance->nTimeFirstKey && | ||||
(pindexRescan->GetBlockTime() < | (pindexRescan->GetBlockTime() < | ||||
(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) { | (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) { | ||||
pindexRescan = chainActive.Next(pindexRescan); | pindexRescan = chainActive.Next(pindexRescan); | ||||
} | } | ||||
nStart = GetTimeMillis(); | nStart = GetTimeMillis(); | ||||
{ | { | ||||
WalletRescanReserver reserver(walletInstance.get()); | WalletRescanReserver reserver(walletInstance.get()); | ||||
if (!reserver.reserve()) { | if (!reserver.reserve()) { | ||||
InitError( | InitError( | ||||
_("Failed to rescan the wallet during initialization")); | _("Failed to rescan the wallet during initialization")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, | walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, | ||||
reserver, true); | reserver, true); | ||||
} | } | ||||
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); | walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", | ||||
GetTimeMillis() - nStart); | |||||
walletInstance->ChainStateFlushed(chainActive.GetLocator()); | walletInstance->ChainStateFlushed(chainActive.GetLocator()); | ||||
walletInstance->database->IncrementUpdateCounter(); | walletInstance->database->IncrementUpdateCounter(); | ||||
// Restore wallet transaction metadata after -zapwallettxes=1 | // Restore wallet transaction metadata after -zapwallettxes=1 | ||||
if (gArgs.GetBoolArg("-zapwallettxes", false) && | if (gArgs.GetBoolArg("-zapwallettxes", false) && | ||||
gArgs.GetArg("-zapwallettxes", "1") != "2") { | gArgs.GetArg("-zapwallettxes", "1") != "2") { | ||||
WalletBatch batch(*walletInstance->database); | WalletBatch batch(*walletInstance->database); | ||||
Show All 22 Lines | CWallet::CreateWalletFromFile(const CChainParams &chainParams, | ||||
// Register with the validation interface. It's ok to do this after rescan | // Register with the validation interface. It's ok to do this after rescan | ||||
// since we're still holding cs_main. | // since we're still holding cs_main. | ||||
RegisterValidationInterface(walletInstance.get()); | RegisterValidationInterface(walletInstance.get()); | ||||
walletInstance->SetBroadcastTransactions( | walletInstance->SetBroadcastTransactions( | ||||
gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); | gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); | ||||
LOCK(walletInstance->cs_wallet); | LOCK(walletInstance->cs_wallet); | ||||
LogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize()); | walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n", | ||||
LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size()); | walletInstance->GetKeyPoolSize()); | ||||
LogPrintf("mapAddressBook.size() = %u\n", | walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", | ||||
walletInstance->mapWallet.size()); | |||||
walletInstance->WalletLogPrintf("mapAddressBook.size() = %u\n", | |||||
walletInstance->mapAddressBook.size()); | walletInstance->mapAddressBook.size()); | ||||
return walletInstance; | return walletInstance; | ||||
} | } | ||||
void CWallet::postInitProcess() { | void CWallet::postInitProcess() { | ||||
// Add wallet transactions that aren't already in a block to mempool. | // Add wallet transactions that aren't already in a block to mempool. | ||||
// Do this here as mempool requires genesis block to be loaded. | // Do this here as mempool requires genesis block to be loaded. | ||||
ReacceptWalletTransactions(); | ReacceptWalletTransactions(); | ||||
▲ Show 20 Lines • Show All 158 Lines • Show Last 20 Lines |