Skip to content

Commit e397c05

Browse files
committed
feat(fast-inbox): check the epoch proof's end inbox rolling hash explicitly (A-1384)
getEpochProofPublicInputs anchored only the start of the consumed rolling-hash chain segment; a wrong endInboxRollingHash was caught transitively, surfacing as a bare Rollup__InvalidProof. Compare it against the hash recorded at propose for the epoch's last checkpoint and revert with Rollup__InvalidEndInboxRollingHash, mirroring the endArchive check. Test helpers that submitted a zero end hash now source it from the proposed header.
1 parent ac7d58f commit e397c05

4 files changed

Lines changed: 46 additions & 10 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ library Errors {
6161
error Rollup__InvalidInHash(bytes32 expected, bytes32 actual); // 0xcd6f4233
6262
error Rollup__InvalidInboxRollingHash(bytes32 expected, bytes32 actual); // 0xed1f7bb5
6363
error Rollup__InvalidPreviousInboxRollingHash(bytes32 expected, bytes32 actual); // 0x2fe7cae5
64+
error Rollup__InvalidEndInboxRollingHash(bytes32 expected, bytes32 actual); // 0x4a9cdb72
6465
error Rollup__InboxBucketStillMutable(uint256 bucketSeq); // 0x7254b980
6566
error Rollup__UnconsumedInboxMessages(uint256 nextBucketSeq); // 0x2bd4bf10
6667
error Rollup__InboxConsumptionBehindParent(uint256 expected, uint256 actual); // 0x54e0c025

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,13 +290,13 @@ library EpochProofLib {
290290
);
291291
}
292292

293+
// Boundary anchoring for the Inbox rolling-hash chain (AZIP-22 Fast Inbox), mirroring previousArchive/endArchive:
294+
// both ends of the claimed chain segment must match the rolling hashes recorded at propose for checkpoints
295+
// _start - 1 and _end. The start needs this to be sound - the previous checkpoint's header is not among this
296+
// proof's public inputs, so nothing else pins where the segment begins. The end is already pinned transitively
297+
// (the checkpoint root binds end_inbox_rolling_hash to the last checkpoint header, whose hash verifyHeaders ties
298+
// to storage), so checking it here only trades a bare proof-verification failure for a specific error.
293299
{
294-
// Start-boundary anchoring for the Inbox rolling-hash chain (AZIP-22 Fast Inbox), mirroring previousArchive:
295-
// the proof's claimed chain start must match the rolling hash recorded at propose for checkpoint _start - 1.
296-
// No end-side check is needed: the checkpoint root writes the parity end into both the checkpoint header and
297-
// end_inbox_rolling_hash, checkpoint merges assert start/end continuity, and verifyHeaders pins the supplied
298-
// headers (whose hash covers inboxRollingHash) to the stored header hashes, so a wrong endInboxRollingHash
299-
// fails proof verification.
300300
bytes32 expectedPreviousInboxRollingHash = STFLib.getInboxRollingHash(_start - 1);
301301
require(
302302
expectedPreviousInboxRollingHash == _args.previousInboxRollingHash,
@@ -305,6 +305,14 @@ library EpochProofLib {
305305
)
306306
);
307307
}
308+
309+
{
310+
bytes32 expectedEndInboxRollingHash = STFLib.getInboxRollingHash(_end);
311+
require(
312+
expectedEndInboxRollingHash == _args.endInboxRollingHash,
313+
Errors.Rollup__InvalidEndInboxRollingHash(expectedEndInboxRollingHash, _args.endInboxRollingHash)
314+
);
315+
}
308316
}
309317

310318
bytes32[] memory publicInputs = new bytes32[](Constants.ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH);

l1-contracts/test/Rollup.t.sol

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,7 @@ contract RollupTest is RollupBase {
905905
endArchive: data.archive,
906906
outHash: data.header.outHash,
907907
previousInboxRollingHash: 0,
908-
endInboxRollingHash: data.header.inboxRollingHash,
908+
endInboxRollingHash: proposedHeaders[1].inboxRollingHash,
909909
proverId: address(0)
910910
});
911911

@@ -954,7 +954,7 @@ contract RollupTest is RollupBase {
954954
endArchive: data.archive,
955955
outHash: data.header.outHash,
956956
previousInboxRollingHash: wrongPrevious,
957-
endInboxRollingHash: data.header.inboxRollingHash,
957+
endInboxRollingHash: proposedHeaders[1].inboxRollingHash,
958958
proverId: address(0)
959959
});
960960

@@ -964,6 +964,33 @@ contract RollupTest is RollupBase {
964964
rollup.getEpochProofPublicInputs(1, 1, args, headers, data.batchedBlobInputs);
965965
}
966966

967+
// The end of the rolling-hash chain segment is pinned to the hash recorded at propose for the epoch's last
968+
// checkpoint (AZIP-22 Fast Inbox), mirroring endArchive. A wrong endInboxRollingHash must be rejected here rather
969+
// than surfacing as a generic proof-verification failure.
970+
function testGetEpochProofPublicInputsRejectsWrongEndInboxRollingHash() public setUpFor("empty_checkpoint_1") {
971+
_proposeCheckpoint("empty_checkpoint_1", 1);
972+
973+
DecoderBase.Data memory data = load("empty_checkpoint_1").checkpoint;
974+
CheckpointLog memory checkpoint = rollup.getCheckpoint(0);
975+
976+
ProposedHeader[] memory headers = new ProposedHeader[](1);
977+
headers[0] = proposedHeaders[1];
978+
979+
bytes32 expectedEnd = proposedHeaders[1].inboxRollingHash;
980+
bytes32 wrongEnd = bytes32(uint256(expectedEnd) + 1);
981+
PublicInputArgs memory args = PublicInputArgs({
982+
previousArchive: checkpoint.archive,
983+
endArchive: data.archive,
984+
outHash: data.header.outHash,
985+
previousInboxRollingHash: 0,
986+
endInboxRollingHash: wrongEnd,
987+
proverId: address(0)
988+
});
989+
990+
vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidEndInboxRollingHash.selector, expectedEnd, wrongEnd));
991+
rollup.getEpochProofPublicInputs(1, 1, args, headers, data.batchedBlobInputs);
992+
}
993+
967994
function _submitEpochProof(
968995
uint256 _start,
969996
uint256 _end,
@@ -989,7 +1016,7 @@ contract RollupTest is RollupBase {
9891016
endArchive: _archive,
9901017
outHash: _outHash,
9911018
previousInboxRollingHash: 0,
992-
endInboxRollingHash: 0,
1019+
endInboxRollingHash: proposedHeaders[_end].inboxRollingHash,
9931020
proverId: _prover
9941021
});
9951022

l1-contracts/test/validator-selection/ValidatorSelection.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ contract ValidatorSelectionTest is ValidatorSelectionTestBase {
752752
endArchive: endFull.checkpoint.archive,
753753
outHash: endFull.checkpoint.header.outHash,
754754
previousInboxRollingHash: 0,
755-
endInboxRollingHash: 0,
755+
endInboxRollingHash: proposedHeaders[endCheckpointNumber].inboxRollingHash,
756756
proverId: prover
757757
});
758758

0 commit comments

Comments
 (0)