Skip to content

Commit cbaa077

Browse files
authored
feat: Delayed signature verification during block proposals (#15813)
Implements AztecProtocol/engineering-designs#69 Median `propose` gas cost is `321250` according to `happy.t.sol#test_100_val`
1 parent 0bae325 commit cbaa077

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1503
-603
lines changed

l1-contracts/src/core/Rollup.sol

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,25 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
102102
* @param _blobsHash - The blobs hash for this block
103103
* @param _flags - The flags to validate
104104
*/
105-
function validateHeader(
105+
function validateHeaderWithAttestations(
106106
ProposedHeader calldata _header,
107107
CommitteeAttestations memory _attestations,
108+
address[] calldata _signers,
108109
bytes32 _digest,
109110
bytes32 _blobsHash,
110111
BlockHeaderValidationFlags memory _flags
111112
) external override(IRollup) {
112113
Timestamp currentTime = Timestamp.wrap(block.timestamp);
113-
ExtRollupLib.validateHeader(
114+
ExtRollupLib.validateHeaderWithAttestations(
114115
ValidateHeaderArgs({
115116
header: _header,
116-
attestations: _attestations,
117117
digest: _digest,
118118
manaBaseFee: getManaBaseFeeAt(currentTime, true),
119119
blobsHashesCommitment: _blobsHash,
120120
flags: _flags
121-
})
121+
}),
122+
_attestations,
123+
_signers
122124
);
123125
}
124126

@@ -181,34 +183,17 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
181183
*
182184
* @param _ts - The timestamp to check
183185
* @param _archive - The archive to check (should be the latest archive)
186+
* @param _who - The address to check
184187
*
185188
* @return uint256 - The slot at the given timestamp
186189
* @return uint256 - The block number at the given timestamp
187190
*/
188-
function canProposeAtTime(Timestamp _ts, bytes32 _archive)
191+
function canProposeAtTime(Timestamp _ts, bytes32 _archive, address _who)
189192
external
190193
override(IRollup)
191194
returns (Slot, uint256)
192195
{
193-
Slot slot = _ts.slotFromTimestamp();
194-
RollupStore storage rollupStore = STFLib.getStorage();
195-
196-
uint256 pendingBlockNumber = STFLib.getEffectivePendingBlockNumber(_ts);
197-
198-
Slot lastSlot = STFLib.getSlotNumber(pendingBlockNumber);
199-
200-
require(slot > lastSlot, Errors.Rollup__SlotAlreadyInChain(lastSlot, slot));
201-
202-
// Make sure that the proposer is up to date and on the right chain (ie no reorgs)
203-
bytes32 tipArchive = rollupStore.archives[pendingBlockNumber];
204-
require(tipArchive == _archive, Errors.Rollup__InvalidArchive(tipArchive, _archive));
205-
206-
address proposer = ExtRollupLib2.getProposerAt(slot);
207-
require(
208-
proposer == msg.sender, Errors.ValidatorSelection__InvalidProposer(proposer, msg.sender)
209-
);
210-
211-
return (slot, pendingBlockNumber + 1);
196+
return ExtRollupLib2.canProposeAtTime(_ts, _archive, _who);
212197
}
213198

214199
function getTargetCommitteeSize() external view override(IValidatorSelection) returns (uint256) {
@@ -365,6 +350,8 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
365350
archive: rollupStore.archives[_blockNumber],
366351
headerHash: tempBlockLog.headerHash,
367352
blobCommitmentsHash: tempBlockLog.blobCommitmentsHash,
353+
attestationsHash: tempBlockLog.attestationsHash,
354+
payloadDigest: tempBlockLog.payloadDigest,
368355
slotNumber: tempBlockLog.slotNumber,
369356
feeHeader: tempBlockLog.feeHeader
370357
});

l1-contracts/src/core/RollupCore.sol

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {CommitteeAttestations} from "@aztec/shared/libraries/SignatureLib.sol";
2020
import {Errors} from "@aztec/core/libraries/Errors.sol";
2121
import {ExtRollupLib} from "@aztec/core/libraries/rollup/ExtRollupLib.sol";
2222
import {ExtRollupLib2} from "@aztec/core/libraries/rollup/ExtRollupLib2.sol";
23+
import {ExtRollupLib3} from "@aztec/core/libraries/rollup/ExtRollupLib3.sol";
2324
import {EthValue, FeeLib} from "@aztec/core/libraries/rollup/FeeLib.sol";
2425
import {ProposeArgs} from "@aztec/core/libraries/rollup/ProposeLib.sol";
2526
import {STFLib, GenesisState} from "@aztec/core/libraries/rollup/STFLib.sol";
@@ -86,7 +87,7 @@ contract RollupCore is
8687
);
8788

8889
Timestamp exitDelay = Timestamp.wrap(_config.exitDelaySeconds);
89-
ISlasher slasher = ExtRollupLib2.deploySlasher(
90+
ISlasher slasher = ExtRollupLib3.deploySlasher(
9091
_config.slashingQuorum,
9192
_config.slashingRoundSize,
9293
_config.slashingLifetimeInRounds,
@@ -101,7 +102,7 @@ contract RollupCore is
101102

102103
// If no booster specifically provided deploy one.
103104
if (address(_config.rewardConfig.booster) == address(0)) {
104-
_config.rewardConfig.booster = ExtRollupLib2.deployRewardBooster(_config.rewardBoostConfig);
105+
_config.rewardConfig.booster = ExtRollupLib3.deployRewardBooster(_config.rewardBoostConfig);
105106
}
106107

107108
RewardLib.setConfig(_config.rewardConfig);
@@ -223,16 +224,15 @@ contract RollupCore is
223224
}
224225

225226
function finaliseWithdraw(address _attester) external override(IStakingCore) {
226-
StakingLib.finaliseWithdraw(_attester);
227+
ExtRollupLib2.finaliseWithdraw(_attester);
227228
}
228229

229230
function slash(address _attester, uint256 _amount) external override(IStakingCore) returns (bool) {
230-
return StakingLib.trySlash(_attester, _amount);
231+
return ExtRollupLib2.slash(_attester, _amount);
231232
}
232233

233234
function prune() external override(IRollupCore) {
234-
require(STFLib.canPruneAtTime(Timestamp.wrap(block.timestamp)), Errors.Rollup__NothingToPrune());
235-
STFLib.prune();
235+
ExtRollupLib.prune();
236236
}
237237

238238
function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args)
@@ -245,9 +245,27 @@ contract RollupCore is
245245
function propose(
246246
ProposeArgs calldata _args,
247247
CommitteeAttestations memory _attestations,
248+
address[] calldata _signers,
248249
bytes calldata _blobInput
249250
) external override(IRollupCore) {
250-
ExtRollupLib.propose(_args, _attestations, _blobInput, checkBlob);
251+
ExtRollupLib.propose(_args, _attestations, _signers, _blobInput, checkBlob);
252+
}
253+
254+
function invalidateBadAttestation(
255+
uint256 _blockNumber,
256+
CommitteeAttestations memory _attestations,
257+
address[] memory _committee,
258+
uint256 _invalidIndex
259+
) external override(IRollupCore) {
260+
ExtRollupLib2.invalidateBadAttestation(_blockNumber, _attestations, _committee, _invalidIndex);
261+
}
262+
263+
function invalidateInsufficientAttestations(
264+
uint256 _blockNumber,
265+
CommitteeAttestations memory _attestations,
266+
address[] memory _committee
267+
) external override(IRollupCore) {
268+
ExtRollupLib2.invalidateInsufficientAttestations(_blockNumber, _attestations, _committee);
251269
}
252270

253271
function setupEpoch() public override(IValidatorSelectionCore) {

l1-contracts/src/core/interfaces/IRollup.sol

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,17 @@ struct SubmitEpochRootProofArgs {
3636
uint256 end; // inclusive
3737
PublicInputArgs args;
3838
bytes32[] fees;
39+
CommitteeAttestations attestations; // attestations for the last block in epoch
3940
bytes blobInputs;
4041
bytes proof;
4142
}
4243

4344
/**
4445
* @notice Struct for storing flags for block header validation
4546
* @param ignoreDA - True will ignore DA check, otherwise checks
46-
* @param ignoreSignature - True will ignore the signatures, otherwise checks
4747
*/
4848
struct BlockHeaderValidationFlags {
4949
bool ignoreDA;
50-
bool ignoreSignatures;
5150
}
5251

5352
struct GenesisState {
@@ -98,6 +97,7 @@ interface IRollupCore {
9897
uint256 indexed blockNumber, bytes32 indexed archive, bytes32[] versionedBlobHashes
9998
);
10099
event L2ProofVerified(uint256 indexed blockNumber, address indexed proverId);
100+
event BlockInvalidated(uint256 indexed blockNumber);
101101
event RewardConfigUpdated(RewardConfig rewardConfig);
102102
event ManaTargetUpdated(uint256 indexed manaTarget);
103103
event PrunedPending(uint256 provenBlockNumber, uint256 pendingBlockNumber);
@@ -119,11 +119,25 @@ interface IRollupCore {
119119
function propose(
120120
ProposeArgs calldata _args,
121121
CommitteeAttestations memory _attestations,
122+
address[] memory _signers,
122123
bytes calldata _blobInput
123124
) external;
124125

125126
function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external;
126127

128+
function invalidateBadAttestation(
129+
uint256 _blockNumber,
130+
CommitteeAttestations memory _attestations,
131+
address[] memory _committee,
132+
uint256 _invalidIndex
133+
) external;
134+
135+
function invalidateInsufficientAttestations(
136+
uint256 _blockNumber,
137+
CommitteeAttestations memory _attestations,
138+
address[] memory _committee
139+
) external;
140+
127141
function setRewardConfig(RewardConfig memory _config) external;
128142
function updateManaTarget(uint256 _manaTarget) external;
129143

@@ -132,15 +146,18 @@ interface IRollupCore {
132146
}
133147

134148
interface IRollup is IRollupCore, IHaveVersion {
135-
function validateHeader(
149+
function validateHeaderWithAttestations(
136150
ProposedHeader calldata _header,
137151
CommitteeAttestations memory _attestations,
152+
address[] memory _signers,
138153
bytes32 _digest,
139154
bytes32 _blobsHash,
140155
BlockHeaderValidationFlags memory _flags
141156
) external;
142157

143-
function canProposeAtTime(Timestamp _ts, bytes32 _archive) external returns (Slot, uint256);
158+
function canProposeAtTime(Timestamp _ts, bytes32 _archive, address _who)
159+
external
160+
returns (Slot, uint256);
144161

145162
function getTips() external view returns (ChainTips memory);
146163

l1-contracts/src/core/libraries/Errors.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ library Errors {
5656
error Rollup__InvalidProof(); // 0xa5b2ba17
5757
error Rollup__InvalidProposedArchive(bytes32 expected, bytes32 actual); // 0x32532e73
5858
error Rollup__InvalidTimestamp(Timestamp expected, Timestamp actual); // 0x3132e895
59+
error Rollup__InvalidAttestations();
60+
error Rollup__AttestationsAreValid();
61+
error Rollup__BlockAlreadyProven();
62+
error Rollup__BlockNotInPendingChain();
5963
error Rollup__InvalidBlobHash(bytes32 expected, bytes32 actual); // 0x13031e6a
6064
error Rollup__InvalidBlobProof(bytes32 blobHash); // 0x5ca17bef
6165
error Rollup__NoEpochToProve(); // 0xcbaa3951
@@ -98,10 +102,12 @@ library Errors {
98102
// Sequencer Selection (ValidatorSelection)
99103
error ValidatorSelection__EpochNotSetup(); // 0x10816cae
100104
error ValidatorSelection__InvalidProposer(address expected, address actual); // 0xa8843a68
105+
error ValidatorSelection__MissingProposerSignature(address proposer, uint256 index);
101106
error ValidatorSelection__InvalidDeposit(address attester, address proposer); // 0x533169bd
102107
error ValidatorSelection__InsufficientAttestations(uint256 minimumNeeded, uint256 provided); // 0xaf47297f
103108
error ValidatorSelection__InvalidCommitteeCommitment(bytes32 reconstructed, bytes32 expected); // 0xca8d5954
104109
error ValidatorSelection__InsufficientCommitteeSize(uint256 actual, uint256 expected); // 0x98673597
110+
error ValidatorSelection__ProposerIndexTooLarge(uint256 index);
105111

106112
// Staking
107113
error Staking__AlreadyQueued(address _attester);

l1-contracts/src/core/libraries/compressed-data/BlockLog.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,34 @@ import {Slot} from "@aztec/shared/libraries/TimeMath.sol";
1515
* @param archive - Archive tree root of the block
1616
* @param headerHash - Hash of the proposed block header
1717
* @param blobCommitmentsHash - H(...H(H(commitment_0), commitment_1).... commitment_n) - used to validate we are using the same blob commitments on L1 and in the rollup circuit
18+
* @param attestationsHash - Hash of the attestations for this block
19+
* @param payloadDigest - Digest of the proposal payload that was attested to
1820
* @param slotNumber - This block's slot
1921
*/
2022
struct BlockLog {
2123
bytes32 archive;
2224
bytes32 headerHash;
2325
bytes32 blobCommitmentsHash;
26+
bytes32 attestationsHash;
27+
bytes32 payloadDigest;
2428
Slot slotNumber;
2529
FeeHeader feeHeader;
2630
}
2731

2832
struct TempBlockLog {
2933
bytes32 headerHash;
3034
bytes32 blobCommitmentsHash;
35+
bytes32 attestationsHash;
36+
bytes32 payloadDigest;
3137
Slot slotNumber;
3238
FeeHeader feeHeader;
3339
}
3440

3541
struct CompressedTempBlockLog {
3642
bytes32 headerHash;
3743
bytes32 blobCommitmentsHash;
44+
bytes32 attestationsHash;
45+
bytes32 payloadDigest;
3846
CompressedSlot slotNumber;
3947
CompressedFeeHeader feeHeader;
4048
}
@@ -53,6 +61,8 @@ library CompressedTempBlockLogLib {
5361
return CompressedTempBlockLog({
5462
headerHash: _blockLog.headerHash,
5563
blobCommitmentsHash: _blockLog.blobCommitmentsHash,
64+
attestationsHash: _blockLog.attestationsHash,
65+
payloadDigest: _blockLog.payloadDigest,
5666
slotNumber: _blockLog.slotNumber.compress(),
5767
feeHeader: _blockLog.feeHeader.compress()
5868
});
@@ -66,6 +76,8 @@ library CompressedTempBlockLogLib {
6676
return TempBlockLog({
6777
headerHash: _compressedBlockLog.headerHash,
6878
blobCommitmentsHash: _compressedBlockLog.blobCommitmentsHash,
79+
attestationsHash: _compressedBlockLog.attestationsHash,
80+
payloadDigest: _compressedBlockLog.payloadDigest,
6981
slotNumber: _compressedBlockLog.slotNumber.decompress(),
7082
feeHeader: _compressedBlockLog.feeHeader.decompress()
7183
});

l1-contracts/src/core/libraries/rollup/EpochProofLib.sol

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ import {
88
IRollupCore,
99
RollupStore
1010
} from "@aztec/core/interfaces/IRollup.sol";
11+
import {CompressedTempBlockLog} from "@aztec/core/libraries/compressed-data/BlockLog.sol";
1112
import {ChainTipsLib, CompressedChainTips} from "@aztec/core/libraries/compressed-data/Tips.sol";
1213
import {Constants} from "@aztec/core/libraries/ConstantsGen.sol";
1314
import {Errors} from "@aztec/core/libraries/Errors.sol";
1415
import {BlobLib} from "@aztec/core/libraries/rollup/BlobLib.sol";
1516
import {CompressedFeeHeader, FeeHeaderLib} from "@aztec/core/libraries/rollup/FeeLib.sol";
1617
import {RewardLib} from "@aztec/core/libraries/rollup/RewardLib.sol";
1718
import {STFLib} from "@aztec/core/libraries/rollup/STFLib.sol";
19+
import {ValidatorSelectionLib} from "@aztec/core/libraries/rollup/ValidatorSelectionLib.sol";
1820
import {Timestamp, Slot, Epoch, TimeLib} from "@aztec/core/libraries/TimeLib.sol";
21+
import {CompressedSlot, CompressedTimeMath} from "@aztec/shared/libraries/CompressedTimeMath.sol";
22+
import {CommitteeAttestations, SignatureLib} from "@aztec/shared/libraries/SignatureLib.sol";
1923
import {Math} from "@oz/utils/math/Math.sol";
2024
import {SafeCast} from "@oz/utils/math/SafeCast.sol";
2125

@@ -26,6 +30,8 @@ library EpochProofLib {
2630
using FeeHeaderLib for CompressedFeeHeader;
2731
using SafeCast for uint256;
2832
using ChainTipsLib for CompressedChainTips;
33+
using SignatureLib for CommitteeAttestations;
34+
using CompressedTimeMath for CompressedSlot;
2935

3036
// This is a temporary struct to avoid stack too deep errors
3137
struct BlobVarsTemp {
@@ -65,6 +71,9 @@ library EpochProofLib {
6571

6672
Epoch endEpoch = assertAcceptable(_args.start, _args.end);
6773

74+
// Verify attestations for the last block in the epoch
75+
verifyLastBlockAttestations(_args.end, _args.attestations);
76+
6877
require(verifyEpochRootProof(_args), Errors.Rollup__InvalidProof());
6978

7079
RollupStore storage rollupStore = STFLib.getStorage();
@@ -207,6 +216,31 @@ library EpochProofLib {
207216
return publicInputs;
208217
}
209218

219+
/**
220+
* @notice Verifies the attestations for the last block in the epoch
221+
* @param _endBlockNumber The last block number in the epoch
222+
* @param _attestations The attestations to verify
223+
*/
224+
function verifyLastBlockAttestations(
225+
uint256 _endBlockNumber,
226+
CommitteeAttestations memory _attestations
227+
) private {
228+
// Get the stored attestation hash and payload digest for the last block
229+
CompressedTempBlockLog storage blockLog = STFLib.getStorageTempBlockLog(_endBlockNumber);
230+
231+
// Verify that the provided attestations match the stored hash
232+
bytes32 providedAttestationsHash = keccak256(abi.encode(_attestations));
233+
require(
234+
providedAttestationsHash == blockLog.attestationsHash, Errors.Rollup__InvalidAttestations()
235+
);
236+
237+
// Get the slot and epoch for the last block
238+
Slot slot = blockLog.slotNumber.decompress();
239+
Epoch epoch = STFLib.getEpochForBlock(_endBlockNumber);
240+
241+
ValidatorSelectionLib.verifyAttestations(slot, epoch, _attestations, blockLog.payloadDigest);
242+
}
243+
210244
function assertAcceptable(uint256 _start, uint256 _end) private view returns (Epoch) {
211245
RollupStore storage rollupStore = STFLib.getStorage();
212246

0 commit comments

Comments
 (0)