Closed code423n4 closed 1 year ago
GalloDaSballo marked the issue as primary issue
Primary for detail
vladbochok marked the issue as sponsor disputed
Operator actually sends the whole bytecode to the L1, otherwise it will be impossible to restore chain observing the L1.
@vladbochok to confirm, you're disputing because the sender must pay for the entire cost, since zkSync / operator will have to publish the entire code, meaning the deployer should pay the entire cost to offset the operator cost?
@GalloDaSballo exactly! The L1 contract enforces the publishing preimage from the bytecode hash
Per the discussion with the sponsor, the cost is paid to offset the operator cost
GalloDaSballo marked the issue as unsatisfactory: Invalid
Lines of code
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/KnownCodesStorage.sol#L97
Vulnerability details
Impact
When deploying new contracts, users are forced to pay more gas than is required to publish the bytecode hash to the L1.
Proof of Concept
When users deploy new smart contracts, the protocol marks the hashes of the bytecodes of the contracts as known and sends the bytecode to the L1. As per the description of the project:
And:
There are two functions that handle publishing of bytecodes and their hashes to the L1 network:
sendToL1
, the amount of the gas that the transaction sender needs to pay is computed as:I.e. the user sends the rate of
gasPerPubdataBytes
per each byte of the sent data (that is, the bytecode). Actual publishing of bytecode is implemented by emitting a message:To put it simply, the user pays for the size of the bytecode.
KnownCodesStorage.markFactoryDeps is used to publish bytecode hashes to the L1. The bootloader always calls this function when processes transaction factory dependencies.
markFactoryDeps
calls_markBytecodeAsPublished
, which in its turn calls_sendBytecodeToL1
to handle publishing. Inside_sendBytecodeToL1
the cost of publishing is computed identically toBytecodeCompressor.publishCompressedBytecode
(+ the overhead cost):Where
_l1PreimageBytesLen
is the length of the bytecode (not the hash). However,_sendBytecodeToL1
doesn't emit aL1MessageSent
log with a full bytecode: similarly toL1Messenger.sendToL1
, it callsSystemContractHelper.toL1
, but it doesn't emit aL1MessageSent
event after that.Thus, the
KnownCodesStorage._sendBytecodeToL1
function charges users for publishing the entire bytecode while publishing only its hash.Tools Used
Manual review
Recommended Mitigation Steps
In the
KnownCodesStorage._sendBytecodeToL1
consider computing the amount of gas to burn based on the actual amount of bytes published to the L1. Judging by the fact that the functions only sends a bytecode hash, users should only be charge to pay for the size of a hash, not the size of an entire bytecode.