Skip to content

Conversation

spalladino
Copy link
Contributor

@spalladino spalladino commented Jul 17, 2025

Implements AztecProtocol/engineering-designs#69

Median propose gas cost is 321250 according to happy.t.sol#test_100_val

@spalladino spalladino force-pushed the palla/delayed-sig-verification branch from 12719d4 to fe70dd3 Compare July 17, 2025 22:04
Copy link
Contributor

@LHerskind LHerskind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall think the direction is good, added some comments that are mostly minor or suggestions for future work.

The main important one is the missing invalidation case 👀

Epoch epoch = STFLib.getEpochForBlock(_endBlockNumber);

// Only verify attestations if they are not empty (for testing compatibility)
if (!_attestations.isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that the if is to be removed and always run later?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, removed, verify already takes care of skipping the check if the committee is empty.

);

// Get the stored block data
TempBlockLog memory blockLog = STFLib.getTempBlockLog(_blockNumber);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to load the entire thing into memory, a bunch of the values wont be used, you could use TempBlockLog storage blockLog.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I added an additional function to STFLib to get the block log as storage directly.

}

// Calculate required threshold (2/3 + 1)
uint256 requiredSignatures = (committeeSize << 1) / 3 + 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would probably keep the *2 just for readability here 😆 Think we can live with the 2 gas more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just copy-pasta from verify :-P

Copy link
Contributor

@LHerskind LHerskind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also want to update the preheatHeaders function in STFLib.sol to not pay a penalty.

@spalladino spalladino force-pushed the palla/delayed-sig-verification branch from 294c5ce to b060765 Compare July 18, 2025 20:06
@spalladino spalladino marked this pull request as ready for review July 18, 2025 22:13
@spalladino spalladino force-pushed the palla/delayed-sig-verification branch 3 times, most recently from 5448803 to 4e0a2bc Compare July 22, 2025 01:03
@spalladino spalladino added the ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure label Jul 22, 2025
@spalladino spalladino force-pushed the palla/delayed-sig-verification branch 2 times, most recently from 621950f to 72f9d52 Compare July 24, 2025 20:10
Copy link
Contributor

@LHerskind LHerskind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think I found a way to have honest committees and provers but still prune due to the changes in verify 👀.

Slot slot = _args.header.slotNumber;
Epoch epoch = slot.epochFromSlot();
ValidatorSelectionLib.verify(slot, epoch, _attestations, _args.digest);
ValidatorSelectionLib.verifyProposer(slot, epoch, _attestations, _signers, _args.digest);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the proposer not already validated during the verify in here, so why the separate call to verifyProposer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the proposer verification from verify above (which I renamed to verifyAttestations)

require(
stack.proposerVerified || proposer == msg.sender,
Errors.ValidatorSelection__InvalidProposer(proposer, msg.sender)
stack.proposerVerified, Errors.ValidatorSelection__InvalidProposer(proposer, address(0))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduce a very strange edge case, or maybe I am misunderstanding again 😬.

Because the verify is applied at the time of proof submission, but the verifyProposer is done at proposer. It is possible to pass 33 attestations that do not include the attester, but have the attester transmit the message. It would pass the verifyProposer check in propose BUT it would fail the verify. At the same time, it cannot be invalidated.

I think this check should actually be removed. We don't care for the validity if the proposer actually attested just that there are enough. We do however care at the time of propose that he controlled it (so that check is good).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this check


// Reset the pending block number to remove this block and all subsequent blocks
RollupStore storage rollupStore = STFLib.getStorage();
rollupStore.tips = rollupStore.tips.updatePendingBlockNumber(_blockNumber - 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are doing this write twice, first here and then in the _invalidateBlock below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

end: _end,
args: args,
fees: fees,
attestations: CommitteeAttestations({signatureIndices: "", signaturesOrAddresses: ""}), // TODO(palla): Add unit tests with non-empty attestations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue, or part of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realized I never pushed the commit Add test for submitEpochProof

// a block if you submit one with no signatures. This was a change from prior behavior where we had had
// that if there were zero validators in a rollup, anyone could build a block

// TODO(palla): What should we do in this scenario? Block the proposal, or allow invalidating later?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The committee is still the same (why it was revert=true before), so as long as that committee is the active committee you kinda gotta wait. As it could also just be a partial move, you are in this weird state where the chain is degraded.
I believe you will also have a very similar thing going on at the new rollup, because of the delayed reads (2 epochs) the first two epochs after attesters were added there won't be much to do 👀

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily for this PR, but we gotta clean those up and split it better, can be hard to follow in here. Some of the extra things for invalidation make it a bit harder as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, there's also a lot of duplicate code across test helpers

@spalladino spalladino force-pushed the palla/delayed-sig-verification branch from 87f2b1f to 2b10c89 Compare July 25, 2025 21:31
Copy link
Collaborator

@just-mitch just-mitch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me code-wise! We definitely need to update the design to just have the cleaned up flows with their proper names.

}

/**
* @notice Propose a pending block from the point-of-view of sequencer selection. Will:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should really be updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateHeader can be restricted to view now the linter is telling me 🎉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! We no longer sample validators in there

}

function validateHeader(ValidateHeaderArgs calldata _args) external {
function validateHeaderWithAttestations(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to document the overall flows here of who calls what, when. Perhaps the design doc should be updated with the implementation and expected flows.

@spalladino spalladino force-pushed the palla/delayed-sig-verification branch from 2b10c89 to c35f762 Compare July 28, 2025 12:56
@spalladino spalladino enabled auto-merge July 28, 2025 12:56
Copy link
Contributor

@LHerskind LHerskind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing it 👍


// @note: not view as sampling validators uses tstore
function validateHeader(ValidateHeaderArgs memory _args) internal {
function validateHeader(ValidateHeaderArgs memory _args) internal view {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhh, a view 🙏

@spalladino spalladino added this pull request to the merge queue Jul 28, 2025
Merged via the queue into next with commit cbaa077 Jul 28, 2025
4 checks passed
@spalladino spalladino deleted the palla/delayed-sig-verification branch July 28, 2025 20:09
github-merge-queue bot pushed a commit that referenced this pull request Jul 29, 2025
Archiver now checks committee attestations and refuses to sync a block
if it does not pass validation.

Note that this addresses scenarios where the proposer is malicious, but
does not handle cases where the entire committee is and produces
signatures for a block with an unattested parent. That'll be left for a
future PR.

Builds on #15813
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants