#20344 removed the divide-by-zero sanitizer suppression in wallet/wallet.cpp but kept a potential devision by zero in wallet.cpp's fee logging.
Detected here https://bitcoinbuilds.org/index.php?job=ffb7d59f-379f-4f27-a273-a5595b8c5f07
#20344 removed the divide-by-zero sanitizer suppression in wallet/wallet.cpp but kept a potential devision by zero in wallet.cpp's fee logging.
Detected here https://bitcoinbuilds.org/index.php?job=ffb7d59f-379f-4f27-a273-a5595b8c5f07
3109 | @@ -3110,10 +3110,10 @@ bool CWallet::CreateTransactionInternal( 3110 | WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", 3111 | nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, 3112 | feeCalc.est.pass.start, feeCalc.est.pass.end, 3113 | - 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool), 3114 | + (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) == 0.0 ? 0.0 : 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool),
Could a local variable with feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool value improve readability?
I did that initially but changed it to the inline-approach because it's just a log print... and I think it's still readable.
Nit: Could re-phrase as (a + b + c) > 0.0 ? 100 * … / (a + b + c) : 0.0 to avoid comparing floating point with == :)
Good point. Fixed.
ACK 2fcc6212ed874c5afb42498ac3c6775b1962d9e9, I have reviewed the code and it looks OK, I agree it can be merged.
Concept ACK
Thanks for fixing this. See #17208 for some historical context.
Somewhat related: I think we have a float divide-by-zero in src/validation.cpp in the case of nBlocksTotal == 0. gmaxwell suggested initializing nBlocksTotal to 1 instead of 0 as a simple way to avoid the float divide-by-zero. I think that makes sense.
3109 | @@ -3110,10 +3110,10 @@ bool CWallet::CreateTransactionInternal( 3110 | WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", 3111 | nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, 3112 | feeCalc.est.pass.start, feeCalc.est.pass.end, 3113 | - 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool), 3114 | + (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) == 0.0 ? 0.0 : 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool), 3115 | feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool, 3116 | feeCalc.est.fail.start, feeCalc.est.fail.end, 3117 | - 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool), 3118 | + (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) == 0.0 ? 0.0 : 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool),
Same nit applies here.
Confirmed that this fixes the broken master builds (https://bitcoinbuilds.org/index.php?job=3b3274e1-c511-4799-aad2-38595f9c81fe).
ACK 440f8d3abe97b96f434dad5216d417a08fc10253
re-ACK 440f8d3abe97b96f434dad5216d417a08fc10253
Somewhat related: I think we have a float divide-by-zero in
src/validation.cppin the case ofnBlocksTotal == 0. gmaxwell suggested initializingnBlocksTotalto1instead of0as a simple way to avoid the float divide-by-zero. I think that makes sense.
It seems like this case got fixed by @instagibbs in ec30a79f1c430cc7fbda37e5d747b0b31b262fa5 (#15283). Nice!
If so test/sanitizer_suppressions/ubsan:float-divide-by-zero:validation.cpp can be dropped :)
3114 | + (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0, 3115 | feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool, 3116 | feeCalc.est.fail.start, feeCalc.est.fail.end, 3117 | - 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool), 3118 | + (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0, 3119 | feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
Suggestion; I haven't tested this yet, but it compiles. (UPDATE: tested)
auto sum = [](const EstimatorBucket& b) { return b.totalConfirmed + b.inMempool + b.leftMempool; };
WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
feeCalc.est.pass.start, feeCalc.est.pass.end,
sum(feeCalc.est.pass) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / sum(feeCalc.est.pass) : 0.0,
feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool,
feeCalc.est.fail.start, feeCalc.est.fail.end,
sum(feeCalc.est.fail) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / sum(feeCalc.est.fail) : 0.0,
feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
Yes, making that a function is a bit more elegant, it makes it clearer that the same value is used for 0-comparison as for division.
Or move the whole thing into a lambda?
auto bucket_pct = [](const EstimatorBucket& b) {
double sum = b.totalConfirmed + b.inMempool + b.leftMempool;
return sum > 0.0 ? (100.0 * b.withinTaget / sum) : 0.0;
};
Code review ACK 440f8d3abe97b96f434dad5216d417a08fc10253
Milestone
0.21.0