diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index dc3c16565bca..d92c96d6f782 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -28,6 +28,7 @@ import {Signature} from "@aztec/shared/libraries/SignatureLib.sol"; import {ChainTipsLib, CompressedChainTips} from "./libraries/compressed-data/Tips.sol"; import {ProposeLib, ValidateHeaderArgs} from "./libraries/rollup/ProposeLib.sol"; import {RewardLib, RewardConfig} from "./libraries/rollup/RewardLib.sol"; +import {DepositArgs} from "./libraries/StakingQueue.sol"; import { RollupCore, GenesisState, @@ -531,6 +532,10 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore { return StakingLib.getStorage().isBootstrapped; } + function getEntryQueueAt(uint256 _index) external view override(IStaking) returns (DepositArgs memory) { + return StakingLib.getEntryQueueAt(_index); + } + function getBurnAddress() external pure override(IRollup) returns (address) { return RewardLib.BURN_ADDRESS; } diff --git a/l1-contracts/src/core/interfaces/IStaking.sol b/l1-contracts/src/core/interfaces/IStaking.sol index 006458e82444..ee12d0b87400 100644 --- a/l1-contracts/src/core/interfaces/IStaking.sol +++ b/l1-contracts/src/core/interfaces/IStaking.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.27; import {StakingQueueConfig} from "@aztec/core/libraries/compressed-data/StakingQueueConfig.sol"; import {Exit, Status, AttesterView} from "@aztec/core/libraries/rollup/StakingLib.sol"; +import {DepositArgs} from "@aztec/core/libraries/StakingQueue.sol"; import {AttesterConfig, GSE} from "@aztec/governance/GSE.sol"; import {G1Point, G2Point} from "@aztec/shared/libraries/BN254Lib.sol"; import {Timestamp, Epoch} from "@aztec/shared/libraries/TimeMath.sol"; @@ -72,6 +73,7 @@ interface IStaking is IStakingCore { function getStatus(address _attester) external view returns (Status); function getNextFlushableEpoch() external view returns (Epoch); function getEntryQueueLength() external view returns (uint256); + function getEntryQueueAt(uint256 _index) external view returns (DepositArgs memory); function getAvailableValidatorFlushes() external view returns (uint256); function getIsBootstrapped() external view returns (bool); } diff --git a/l1-contracts/src/core/libraries/StakingQueue.sol b/l1-contracts/src/core/libraries/StakingQueue.sol index 314289b8f436..9c805986614f 100644 --- a/l1-contracts/src/core/libraries/StakingQueue.sol +++ b/l1-contracts/src/core/libraries/StakingQueue.sol @@ -72,11 +72,7 @@ library StakingQueueLib { len = self.last - self.first; } - function getFirst(StakingQueue storage self) internal view returns (uint256) { - return self.first; - } - - function getLast(StakingQueue storage self) internal view returns (uint256) { - return self.last; + function at(StakingQueue storage self, uint256 index) internal view returns (DepositArgs memory validator) { + validator = self.validators[self.first + index]; } } diff --git a/l1-contracts/src/core/libraries/compressed-data/fees/FeeConfig.sol b/l1-contracts/src/core/libraries/compressed-data/fees/FeeConfig.sol index d597f987067e..be3d8041e386 100644 --- a/l1-contracts/src/core/libraries/compressed-data/fees/FeeConfig.sol +++ b/l1-contracts/src/core/libraries/compressed-data/fees/FeeConfig.sol @@ -58,7 +58,7 @@ library FeeConfigLib { } function getCongestionUpdateFraction(CompressedFeeConfig _compressedFeeConfig) internal pure returns (uint256) { - return (CompressedFeeConfig.unwrap(_compressedFeeConfig) >> 64) & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + return (CompressedFeeConfig.unwrap(_compressedFeeConfig) >> 64) & MASK_128_BITS; } function getProvingCostPerMana(CompressedFeeConfig _compressedFeeConfig) internal pure returns (EthValue) { diff --git a/l1-contracts/src/core/libraries/compressed-data/fees/FeeStructs.sol b/l1-contracts/src/core/libraries/compressed-data/fees/FeeStructs.sol index 4f516929a978..910de7c7e514 100644 --- a/l1-contracts/src/core/libraries/compressed-data/fees/FeeStructs.sol +++ b/l1-contracts/src/core/libraries/compressed-data/fees/FeeStructs.sol @@ -73,30 +73,30 @@ library FeeStructsLib { library FeeHeaderLib { using SafeCast for uint256; + uint256 internal constant MASK_32_BITS = 0xFFFFFFFF; + uint256 internal constant MASK_48_BITS = 0xFFFFFFFFFFFF; + uint256 internal constant MASK_63_BITS = 0x7FFFFFFFFFFFFFFF; + uint256 internal constant MASK_64_BITS = 0xFFFFFFFFFFFFFFFF; + function getManaUsed(CompressedFeeHeader _compressedFeeHeader) internal pure returns (uint256) { - // Reads the bits 224-256 - return CompressedFeeHeader.unwrap(_compressedFeeHeader) & 0xFFFFFFFF; + return CompressedFeeHeader.unwrap(_compressedFeeHeader) & MASK_32_BITS; } function getExcessMana(CompressedFeeHeader _compressedFeeHeader) internal pure returns (uint256) { - // Reads the bits 176-223 - return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 32) & 0xFFFFFFFFFFFF; + return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 32) & MASK_48_BITS; } function getFeeAssetPriceNumerator(CompressedFeeHeader _compressedFeeHeader) internal pure returns (uint256) { - // Reads the bits 128-175 - return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 80) & 0xFFFFFFFFFFFF; + return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 80) & MASK_48_BITS; } function getCongestionCost(CompressedFeeHeader _compressedFeeHeader) internal pure returns (uint256) { - // Reads the bits 64-127 - return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 128) & 0xFFFFFFFFFFFFFFFF; + return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 128) & MASK_64_BITS; } function getProverCost(CompressedFeeHeader _compressedFeeHeader) internal pure returns (uint256) { // The prover cost is only 63 bits so use mask to remove first bit - // Reads the bits 1-63 - return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 192) & 0x7FFFFFFFFFFFFFFF; + return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 192) & MASK_63_BITS; } function compress(FeeHeader memory _feeHeader) internal pure returns (CompressedFeeHeader) { @@ -107,7 +107,7 @@ library FeeHeaderLib { value |= uint256(_feeHeader.congestionCost.toUint64()) << 128; uint256 proverCost = uint256(_feeHeader.proverCost.toUint64()); - require(proverCost == proverCost & 0x7FFFFFFFFFFFFFFF); + require(proverCost == proverCost & MASK_63_BITS); value |= proverCost << 192; // Preheat @@ -119,15 +119,15 @@ library FeeHeaderLib { function decompress(CompressedFeeHeader _compressedFeeHeader) internal pure returns (FeeHeader memory) { uint256 value = CompressedFeeHeader.unwrap(_compressedFeeHeader); - uint256 manaUsed = value & 0xFFFFFFFF; + uint256 manaUsed = value & MASK_32_BITS; value >>= 32; - uint256 excessMana = value & 0xFFFFFFFFFFFF; + uint256 excessMana = value & MASK_48_BITS; value >>= 48; - uint256 feeAssetPriceNumerator = value & 0xFFFFFFFFFFFF; + uint256 feeAssetPriceNumerator = value & MASK_48_BITS; value >>= 48; - uint256 congestionCost = value & 0xFFFFFFFFFFFFFFFF; + uint256 congestionCost = value & MASK_64_BITS; value >>= 64; - uint256 proverCost = value & 0x7FFFFFFFFFFFFFFF; + uint256 proverCost = value & MASK_63_BITS; return FeeHeader({ manaUsed: uint256(manaUsed), @@ -137,10 +137,4 @@ library FeeHeaderLib { proverCost: uint256(proverCost) }); } - - function preheat(CompressedFeeHeader _compressedFeeHeader) internal pure returns (CompressedFeeHeader) { - uint256 value = CompressedFeeHeader.unwrap(_compressedFeeHeader); - value |= 1 << 255; - return CompressedFeeHeader.wrap(value); - } } diff --git a/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol b/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol index 0d5b39152eea..6a82f7da9524 100644 --- a/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol +++ b/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol @@ -38,9 +38,9 @@ import {SafeCast} from "@oz/utils/math/SafeCast.sol"; * status. * * Attestation Verification: - * Before accepting an epoch proof, this library verifies the attestations for the last block in the epoch. - * This ensures that the committee has properly validated the final state of the epoch. Note that this is - * equivalent to verifying the attestations for every block in the epoch, since the committee should not attest + * Before accepting an epoch proof, this library verifies the attestations for the end block of the proof. + * This ensures that the committee has properly validated the final state of the proof. Note that this is + * equivalent to verifying the attestations for every prior block, since the committee should not attest * to a block unless its ancestors are also valid and have been attested to. This step checks that the committee * have agreed on the same output state of the proven range. For honest nodes, this is done by re-executing the * transactions in the proven range and matching the state root, effectively acting as training wheels for the diff --git a/l1-contracts/src/core/libraries/rollup/RewardLib.sol b/l1-contracts/src/core/libraries/rollup/RewardLib.sol index 946ccce24ad4..bf3e88655dca 100644 --- a/l1-contracts/src/core/libraries/rollup/RewardLib.sol +++ b/l1-contracts/src/core/libraries/rollup/RewardLib.sol @@ -177,10 +177,10 @@ library RewardLib { } } - uint256 sequencerShare = BpsLib.mul(blockRewardsAvailable, rewardStorage.config.sequencerBps); - v.sequencerBlockReward = sequencerShare / added; + uint256 sequenceBlockRewards = BpsLib.mul(blockRewardsAvailable, rewardStorage.config.sequencerBps); + v.sequencerBlockReward = sequenceBlockRewards / added; - $er.rewards += (blockRewardsAvailable - sequencerShare).toUint128(); + $er.rewards += (blockRewardsAvailable - sequenceBlockRewards).toUint128(); } bool isTxsEnabled = FeeLib.isTxsEnabled(); diff --git a/l1-contracts/src/core/libraries/rollup/StakingLib.sol b/l1-contracts/src/core/libraries/rollup/StakingLib.sol index d409ca5b3d73..ff68ded20acf 100644 --- a/l1-contracts/src/core/libraries/rollup/StakingLib.sol +++ b/l1-contracts/src/core/libraries/rollup/StakingLib.sol @@ -499,6 +499,10 @@ library StakingLib { return getStorage().gse.getAttesterFromIndexAtTime(address(this), _index, Timestamp.wrap(block.timestamp)); } + function getEntryQueueAt(uint256 _index) internal view returns (DepositArgs memory) { + return getStorage().entryQueue.at(_index); + } + function getAttesterFromIndexAtTime(uint256 _index, Timestamp _timestamp) internal view returns (address) { return getStorage().gse.getAttesterFromIndexAtTime(address(this), _index, _timestamp); } diff --git a/l1-contracts/src/core/libraries/rollup/ValidatorSelectionLib.sol b/l1-contracts/src/core/libraries/rollup/ValidatorSelectionLib.sol index 3819c86a45d2..5ebecef110f7 100644 --- a/l1-contracts/src/core/libraries/rollup/ValidatorSelectionLib.sol +++ b/l1-contracts/src/core/libraries/rollup/ValidatorSelectionLib.sol @@ -36,7 +36,7 @@ import {TransientSlot} from "@oz/utils/TransientSlot.sol"; * * 1. Committee Selection: * - At the start of each epoch, a committee is sampled from the active validator set - * - Committee size is configurable (targetCommitteeSize) but validators must meet this minimum + * - Committee size is configurable at deployment (targetCommitteeSize), and must be met * - Selection uses cryptographic randomness (prevrandao + epoch) * - Committee remains stable throughout the entire epoch for consistency * - Committee commitment is stored onchain and validated against reconstructed committees diff --git a/l1-contracts/test/staking/flushEntryQueue.t.sol b/l1-contracts/test/staking/flushEntryQueue.t.sol index 20aeadaf10e5..c137e1bfb8a6 100644 --- a/l1-contracts/test/staking/flushEntryQueue.t.sol +++ b/l1-contracts/test/staking/flushEntryQueue.t.sol @@ -12,7 +12,7 @@ import {Epoch, Timestamp} from "@aztec/shared/libraries/TimeMath.sol"; import {Status, AttesterView, IStakingCore} from "@aztec/core/interfaces/IStaking.sol"; import {Math} from "@oz/utils/math/Math.sol"; import {GSE, IGSECore} from "@aztec/governance/GSE.sol"; -import {StakingQueueLib} from "@aztec/core/libraries/StakingQueue.sol"; +import {StakingQueueLib, DepositArgs} from "@aztec/core/libraries/StakingQueue.sol"; import {StakingQueueConfig, StakingQueueConfigLib} from "@aztec/core/libraries/compressed-data/StakingQueueConfig.sol"; import {Rollup} from "@aztec/core/Rollup.sol"; import {BN254Lib, G1Point, G2Point} from "@aztec/shared/libraries/BN254Lib.sol"; @@ -204,6 +204,14 @@ contract FlushEntryQueueTest is StakingBase { }); assertEq(stakingAsset.balanceOf(address(staking)), balance + ACTIVATION_THRESHOLD, "invalid balance"); + + DepositArgs memory validator = staking.getEntryQueueAt(staking.getEntryQueueLength() - 1); + assertEq(validator.attester, _attester, "invalid attester"); + assertEq(validator.withdrawer, _withdrawer, "invalid withdrawer"); + assertTrue(BN254Lib.isZero(validator.publicKeyInG1), "invalid public key in G1"); + assertTrue(BN254Lib.isZero(validator.publicKeyInG2), "invalid public key in G2"); + assertTrue(BN254Lib.isZero(validator.proofOfPossession), "invalid proof of possession"); + assertEq(validator.moveWithLatestRollup, _moveWithLatestRollup, "invalid move with latest rollup"); } function _help_flushEntryQueue(uint256 _numValidators, uint256 _expectedFlushSize) internal {