Open sherlock-admin opened 1 year ago
Sponsor comment: This should improve gas estimation but is low in severity since it does not affect usage or impact the intended functionality.
Would judge in the same way as #77 the incorrect math can lead to an issue
I see this as logically equivalent to #77 so I believe it should be awarded as Med
This can also be quantified as
140 * 16 = 2240
All l1 -> l2 tx are underpriced by that amount (roughly 10% of fixed base cost)
Can see this being escalated against because it's "only" 10% incorrect, but find hard to argue against the math not being correct
Jeiwan
medium
Gas usage of cross-chain messages is undercounted, causing discrepancy between L1 and L2 and impacting intrinsic gas calculation
Summary
Gas consumption of messages sent via CrossDomainMessenger (including both L1CrossDomainMessenger and L2CrossDomainMessenger) is calculated incorrectly: the gas usage of the "relayMessage" wrapper is not counted. As a result, the actual gas consumption of sending a message will be higher than expected. Users will pay less for gas on L1, and L2 blocks may be filled earlier than expected. This will also affect gas metering via ResourceMetering: metered gas will be lower than actual consumed gas, and the EIP-1559-like gas pricing mechanism won't reflect the actual demand for gas.
Vulnerability Detail
The CrossDomainMessenger.sendMessage function is used to send cross-chain messages. Users are required to set the
_minGasLimit
argument, which is the expected amount of gas that the message will consume on the other chain. The function also computes the amount of gas required to pass the message to the other chain: this is done in the baseGas function, which computes the byte-wise cost of the message.CrossDomainMessenger
also allows users to replay their messages on the destination chain if they failed: to allow this, the contract wraps user messages inrelayMessage
calls. This increases the size of messages, but thebaseGas
call above counts gas usage of only the original, not wrapped in therelayMessage
call, message.This contradicts the intrinsic gas calculation in
op-geth
, which calculates gas of an entire message data:Thus, there's a discrepancy between the contract and the node, which will result in the node consuming more gas than users paid for.
This behaviour also disagrees with how the migration process works:
data
is the entire messages, including therelayMessage
calldata;Taking into account the logic of paying cross-chain messages' gas consumption on L1, I think the implementation in the migration code is correct and the implementation in
CrossDomainMessenger
is wrong: users should pay for sending the entire cross-chain message, not just the calldata that will be execute on the recipient on the other chain.Impact
Since the
CrossDomainMessenger
contract is recommended to be used as the main cross-chain messaging contract and since it's used by both L1 and L2 bridges (when bridging ETH or ERC20 tokens), the undercounted gas will have a broad impact on the system. It'll create a discrepancy in gas usage and payment on L1 and L2: on L1, users will pay for less gas than actually will be consumed by cross-chain messages.Also, since messages sent from L1 to L2 (via OptimismPortal.depositTransaction) are priced using an EIP-1559-like mechanism (via ResourceMetering._metered), the mechanism will fail to detect the actual demand for gas and will generally set lower gas prices, while actual gas consumption will be higher.
The following bytes are excluded from gas usage counting:
relayMessage
selector;Thus, every cross-chain message sent via the bridge or the messenger will contain 140 bytes that won't be paid by users. The bytes will however be processed by the node and accounted in the gas consumption.
Code Snippet
CrossDomainMessenger.sendMessage
wraps cross-chain messages inrelayMessage
calls: https://github.com/ethereum-optimism/optimism/blob/9b9f78c6613c6ee53b93ca43c71bb74479f4b975/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L260-L268relayMessage
wrapping: https://github.com/ethereum-optimism/optimism/blob/9b9f78c6613c6ee53b93ca43c71bb74479f4b975/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L258When pre-Bedrock withdrawals are migrated, gas limit calculation does include the
relayMessage
wrapping: https://github.com/ethereum-optimism/optimism/blob/9b9f78c6613c6ee53b93ca43c71bb74479f4b975/op-chain-ops/crossdomain/migrate.go#L73-L86Tool used
Manual Review
Recommendation
When counting gas limit in the
CrossDomainMessenger.sendMessage
function, consider counting the entire message, including therelayMessage
calldata wrapping. Consider a change like that: