Deep code review for an Ethereum execution client. Checks consensus correctness, security, robustness, performance, DI patterns, breaking changes, and observability. Use when asked to "review", "check this PR", "look for bugs", "audit", or "review my changes".
Nethermind is an Ethereum execution client built on .NET. Consensus correctness is non-negotiable — a wrong opcode, a missed fork condition, or an Engine API violation means invalid blocks on a live network.
Only comment when you have HIGH CONFIDENCE (>80%) that a real issue exists. Be concise: one sentence per comment when possible. If uncertain, stay silent.
Subagent warning: Subagents do not inherit full review context automatically. If you launch subagents for this review, you must paste the Codebase Rules and all applicable domain review sections from this skill into the subagent prompt. After receiving subagent output, cross-check their findings against the rules in your own context.
All recon calls are independent — emit them in a single parallel batch.
Step 1: Identify the diff base. Run in ONE parallel batch:
git merge-base HEAD origin/mastergit log --oneline --merges <base>..HEAD (detect merge commits)git diff <base> HEAD --name-only (full file list — always cheap)git status --short (detect untracked files)Untracked file warning. If git status --short output contains any ?? entries, emit a warning before proceeding to Step 2:
Warning: The following files are untracked and will NOT be included in this review: (list untracked files) If these should be reviewed, stage them first with
git add.
This warning is mandatory — do not skip it even if the untracked files appear unrelated.
Never diff against master directly. git diff master compares against the current master tip, which includes commits merged after the branch diverged. Always use the merge base.
Stale-ref detection. If merge-base equals origin/master exactly (same SHA), the branch appears to descend directly from the remote-tracking tip. This is the signature of a stale ref when the branch was rebased onto a newer master than the local ref. Verify by running git ls-remote origin refs/heads/master (read-only, does not modify local refs). If the remote SHA differs from the local origin/master:
<short-sha> vs remote <short-sha>) — the local diff shows N files but the true diff is likely smaller."git fetch origin master to update the ref. Do not fetch silently.Step 2: Filter the file list. From the file list, remove:
.zst, .zip, .gz, .tar, .bin, .png, .jpg, .wasm, .dll, .exe, .dat, Git LFS pointersChains/*.json, runner configsgit log --no-merges --format="" --name-only <base>..HEAD | sort -u to get only files touched by the PR author's commits. Files only in the full list but not in the author-only list came in through merges — skip them unless the PR description says otherwise.List skipped files and reason in scope.
Step 3: Categorize remaining files: production / test / benchmark / CI / docs.
Step 4: Size the diffs. Run git diff <base> HEAD --stat to get per-file changed-line counts.
Step 4a: Fetch small diffs. Files with ≤1000 changed lines. Fetch in a single git diff <base> HEAD -- file1 file2 ... call. If the combined output exceeds 8000 lines, split into two calls.
Step 4b: Delegate large diffs. Files with >1000 changed lines go to subagents (see Subagent warning). Each subagent runs the full diff, reviews it, and returns only findings. Group 2–3 related large files per subagent when possible. Collect subagent findings for the verification pass (Plan step 7).
Prioritization: If more than 20 production files survived filtering, review critical paths first (EVM, Engine API, State, Trie, Specs, Blockchain), then the rest.
Review each changed .cs file independently. Read the diff, check it, report findings, move to the next.
Codebase Rules rule covers a pattern and the code matches that pattern, report it. Do not dismiss a violation by reasoning that the context "probably" justifies it — that is the author's call, not yours. If the rule is wrong, flag the violation and note the rule may need updating. If you believe the rule is wrong for this case, report the violation AND add a note that the rule may need an exception — but the violation still appears in the count.--stat (Step 4) before fetching full diffs.git diff call. Large files: subagents run their own.Follow these steps in order. At each checkpoint, list your findings for that category (or explicitly state "no findings") before proceeding to the next step. Do not skip steps.
Codebase Rules. See "Project rules check" section below. Large files: checked by subagents (Step 4b).Do not comment on anything below. CI will block the merge if any of it fails.
| Concern | Workflow |
|---|---|
| Whitespace formatting only (indentation, spacing) | code-formatting.yml (dotnet format whitespace) |
| Build errors | build-solutions.yml |
| All unit & integration tests | nethermind-tests.yml |
| CodeQL security analysis | codeql.yml |
| Dependency vulnerabilities | dependency-review.yml |
| Ethereum Foundation hive tests (consensus, RPC, Engine API) | hive-tests.yml, hive-consensus-tests.yml |
| JSON-RPC output correctness | rpc-comparison.yml |
Also skip: naming conventions, missing XML docs on internal/private members, minor grammar, refactoring suggestions that don't fix a real bug, logging improvements (unless security-related), build warnings that have no behavioural impact.
For every changed file, check the diff against Codebase Rules in step 3. Those rules define conventions for DI patterns, coding style, robustness, performance, test infrastructure, package management, and .github automation.
Reminder: You must report findings for every rule category, even if the finding is "no violations". Do not skip any.
CI runs all tests and fails on regressions — don't flag test failures. Do flag genuine gaps:
UInt256.MaxValue)Thread.Sleep or wall-clock time instead of deterministic mocks — these are flaky in CIOnly flag when it genuinely affects correctness or usability:
IPlugin, INethermindPlugin, JSON-RPC method, Engine API handler, config key) with no XML doc comment — public surfaces need documentation because consumers cannot read intent from implementationWrong EVM behaviour produces invalid blocks, causing chain desync and validators building on an invalid chain — leading to missed attestations and potential safety failures.
engine_newPayloadV1 (pre-Shanghai), engine_newPayloadV2 (+ withdrawals), engine_newPayloadV3 (+ blobGasUsed, excessBlobGas, parentBeaconBlockRoot), engine_newPayloadV4 (+ executionRequests)stateRoot; this is the ground truth for block validitycumulativeGasUsed, bloom filter, logs, status) — receipts are hashed into receiptsRoot in the block header; wrong values propagate as invalid blocks to peersUInt256 arithmetic that could overflow or truncate (Ethereum arithmetic is exact 256-bit)System.Security.Cryptography.SHA3_256 instead of Nethermind's ValueKeccak / KeccakHash — NIST SHA3 and Ethereum's Keccak-256 use different padding and produce different output on every inputsrc/Nethermind/Nethermind.Evm/VirtualMachine.cssrc/Nethermind/Nethermind.Evm/GasCostOf.cssrc/Nethermind/Nethermind.Merge.Plugin/Handlers/src/Nethermind/Nethermind.Specs/src/Nethermind/Nethermind.State/src/Nethermind/Nethermind.Blockchain/Receipts/engine_*) reachable without JWT authenticationunsafe block without a comment justifying the safety invariant — reviewers cannot verify correctness without itGetBlockHeaders ranges or oversized transaction batches are a DoS vectorNethermind is the fastest Ethereum execution client. Guard regressions in hot paths.
byte[] passed through a hot call stack where ReadOnlySpan<byte> or Memory<byte> would avoid copiesasync path — ties up thread-pool threadsIBatch or caching — amplifies I/O significantlyToList() in per-block or per-transaction logic — allocates on every callObjectPool<T> or ArrayPool<T>These are the most expensive issues to fix after release and the hardest for automated tools to catch. Always review carefully.
IPlugin, INethermindPlugin, or any interface in Nethermind.Core) without a versioning strategy — third-party plugin authors break silently at runtimeRocksDb-specific type in a Core namespace) — internals that leak into the contract become impossible to change laterIPlugin interface without a default implementation — all existing plugin authors break at compile timeNethermind.Core, Nethermind.Evm, or Nethermind.Blockchain instead of the appropriate chain plugin (Nethermind.Optimism, Nethermind.Merge.Plugin, etc.) — poisons the shared abstractionNew source files missing the required SPDX header (replace year with the current year):
// SPDX-FileCopyrightText: <year> Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
Example:
GetGasCostreturnslongbut is compared against aUInt256balance — values abovelong.MaxValuewill silently truncate and pass the check incorrectly. Cast before the comparison:if ((UInt256)gasCost > balance).
## Review report
**Scope:** <files reviewed, grouped by category; list skipped files>
### Findings
<numbered list of every finding, each with: category tag, file:line, problem, impact (if not obvious), fix>
<if no findings in a category, omit it — don't write "clean" or "N/A">
### Summary
- Findings: N (list categories with counts, e.g. "DI: 1, Security: 1")
- Total: N
If the review is genuinely clean — zero findings across all categories — write:
### Findings
None.
### Summary
- Total: 0
Rule violations (project rules check) are always reported — the confidence threshold does not apply. If a rule pattern check matched, report it. You may add a note that the rule may need an exception for this case, but the finding must appear in the report. It is the author's decision to dismiss it, not yours.
For domain findings (step 5), if you are not at least 80% confident that something is a real problem, do not comment. One high-confidence comment is worth more than five uncertain ones.