Skip to content

Commit 2ae58d5

Browse files
committed
Merge #11864: Make CWallet::FundTransaction atomic
03a5dc9 [wallet] Make CWallet::FundTransaction atomic (João Barbosa) 95d4450 [wallet] Tidy up CWallet::FundTransaction (João Barbosa) Pull request description: This PR fixes a race for `setLockedCoins` when `lockUnspents` is true. For instance, it should not be possible to use the same unspent in concurrent `fundrawtransaction` calls. Now the `cs_main` and `cs_wallet` locks are held during `CreateTransaction` and `LockCoin`(s). Also added some style nits around the change. Tree-SHA512: ccf383c0c5f6db775655a3e9ccd200c3bd831a83afae2b7c389564c74f7227f5bea86a4775727de2c3603b188f383f8a12d3f9d6d94f7887865c31c94ce95ef6
2 parents d4991c0 + 03a5dc9 commit 2ae58d5

File tree

1 file changed

+19
-18
lines changed

1 file changed

+19
-18
lines changed

src/wallet/wallet.cpp

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,18 +2595,22 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
25952595
{
25962596
std::vector<CRecipient> vecSend;
25972597

2598-
// Turn the txout set into a CRecipient vector
2599-
for (size_t idx = 0; idx < tx.vout.size(); idx++)
2600-
{
2598+
// Turn the txout set into a CRecipient vector.
2599+
for (size_t idx = 0; idx < tx.vout.size(); idx++) {
26012600
const CTxOut& txOut = tx.vout[idx];
26022601
CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1};
26032602
vecSend.push_back(recipient);
26042603
}
26052604

26062605
coinControl.fAllowOtherInputs = true;
26072606

2608-
for (const CTxIn& txin : tx.vin)
2607+
for (const CTxIn& txin : tx.vin) {
26092608
coinControl.Select(txin.prevout);
2609+
}
2610+
2611+
// Acquire the locks to prevent races to the new locked unspents between the
2612+
// CreateTransaction call and LockCoin calls (when lockUnspents is true).
2613+
LOCK2(cs_main, cs_wallet);
26102614

26112615
CReserveKey reservekey(this);
26122616
CWalletTx wtx;
@@ -2616,31 +2620,28 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
26162620

26172621
if (nChangePosInOut != -1) {
26182622
tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);
2619-
// we don't have the normal Create/Commit cycle, and don't want to risk reusing change,
2620-
// so just remove the key from the keypool here.
2623+
// We don't have the normal Create/Commit cycle, and don't want to risk
2624+
// reusing change, so just remove the key from the keypool here.
26212625
reservekey.KeepKey();
26222626
}
26232627

2624-
// Copy output sizes from new transaction; they may have had the fee subtracted from them
2625-
for (unsigned int idx = 0; idx < tx.vout.size(); idx++)
2628+
// Copy output sizes from new transaction; they may have had the fee
2629+
// subtracted from them.
2630+
for (unsigned int idx = 0; idx < tx.vout.size(); idx++) {
26262631
tx.vout[idx].nValue = wtx.tx->vout[idx].nValue;
2632+
}
26272633

2628-
// Add new txins (keeping original txin scriptSig/order)
2629-
for (const CTxIn& txin : wtx.tx->vin)
2630-
{
2631-
if (!coinControl.IsSelected(txin.prevout))
2632-
{
2634+
// Add new txins while keeping original txin scriptSig/order.
2635+
for (const CTxIn& txin : wtx.tx->vin) {
2636+
if (!coinControl.IsSelected(txin.prevout)) {
26332637
tx.vin.push_back(txin);
26342638

2635-
if (lockUnspents)
2636-
{
2637-
LOCK2(cs_main, cs_wallet);
2638-
LockCoin(txin.prevout);
2639+
if (lockUnspents) {
2640+
LockCoin(txin.prevout);
26392641
}
26402642
}
26412643
}
26422644

2643-
26442645
return true;
26452646
}
26462647

0 commit comments

Comments
 (0)