Skip to content

Commit

Permalink
feat!: time library (#11542)
Browse files Browse the repository at this point in the history
Fixed #11520
  • Loading branch information
LHerskind authored Feb 3, 2025
1 parent 65a3f11 commit 3b463f9
Show file tree
Hide file tree
Showing 68 changed files with 209 additions and 176 deletions.
2 changes: 1 addition & 1 deletion docs/docs/guides/developer_guides/js_apps/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ The [`CheatCodes`](../../../reference/developer_references/sandbox_reference/che
### Set next block timestamp

Since the rollup time is dependent on what "slot" the block is included in, time can be progressed by progressing slots.
The duration of a slot is available by calling `SLOT_DURATION()` on the Rollup (code in Leonidas.sol).
The duration of a slot is available by calling `getSlotDuration()` on the Rollup (code in Leonidas.sol).

You can then use the `warp` function on the EthCheatCodes to progress the underlying chain.

Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/ProofCommitmentEscrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity >=0.8.27;

import {IProofCommitmentEscrow} from "@aztec/core/interfaces/IProofCommitmentEscrow.sol";
import {Errors} from "@aztec/core/libraries/Errors.sol";
import {Timestamp} from "@aztec/core/libraries/TimeMath.sol";
import {Timestamp} from "@aztec/core/libraries/TimeLib.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol";

Expand Down
15 changes: 7 additions & 8 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
} from "@aztec/core/libraries/RollupLibs/ExtRollupLib.sol";
import {IntRollupLib, EpochProofQuote} from "@aztec/core/libraries/RollupLibs/IntRollupLib.sol";
import {ProposeArgs, ProposeLib} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol";
import {Timestamp, Slot, Epoch, SlotLib, EpochLib} from "@aztec/core/libraries/TimeMath.sol";
import {Timestamp, Slot, Epoch, TimeLib} from "@aztec/core/libraries/TimeLib.sol";
import {Inbox} from "@aztec/core/messagebridge/Inbox.sol";
import {Outbox} from "@aztec/core/messagebridge/Outbox.sol";
import {ProofCommitmentEscrow} from "@aztec/core/ProofCommitmentEscrow.sol";
Expand Down Expand Up @@ -63,8 +63,6 @@ struct Config {
* @dev WARNING: This contract is VERY close to the size limit (500B at time of writing).
*/
contract Rollup is EIP712("Aztec Rollup", "1"), Ownable, ValidatorSelection, IRollup, ITestRollup {
using SlotLib for Slot;
using EpochLib for Epoch;
using ProposeLib for ProposeArgs;
using IntRollupLib for uint256;
using IntRollupLib for ManaBaseFeeComponents;
Expand Down Expand Up @@ -662,7 +660,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Ownable, ValidatorSelection, IRo
rollupStore.blocks[blockOfInterest].feeHeader,
getL1FeesAt(_timestamp),
_inFeeAsset ? getFeeAssetPrice() : 1e9,
EPOCH_DURATION
TimeLib.getStorage().epochDuration
);
}

Expand All @@ -683,7 +681,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Ownable, ValidatorSelection, IRo
Slot currentSlot = getSlotAt(_ts);
address currentProposer = getProposerAt(_ts);
Epoch epochToProve = getEpochToProve();
uint256 posInEpoch = positionInEpoch(currentSlot);
uint256 posInEpoch = TimeLib.positionInEpoch(currentSlot);
bytes32 digest = quoteToDigest(_quote.quote);

ExtRollupLib.validateEpochProofRightClaimAtTime(
Expand Down Expand Up @@ -786,16 +784,17 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Ownable, ValidatorSelection, IRo

Slot currentSlot = getSlotAt(_ts);
Epoch oldestPendingEpoch = getEpochForBlock(rollupStore.tips.provenBlockNumber + 1);
Slot startSlotOfPendingEpoch = toSlots(oldestPendingEpoch);
Slot startSlotOfPendingEpoch = TimeLib.toSlots(oldestPendingEpoch);

// suppose epoch 1 is proven, epoch 2 is pending, epoch 3 is the current epoch.
// we prune the pending chain back to the end of epoch 1 if:
// - the proof claim phase of epoch 3 has ended without a claim to prove epoch 2 (or proof of epoch 2)
// - we reach epoch 4 without a proof of epoch 2 (regardless of whether a proof claim was submitted)
bool inClaimPhase = currentSlot
< startSlotOfPendingEpoch + toSlots(Epoch.wrap(1)) + Slot.wrap(CLAIM_DURATION_IN_L2_SLOTS);
< startSlotOfPendingEpoch + TimeLib.toSlots(Epoch.wrap(1))
+ Slot.wrap(CLAIM_DURATION_IN_L2_SLOTS);

bool claimExists = currentSlot < startSlotOfPendingEpoch + toSlots(Epoch.wrap(2))
bool claimExists = currentSlot < startSlotOfPendingEpoch + TimeLib.toSlots(Epoch.wrap(2))
&& rollupStore.proofClaim.epochToProve == oldestPendingEpoch
&& rollupStore.proofClaim.proposerClaimant != address(0);

Expand Down
42 changes: 25 additions & 17 deletions l1-contracts/src/core/ValidatorSelection.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {Signature} from "@aztec/core/libraries/crypto/SignatureLib.sol";
import {DataStructures} from "@aztec/core/libraries/DataStructures.sol";
import {Errors} from "@aztec/core/libraries/Errors.sol";
import {
Timestamp, Slot, Epoch, SlotLib, EpochLib, TimeFns
} from "@aztec/core/libraries/TimeMath.sol";
Timestamp, Slot, Epoch, SlotLib, EpochLib, TimeLib
} from "@aztec/core/libraries/TimeLib.sol";
import {ValidatorSelectionLib} from
"@aztec/core/libraries/ValidatorSelectionLib/ValidatorSelectionLib.sol";
import {Staking} from "@aztec/core/staking/Staking.sol";
Expand All @@ -27,19 +27,19 @@ import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol";
* It is a reference implementation, it is not optimized for gas.
*
*/
contract ValidatorSelection is Staking, TimeFns, IValidatorSelection {
contract ValidatorSelection is Staking, IValidatorSelection {
using EnumerableSet for EnumerableSet.AddressSet;

using SlotLib for Slot;
using EpochLib for Epoch;
using TimeLib for Timestamp;
using TimeLib for Slot;
using TimeLib for Epoch;

// The target number of validators in a committee
// @todo #8021
uint256 public immutable TARGET_COMMITTEE_SIZE;

// The time that the contract was deployed
Timestamp public immutable GENESIS_TIME;

ValidatorSelectionStorage private validatorSelectionStore;

constructor(
Expand All @@ -50,14 +50,22 @@ contract ValidatorSelection is Staking, TimeFns, IValidatorSelection {
uint256 _slotDuration,
uint256 _epochDuration,
uint256 _targetCommitteeSize
)
Staking(_stakingAsset, _minimumStake, _slashingQuorum, _roundSize)
TimeFns(_slotDuration, _epochDuration)
{
GENESIS_TIME = Timestamp.wrap(block.timestamp);
SLOT_DURATION = _slotDuration;
EPOCH_DURATION = _epochDuration;
) Staking(_stakingAsset, _minimumStake, _slashingQuorum, _roundSize) {
TARGET_COMMITTEE_SIZE = _targetCommitteeSize;

TimeLib.initialize(block.timestamp, _slotDuration, _epochDuration);
}

function getGenesisTime() external view override(IValidatorSelection) returns (Timestamp) {
return Timestamp.wrap(TimeLib.getStorage().genesisTime);
}

function getSlotDuration() external view override(IValidatorSelection) returns (uint256) {
return TimeLib.getStorage().slotDuration;
}

function getEpochDuration() external view override(IValidatorSelection) returns (uint256) {
return TimeLib.getStorage().epochDuration;
}

/**
Expand Down Expand Up @@ -224,7 +232,7 @@ contract ValidatorSelection is Staking, TimeFns, IValidatorSelection {
override(IValidatorSelection)
returns (Timestamp)
{
return GENESIS_TIME + toTimestamp(_slotNumber);
return _slotNumber.toTimestamp();
}

/**
Expand Down Expand Up @@ -275,7 +283,7 @@ contract ValidatorSelection is Staking, TimeFns, IValidatorSelection {
* @return The computed epoch
*/
function getEpochAt(Timestamp _ts) public view override(IValidatorSelection) returns (Epoch) {
return _ts < GENESIS_TIME ? Epoch.wrap(0) : epochFromTimestamp(_ts - GENESIS_TIME);
return _ts.epochFromTimestamp();
}

/**
Expand All @@ -286,7 +294,7 @@ contract ValidatorSelection is Staking, TimeFns, IValidatorSelection {
* @return The computed slot
*/
function getSlotAt(Timestamp _ts) public view override(IValidatorSelection) returns (Slot) {
return _ts < GENESIS_TIME ? Slot.wrap(0) : slotFromTimestamp(_ts - GENESIS_TIME);
return _ts.slotFromTimestamp();
}

/**
Expand All @@ -302,7 +310,7 @@ contract ValidatorSelection is Staking, TimeFns, IValidatorSelection {
override(IValidatorSelection)
returns (Epoch)
{
return Epoch.wrap(_slotNumber.unwrap() / EPOCH_DURATION);
return _slotNumber.epochFromSlot();
}

// Can be used to add validators without setting up the epoch, useful for the initial set.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {Timestamp} from "@aztec/core/libraries/TimeMath.sol";
import {Timestamp} from "@aztec/core/libraries/TimeLib.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";

interface IProofCommitmentEscrow {
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
FeeHeader, L1FeeData, ManaBaseFeeComponents
} from "@aztec/core/libraries/RollupLibs/FeeMath.sol";
import {ProposeArgs} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol";
import {Timestamp, Slot, Epoch} from "@aztec/core/libraries/TimeMath.sol";
import {Timestamp, Slot, Epoch} from "@aztec/core/libraries/TimeLib.sol";

struct SubmitEpochRootProofArgs {
uint256 epochSize;
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/interfaces/IStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {Timestamp} from "@aztec/core/libraries/TimeMath.sol";
import {Timestamp} from "@aztec/core/libraries/TimeLib.sol";
import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol";

// None -> Does not exist in our setup
Expand Down
6 changes: 5 additions & 1 deletion l1-contracts/src/core/interfaces/IValidatorSelection.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {Timestamp, Slot, Epoch} from "@aztec/core/libraries/TimeMath.sol";
import {Timestamp, Slot, Epoch} from "@aztec/core/libraries/TimeLib.sol";

/**
* @notice The data structure for an epoch
Expand Down Expand Up @@ -49,4 +49,8 @@ interface IValidatorSelection {
function getEpochAt(Timestamp _ts) external view returns (Epoch);
function getSlotAt(Timestamp _ts) external view returns (Slot);
function getEpochAtSlot(Slot _slotNumber) external view returns (Epoch);

function getGenesisTime() external view returns (Timestamp);
function getSlotDuration() external view returns (uint256);
function getEpochDuration() external view returns (uint256);
}
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/DataStructures.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {Epoch} from "@aztec/core/libraries/TimeMath.sol";
import {Epoch} from "@aztec/core/libraries/TimeLib.sol";

/**
* @title Data Structures Library
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {Timestamp, Slot, Epoch} from "@aztec/core/libraries/TimeMath.sol";
import {Timestamp, Slot, Epoch} from "@aztec/core/libraries/TimeLib.sol";

/**
* @title Errors Library
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from "@aztec/core/interfaces/IRollup.sol";
import {Constants} from "@aztec/core/libraries/ConstantsGen.sol";
import {Errors} from "@aztec/core/libraries/Errors.sol";
import {Epoch} from "@aztec/core/libraries/TimeMath.sol";
import {Epoch} from "@aztec/core/libraries/TimeLib.sol";
import {IRewardDistributor} from "@aztec/governance/interfaces/IRewardDistributor.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
pragma solidity >=0.8.27;

import {Signature} from "@aztec/core/libraries/crypto/SignatureLib.sol";
import {Slot, Epoch} from "@aztec/core/libraries/TimeMath.sol";
import {Slot, Epoch} from "@aztec/core/libraries/TimeLib.sol";

/**
* @notice Struct encompassing an epoch proof quote
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {BlockLog, RollupStore, SubmitEpochRootProofArgs} from "@aztec/core/inter
import {IRewardDistributor} from "@aztec/governance/interfaces/IRewardDistributor.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {DataStructures} from "./../DataStructures.sol";
import {Slot, Epoch} from "./../TimeMath.sol";
import {Slot, Epoch} from "./../TimeLib.sol";
import {BlobLib} from "./BlobLib.sol";
import {
EpochProofLib,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {BlockLog} from "@aztec/core/interfaces/IRollup.sol";
import {SignatureLib} from "@aztec/core/libraries/crypto/SignatureLib.sol";
import {DataStructures} from "./../DataStructures.sol";
import {Errors} from "./../Errors.sol";
import {Timestamp, Slot, Epoch} from "./../TimeMath.sol";
import {Timestamp, Slot, Epoch} from "./../TimeLib.sol";
import {SignedEpochProofQuote} from "./EpochProofQuoteLib.sol";
import {Header} from "./HeaderLib.sol";

Expand Down
63 changes: 63 additions & 0 deletions l1-contracts/src/core/libraries/TimeLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

// solhint-disable-next-line no-unused-import
import {Timestamp, Slot, Epoch, SlotLib, EpochLib} from "@aztec/core/libraries/TimeMath.sol";

struct TimeStorage {
uint256 genesisTime;
uint256 slotDuration; // Number of seconds in a slot
uint256 epochDuration; // Number of slots in an epoch
}

library TimeLib {
bytes32 private constant TIME_STORAGE_POSITION = keccak256("aztec.time.storage");

function initialize(uint256 _genesisTime, uint256 _slotDuration, uint256 _epochDuration) internal {
TimeStorage storage store = getStorage();
store.genesisTime = _genesisTime;
store.slotDuration = _slotDuration;
store.epochDuration = _epochDuration;
}

function toTimestamp(Slot _a) internal view returns (Timestamp) {
TimeStorage storage store = getStorage();
return Timestamp.wrap(store.genesisTime) + Timestamp.wrap(Slot.unwrap(_a) * store.slotDuration);
}

function slotFromTimestamp(Timestamp _a) internal view returns (Slot) {
TimeStorage storage store = getStorage();
return Slot.wrap((Timestamp.unwrap(_a) - store.genesisTime) / store.slotDuration);
}

function positionInEpoch(Slot _a) internal view returns (uint256) {
return Slot.unwrap(_a) % getStorage().epochDuration;
}

function toSlots(Epoch _a) internal view returns (Slot) {
return Slot.wrap(Epoch.unwrap(_a) * getStorage().epochDuration);
}

function toTimestamp(Epoch _a) internal view returns (Timestamp) {
return toTimestamp(toSlots(_a));
}

function epochFromTimestamp(Timestamp _a) internal view returns (Epoch) {
TimeStorage storage store = getStorage();
return Epoch.wrap(
(Timestamp.unwrap(_a) - store.genesisTime) / (store.epochDuration * store.slotDuration)
);
}

function epochFromSlot(Slot _a) internal view returns (Epoch) {
return Epoch.wrap(Slot.unwrap(_a) / getStorage().epochDuration);
}

function getStorage() internal pure returns (TimeStorage storage storageStruct) {
bytes32 position = TIME_STORAGE_POSITION;
assembly {
storageStruct.slot := position
}
}
}
47 changes: 0 additions & 47 deletions l1-contracts/src/core/libraries/TimeMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,6 @@ type Slot is uint256;

type Epoch is uint256;

abstract contract TimeFns {
// @note @LHerskind The multiple cause pain and suffering in the E2E tests as we introduce
// a timeliness requirement into the publication that did not exists before,
// and at the same time have a setup that will impact the time at every tx
// because of auto-mine. By using just 1, we can make our test work
// but anything using an actual working chain would eat dung as simulating
// transactions is slower than an actual ethereum slot.
//
// The value should be a higher multiple for any actual chain
// @todo #8019
uint256 public immutable SLOT_DURATION;

// The duration of an epoch in slots
// @todo @LHerskind - This value should be updated when we are not blind.
// @todo #8020
uint256 public immutable EPOCH_DURATION;

constructor(uint256 _slotDuration, uint256 _epochDuration) {
SLOT_DURATION = _slotDuration;
EPOCH_DURATION = _epochDuration;
}

function toTimestamp(Slot _a) internal view returns (Timestamp) {
return Timestamp.wrap(Slot.unwrap(_a) * SLOT_DURATION);
}

function slotFromTimestamp(Timestamp _a) internal view returns (Slot) {
return Slot.wrap(Timestamp.unwrap(_a) / SLOT_DURATION);
}

function positionInEpoch(Slot _a) internal view returns (uint256) {
return Slot.unwrap(_a) % EPOCH_DURATION;
}

function toSlots(Epoch _a) internal view returns (Slot) {
return Slot.wrap(Epoch.unwrap(_a) * EPOCH_DURATION);
}

function toTimestamp(Epoch _a) internal view returns (Timestamp) {
return toTimestamp(toSlots(_a));
}

function epochFromTimestamp(Timestamp _a) internal view returns (Epoch) {
return Epoch.wrap(Timestamp.unwrap(_a) / (EPOCH_DURATION * SLOT_DURATION));
}
}

library SlotLib {
function unwrap(Slot _a) internal pure returns (uint256) {
return Slot.unwrap(_a);
Expand Down
Loading

0 comments on commit 3b463f9

Please sign in to comment.