Skip to content

Commit 91ad7df

Browse files
committed
chore: update
1 parent 8f39d42 commit 91ad7df

File tree

3 files changed

+82
-2
lines changed

3 files changed

+82
-2
lines changed

pkg/bindings/OperatorTableUpdater/binding.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/contracts/multichain/OperatorTableUpdater.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ contract OperatorTableUpdater is
8686
uint32 referenceTimestamp,
8787
uint32 referenceBlockNumber
8888
) external onlyWhenNotPaused(PAUSED_GLOBAL_ROOT_UPDATE) nonReentrant {
89+
// Silently return if the `globalTableRoot` is already confirmed
90+
// We do this to avoid race conditions with the offchain transport of the operator table
91+
if (_isRootValid[globalTableRoot]) {
92+
return;
93+
}
94+
8995
// Table roots can only be updated for current or past timestamps and after the latest reference timestamp
9096
require(referenceTimestamp <= block.timestamp, GlobalTableRootInFuture());
9197
require(referenceTimestamp > _latestReferenceTimestamp, GlobalTableRootStale());

src/test/unit/OperatorTableUpdaterUnit.t.sol

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,80 @@ contract OperatorTableUpdaterUnitTests_confirmGlobalTableRoot is OperatorTableUp
303303
assertEq(operatorTableUpdater.getReferenceBlockNumberByTimestamp(referenceTimestamp), referenceBlockNumber);
304304
assertEq(operatorTableUpdater.getReferenceTimestampByBlockNumber(referenceBlockNumber), referenceTimestamp);
305305
}
306+
307+
function testFuzz_silentReturn_alreadyConfirmed(Randomness r) public rand(r) {
308+
uint32 referenceTimestamp = r.Uint32(operatorTableUpdater.getLatestReferenceTimestamp() + 1, type(uint32).max);
309+
uint32 referenceBlockNumber = r.Uint32();
310+
cheats.warp(uint(referenceTimestamp));
311+
bytes32 globalTableRoot = bytes32(r.Uint256(1, type(uint).max));
312+
mockCertificate.messageHash =
313+
operatorTableUpdater.getGlobalTableUpdateMessageHash(globalTableRoot, referenceTimestamp, referenceBlockNumber);
314+
_setIsValidCertificate(mockCertificate, true);
315+
316+
// First confirmation should succeed and emit event
317+
operatorTableUpdater.confirmGlobalTableRoot(mockCertificate, globalTableRoot, referenceTimestamp, referenceBlockNumber);
318+
319+
// Verify the root is now valid
320+
assertTrue(operatorTableUpdater.isRootValid(globalTableRoot), "Global table root should be valid after first confirmation");
321+
322+
// Store initial state to verify it doesn't change
323+
bytes32 initialCurrentRoot = operatorTableUpdater.getCurrentGlobalTableRoot();
324+
uint32 initialLatestTimestamp = operatorTableUpdater.getLatestReferenceTimestamp();
325+
uint32 initialLatestBlockNumber = operatorTableUpdater.getLatestReferenceBlockNumber();
326+
327+
// Second confirmation with the same root should silently return without emitting events
328+
// We should NOT expect any events to be emitted
329+
cheats.recordLogs();
330+
operatorTableUpdater.confirmGlobalTableRoot(mockCertificate, globalTableRoot, referenceTimestamp, referenceBlockNumber);
331+
332+
// Verify no events were emitted on the second call
333+
Vm.Log[] memory logs = cheats.getRecordedLogs();
334+
assertEq(logs.length, 0, "No events should be emitted when confirming an already valid root");
335+
336+
// Verify state remains unchanged after silent return
337+
assertEq(operatorTableUpdater.getCurrentGlobalTableRoot(), initialCurrentRoot, "Current root should remain unchanged");
338+
assertEq(operatorTableUpdater.getLatestReferenceTimestamp(), initialLatestTimestamp, "Latest timestamp should remain unchanged");
339+
assertEq(
340+
operatorTableUpdater.getLatestReferenceBlockNumber(), initialLatestBlockNumber, "Latest block number should remain unchanged"
341+
);
342+
assertTrue(operatorTableUpdater.isRootValid(globalTableRoot), "Global table root should still be valid");
343+
}
344+
345+
function test_silentReturn_alreadyConfirmed_differentCertificate() public {
346+
uint32 referenceTimestamp = uint32(block.timestamp);
347+
uint32 referenceBlockNumber = uint32(block.number);
348+
bytes32 globalTableRoot = bytes32(uint(12_345));
349+
350+
// First certificate
351+
BN254Certificate memory firstCertificate;
352+
firstCertificate.referenceTimestamp = 1; // GENERATOR_REFERENCE_TIMESTAMP
353+
firstCertificate.messageHash =
354+
operatorTableUpdater.getGlobalTableUpdateMessageHash(globalTableRoot, referenceTimestamp, referenceBlockNumber);
355+
_setIsValidCertificate(firstCertificate, true);
356+
357+
// Confirm with first certificate
358+
cheats.expectEmit(true, true, true, true);
359+
emit NewGlobalTableRoot(referenceTimestamp, globalTableRoot);
360+
operatorTableUpdater.confirmGlobalTableRoot(firstCertificate, globalTableRoot, referenceTimestamp, referenceBlockNumber);
361+
362+
// Create a different certificate for the same global table root
363+
BN254Certificate memory secondCertificate;
364+
secondCertificate.referenceTimestamp = 1; // GENERATOR_REFERENCE_TIMESTAMP
365+
secondCertificate.messageHash = firstCertificate.messageHash; // Same message hash
366+
secondCertificate.signature = BN254.G1Point({X: 999, Y: 888}); // Different signature to make it a different certificate
367+
_setIsValidCertificate(secondCertificate, true);
368+
369+
// Second confirmation with different certificate but same root should silently return
370+
cheats.recordLogs();
371+
operatorTableUpdater.confirmGlobalTableRoot(secondCertificate, globalTableRoot, referenceTimestamp, referenceBlockNumber);
372+
373+
// Verify no events were emitted
374+
Vm.Log[] memory logs = cheats.getRecordedLogs();
375+
assertEq(logs.length, 0, "No events should be emitted when confirming an already valid root with different certificate");
376+
377+
// Verify the root is still valid
378+
assertTrue(operatorTableUpdater.isRootValid(globalTableRoot), "Global table root should still be valid");
379+
}
306380
}
307381

308382
contract OperatorTableUpdaterUnitTests_updateOperatorTable_BN254 is OperatorTableUpdaterUnitTests {

0 commit comments

Comments
 (0)