More fee rate guard protection - #1889
Conversation
Coverage Report for CI Build 35689112778Coverage increased (+0.06%) to 86.761%Details
Uncovered Changes
Coverage Regressions2 previously-covered lines in 1 file lost coverage.
Coverage Stats
💛 - Coveralls |
xstoicunicornx
left a comment
There was a problem hiding this comment.
utACK 2aec221
Straight forward guards added to protect against an sender output with insufficient value getting selected for fee contributions and overflowing multiplication in fee rate calculations.
One small nit below. Another item that can be follow up is maybe we want to add tests to exercise these error paths? (disclaimer - these code snippets are untested)
// payjoin/src/core/receive/mod.rs
#[test]
fn psbt_fee_rate_rejects_overflowing_fee() {
let mut original = original_from_test_vector();
// A sender is free to claim any prevout value it likes. Anything above
// u64::MAX / 1000 sats makes the sat/kwu conversion overflow, which used
// to wrap silently in release builds and hand back a bogus fee rate.
original
.psbt
.inputs
.first_mut()
.expect("test vector has an input")
.witness_utxo
.as_mut()
.expect("test vector input has a witness utxo")
.value = Amount::MAX;
let err = original
.clone()
.psbt_fee_rate()
.expect_err("Fee rate should not be computable from an overflowing fee");
assert!(matches!(err, InternalPayloadError::FeeCalculationOverflow));
// The same PSBT must be rejected at the first receiver check rather than
// panicking or passing the min-fee-rate comparison with a wrapped value.
let err = original
.check_broadcast_suitability(Some(FeeRate::from_sat_per_vb_u32(1)), |_| Ok(true))
.expect_err("Broadcast suitability should fail on an overflowing fee");
assert!(matches!(
err,
Error::Protocol(ProtocolError::OriginalPayload(PayloadError(
InternalPayloadError::FeeCalculationOverflow
)))
));
}// payjoin/src/core/send/mod.rs
#[test]
fn test_fee_calculation_overflow() -> Result<(), BoxError> {
let mut ctx = create_psbt_context()?;
// The guard only runs when a minimum fee rate was requested.
ctx.min_fee_rate = FeeRate::from_sat_per_vb_u32(1);
let mut proposal = ctx.original_psbt.clone();
// A malicious receiver claims an absurd value for an input it
// contributed. Anything above u64::MAX / 1000 sats makes the
// sat/kwu conversion overflow, which used to wrap silently in
// release builds and could pass the min-fee-rate check.
proposal.inputs[0]
.witness_utxo
.as_mut()
.expect("test vector input has a witness utxo")
.value = Amount::MAX;
let err = ctx
.check_fees(&proposal, Amount::ZERO)
.expect_err("Fee rate should not be computable from an overflowing fee");
assert!(matches!(err, InternalProposalError::FeeCalculationOverflow));
Ok(())
}2aec221 to
67913e3
Compare
There was a problem hiding this comment.
ACK 67913e3
I see the tests @xstoicunicornx suggested were included and .position(|txo| txo == sender_fee_output) was kept without the additional comment since the original comment describes this behavior.
Regression test to prevent .position(|txo| txo.script_pubkey == sender_fee_output.script_pubkey) from being re-introduced was not added, however. Difficult to place it?
Edit: since there was no rationale against it, I just added the test and rebased into the commit that introduced it so it's harder to regress.
This change checks that the sender_additional_fee is actually able to cover the sender_additional_fee
This prevents an overflow by a malicious receiver to keep the fee / weight calculation sane. This is specifically a DOS protection for the sender and directory as this code path cannot occur without a custom malicious receiver. An honest payjoin counterparty would not be able to generate overflows in these cases.
This adds a weight check to the receiver to prevent any overflow behavior on the receiver psbt fee check. This check is specifically protection for receiver and directory DOS protection as an honest counterparty would not be able to cause the overflow in these cases. It would require a malicious sender to hit this code.
2c9a0b6 to
0110cc3
Compare
|
That's a doozy of a test but it does look like it does what its supposed to 😅 |
xstoicunicornx
left a comment
There was a problem hiding this comment.
utACK 0110cc3
Fixes look good and have complete test coverage.
| PsbtBelowFeeRate(bitcoin::FeeRate, bitcoin::FeeRate), | ||
| /// Effective receiver feerate exceeds maximum allowed feerate | ||
| FeeTooHigh(bitcoin::FeeRate, bitcoin::FeeRate), | ||
| /// Fee calculation overflowed in arithmetic and was incomputeable |
There was a problem hiding this comment.
nit
| /// Fee calculation overflowed in arithmetic and was incomputeable | |
| /// Fee calculation overflowed in arithmetic and was incomputable |

This is a followup to #1845 with some different fee checks as well as some further enhancements on existing changes from that PR.
Coded with gGLM-5.3
Pull Request Checklist
Please confirm the following before requesting review:
AI
in the body of this PR.