Closed c4-bot-3 closed 2 months ago
saxenism (sponsor) acknowledged
saxenism marked the issue as disagree with severity
Design Decision, We consider this a QA report.
The Warden describes that gas adjustments overcompensate operator overheads, which we consider an acceptable business practice as it is in the interest of the protocol to protect the operators instead of the users when it comes to transaction gas costs. As such, this finding is better suited as part of an Analysis or QA report as criticism.
alex-ppg marked the issue as unsatisfactory: Overinflated severity
Lines of code
https://github.com/code-423n4/2024-03-zksync/blob/4f0ba34f34a864c354c7e8c47643ed8f4a250e13/code/contracts/ethereum/contracts/state-transition/libraries/TransactionValidator.sol#L128-L137 https://github.com/code-423n4/2024-03-zksync/blob/4f0ba34f34a864c354c7e8c47643ed8f4a250e13/code/system-contracts/bootloader/bootloader.yul#L37-L53 https://github.com/code-423n4/2024-03-zksync/blob/4f0ba34f34a864c354c7e8c47643ed8f4a250e13/code/system-contracts/bootloader/bootloader.yul#L2010-L2017
Vulnerability details
Proof of Concept
Consider https://github.com/code-423n4/2024-03-zksync/blob/4f0ba34f34a864c354c7e8c47643ed8f4a250e13/code/contracts/ethereum/contracts/state-transition/libraries/TransactionValidator.sol#L128-L137
This function is used to get the overhead that a user is to pay upfront for a tx, now both
TX_SLOT_OVERHEAD_L2_GAS
andMEMORY_OVERHEAD_GAS
are10000
and10
respectively.Navigating to the bootloader we can see that the logic/values are as expected the same, i.e https://github.com/code-423n4/2024-03-zksync/blob/4f0ba34f34a864c354c7e8c47643ed8f4a250e13/code/system-contracts/bootloader/bootloader.yul#L37-L53
And https://github.com/code-423n4/2024-03-zksync/blob/4f0ba34f34a864c354c7e8c47643ed8f4a250e13/code/system-contracts/bootloader/bootloader.yul#L2010-L2017
Evidently, the
MEMORY_OVERHEAD_GAS
value has been set to 10, issue with this is that, according to the docs we can see that to get this value this is the formula usedTX_MEMORY_OVERHEAD_GAS = MAX_GAS_PER_BATCH / MAX_MEMORY_FOR_BATCH
.Now would be key to note that, for L1->L2 transactions the
MAX_GAS_PER_BATCH
is considered to be80kk
andMAX_MEMORY_FOR_BATCH
is32 * 600_000
, i.e19.2kk
.Causing the
TX_MEMORY_OVERHEAD_GAS = 80kk/19.2kk
~=4,17
, now protocol decided to use10
gas value so as to better take into account the load on the operator from storing the information about the transaction, but this is too much of an increase, i.e ~ a140%
increase that is implaced on the users to pay.To put this in perspective, due to the same reason of taking into account the load on the operator for storing the information the
TX_OVERHEAD_GAS
was only increased by a mere25%
, which is less than1/5
of the increase applied the memory overhead gas.Impact
Medium since this leads to extremely unfair gas prices for users, way over the original gas price that could be paid.
Keep in mind that the current implementation of getting the overhead price mixes both incrementing instances, i.e it first places a
~140%
fromsafeMul(txEncodeLen, MEMORY_OVERHEAD_GAS()
and then if this value is less thanTX_SLOT_OVERHEAD_GAS
it applies up to25%
more increment Remember https://github.com/code-423n4/2024-03-zksync/blob/4f0ba34f34a864c354c7e8c47643ed8f4a250e13/code/system-contracts/bootloader/bootloader.yul#L2010-L2018Lets show case two scenarios within two cases:
Case A
- doesn't take into account the load on the operator from storing the information about the transaction for both variables whereasCase B
does(i.e Under A (MEMORY_OVERHEAD_GAS = 4,17
andTX_SLOT_OVERHEAD_GAS = 8kk
) and Under B (MEMORY_OVERHEAD_GAS = 10
andTX_SLOT_OVERHEAD_GAS = 10kk
)Case A
1..=999
1..999
safeMul(txEncodeLen, MEMORY_OVERHEAD_GAS(),
=4.17..4166
getTransactionUpfrontOverhead()
returns8000
since any of4.17..4166
<TX_SLOT_OVERHEAD_GAS
indicating a percentage increase of 191747%..92%2000
safeMul(txEncodeLen, MEMORY_OVERHEAD_GAS(),
= 8340getTransactionUpfrontOverhead()
returns8340
since8340
>TX_SLOT_OVERHEAD_GAS
TLDR of Case A: In comparison to the real _
safeMul(txEncodeLen, MEMORY_OVERHEAD_GAS()
_ users could get charged as high as190K%
due to the minimum enforcement... let's just pick one from the aforementioned scenarios, i.etxEncodeLen = 100
this still leads to an increase of> 1800%
(keep in mind that we are not considering the operator's storage load but users are still being charged this much increment).1..=999
1..=999
safeMul(txEncodeLen, MEMORY_OVERHEAD_GAS(),
=10..9990
getTransactionUpfrontOverhead()
returns10000
since any of10..9990
<TX_SLOT_OVERHEAD_GAS
... indicating a percentage increase 99000%..0,1% of the real _safeMul(txEncodeLen, MEMORY_OVERHEAD_GAS(),
_ .2000
safeMul(txEncodeLen, MEMORY_OVERHEAD_GAS(),
= 20000getTransactionUpfrontOverhead()
returns20000
since20000
>TX_SLOT_OVERHEAD_GAS
TLDR of Case B: In comparison to the real _
safeMul(txEncodeLen, MEMORY_OVERHEAD_GAS()
_ users could get charged as high as99K%
due to the minimum enforcement, keep in mind that here we have considered the operator's storage load sinceMEMORY_OVERHEAD_GAS
==10
and users are being charged this much.TLDR of Both Cases: Comparing Case A to B, we can see that whereas the
encodedLength
is the same, Case A would have a massive increase up untiltxEncodeLen = 1918
where the real _safeMul(txEncodeLen, MEMORY_OVERHEAD_GAS(),
_ would be ~8000 as shown in the first scenarios of both cases, but from there onwards Case B charges even higher since automatically using scenario 2000, overhead gas for case A is8340
where as that of case B is20000
a ~140% increaseSo in short, transactions that would get succeeded would need to provide insane amount as their gas limit otherwise this check would cause their transaction to revert
Recommended Mitigation Steps
Consider reimplementing the logic for the amount of increase being applied to
MEMORY_OVERHEAD_GAS
, since there is already an increase for the slot overhead to consider the operator's attempt of storing the data, i.e the increment of the overhead slot as at current already sorts out when the length of the encoded data is little, since it defaults to the max from bootloader.yul#L2013-L2016, i.esafeMul(txEncodeLen, MEMORY_OVERHEAD_GAS(), "iot")
&TX_SLOT_OVERHEAD_GAS()
, which is understandable.Assessed type
Context