Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 3,148 Lines • ▼ Show 20 Lines | for (const CTxIn &txin : tx.vin) { | ||||
coinControl.Select(txin.prevout); | coinControl.Select(txin.prevout); | ||||
} | } | ||||
// Acquire the locks to prevent races to the new locked unspents between the | // Acquire the locks to prevent races to the new locked unspents between the | ||||
// CreateTransaction call and LockCoin calls (when lockUnspents is true). | // CreateTransaction call and LockCoin calls (when lockUnspents is true). | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CReserveKey reservekey(this); | ReserveDestination reservedest(this); | ||||
CTransactionRef tx_new; | CTransactionRef tx_new; | ||||
if (!CreateTransaction(*locked_chain, vecSend, tx_new, reservekey, nFeeRet, | if (!CreateTransaction(*locked_chain, vecSend, tx_new, reservedest, nFeeRet, | ||||
nChangePosInOut, strFailReason, coinControl, | nChangePosInOut, strFailReason, coinControl, | ||||
false)) { | false)) { | ||||
return false; | return false; | ||||
} | } | ||||
if (nChangePosInOut != -1) { | if (nChangePosInOut != -1) { | ||||
tx.vout.insert(tx.vout.begin() + nChangePosInOut, | tx.vout.insert(tx.vout.begin() + nChangePosInOut, | ||||
tx_new->vout[nChangePosInOut]); | tx_new->vout[nChangePosInOut]); | ||||
// We don't have the normal Create/Commit cycle, and don't want to | // We don't have the normal Create/Commit cycle, and don't want to | ||||
// risk reusing change, so just remove the key from the keypool | // risk reusing change, so just remove the key from the keypool | ||||
// here. | // here. | ||||
reservekey.KeepKey(); | reservedest.KeepDestination(); | ||||
} | } | ||||
// Copy output sizes from new transaction; they may have had the fee | // Copy output sizes from new transaction; they may have had the fee | ||||
// subtracted from them. | // subtracted from them. | ||||
for (size_t idx = 0; idx < tx.vout.size(); idx++) { | for (size_t idx = 0; idx < tx.vout.size(); idx++) { | ||||
tx.vout[idx].nValue = tx_new->vout[idx].nValue; | tx.vout[idx].nValue = tx_new->vout[idx].nValue; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | CWallet::TransactionChangeType(OutputType change_type, | ||||
} | } | ||||
// else use m_default_address_type for change | // else use m_default_address_type for change | ||||
return m_default_address_type; | return m_default_address_type; | ||||
} | } | ||||
bool CWallet::CreateTransaction(interfaces::Chain::Lock &locked_chainIn, | bool CWallet::CreateTransaction(interfaces::Chain::Lock &locked_chainIn, | ||||
const std::vector<CRecipient> &vecSend, | const std::vector<CRecipient> &vecSend, | ||||
CTransactionRef &tx, CReserveKey &reservekey, | CTransactionRef &tx, | ||||
ReserveDestination &reservedest, | |||||
Amount &nFeeRet, int &nChangePosInOut, | Amount &nFeeRet, int &nChangePosInOut, | ||||
std::string &strFailReason, | std::string &strFailReason, | ||||
const CCoinControl &coinControl, bool sign) { | const CCoinControl &coinControl, bool sign) { | ||||
Amount nValue = Amount::zero(); | Amount nValue = Amount::zero(); | ||||
int nChangePosRequest = nChangePosInOut; | int nChangePosRequest = nChangePosInOut; | ||||
unsigned int nSubtractFeeFromAmount = 0; | unsigned int nSubtractFeeFromAmount = 0; | ||||
for (const auto &recipient : vecSend) { | for (const auto &recipient : vecSend) { | ||||
if (nValue < Amount::zero() || recipient.nAmount < Amount::zero()) { | if (nValue < Amount::zero() || recipient.nAmount < Amount::zero()) { | ||||
Show All 25 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::vector<COutput> vAvailableCoins; | std::vector<COutput> vAvailableCoins; | ||||
AvailableCoins(*locked_chain, vAvailableCoins, true, &coinControl); | AvailableCoins(*locked_chain, vAvailableCoins, true, &coinControl); | ||||
// Parameters for coin selection, init with dummy | // Parameters for coin selection, init with dummy | ||||
CoinSelectionParams coin_selection_params; | CoinSelectionParams coin_selection_params; | ||||
// Create change script that will be used if we need change | // Create change script that will be used if we need change | ||||
// TODO: pass in scriptChange instead of reservekey so | // TODO: pass in scriptChange instead of reservedest so | ||||
// change transaction isn't always pay-to-bitcoin-address | // change transaction isn't always pay-to-bitcoin-address | ||||
CScript scriptChange; | CScript scriptChange; | ||||
// coin control: send change to custom address | // coin control: send change to custom address | ||||
if (!boost::get<CNoDestination>(&coinControl.destChange)) { | if (!boost::get<CNoDestination>(&coinControl.destChange)) { | ||||
scriptChange = GetScriptForDestination(coinControl.destChange); | scriptChange = GetScriptForDestination(coinControl.destChange); | ||||
// no coin control: send change to newly generated address | // no coin control: send change to newly generated address | ||||
Show All 10 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
// Reserve a new key pair from key pool | // Reserve a new key pair from key pool | ||||
if (!CanGetAddresses(true)) { | if (!CanGetAddresses(true)) { | ||||
strFailReason = | strFailReason = | ||||
_("Can't generate a change-address key. No keys in the " | _("Can't generate a change-address key. No keys in the " | ||||
"internal keypool and can't generate any keys.") | "internal keypool and can't generate any keys.") | ||||
.translated; | .translated; | ||||
return false; | return false; | ||||
} | } | ||||
CPubKey vchPubKey; | CTxDestination dest; | ||||
bool ret; | const OutputType change_type = TransactionChangeType( | ||||
ret = reservekey.GetReservedKey(vchPubKey, true); | coinControl.m_change_type ? *coinControl.m_change_type | ||||
: m_default_change_type, | |||||
vecSend); | |||||
bool ret = | |||||
reservedest.GetReservedDestination(change_type, dest, true); | |||||
if (!ret) { | if (!ret) { | ||||
strFailReason = | strFailReason = | ||||
_("Keypool ran out, please call keypoolrefill first") | _("Keypool ran out, please call keypoolrefill first") | ||||
.translated; | .translated; | ||||
return false; | return false; | ||||
} | } | ||||
const OutputType change_type = TransactionChangeType( | scriptChange = GetScriptForDestination(dest); | ||||
coinControl.m_change_type ? *coinControl.m_change_type | |||||
: m_default_change_type, | |||||
vecSend); | |||||
LearnRelatedScripts(vchPubKey, change_type); | |||||
scriptChange = GetScriptForDestination( | |||||
GetDestinationForKey(vchPubKey, change_type)); | |||||
} | } | ||||
CTxOut change_prototype_txout(Amount::zero(), scriptChange); | CTxOut change_prototype_txout(Amount::zero(), scriptChange); | ||||
coin_selection_params.change_output_size = | coin_selection_params.change_output_size = | ||||
GetSerializeSize(change_prototype_txout); | GetSerializeSize(change_prototype_txout); | ||||
// Get the fee rate to use effective values in coin selection | // Get the fee rate to use effective values in coin selection | ||||
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coinControl); | CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coinControl); | ||||
▲ Show 20 Lines • Show All 212 Lines • ▼ Show 20 Lines | txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chainIn); | ||||
// Include more fee and try again. | // Include more fee and try again. | ||||
nFeeRet = nFeeNeeded; | nFeeRet = nFeeNeeded; | ||||
coin_selection_params.use_bnb = false; | coin_selection_params.use_bnb = false; | ||||
continue; | continue; | ||||
} | } | ||||
if (nChangePosInOut == -1) { | if (nChangePosInOut == -1) { | ||||
// Return any reserved key if we don't have change | // Return any reserved address if we don't have change | ||||
reservekey.ReturnKey(); | reservedest.ReturnDestination(); | ||||
} | } | ||||
// Shuffle selected coins and fill in final vin | // Shuffle selected coins and fill in final vin | ||||
txNew.vin.clear(); | txNew.vin.clear(); | ||||
std::vector<CInputCoin> selected_coins(setCoins.begin(), | std::vector<CInputCoin> selected_coins(setCoins.begin(), | ||||
setCoins.end()); | setCoins.end()); | ||||
Shuffle(selected_coins.begin(), selected_coins.end(), | Shuffle(selected_coins.begin(), selected_coins.end(), | ||||
FastRandomContext()); | FastRandomContext()); | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/** | /** | ||||
* Call after CreateTransaction unless you want to abort | * Call after CreateTransaction unless you want to abort | ||||
*/ | */ | ||||
bool CWallet::CommitTransaction( | bool CWallet::CommitTransaction( | ||||
CTransactionRef tx, mapValue_t mapValue, | CTransactionRef tx, mapValue_t mapValue, | ||||
std::vector<std::pair<std::string, std::string>> orderForm, | std::vector<std::pair<std::string, std::string>> orderForm, | ||||
CReserveKey &reservekey, CValidationState &state) { | ReserveDestination &reservedest, CValidationState &state) { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
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.fTimeReceivedIsTxTime = true; | wtxNew.fTimeReceivedIsTxTime = true; | ||||
wtxNew.fFromMe = true; | wtxNew.fFromMe = true; | ||||
WalletLogPrintfToBeContinued("CommitTransaction:\n%s", | WalletLogPrintfToBeContinued("CommitTransaction:\n%s", | ||||
wtxNew.tx->ToString()); | 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(); | reservedest.KeepDestination(); | ||||
// 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()); | ||||
▲ Show 20 Lines • Show All 633 Lines • ▼ Show 20 Lines | for (const std::pair<const CTxDestination, CAddressBookData> &item : | ||||
if (strName == label) { | if (strName == label) { | ||||
result.insert(address); | result.insert(address); | ||||
} | } | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
bool CReserveKey::GetReservedKey(CPubKey &pubkey, bool internal) { | bool ReserveDestination::GetReservedDestination(const OutputType type, | ||||
CTxDestination &dest, | |||||
bool internal) { | |||||
if (!pwallet->CanGetAddresses(internal)) { | if (!pwallet->CanGetAddresses(internal)) { | ||||
return false; | return false; | ||||
} | } | ||||
if (nIndex == -1) { | if (nIndex == -1) { | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
if (!pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal)) { | if (!pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal)) { | ||||
return false; | return false; | ||||
} | } | ||||
vchPubKey = keypool.vchPubKey; | vchPubKey = keypool.vchPubKey; | ||||
fInternal = keypool.fInternal; | fInternal = keypool.fInternal; | ||||
} | } | ||||
assert(vchPubKey.IsValid()); | assert(vchPubKey.IsValid()); | ||||
pubkey = vchPubKey; | pwallet->LearnRelatedScripts(vchPubKey, type); | ||||
address = GetDestinationForKey(vchPubKey, type); | |||||
dest = address; | |||||
return true; | return true; | ||||
} | } | ||||
void CReserveKey::KeepKey() { | void ReserveDestination::KeepDestination() { | ||||
if (nIndex != -1) { | if (nIndex != -1) { | ||||
pwallet->KeepKey(nIndex); | pwallet->KeepKey(nIndex); | ||||
} | } | ||||
nIndex = -1; | nIndex = -1; | ||||
vchPubKey = CPubKey(); | vchPubKey = CPubKey(); | ||||
address = CNoDestination(); | |||||
} | } | ||||
void CReserveKey::ReturnKey() { | void ReserveDestination::ReturnDestination() { | ||||
if (nIndex != -1) { | if (nIndex != -1) { | ||||
pwallet->ReturnKey(nIndex, fInternal, vchPubKey); | pwallet->ReturnKey(nIndex, fInternal, vchPubKey); | ||||
} | } | ||||
nIndex = -1; | nIndex = -1; | ||||
vchPubKey = CPubKey(); | vchPubKey = CPubKey(); | ||||
address = CNoDestination(); | |||||
} | } | ||||
void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { | void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
bool internal = setInternalKeyPool.count(keypool_id); | bool internal = setInternalKeyPool.count(keypool_id); | ||||
if (!internal) { | if (!internal) { | ||||
assert(setExternalKeyPool.count(keypool_id) || | assert(setExternalKeyPool.count(keypool_id) || | ||||
set_pre_split_keypool.count(keypool_id)); | set_pre_split_keypool.count(keypool_id)); | ||||
▲ Show 20 Lines • Show All 1,083 Lines • Show Last 20 Lines |