Skip to content

Commit a1f94c3

Browse files
authored
fix: constrain proposed block header (#13693)
#12928
1 parent 4c9da5b commit a1f94c3

File tree

91 files changed

+3767
-3202
lines changed

Some content is hidden

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

91 files changed

+3767
-3202
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct SubmitEpochRootProofArgs {
3535

3636
struct BlockLog {
3737
bytes32 archive;
38+
bytes32 headerHash; // hash of the proposed block header
3839
Slot slotNumber;
3940
}
4041

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ library Constants {
193193
uint256 internal constant TOTAL_MANA_USED_LENGTH = 1;
194194
uint256 internal constant BLOCK_HEADER_LENGTH = 25;
195195
uint256 internal constant BLOCK_HEADER_LENGTH_BYTES = 648;
196+
uint256 internal constant PROPOSED_BLOCK_HEADER_LENGTH = 12;
197+
uint256 internal constant PROPOSED_BLOCK_HEADER_LENGTH_BYTES = 348;
196198
uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 724;
197199
uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 908;
198200
uint256 internal constant PRIVATE_CONTEXT_INPUTS_LENGTH = 40;
@@ -214,8 +216,8 @@ library Constants {
214216
uint256 internal constant PRIVATE_TO_ROLLUP_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 782;
215217
uint256 internal constant CONSTANT_ROLLUP_DATA_LENGTH = 13;
216218
uint256 internal constant BASE_OR_MERGE_PUBLIC_INPUTS_LENGTH = 52;
217-
uint256 internal constant BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH = 984;
218-
uint256 internal constant ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 970;
219+
uint256 internal constant BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH = 1032;
220+
uint256 internal constant ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 1020;
219221
uint256 internal constant GET_NOTES_ORACLE_RETURN_LENGTH = 674;
220222
uint256 internal constant NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP = 2048;
221223
uint256 internal constant NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP = 2048;

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,11 @@ library Errors {
5252
error Rollup__InsufficientFundsInEscrow(uint256 required, uint256 available); // 0xa165f276
5353
error Rollup__InvalidArchive(bytes32 expected, bytes32 actual); // 0xb682a40e
5454
error Rollup__InvalidBlockNumber(uint256 expected, uint256 actual); // 0xe5edf847
55-
error Rollup__InvalidChainId(uint256 expected, uint256 actual); // 0x37b5bc12
5655
error Rollup__InvalidInHash(bytes32 expected, bytes32 actual); // 0xcd6f4233
5756
error Rollup__InvalidPreviousArchive(bytes32 expected, bytes32 actual); // 0xb682a40e
5857
error Rollup__InvalidProof(); // 0xa5b2ba17
5958
error Rollup__InvalidProposedArchive(bytes32 expected, bytes32 actual); // 0x32532e73
6059
error Rollup__InvalidTimestamp(Timestamp expected, Timestamp actual); // 0x3132e895
61-
error Rollup__InvalidVersion(uint256 expected, uint256 actual); // 0x9ef30794
6260
error Rollup__InvalidBlobHash(bytes32 blobHash); // 0xc4a168c6
6361
error Rollup__InvalidBlobProof(bytes32 blobHash); // 0x5ca17bef
6462
error Rollup__InvalidBlobPublicInputsHash(bytes32 expected, bytes32 actual); // 0xfe6b4994

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,6 @@ library EpochProofLib {
116116
bytes calldata _blobPublicInputs
117117
) internal view returns (bytes32[] memory) {
118118
RollupStore storage rollupStore = STFLib.getStorage();
119-
// Args are defined as an array because Solidity complains with "stack too deep" otherwise
120-
// 0 bytes32 _previousArchive,
121-
// 1 bytes32 _endArchive,
122-
// 2 bytes32 _endTimestamp,
123-
// 3 bytes32 _outHash,
124-
// 4 bytes32 _proverId,
125119

126120
// TODO(#7373): Public inputs are not fully verified
127121

@@ -154,7 +148,10 @@ library EpochProofLib {
154148
// end_timestamp: u64,
155149
// end_block_number: Field,
156150
// out_hash: Field,
151+
// proposedBlockHeaderHashes: [Field; Constants.AZTEC_MAX_EPOCH_DURATION],
157152
// fees: [FeeRecipient; Constants.AZTEC_MAX_EPOCH_DURATION],
153+
// chain_id: Field,
154+
// version: Field,
158155
// vk_tree_root: Field,
159156
// protocol_contract_tree_root: Field,
160157
// prover_id: Field,
@@ -185,12 +182,26 @@ library EpochProofLib {
185182
publicInputs[6] = _args.outHash;
186183
}
187184

185+
uint256 numBlocks = _end - _start + 1;
186+
187+
for (uint256 i = 0; i < numBlocks; i++) {
188+
publicInputs[7 + i] = rollupStore.blocks[_start + i].headerHash;
189+
}
190+
191+
uint256 offset = 7 + Constants.AZTEC_MAX_EPOCH_DURATION;
192+
188193
uint256 feesLength = Constants.AZTEC_MAX_EPOCH_DURATION * 2;
189-
// fees[9 to (9+feesLength-1)]: array of recipient-value pairs
194+
// fees[2n to 2n + 1]: a fee element, which contains of a recipient and a value
190195
for (uint256 i = 0; i < feesLength; i++) {
191-
publicInputs[7 + i] = _fees[i];
196+
publicInputs[offset + i] = _fees[i];
192197
}
193-
uint256 offset = 7 + feesLength;
198+
offset += feesLength;
199+
200+
publicInputs[offset] = bytes32(block.chainid);
201+
offset += 1;
202+
203+
publicInputs[offset] = bytes32(rollupStore.config.version);
204+
offset += 1;
194205

195206
// vk_tree_root
196207
publicInputs[offset] = rollupStore.config.vkTreeRoot;
@@ -207,7 +218,7 @@ library EpochProofLib {
207218
{
208219
BlobVarsTemp memory tmp = BlobVarsTemp({blobOffset: 0, offset: offset, i: 0});
209220
// blob_public_inputs
210-
for (; tmp.i < _end - _start + 1; tmp.i++) {
221+
for (; tmp.i < numBlocks; tmp.i++) {
211222
uint8 blobsInBlock = uint8(_blobPublicInputs[tmp.blobOffset++]);
212223
for (uint256 j = 0; j < Constants.BLOBS_PER_BLOCK; j++) {
213224
if (j < blobsInBlock) {

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

Lines changed: 39 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,16 @@
33
pragma solidity >=0.8.27;
44

55
import {Constants} from "@aztec/core/libraries/ConstantsGen.sol";
6+
import {Hash} from "@aztec/core/libraries/crypto/Hash.sol";
67
import {Errors} from "@aztec/core/libraries/Errors.sol";
78

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

10-
struct AppendOnlyTreeSnapshot {
11-
bytes32 root;
12-
uint32 nextAvailableLeafIndex;
13-
}
14-
15-
struct PartialStateReference {
16-
AppendOnlyTreeSnapshot noteHashTree;
17-
AppendOnlyTreeSnapshot nullifierTree;
18-
AppendOnlyTreeSnapshot contractTree;
19-
AppendOnlyTreeSnapshot publicDataTree;
20-
}
21-
22-
struct StateReference {
23-
AppendOnlyTreeSnapshot l1ToL2MessageTree;
24-
// Note: Can't use "partial" name here as in protocol specs because it is a reserved solidity keyword
25-
PartialStateReference partialStateReference;
26-
}
27-
2811
struct GasFees {
2912
uint256 feePerDaGas;
3013
uint256 feePerL2Gas;
3114
}
3215

33-
struct GlobalVariables {
34-
uint256 chainId;
35-
uint256 version;
36-
uint256 blockNumber;
37-
Slot slotNumber;
38-
Timestamp timestamp;
39-
address coinbase;
40-
bytes32 feeRecipient;
41-
GasFees gasFees;
42-
}
43-
4416
struct ContentCommitment {
4517
uint256 numTxs;
4618
bytes32 blobsHash;
@@ -49,11 +21,13 @@ struct ContentCommitment {
4921
}
5022

5123
struct Header {
52-
AppendOnlyTreeSnapshot lastArchive;
24+
bytes32 lastArchiveRoot;
5325
ContentCommitment contentCommitment;
54-
StateReference stateReference;
55-
GlobalVariables globalVariables;
56-
uint256 totalFees;
26+
Slot slotNumber;
27+
Timestamp timestamp;
28+
address coinbase;
29+
bytes32 feeRecipient;
30+
GasFees gasFees;
5731
uint256 totalManaUsed;
5832
}
5933

@@ -71,44 +45,24 @@ struct Header {
7145
*
7246
* | byte start | num bytes | name
7347
* | --- | --- | ---
74-
* | | | Header {
75-
* | 0x0000 | 0x20 | lastArchive.root
76-
* | 0x0020 | 0x04 | lastArchive.nextAvailableLeafIndex
77-
* | | | ContentCommitment {
78-
* | 0x0024 | 0x20 | numTxs
79-
* | 0x0044 | 0x20 | blobsHash
80-
* | 0x0064 | 0x20 | inHash
81-
* | 0x0084 | 0x20 | outHash
82-
* | | | StateReference {
83-
* | 0x00a4 | 0x20 | l1ToL2MessageTree.root
84-
* | 0x00c4 | 0x04 | l1ToL2MessageTree.nextAvailableLeafIndex
85-
* | | | PartialStateReference {
86-
* | 0x00c8 | 0x20 | noteHashTree.root
87-
* | 0x00e8 | 0x04 | noteHashTree.nextAvailableLeafIndex
88-
* | 0x00ec | 0x20 | nullifierTree.root
89-
* | 0x010c | 0x04 | nullifierTree.nextAvailableLeafIndex
90-
* | 0x0110 | 0x20 | publicDataTree.root
91-
* | 0x0130 | 0x04 | publicDataTree.nextAvailableLeafIndex
92-
* | | | }
93-
* | | | }
94-
* | | | GlobalVariables {
95-
* | 0x0134 | 0x20 | chainId
96-
* | 0x0154 | 0x20 | version
97-
* | 0x0174 | 0x20 | blockNumber
98-
* | 0x0194 | 0x20 | slotNumber
99-
* | 0x01b4 | 0x20 | timestamp
100-
* | 0x01d4 | 0x14 | coinbase
101-
* | 0x01e8 | 0x20 | feeRecipient
102-
* | 0x0208 | 0x20 | gasFees.feePerDaGas
103-
* | 0x0228 | 0x20 | gasFees.feePerL2Gas
104-
* | | | }
48+
* | 0x0000 | 0x20 | lastArchiveRoot
49+
* | | | ContentCommitment {
50+
* | 0x0020 | 0x20 | numTxs
51+
* | 0x0040 | 0x20 | blobsHash
52+
* | 0x0060 | 0x20 | inHash
53+
* | 0x0080 | 0x20 | outHash
10554
* | | | }
106-
* | 0x0248 | 0x20 | total_fees
107-
* | 0x0268 | 0x20 | total_mana_used
55+
* | 0x00a0 | 0x20 | slotNumber
56+
* | 0x00c0 | 0x08 | timestamp
57+
* | 0x00c8 | 0x14 | coinbase
58+
* | 0x00dc | 0x20 | feeRecipient
59+
* | 0x00fc | 0x20 | gasFees.feePerDaGas
60+
* | 0x011c | 0x20 | gasFees.feePerL2Gas
61+
* | 0x013c | 0x20 | totalManaUsed
10862
* | --- | --- | ---
10963
*/
11064
library HeaderLib {
111-
uint256 private constant HEADER_LENGTH = Constants.BLOCK_HEADER_LENGTH_BYTES; // Header byte length
65+
uint256 private constant HEADER_LENGTH = Constants.PROPOSED_BLOCK_HEADER_LENGTH_BYTES; // Header byte length
11266

11367
/**
11468
* @notice Decodes the header
@@ -124,47 +78,29 @@ library HeaderLib {
12478
Header memory header;
12579

12680
// Reading lastArchive
127-
header.lastArchive = AppendOnlyTreeSnapshot(
128-
bytes32(_header[0x0000:0x0020]), uint32(bytes4(_header[0x0020:0x0024]))
129-
);
81+
header.lastArchiveRoot = bytes32(_header[0x0000:0x0020]);
13082

13183
// Reading ContentCommitment
132-
header.contentCommitment.numTxs = uint256(bytes32(_header[0x0024:0x0044]));
133-
header.contentCommitment.blobsHash = bytes32(_header[0x0044:0x0064]);
134-
header.contentCommitment.inHash = bytes32(_header[0x0064:0x0084]);
135-
header.contentCommitment.outHash = bytes32(_header[0x0084:0x00a4]);
136-
137-
// Reading StateReference
138-
header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(
139-
bytes32(_header[0x00a4:0x00c4]), uint32(bytes4(_header[0x00c4:0x00c8]))
140-
);
141-
header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(
142-
bytes32(_header[0x00c8:0x00e8]), uint32(bytes4(_header[0x00e8:0x00ec]))
143-
);
144-
header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(
145-
bytes32(_header[0x00ec:0x010c]), uint32(bytes4(_header[0x010c:0x0110]))
146-
);
147-
header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(
148-
bytes32(_header[0x0110:0x0130]), uint32(bytes4(_header[0x0130:0x0134]))
149-
);
150-
151-
// Reading GlobalVariables
152-
header.globalVariables.chainId = uint256(bytes32(_header[0x0134:0x0154]));
153-
header.globalVariables.version = uint256(bytes32(_header[0x0154:0x0174]));
154-
header.globalVariables.blockNumber = uint256(bytes32(_header[0x0174:0x0194]));
155-
header.globalVariables.slotNumber = Slot.wrap(uint256(bytes32(_header[0x0194:0x01b4])));
156-
header.globalVariables.timestamp = Timestamp.wrap(uint256(bytes32(_header[0x01b4:0x01d4])));
157-
header.globalVariables.coinbase = address(bytes20(_header[0x01d4:0x01e8]));
158-
header.globalVariables.feeRecipient = bytes32(_header[0x01e8:0x0208]);
159-
header.globalVariables.gasFees.feePerDaGas = uint256(bytes32(_header[0x0208:0x0228]));
160-
header.globalVariables.gasFees.feePerL2Gas = uint256(bytes32(_header[0x0228:0x0248]));
161-
162-
// Reading totalFees
163-
header.totalFees = uint256(bytes32(_header[0x0248:0x0268]));
84+
header.contentCommitment.numTxs = uint256(bytes32(_header[0x0020:0x0040]));
85+
header.contentCommitment.blobsHash = bytes32(_header[0x0040:0x0060]);
86+
header.contentCommitment.inHash = bytes32(_header[0x0060:0x0080]);
87+
header.contentCommitment.outHash = bytes32(_header[0x0080:0x00a0]);
88+
89+
// Reading partial GlobalVariables
90+
header.slotNumber = Slot.wrap(uint256(bytes32(_header[0x00a0:0x00c0])));
91+
header.timestamp = Timestamp.wrap(uint256(uint64(bytes8(_header[0x00c0:0x00c8]))));
92+
header.coinbase = address(bytes20(_header[0x00c8:0x00dc]));
93+
header.feeRecipient = bytes32(_header[0x00dc:0x00fc]);
94+
header.gasFees.feePerDaGas = uint256(bytes32(_header[0x00fc:0x011c]));
95+
header.gasFees.feePerL2Gas = uint256(bytes32(_header[0x011c:0x013c]));
16496

16597
// Reading totalManaUsed
166-
header.totalManaUsed = uint256(bytes32(_header[0x0268:0x0288]));
98+
header.totalManaUsed = uint256(bytes32(_header[0x013c:0x015c]));
16799

168100
return header;
169101
}
102+
103+
function hash(bytes memory _header) internal pure returns (bytes32) {
104+
return Hash.sha256ToField(_header);
105+
}
170106
}

0 commit comments

Comments
 (0)