Tangy Coconut Crocodile
Medium
The missing check for stakerRankings
to determine whether it is "0" in L2Staking.sol:176-178 will unnecessarily update the sequencer set in Sequencer.sol
, which will result in incorrect sequencer sets in Sequencer.sol
.
When a staker calls the internal function L1Staking.sol::removeStaker(), the _msgRemoveStakers
function is executed, sending a message to L2 to inform that the sequencer is removed.
After the staker is removed, the same staker can still be removed from L2 if they are slashed and the slashed sequencer is that same sequencer. Since the L2 sequencer is already removed, or will be removed again, when L2Staking.sol::removeStakers() is called for the second time, the stakerRankings
will be "0." Therefore, this check will always pass, unnecessarily updating the sequencer set, even though it shouldn't be updated because the sequencer was already removed during the first removal. This extra, unnecessary update of sequencers will modify the sequencerSets
inside Sequencer.sol:86.
- Staker gets removed in L1 or withdraws its stake which sends the
removeStakers
message to L2 - Staker gets slashed and an another
removeStakers
message is sent to L2
none needed
Same as the internal pre-conditions. As long as the pre-conditions are met then the bug will exist without any additional change
The Sequencer.sol
contract is responsible for storing data related to the sequencerSet
. The L2 node reads the real-time changes in the contract to update the sequencerSet
of the consensus layer. As a result, an incorrect update to the sequencerSet
will alter the sequencer sets stored inside Sequencer.sol
, leading to inaccurate sequencer sets being maintained in Sequencer.sol
.
None needed
add the stakerRankings[remove[i]] != 0
check to L2Staking.sol:176-178:
if (stakerRankings[remove[i]] <= latestSequencerSetSize && stakerRankings[remove[i]] != 0) {
updateSequencerSet = true;
}