-
Notifications
You must be signed in to change notification settings - Fork 506
feat: Delayed signature verification during block proposals #15813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
12719d4
to
fe70dd3
Compare
There was a problem hiding this 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()) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this 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.
294c5ce
to
b060765
Compare
5448803
to
4e0a2bc
Compare
621950f
to
72f9d52
Compare
There was a problem hiding this 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); |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch
l1-contracts/test/Rollup.t.sol
Outdated
end: _end, | ||
args: args, | ||
fees: fees, | ||
attestations: CommitteeAttestations({signatureIndices: "", signaturesOrAddresses: ""}), // TODO(palla): Add unit tests with non-empty attestations |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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? |
There was a problem hiding this comment.
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 👀
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
87f2b1f
to
2b10c89
Compare
There was a problem hiding this 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: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch
There was a problem hiding this comment.
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 🎉
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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.
2b10c89
to
c35f762
Compare
There was a problem hiding this 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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uhh, a view 🙏
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
Implements AztecProtocol/engineering-designs#69
Median
propose
gas cost is321250
according tohappy.t.sol#test_100_val