forked from erpc/erpc
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path.cursorrules
More file actions
205 lines (170 loc) · 7.97 KB
/
.cursorrules
File metadata and controls
205 lines (170 loc) · 7.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# Repository Guide for AI Agents
This project hosts **eRPC**, a fault-tolerant EVM RPC proxy with built-in caching, failover logic and a TypeScript CLI. The repository contains Go code for the server and TypeScript packages for tooling and configuration.
## Local Development
### Go code
- Install dependencies with `make setup`.
- Format all Go files with `make fmt`.
- Build the server with `make build` or run it via `make run`.
- Execute the unit tests quickly with `make test-fast` (no race checks).
- Run the full suite with race checks using `make test` when needed.
- To run a single test or pattern, use `LOG_LEVEL=trace go test -run <pattern> ./...`.
### Node/TypeScript packages
- Use `pnpm install` to install dependencies.
- Format TypeScript sources with `pnpm -r run format` (uses [Biome](https://biomejs.dev/)).
- Build all packages with `pnpm -r run build`.
## Testing Guidelines
### Test Initialization
- **Always** include this in test files that need logging:
```go
func init() {
util.ConfigureTestLogger()
}
```
- Use the global logger: `log.Logger` from `github.com/rs/zerolog/log`
- For debugging tests, use: `LOG_LEVEL=debug go test -run <pattern> ./...` or `LOG_LEVEL=trace` for even more detail
### Gock (HTTP Mocking) Best Practices
1. **Setup Order is Critical**: Always set up ALL gock mocks BEFORE initializing any network components or services
```go
// ✅ CORRECT: Set up mocks first
util.ResetGock()
defer util.ResetGock()
util.SetupMocksForEvmStatePoller()
// Set up your test-specific mocks here
gock.New("http://rpc1.localhost")...
// THEN initialize network/services
network := setupTestNetwork(t, ctx, ...)
```
2. **Standard Gock Pattern**:
```go
util.ResetGock()
defer util.ResetGock()
util.SetupMocksForEvmStatePoller() // Handles background state polling
defer util.AssertNoPendingMocks(t, 0) // 0 or expected pending count
```
3. **Filtering Requests**: Use filters to distinguish between different request types
```go
gock.New("http://rpc1.localhost").
Post("").
Filter(func(r *http.Request) bool {
body := util.SafeReadBody(r)
return strings.Contains(body, "eth_getBalance")
}).
Reply(200).
JSON(map[string]interface{}{...})
```
4. **Mock Persistence**:
- Use `Times(1)` for mocks that should only match once
- Use `Persist()` for mocks that might be called multiple times or in unpredictable patterns
- For concurrent tests or tests with background processes, use `Persist()`
### Context Management
- Always create and properly clean up contexts:
```go
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
```
- Pass this context through all network/service operations
### Race Condition Prevention
1. **Never use `t.Parallel()`** with tests that use gock mocks
2. Set up all mocks before component initialization (components may start background goroutines)
3. Be aware that network initialization starts background processes like state pollers
4. For concurrent tests, don't use `defer util.AssertNoPendingMocks(t, 0)`
### Common Debugging Tips
- If tests fail with "gock: cannot match any request", check:
1. Are mocks set up BEFORE component initialization?
2. Are filters too restrictive?
3. Are background processes consuming mocks?
- Run with race detector during development: `go test -race -run TestName ./...`
- Use `gock.Clean()` sparingly - prefer proper mock setup order
## Error Handling Patterns
### Error Type Checking
- Use `common.HasErrorCode(err, codes...)` to check for specific error codes in the error chain
- For extracting specific error types from nested errors, use `errors.As()`:
```go
var jre *common.ErrJsonRpcExceptionInternal
if errors.As(err, &jre) {
normalizedCode := jre.NormalizedCode()
// Use the extracted error
}
```
### JSON-RPC Error Handling
- `ErrJsonRpcExceptionInternal` contains normalized codes that should be used for comparison
- Access normalized code with `err.NormalizedCode()` method
- Common normalized codes:
- `JsonRpcErrorEvmReverted` (3) - EVM execution reverted
- `JsonRpcErrorCallException` (-32000) - Call exception
- `JsonRpcErrorTransactionRejected` (-32003) - Transaction rejected
### Consensus Error Patterns
- Execution exceptions (like smart contract reverts) are valid consensus results
- When comparing errors for consensus, compare by their normalized JSON-RPC codes
- Use `common.ErrCodeEndpointExecutionException` to identify execution errors
## Consensus Implementation
### Treating Errors as Valid Results
- Execution exceptions represent valid blockchain state (e.g., all nodes agree a transaction reverts)
- Implement `isConsensusValidError()` to identify which errors contribute to consensus
- Error consensus should compare normalized codes, not just error types
### Error Hash Generation
- For consensus comparison, generate hashes that include the normalized error code:
```go
if errors.As(err, &jre) {
return fmt.Sprintf("%d", jre.NormalizedCode())
}
```
### Short-Circuit Logic
- Consensus can be reached on errors just like successful results
- When implementing short-circuit logic, consider both successful responses and consensus-valid errors
- Count participants with consensus-valid errors as valid participants
## Advanced Testing Patterns
### Mock Creation Best Practices
- When working with gock for different response bodies, create separate simple mocks:
```go
// ✅ GOOD: Simple, readable mocks
gock.New(url).Post("").Times(1).Reply(200).JSON(response1)
gock.New(url).Post("").Times(1).Reply(200).JSON(response2)
// ❌ AVOID: Complex body generation functions
```
### Subscription and Integration Tests
- Always set required configuration values that might have zero defaults:
- `MaxSyncRangeSize` (e.g., 1000)
- `HttpParallelBlockFetches` (e.g., 10)
- Ensure parent hash chains are properly connected in blockchain tests:
```go
if blockNum == 1 {
parentHash = []byte("genesis") // Block 1 points to genesis
} else {
parentHash = []byte(fmt.Sprintf("hash-%d", blockNum-1))
}
```
- Use `SetID()` method on JsonRpcResponse objects to set the ID field correctly
### Test Execution Patterns
- For debugging specific tests: `LOG_LEVEL=trace go test -run TestName ./... -v`
- When tests timeout, check for:
- Parent hash mismatches in blockchain data
- Missing required configuration values
- Improper mock setup order
## Code Patterns and Best Practices
### Working with Upstreams
- Always check if upstream and its config are not nil before accessing:
```go
if r.upstream != nil && r.upstream.Config() != nil {
upstreamId := r.upstream.Config().Id
}
```
### Goroutine Management in Tests
- Be careful with goroutine leaks in tests
- Use proper context cancellation to clean up goroutines
- When collecting responses from multiple goroutines, always handle nil responses from cancelled goroutines
## Kubernetes and Infrastructure Safety
### Working with Production Systems
- **NEVER** run commands that modify, delete, or create Kubernetes resources directly
- Only run read-only commands for investigation and debugging
- Always provide commands as suggestions and let the user execute them
- Safe commands examples: `kubectl get`, `kubectl describe`, `kubectl logs`
- Unsafe commands (never run): `kubectl delete`, `kubectl apply`, `terraform apply`
## Commit and PR Guidelines
- Follow the [Conventional Commits](https://www.conventionalcommits.org/) style (e.g. `feat:`, `fix:`) when writing commit messages.
- Create feature branches for new work and open Pull Requests against `main`.
- Ensure `make fmt` and `make test` succeed before pushing your changes.
- Reference relevant issues in the PR description when applicable.
## Additional Notes
- All contributions fall under the [Contributor License Agreement](CLA.md) and require adherence to the [Code of Conduct](CODE_OF_CONDUCT.md).
- Documentation lives under the `docs/` directory. See `README.md` for a project overview and quick start instructions.