livepeer / livepeer/protocol

Missing freshness check in L2LPTDataCache.finalizeCacheTotalSupply leads to underpaid (or overpaid) rewards in the next rounds

Open
#664 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

known-issue
Dominant language
JavaScript
Stars
155
Forks
50
PR merge metrics
No merged PRs in 30d

Description

### Summary

The current implementation of the [L1LPTDataCache.cacheTotalSupply](https://github.com/livepeer/arbitrum-lpt-bridge/blob/fb75e274c28e5d4332fc177961904df67a38745d/contracts/L1/gateway/L1LPTDataCache.sol#L29-L52) and its counterpart [L2LPTDataCache.finalizeCacheTotalSupply](https://github.com/livepeer/arbitrum-lpt-bridge/blob/fb75e274c28e5d4332fc177961904df67a38745d/contracts/L2/gateway/L2LPTDataCache.sol#L81-L92) does not implement a freshness check of the received `_totalSupply` amount via a cross-chain message. This is a problem, since cross-chain messages sent via Arbitrum's native retryable tickets can fail immediate automatic redemption, but can be redeemed manually much later, when bridged `totalSupply` is severely outdated. This can, in the worst case, affect the amount of rewards delegates will receive on L2 – this number is currently used only by the Minter to determine rewards at the round start.

### Root causes

1. Preventable automatic redemption. Permissionless `L1LPTDataCache.cacheTotalSupply` function accepts arbitrary `gasLimit` value used by the automatic redeemer.
2. A potential for severely outdated `totalSupply` amount. Native retryable ticket can be kept alive indefinitely by calling [ArbRetryableTx.keepalive(bytes32 ticketId)](https://docs.arbitrum.io/for-devs/dev-tools-and-resources/partials/precompile-tables/_ArbRetryableTx#:~:text=keepalive(bytes32%20ticketId))) every 7 days.
3. Missing freshness check. The receiving L2 method `finalizeCacheTotalSupply` does not validate that the message was sent recently; no expiration is implemented at the application level.

### Why it's not an issue

- This hasn't been a problem so far: the `totalSupply` on L1 hasn't changed since the migration to Arbitrum (and the deployment of the contracts in question).
- Triggering the attack currently requires either burning LPT tokens on L1 or bridging a massive amount of LPT from Arbitrum back to the Ethereum Mainnet. This makes the likelihood extremely low.
- The issue is monitored offchain. If an outdated ticket is found, it can be pemissionlessly redeemed by anyone, effectively cancelling the attack even before the value gets outdated.

### Out of scope for the bug bounty

Reports targeting missing freshness check inside [L2LPTDataCache.finalizeCacheTotalSupply](https://github.com/livepeer/arbitrum-lpt-bridge/blob/fb75e274c28e5d4332fc177961904df67a38745d/contracts/L2/gateway/L2LPTDataCache.sol#L81-L92) method will be closed as known issues and are not eligible for rewards under the [Livepeer Immunefi bug bounty program](https://immunefi.com/bug-bounty/livepeer/information/), unless the report shows how the severity can be escalated beyond what is already shown above.

### References

- [L2LPTDataCache.finalizeCacheTotalSupply](https://github.com/livepeer/arbitrum-lpt-bridge/blob/fb75e274c28e5d4332fc177961904df67a38745d/contracts/L2/gateway/L2LPTDataCache.sol#L81-L92) – source code of the method with missing freshness check
- [0xd78b6bd09cd28a83cfb21afa0da95c685a6bb0b1](https://arbiscan.io/address/0xd78b6bd09cd28a83cfb21afa0da95c685a6bb0b1#code) – deployed, currently active version of the contract

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with contracts/L2/gateway/L2LPTDataCache.sol, especially finalizeCacheTotalSupply, and compare it with contracts/L1/gateway/L1LPTDataCache.sol and the linked Arbitrum retryable-ticket behavior. Search the repository for related cache, bridge, and reward tests. Done should include a decided freshness rule and validation that outdated cross-chain messages cannot affect later reward rounds.

Written by the indexing model from the issue text.

Assessment

Tech stack
solidity
Domain
blockchain, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.