-
Notifications
You must be signed in to change notification settings - Fork 15
dd: Optimistic attestation signature validation #69
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
base: main
Are you sure you want to change the base?
Conversation
0938b8c
to
c83a23b
Compare
|
||
It's unclear to me whether this may lead to situations where proposers purposefully omit attestations for a block, knowing that this gets "patched" in the following one. This doesn't seem to be the case if the attestation committee refuses to sign off N+1 given the lack of attestations on L1 for N, but I still wanted to flag it. | ||
|
||
The open question remains on whether L2 nodes should accept blocks N and N+1 in the example above, or wait until their epoch gets proven. For simplicity, I'd push for only accepting such blocks once they get proven. |
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.
Rephrasing the discussion here as "should proposers build on top of (potentially valid) blocks that have no attestations?"
This is equivalent to - borrowing your terminology - taking off the training wheels provided by the attestations. If we don't trust our proving system, wouldn't we then impose the requirement that attestations are posted? If we don't, then the equilibria is that no one posts any attestations except for the last proposer who must post them (or just make them available to the prover - depending on design).
If this is an acceptable outcome then probably better to remove attestations altogether.
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.
Rephrasing the discussion here as "should proposers build on top of (potentially valid) blocks that have no attestations?"
I agree that the answer to that discussion should be "no". But we still need to consider what should a node do if, for whatever reason, this happens (since it is a valid state in which the system can be).
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 could allow the proposer N+1 to collect "prune" votes from the validator committee if you are worried about providing a KZG inclusion proof for the signatures?
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-CTra1n I assume you're referring here to moving attestations to blobs, which is a separate discussion? Moving stuff to blobs increases complexity, yes, not just for providing the KZG proof but also because it changes our blob layout, which impacts other circuits. Still, if we decided to go down that route, I think that the KZG proof is easier than collecting votes.
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.
Oh, I just thought this was for general discussion on the big doc you wrote. My bad.
Ok so your current angle is posting the sigs to calldata, not blobs, but not verifying. Seems like a non negligible cost given we would expect the unhappy case of reverifying the sigs to almost never get called. You have better line of sight on the circuit changes necessary though.
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 have better line of sight on the circuit changes necessary though
I don't, I'll have to bring this up with @iAmMichaelConnor heh
c83a23b
to
8b27682
Compare
8b27682
to
0432a0e
Compare
|
||
### `submitProof` | ||
|
||
In addition to verifying the rollup validity proof, `submitProof` also needs to check the validity of attestations in the last block in the epoch. |
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.
Why?
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.
See "Proving" and "How do provers verify the attestations from the last block in the epoch" above
let committeeCommitment = getCommitteeCommitmentAtSlot(slotNumber) | ||
|
||
let digest = block.proposalDigest | ||
let recoveredCommittee = [ecrecover(attestation, digest) if attestation is signature else attestation for attestation in 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.
I don't follow the if attestation is signature else attestation
. What is an attestation
if not a signature
?
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.
I've renamed it to attestationsOrAddresses
. In its current implementation, the attestations array at position i
contains either the signature from committee[i]
, or the address from committee[i]
(if committee member i
did not attest). This means that the correctness of attestation is checked by hashing together all committee addresses as provided via the attestationsOrAddresses
argument, and comparing against the stored committee commitment.
|
||
Two options remain: posting them to calldata or to blobs. The flow in both cases is similar: proposers post attestations in either of them, and store in L1 a commitment to them (we can also modify the block hash to include a commitment to a set of attestations, to avoid an extra `SSTORE`, but this is a larger change). On block proposal, we check that the hash corresponds to the data posted. On (in)validation, the caller re-posts the attestations to L1, which get re-hashed and compared against the stored commitment, and then verified. | ||
|
||
Calldata for a 48-sized committee is `(48 * 2/3 * 65) + (48 * 1/3 * 20) = 2400` bytes, or `38400` gas (note that after [EIP7623](https://eips.ethereum.org/EIPS/eip-7623) this could shoot up to `96k` depending on execution gas). This can be saved in favor of moving the attestations to blocks. |
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.
That EIP7623 went live in pectra, so we're living in a 96K world now.
Also, now that we have EIP2537, we might should revisit BLS. @LHerskind had calculated that it was quite more expensive to use BLS with verification at a committee of 48, but if in the happy path, all we pay for is ~100B calldata and storing ~4 words (which can be in a roundabout so basically free), plus some new complexity in validator registration and setting up an epoch, it could be worth it.
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.
That's really really interesting
|
||
Alternatively, we can keep this method open for anyone to call. Assuming there is no reward for this action, we expect only block proposers to actually call it. Should we introduce a gas rebate and reward, we could end up with multiple nodes racing to claim this reward. | ||
|
||
Given the incentives, I suggest keeping the method open and with no rewards. And to minimize complexity, I suggest no gas rebates at all. |
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.
I have doubts about this approach.
Users' tx fees won't allow for the L1 cost of a fraud demonstration.
In a bull market, with high L1 demand, the Proposer could find the costs of a fraud demonstration to be significant. They (and subsequent proposerts) might be disincentivised from submitting a proposal altogether. This subsection hasn't convinced me that it won't be a problem, so perhaps it'll need modelling.
Half-baked idea:
In the early life of the network, whilst proposers are given block rewards (rather than burning tokens), perhaps the good proposer could be given the bad proposer's block reward. But again, this would need to be modelled. And it doesn't work once rewards go negative.
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.
Eventually, the offending proposer needs to be slashed to cover the cost of the honest proposer trying to fork out the offending block: this is an objective (slashable) offense. To start though, tuning block rewards to cover any need to fork a bad block should be fine
- @aminsammara or @joeandrews | ||
- @PhilWindle | ||
|
||
## Summary |
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 summary and title alludes to going full optimistic. I really don't like the fully optimistic approach. In my view, it opens up wide for certain attacks that we don't have with delayed verification and support for early challenges. I would avoid saying optimistic unless it is optimistic, otherwise it is just confusing.
|
||
Given the tradeoffs in security, I push for the second option. | ||
|
||
### Who can invalidate blocks, and what is the incentive? |
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.
If it is required to verify attestations at the time of proof submission, there is automatically a method of invalidation there - though a slow and nasty one.
For the early challenge, I agree going with no reward or rebate for the short term. Longer term it is something that can be covered in a slash.
|
||
Two options remain: posting them to calldata or to blobs. The flow in both cases is similar: proposers post attestations in either of them, and store in L1 a commitment to them (we can also modify the block hash to include a commitment to a set of attestations, to avoid an extra `SSTORE`, but this is a larger change). On block proposal, we check that the hash corresponds to the data posted. On (in)validation, the caller re-posts the attestations to L1, which get re-hashed and compared against the stored commitment, and then verified. | ||
|
||
Calldata for a 48-sized committee is `(48 * 2/3 * 65) + (48 * 1/3 * 20) = 2400` bytes, or `38400` gas (note that after [EIP7623](https://eips.ethereum.org/EIPS/eip-7623) this could shoot up to `96k` depending on execution gas). This can be saved in favor of moving the attestations to blobs. |
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.
I don't think we would really hit the 96K
as it would require that the rest was below those 🤷. Also something where version of "chunking" can influence to the point where the extra complexity of blobs here might just not be worth it.
require(committeeCommitment == hash(committee)) | ||
|
||
let digest = block.proposalDigest | ||
require(ecrecover(attestations[invalidIndex], digest) !== committee[invalidIndex]) |
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.
Need to make sure that the attestation checked is actually provided, as it would be a bit of a mess if possible to invalidate because you point out that one of the 15 "excess" values was not provided.
storage.tips = storage.tips.updatePendingBlockNumber(blockNumber - 1) | ||
``` | ||
|
||
Considering we need to do only a single `ECRECOVER`, we can estimate the gas for this operation to be `3000 * 31 = 93k` gas less than the current proposal validation, plus `4200` for the two SLOAD operations (`attestationsHash` and `committeeCommitment`), and the 38400 gas for calldata. This results in `160k - 93k + 4.2k + 38k = 110k` gas. Note that this function should hardly ever be called. |
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.
As I recall we need > 2/3, so it it would be 32 less, as we usually do 33.
|
||
Assuming our circuits and proving systems are sound, a prover can post a proof for a given epoch without having to verify any attestation, which is enough for convincing L1 of the correctness of the proven state root. However, if we were to do this, we lose the training wheels provided by the economic security of the attestation committee, in the event of a bug in proving. | ||
|
||
It follows that we want attestations to be verified. And as mentioned above, we know that verifying the attestations for the last block in an epoch is equivalent to verifying them for every block _in the epoch_, since every block in the epoch is attested by the same committee members, so the total stake is the same. So we should demand provers to verify the attestations of the last block in the epoch when they upload a proof. |
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.
Based on messages seen in slack, we will need to improve clarity of this section to better convey that we are verifying the attestations on L1, and not just having the provers do it locally for themselves.
Implements AztecProtocol/engineering-designs#69 Median `propose` gas cost is `321250` according to `happy.t.sol#test_100_val`
No description provided.