-
Notifications
You must be signed in to change notification settings - Fork 0
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
Redundant write on EigenDA failure #1
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThe changes introduced in this pull request enhance the error handling and logging capabilities of the Changes
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
66f7f91
to
1f3368d
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.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (1)
store/router.go (1)
212-218
: Avoid reassigning 'key' variable to improve readabilityReassigning the
key
variable in line 216 may lead to confusion and potential errors. Consider using a new variable, such asactualKey
, to enhance code clarity and maintain the integrity of the originalkey
.Apply this diff to enhance readability:
key := crypto.Keccak256(commitment) // check if key is an RLP encoded certificate, if not, assume it's an s3 key var cert verify.Certificate err := rlp.DecodeBytes(key, &cert) if err != nil { - key = commitment + actualKey := commitment + } else { + actualKey := key } for _, src := range sources { - data, err := src.Get(ctx, key) + data, err := src.Get(ctx, actualKey) if err != nil { r.log.Warn("Failed to read from redundant target", "backend", src.BackendType(), "err", err) continue }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- store/router.go (3 hunks)
🔇 Additional comments (1)
store/router.go (1)
9-10
: Add necessary imports for 'verify' and 'rlp' packagesThe added imports for the
verify
andrlp
packages are appropriate and necessary for the new functionality introduced in the code.
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.
LGTM, if this is the only change we need to maintain this approach seems very good to me
efb3828
to
0a84c64
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.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- store/router.go (3 hunks)
🔇 Additional comments (1)
store/router.go (1)
136-146
: Use 'r.log.Error' instead of 'log.Error' for consistent loggingAs noted in previous reviews, it's recommended to use the router's logger
r.log.Error
instead of the globallog.Error
to maintain consistent logging and proper configuration.
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.
we need to test our new behavior in either server/server_test.go
or a separate test file with cases where writes to EigenDA fails, we can still recover data from redundant stores
store/router.go
Outdated
// write to EigenDA failed, which shouldn't happen if the backend is functioning properly | ||
// use the payload as the key to avoid data loss | ||
if r.cacheEnabled() || r.fallbackEnabled() { | ||
redundantErr := r.handleRedundantWrites(ctx, value, value) |
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.
add a comment to clarify why value
is passed in here?
First value
is used instead of EigenDA's commitment
and it'll be keccak'ed as key in redundant writes.
store/router.go
Outdated
// check if key is an RLP encoded certificate, if not, assume it's an s3 key | ||
var cert verify.Certificate | ||
err := rlp.DecodeBytes(commitment, &cert) | ||
if err != nil { | ||
key = commitment | ||
} | ||
|
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 change verify logic on line 233. When key == commitment
(ie. EigneDA failes), verify that keccak256(data) == key
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.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- store/router.go (4 hunks)
🧰 Additional context used
🔇 Additional comments (7)
store/router.go (7)
6-6
: Import 'bytes' package is necessary for byte comparisons.The addition of the
"bytes"
package is appropriate as it is used for byte slice comparisons in the code (e.g.,bytes.Equal()
function).
12-14
: Added imports for 'verify' and 'rlp' packages are appropriate.The imports of
"github.com/Layr-Labs/eigenda-proxy/verify"
and"github.com/ethereum/go-ethereum/rlp"
are necessary for certificate verification and RLP decoding introduced in the code changes.
137-147
: Use 'r.log.Error' instead of 'log.Error' for consistent logging.In lines 137 and 143, the global
log.Error
is used directly. To maintain consistency and ensure proper logging configuration, consider using the router's loggerr.log.Error
instead.
141-141
: Clarify the use of 'value' as the key in redundant writes.It would be helpful to add a comment explaining why
value
is used as the key in thehandleRedundantWrites
function when writing to redundant backends. This can improve code readability and maintainability.
214-220
: Ensure key assignment logic is clear after RLP decoding.In the
multiSourceRead
function, when decodingcommitment
as an RLP-encoded certificate, if decoding fails,key
is set tocommitment
. For better readability and to avoid confusion, consider making the key assignment explicit in both cases.
233-239
: Verify data integrity when EigenDA verification is not applicable.In lines 233-239, when
bytes.Equal(key, commitment)
istrue
, the code verifies the data usingsrc.Verify(commitment, data)
. However, whenbytes.Equal
returnsfalse
, it usesr.eigenda.Verify(commitment, data)
. According to a prior review comment, we need to adjust the verification logic whenkey == commitment
(i.e., EigenDA fails) to ensure thatkeccak256(data) == key
.Please verify that the implementation correctly handles the data verification in both cases to maintain data integrity.
141-141
: Ensure the correct key is used when writing to redundant backends.In the call to
handleRedundantWrites
:redundantErr := r.handleRedundantWrites(ctx, value, value)Passing
value
as bothcommitment
andvalue
may cause inconsistency in how keys are computed in redundant backends. Ensure that the key used in redundant writes aligns with the expected key computation method in those backends.Run the following script to check if redundant backends expect the key to be
crypto.Keccak256(value)
:
store/router.go
Outdated
|
||
// check if key is an RLP encoded certificate, if not, assume it's an s3 key | ||
var cert verify.Certificate | ||
err := rlp.DecodeBytes(commitment, &cert) | ||
if err != nil { | ||
key = commitment | ||
} | ||
|
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.
🛠️ Refactor suggestion
Consider handling the case where RLP decoding succeeds.
In the multiSourceRead
function, after decoding commitment
as an RLP-encoded certificate:
var cert verify.Certificate
err := rlp.DecodeBytes(commitment, &cert)
if err != nil {
key = commitment
}
If RLP decoding succeeds (err == nil
), key
remains as crypto.Keccak256(commitment)
. For clarity, consider adding an else
clause to make the key assignment explicit, and possibly add comments explaining the logic.
Apply this diff for improved clarity:
key := crypto.Keccak256(commitment)
// Check if key is an RLP encoded certificate; if not, assume it's an S3 key
var cert verify.Certificate
err := rlp.DecodeBytes(commitment, &cert)
if err != nil {
key = commitment
+} else {
+ // If decoding succeeds, key remains as crypto.Keccak256(commitment)
+ // No action needed; consider adding a comment if necessary
}
store/router.go
Outdated
if bytes.Equal(key, commitment) { | ||
err = src.Verify(commitment, data) | ||
} else { | ||
// verify cert:data using EigenDA verification checks | ||
err = r.eigenda.Verify(commitment, data) | ||
} | ||
|
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.
Adjust data verification logic based on key equality.
In the verification step:
if bytes.Equal(key, commitment) {
err = src.Verify(commitment, data)
} else {
// Verify cert:data using EigenDA verification checks
err = r.eigenda.Verify(commitment, data)
}
Ensure that when key == commitment
(indicating EigenDA has failed and we're using a fallback), the verification correctly checks that keccak256(data) == key
. Currently, src.Verify
may not perform this check as expected.
Would you like assistance in updating the verification logic to ensure data integrity when using redundant backends? I can help draft the necessary code changes or open a GitHub issue to track this task.
4dfb545
to
bb354f7
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.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- store/router.go (4 hunks)
🧰 Additional context used
🔇 Additional comments (3)
store/router.go (3)
12-13
: LGTM: New imports added for enhanced functionality.The new import statements for
verify
andrlp
packages are correctly added and necessary for the new functionality in themultiSourceRead
method.
Line range hint
1-339
: Overall assessment: Good improvements with some areas for refinement.The changes in this file generally improve error handling, add flexibility in key handling, and enhance the robustness of the
Router
implementation. Key improvements include:
- Better error logging and fallback mechanisms in the
Put
method.- Flexible handling of RLP encoded certificates in
multiSourceRead
.- Differentiated verification logic based on key types.
However, there are a few areas that could benefit from further refinement:
- Ensuring non-nil error returns in edge cases.
- Clarifying the logic for key assignment when handling RLP encoded certificates.
- Verifying data integrity correctly when using fallback keys.
Addressing these points will further enhance the reliability and clarity of the code.
233-239
:⚠️ Potential issueVerify data integrity for different key types, but consider edge case.
The new verification logic differentiates between different key types, which is good. However, there's a potential issue:
When
key == commitment
(indicating EigenDA has failed and we're using a fallback), the verification should check thatkeccak256(data) == key
. The currentsrc.Verify(commitment, data)
may not perform this check as expected.Consider updating the verification logic:
if bytes.Equal(key, commitment) { - err = src.Verify(commitment, data) + if !bytes.Equal(crypto.Keccak256(data), key) { + err = errors.New("data integrity check failed for fallback key") + } } else { // verify cert:data using EigenDA verification checks err = r.eigenda.Verify(commitment, data) }This ensures proper data integrity verification when using fallback keys.
To confirm this behavior, we can run the following script:
This will help us understand how
src.Verify
is implemented across different store types and ensure our suggested change is necessary and correct.
store/router.go
Outdated
// check if key is an RLP encoded certificate, if not, assume it's an s3 key | ||
var cert verify.Certificate | ||
err := rlp.DecodeBytes(commitment, &cert) | ||
if err != nil { | ||
key = commitment | ||
} | ||
|
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.
🛠️ Refactor suggestion
Good addition for handling RLP encoded certificates.
The new code to handle RLP encoded certificates adds flexibility in key handling. However, the logic could be made more explicit:
Consider clarifying the key assignment in both cases:
key := crypto.Keccak256(commitment)
// check if key is an RLP encoded certificate, if not, assume it's an s3 key
var cert verify.Certificate
err := rlp.DecodeBytes(commitment, &cert)
if err != nil {
key = commitment
+} else {
+ // Decoding succeeded, key remains as Keccak256 hash
+ // No action needed, but consider adding a comment for clarity
}
This makes the logic more explicit and easier to understand at a glance.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// check if key is an RLP encoded certificate, if not, assume it's an s3 key | |
var cert verify.Certificate | |
err := rlp.DecodeBytes(commitment, &cert) | |
if err != nil { | |
key = commitment | |
} | |
key := crypto.Keccak256(commitment) | |
// check if key is an RLP encoded certificate, if not, assume it's an s3 key | |
var cert verify.Certificate | |
err := rlp.DecodeBytes(commitment, &cert) | |
if err != nil { | |
key = commitment | |
} else { | |
// Decoding succeeded, key remains as Keccak256 hash | |
// No action needed, but consider adding a comment for clarity | |
} |
store/router.go
Outdated
log.Error("Failed to write to EigenDA backend", "err", err) | ||
// write to EigenDA failed, which shouldn't happen if the backend is functioning properly | ||
// use the payload as the key to avoid data loss | ||
if r.cacheEnabled() || r.fallbackEnabled() { | ||
redundantErr := r.handleRedundantWrites(ctx, value, value) | ||
if redundantErr != nil { | ||
log.Error("Failed to write to redundant backends", "err", redundantErr) | ||
return nil, redundantErr | ||
} | ||
return crypto.Keccak256(value), nil | ||
} |
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.
Improved error handling, but consider edge case.
The new error handling and logging for EigenDA write failures is a good addition. The fallback mechanism to write to redundant backends enhances data reliability.
However, there's a potential issue:
If both caching and fallbacks are disabled (!r.cacheEnabled() && !r.fallbackEnabled()
), and the EigenDA write fails, the function will return nil, err
. There's a risk that err
might be nil
here, which could lead to silent failures.
Consider adding a check to ensure err
is not nil
before returning:
if r.cacheEnabled() || r.fallbackEnabled() {
redundantErr := r.handleRedundantWrites(ctx, value, value)
if redundantErr != nil {
log.Error("Failed to write to redundant backends", "err", redundantErr)
return nil, redundantErr
}
return crypto.Keccak256(value), nil
}
+ if err == nil {
+ err = errors.New("failed to write to EigenDA and no redundant backends available")
+ }
return nil, err
This ensures that an error is always returned when the write operation fails.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
log.Error("Failed to write to EigenDA backend", "err", err) | |
// write to EigenDA failed, which shouldn't happen if the backend is functioning properly | |
// use the payload as the key to avoid data loss | |
if r.cacheEnabled() || r.fallbackEnabled() { | |
redundantErr := r.handleRedundantWrites(ctx, value, value) | |
if redundantErr != nil { | |
log.Error("Failed to write to redundant backends", "err", redundantErr) | |
return nil, redundantErr | |
} | |
return crypto.Keccak256(value), nil | |
} | |
log.Error("Failed to write to EigenDA backend", "err", err) | |
// write to EigenDA failed, which shouldn't happen if the backend is functioning properly | |
// use the payload as the key to avoid data loss | |
if r.cacheEnabled() || r.fallbackEnabled() { | |
redundantErr := r.handleRedundantWrites(ctx, value, value) | |
if redundantErr != nil { | |
log.Error("Failed to write to redundant backends", "err", redundantErr) | |
return nil, redundantErr | |
} | |
return crypto.Keccak256(value), nil | |
} | |
if err == nil { | |
err = errors.New("failed to write to EigenDA and no redundant backends available") | |
} | |
return nil, err |
bb354f7
to
01ad05a
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.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (3)
store/manager.go (1)
129-130
: Consider validating value before using as key.When using the payload as the key, we should validate its size and content to ensure it's suitable for use as a key in the secondary storage system.
// use the payload as the key to avoid data loss +if len(value) > MaxKeySize { // Define appropriate MaxKeySize constant + return nil, fmt.Errorf("payload too large to use as key: %d bytes", len(value)) +} if m.secondary.Enabled() {store/secondary.go (2)
157-162
: Add logging for certificate decode failures.When the RLP decode fails, it silently falls back to using the commitment as key. While this aligns with the PR objectives, logging this fallback would help with debugging and monitoring.
var cert verifypackage.Certificate err := rlp.DecodeBytes(commitment, &cert) if err != nil { + sm.log.Debug("Commitment is not an RLP certificate, using as direct key", "err", err) key = commitment }
153-155
: Update method documentation to reflect new behavior.The method comment should be updated to explain the new certificate handling behavior.
// MultiSourceRead ... reads from a set of backends and returns the first successfully read blob // NOTE: - this can also be parallelized when reading from multiple sources and discarding connections that fail -// - for complete optimization we can profile secondary storage backends to determine the fastest / most reliable and always rout to it first +// - for complete optimization we can profile secondary storage backends to determine the fastest / most reliable and always route to it first +// - handles both direct keys and RLP encoded certificates, falling back to using commitment as key if certificate decode fails +// - uses appropriate verification method based on whether the key is a direct match or certificate
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
store/manager.go
(2 hunks)store/secondary.go
(3 hunks)
🔇 Additional comments (4)
store/manager.go (2)
11-11
: LGTM: Required import for fallback hash calculation.
The addition of the crypto package is necessary for computing Keccak256 hash when EigenDA fails.
127-137
: Verify handling of different commitment types by consumers.
The implementation correctly handles EigenDA failures by:
- Writing to secondary storage using value as both key and value
- Returning Keccak256 hash as commitment
However, we should verify that consumers can handle receiving a Keccak256 hash instead of the expected EigenDA certificate.
✅ Verification successful
Let me generate additional scripts to search for commitment handling patterns:
No concerns with different commitment types being returned
The codebase is well-designed to handle different commitment types:
- The
Put
method is called through theManager
interface which returns[]byte
- consumers are already designed to handle opaque byte arrays. - The codebase has a robust commitment type system (
commitments/
package) that supports multiple commitment types includingKeccak256Commitment
andGenericCommitment
. - Verification is properly abstracted through interfaces like
CertCommitment
,OPCommitment
, andDaSvcCommitment
, each with their own verification logic. - The server handler in
server/handlers.go
treats all commitments uniformly, wrapping any errors with proper context.
The fallback to Keccak256 hash is safe and consistent with the existing commitment handling patterns.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for places where Put method's return value is used
rg -A 5 "Put\(ctx.*\)" | grep -v "func.*Put"
# Search for type assertions or comparisons on commitment values
ast-grep --pattern 'if commit := $_.Put($$$); $$$'
Length of output: 4024
Script:
#!/bin/bash
# Search for commitment type checks or handling
rg -A 5 "type.*Commitment"
# Search for places where Put result is assigned and used
rg -B 2 -A 5 "(commit|key|cert).* := .*Put\("
# Search for any verification or type-specific handling
rg -A 5 "(Verify|Check|Handle|Process).*Commitment"
Length of output: 9464
store/secondary.go (2)
4-4
: LGTM: New imports support certificate handling.
The added imports align with the PR objectives for handling EigenDA certificates and fallback scenarios.
Also applies to: 11-11, 14-14
182-187
: Verify the impact on existing certificate verification.
The new branching logic for verification needs careful testing to ensure it doesn't break existing certificate verification flows.
✅ Verification successful
Let me gather more specific information about the verification implementations to better understand the impact.
Based on the gathered information, I can now provide a final response:
The verification branching logic is safe and maintains existing certificate verification flows
The code change implements a clear separation between two verification paths:
- Direct key-value verification using
src.Verify()
when key matches commitment - EigenDA certificate verification using the provided
verify()
function when dealing with certificates
The implementation is consistent with existing verification patterns across the codebase where:
- Each store type implements its own
Verify()
method with appropriate checks - Certificate verification is properly handled in the EigenDA store
- The locking mechanism (
verifyLock
) ensures thread-safe verification
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for other certificate verification patterns in the codebase
# that might be affected by this change
# Look for certificate verification patterns
echo "Searching for certificate verification patterns..."
rg -l "Certificate" --type go
# Look for Verify method calls
echo "Searching for Verify method calls..."
ast-grep --pattern 'Verify($_, $_, $_)'
Length of output: 474
Script:
#!/bin/bash
# Look for Verify method implementations and their context
echo "Searching for Verify method implementations..."
rg -A 5 "func.*Verify" --type go
# Look for verify function implementations
echo "Searching for verify function implementations..."
rg -A 5 "func verify" --type go
# Look for specific verification patterns in secondary.go
echo "Checking verification patterns in secondary.go..."
rg -A 10 "verify|Verify" store/secondary.go
Length of output: 5379
|
||
if bytes.Equal(key, commitment) { | ||
err = src.Verify(ctx, commitment, data) | ||
} else { | ||
// verify cert:data using EigenDA verification checks | ||
err = verify(ctx, commitment, data) | ||
} | ||
|
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.
🛠️ Refactor suggestion
Optimize lock scope and improve verification logic.
The verification lock's scope is wider than necessary, and the branching logic could be more explicit.
- sm.verifyLock.Lock()
+ var verifyErr error
+ sm.verifyLock.Lock()
if bytes.Equal(key, commitment) {
- err = src.Verify(ctx, commitment, data)
+ verifyErr = src.Verify(ctx, commitment, data)
} else {
// verify cert:data using EigenDA verification checks
- err = verify(ctx, commitment, data)
+ verifyErr = verify(ctx, commitment, data)
}
+ sm.verifyLock.Unlock()
- if err != nil {
+ if verifyErr != nil {
cb(Failed)
- log.Warn("Failed to verify blob", "err", err, "backend", src.BackendType())
- sm.verifyLock.Unlock()
+ log.Warn("Failed to verify blob", "err", verifyErr, "backend", src.BackendType(), "using_direct_key", bytes.Equal(key, commitment))
continue
}
- sm.verifyLock.Unlock()
Committable suggestion skipped: line range outside the PR's diff.
If EigenDA fails for any reason, we still want to write our commitment to the DA layer if we have caches or fallbacks enabled.
Since certificate is not available at this point, a keccak of the payload has to be used as commitment.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation