Skip to content

Commit be64d75

Browse files
committed
chore(host-listener): add auditable slow-lane validation runbook
1 parent 205bf45 commit be64d75

File tree

2 files changed

+297
-0
lines changed

2 files changed

+297
-0
lines changed

coprocessor/fhevm-engine/host-listener/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,31 @@ throttling accounting, and promotes seen chains to
5454
Goal: validate slow-lane behavior with both `host_listener` and
5555
`host_listener_poller` running concurrently against the same DB.
5656

57+
Recommended (auditable) entrypoint:
58+
59+
```bash
60+
cd test-suite/fhevm
61+
./scripts/validate-slow-lane.sh --cap 1 --load-test operators
62+
```
63+
64+
What this enforces:
65+
66+
- deterministic integration assertions (`threshold matrix`, `cross-block below-cap`, `off-mode promote-on-seen`)
67+
- bootstrap gate (`ActivateKey` + `Fetched keyset` + non-empty `sns_pk`)
68+
- local multi-listener stack assertions with SQL pass/fail checks
69+
- at least one fast and one slow chain after baseline
70+
- schedulable ordering head is fast lane (`schedule_priority=0`)
71+
72+
Useful modes:
73+
74+
```bash
75+
# deterministic tests only
76+
./scripts/validate-slow-lane.sh --integration-only
77+
78+
# local stack only (assumes stack already up)
79+
./scripts/validate-slow-lane.sh --stack-only --cap 1 --load-test operators
80+
```
81+
5782
1. Start local stack
5883

5984
```bash
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
FHEVM_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
6+
REPO_ROOT="$(cd "${FHEVM_DIR}/../.." && pwd)"
7+
ENGINE_DIR="${REPO_ROOT}/coprocessor/fhevm-engine"
8+
9+
COMPOSE_PROJECT="fhevm"
10+
COMPOSE_ENV="${FHEVM_DIR}/env/staging/.env.coprocessor.local"
11+
COMPOSE_FILE="${FHEVM_DIR}/docker-compose/coprocessor-docker-compose.yml"
12+
13+
RUN_INTEGRATION=true
14+
RUN_STACK=true
15+
CAP=1
16+
LOAD_TEST="operators"
17+
BOOTSTRAP_TIMEOUT_SECONDS=180
18+
19+
RED='\033[0;31m'
20+
GREEN='\033[0;32m'
21+
YELLOW='\033[0;33m'
22+
BLUE='\033[0;34m'
23+
RESET='\033[0m'
24+
25+
override_file=""
26+
listeners_overridden=false
27+
28+
usage() {
29+
cat <<EOF
30+
Usage: $(basename "$0") [options]
31+
32+
Options:
33+
--integration-only Run only deterministic Rust integration assertions.
34+
--stack-only Run only local-stack multi-listener SQL assertions.
35+
--cap N Slow-lane cap for stack scenario (default: 1).
36+
--load-test NAME fhevm-cli test to generate load (default: operators).
37+
--bootstrap-timeout SEC Timeout for key-bootstrap gate (default: 180).
38+
-h, --help Show this help.
39+
EOF
40+
}
41+
42+
log() {
43+
printf "${BLUE}[slow-lane-validate]${RESET} %s\n" "$*"
44+
}
45+
46+
warn() {
47+
printf "${YELLOW}[slow-lane-validate]${RESET} %s\n" "$*"
48+
}
49+
50+
die() {
51+
printf "${RED}[slow-lane-validate] %s${RESET}\n" "$*" >&2
52+
exit 1
53+
}
54+
55+
compose() {
56+
docker compose -p "${COMPOSE_PROJECT}" \
57+
--env-file "${COMPOSE_ENV}" \
58+
-f "${COMPOSE_FILE}" \
59+
"$@"
60+
}
61+
62+
compose_with_override() {
63+
local file="$1"
64+
shift
65+
docker compose -p "${COMPOSE_PROJECT}" \
66+
--env-file "${COMPOSE_ENV}" \
67+
-f "${COMPOSE_FILE}" \
68+
-f "${file}" \
69+
"$@"
70+
}
71+
72+
db_query() {
73+
local sql="$1"
74+
docker exec -i coprocessor-and-kms-db \
75+
psql -U postgres -d coprocessor -v ON_ERROR_STOP=1 -At -c "${sql}"
76+
}
77+
78+
cleanup() {
79+
if [[ "${listeners_overridden}" == "true" ]]; then
80+
warn "Restoring listener defaults (no slow-lane override)"
81+
compose up -d --force-recreate \
82+
coprocessor-host-listener \
83+
coprocessor-host-listener-poller >/dev/null
84+
fi
85+
86+
if [[ -n "${override_file}" && -f "${override_file}" ]]; then
87+
rm -f "${override_file}"
88+
fi
89+
}
90+
91+
trap cleanup EXIT
92+
93+
wait_for_bootstrap() {
94+
local deadline=$((SECONDS + BOOTSTRAP_TIMEOUT_SECONDS))
95+
while (( SECONDS < deadline )); do
96+
local has_activate_key
97+
local has_fetched_keyset
98+
local has_key_material
99+
100+
has_activate_key="$(docker logs --since=20m coprocessor-gw-listener 2>&1 | rg -c "ActivateKey event successful" || true)"
101+
has_fetched_keyset="$(docker logs --since=20m coprocessor-sns-worker 2>&1 | rg -c "Fetched keyset" || true)"
102+
has_key_material="$(db_query "SELECT COALESCE(bool_and(COALESCE(octet_length(lo_get(sns_pk)), 0) > 0), false) FROM tenants;")"
103+
104+
if [[ "${has_activate_key}" -gt 0 && "${has_fetched_keyset}" -gt 0 && "${has_key_material}" == "t" ]]; then
105+
log "Bootstrap gate passed (ActivateKey + keyset + non-empty sns_pk)"
106+
return 0
107+
fi
108+
sleep 3
109+
done
110+
111+
warn "Bootstrap gate timed out, restarting gw-listener once"
112+
compose up -d --no-deps coprocessor-gw-listener >/dev/null
113+
114+
local retry_deadline=$((SECONDS + BOOTSTRAP_TIMEOUT_SECONDS))
115+
while (( SECONDS < retry_deadline )); do
116+
local has_fetched_keyset
117+
has_fetched_keyset="$(docker logs --since=20m coprocessor-sns-worker 2>&1 | rg -c "Fetched keyset" || true)"
118+
if [[ "${has_fetched_keyset}" -gt 0 ]]; then
119+
log "Bootstrap recovered after gw-listener restart"
120+
return 0
121+
fi
122+
sleep 3
123+
done
124+
125+
die "Bootstrap gate failed: sns-worker did not fetch keyset"
126+
}
127+
128+
apply_listener_cap_override() {
129+
local cap="$1"
130+
override_file="$(mktemp)"
131+
cat >"${override_file}" <<EOF
132+
services:
133+
coprocessor-host-listener:
134+
command:
135+
- host_listener
136+
- --database-url=\${DATABASE_URL}
137+
- --coprocessor-api-key=\${TENANT_API_KEY}
138+
- --acl-contract-address=\${ACL_CONTRACT_ADDRESS}
139+
- --tfhe-contract-address=\${FHEVM_EXECUTOR_CONTRACT_ADDRESS}
140+
- --url=\${RPC_WS_URL}
141+
- --initial-block-time=1
142+
- --dependent-ops-max-per-chain=${cap}
143+
144+
coprocessor-host-listener-poller:
145+
command:
146+
- host_listener_poller
147+
- --database-url=\${DATABASE_URL}
148+
- --coprocessor-api-key=\${TENANT_API_KEY}
149+
- --acl-contract-address=\${ACL_CONTRACT_ADDRESS}
150+
- --tfhe-contract-address=\${FHEVM_EXECUTOR_CONTRACT_ADDRESS}
151+
- --url=\${RPC_HTTP_URL}
152+
- --dependent-ops-max-per-chain=${cap}
153+
EOF
154+
155+
compose_with_override "${override_file}" up -d --force-recreate \
156+
coprocessor-host-listener \
157+
coprocessor-host-listener-poller >/dev/null
158+
listeners_overridden=true
159+
log "Applied listener override with --dependent-ops-max-per-chain=${cap}"
160+
}
161+
162+
run_integration_assertions() {
163+
log "Running deterministic integration assertions"
164+
(
165+
cd "${ENGINE_DIR}"
166+
cargo +1.91.1 test -p host-listener --test host_listener_integration_tests \
167+
test_slow_lane_threshold_matrix_locally -- --nocapture
168+
cargo +1.91.1 test -p host-listener --test host_listener_integration_tests \
169+
test_slow_lane_cross_block_sustained_below_cap_stays_fast_locally -- --nocapture
170+
cargo +1.91.1 test -p host-listener --test host_listener_integration_tests \
171+
test_slow_lane_off_mode_promotes_seen_chain_locally -- --nocapture
172+
)
173+
printf "${GREEN}[slow-lane-validate] Integration assertions passed${RESET}\n"
174+
}
175+
176+
run_stack_assertions() {
177+
log "Running local-stack assertions (cap=${CAP}, load=${LOAD_TEST})"
178+
wait_for_bootstrap
179+
180+
local before_block_height
181+
before_block_height="$(db_query "SELECT COALESCE(MAX(block_height), 0) FROM dependence_chain;")"
182+
log "Baseline block_height=${before_block_height}"
183+
184+
apply_listener_cap_override "${CAP}"
185+
186+
(
187+
cd "${FHEVM_DIR}"
188+
./fhevm-cli test "${LOAD_TEST}"
189+
)
190+
191+
local counts
192+
counts="$(db_query "
193+
SELECT
194+
COUNT(*) FILTER (WHERE schedule_priority = 0),
195+
COUNT(*) FILTER (WHERE schedule_priority = 1),
196+
COUNT(*)
197+
FROM dependence_chain
198+
WHERE block_height > ${before_block_height};
199+
")"
200+
IFS='|' read -r fast_count slow_count total_count <<<"${counts}"
201+
202+
log "Observed chains after baseline: total=${total_count}, fast=${fast_count}, slow=${slow_count}"
203+
204+
[[ "${total_count}" -gt 0 ]] || die "No new dependence chains were ingested"
205+
[[ "${fast_count}" -gt 0 ]] || die "Expected at least one fast chain"
206+
[[ "${slow_count}" -gt 0 ]] || die "Expected at least one slow chain (raise load or lower cap)"
207+
208+
local schedulable_order_head
209+
schedulable_order_head="$(db_query "
210+
SELECT schedule_priority
211+
FROM dependence_chain
212+
WHERE status = 'updated'
213+
AND worker_id IS NULL
214+
AND dependency_count = 0
215+
AND block_height > ${before_block_height}
216+
ORDER BY schedule_priority ASC, last_updated_at ASC
217+
LIMIT 1;
218+
")"
219+
220+
if [[ -n "${schedulable_order_head}" && "${schedulable_order_head}" != "0" ]]; then
221+
die "Expected fast-lane first in schedulable ordering, got schedule_priority=${schedulable_order_head}"
222+
fi
223+
224+
printf "${GREEN}[slow-lane-validate] Stack assertions passed${RESET}\n"
225+
}
226+
227+
while (( "$#" )); do
228+
case "$1" in
229+
--integration-only)
230+
RUN_INTEGRATION=true
231+
RUN_STACK=false
232+
shift
233+
;;
234+
--stack-only)
235+
RUN_INTEGRATION=false
236+
RUN_STACK=true
237+
shift
238+
;;
239+
--cap)
240+
[[ $# -ge 2 ]] || die "--cap requires a value"
241+
CAP="$2"
242+
shift 2
243+
;;
244+
--load-test)
245+
[[ $# -ge 2 ]] || die "--load-test requires a value"
246+
LOAD_TEST="$2"
247+
shift 2
248+
;;
249+
--bootstrap-timeout)
250+
[[ $# -ge 2 ]] || die "--bootstrap-timeout requires a value"
251+
BOOTSTRAP_TIMEOUT_SECONDS="$2"
252+
shift 2
253+
;;
254+
-h|--help)
255+
usage
256+
exit 0
257+
;;
258+
*)
259+
die "Unknown argument: $1"
260+
;;
261+
esac
262+
done
263+
264+
if [[ "${RUN_INTEGRATION}" == "true" ]]; then
265+
run_integration_assertions
266+
fi
267+
268+
if [[ "${RUN_STACK}" == "true" ]]; then
269+
run_stack_assertions
270+
fi
271+
272+
printf "${GREEN}[slow-lane-validate] All selected checks passed${RESET}\n"

0 commit comments

Comments
 (0)