38286 [SC-Low] bitcoinutils getdustlimitforoutput calculate wrongly the dust limit for a given bitcoin script public key
#38286 [SC-Low] BitcoinUtils.getDustLimitForOutput calculate wrongly the dust limit for a given Bitcoin script public key
Submitted on Dec 30th 2024 at 09:27:53 UTC by @perseverance for Audit Comp | Lombard
Report ID: #38286
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/lombard-finance/evm-smart-contracts/blob/main/contracts/LBTC/LBTC.sol
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Description
Brief/Intro
During the redeem transaction, LBTC contract calculate if the amount after fee is above DustLimit. If so, then user can redeem successfully. Otherwise, the transaction will revert.
https://github.com/lombard-finance/evm-smart-contracts/blob/main/contracts/LBTC/LBTC.sol#L476-L482
The internal _calcFeeAndDustLimit will call BitcoinUtils.getDustLimitForOutput to calculate the Dust Limit
https://github.com/lombard-finance/evm-smart-contracts/blob/main/contracts/libs/BitcoinUtils.sol#L65-L88
This DustLimit is related to Dust that is defined in terms of dustRelayFee, which has units satoshis-per-kilobyte. If users pay more in fees than the value of the output to spend something, then Bitcoin consider it dust. See Reference
So if the amount after fee is less than the DustLimit on BitcoinBitcoin, then the output amount can not be spent.
The check in LBTC is important to prevent users to loose Bitcoin due to Dust Limit when redeem Bitcoin. It is important to calculate correctly this DustLimit to prevent users to loose Bitcoin.
The vulnerability
Vulnerability Details
The bug here is function getDustLimitForOutput caculates wrong Dust Limit.
Currently the protocol supports three types of scriptPubkey: P2TR, P2WPKH, P2WSH.
To simplify, I will explain this for P2WPKH.
For P2WPKH, the function getDustLimitForOutput will return dustLimit is 291 for default dustFeeRate 3000
But according to the comment in reference https://github.com/lombard-finance/evm-smart-contracts/blob/main/contracts/libs/BitcoinUtils.sol#L64 in Bitcoin implementation:
https://github.com/bitcoin/bitcoin/blob/43740f4971f45cd5499470b6a085b3ecd8b96d28/src/policy/policy.cpp#L37-L41
So the DustLimit should be 294 for the dustFeeRate of 3000
So with the amountAfterfee is 292, 293, 294 is considered valid to unstake according to contract LBTC redeem function, but according to Bitcoin network that is still Dust. So if users redeem such amount, the amount will not be able to spendd on Bitcoin.
I double checked the documentation for Bitcoin and I see that the comment in Bitcoin is correct and understandable.
The Output structure of a transaction would contains 3 fields as below:
https://learnmeabitcoin.com/technical/transaction/#structure-outputs
The Input structure contains 5 fields as below
Witness size: 107 bytes but with 75% discount, it is considered as 26 bytes.
Reference: https://github.com/bitcoin/bitcoin/blob/43740f4971f45cd5499470b6a085b3ecd8b96d28/src/policy/policy.cpp#L55-L57
Total input + witness = 41 + 26 = 67 bytes => Total size for dustLimit calculation should be 67 + 31 = 98 bytes. This is correct according to Bitcoin documentation.
To summarize, the documentation of Bitcoin shows that DustLimit for P2WPKH should be 294 for defaultFeeRate of 3000. But the getDustLimitForOutput returns 291. So this results in some amount after fee that are 292, 293, 294 are considered valid for Lombard LBTC to redeem, but consider as Dust on Bitcoin. This will result in the output redeemed amount will not be able to spend on Bitcoin.
Impacts
About the severity assessment
Bug Severity: Low
Impact category:
Contract fails to deliver promised returns, but doesn't lose value
User lost of money
Freeze of user fund on Bitcoin
Note: Although for the reported bug, user redeemed amount will not be spend, means lost forever. But since the amount difference is small and the probability that user redeem such small amount after fee is not high, so I think it is more appropriate to set the reported but as Low severity.
Proof of Concept
Proof of concept
Steps to reproduce the bug:
Step 1: The test code to show this bug:
Just copy the test case into test suite "Positive cases" in evm-smart-contracts\test\LBTC.ts
To run the test
Test log:
Explanation:
So the Amount just above dust limit: 1292n . After the fee, the expectedAmountAfterFee is 292. With this amount, the redeem is succesful. But on Bitcoin, this amount is less than Dust Limit that is 294 so it will not be able to spend.
Was this helpful?