Skip to content

Commit

Permalink
Merge pull request #102 from NethermindEth/anshu/fallback-within-look…
Browse files Browse the repository at this point in the history
…ahead

Keep fallback preconfer within lookahead post invalidation
  • Loading branch information
AnshuJalan authored Sep 3, 2024
2 parents 9c40850 + 2c4b2a5 commit 2fef9a6
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 58 deletions.
126 changes: 72 additions & 54 deletions SmartContracts/src/avs/PreconfTaskManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
uint256 internal constant LOOKAHEAD_BUFFER_SIZE = 64;
LookaheadBufferEntry[LOOKAHEAD_BUFFER_SIZE] internal lookahead;

// Maps the epoch timestamp to the lookahead poster, correctness status and fallback preconfer
// Maps the epoch timestamp to the lookahead poster.
// If the lookahead and the poster has been slashed, it maps to the 0-address.
// Note: This may be optimised to re-use existing slots and reduce gas cost.
mapping(uint256 epochTimestamp => LookaheadMetadata) internal lookaheadMetadatas;
mapping(uint256 epochTimestamp => address poster) internal lookaheadPosters;

// Maps the block height to the associated proposer
// This is required since the stored block in Taiko has this contract as the proposer
Expand Down Expand Up @@ -77,24 +78,18 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
LookaheadBufferEntry memory lookaheadEntry = lookahead[lookaheadPointer % LOOKAHEAD_BUFFER_SIZE];

uint256 currentEpochTimestamp = _getEpochTimestamp(block.timestamp);
LookaheadMetadata memory lookaheadMetadata = lookaheadMetadatas[currentEpochTimestamp];

// Revert if the lookahead was proven incorrect and the sender is not the fallback preconfer
if (lookaheadMetadata.incorrect && msg.sender != lookaheadMetadata.fallbackPreconfer) {
revert SenderIsNotTheFallbackPreconfer();
} else {
// The current L1 block's timestamp must be within the range retrieved from the lookahead entry.
// The preconfer is allowed to propose a block in advanced if there are no other entries in the
// lookahead between the present slot and the preconfer's own slot.
//
// ------[Last slot with an entry]---[X]---[X]----[X]----[Preconfer]-------
// ------[ prevTimestamp ]---[ ]---[ ]----[ ]----[timestamp]-------
//
if (block.timestamp <= lookaheadEntry.prevTimestamp || block.timestamp > lookaheadEntry.timestamp) {
revert InvalidLookaheadPointer();
} else if (msg.sender != lookaheadEntry.preconfer) {
revert SenderIsNotThePreconfer();
}
// The current L1 block's timestamp must be within the range retrieved from the lookahead entry.
// The preconfer is allowed to propose a block in advanced if there are no other entries in the
// lookahead between the present slot and the preconfer's own slot.
//
// ------[Last slot with an entry]---[X]---[X]----[X]----[Preconfer]-------
// ------[ prevTimestamp ]---[ ]---[ ]----[ ]----[timestamp]-------
//
if (block.timestamp <= lookaheadEntry.prevTimestamp || block.timestamp > lookaheadEntry.timestamp) {
revert InvalidLookaheadPointer();
} else if (msg.sender != lookaheadEntry.preconfer) {
revert SenderIsNotThePreconfer();
}

uint256 nextEpochTimestamp = currentEpochTimestamp + PreconfConstants.SECONDS_IN_EPOCH;
Expand Down Expand Up @@ -181,10 +176,10 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
) external {
uint256 epochTimestamp = _getEpochTimestamp(slotTimestamp);

LookaheadMetadata memory lookaheadMetadata = lookaheadMetadatas[epochTimestamp];
address poster = lookaheadPosters[epochTimestamp];

// The poster must not already be slashed
if (lookaheadMetadata.incorrect || lookaheadMetadata.poster == address(0)) {
// Poster must not have been slashed
if (poster == address(0)) {
revert PosterAlreadySlashedOrLookaheadIsEmpty();
}

Expand Down Expand Up @@ -241,15 +236,46 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
revert LookaheadEntryIsCorrect();
}

uint256 epochEndTimestamp = epochTimestamp + PreconfConstants.SECONDS_IN_EPOCH;

// If it is the current epoch's lookahead being proved incorrect then insert a fallback preconfer
if (block.timestamp < epochTimestamp + PreconfConstants.SECONDS_IN_EPOCH) {
lookaheadMetadatas[epochTimestamp].fallbackPreconfer = getFallbackPreconfer(epochTimestamp);
if (block.timestamp < epochEndTimestamp) {
uint256 _lookaheadTail = lookaheadTail;

// Get to the entry in the next epoch that connects to a slot in the current epoch
while (lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].prevTimestamp >= epochEndTimestamp) {
_lookaheadTail -= 1;
}

uint256 lastSlotTimestamp = epochEndTimestamp - PreconfConstants.SECONDS_IN_SLOT;

// Switch the connection to the last slot of the current epoch
lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].prevTimestamp = uint40(lastSlotTimestamp);

// Add the fallback preconfer to the last slot of the current epoch
_lookaheadTail -= 1;
lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE] = LookaheadBufferEntry({
isFallback: true,
timestamp: uint40(lastSlotTimestamp),
prevTimestamp: uint40(epochTimestamp - PreconfConstants.SECONDS_IN_SLOT),
preconfer: getFallbackPreconfer(epochTimestamp)
});

_lookaheadTail -= 1;

// Nullify the rest of the lookahead entries for this epoch
while (lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].timestamp >= epochTimestamp) {
lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE] =
LookaheadBufferEntry({isFallback: false, timestamp: 0, prevTimestamp: 0, preconfer: address(0)});
_lookaheadTail -= 1;
}
}

lookaheadMetadatas[epochTimestamp].incorrect = true;
preconfServiceManager.slashOperator(lookaheadMetadata.poster);
// Slash the poster
lookaheadPosters[epochTimestamp] = address(0);
preconfServiceManager.slashOperator(poster);

emit ProvedIncorrectLookahead(lookaheadMetadata.poster, slotTimestamp, msg.sender);
emit ProvedIncorrectLookahead(poster, slotTimestamp, msg.sender);
}

/**
Expand Down Expand Up @@ -343,7 +369,7 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
}

lookaheadTail = _lookaheadTail;
lookaheadMetadatas[epochTimestamp].poster = msg.sender;
lookaheadPosters[epochTimestamp] = msg.sender;

// We directly use the lookahead set params even in the case of a fallback preconfer to
// assist the nodes in identifying an incorrect lookahead. The contents of this event can be matched against
Expand Down Expand Up @@ -407,35 +433,27 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
function getLookaheadForEpoch(uint256 epochTimestamp) external view returns (address[SLOTS_IN_EPOCH] memory) {
address[SLOTS_IN_EPOCH] memory lookaheadForEpoch;

LookaheadMetadata memory lookaheadMetadata = lookaheadMetadatas[epochTimestamp];

if (lookaheadMetadata.incorrect) {
for (uint256 i; i < SLOTS_IN_EPOCH; ++i) {
lookaheadForEpoch[i] = lookaheadMetadata.fallbackPreconfer;
}
} else {
uint256 _lookaheadTail = lookaheadTail;
uint256 lastSlotTimestamp =
epochTimestamp + PreconfConstants.SECONDS_IN_EPOCH - PreconfConstants.SECONDS_IN_SLOT;
uint256 _lookaheadTail = lookaheadTail;
uint256 lastSlotTimestamp =
epochTimestamp + PreconfConstants.SECONDS_IN_EPOCH - PreconfConstants.SECONDS_IN_SLOT;

// Find the entry that fills the last slot of the epoch
while (lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].prevTimestamp > lastSlotTimestamp) {
_lookaheadTail -= 1;
}
// Find the entry that fills the last slot of the epoch
while (lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].prevTimestamp > lastSlotTimestamp) {
_lookaheadTail -= 1;
}

address preconfer = lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].preconfer;
uint256 prevTimestamp = lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].prevTimestamp;
address preconfer = lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].preconfer;
uint256 prevTimestamp = lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].prevTimestamp;

// Iterate backwards and fill in the slots
for (uint256 i = SLOTS_IN_EPOCH - 1; i >= 0; --i) {
lookaheadForEpoch[i] = preconfer;
// Iterate backwards and fill in the slots
for (uint256 i = SLOTS_IN_EPOCH - 1; i >= 0; --i) {
lookaheadForEpoch[i] = preconfer;

lastSlotTimestamp -= PreconfConstants.SECONDS_IN_SLOT;
if (lastSlotTimestamp == prevTimestamp) {
_lookaheadTail -= 1;
preconfer = lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].preconfer;
prevTimestamp = lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].prevTimestamp;
}
lastSlotTimestamp -= PreconfConstants.SECONDS_IN_SLOT;
if (lastSlotTimestamp == prevTimestamp) {
_lookaheadTail -= 1;
preconfer = lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].preconfer;
prevTimestamp = lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].prevTimestamp;
}
}

Expand Down Expand Up @@ -491,7 +509,7 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
}

function isLookaheadRequired(uint256 epochTimestamp) public view returns (bool) {
return lookaheadMetadatas[epochTimestamp].poster == address(0);
return lookaheadPosters[epochTimestamp] == address(0);
}

function getLookaheadBuffer() external view returns (LookaheadBufferEntry[LOOKAHEAD_BUFFER_SIZE] memory) {
Expand Down
4 changes: 0 additions & 4 deletions SmartContracts/src/interfaces/IPreconfTaskManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ interface IPreconfTaskManager {
event ProvedIncorrectPreconfirmation(address indexed preconfer, uint256 indexed blockId, address indexed disputer);
event ProvedIncorrectLookahead(address indexed poster, uint256 indexed timestamp, address indexed disputer);

/// @dev The block proposer is not the randomly chosen fallback preconfer for the current slot/timestamp
error SenderIsNotTheFallbackPreconfer();
/// @dev The current (or provided) timestamp does not fall in the range provided by the lookahead pointer
error InvalidLookaheadPointer();
/// @dev The block proposer is not the assigned preconfer for the current slot/timestamp
Expand All @@ -65,8 +63,6 @@ interface IPreconfTaskManager {
error PreconfirmationIsCorrect();
/// @dev The sent block metadata does not match the one retrieved from Taiko
error MetadataMismatch();
/// @dev The expected validator has been slashed on CL
error ExpectedValidatorMustNotBeSlashed();
/// @dev The lookahead poster for the epoch has already been slashed or there is no lookahead for epoch
error PosterAlreadySlashedOrLookaheadIsEmpty();
/// @dev The lookahead preconfer matches the one the actual validator is proposing for
Expand Down

0 comments on commit 2fef9a6

Please sign in to comment.