Skip to content

Commit dcbed56

Browse files
committed
feat: expose inbox rolling hash on root rollup public inputs (A-1373)
Add the {previous, end} inbox rolling-hash pair to RootRollupPublicInputs so the epoch's consumed chain segment is carried through to proof verification. The root rollup sources the pair from the merged checkpoint public inputs; continuity within the range is already enforced by the checkpoint merges. On L1, PublicInputArgs gains previousInboxRollingHash/endInboxRollingHash and EpochProofLib places them at public-input positions 3 and 4 (header hashes, fees, constants and blob inputs shift by two). Both values are deliberately unvalidated until the Fast Inbox flip, when they will be checked against the per-checkpoint records written at propose; for now they are pass-through only. Also fixes two ivc-integration benchmark generators whose hand-built parity base inputs were missing start_rolling_hash/num_msgs.
1 parent 567526e commit dcbed56

25 files changed

Lines changed: 154 additions & 15 deletions

File tree

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ struct PublicInputArgs {
2727
bytes32 previousArchive;
2828
bytes32 endArchive;
2929
bytes32 outHash;
30+
// Inbox rolling-hash chain segment consumed across the proven epoch (AZIP-22 Fast Inbox). Deliberately UNVALIDATED
31+
// until the Fast Inbox flip, when they get checked against per-checkpoint records written at propose; for now they
32+
// are only passed through to the proof's public inputs.
33+
bytes32 previousInboxRollingHash;
34+
bytes32 endInboxRollingHash;
3035
address proverId;
3136
}
3237

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ library Constants {
2525
355_785_372_471_781_095_838_790_036_702_437_931_769_306_153_278_986_832_745_847_530_947_941_691_539;
2626
uint256 internal constant FEE_JUICE_ADDRESS = 3;
2727
uint256 internal constant BLS12_POINT_COMPRESSED_BYTES = 48;
28-
uint256 internal constant ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 111;
28+
uint256 internal constant ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 113;
2929
uint256 internal constant NUM_MSGS_PER_BASE_PARITY = 256;
3030
uint256 internal constant NUM_BASE_PARITY_PER_ROOT_PARITY = 4;
3131
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ library EpochProofLib {
191191
// previous_archive_root: Field,
192192
// end_archive_root: Field,
193193
// out_hash: Field,
194+
// previous_inbox_rolling_hash: Field,
195+
// end_inbox_rolling_hash: Field,
194196
// checkpointHeaderHashes: [Field; Constants.MAX_CHECKPOINTS_PER_EPOCH],
195197
// fees: [FeeRecipient; Constants.MAX_CHECKPOINTS_PER_EPOCH],
196198
// chain_id: Field,
@@ -208,15 +210,21 @@ library EpochProofLib {
208210
publicInputs[1] = _args.endArchive;
209211

210212
publicInputs[2] = _args.outHash;
213+
214+
// Inbox rolling-hash chain segment consumed across the epoch (AZIP-22 Fast Inbox). Deliberately UNVALIDATED
215+
// until the Fast Inbox flip, when they get checked against per-checkpoint records written at propose; for now
216+
// they are only passed through to the proof's public inputs.
217+
publicInputs[3] = _args.previousInboxRollingHash;
218+
publicInputs[4] = _args.endInboxRollingHash;
211219
}
212220

213221
uint256 numCheckpoints = _end - _start + 1;
214222

215223
for (uint256 i = 0; i < numCheckpoints; i++) {
216-
publicInputs[3 + i] = STFLib.getHeaderHash(_start + i);
224+
publicInputs[5 + i] = STFLib.getHeaderHash(_start + i);
217225
}
218226

219-
uint256 offset = 3 + Constants.MAX_CHECKPOINTS_PER_EPOCH;
227+
uint256 offset = 5 + Constants.MAX_CHECKPOINTS_PER_EPOCH;
220228

221229
// Taking recipient/value from the checkpoint headers rather than the prover
222230
// as defense in depth. Slots past numCheckpoints stay zero.

l1-contracts/test/Rollup.t.sol

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,52 @@ contract RollupTest is RollupBase {
874874
assertEq(outbox.getRootData(Epoch.wrap(0), 2), outHash2, "Root at K=2 should be outHash2");
875875
}
876876

877+
// getEpochProofPublicInputs is the view that the prover-publisher calls off-chain to validate its inputs before
878+
// submitting. Because the fee recipient/value public inputs are taken from the supplied headers, the header check
879+
// must run here too - not only on the submit path - so a mismatch is caught before publishing rather than reverting
880+
// on-chain.
881+
function testGetEpochProofPublicInputsVerifiesHeaders() public setUpFor("empty_checkpoint_1") {
882+
_proposeCheckpoint("empty_checkpoint_1", 1);
883+
884+
DecoderBase.Data memory data = load("empty_checkpoint_1").checkpoint;
885+
CheckpointLog memory checkpoint = rollup.getCheckpoint(0);
886+
887+
PublicInputArgs memory args = PublicInputArgs({
888+
previousArchive: checkpoint.archive,
889+
endArchive: data.archive,
890+
outHash: data.header.outHash,
891+
previousInboxRollingHash: 0,
892+
endInboxRollingHash: data.header.inboxRollingHash,
893+
proverId: address(0)
894+
});
895+
896+
ProposedHeader[] memory headers = new ProposedHeader[](1);
897+
headers[0] = proposedHeaders[1];
898+
899+
// With the canonical header, the getter assembles the public inputs, sourcing the fee recipient/value from the
900+
// header.
901+
bytes32[] memory publicInputs = rollup.getEpochProofPublicInputs(1, 1, args, headers, data.batchedBlobInputs);
902+
assertEq(publicInputs.length, Constants.ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH, "Unexpected public inputs length");
903+
904+
uint256 feesOffset = 5 + Constants.MAX_CHECKPOINTS_PER_EPOCH;
905+
assertEq(
906+
publicInputs[feesOffset], bytes32(uint256(uint160(headers[0].coinbase))), "Coinbase not sourced from header"
907+
);
908+
assertEq(
909+
publicInputs[feesOffset + 1], bytes32(headers[0].accumulatedFees), "Accumulated fees not sourced from header"
910+
);
911+
912+
// Tamper a fee field so the header no longer hashes to the stored value; the getter must reject it.
913+
bytes32 expectedHeaderHash = ProposedHeaderLib.hash(headers[0]);
914+
headers[0].accumulatedFees += 1;
915+
bytes32 providedHeaderHash = ProposedHeaderLib.hash(headers[0]);
916+
917+
vm.expectRevert(
918+
abi.encodeWithSelector(Errors.Rollup__InvalidCheckpointHeader.selector, expectedHeaderHash, providedHeaderHash)
919+
);
920+
rollup.getEpochProofPublicInputs(1, 1, args, headers, data.batchedBlobInputs);
921+
}
922+
877923
function _submitEpochProof(
878924
uint256 _start,
879925
uint256 _end,
@@ -895,7 +941,12 @@ contract RollupTest is RollupBase {
895941
address _prover
896942
) internal {
897943
PublicInputArgs memory args = PublicInputArgs({
898-
previousArchive: _prevArchive, endArchive: _archive, outHash: _outHash, proverId: _prover
944+
previousArchive: _prevArchive,
945+
endArchive: _archive,
946+
outHash: _outHash,
947+
previousInboxRollingHash: 0,
948+
endInboxRollingHash: 0,
949+
proverId: _prover
899950
});
900951

901952
uint256 size = _end - _start + 1;

l1-contracts/test/base/RollupBase.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ contract RollupBase is DecoderBase {
7878
previousArchive: parentCheckpointLog.archive,
7979
endArchive: endFull.checkpoint.archive,
8080
outHash: endFull.checkpoint.header.outHash,
81+
previousInboxRollingHash: 0,
82+
endInboxRollingHash: 0,
8183
proverId: _prover
8284
});
8385

l1-contracts/test/benchmark/happy.t.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,8 @@ contract BenchmarkRollupTest is FeeModelTestPoints, DecoderBase {
511511
previousArchive: rollup.getCheckpoint(start).archive,
512512
endArchive: endCheckpoint.archive,
513513
outHash: endCheckpoint.outHash,
514+
previousInboxRollingHash: 0,
515+
endInboxRollingHash: 0,
514516
proverId: address(0)
515517
});
516518

l1-contracts/test/compression/PreHeating.t.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ contract PreHeatingTest is FeeModelTestPoints, DecoderBase {
263263
previousArchive: rollup.getCheckpoint(start).archive,
264264
endArchive: endCheckpoint.archive,
265265
outHash: endCheckpoint.outHash,
266+
previousInboxRollingHash: 0,
267+
endInboxRollingHash: 0,
266268
proverId: address(0)
267269
});
268270

l1-contracts/test/escape-hatch/integration/EscapeHatchIntegrationBase.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,8 @@ abstract contract EscapeHatchIntegrationBase is ValidatorSelectionTestBase {
325325
previousArchive: previousArchive,
326326
endArchive: endArchive,
327327
outHash: endFull.checkpoint.header.outHash,
328+
previousInboxRollingHash: 0,
329+
endInboxRollingHash: 0,
328330
proverId: _prover
329331
});
330332

l1-contracts/test/escape-hatch/integration/regression/OutHashValidationSkipped.t.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ contract OutHashValidationSkippedTest is EscapeHatchIntegrationBase {
6060
previousArchive: rollup.archiveAt(0),
6161
endArchive: rollup.archiveAt(1),
6262
outHash: wrongOutHash,
63+
previousInboxRollingHash: 0,
64+
endInboxRollingHash: 0,
6365
proverId: address(this)
6466
});
6567

l1-contracts/test/escape-hatch/integration/submitEpochRootProof.t.sol

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,12 @@ contract submitEpochRootProofTest is EscapeHatchIntegrationBase {
112112
bytes32 outHash = rollup.getCheckpoint(1).outHash;
113113

114114
PublicInputArgs memory args = PublicInputArgs({
115-
previousArchive: previousArchive, endArchive: endArchive, outHash: outHash, proverId: address(this)
115+
previousArchive: previousArchive,
116+
endArchive: endArchive,
117+
outHash: outHash,
118+
previousInboxRollingHash: 0,
119+
endInboxRollingHash: 0,
120+
proverId: address(this)
116121
});
117122

118123
ProposedHeader[] memory headers = new ProposedHeader[](1);

0 commit comments

Comments
 (0)