Skip to content

Commit b3d7b1c

Browse files
committed
Wallet: Do not perform ECDSA in the fee calculation inner loop.
Performing signing in the inner loop has terrible performance when many passes through are needed to complete the selection. Signing before the algorithm is complete also gets in the way of correctly setting the fee (e.g. preventing over-payment when the fee required goes down on the final selection.) Use of the dummy might overpay on the signatures by a couple bytes in uncommon cases where the signatures' DER encoding is smaller than the dummy: Who cares?
1 parent 649cf5f commit b3d7b1c

File tree

1 file changed

+46
-31
lines changed

1 file changed

+46
-31
lines changed

src/wallet/wallet.cpp

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2245,7 +2245,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
22452245
CAmount nValue = 0;
22462246
int nChangePosRequest = nChangePosInOut;
22472247
unsigned int nSubtractFeeFromAmount = 0;
2248-
BOOST_FOREACH (const CRecipient& recipient, vecSend)
2248+
for (const auto& recipient : vecSend)
22492249
{
22502250
if (nValue < 0 || recipient.nAmount < 0)
22512251
{
@@ -2300,6 +2300,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
23002300
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
23012301

23022302
{
2303+
set<pair<const CWalletTx*,unsigned int> > setCoins;
23032304
LOCK2(cs_main, cs_wallet);
23042305
{
23052306
std::vector<COutput> vAvailableCoins;
@@ -2320,7 +2321,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
23202321
nValueToSelect += nFeeRet;
23212322
double dPriority = 0;
23222323
// vouts to the payees
2323-
BOOST_FOREACH (const CRecipient& recipient, vecSend)
2324+
for (const auto& recipient : vecSend)
23242325
{
23252326
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
23262327

@@ -2352,14 +2353,14 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
23522353
}
23532354

23542355
// Choose coins to use
2355-
set<pair<const CWalletTx*,unsigned int> > setCoins;
23562356
CAmount nValueIn = 0;
2357+
setCoins.clear();
23572358
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl))
23582359
{
23592360
strFailReason = _("Insufficient funds");
23602361
return false;
23612362
}
2362-
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
2363+
for (const auto& pcoin : setCoins)
23632364
{
23642365
CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue;
23652366
//The coin age after the next block (depth+1) is used instead of the current,
@@ -2470,24 +2471,18 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
24702471
// to avoid conflicting with other possible uses of nSequence,
24712472
// and in the spirit of "smallest posible change from prior
24722473
// behavior."
2473-
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
2474+
for (const auto& coin : setCoins)
24742475
txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
24752476
std::numeric_limits<unsigned int>::max() - (fWalletRbf ? 2 : 1)));
24762477

2477-
// Sign
2478+
// Fill in dummy signatures for fee calculation.
24782479
int nIn = 0;
2479-
CTransaction txNewConst(txNew);
2480-
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
2480+
for (const auto& coin : setCoins)
24812481
{
2482-
bool signSuccess;
24832482
const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
24842483
SignatureData sigdata;
2485-
if (sign)
2486-
signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
2487-
else
2488-
signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);
24892484

2490-
if (!signSuccess)
2485+
if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata))
24912486
{
24922487
strFailReason = _("Signing transaction failed");
24932488
return false;
@@ -2500,26 +2495,15 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
25002495

25012496
unsigned int nBytes = GetVirtualTransactionSize(txNew);
25022497

2503-
// Remove scriptSigs if we used dummy signatures for fee calculation
2504-
if (!sign) {
2505-
BOOST_FOREACH (CTxIn& vin, txNew.vin) {
2506-
vin.scriptSig = CScript();
2507-
vin.scriptWitness.SetNull();
2508-
}
2509-
}
2510-
2511-
// Embed the constructed transaction data in wtxNew.
2512-
wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
2498+
CTransaction txNewConst(txNew);
2499+
dPriority = txNewConst.ComputePriority(dPriority, nBytes);
25132500

2514-
// Limit size
2515-
if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
2516-
{
2517-
strFailReason = _("Transaction too large");
2518-
return false;
2501+
// Remove scriptSigs to eliminate the fee calculation dummy signatures
2502+
for (auto& vin : txNew.vin) {
2503+
vin.scriptSig = CScript();
2504+
vin.scriptWitness.SetNull();
25192505
}
25202506

2521-
dPriority = wtxNew.tx->ComputePriority(dPriority, nBytes);
2522-
25232507
// Allow to override the default confirmation target over the CoinControl instance
25242508
int currentConfirmationTarget = nTxConfirmTarget;
25252509
if (coinControl && coinControl->nConfirmTarget > 0)
@@ -2558,6 +2542,37 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
25582542
continue;
25592543
}
25602544
}
2545+
2546+
if (sign)
2547+
{
2548+
CTransaction txNewConst(txNew);
2549+
int nIn = 0;
2550+
for (const auto& coin : setCoins)
2551+
{
2552+
const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
2553+
SignatureData sigdata;
2554+
2555+
if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata))
2556+
{
2557+
strFailReason = _("Signing transaction failed");
2558+
return false;
2559+
} else {
2560+
UpdateTransaction(txNew, nIn, sigdata);
2561+
}
2562+
2563+
nIn++;
2564+
}
2565+
}
2566+
2567+
// Embed the constructed transaction data in wtxNew.
2568+
wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
2569+
2570+
// Limit size
2571+
if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
2572+
{
2573+
strFailReason = _("Transaction too large");
2574+
return false;
2575+
}
25612576
}
25622577

25632578
if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {

0 commit comments

Comments
 (0)