Skip to content

Commit 078f6fd

Browse files
committed
feat: Delayed signature verification during block proposals
Implements AztecProtocol/engineering-designs#69
1 parent 8171326 commit 078f6fd

File tree

18 files changed

+454
-47
lines changed

18 files changed

+454
-47
lines changed

l1-contracts/src/core/Rollup.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,12 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
113113
ExtRollupLib.validateHeader(
114114
ValidateHeaderArgs({
115115
header: _header,
116-
attestations: _attestations,
117116
digest: _digest,
118117
manaBaseFee: getManaBaseFeeAt(currentTime, true),
119118
blobsHashesCommitment: _blobsHash,
120119
flags: _flags
121-
})
120+
}),
121+
_attestations
122122
);
123123
}
124124

@@ -365,6 +365,8 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
365365
archive: rollupStore.archives[_blockNumber],
366366
headerHash: tempBlockLog.headerHash,
367367
blobCommitmentsHash: tempBlockLog.blobCommitmentsHash,
368+
attestationsHash: tempBlockLog.attestationsHash,
369+
payloadDigest: tempBlockLog.payloadDigest,
368370
slotNumber: tempBlockLog.slotNumber,
369371
feeHeader: tempBlockLog.feeHeader
370372
});

l1-contracts/src/core/RollupCore.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,23 @@ contract RollupCore is
250250
ExtRollupLib.propose(_args, _attestations, _blobInput, checkBlob);
251251
}
252252

253+
function invalidateBadAttestation(
254+
uint256 _blockNumber,
255+
CommitteeAttestations memory _attestations,
256+
address[] memory _committee,
257+
uint256 _invalidIndex
258+
) external {
259+
ExtRollupLib2.invalidateBadAttestation(_blockNumber, _attestations, _committee, _invalidIndex);
260+
}
261+
262+
function invalidateInsufficientAttestations(
263+
uint256 _blockNumber,
264+
CommitteeAttestations memory _attestations,
265+
address[] memory _committee
266+
) external {
267+
ExtRollupLib2.invalidateInsufficientAttestations(_blockNumber, _attestations, _committee);
268+
}
269+
253270
function setupEpoch() public override(IValidatorSelectionCore) {
254271
ExtRollupLib2.setupEpoch();
255272
}

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

Lines changed: 15 additions & 2 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);
@@ -124,6 +124,19 @@ interface IRollupCore {
124124

125125
function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external;
126126

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

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

Lines changed: 5 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
@@ -102,6 +106,7 @@ library Errors {
102106
error ValidatorSelection__InsufficientAttestations(uint256 minimumNeeded, uint256 provided); // 0xaf47297f
103107
error ValidatorSelection__InvalidCommitteeCommitment(bytes32 reconstructed, bytes32 expected); // 0xca8d5954
104108
error ValidatorSelection__InsufficientCommitteeSize(uint256 actual, uint256 expected); // 0x98673597
109+
error ValidatorSelection__ProposerIndexTooLarge(uint256 index);
105110

106111
// Staking
107112
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: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@ import {
66
SubmitEpochRootProofArgs,
77
PublicInputArgs,
88
IRollupCore,
9-
RollupStore
9+
RollupStore,
10+
BlockHeaderValidationFlags
1011
} from "@aztec/core/interfaces/IRollup.sol";
1112
import {ChainTipsLib, CompressedChainTips} from "@aztec/core/libraries/compressed-data/Tips.sol";
13+
import {TempBlockLog} from "@aztec/core/libraries/compressed-data/BlockLog.sol";
1214
import {Constants} from "@aztec/core/libraries/ConstantsGen.sol";
1315
import {Errors} from "@aztec/core/libraries/Errors.sol";
1416
import {BlobLib} from "@aztec/core/libraries/rollup/BlobLib.sol";
1517
import {CompressedFeeHeader, FeeHeaderLib} from "@aztec/core/libraries/rollup/FeeLib.sol";
1618
import {RewardLib} from "@aztec/core/libraries/rollup/RewardLib.sol";
1719
import {STFLib} from "@aztec/core/libraries/rollup/STFLib.sol";
20+
import {ValidatorSelectionLib} from "@aztec/core/libraries/rollup/ValidatorSelectionLib.sol";
1821
import {Timestamp, Slot, Epoch, TimeLib} from "@aztec/core/libraries/TimeLib.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,7 @@ library EpochProofLib {
2630
using FeeHeaderLib for CompressedFeeHeader;
2731
using SafeCast for uint256;
2832
using ChainTipsLib for CompressedChainTips;
33+
using SignatureLib for CommitteeAttestations;
2934

3035
// This is a temporary struct to avoid stack too deep errors
3136
struct BlobVarsTemp {
@@ -65,6 +70,9 @@ library EpochProofLib {
6570

6671
Epoch endEpoch = assertAcceptable(_args.start, _args.end);
6772

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

7078
RollupStore storage rollupStore = STFLib.getStorage();
@@ -286,4 +294,32 @@ library EpochProofLib {
286294
function addressToField(address _a) private pure returns (bytes32) {
287295
return bytes32(uint256(uint160(_a)));
288296
}
297+
298+
/**
299+
* @notice Verifies the attestations for the last block in the epoch
300+
* @param _endBlockNumber The last block number in the epoch
301+
* @param _attestations The attestations to verify
302+
*/
303+
function verifyLastBlockAttestations(
304+
uint256 _endBlockNumber,
305+
CommitteeAttestations memory _attestations
306+
) private {
307+
// Get the stored attestation hash and payload digest for the last block
308+
TempBlockLog memory blockLog = STFLib.getTempBlockLog(_endBlockNumber);
309+
310+
// Verify that the provided attestations match the stored hash
311+
bytes32 providedAttestationsHash = keccak256(abi.encode(_attestations));
312+
require(
313+
providedAttestationsHash == blockLog.attestationsHash, Errors.Rollup__InvalidAttestations()
314+
);
315+
316+
// Get the slot and epoch for the last block
317+
Slot slot = blockLog.slotNumber;
318+
Epoch epoch = STFLib.getEpochForBlock(_endBlockNumber);
319+
320+
// Only verify attestations if they are not empty (for testing compatibility)
321+
if (!_attestations.isEmpty()) {
322+
ValidatorSelectionLib.verify(slot, epoch, _attestations, blockLog.payloadDigest);
323+
}
324+
}
289325
}

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,45 @@
44
pragma solidity >=0.8.27;
55

66
import {SubmitEpochRootProofArgs, PublicInputArgs} from "@aztec/core/interfaces/IRollup.sol";
7-
import {Timestamp, TimeLib} from "@aztec/core/libraries/TimeLib.sol";
7+
import {TempBlockLog} from "@aztec/core/libraries/compressed-data/BlockLog.sol";
8+
import {STFLib} from "@aztec/core/libraries/rollup/STFLib.sol";
9+
import {Timestamp, TimeLib, Slot, Epoch} from "@aztec/core/libraries/TimeLib.sol";
810
import {BlobLib} from "./BlobLib.sol";
911
import {EpochProofLib} from "./EpochProofLib.sol";
12+
import {InvalidateLib} from "./InvalidateLib.sol";
13+
import {SignatureLib} from "@aztec/shared/libraries/SignatureLib.sol";
1014
import {
11-
ProposeLib, ProposeArgs, CommitteeAttestations, ValidateHeaderArgs
15+
ProposeLib,
16+
ProposeArgs,
17+
CommitteeAttestations,
18+
ValidateHeaderArgs,
19+
ValidatorSelectionLib
1220
} from "./ProposeLib.sol";
1321

1422
// We are using this library such that we can more easily "link" just a larger external library
1523
// instead of a few smaller ones.
1624
library ExtRollupLib {
1725
using TimeLib for Timestamp;
26+
using TimeLib for Slot;
27+
using SignatureLib for CommitteeAttestations;
1828

1929
function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external {
2030
EpochProofLib.submitEpochRootProof(_args);
2131
}
2232

23-
function validateHeader(ValidateHeaderArgs calldata _args) external {
33+
function validateHeader(
34+
ValidateHeaderArgs calldata _args,
35+
CommitteeAttestations calldata _attestations
36+
) external {
2437
ProposeLib.validateHeader(_args);
38+
if (_attestations.isEmpty()) {
39+
return; // No attestations to validate
40+
}
41+
42+
Slot slot = _args.header.slotNumber;
43+
Epoch epoch = slot.epochFromSlot();
44+
ValidatorSelectionLib.verify(slot, epoch, _attestations, _args.digest);
45+
ValidatorSelectionLib.verifyProposer(slot, _attestations, _args.digest);
2546
}
2647

2748
function propose(

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ pragma solidity >=0.8.27;
66
import {Epoch, Slot, Timestamp, TimeLib} from "@aztec/core/libraries/TimeLib.sol";
77
import {StakingQueueConfig} from "@aztec/core/libraries/compressed-data/StakingQueueConfig.sol";
88
import {StakingLib} from "./StakingLib.sol";
9+
import {InvalidateLib} from "./InvalidateLib.sol";
910
import {ValidatorSelectionLib} from "./ValidatorSelectionLib.sol";
11+
import {CommitteeAttestations} from "@aztec/shared/libraries/SignatureLib.sol";
1012
import {
1113
RewardBooster,
1214
RewardBoostConfig,
@@ -78,12 +80,29 @@ library ExtRollupLib2 {
7880
StakingLib.updateStakingQueueConfig(_config);
7981
}
8082

83+
function invalidateBadAttestation(
84+
uint256 _blockNumber,
85+
CommitteeAttestations memory _attestations,
86+
address[] memory _committee,
87+
uint256 _invalidIndex
88+
) external {
89+
InvalidateLib.invalidateBadAttestation(_blockNumber, _attestations, _committee, _invalidIndex);
90+
}
91+
92+
function invalidateInsufficientAttestations(
93+
uint256 _blockNumber,
94+
CommitteeAttestations memory _attestations,
95+
address[] memory _committee
96+
) external {
97+
InvalidateLib.invalidateInsufficientAttestations(_blockNumber, _attestations, _committee);
98+
}
99+
81100
function getCommitteeAt(Epoch _epoch) external returns (address[] memory) {
82101
return ValidatorSelectionLib.getCommitteeAt(_epoch);
83102
}
84103

85-
function getProposerAt(Slot _slot) external returns (address) {
86-
return ValidatorSelectionLib.getProposerAt(_slot);
104+
function getProposerAt(Slot _slot) external returns (address proposer) {
105+
(proposer,) = ValidatorSelectionLib.getProposerAt(_slot);
87106
}
88107

89108
function getCommitteeCommitmentAt(Epoch _epoch) external returns (bytes32, uint256) {

0 commit comments

Comments
 (0)