Skip to content

Commit 24e8c26

Browse files
authored
Merge branch 'main' into fix/gas-optimization
2 parents a49a84c + fb7cc60 commit 24e8c26

216 files changed

Lines changed: 305916 additions & 2325 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,15 @@ ADMIN_KEYS=admin-key-123
2020
# ── Optional ─────────────────────────────────────────────────────────────────
2121
REDIS_URL=
2222
LOG_LEVEL=debug
23+
24+
# ── Monitoring & Alerting ─────────────────────────────────────────────────────
25+
GRAFANA_ADMIN_PASSWORD=
26+
GRAFANA_URL=http://localhost:3002
27+
ALERT_WEBHOOK_URL=http://indexer:3000/monitoring/alerts
28+
INCIDENT_WEBHOOK_URL=http://indexer:3000/monitoring/alerts
29+
PAGERDUTY_ROUTING_KEY=
30+
ONCALL_EMAIL=oncall@stellarescrow.app
31+
SMTP_HOST=localhost:587
32+
SMTP_FROM=alerts@stellarescrow.app
33+
SMTP_USER=
34+
SMTP_PASSWORD=

.github/workflows/cost-report.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Monthly Cost Report
2+
3+
on:
4+
schedule:
5+
# 1st of every month at 08:00 UTC
6+
- cron: '0 8 1 * *'
7+
workflow_dispatch:
8+
inputs:
9+
month:
10+
description: 'Month to report (YYYY-MM, default: current)'
11+
required: false
12+
13+
jobs:
14+
cost-report:
15+
name: Generate Cost Report
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: write
19+
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Configure AWS credentials
24+
uses: aws-actions/configure-aws-credentials@v4
25+
with:
26+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
27+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
28+
aws-region: us-east-1
29+
30+
- name: Run cost report
31+
run: |
32+
MONTH="${{ github.event.inputs.month || '' }}"
33+
if [[ -n "$MONTH" ]]; then
34+
bash infra/cost/cost-report.sh "$MONTH" json
35+
else
36+
bash infra/cost/cost-report.sh --json
37+
fi
38+
39+
- name: Upload report artifact
40+
uses: actions/upload-artifact@v4
41+
with:
42+
name: cost-report-${{ github.run_id }}
43+
path: infra/cost/reports/
44+
retention-days: 90
45+
46+
- name: Post summary to job
47+
run: |
48+
REPORT=$(ls infra/cost/reports/*.json | tail -1)
49+
echo "## Cost Report" >> $GITHUB_STEP_SUMMARY
50+
echo '```json' >> $GITHUB_STEP_SUMMARY
51+
cat "$REPORT" | python3 -m json.tool >> $GITHUB_STEP_SUMMARY
52+
echo '```' >> $GITHUB_STEP_SUMMARY
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Smart Contract Tests
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main, develop]
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: smart-contract-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
contract-tests:
16+
name: Contract Test Matrix
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- name: Install Rust
23+
uses: dtolnay/rust-toolchain@stable
24+
25+
- uses: Swatinem/rust-cache@v2
26+
27+
- name: Install cargo-llvm-cov
28+
uses: taiki-e/install-action@cargo-llvm-cov
29+
30+
- name: Edge cases
31+
run: cargo test --manifest-path contract/Cargo.toml --test edge_cases
32+
33+
- name: Security scenarios
34+
run: cargo test --manifest-path contract/Cargo.toml --test security
35+
36+
- name: Integration scenarios
37+
run: cargo test --manifest-path contract/Cargo.toml --test integration
38+
39+
- name: Performance benchmarks
40+
run: cargo test --manifest-path contract/Cargo.toml --test performance -- --nocapture
41+
42+
- name: Stress benchmarks
43+
run: cargo test --manifest-path contract/Cargo.toml --test stress -- --nocapture
44+
45+
- name: Coverage report
46+
run: cargo llvm-cov --manifest-path contract/Cargo.toml --workspace --lcov --output-path contract/coverage/lcov.info --html --output-dir contract/coverage/html
47+
48+
- name: Upload contract coverage
49+
uses: actions/upload-artifact@v4
50+
if: always()
51+
with:
52+
name: smart-contract-coverage-${{ github.run_number }}
53+
path: contract/coverage/
54+
retention-days: 30

PERFORMANCE.md

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,54 @@ The script checks:
1313
1. **Slow queries**`pg_stat_statements` top 10 by mean execution time
1414
2. **Index usage** — tables with low index-scan ratio (candidates for new indexes)
1515
3. **API latency** — live data from `/health/metrics`
16-
4. **Container resources** — CPU/memory via `docker stats`
17-
5. **Redis hit rate**`keyspace_hits` vs `keyspace_misses`
16+
4. **APM bottlenecks** — live data from `/performance/bottlenecks` (slow queries + index usage + cache stats)
17+
5. **Container resources** — CPU/memory via `docker stats`
18+
6. **Redis hit rate**`keyspace_hits` vs `keyspace_misses`
19+
20+
### Live Bottleneck API
21+
22+
`GET /performance/bottlenecks` returns a JSON report combining:
23+
- Top 10 slow queries from `pg_stat_statements`
24+
- Tables with low index-scan ratio
25+
- Current cache hit/miss stats
26+
27+
```bash
28+
curl http://localhost:3000/performance/bottlenecks | jq .
29+
```
30+
31+
### Slow Query Threshold
32+
33+
Queries exceeding **100ms** are automatically logged to the `slow_query_log` table.
34+
Query the table to find recurring slow patterns:
35+
36+
```sql
37+
SELECT query_hash, query_text, round(avg(duration_ms)::numeric, 1) AS avg_ms, count(*) AS hits
38+
FROM slow_query_log
39+
WHERE recorded_at > NOW() - INTERVAL '24 hours'
40+
GROUP BY query_hash, query_text
41+
ORDER BY avg_ms DESC
42+
LIMIT 20;
43+
```
1844

1945
---
2046

2147
## Caching Strategy
2248

2349
### Redis API Cache (`indexer/src/cache.rs`)
2450

25-
| Endpoint pattern | TTL | Rationale |
26-
|-----------------|-----|-----------|
27-
| `GET /events*` | 10s | High-frequency reads; Stellar ledger closes every ~5s |
28-
| `GET /search*` | 30s | Search results change infrequently |
29-
| `GET /stats` | 60s | Aggregate — expensive to compute |
30-
| `POST /events/replay` | no cache | Mutating |
51+
| Endpoint pattern | TTL | Config key | Rationale |
52+
|-----------------|-----|-----------|-----------|
53+
| `GET /events*` | 10s | `events_ttl_secs` | High-frequency reads; Stellar ledger closes every ~5s |
54+
| `GET /search*` | 30s | `search_ttl_secs` | Search results change infrequently |
55+
| `GET /stats` | 60s | `stats_ttl_secs` | Aggregate — expensive to compute |
56+
| `GET /analytics/dashboard` | 60s | `analytics_ttl_secs` | Heavy aggregation query |
57+
| `POST /events/replay` | no cache || Mutating |
3158

32-
**Activate:** set `redis_url` in `config.toml` or `REDIS_URL` env var.
59+
**Activate:** set `redis_url` in `config.toml` or `STELLAR_ESCROW__CACHE__REDIS_URL` env var.
3360
**Fallback:** if Redis is unavailable, all requests hit Postgres directly — no errors.
3461

62+
All TTLs are configurable in `indexer/config.toml` under `[cache]`.
63+
3564
### Client-Side Cache (`frontend/performance.js`)
3665

3766
`cachedFetch()` provides an in-memory TTL cache (default 30s) for API responses.
@@ -50,6 +79,17 @@ Call `invalidateCache(url)` after any write operation to keep the UI consistent.
5079

5180
## Resource Allocation
5281

82+
### Docker Resource Limits
83+
84+
Applied in `docker-compose.yml` for all application services:
85+
86+
| Service | CPU limit | Memory limit | CPU reservation | Memory reservation |
87+
|---------|-----------|-------------|-----------------|-------------------|
88+
| indexer | 1.0 | 512M | 0.25 | 128M |
89+
| api | 0.5 | 256M | 0.1 | 64M |
90+
91+
Adjust limits based on observed usage from `docker stats` or the Grafana infrastructure dashboard.
92+
5393
### Database Connection Pool
5494

5595
Configured in `indexer/config.toml`:
@@ -63,6 +103,8 @@ min_connections = 2 # keep warm connections ready
63103
**Rule of thumb:** `max_connections = (2 × CPU cores) + effective_spindle_count`
64104
For a 4-core host: set `max_connections = 10–15`.
65105

106+
Monitor pool saturation via `GET /performance/bottlenecks``index_usage` or the Grafana PostgreSQL connections panel.
107+
66108
### Nginx (`performance.nginx.conf`)
67109

68110
- `worker_processes auto` — one worker per CPU core
@@ -94,14 +136,46 @@ Metrics are collected at three layers:
94136
| Layer | Mechanism | Endpoint |
95137
|-------|-----------|---------|
96138
| Infrastructure | `HealthMonitor` (Rust) | `GET /health/metrics` |
139+
| APM (request-level) | `PerformanceService` (Rust) | `GET /performance/dashboard` |
140+
| APM alerts | `PerformanceService` alert rules | `GET /performance/alerts` |
141+
| APM history | Hourly rollup (materialized view) | `GET /performance/history` |
142+
| Prometheus scrape | `MonitoringService` + APM metrics | `GET /metrics` |
97143
| Web Vitals | `observeWebVitals()` (JS) | beacons → `POST /api/metrics` |
98144
| CDN | `observeCdnPerformance()` (JS) | beacons → `POST /api/metrics` |
99145
| SSL | `monitorTlsConnection()` (JS) | beacons → `POST /api/metrics` |
100146

147+
### APM Endpoints
148+
149+
| Endpoint | Description |
150+
|----------|-------------|
151+
| `GET /performance/dashboard` | Full APM snapshot: per-route stats, overall P95/P99, active alerts |
152+
| `GET /performance/alerts` | Active performance alerts only |
153+
| `GET /performance/history` | Hourly rollup from `perf_metrics_hourly` (last 24h) |
154+
| `POST /performance/record` | Ingest a sample `{ route, method, status, duration_ms }` |
155+
| `GET /metrics` | Prometheus text format (monitoring + APM metrics) |
156+
157+
### Grafana Dashboards
158+
159+
| Dashboard | UID | Description |
160+
|-----------|-----|-------------|
161+
| StellarEscrow Platform | `stellar-escrow-main` | Business metrics (trades, compliance, fraud) |
162+
| APM — Performance | `stellar-escrow-apm` | Latency (avg/P95/P99), error rate, throughput, DB query time |
163+
| Code Quality | `code-quality` | CI lint/security metrics |
164+
165+
### Alert Rules
166+
167+
| File | Group | Alerts |
168+
|------|-------|--------|
169+
| `alert_rules.yml` | `stellar_escrow_platform` | Error rate, disputes, fraud |
170+
| `alert_rules_security.yml` | `stellar_escrow_compliance` | AML, compliance blocks |
171+
| `alert_rules_performance.yml` | `stellar_escrow_performance` | Latency (avg/P95), error rate, DB queries, throughput |
172+
101173
### Key Metrics to Watch
102174

103175
- **TTFB** < 200ms (good), < 800ms (acceptable)
104176
- **LCP** < 2.5s
177+
- **Avg response time** < 500ms (warning at 500ms, critical at 2000ms)
178+
- **P95 response time** < 1000ms (warning at 1000ms, critical at 5000ms)
105179
- **DB query mean** < 10ms for hot paths (`get_events`, `search_trades`)
106180
- **Redis hit rate** > 80% under normal load
107181
- **Indexer memory** < 256MB under normal load
@@ -111,8 +185,11 @@ Metrics are collected at three layers:
111185
## Quick Wins Checklist
112186

113187
- [ ] Enable Redis (`redis_url` in config) — eliminates repeat DB hits for read-heavy endpoints
114-
- [ ] Enable `pg_stat_statements` extension — required for slow query analysis
188+
- [ ] Enable `pg_stat_statements` extension — required for slow query analysis (migration `20260329000001_perf_optimization.sql` does this)
115189
- [ ] Set `max_connections` based on actual CPU count
116190
- [ ] Include `performance.nginx.conf` in nginx http block
117-
- [ ] Add `REDIS_URL` to docker-compose environment
191+
- [ ] Add `STELLAR_ESCROW__CACHE__REDIS_URL` to docker-compose environment
118192
- [ ] Run `perf-analyze.sh` weekly and track trends
193+
- [ ] Review `GET /performance/bottlenecks` after each deploy
194+
- [ ] Check `slow_query_log` table weekly for recurring slow patterns
195+
- [ ] Tune Docker resource limits based on `docker stats` observations

PR_DESCRIPTION.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## Summary
2+
3+
- expands the Soroban smart contract suite with dedicated edge case, security, integration, performance, and stress coverage
4+
- repairs the contract core so the smart contract crate builds and all contract tests pass again
5+
- adds root test commands, contract testing docs, snapshot artifacts, and a GitHub Actions workflow for contract testing and coverage
6+
7+
## What Changed
8+
9+
- added shared contract test harnesses and new suites under `contract/tests/`
10+
- restored a working contract implementation in `contract/src/` for escrow, bridge, insurance, pause, compliance, migration, and analytics flows
11+
- enabled importing the contract crate in integration tests via `rlib`
12+
- documented the new contract test matrix and commands
13+
- added `.github/workflows/smart-contract-tests.yml`
14+
15+
## Verification
16+
17+
```bash
18+
cargo test --manifest-path contract/Cargo.toml
19+
```
20+
21+
All contract unit, edge, security, integration, performance, and stress tests pass locally.
22+
23+
## Notes
24+
25+
- stress scenarios were tuned to stable CI-safe volumes so the Soroban test host does not hit budget exhaustion during the suite
26+
- contract snapshots were generated under `contract/test_snapshots/` during verification

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,17 @@ cargo test # Backend tests
5656
docker-compose up # Indexer + DB
5757
```
5858
59+
## Smart Contract Testing
60+
61+
The Soroban contract now has dedicated suites for edge cases, security scenarios, integration flows, stress tests, and performance benchmarks under `contract/tests/`.
62+
63+
```bash
64+
npm run test:contract
65+
npm run test:contract:edge
66+
npm run test:contract:security
67+
npm run test:contract:integration
68+
npm run test:contract:performance
69+
npm run test:contract:stress
70+
npm run test:contract:coverage
71+
```
72+

TESTING.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This project uses a three-tier testing strategy:
1515
| Scalability | Jest + performance harness | `api/src/**/*.scalability.test.ts` | Throughput and latency trends across concurrency levels |
1616
| Monitoring | Jest + performance harness | `api/src/**/*.monitoring.test.ts` | Threshold alerts, error-rate visibility, and per-operation telemetry |
1717
| Security | Jest + scenario harness | `security/src/security.assessment.test.ts` | Penetration tests, vulnerability scans, compliance, monitoring |
18+
| Smart Contract | Soroban test harness | `contract/tests/*.rs`, `contract/tests/stress.rs` | Contract edge cases, security, integration, stress, benchmarks, coverage |
1819
| E2E | Cypress | `components/cypress/e2e/` | Full user flows |
1920

2021
## Running Tests
@@ -41,6 +42,15 @@ npm run test:docs --workspace=api
4142
npm run test:security
4243
npm run test:security --workspace=security
4344

45+
# Smart contract suites
46+
npm run test:contract
47+
npm run test:contract:edge
48+
npm run test:contract:security
49+
npm run test:contract:integration
50+
npm run test:contract:performance
51+
npm run test:contract:stress
52+
npm run test:contract:coverage
53+
4454
# E2E tests (requires app running on localhost:3000)
4555
npm run test:e2e
4656

@@ -52,6 +62,7 @@ cd components && npm run test:watch
5262

5363
All packages enforce **70% minimum** on branches, functions, lines, and statements.
5464
Coverage reports are written to `coverage/` in each package directory.
65+
For the contract crate, coverage artifacts are written to `contract/coverage/`.
5566

5667
## Unit Tests
5768

0 commit comments

Comments
 (0)