Skip to content

Commit

Permalink
Merge pull request #103 from NethermindEth/anshu/test-lookahead-posting
Browse files Browse the repository at this point in the history
Test lookahead posting
  • Loading branch information
AnshuJalan authored Sep 4, 2024
2 parents 2fef9a6 + e29a9ee commit 95b397d
Show file tree
Hide file tree
Showing 11 changed files with 550 additions and 14 deletions.
3 changes: 2 additions & 1 deletion SmartContracts/remappings.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
forge-std/=lib/forge-std/src/
openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts
openzeppelin-contracts/=lib/openzeppelin-contracts/contracts
openzeppelin-contracts/=lib/openzeppelin-contracts/contracts
src=src
1 change: 1 addition & 0 deletions SmartContracts/src/avs/PreconfConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.25;

library PreconfConstants {
uint256 internal constant MAINNET_BEACON_GENESIS = 1606824023;
uint256 internal constant SECONDS_IN_SLOT = 12;
uint256 internal constant SECONDS_IN_EPOCH = SECONDS_IN_SLOT * 32;
uint256 internal constant TWO_EPOCHS = 2 * SECONDS_IN_EPOCH;
Expand Down
40 changes: 28 additions & 12 deletions SmartContracts/src/avs/PreconfTaskManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -286,18 +286,17 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
*/
function forcePushLookahead(LookaheadSetParam[] calldata lookaheadSetParams) external {
// Sender must be a preconfer
if (preconfRegistry.getPreconferIndex(msg.sender) != 0) {
if (preconfRegistry.getPreconferIndex(msg.sender) == 0) {
revert PreconferNotRegistered();
}

// Lookahead must be lagging behind
LookaheadBufferEntry memory lastLookaheadEntry = lookahead[lookaheadTail % LOOKAHEAD_BUFFER_SIZE];
if (lastLookaheadEntry.timestamp >= block.timestamp) {
revert LookaheadIsNotLagging();
// Lookahead must be missing
uint256 nextEpochTimestamp = _getEpochTimestamp(block.timestamp) + PreconfConstants.SECONDS_IN_EPOCH;
if (!isLookaheadRequired(nextEpochTimestamp)) {
revert LookaheadIsNotRequired();
}

// Update the lookahead for next epoch
uint256 nextEpochTimestamp = _getEpochTimestamp(block.timestamp) + PreconfConstants.SECONDS_IN_EPOCH;
_updateLookahead(nextEpochTimestamp, lookaheadSetParams);

// Block the preconfer from withdrawing stake from Eigenlayer during the dispute window
Expand Down Expand Up @@ -345,7 +344,7 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
uint256 slotTimestamp = lookaheadSetParams[i].timestamp;

// Each entry must be registered in the preconf registry
if (preconfRegistry.getPreconferIndex(preconfer) != 0) {
if (preconfRegistry.getPreconferIndex(preconfer) == 0) {
revert PreconferNotRegistered();
}

Expand Down Expand Up @@ -420,6 +419,10 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
uint256 randomness = uint256(_getBeaconBlockRoot(lastEpochTimestamp));
uint256 preconferIndex = randomness % preconfRegistry.getNextPreconferIndex();

if (preconferIndex == 0) {
preconferIndex = 1;
}

return preconfRegistry.getPreconferAtIndex(preconferIndex);
}

Expand All @@ -437,17 +440,22 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
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) {
// Take the tail to the entry that fills the last slot of the epoch.
// This may be an entry in the next epoch who starts preconfing in advanced.
// This may also be an empty slot since the lookahead for next epoch is not yet posted.
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;
uint256 timestamp = uint256(lookahead[_lookaheadTail % LOOKAHEAD_BUFFER_SIZE].timestamp);

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

lastSlotTimestamp -= PreconfConstants.SECONDS_IN_SLOT;
if (lastSlotTimestamp == prevTimestamp) {
Expand Down Expand Up @@ -499,7 +507,7 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
}
}

// Not very gas efficient, but is okay for a view
// Not very gas efficient, but is okay for a view expected to be used offchain
LookaheadSetParam[] memory lookaheadSetParams = new LookaheadSetParam[](index);
for (uint256 i; i < index; ++i) {
lookaheadSetParams[i] = lookaheadSetParamsTemp[i];
Expand All @@ -512,7 +520,15 @@ contract PreconfTaskManager is IPreconfTaskManager, Initializable {
return lookaheadPosters[epochTimestamp] == address(0);
}

function getLookaheadTail() external view returns (uint256) {
return lookaheadTail;
}

function getLookaheadBuffer() external view returns (LookaheadBufferEntry[LOOKAHEAD_BUFFER_SIZE] memory) {
return lookahead;
}

function getLookaheadPoster(uint256 epochTimestamp) external view returns (address) {
return lookaheadPosters[epochTimestamp];
}
}
8 changes: 7 additions & 1 deletion SmartContracts/src/interfaces/IPreconfTaskManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ interface IPreconfTaskManager {
/// @dev The lookahead preconfer matches the one the actual validator is proposing for
error LookaheadEntryIsCorrect();
/// @dev Cannot force push a lookahead since it is not lagging behind
error LookaheadIsNotLagging();
error LookaheadIsNotRequired();

/// @dev Accepts block proposal by an operator and forwards it to TaikoL1 contract
function newBlockProposal(
Expand Down Expand Up @@ -112,6 +112,12 @@ interface IPreconfTaskManager {
/// @dev In the event that a lookahead was posted but later invalidated, this returns false
function isLookaheadRequired(uint256 epochTimestamp) external view returns (bool);

/// @dev Returns the current lookahead tail
function getLookaheadTail() external view returns (uint256);

/// @dev Returns the entire lookahead buffer
function getLookaheadBuffer() external view returns (LookaheadBufferEntry[64] memory);

/// @dev Returns the lookahead poster for an epoch
function getLookaheadPoster(uint256 epochTimestamp) external view returns (address);
}
17 changes: 17 additions & 0 deletions SmartContracts/test/BaseTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

import {Test} from "forge-std/Test.sol";

contract BaseTest is Test {
address addr_1 = vm.addr(1);
address addr_2 = vm.addr(2);
address addr_3 = vm.addr(3);
address addr_4 = vm.addr(4);
address addr_5 = vm.addr(5);
address addr_6 = vm.addr(6);
address addr_7 = vm.addr(7);
address addr_8 = vm.addr(8);
address addr_9 = vm.addr(9);
address addr_10 = vm.addr(10);
}
43 changes: 43 additions & 0 deletions SmartContracts/test/fixtures/LookaheadFixtures.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

import {BaseTest} from "../BaseTest.sol";
import {MockPreconfRegistry} from "../mocks/MockPreconfRegistry.sol";
import {MockPreconfServiceManager} from "../mocks/MockPreconfServiceManager.sol";
import {MockBeaconBlockRoot} from "../mocks/MockBeaconBlockRoot.sol";
import {MockTaikoL1} from "../mocks/MockTaikoL1.sol";

import {PreconfConstants} from "src/avs/PreconfConstants.sol";
import {PreconfTaskManager} from "src/avs/PreconfTaskManager.sol";
import {IPreconfRegistry} from "src/interfaces/IPreconfRegistry.sol";
import {IPreconfServiceManager} from "src/interfaces/IPreconfServiceManager.sol";
import {ITaikoL1} from "src/interfaces/taiko/ITaikoL1.sol";

contract LookaheadFixtures is BaseTest {
PreconfTaskManager internal preconfTaskManager;
MockPreconfRegistry internal preconfRegistry;
MockPreconfServiceManager internal preconfServiceManager;
MockBeaconBlockRoot internal beaconBlockRootContract;
MockTaikoL1 internal taikoL1;

function setUp() public virtual {
preconfRegistry = new MockPreconfRegistry();
preconfServiceManager = new MockPreconfServiceManager();
beaconBlockRootContract = new MockBeaconBlockRoot();
taikoL1 = new MockTaikoL1();

preconfTaskManager = new PreconfTaskManager(
IPreconfServiceManager(address(preconfServiceManager)),
IPreconfRegistry(address(preconfRegistry)),
ITaikoL1(taikoL1),
PreconfConstants.MAINNET_BEACON_GENESIS,
address(beaconBlockRootContract)
);
}

function addPreconfersToRegistry(uint256 count) internal {
for (uint256 i = 1; i <= count; i++) {
preconfRegistry.registerPreconfer(vm.addr(i));
}
}
}
Loading

0 comments on commit 95b397d

Please sign in to comment.