A Security Engineer's Guide to Reviewing Core Blockchain Nodes
Reviewing the security of a core blockchain node is one of the most challenging tasks a security engineer can undertake. Unlike traditional web applications or smart contracts, blockchain nodes sit at the intersection of cryptography, distributed systems, and network protocols—each with their own unique attack vectors and failure modes.
If you've ever looked at a blockchain node codebase and felt overwhelmed, you're not alone. I've been there too, staring at hundreds of thousands of lines of Rust code wondering where to even begin.
This guide provides a systematic approach to conducting thorough security reviews of blockchain node implementations, using Reth (Paradigm's Rust Ethereum execution client) as our primary example. Whether you're new to blockchain security or looking to formalise your review process, this methodology will help you identify critical vulnerabilities while managing the complexity of large-scale distributed systems.
Understanding the Challenge
Core blockchain nodes are fundamentally different from other software systems. They must:
- Maintain consensus across a distributed network without central authority
- Process and validate cryptographic proofs continuously
- Handle adversarial network conditions and malicious peers
- Manage state transitions that can involve significant financial value
- Operate with high availability requirements in hostile environments
This complexity means that traditional security review approaches often fall short. A systematic methodology is essential.
Phase 1: Pre-Review Preparation
Understanding the Architecture (1-3 days)
Before diving into code, invest time in understanding the system architecture. This preparation phase is crucial and often underestimated by new reviewers.
Now I've listed this section as 1-3 days, but let's be honest, this is an unrealistic amount of time to fully understand the entire Ethereum execution layer architecture (1-3 weeks wouldn't be enough). What we're going for here is a decent high-level understanding of what's happening. If this is your very first review, and you're not under time pressure, I'd recommend increasing this to at least 1 week, possibly more (build your domain knowledge). Now if you are time pressured, get a head start on the theoretical learning and start before the official kick-off.
In the case of Reth your goal should be to have a basic understanding of what each of the following components does and how they connect to others:
Core System Components
Component | Purpose | Key Connections |
---|---|---|
P2P Networking | Peer communication for gossiping transactions and blocks, plus historical data for node sync | • Mempool (new transactions) • DB (blocks) • State transition (syncing) |
RPC Layer | User-facing HTTP/WebSocket endpoints where dApps connect to submit transactions and query data | • State transition (gas estimation, calls) • DB (data fetching) |
Engine API | Consensus-Execution layer bridge for receiving execution payloads and fork choice decisions | • State transition (block execution) • Mempool (payload building) • DB (storing finalized blocks) |
State Transition | Core transaction processing logic that executes transactions and updates blockchain state | • EVM (smart contract execution) • DB (state read/write) • Mempool (transaction validation) |
Database | Persistent storage for blocks, transactions, account states, and receipts | • State transition (storing updates) • P2P (block persistence) • RPC (serving data) • Engine API (finalized blocks) |
EVM | Virtual machine that executes smart contract code with gas management and deterministic execution | • State transition (transaction execution) • DB (reading contract code/storage) • Engine API (payload validation) |
Mempool | Temporary holding area managing pending transaction ordering, fees, and spam prevention | • P2P (receiving transactions) • RPC (user submissions) • Engine API (payload building) • State transition (validation) |
Documentation Deep Dive:
- Study the protocol's whitepaper and technical specifications
- Understand the specific consensus mechanism (Proof of Work, Proof of Stake, etc.)
- Review networking protocols and peer-to-peer communication standards
- Examine any previous audit reports and disclosed vulnerabilities
For Reth specifically, start with:
- The Reth Book for architectural overview
- Ethereum's execution client specifications - if you're finding this too dense, dial it back and try some more digestible materials like What is Ethereum?
- Previous audit reports of similar Ethereum clients (shameless plug: here's a Reth Security report I prepared earlier). These reports are excellent resources, but don't worry if you don't understand the issue and they seem overwhelming at first, come back to them later in the review once you've built up more knowledge. Even experienced security reviewers can struggle to understand issues just from a report, needing a solid understanding of the code too.
Codebase Mapping:
💡 Pro Tip: A developer walkthrough is extremely valuable here. These sessions let you ask questions about each crate or module. Don't hesitate to ask basic questions even if your nervous about sounding dumb, it's better to clarify fundamentals early to accelerate your understanding and speed up the review.
The purpose of codebase mapping is to link what you've learned from the previous section "Understanding the Architecture" and connect it to the source code.
After performing this, hopefully we won't be quite so lost as when we started. You'll be able to look at the code and think "oh, that's where the data is stored," "ah, the networking folder contains the code for managing our peers and receiving things via gossip," and "the consensus crate validates blocks and prepares them for execution."
To do this we need to look at the code, so clone the repository, open up the code and familiarise yourself with the structure. Reth's modular design makes this particularly helpful:
git clone https://github.com/paradigmxyz/reth
cd reth
code .
Key Reth crates to understand:
reth-primitives
- Core data structures and typesreth-consensus
- Block validation and consensus logicreth-network
- P2P networking and protocol implementationreth-rpc
- JSON-RPC API implementationreth-db
- Database abstraction and storage layer
Generate dependency graphs to map out how the modules connect. The command below gives you a nice two-level view: first the main reth
dependencies, then what those depend on. Keep an eye on any second-layer crates that pull in tons of other dependencies, those are the ones that'll need more background when we get to the bottom-up review strategy.
cargo tree --depth 2
Operational Understanding:
Set up a local test environment, see if the code actually runs and works.
# Run Reth's test suite
cargo test
# Start a local development node
cargo run --bin reth node --dev
💡 Pro Tip: If the build fails or tests are flaky, sorry, but you're probably in for a tough review. It's often indicative of deeper issues you'll encounter during the security review.
Threat Modeling (1-2 days)
Think of this as putting on your attacker hat for a moment. We want to understand two key things: where are the entry points that bad actors could exploit (the attack surface), and what types of bugs commonly plague blockchain nodes? This upfront threat modelling will guide our manual review and help us focus on the areas that actually matter from a security perspective.
💡 Pro tip: This step can be rapidly fast forwarded by asking the developers what their main attack concerns. Similarly, LLMs are great for this high level brainstorming.
Attack Surface Analysis:
Okay for this example we're going head first into the Reth architecture here. If you feel out of your depth consider trying the previous steps yourself to build up your understanding of Ethereum and Reth, or ask your friendly neighbourhood LLM. Alternatively, if you're not feeling a coding deep dive aim to understand why we are doing these steps without getting lost in the details.
Network Layer
- P2P message handling in
reth-network/src/protocol
- what if our peers send us bad messages? - Peer discovery mechanisms - can we make ourselves the only visible peer?
- DevP2P and Ethereum wire protocol implementation - what about the layers of networking stack specific to Ethereum?
- Connection limits and resource management - can we exhaust the node's connection pool?
- Message size limits and DoS protection - what happens with massive or malformed packets?
Consensus Layer
- Block validation logic in
reth-consensus
- what if we receive a bad execution payload / block? - Fork choice algorithms - are we selecting the right block if there are two competing chains?
- Finality and reorganisation handling - can we re-org out other users' blocks maliciously?
- Timestamp validation - can we manipulate block times?
- Gas limit enforcement - what if we submit blocks that exceed gas limits?
State Layer
- Transaction processing and validation - can we craft transactions that bypass validation checks and spend more balance than we have?
- EVM execution environment - what about gas exhaustion attacks or infinite loops?
- State root computation and verification - can we cause state inconsistencies between nodes?
- Memory pool management - can we flood the mempool with spam transactions?
- Storage trie operations - what happens with deeply nested or malicious state structures?
API Layer
- JSON-RPC endpoints in
reth-rpc
- can we overload the node with expensive API calls? - Administrative interfaces - are there privileged endpoints without proper authentication?
- Debug and trace APIs - can we extract sensitive information or cause resource exhaustion?
- Rate limiting and authentication - what stops us from spamming API requests?
- Input validation and sanitisation - can we inject malicious data through API parameters?
Blockchain-Specific Attack Vectors:
- Resource pricing (are gas price set correctly for each instruction)
- Eclipse attacks (isolating nodes from the network)
- Long-range attacks (rewriting blockchain history)
- Resource exhaustion through state bloat
- Consensus manipulation and finality reversion
- Time-based attacks exploiting timestamp validation
Continuous Threat Modelling
While the initial threat modelling session provides the foundation, the most effective approach is continuous threat modelling throughout your review process. Your initial threat model is necessarily limited by your surface-level understanding of the system, but as you spend days reviewing code and understanding component interactions, you'll discover hidden attack surfaces that weren't obvious from architecture diagrams and subtle interaction bugs between components you initially thought were independent. This is so very true when you're first starting out as a core node reviewer.
The practical approach is simple: schedule brief 30-minute sessions every few days to revisit your threat model, personally I like to do this each time I complete a crate or module. Then ask yourself what new components you discovered, which assumptions about system behaviour turned out to be wrong, and what new attack paths became apparent after understanding the code flows. After completing review of each major component, pause to think about how an attacker could specifically target this component now that you understand its implementation, and look for cross-component vulnerabilities as you understand more.
This continuous refinement transforms your threat model from a generic checklist into a focused guide, which can inform you where to spend your remaining review time most effectively. Generally, the most critical vulnerabilities emerge from this iterative process rather than the initial brainstorming session, as your evolving understanding reveals attack chains that connect multiple components in ways you couldn't see at the beginning.
Phase 2: Automated Analysis
Be lazy! Just kidding, be efficient. If a tool can do the job for you fast and leave less work for you, great :) While manual review is the core of security assessment, automated tools provide valuable coverage to save you time and find easy bugs:
# Rust-specific security analysis
cargo clippy --all-targets --all-features -- -W clippy::all
cargo audit
cargo deny check
# Additional security-focused tools
cargo install cargo-geiger # Unsafe code detection
cargo geiger
Focus automated analysis on:
- Dependency vulnerabilities (especially in cryptographic libraries)
- Unsafe code usage patterns
- Potential panic conditions
- Integer overflow possibilities
LLM-Assisted Code Analysis:
Large Language Models can be valuable allies in your security review, especially for initial code understanding and pattern detection. However, they're tools to accelerate your review, not replace your expertise.
Example LLM Use Cases:
# Use LLMs for code explanation and documentation
# Example prompts for GitHub Copilot, Claude, or GPT-4:
"What does the function execute_block() do?"
[paste execute_block() function]
"As a security engineer what attack vectors should I consider when dealing with an HTTP end-point?"
[paste HTTP end-point file]
"Review this transaction validation logic for edge cases I might have missed:"
[paste validation function]
"I've found this bug are there more cases of it:"
[paste code snippet of bug]
Code Pattern Analysis:
- Ask LLMs to identify common vulnerability patterns (buffer overflows, integer overflows, race conditions)
- Request explanations of complex cryptographic operations
- Generate test cases for edge conditions you might not have considered
- Help understand unfamiliar Rust idioms or blockchain-specific patterns
Documentation Generation:
- Convert complex code flows into readable summaries
- Generate attack surface maps from code structure
- Create threat model outlines based on codebase analysis
LLM Limitations (Critical to Remember):
- ⚠️ Data Privacy Concerns - LLMs may retain and potentially expose sensitive data from your prompts, including vulnerability details, proprietary code, and confidential findings. At Sigma Prime, we use isolated, dedicated instances to prevent data leakage.
- ⚠️ Never trust LLM findings blindly - The false positive rate is high, they can hallucinate vulnerabilities that don't exist or miss real issues
- ⚠️ Context limitations - LLMs can't see the full codebase interactions that create real vulnerabilities
- ⚠️ False confidence - LLMs sound authoritative even when wrong
- ⚠️ Overstated severity - from personal experience these tool tend to way over inflate the severity as they don't understand the full picture
Best Practices:
- Use LLMs for initial understanding, not final security assessment
- Always verify LLM suggestions by manually tracing through the code
- Focus LLMs on explaining "what" and "how," while you determine the security implications
- Use them to generate questions to investigate, not to provide answers
Remember: LLMs are research assistants, not security experts. The critical thinking, context understanding, and final vulnerability assessment must come from you.
Phase 3: Manual Code Review Strategy
Manual review typically consumes 80% of your time and provides the deepest security insights. This is the part we want to be most efficient at, but it's also the most daunting and hardest which makes you most likely to procrastinate and hide. The key to a successful manual review is systematic prioritisation.
Priority Framework
1. Consensus-Critical Code (Highest Priority)
- Block validation in
reth-consensus
- EngineAPI implementation and payload handling
- Finality and reorganisation handling
- State root computation and verification
- EVM execution environment and gas accounting
2. Network Security (High Priority)
- Message serialisation/deserialisation in
reth-network
- Peer connection management and discovery
- Rate limiting and DoS protection
- Cryptographic verification of network messages
3. State Management (High Priority)
- Merkle tree implementations in
reth-trie
- Database consistency mechanisms
- Mempool transaction ordering and spam prevention
4. API and Interface Security (Medium Priority)
- JSON-RPC endpoint validation and rate limiting
- WebSocket connection management
- Input sanitisation and parameter validation
- Authentication and authorisation mechanisms
- Admin API access controls
5. Configuration and Startup (Medium Priority)
- Configuration file parsing and validation
- Command-line argument processing
- Environment variable handling
- Default security settings and hardening
6. Resource Management (Medium Priority)
- Memory allocation patterns and limits
- Database connection pooling and limits
- File handle management
- CPU usage monitoring and throttling
- Disk space management and cleanup
7. Observability and Monitoring (Low Priority)
- Logging mechanisms and log injection prevention
- Metrics collection and exposure
- Health check endpoints
- Performance monitoring infrastructure
- Alerting and notification systems
8. Development and Debug Features (Low Priority)
- Debug API endpoints and access controls
- Test mode configurations and security implications
- Development-only features in production builds
- Trace and profiling functionality
- Unsafe compilation flags and debug symbols
Review Approaches
Now we've got our priority of where to start and what component look at first. The next questions becomes how do I look at this component? There's two generalised approaches you can apply.
Bottom-Up Approach (Recommended for beginners)
Start small and build up systematically. Begin with the easiest code to understand: basic data structures, serialisation functions, and utility helpers. Choose crates which don't rely on other parts of the code (cargo tree
will help here). Use these as stepping stones toward comprehending the entire system. The progression below shows large jumps between components for brevity, but in practice you'd examine many more intermediate layers between each step:
- Primitives & Types - Review
reth-primitives
for basic data structures (blocks, transactions, headers) - Execution Layer - Analyse
reth-evm
for transaction execution and state transitions - Block Processing - Review
reth-consensus
for block validation logic - Engine API Interface - Finally examine
reth-engine-api
for consensus client communication
Top-Down Approach (For experienced reviewers)
The top-down approach flips the bottom-up methodology on its head. Instead of starting with basic components, you begin at external entry points where users or other systems interact with your node, then trace the execution paths much like a depth-first search through a code call graph. It's like a detective following leads in a case, you start with the initial crime scene (API call) and follow each clue as deep as it goes, investigating every witness and piece of evidence in that chain before returning to pursue the next lead. The major advantage of this approach is that you're immediately analysing realistic attack scenarios, walking the exact same code paths that real attackers would exploit to compromise the system.
For Reth specifically, the Engine API serves as an ideal starting point because it's where Consensus Layer clients (Lighthouse) communicate with the Execution Layer (Reth), sending critical data like new blocks and fork choice decisions into the system. This interface represents a natural entry point where malicious or malformed data could enter Reth, making it perfect for tracing how potential attacks would propagate through the codebase. The following progression is presented as a linear list for simplicity, but keep in mind that top-down analysis is actually tree like, each component branches into multiple sub-components and dependencies. You'll find yourself diving deep into one branch, then backtracking to explore another then jumping to the previous branch again, embrace the chaos.
- Engine API Interface - Start with the endpoints
reth-engine-api
, or even more specifically theengine_newPayloadV3
endpoint which handles new execution payloads and processes blocks - Block Processing - Follow this down the call path to block validation and fork choice
reth-consensus
- Execution Layer - Eventually it'll be executed by the EVM
reth-evm
- Primitives & Types - While this appears last in our logical flow, you'll probably encounter
reth-primitives
throughout the execution path as they are fundamental building blocks used by all other components.
Aspect | Bottom-Up Approach | Top-Down Approach |
---|---|---|
Complexity Management | ✅ Manageable with smaller, focused modules | ❌ Can be overwhelming jumping between components |
Understanding Depth | ✅ Deep understanding of foundational components | ❌ May miss subtle vulnerabilities in utility functions |
Coverage | ✅ Systematic coverage of all core components | ❌ Might skip unused code paths (not always a bad thing) |
Edge Cases | ✅ Excellent for finding utility function edge cases | ❌ May miss foundational edge cases |
Attack Realism | ❌ Difficult to assess realistic attack scenarios initially | ✅ Natural alignment with attacker methodology |
Time Efficiency | ❌ May spend time on unused or low-impact code paths | ✅ Efficient focus on reachable code paths |
Business Logic | ❌ Business logic vulnerabilities emerge late | ✅ Rapid understanding of external attack surface |
Experience Required | ✅ Suitable for beginners | ❌ Requires significant experience to navigate effectively |
Other Approaches
As you see to the graph above there are pros and cons to each approach. When starting out I originally ran with the bottom-up approach and found it great for learning and getting systematic code coverage, then I eventually migrated to the top-down. After years of honing my processes by diving head first into these large code bases, I now do a variant of top-down which also incorporates some of the following techniques.
main()
function - there's a lot of boilerplate code in the setup and configuration issues are generally low severity, though it is helpful for building understanding of the system and how all the components are connected.- High risk hotspots - jump straight into the areas you think are most critical and most likely to have bugs
Phase 4: Dynamic Analysis and Testing
When time permits, complement static analysis with dynamic testing:
Fuzzing Critical Components:
Fuzz testing typically offers excellent return on investment when comparing time spent to vulnerabilities discovered. For blockchain nodes like Reth, you have several fuzzing approaches available:
Differential Fuzzing (Recommended for Reth)
We employed differential fuzzing similar to Beacon Fuzz, which works particularly well for execution clients since there are multiple independent implementations (Reth, Geth, Erigon, etc.) that should produce identical results for the same inputs. This approach can catch subtle consensus bugs that might not trigger obvious crashes.
Single-Client Fuzzing
Single client fuzzing will generally search for DoS style bugs such as panics, memory exhaustion and slow execution, though it can also pick up some little ones like arithmetic overflows and underflows. You can also fuzz individual components within Reth itself:
- Execution endpoints and state transitions
- Serialization/deserialization (serde) functions
- P2P message parsing
- RPC input validation
- EVM execution edge cases
Getting Started
The Rust Fuzz Book provides an excellent introduction to fuzzing Rust codebases. Start with simple serialization functions before moving to more complex state transition logic.
Fuzzing is particularly valuable for finding edge cases in parsing logic and state transitions that manual review might miss.
Edge Case Testing:
It takes is a decent amount of time to develop the testing infrastructure however, it can pay dividends. Generally, good to attempt this when the development team has provided good infrastructure setup already.
- Maximum and minimum value inputs
- Malformed network messages
- Concurrent access patterns
- Resource exhaustion scenarios
Integration Testing:
Generally requires a significant amount of time to setup and perform bug discovery. Keep this for a tool when you've got excess time or better yet, recommend to the development team to perform these tests in their own time.
- Multi-node test networks
- Network partition simulations
- High-load transaction scenarios
- Upgrade and migration procedures
Phase 5: Documentation and Reporting
Findings Classification
Bug discovery is only half the battle in security reviews—severity classification is where the real expertise shows. Accurate risk assessment requires understanding three critical factors: attack reachability (can an attacker actually trigger this code path?), and potential impact. This nuanced analysis demands extensive protocol knowledge and is a complex topic that deserves its own dedicated guide. For now, here are foundational examples of how findings typically break down by severity level.
Critical: Consensus failure, network halt, fund loss
- Example: Invalid block acceptance breaking consensus
- Example: Double-spend vulnerabilities
High: DoS attacks, significant performance degradation
- Example: Unbounded memory allocation in message processing
- Example: Peer connection exhaustion attacks
Medium: Information disclosure, minor DoS
- Example: Sensitive data leakage through debug APIs
- Example: Predictable peer connection patterns
Low: Code quality issues, best practice violations
- Example: Unsafe code usage without justification
- Example: Missing error handling in non-critical paths
Evidence Collection
Document findings with:
- Precise code locations and line numbers
- Proof-of-concept exploits where applicable
- Impact assessment and attack scenarios
- Suggested remediation with code examples
Report Writing
Report writing takes significantly longer than most reviewers anticipate, often +20% of your total review time. Don't leave it as a massive task at the end; document findings as you discover them to maintain context and detail.
Key Principles:
Write for your audience: Your report is what the wider world sees of your work. Developers need to understand and fix the issues, while user need to assess whether this protocol is viable.
Self-contained explanations: Each finding should be understandable without opening an IDE or reading the source code. Include relevant code snippets, explain the vulnerable logic, and clearly describe the attack path.
💡 Pro Tips:
- Use consistent formatting and terminology throughout
- Include severity justification referencing attack reachability and impact
- Provide actionable recommendations, not just problem descriptions
- Review your own report—if you can't understand an issue after a week away from the code, neither can the development team
Remember: A well-written report amplifies the impact of your security work, while a poor one can render even critical findings ineffective.
Tools and Resources
Static Analysis:
- Rust: Clippy, Cargo-audit, Cargo-deny
- Go: GoSec, StaticCheck, Go-critic
Dynamic Analysis:
- LibFuzzer for Rust fuzzing
- Cargo-fuzz for structured fuzzing
- Built-in race detectors for concurrency issues
Protocol Testing:
- Testground for network simulation
- Jepsen for distributed systems testing
- Develop you own custom load testing scripts for transaction volume simulation
Conclusion
Security review of core blockchain nodes requires a methodical approach that balances comprehensive coverage with practical time constraints. The key principles are:
- Systematic preparation - Understand architecture before diving into code
- Risk-based prioritisation - Focus on consensus-critical and externally-facing components
- Appropriate methodology - Choose bottom-up or top-down based on your experience
- Comprehensive documentation - Provide actionable findings with clear impact assessment
Remember that blockchain node security is an evolving field. Stay updated with the latest attack vectors, participate in security communities, and continuously refine your methodology based on new learnings.
The complexity of systems like Reth can be overwhelming, but with a structured approach and persistence, you can identify critical vulnerabilities. Your work as a security engineer in this space directly contributes to the security and stability of decentralised infrastructure that millions of users depend on. Start with smaller components, build your understanding systematically, and don't hesitate to dive deep into the areas that matter most.