-
Notifications
You must be signed in to change notification settings - Fork 245
fix: Validate block headers against state #2763
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
|
The latest Buf updates on your PR. Results from workflow CI and Release / buf-check (pull_request).
|
|
Claude finished @alpe's task —— View job Comprehensive Code Review CompleteI've performed a comprehensive code review of PR #2763 focusing on the areas requested. Below is my analysis organized by focus area: 1. Code Quality & ArchitectureStrong Points
Areas for Improvement1. Proto field ordering (proto/evnode/v1/state.proto:18) bytes last_header_hash = 9;As noted in review comments, this should be 2. Backward compatibility issue (types/state.go:66-68) - CRITICAL if len(s.LastHeaderHash) == 0 { // initial state
return nil
}This addresses the testnet compatibility concern raised by @tac0turtle. When upgrading from an older binary, Recommendation: Consider adding a migration path or clearer documentation about the upgrade procedure. 3. Error handling pattern inconsistency (syncer.go:405-411) case errors.Is(err, errInvalidBlock):
// do not reschedule
case errors.Is(err, errInvalidState):
s.sendCriticalError(fmt.Errorf("invalid state detected..."))
default:
s.cache.SetPendingEvent(height, event)The 2. Error Handling & ResilienceGood Practices
Concerns1. Error propagation in fullnode (node/full.go:324-332) var runtimeErr error
if err := n.blockComponents.Start(ctx); err != nil {
if !errors.Is(err, context.Canceled) {
runtimeErr = fmt.Errorf("running block components: %w", err)
}
}This is an improvement over previous code, but the runtime error only captures errors from Recommendation: Consider reading from 2. Missing context for validation errors (types/state.go:77-82) return fmt.Errorf("invalid last header hash - got: %x, want: %x (state height: %d)",
header.LastHeaderHash, s.LastHeaderHash, s.LastBlockHeight)3. SecurityPositive Security Measures
Potential Issues1. Empty hash bypass (types/state.go:66-68) if len(s.LastHeaderHash) == 0 { // initial state
return nil
}While this is needed for backward compatibility, it creates a window where validation is skipped. An attacker could potentially exploit this during the upgrade window. Consider adding additional checks like verifying that 2. No signature verification in AssertValidForNextState 4. Performance & Resource EfficiencyEfficient Patterns
Performance Considerations1. Hash computation overhead (types/hashing.go:14-63) func (h *Header) Hash() Hash {
slimHash, err := h.HashSlim()
if err != nil {
return nil
}
if h.Legacy != nil && !h.Legacy.IsZero() {
legacyHash, err := h.HashLegacy()
// ...
}
return slimHash
}The 2. Repeated state cloning (syncer.go:159-163) stateCopy := *state
stateCopy.AppHash = bytes.Clone(state.AppHash)
stateCopy.LastHeaderHash = bytes.Clone(state.LastHeaderHash)Every call to 5. Testing & ReliabilityTest Coverage AnalysisStrong Coverage:
Coverage Gaps (39 missing lines per Codecov):
Missing Test Scenarios:
Recommendation: Add integration tests for:
6. Documentation & Developer ExperienceGood Documentation
Documentation Gaps1. Migration guide missing
2. Error message documentation
3. Proto field reservation 7. Specific Issues from Review CommentsIssue 1: Proto field naming (RESOLVED)Comment: @tac0turtle suggested Issue 2: Testnet compatibility (ADDRESSED)Comment: @tac0turtle tested on Eden testnet and found it can't sync when switching branches Issue 3: Orphaned error variable (ACKNOWLEDGED)Comment: @julienrbrt noted Summary & RecommendationsCritical Issues (Must Fix)
High Priority (Should Fix)
Medium Priority (Consider)
Low Priority (Nice to Have)
ConclusionThis PR makes important improvements to block validation and error handling, particularly by consolidating validation logic and improving error traceability. The main concerns are around the backward compatibility implementation (which could create security issues) and the incomplete error propagation in the fullnode. With the critical issues addressed, this will be a solid improvement to the codebase. The code quality is generally high, following Go idioms and the project's architectural patterns. The test coverage is good for happy paths but could be strengthened for error scenarios and the upgrade path. |
d3aa64e to
74cc608
Compare
* main: fix: make signature verification backwards compatible (#2782) chore: adding upgrade test for evm-single (#2780) refactor: replace interface{} with any for clarity and modernization (#2781) feat: replay mechanism to sync node with execution layer (#2771) docs: update readme for sync pkg (#2776) build(deps): Bump the all-go group across 6 directories with 4 updates (#2772) refactor: remove obsolete // +build tag (#2774) build(deps): Bump vite from 5.4.20 to 5.4.21 in /docs in the npm_and_yarn group across 1 directory (#2775) build(deps): Bump actions/setup-node from 5 to 6 (#2773)
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2763 +/- ##
==========================================
+ Coverage 62.24% 62.40% +0.15%
==========================================
Files 82 82
Lines 7221 7273 +52
==========================================
+ Hits 4495 4539 +44
- Misses 2186 2189 +3
- Partials 540 545 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| outReader, err := cmd.StdoutPipe() | ||
| require.NoError(s.t, err) | ||
|
|
||
| if s.debug { |
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.
store logs for debugging issues
| cmdToPids: make(map[string][]int), | ||
| outBuff: ring.New(100), | ||
| errBuff: ring.New(100), | ||
| debug: testing.Verbose(), |
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.
enabled, when tests are run with -v flag
proto/evnode/v1/state.proto
Outdated
| google.protobuf.Timestamp last_block_time = 5; | ||
| uint64 da_height = 6; | ||
| bytes app_hash = 8; | ||
| bytes LastHeaderHash = 9; |
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.
store last header hash to validate ensure consistency
| if headerTime := header.Time(); s.LastBlockTime.After(headerTime) { | ||
| return fmt.Errorf("invalid block time - got: %v, last: %v", headerTime, s.LastBlockTime) | ||
| } | ||
| if !bytes.Equal(header.LastHeaderHash, s.LastHeaderHash) { |
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.
ensures that state is correct in scenarios with empty data. Sometimes only header timestamps differed on double sign scenarios
julienrbrt
left a comment
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!
<!-- Please read and fill out this form before submitting your PR. Please make sure you have reviewed our contributors guide before submitting your first PR. NOTE: PR titles should follow semantic commits: https://www.conventionalcommits.org/en/v1.0.0/ --> ## Overview <!-- Please provide an explanation of the PR, including the appropriate context, background, goal, and rationale. If there is an issue with this information, please provide a tl;dr and link the issue. Ex: Closes #<issue number> --> Updated header hashing to respect both encodings. Header.Hash() now tries the slim hash first and, when legacy fields are present, recomputes using the legacy binary payload so historical blocks keep their original digest. Added standalone HashSlim / HashLegacy helpers and a regression test that ensures the legacy path is chosen even when legacy hashes are zero-filled. Tests run: go test ./types. You may still see state mismatches from other code paths that compare hashes generated before this change—rerun your sync tests to confirm where the remaining divergence happens.
This PR got bigger than expected when debugging test failures. The PR includes
InitChainby Syncer intial state setup as Executor doesstate.AssertValidForNextStatewith additional checks to ensure consistency. Used by Syncer and Executor