mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-04-23 03:00:50 -04:00
fix(contracts): fix dropping message with nonce 0 (#640)
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
This commit is contained in:
@@ -66,10 +66,16 @@ contract L1ScrollMessenger is PausableUpgradeable, ScrollMessengerBase, IL1Scrol
|
||||
mapping(bytes32 => ReplayState) public replayStates;
|
||||
|
||||
/// @notice Mapping from queue index to previous replay queue index.
|
||||
///
|
||||
/// @dev If a message `x` was replayed 3 times with index `q1`, `q2` and `q3`, the
|
||||
/// value of `prevReplayIndex` and `replayStates` will be `replayStates[hash(x)].lastIndex = q3`,
|
||||
/// `replayStates[hash(x)].times = 3`, `prevReplayIndex[q3] = q2`, `prevReplayIndex[q2] = q1`
|
||||
/// and `prevReplayIndex[q1] = x`.
|
||||
/// `replayStates[hash(x)].times = 3`, `prevReplayIndex[q3] = q2`, `prevReplayIndex[q2] = q1`,
|
||||
/// `prevReplayIndex[q1] = x` and `prevReplayIndex[x]=nil`.
|
||||
///
|
||||
/// @dev The index `x` that `prevReplayIndex[x]=nil` is used as the termination of the list.
|
||||
/// Usually we use `0` to represent `nil`, but we cannot distinguish it with the first message
|
||||
/// with index zero. So a nonzero offset `1` is added to the value of `prevReplayIndex[x]` to
|
||||
/// avoid such situation.
|
||||
mapping(uint256 => uint256) public prevReplayIndex;
|
||||
|
||||
/***************
|
||||
@@ -200,11 +206,13 @@ contract L1ScrollMessenger is PausableUpgradeable, ScrollMessengerBase, IL1Scrol
|
||||
|
||||
ReplayState memory _replayState = replayStates[_xDomainCalldataHash];
|
||||
// update the replayed message chain.
|
||||
if (_replayState.lastIndex == 0) {
|
||||
// the message has not been replayed before.
|
||||
prevReplayIndex[_nextQueueIndex] = _messageNonce;
|
||||
} else {
|
||||
prevReplayIndex[_nextQueueIndex] = _replayState.lastIndex;
|
||||
unchecked {
|
||||
if (_replayState.lastIndex == 0) {
|
||||
// the message has not been replayed before.
|
||||
prevReplayIndex[_nextQueueIndex] = _messageNonce + 1;
|
||||
} else {
|
||||
prevReplayIndex[_nextQueueIndex] = _replayState.lastIndex + 1;
|
||||
}
|
||||
}
|
||||
_replayState.lastIndex = uint128(_nextQueueIndex);
|
||||
|
||||
@@ -265,6 +273,9 @@ contract L1ScrollMessenger is PausableUpgradeable, ScrollMessengerBase, IL1Scrol
|
||||
IL1MessageQueue(_messageQueue).dropCrossDomainMessage(_lastIndex);
|
||||
_lastIndex = prevReplayIndex[_lastIndex];
|
||||
if (_lastIndex == 0) break;
|
||||
unchecked {
|
||||
_lastIndex = _lastIndex - 1;
|
||||
}
|
||||
}
|
||||
|
||||
isL1MessageDropped[_xDomainCalldataHash] = true;
|
||||
|
||||
@@ -144,9 +144,9 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
|
||||
(_replayTimes, _lastIndex) = l1Messenger.replayStates(hash);
|
||||
assertEq(_replayTimes, i + 1);
|
||||
assertEq(_lastIndex, i + 3);
|
||||
assertEq(l1Messenger.prevReplayIndex(i + 3), i + 2);
|
||||
assertEq(l1Messenger.prevReplayIndex(i + 3), i + 2 + 1);
|
||||
for (uint256 j = 0; j <= i; j++) {
|
||||
assertEq(l1Messenger.prevReplayIndex(i + 3 - j), i + 2 - j);
|
||||
assertEq(l1Messenger.prevReplayIndex(i + 3 - j), i + 2 - j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,51 +239,76 @@ contract L1ScrollMessengerTest is L1GatewayTestBase {
|
||||
|
||||
l1Messenger.updateMaxReplayTimes(10);
|
||||
|
||||
// replay 3 times
|
||||
for (uint256 i = 0; i < 3; i++) {
|
||||
l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), 0, address(0));
|
||||
}
|
||||
assertEq(messageQueue.nextCrossDomainMessageIndex(), 4);
|
||||
// replay 1 time
|
||||
l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), 0, address(0));
|
||||
assertEq(messageQueue.nextCrossDomainMessageIndex(), 2);
|
||||
|
||||
// only first 3 are skipped
|
||||
// skip all 2 messages
|
||||
hevm.startPrank(address(rollup));
|
||||
messageQueue.popCrossDomainMessage(0, 4, 0x7);
|
||||
assertEq(messageQueue.pendingQueueIndex(), 4);
|
||||
messageQueue.popCrossDomainMessage(0, 2, 0x3);
|
||||
assertEq(messageQueue.pendingQueueIndex(), 2);
|
||||
hevm.stopPrank();
|
||||
|
||||
// message already dropped or executed, revert
|
||||
hevm.expectRevert("message already dropped or executed");
|
||||
l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0));
|
||||
|
||||
// send one message with nonce 4 and replay 4 times
|
||||
l1Messenger.sendMessage(address(0), 0, new bytes(0), 0);
|
||||
for (uint256 i = 0; i < 4; i++) {
|
||||
l1Messenger.replayMessage(address(this), address(0), 0, 4, new bytes(0), 0, address(0));
|
||||
}
|
||||
assertEq(messageQueue.nextCrossDomainMessageIndex(), 9);
|
||||
|
||||
// skip all 5 messages
|
||||
hevm.startPrank(address(rollup));
|
||||
messageQueue.popCrossDomainMessage(4, 5, 0x1f);
|
||||
assertEq(messageQueue.pendingQueueIndex(), 9);
|
||||
hevm.stopPrank();
|
||||
for (uint256 i = 4; i < 9; ++i) {
|
||||
for (uint256 i = 0; i < 2; ++i) {
|
||||
assertGt(uint256(messageQueue.getCrossDomainMessage(i)), 0);
|
||||
}
|
||||
hevm.expectEmit(false, false, false, true);
|
||||
emit OnDropMessageCalled(new bytes(0));
|
||||
l1Messenger.dropMessage(address(this), address(0), 0, 4, new bytes(0));
|
||||
for (uint256 i = 4; i < 9; ++i) {
|
||||
l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0));
|
||||
for (uint256 i = 0; i < 2; ++i) {
|
||||
assertEq(messageQueue.getCrossDomainMessage(i), bytes32(0));
|
||||
}
|
||||
|
||||
// send one message with nonce 2 and replay 3 times
|
||||
l1Messenger.sendMessage(address(0), 0, new bytes(0), 0);
|
||||
assertEq(messageQueue.nextCrossDomainMessageIndex(), 3);
|
||||
for (uint256 i = 0; i < 3; i++) {
|
||||
l1Messenger.replayMessage(address(this), address(0), 0, 2, new bytes(0), 0, address(0));
|
||||
}
|
||||
assertEq(messageQueue.nextCrossDomainMessageIndex(), 6);
|
||||
|
||||
// only first 3 are skipped
|
||||
hevm.startPrank(address(rollup));
|
||||
messageQueue.popCrossDomainMessage(2, 4, 0x7);
|
||||
assertEq(messageQueue.pendingQueueIndex(), 6);
|
||||
hevm.stopPrank();
|
||||
|
||||
// message already dropped or executed, revert
|
||||
hevm.expectRevert("message already dropped or executed");
|
||||
l1Messenger.dropMessage(address(this), address(0), 0, 2, new bytes(0));
|
||||
|
||||
// send one message with nonce 6 and replay 4 times
|
||||
l1Messenger.sendMessage(address(0), 0, new bytes(0), 0);
|
||||
for (uint256 i = 0; i < 4; i++) {
|
||||
l1Messenger.replayMessage(address(this), address(0), 0, 6, new bytes(0), 0, address(0));
|
||||
}
|
||||
assertEq(messageQueue.nextCrossDomainMessageIndex(), 11);
|
||||
|
||||
// skip all 5 messages
|
||||
hevm.startPrank(address(rollup));
|
||||
messageQueue.popCrossDomainMessage(6, 5, 0x1f);
|
||||
assertEq(messageQueue.pendingQueueIndex(), 11);
|
||||
hevm.stopPrank();
|
||||
for (uint256 i = 6; i < 11; ++i) {
|
||||
assertGt(uint256(messageQueue.getCrossDomainMessage(i)), 0);
|
||||
}
|
||||
hevm.expectEmit(false, false, false, true);
|
||||
emit OnDropMessageCalled(new bytes(0));
|
||||
l1Messenger.dropMessage(address(this), address(0), 0, 6, new bytes(0));
|
||||
for (uint256 i = 6; i < 11; ++i) {
|
||||
assertEq(messageQueue.getCrossDomainMessage(i), bytes32(0));
|
||||
}
|
||||
|
||||
// Message already dropped, revert
|
||||
hevm.expectRevert("Message already dropped");
|
||||
l1Messenger.dropMessage(address(this), address(0), 0, 4, new bytes(0));
|
||||
l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0));
|
||||
hevm.expectRevert("Message already dropped");
|
||||
l1Messenger.dropMessage(address(this), address(0), 0, 6, new bytes(0));
|
||||
|
||||
// replay dropped message, revert
|
||||
hevm.expectRevert("Message already dropped");
|
||||
l1Messenger.replayMessage(address(this), address(0), 0, 4, new bytes(0), 0, address(0));
|
||||
l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), 0, address(0));
|
||||
hevm.expectRevert("Message already dropped");
|
||||
l1Messenger.replayMessage(address(this), address(0), 0, 6, new bytes(0), 0, address(0));
|
||||
}
|
||||
|
||||
function onDropMessage(bytes memory message) external payable {
|
||||
|
||||
Reference in New Issue
Block a user