-
Notifications
You must be signed in to change notification settings - Fork 28
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
feat: blob staleness check #226
base: main
Are you sure you want to change the base?
Conversation
We add a check to discard blobs that have been included in the rollup's batcher inbox a long time after it was included in an EigenDA batch by the eigenda disperser.
…om GET queries is correctly parsed
Made the Verifier take an interface for the certVerifier instead of the struct, to allow me to test functionality that didn't depend on having a blockchain connection (like the certVerifier does). This way we can test the blob staleness check by using a noopCertVerifier to skip all the other verification that require an eth rpc.
// The eigenDA batch header contains a reference block number (RBN) which is used to pin the stake of the eigenda operators at that specific blocks. | ||
// The rollup batch containing the eigenDA cert is only valid if it was included within a certain number of blocks after the RBN. | ||
// validity condition is: RBN < l1_inclusion_block_number < RBN + some_delta | ||
RollupL1InclusionBlockNum int64 |
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.
shouldn't this be uint64
?
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'm using -1 to mean that this query param was not passed, and hence to skip the test. Could using a pointer to uint64 instead and use nil to mean this? I don't like either tbh... but go is dumb.
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.
Can't you use 0
to indicate "skip the test"?
RollupL1InclusionBlockNum
must be strictly greater RBN, and the min value of RBN is 0
, therefore RollupL1InclusionBlockNum == 0
isn't sensical, and is free realestate to have special meaning
// to ensure disperser returned fields haven't been tampered with | ||
type CertVerifier struct { | ||
type CertVerification struct { |
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.
knit - any reason to make this public if we're binding to an interface?
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 really, I can change. Don't think using an interface matters here though, it's more about whether we think people will want to import and use this struct elsewhere.
// If batch.RBN + rollupBlobInclusionWindow < cert.L1InclusionBlock, the batch is considered stale and verification will fail. | ||
// This check is optional and will be skipped when rollupBlobInclusionWindow is set to 0. | ||
// Note: if there are more rollup related properties that we need to check in the future, then maybe create a RollupVerifier struct | ||
rollupBlobInclusionWindow uint32 |
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.
wouldn't this need to be globally set per the rollup? Otherwise couldn't you have multiple verifiers articulating alternative views due to different values?
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.
Yes that's a good point. I wrote the code out but now's the time to discuss how we want to handle this feature. cc @bxue-l2 for discussion. I'll at least add a bit WARNING comment to the flag, saying that the feature is experimental and not really meant to be turned on? But then what's our plan for eventually turning it on?
// 1 - verify batch in the cert is confirmed onchain | ||
err := v.cv.verifyBatchConfirmedOnChain(ctx, cert.Proof().GetBatchId(), cert.Proof().GetBatchMetadata()) | ||
if err != nil { | ||
return fmt.Errorf("failed to verify batch: %w", err) | ||
} | ||
|
||
// 2 - verify merkle inclusion proof | ||
// 2 - verify that the blob cert was submitted to the rollup's batch inbox within the allowed RollupBlobInclusionWindow window. | ||
// This is to prevent timing attacks where a rollup batcher could try to game the fraud proof window by including an old DA blob that is about to expire on the DA layer |
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.
how will this verification look when one step proving the certificate? ie, will a challenger need to provide the inbox confirmation tx block #, if so then how do they do so in a way that isn't tamper resistant?
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.
Right precisely the right question to ask. Discussed this at length with Bowen. I'll need to add comments detailing this.
The 2 options for securing this check is:
- do it in derivation pipeline
- do it in preimage oracle contract (as you mention, this would require a zk proof that the inclusion block # is correct)
We are opting for 1, to do this check in the derivation pipeline. It's a simple check to make in the rust hokulea client, so we'll add it there. Ideally we'd add the same check to the golang derivation pipeline, but the problem is that we haven't had much success lately getting our PRs upstreamed. Also if ever a problem happens and we need to change the logic in the golang binary then we depend on optimism's team to review and merge our PR, which feels less than ideal (this is different from the rust code b/c in rust we have our own Hokulea library which projects using eigenda can import directly, and which we have full control over).
So the idea is that the rust stack will be a requirement to do the fraud proofs. But we still want normal validators running op-node to be able to follow the chain properly, which is why we are adding this check here in the proxy.
cc @bxue-l2 for any more thoughts.
// | ||
// Generated by this command: | ||
// | ||
// mockgen -package mocks --destination ../mocks/manager.go . IManager | ||
// |
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.
There's also make go-gen-mocks
// This is optional, and should be set to -1 to mean to not verify the reference block number distance check. | ||
// | ||
// Used to determine the validity of the eigenDA batch. | ||
// The eigenDA batch header contains a reference block number (RBN) which is used to pin the stake of the eigenda operators at that specific 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.
"at that specific blocks" -> "at that specific block" ?
if !(blockNumEigenDA < blockNumRollup) { | ||
return fmt.Errorf("eigenda batch reference block number (%d) needs to be < rollup inclusion block number (%d)", blockNumEigenDA, blockNumRollup) | ||
} | ||
if !(blockNumRollup < blockNumEigenDA+int64(v.rollupBlobInclusionWindow)) { |
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 believe you have an off-by-one error here
The comment in the config struct above states that the following constitutes a stale batch:
batch.RBN + RollupBlobInclusionWindow < cert.L1InclusionBlock
but it seems what's actually implemented is this:
batch.RBN + RollupBlobInclusionWindow <= cert.L1InclusionBlock
It might help to have consistent variable names between the two.
blockNumRollup := args.RollupL1InclusionBlockNum | ||
// We need blockNumEigenDA < blockNumRollup < blockNumEigenDA + rollupBlobInclusionWindow | ||
if !(blockNumEigenDA < blockNumRollup) { | ||
return fmt.Errorf("eigenda batch reference block number (%d) needs to be < rollup inclusion block number (%d)", blockNumEigenDA, blockNumRollup) |
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.
Suggest adding a test case which covers this error, since the conditional is non trivial
We add a check to discard blobs that have been included in the rollup's batcher inbox a long time after it was included in an EigenDA batch by the eigenda disperser.
It is now expected that GET requests to the proxy can include an optional
l1_inclusion_block_number
query argument, which will cause a blob to be rejected if it was included onchain >RollupBlobInclusionWindow
L1 blocks after the blob's batch's RBN.The
RollupBlobInclusionWindow
is a flag added to the proxy binary, which is set to 0 by default, which skips the check.Fixes Issue
Fixes #
Changes proposed
Screenshots (Optional)
Note to reviewers
Commits are: