@fairmint/canton-node-sdk 0.0.250 → 0.0.252
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -7
- package/build/src/Canton.d.ts +3 -1
- package/build/src/Canton.d.ts.map +1 -1
- package/build/src/Canton.js +2 -0
- package/build/src/Canton.js.map +1 -1
- package/build/src/clients/ledger-json-api/operations/v2/state/get-active-contracts.d.ts +1 -0
- package/build/src/clients/ledger-json-api/operations/v2/state/get-active-contracts.d.ts.map +1 -1
- package/build/src/clients/ledger-json-api/operations/v2/state/get-active-contracts.js +14 -1
- package/build/src/clients/ledger-json-api/operations/v2/state/get-active-contracts.js.map +1 -1
- package/build/src/core/BaseClient.d.ts +6 -1
- package/build/src/core/BaseClient.d.ts.map +1 -1
- package/build/src/core/BaseClient.js +11 -3
- package/build/src/core/BaseClient.js.map +1 -1
- package/build/src/core/auth/AuthenticationManager.d.ts +21 -2
- package/build/src/core/auth/AuthenticationManager.d.ts.map +1 -1
- package/build/src/core/auth/AuthenticationManager.js +57 -6
- package/build/src/core/auth/AuthenticationManager.js.map +1 -1
- package/build/src/core/errors.d.ts +13 -0
- package/build/src/core/errors.d.ts.map +1 -1
- package/build/src/core/errors.js +18 -1
- package/build/src/core/errors.js.map +1 -1
- package/build/src/core/http/HttpClient.d.ts +49 -1
- package/build/src/core/http/HttpClient.d.ts.map +1 -1
- package/build/src/core/http/HttpClient.js +111 -7
- package/build/src/core/http/HttpClient.js.map +1 -1
- package/build/src/core/types.d.ts +11 -0
- package/build/src/core/types.d.ts.map +1 -1
- package/build/src/core/ws/WebSocketClient.d.ts +6 -9
- package/build/src/core/ws/WebSocketClient.d.ts.map +1 -1
- package/build/src/core/ws/WebSocketClient.js +40 -0
- package/build/src/core/ws/WebSocketClient.js.map +1 -1
- package/package.json +14 -15
- package/bin/canton-localnet +0 -213
- package/scripts/localnet-cloud.sh +0 -1178
|
@@ -1,1178 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
-
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
6
|
-
PROJECT_ROOT="${CANTON_LOCALNET_PROJECT_ROOT:-$(pwd)}"
|
|
7
|
-
QUICKSTART_DIR="${CANTON_LOCALNET_QUICKSTART_DIR:-${REPO_ROOT}/libs/cn-quickstart/quickstart}"
|
|
8
|
-
DOCKERD_PID_FILE="/tmp/localnet-dockerd.pid"
|
|
9
|
-
DOCKERD_LOG_FILE="/tmp/localnet-dockerd.log"
|
|
10
|
-
HOSTS_ENTRY="127.0.0.1 scan.localhost sv.localhost wallet.localhost"
|
|
11
|
-
CURL_CONNECT_TIMEOUT=2
|
|
12
|
-
CURL_MAX_TIME=5
|
|
13
|
-
LOCALNET_SPLICE_VERSION="${CANTON_LOCALNET_SPLICE_VERSION:-}"
|
|
14
|
-
LOCALNET_SCRIBE_VERSION="${CANTON_LOCALNET_SCRIBE_VERSION:-}"
|
|
15
|
-
LOCALNET_PROTOCOL_VERSION="${CANTON_LOCALNET_PROTOCOL_VERSION:-}"
|
|
16
|
-
VALIDATOR_READY_ATTEMPTS="${CANTON_LOCALNET_VALIDATOR_READY_ATTEMPTS:-90}"
|
|
17
|
-
SCAN_READY_ATTEMPTS="${CANTON_LOCALNET_SCAN_READY_ATTEMPTS:-90}"
|
|
18
|
-
SPLICE_CONFIG_PENDING_KEY="CANTON_NODE_SDK_SPLICE_CONFIG_PENDING"
|
|
19
|
-
SPLICE_CONFIG_CHANGED="false"
|
|
20
|
-
|
|
21
|
-
log() {
|
|
22
|
-
printf '[localnet] %s\n' "$*"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
is_truthy() {
|
|
26
|
-
case "${1,,}" in
|
|
27
|
-
1 | true | yes | on)
|
|
28
|
-
return 0
|
|
29
|
-
;;
|
|
30
|
-
*)
|
|
31
|
-
return 1
|
|
32
|
-
;;
|
|
33
|
-
esac
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
require_command() {
|
|
37
|
-
local cmd="$1"
|
|
38
|
-
if ! command -v "${cmd}" >/dev/null 2>&1; then
|
|
39
|
-
log "Missing required command: ${cmd}"
|
|
40
|
-
exit 1
|
|
41
|
-
fi
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
require_positive_integer() {
|
|
45
|
-
local name="$1"
|
|
46
|
-
local value="$2"
|
|
47
|
-
|
|
48
|
-
if [[ ! "${value}" =~ ^[1-9][0-9]*$ ]]; then
|
|
49
|
-
log "${name} must be a positive integer; received '${value}'."
|
|
50
|
-
exit 1
|
|
51
|
-
fi
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
validate_configuration() {
|
|
55
|
-
require_positive_integer "CANTON_LOCALNET_VALIDATOR_READY_ATTEMPTS" "${VALIDATOR_READY_ATTEMPTS}"
|
|
56
|
-
require_positive_integer "CANTON_LOCALNET_SCAN_READY_ATTEMPTS" "${SCAN_READY_ATTEMPTS}"
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
ensure_sudo() {
|
|
60
|
-
if ! sudo_noninteractive_available; then
|
|
61
|
-
log "Passwordless sudo is required in this cloud environment."
|
|
62
|
-
exit 1
|
|
63
|
-
fi
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
sudo_noninteractive_available() {
|
|
67
|
-
command -v sudo >/dev/null 2>&1 && sudo -n true >/dev/null 2>&1
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
ensure_docker_packages() {
|
|
71
|
-
if command -v docker >/dev/null 2>&1 \
|
|
72
|
-
&& docker compose version >/dev/null 2>&1; then
|
|
73
|
-
return
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
if ! sudo_noninteractive_available || ! command -v apt-get >/dev/null 2>&1; then
|
|
77
|
-
log "Docker with Compose is required. Install Docker and start it, then retry."
|
|
78
|
-
exit 1
|
|
79
|
-
fi
|
|
80
|
-
|
|
81
|
-
log "Installing Docker packages..."
|
|
82
|
-
sudo apt-get update
|
|
83
|
-
sudo apt-get install -y docker.io docker-compose docker-compose-v2
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
ensure_legacy_iptables() {
|
|
87
|
-
if ! command -v update-alternatives >/dev/null 2>&1 \
|
|
88
|
-
|| [[ ! -x /usr/sbin/iptables-legacy || ! -x /usr/sbin/ip6tables-legacy ]]; then
|
|
89
|
-
log "iptables legacy binaries unavailable; skipping backend switch."
|
|
90
|
-
return
|
|
91
|
-
fi
|
|
92
|
-
|
|
93
|
-
if ! iptables --version 2>/dev/null | grep -q 'legacy'; then
|
|
94
|
-
log "Switching iptables to legacy backend..."
|
|
95
|
-
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
if ! ip6tables --version 2>/dev/null | grep -q 'legacy'; then
|
|
99
|
-
log "Switching ip6tables to legacy backend..."
|
|
100
|
-
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
|
|
101
|
-
fi
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
docker_ready() {
|
|
105
|
-
docker info >/dev/null 2>&1 || (sudo_noninteractive_available && sudo docker info >/dev/null 2>&1)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
configure_docker_socket_permissions() {
|
|
109
|
-
if [[ ! -S /var/run/docker.sock ]]; then
|
|
110
|
-
return
|
|
111
|
-
fi
|
|
112
|
-
|
|
113
|
-
if ! sudo_noninteractive_available; then
|
|
114
|
-
return
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
sudo groupadd -f docker >/dev/null 2>&1 || true
|
|
118
|
-
sudo chown root:docker /var/run/docker.sock >/dev/null 2>&1 || true
|
|
119
|
-
sudo chmod 660 /var/run/docker.sock || true
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
run_docker() {
|
|
123
|
-
if docker info >/dev/null 2>&1; then
|
|
124
|
-
docker "$@"
|
|
125
|
-
return
|
|
126
|
-
fi
|
|
127
|
-
if ! sudo_noninteractive_available || ! sudo docker info >/dev/null 2>&1; then
|
|
128
|
-
log "Docker daemon is not available."
|
|
129
|
-
exit 1
|
|
130
|
-
fi
|
|
131
|
-
sudo docker "$@"
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
resolve_quickstart_image_tag() {
|
|
135
|
-
local env_file=""
|
|
136
|
-
local splice_version=""
|
|
137
|
-
local parsed_value=""
|
|
138
|
-
|
|
139
|
-
for env_file in "${QUICKSTART_DIR}/.env" "${QUICKSTART_DIR}/.env.local"; do
|
|
140
|
-
if [[ ! -f "${env_file}" ]]; then
|
|
141
|
-
continue
|
|
142
|
-
fi
|
|
143
|
-
|
|
144
|
-
parsed_value="$(awk -F= -v key="SPLICE_VERSION" '
|
|
145
|
-
$0 ~ /^[[:space:]]*#/ { next }
|
|
146
|
-
$1 == key {
|
|
147
|
-
value = substr($0, index($0, "=") + 1)
|
|
148
|
-
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
|
149
|
-
gsub(/^"|"$/, "", value)
|
|
150
|
-
gsub(/^'\''|'\''$/, "", value)
|
|
151
|
-
parsed = value
|
|
152
|
-
}
|
|
153
|
-
END {
|
|
154
|
-
if (parsed != "") {
|
|
155
|
-
print parsed
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
' "${env_file}")"
|
|
159
|
-
|
|
160
|
-
if [[ -n "${parsed_value}" ]]; then
|
|
161
|
-
splice_version="${parsed_value}"
|
|
162
|
-
fi
|
|
163
|
-
done
|
|
164
|
-
|
|
165
|
-
printf '%s' "${splice_version}"
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
run_quickstart_command() {
|
|
169
|
-
local command="$1"
|
|
170
|
-
local docker_shim_dir=""
|
|
171
|
-
local quickstart_image_tag=""
|
|
172
|
-
local quickstart_path="${HOME}/.dpm/bin:${HOME}/.daml/bin:${PATH}"
|
|
173
|
-
local status=0
|
|
174
|
-
|
|
175
|
-
quickstart_image_tag="$(resolve_quickstart_image_tag)"
|
|
176
|
-
|
|
177
|
-
if ! docker info >/dev/null 2>&1 && sudo docker info >/dev/null 2>&1; then
|
|
178
|
-
docker_shim_dir="$(mktemp -d "/tmp/canton-localnet-docker-shim.XXXXXX")"
|
|
179
|
-
cat >"${docker_shim_dir}/docker" <<'EOF'
|
|
180
|
-
#!/usr/bin/env bash
|
|
181
|
-
exec sudo -E docker "$@"
|
|
182
|
-
EOF
|
|
183
|
-
chmod +x "${docker_shim_dir}/docker"
|
|
184
|
-
log "Using sudo docker shim for quickstart command."
|
|
185
|
-
fi
|
|
186
|
-
|
|
187
|
-
if [[ -n "${docker_shim_dir}" ]]; then
|
|
188
|
-
quickstart_path="${docker_shim_dir}:${quickstart_path}"
|
|
189
|
-
fi
|
|
190
|
-
|
|
191
|
-
set +e
|
|
192
|
-
(
|
|
193
|
-
cd "${QUICKSTART_DIR}"
|
|
194
|
-
MODULES_DIR="${QUICKSTART_DIR}/docker/modules" \
|
|
195
|
-
LOCALNET_DIR="${QUICKSTART_DIR}/docker/modules/localnet" \
|
|
196
|
-
IMAGE_TAG="${quickstart_image_tag}" \
|
|
197
|
-
PATH="${quickstart_path}" \
|
|
198
|
-
bash -lc "${command}"
|
|
199
|
-
)
|
|
200
|
-
status=$?
|
|
201
|
-
set -e
|
|
202
|
-
|
|
203
|
-
if [[ -n "${docker_shim_dir}" ]]; then
|
|
204
|
-
rm -rf "${docker_shim_dir}"
|
|
205
|
-
fi
|
|
206
|
-
|
|
207
|
-
return "${status}"
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
run_quickstart_make() {
|
|
211
|
-
local target="$1"
|
|
212
|
-
run_quickstart_command "make ${target}"
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
run_infra_compose() {
|
|
216
|
-
local compose_args="$1"
|
|
217
|
-
local keycloak_compose_file=""
|
|
218
|
-
local keycloak_env_file=""
|
|
219
|
-
local keycloak_profile=""
|
|
220
|
-
|
|
221
|
-
if [[ "$(current_auth_mode)" == "oauth2" ]]; then
|
|
222
|
-
keycloak_compose_file='-f "${MODULES_DIR}/keycloak/compose.yaml"'
|
|
223
|
-
keycloak_env_file='--env-file "${MODULES_DIR}/keycloak/compose.env"'
|
|
224
|
-
keycloak_profile='--profile keycloak'
|
|
225
|
-
fi
|
|
226
|
-
|
|
227
|
-
run_quickstart_command "docker compose \
|
|
228
|
-
-f \"\${LOCALNET_DIR}/compose.yaml\" \
|
|
229
|
-
${keycloak_compose_file} \
|
|
230
|
-
--env-file .env \
|
|
231
|
-
--env-file .env.local \
|
|
232
|
-
--env-file \"\${LOCALNET_DIR}/compose.env\" \
|
|
233
|
-
--env-file \"\${LOCALNET_DIR}/env/common.env\" \
|
|
234
|
-
${keycloak_env_file} \
|
|
235
|
-
--profile app-provider \
|
|
236
|
-
--profile app-user \
|
|
237
|
-
--profile sv \
|
|
238
|
-
${keycloak_profile} \
|
|
239
|
-
${compose_args}"
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
start_docker_daemon() {
|
|
243
|
-
local dockerd_pid=""
|
|
244
|
-
|
|
245
|
-
if docker_ready; then
|
|
246
|
-
configure_docker_socket_permissions
|
|
247
|
-
return
|
|
248
|
-
fi
|
|
249
|
-
|
|
250
|
-
if ! sudo_noninteractive_available; then
|
|
251
|
-
log "Docker daemon is not running. Start Docker and retry."
|
|
252
|
-
exit 1
|
|
253
|
-
fi
|
|
254
|
-
|
|
255
|
-
ensure_legacy_iptables
|
|
256
|
-
|
|
257
|
-
log "Starting Docker daemon with vfs storage driver..."
|
|
258
|
-
sudo nohup dockerd --host=unix:///var/run/docker.sock --pidfile="${DOCKERD_PID_FILE}" --storage-driver=vfs >"${DOCKERD_LOG_FILE}" 2>&1 &
|
|
259
|
-
|
|
260
|
-
for _ in $(seq 1 60); do
|
|
261
|
-
if sudo docker info >/dev/null 2>&1; then
|
|
262
|
-
configure_docker_socket_permissions
|
|
263
|
-
return
|
|
264
|
-
fi
|
|
265
|
-
if [[ -f "${DOCKERD_PID_FILE}" ]]; then
|
|
266
|
-
dockerd_pid="$(cat "${DOCKERD_PID_FILE}" 2>/dev/null || true)"
|
|
267
|
-
if [[ -n "${dockerd_pid}" ]] && ! ps -p "${dockerd_pid}" >/dev/null 2>&1; then
|
|
268
|
-
log "Docker daemon exited before becoming ready."
|
|
269
|
-
log "Inspect ${DOCKERD_LOG_FILE}."
|
|
270
|
-
exit 1
|
|
271
|
-
fi
|
|
272
|
-
fi
|
|
273
|
-
sleep 1
|
|
274
|
-
done
|
|
275
|
-
|
|
276
|
-
log "Docker failed to start. Inspect ${DOCKERD_LOG_FILE}."
|
|
277
|
-
exit 1
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
ensure_submodules() {
|
|
281
|
-
if [[ -n "${CANTON_LOCALNET_QUICKSTART_DIR:-}" ]]; then
|
|
282
|
-
if [[ -d "${QUICKSTART_DIR}" ]]; then
|
|
283
|
-
return
|
|
284
|
-
fi
|
|
285
|
-
log "Configured CANTON_LOCALNET_QUICKSTART_DIR does not exist: ${QUICKSTART_DIR}"
|
|
286
|
-
exit 1
|
|
287
|
-
fi
|
|
288
|
-
|
|
289
|
-
if [[ -d "${REPO_ROOT}/libs/splice" && -d "${QUICKSTART_DIR}" ]]; then
|
|
290
|
-
return
|
|
291
|
-
fi
|
|
292
|
-
|
|
293
|
-
require_command git
|
|
294
|
-
|
|
295
|
-
if ! git -C "${REPO_ROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
296
|
-
log "Missing localnet assets and not in a git checkout. Install from npm with bundled assets or clone SDK with submodules."
|
|
297
|
-
exit 1
|
|
298
|
-
fi
|
|
299
|
-
|
|
300
|
-
if [[ ! -d "${REPO_ROOT}/libs/splice" ]]; then
|
|
301
|
-
log "Initializing libs/splice submodule..."
|
|
302
|
-
git -C "${REPO_ROOT}" submodule update --init --depth 1 libs/splice
|
|
303
|
-
fi
|
|
304
|
-
|
|
305
|
-
if [[ ! -d "${REPO_ROOT}/libs/cn-quickstart" ]]; then
|
|
306
|
-
log "Initializing libs/cn-quickstart submodule..."
|
|
307
|
-
git -C "${REPO_ROOT}" submodule update --init --recursive libs/cn-quickstart
|
|
308
|
-
fi
|
|
309
|
-
|
|
310
|
-
if [[ ! -d "${QUICKSTART_DIR}" ]]; then
|
|
311
|
-
log "cn-quickstart directory not found after submodule init."
|
|
312
|
-
exit 1
|
|
313
|
-
fi
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
ensure_hosts_entries() {
|
|
317
|
-
if ! grep -Eq '(^|[[:space:]])scan\.localhost([[:space:]]|$)' /etc/hosts \
|
|
318
|
-
|| ! grep -Eq '(^|[[:space:]])sv\.localhost([[:space:]]|$)' /etc/hosts \
|
|
319
|
-
|| ! grep -Eq '(^|[[:space:]])wallet\.localhost([[:space:]]|$)' /etc/hosts; then
|
|
320
|
-
log "Adding localnet host aliases to /etc/hosts..."
|
|
321
|
-
if sudo_noninteractive_available; then
|
|
322
|
-
echo "${HOSTS_ENTRY}" | sudo tee -a /etc/hosts >/dev/null
|
|
323
|
-
return
|
|
324
|
-
fi
|
|
325
|
-
if [[ -t 0 ]] && command -v sudo >/dev/null 2>&1; then
|
|
326
|
-
echo "${HOSTS_ENTRY}" | sudo tee -a /etc/hosts >/dev/null
|
|
327
|
-
return
|
|
328
|
-
fi
|
|
329
|
-
log "Missing localnet host aliases. Add this line to /etc/hosts, then retry:"
|
|
330
|
-
log "${HOSTS_ENTRY}"
|
|
331
|
-
exit 1
|
|
332
|
-
fi
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
set_quickstart_env_value() {
|
|
336
|
-
local file="$1"
|
|
337
|
-
local key="$2"
|
|
338
|
-
local value="$3"
|
|
339
|
-
|
|
340
|
-
if [[ ! -f "${file}" ]]; then
|
|
341
|
-
return
|
|
342
|
-
fi
|
|
343
|
-
|
|
344
|
-
if grep -Eq "^${key}=" "${file}"; then
|
|
345
|
-
KEY="${key}" VALUE="${value}" perl -0pi -e '
|
|
346
|
-
my $key = $ENV{"KEY"};
|
|
347
|
-
my $value = $ENV{"VALUE"};
|
|
348
|
-
s/^\Q$key\E=.*/$key=$value/m;
|
|
349
|
-
' "${file}"
|
|
350
|
-
else
|
|
351
|
-
printf '\n%s=%s\n' "${key}" "${value}" >>"${file}"
|
|
352
|
-
fi
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
patch_quickstart_canton_healthcheck() {
|
|
356
|
-
local healthcheck="${QUICKSTART_DIR}/docker/modules/localnet/docker/canton/health-check.sh"
|
|
357
|
-
|
|
358
|
-
if [[ ! -f "${healthcheck}" ]]; then
|
|
359
|
-
log "Canton quickstart healthcheck not found: ${healthcheck}"
|
|
360
|
-
exit 1
|
|
361
|
-
fi
|
|
362
|
-
|
|
363
|
-
cat >"${healthcheck}" <<'EOF'
|
|
364
|
-
#!/bin/bash
|
|
365
|
-
set -eou pipefail
|
|
366
|
-
|
|
367
|
-
http_check() {
|
|
368
|
-
local port="$1"
|
|
369
|
-
local path="$2"
|
|
370
|
-
local status=""
|
|
371
|
-
|
|
372
|
-
echo "Checking http://localhost:${port}${path}"
|
|
373
|
-
exec 3<>"/dev/tcp/127.0.0.1/${port}"
|
|
374
|
-
printf 'GET %s HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' "${path}" >&3
|
|
375
|
-
IFS=$'\r' read -r status <&3 || true
|
|
376
|
-
exec 3<&-
|
|
377
|
-
exec 3>&-
|
|
378
|
-
|
|
379
|
-
case "${status}" in
|
|
380
|
-
HTTP/*\ 2*) ;;
|
|
381
|
-
*)
|
|
382
|
-
echo "Unexpected status from ${port}${path}: ${status}"
|
|
383
|
-
return 1
|
|
384
|
-
;;
|
|
385
|
-
esac
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if [ "${APP_USER_PROFILE:-off}" = "on" ]; then
|
|
389
|
-
http_check "2${CANTON_HTTP_HEALTHCHECK_PORT_SUFFIX}" "/health"
|
|
390
|
-
fi
|
|
391
|
-
if [ "${APP_PROVIDER_PROFILE:-off}" = "on" ]; then
|
|
392
|
-
http_check "3${CANTON_HTTP_HEALTHCHECK_PORT_SUFFIX}" "/health"
|
|
393
|
-
fi
|
|
394
|
-
if [ "${SV_PROFILE:-off}" = "on" ]; then
|
|
395
|
-
http_check "4${CANTON_HTTP_HEALTHCHECK_PORT_SUFFIX}" "/health"
|
|
396
|
-
fi
|
|
397
|
-
EOF
|
|
398
|
-
chmod +x "${healthcheck}"
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
patch_quickstart_splice_healthcheck() {
|
|
402
|
-
local healthcheck="${QUICKSTART_DIR}/docker/modules/localnet/docker/splice/health-check.sh"
|
|
403
|
-
|
|
404
|
-
if [[ ! -f "${healthcheck}" ]]; then
|
|
405
|
-
log "Splice quickstart healthcheck not found: ${healthcheck}"
|
|
406
|
-
exit 1
|
|
407
|
-
fi
|
|
408
|
-
|
|
409
|
-
cat >"${healthcheck}" <<'EOF'
|
|
410
|
-
#!/bin/bash
|
|
411
|
-
set -eou pipefail
|
|
412
|
-
|
|
413
|
-
http_check() {
|
|
414
|
-
local port="$1"
|
|
415
|
-
local path="$2"
|
|
416
|
-
local status=""
|
|
417
|
-
|
|
418
|
-
echo "Checking http://localhost:${port}${path}"
|
|
419
|
-
exec 3<>"/dev/tcp/127.0.0.1/${port}"
|
|
420
|
-
printf 'GET %s HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' "${path}" >&3
|
|
421
|
-
IFS=$'\r' read -r status <&3 || true
|
|
422
|
-
exec 3<&-
|
|
423
|
-
exec 3>&-
|
|
424
|
-
|
|
425
|
-
case "${status}" in
|
|
426
|
-
HTTP/*\ 2*) ;;
|
|
427
|
-
*)
|
|
428
|
-
echo "Unexpected status from ${port}${path}: ${status}"
|
|
429
|
-
return 1
|
|
430
|
-
;;
|
|
431
|
-
esac
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
if [ "${APP_USER_PROFILE:-off}" = "on" ]; then
|
|
435
|
-
http_check "2${VALIDATOR_ADMIN_API_PORT_SUFFIX}" "/api/validator/readyz"
|
|
436
|
-
fi
|
|
437
|
-
if [ "${APP_PROVIDER_PROFILE:-off}" = "on" ]; then
|
|
438
|
-
http_check "3${VALIDATOR_ADMIN_API_PORT_SUFFIX}" "/api/validator/readyz"
|
|
439
|
-
fi
|
|
440
|
-
if [ "${SV_PROFILE:-off}" = "on" ]; then
|
|
441
|
-
http_check "4${VALIDATOR_ADMIN_API_PORT_SUFFIX}" "/api/validator/readyz"
|
|
442
|
-
http_check "5012" "/api/scan/readyz"
|
|
443
|
-
http_check "5014" "/api/sv/readyz"
|
|
444
|
-
fi
|
|
445
|
-
EOF
|
|
446
|
-
chmod +x "${healthcheck}"
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
configure_quickstart_localnet() {
|
|
450
|
-
local env_file=""
|
|
451
|
-
local canton_conf="${QUICKSTART_DIR}/docker/modules/localnet/conf/canton/app.conf"
|
|
452
|
-
local splice_sv_conf="${QUICKSTART_DIR}/docker/modules/localnet/conf/splice/sv/app.conf"
|
|
453
|
-
|
|
454
|
-
SPLICE_CONFIG_CHANGED="false"
|
|
455
|
-
if [[ "$(quickstart_env_value "${SPLICE_CONFIG_PENDING_KEY}")" == "true" ]]; then
|
|
456
|
-
SPLICE_CONFIG_CHANGED="true"
|
|
457
|
-
fi
|
|
458
|
-
|
|
459
|
-
for env_file in "${QUICKSTART_DIR}/.env" "${QUICKSTART_DIR}/.env.local"; do
|
|
460
|
-
if [[ -n "${LOCALNET_SPLICE_VERSION}" ]]; then
|
|
461
|
-
set_quickstart_env_value "${env_file}" "SPLICE_VERSION" "${LOCALNET_SPLICE_VERSION}"
|
|
462
|
-
fi
|
|
463
|
-
if [[ -n "${LOCALNET_SCRIBE_VERSION}" ]]; then
|
|
464
|
-
set_quickstart_env_value "${env_file}" "SCRIBE_VERSION" "${LOCALNET_SCRIBE_VERSION}"
|
|
465
|
-
fi
|
|
466
|
-
if [[ -n "${CANTON_LOCALNET_DAML_RUNTIME_VERSION:-}" ]]; then
|
|
467
|
-
set_quickstart_env_value "${env_file}" "DAML_RUNTIME_VERSION" "${CANTON_LOCALNET_DAML_RUNTIME_VERSION}"
|
|
468
|
-
fi
|
|
469
|
-
done
|
|
470
|
-
|
|
471
|
-
if [[ ! -f "${canton_conf}" ]]; then
|
|
472
|
-
log "Canton quickstart config not found: ${canton_conf}"
|
|
473
|
-
exit 1
|
|
474
|
-
fi
|
|
475
|
-
|
|
476
|
-
if [[ ! -f "${splice_sv_conf}" ]]; then
|
|
477
|
-
log "Splice SV quickstart config not found: ${splice_sv_conf}"
|
|
478
|
-
exit 1
|
|
479
|
-
fi
|
|
480
|
-
|
|
481
|
-
if ! grep -Eq '^[[:space:]]*canton\.scan-apps\.scan-app\.enable-forced-acs-snapshots[[:space:]]*=[[:space:]]*(true|yes|on)[[:space:]]*$' "${splice_sv_conf}"; then
|
|
482
|
-
cat >>"${splice_sv_conf}" <<'EOF'
|
|
483
|
-
|
|
484
|
-
# LocalNet integration tests use this endpoint to create deterministic snapshot-backed fixtures.
|
|
485
|
-
canton.scan-apps.scan-app.enable-forced-acs-snapshots = true
|
|
486
|
-
EOF
|
|
487
|
-
set_quickstart_env_value "${QUICKSTART_DIR}/.env.local" "${SPLICE_CONFIG_PENDING_KEY}" "true"
|
|
488
|
-
SPLICE_CONFIG_CHANGED="true"
|
|
489
|
-
fi
|
|
490
|
-
|
|
491
|
-
if [[ -n "${LOCALNET_PROTOCOL_VERSION}" ]]; then
|
|
492
|
-
PROTOCOL_VERSION="${LOCALNET_PROTOCOL_VERSION}" perl -0pi -e '
|
|
493
|
-
my $protocol_version = $ENV{"PROTOCOL_VERSION"};
|
|
494
|
-
s/initial-protocol-version = \d+/initial-protocol-version = $protocol_version/g;
|
|
495
|
-
s/(non-standard-config = yes\n)(?![[:space:]]*alpha-version-support = yes)/$1 alpha-version-support = yes\n/;
|
|
496
|
-
s/(initial-protocol-version = \d+\n)(?![[:space:]]*alpha-version-support = yes)/$1 alpha-version-support = yes\n/;
|
|
497
|
-
' "${canton_conf}"
|
|
498
|
-
fi
|
|
499
|
-
|
|
500
|
-
patch_quickstart_canton_healthcheck
|
|
501
|
-
patch_quickstart_splice_healthcheck
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
requested_auth_mode() {
|
|
505
|
-
printf '%s' "${CANTON_LOCALNET_AUTH_MODE:-oauth2}"
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
quickstart_env_value() {
|
|
509
|
-
local key="$1"
|
|
510
|
-
local parsed_value=""
|
|
511
|
-
|
|
512
|
-
if [[ -f "${QUICKSTART_DIR}/.env.local" ]]; then
|
|
513
|
-
parsed_value="$(awk -F= -v key="${key}" '
|
|
514
|
-
$0 ~ /^[[:space:]]*#/ { next }
|
|
515
|
-
$1 == key {
|
|
516
|
-
value = substr($0, index($0, "=") + 1)
|
|
517
|
-
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
|
518
|
-
gsub(/^"|"$/, "", value)
|
|
519
|
-
gsub(/^'\''|'\''$/, "", value)
|
|
520
|
-
parsed = value
|
|
521
|
-
}
|
|
522
|
-
END {
|
|
523
|
-
if (parsed != "") {
|
|
524
|
-
print parsed
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
' "${QUICKSTART_DIR}/.env.local")"
|
|
528
|
-
fi
|
|
529
|
-
|
|
530
|
-
printf '%s' "${parsed_value}"
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
configured_auth_mode() {
|
|
534
|
-
quickstart_env_value "AUTH_MODE"
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
quickstart_profile_config_complete() {
|
|
538
|
-
local key=""
|
|
539
|
-
|
|
540
|
-
for key in OBSERVABILITY_ENABLED AUTH_MODE PARTY_HINT TEST_MODE; do
|
|
541
|
-
if [[ -z "$(quickstart_env_value "${key}")" ]]; then
|
|
542
|
-
return 1
|
|
543
|
-
fi
|
|
544
|
-
done
|
|
545
|
-
|
|
546
|
-
return 0
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
verify_configured_auth_mode() {
|
|
550
|
-
local expected_auth_mode="$1"
|
|
551
|
-
local actual_auth_mode=""
|
|
552
|
-
|
|
553
|
-
actual_auth_mode="$(configured_auth_mode)"
|
|
554
|
-
if [[ "${actual_auth_mode}" != "${expected_auth_mode}" ]]; then
|
|
555
|
-
log "cn-quickstart setup did not configure AUTH_MODE=${expected_auth_mode}; found '${actual_auth_mode:-unset}'."
|
|
556
|
-
exit 1
|
|
557
|
-
fi
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
current_auth_mode() {
|
|
561
|
-
local configured=""
|
|
562
|
-
|
|
563
|
-
configured="$(configured_auth_mode)"
|
|
564
|
-
if [[ -n "${configured}" ]]; then
|
|
565
|
-
printf '%s' "${configured}"
|
|
566
|
-
return
|
|
567
|
-
fi
|
|
568
|
-
|
|
569
|
-
requested_auth_mode
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
run_quickstart_setup() {
|
|
573
|
-
local auth_mode=""
|
|
574
|
-
|
|
575
|
-
auth_mode="$(requested_auth_mode)"
|
|
576
|
-
case "${auth_mode}" in
|
|
577
|
-
shared-secret)
|
|
578
|
-
log "Running cn-quickstart setup (shared-secret mode)..."
|
|
579
|
-
(
|
|
580
|
-
cd "${QUICKSTART_DIR}"
|
|
581
|
-
printf 'Y\nn\n\n' | make setup || true
|
|
582
|
-
)
|
|
583
|
-
verify_configured_auth_mode "shared-secret"
|
|
584
|
-
;;
|
|
585
|
-
oauth2)
|
|
586
|
-
log "Running cn-quickstart setup (OAuth2 enabled)..."
|
|
587
|
-
(
|
|
588
|
-
cd "${QUICKSTART_DIR}"
|
|
589
|
-
# Match CI behavior first, then fall back to prompt-based answers for newer setup flows.
|
|
590
|
-
echo "2" | make setup || true
|
|
591
|
-
if [[ "$(configured_auth_mode)" != "oauth2" ]]; then
|
|
592
|
-
printf 'y\ny\n\nn\n' | make setup
|
|
593
|
-
fi
|
|
594
|
-
)
|
|
595
|
-
verify_configured_auth_mode "oauth2"
|
|
596
|
-
;;
|
|
597
|
-
*)
|
|
598
|
-
log "Unsupported CANTON_LOCALNET_AUTH_MODE: ${auth_mode}"
|
|
599
|
-
exit 1
|
|
600
|
-
;;
|
|
601
|
-
esac
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
quickstart_setup() {
|
|
605
|
-
if [[ ! -f "${QUICKSTART_DIR}/.env.local" ]]; then
|
|
606
|
-
run_quickstart_setup
|
|
607
|
-
elif ! quickstart_profile_config_complete; then
|
|
608
|
-
log "Regenerating incomplete cn-quickstart config."
|
|
609
|
-
rm -f "${QUICKSTART_DIR}/.env.local"
|
|
610
|
-
run_quickstart_setup
|
|
611
|
-
elif [[ -n "${CANTON_LOCALNET_AUTH_MODE:-}" && "$(configured_auth_mode)" != "${CANTON_LOCALNET_AUTH_MODE}" ]]; then
|
|
612
|
-
log "Regenerating cn-quickstart config for CANTON_LOCALNET_AUTH_MODE=${CANTON_LOCALNET_AUTH_MODE}."
|
|
613
|
-
rm -f "${QUICKSTART_DIR}/.env.local"
|
|
614
|
-
run_quickstart_setup
|
|
615
|
-
else
|
|
616
|
-
log "Reusing existing ${QUICKSTART_DIR}/.env.local."
|
|
617
|
-
fi
|
|
618
|
-
|
|
619
|
-
if [[ ! -f "${QUICKSTART_DIR}/.env.local" ]]; then
|
|
620
|
-
log "cn-quickstart setup failed: ${QUICKSTART_DIR}/.env.local was not created."
|
|
621
|
-
exit 1
|
|
622
|
-
fi
|
|
623
|
-
|
|
624
|
-
configure_quickstart_localnet
|
|
625
|
-
|
|
626
|
-
if quickstart_infra_only_enabled; then
|
|
627
|
-
log "Skipping Daml SDK install for infrastructure-only localnet."
|
|
628
|
-
return
|
|
629
|
-
fi
|
|
630
|
-
|
|
631
|
-
if [[ ! -x "${HOME}/.daml/bin/daml" ]]; then
|
|
632
|
-
log "Installing Daml SDK..."
|
|
633
|
-
(
|
|
634
|
-
cd "${QUICKSTART_DIR}"
|
|
635
|
-
make install-daml-sdk
|
|
636
|
-
)
|
|
637
|
-
else
|
|
638
|
-
log "Reusing existing Daml SDK at ${HOME}/.daml/bin/daml."
|
|
639
|
-
fi
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
quickstart_fast_start_enabled() {
|
|
643
|
-
is_truthy "${CANTON_LOCALNET_FAST_START:-true}"
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
quickstart_force_full_start() {
|
|
647
|
-
is_truthy "${CANTON_LOCALNET_FORCE_FULL_START:-false}"
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
quickstart_infra_only_enabled() {
|
|
651
|
-
is_truthy "${CANTON_LOCALNET_INFRA_ONLY:-false}"
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
quickstart_build_artifacts_ready() {
|
|
655
|
-
local missing_paths=()
|
|
656
|
-
|
|
657
|
-
if [[ ! -f "${QUICKSTART_DIR}/backend/build/distributions/backend.tar" ]]; then
|
|
658
|
-
missing_paths+=("backend/build/distributions/backend.tar")
|
|
659
|
-
fi
|
|
660
|
-
|
|
661
|
-
if [[ ! -d "${QUICKSTART_DIR}/frontend/dist" ]]; then
|
|
662
|
-
missing_paths+=("frontend/dist")
|
|
663
|
-
fi
|
|
664
|
-
|
|
665
|
-
if ! compgen -G "${QUICKSTART_DIR}/daml/licensing/.daml/dist/*.dar" >/dev/null; then
|
|
666
|
-
missing_paths+=("daml/licensing/.daml/dist/*.dar")
|
|
667
|
-
fi
|
|
668
|
-
|
|
669
|
-
if ! compgen -G "${QUICKSTART_DIR}/backend/build/otel-agent/opentelemetry-javaagent-*.jar" >/dev/null; then
|
|
670
|
-
missing_paths+=("backend/build/otel-agent/opentelemetry-javaagent-*.jar")
|
|
671
|
-
fi
|
|
672
|
-
|
|
673
|
-
if [[ ${#missing_paths[@]} -gt 0 ]]; then
|
|
674
|
-
log "Fast start unavailable; missing quickstart build artifacts: ${missing_paths[*]}"
|
|
675
|
-
return 1
|
|
676
|
-
fi
|
|
677
|
-
|
|
678
|
-
return 0
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
extract_quickstart_compose_up_command() {
|
|
682
|
-
(
|
|
683
|
-
cd "${QUICKSTART_DIR}"
|
|
684
|
-
make -n start 2>/dev/null | awk '/docker compose .* up -d --no-recreate/{line=$0} END{print line}'
|
|
685
|
-
)
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
try_fast_start_localnet() {
|
|
689
|
-
local compose_up_command=""
|
|
690
|
-
|
|
691
|
-
compose_up_command="$(extract_quickstart_compose_up_command)"
|
|
692
|
-
if [[ -z "${compose_up_command}" ]]; then
|
|
693
|
-
log "Unable to determine quickstart compose startup command."
|
|
694
|
-
return 1
|
|
695
|
-
fi
|
|
696
|
-
|
|
697
|
-
log "Starting cn-quickstart with fast path (skip quickstart rebuild)..."
|
|
698
|
-
run_quickstart_command "${compose_up_command}"
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
start_infra_only_localnet() {
|
|
702
|
-
log "Starting cn-quickstart LocalNet infrastructure only (skip quickstart app, PQS, and onboarding)."
|
|
703
|
-
run_infra_compose 'up -d --no-recreate'
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
splice_container_running() {
|
|
707
|
-
local running=""
|
|
708
|
-
|
|
709
|
-
running="$(run_docker inspect --format '{{.State.Running}}' splice 2>/dev/null || true)"
|
|
710
|
-
[[ "${running}" == "true" ]]
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
recreate_splice_after_config_change() {
|
|
714
|
-
local should_recreate="$1"
|
|
715
|
-
|
|
716
|
-
if [[ "${should_recreate}" != "true" ]]; then
|
|
717
|
-
return
|
|
718
|
-
fi
|
|
719
|
-
|
|
720
|
-
log "Recreating the running Splice service to apply updated LocalNet configuration."
|
|
721
|
-
run_infra_compose 'up -d --no-deps --force-recreate splice'
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
mark_splice_config_applied() {
|
|
725
|
-
if [[ "${SPLICE_CONFIG_CHANGED}" != "true" ]]; then
|
|
726
|
-
return
|
|
727
|
-
fi
|
|
728
|
-
|
|
729
|
-
set_quickstart_env_value "${QUICKSTART_DIR}/.env.local" "${SPLICE_CONFIG_PENDING_KEY}" "false"
|
|
730
|
-
SPLICE_CONFIG_CHANGED="false"
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
stop_infra_only_localnet() {
|
|
734
|
-
if [[ ! -f "${QUICKSTART_DIR}/.env.local" ]]; then
|
|
735
|
-
return
|
|
736
|
-
fi
|
|
737
|
-
|
|
738
|
-
log "Stopping cn-quickstart LocalNet infrastructure-only stack..."
|
|
739
|
-
run_infra_compose down || true
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
wait_for_services() {
|
|
743
|
-
local auth_mode=""
|
|
744
|
-
local code=""
|
|
745
|
-
local ledger_code=""
|
|
746
|
-
|
|
747
|
-
auth_mode="$(current_auth_mode)"
|
|
748
|
-
|
|
749
|
-
if [[ "${auth_mode}" == "oauth2" ]]; then
|
|
750
|
-
log "Waiting for Keycloak..."
|
|
751
|
-
for _ in $(seq 1 30); do
|
|
752
|
-
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then
|
|
753
|
-
break
|
|
754
|
-
fi
|
|
755
|
-
sleep 2
|
|
756
|
-
done
|
|
757
|
-
if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then
|
|
758
|
-
log "Keycloak did not become ready."
|
|
759
|
-
exit 1
|
|
760
|
-
fi
|
|
761
|
-
else
|
|
762
|
-
log "Checking for Keycloak (optional in ${auth_mode} mode)..."
|
|
763
|
-
for _ in $(seq 1 5); do
|
|
764
|
-
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then
|
|
765
|
-
log "Keycloak is ready."
|
|
766
|
-
break
|
|
767
|
-
fi
|
|
768
|
-
sleep 2
|
|
769
|
-
done
|
|
770
|
-
fi
|
|
771
|
-
|
|
772
|
-
log "Waiting for Validator API..."
|
|
773
|
-
for _ in $(seq 1 "${VALIDATOR_READY_ATTEMPTS}"); do
|
|
774
|
-
code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)"
|
|
775
|
-
if [[ "${code}" == "200" || "${code}" == "401" ]]; then
|
|
776
|
-
break
|
|
777
|
-
fi
|
|
778
|
-
sleep 2
|
|
779
|
-
done
|
|
780
|
-
code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)"
|
|
781
|
-
if [[ "${code}" != "200" && "${code}" != "401" ]]; then
|
|
782
|
-
log "Validator API did not become ready."
|
|
783
|
-
exit 1
|
|
784
|
-
fi
|
|
785
|
-
|
|
786
|
-
log "Waiting for Scan API..."
|
|
787
|
-
for _ in $(seq 1 "${SCAN_READY_ATTEMPTS}"); do
|
|
788
|
-
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then
|
|
789
|
-
break
|
|
790
|
-
fi
|
|
791
|
-
sleep 2
|
|
792
|
-
done
|
|
793
|
-
if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then
|
|
794
|
-
log "Scan API did not become ready."
|
|
795
|
-
exit 1
|
|
796
|
-
fi
|
|
797
|
-
|
|
798
|
-
log "Waiting for Ledger JSON API..."
|
|
799
|
-
for _ in $(seq 1 60); do
|
|
800
|
-
ledger_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3975/v2/version || true)"
|
|
801
|
-
if [[ "${ledger_code}" == "200" || "${ledger_code}" == "401" ]]; then
|
|
802
|
-
break
|
|
803
|
-
fi
|
|
804
|
-
sleep 2
|
|
805
|
-
done
|
|
806
|
-
ledger_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3975/v2/version || true)"
|
|
807
|
-
if [[ "${ledger_code}" != "200" && "${ledger_code}" != "401" ]]; then
|
|
808
|
-
log "Ledger JSON API did not become ready (HTTP ${ledger_code})."
|
|
809
|
-
exit 1
|
|
810
|
-
fi
|
|
811
|
-
|
|
812
|
-
log "All localnet services are ready."
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
start_localnet() {
|
|
816
|
-
local should_recreate_splice="false"
|
|
817
|
-
|
|
818
|
-
if [[ "${SPLICE_CONFIG_CHANGED}" == "true" ]] && splice_container_running; then
|
|
819
|
-
should_recreate_splice="true"
|
|
820
|
-
fi
|
|
821
|
-
|
|
822
|
-
if quickstart_force_full_start; then
|
|
823
|
-
log "Forcing full cn-quickstart start (CANTON_LOCALNET_FORCE_FULL_START=true)."
|
|
824
|
-
run_quickstart_make start
|
|
825
|
-
recreate_splice_after_config_change "${should_recreate_splice}"
|
|
826
|
-
wait_for_services
|
|
827
|
-
mark_splice_config_applied
|
|
828
|
-
return
|
|
829
|
-
fi
|
|
830
|
-
|
|
831
|
-
if quickstart_infra_only_enabled; then
|
|
832
|
-
start_infra_only_localnet
|
|
833
|
-
recreate_splice_after_config_change "${should_recreate_splice}"
|
|
834
|
-
wait_for_services
|
|
835
|
-
mark_splice_config_applied
|
|
836
|
-
return
|
|
837
|
-
fi
|
|
838
|
-
|
|
839
|
-
if quickstart_fast_start_enabled && quickstart_build_artifacts_ready; then
|
|
840
|
-
if try_fast_start_localnet; then
|
|
841
|
-
recreate_splice_after_config_change "${should_recreate_splice}"
|
|
842
|
-
wait_for_services
|
|
843
|
-
mark_splice_config_applied
|
|
844
|
-
return
|
|
845
|
-
fi
|
|
846
|
-
log "Fast start failed; falling back to full cn-quickstart start."
|
|
847
|
-
fi
|
|
848
|
-
|
|
849
|
-
log "Starting cn-quickstart with full build..."
|
|
850
|
-
run_quickstart_make start
|
|
851
|
-
recreate_splice_after_config_change "${should_recreate_splice}"
|
|
852
|
-
wait_for_services
|
|
853
|
-
mark_splice_config_applied
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
stop_localnet() {
|
|
857
|
-
if [[ ! -d "${QUICKSTART_DIR}" ]]; then
|
|
858
|
-
log "cn-quickstart directory not found; nothing to stop."
|
|
859
|
-
stop_managed_dockerd
|
|
860
|
-
return
|
|
861
|
-
fi
|
|
862
|
-
|
|
863
|
-
log "Stopping cn-quickstart..."
|
|
864
|
-
stop_infra_only_localnet
|
|
865
|
-
if [[ -f "${QUICKSTART_DIR}/Makefile" ]]; then
|
|
866
|
-
run_quickstart_make stop || true
|
|
867
|
-
else
|
|
868
|
-
log "Quickstart Makefile not found; skipping quickstart stop target."
|
|
869
|
-
fi
|
|
870
|
-
stop_managed_dockerd
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
stop_managed_dockerd() {
|
|
874
|
-
local pid=""
|
|
875
|
-
local cmd=""
|
|
876
|
-
|
|
877
|
-
if [[ ! -f "${DOCKERD_PID_FILE}" ]]; then
|
|
878
|
-
return
|
|
879
|
-
fi
|
|
880
|
-
|
|
881
|
-
pid="$(cat "${DOCKERD_PID_FILE}" 2>/dev/null || true)"
|
|
882
|
-
if [[ -z "${pid}" ]]; then
|
|
883
|
-
rm -f "${DOCKERD_PID_FILE}" "${DOCKERD_LOG_FILE}"
|
|
884
|
-
return
|
|
885
|
-
fi
|
|
886
|
-
|
|
887
|
-
if ! ps -p "${pid}" >/dev/null 2>&1; then
|
|
888
|
-
rm -f "${DOCKERD_PID_FILE}" "${DOCKERD_LOG_FILE}"
|
|
889
|
-
return
|
|
890
|
-
fi
|
|
891
|
-
|
|
892
|
-
cmd="$(ps -p "${pid}" -o comm= 2>/dev/null | tr -d '[:space:]')"
|
|
893
|
-
if [[ "${cmd}" != "dockerd" ]]; then
|
|
894
|
-
log "PID ${pid} is not dockerd; skipping daemon cleanup."
|
|
895
|
-
rm -f "${DOCKERD_PID_FILE}"
|
|
896
|
-
return
|
|
897
|
-
fi
|
|
898
|
-
|
|
899
|
-
if ! sudo -n true >/dev/null 2>&1; then
|
|
900
|
-
log "Cannot stop managed dockerd without passwordless sudo."
|
|
901
|
-
return
|
|
902
|
-
fi
|
|
903
|
-
|
|
904
|
-
log "Stopping managed dockerd (pid ${pid})..."
|
|
905
|
-
sudo kill -TERM "${pid}" >/dev/null 2>&1 || true
|
|
906
|
-
for _ in $(seq 1 10); do
|
|
907
|
-
if ! ps -p "${pid}" >/dev/null 2>&1; then
|
|
908
|
-
break
|
|
909
|
-
fi
|
|
910
|
-
sleep 1
|
|
911
|
-
done
|
|
912
|
-
if ps -p "${pid}" >/dev/null 2>&1; then
|
|
913
|
-
sudo kill -KILL "${pid}" >/dev/null 2>&1 || true
|
|
914
|
-
fi
|
|
915
|
-
rm -f "${DOCKERD_PID_FILE}" "${DOCKERD_LOG_FILE}"
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
status_localnet() {
|
|
919
|
-
if docker_ready; then
|
|
920
|
-
log "Docker daemon is running."
|
|
921
|
-
else
|
|
922
|
-
log "Docker daemon is not running."
|
|
923
|
-
exit 1
|
|
924
|
-
fi
|
|
925
|
-
|
|
926
|
-
run_docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
|
|
927
|
-
echo
|
|
928
|
-
|
|
929
|
-
keycloak_ok="no"
|
|
930
|
-
validator_ok="no"
|
|
931
|
-
scan_ok="no"
|
|
932
|
-
ledger_ok="no"
|
|
933
|
-
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then
|
|
934
|
-
keycloak_ok="yes"
|
|
935
|
-
fi
|
|
936
|
-
validator_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)"
|
|
937
|
-
if [[ "${validator_code}" == "200" || "${validator_code}" == "401" ]]; then
|
|
938
|
-
validator_ok="yes"
|
|
939
|
-
fi
|
|
940
|
-
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then
|
|
941
|
-
scan_ok="yes"
|
|
942
|
-
fi
|
|
943
|
-
ledger_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3975/v2/version || true)"
|
|
944
|
-
if [[ "${ledger_code}" == "200" || "${ledger_code}" == "401" ]]; then
|
|
945
|
-
ledger_ok="yes"
|
|
946
|
-
fi
|
|
947
|
-
|
|
948
|
-
printf 'Keycloak ready: %s\n' "${keycloak_ok}"
|
|
949
|
-
printf 'Validator ready: %s (HTTP %s)\n' "${validator_ok}" "${validator_code:-n/a}"
|
|
950
|
-
printf 'Scan ready: %s\n' "${scan_ok}"
|
|
951
|
-
printf 'Ledger JSON API ready: %s (HTTP %s)\n' "${ledger_ok}" "${ledger_code:-n/a}"
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
show_localnet_logs() {
|
|
955
|
-
log "==================== Docker Containers ===================="
|
|
956
|
-
if docker_ready; then
|
|
957
|
-
run_docker ps -a || true
|
|
958
|
-
else
|
|
959
|
-
log "Docker daemon is not running."
|
|
960
|
-
fi
|
|
961
|
-
|
|
962
|
-
echo
|
|
963
|
-
log "==================== Docker Compose Logs ===================="
|
|
964
|
-
if [[ -f "${QUICKSTART_DIR}/.env.local" ]]; then
|
|
965
|
-
run_infra_compose 'logs --tail=100' || true
|
|
966
|
-
elif [[ -d "${QUICKSTART_DIR}" ]]; then
|
|
967
|
-
log "Quickstart local env not found; skipping compose logs: ${QUICKSTART_DIR}/.env.local"
|
|
968
|
-
else
|
|
969
|
-
log "cn-quickstart directory not found: ${QUICKSTART_DIR}"
|
|
970
|
-
fi
|
|
971
|
-
|
|
972
|
-
echo
|
|
973
|
-
log "==================== Canton Logs ===================="
|
|
974
|
-
if [[ -f "${QUICKSTART_DIR}/logs/canton.log" ]]; then
|
|
975
|
-
tail -100 "${QUICKSTART_DIR}/logs/canton.log" || true
|
|
976
|
-
else
|
|
977
|
-
log "Canton log not found: ${QUICKSTART_DIR}/logs/canton.log"
|
|
978
|
-
fi
|
|
979
|
-
|
|
980
|
-
if [[ -f "${DOCKERD_LOG_FILE}" ]]; then
|
|
981
|
-
echo
|
|
982
|
-
log "==================== Managed Docker Daemon Logs ===================="
|
|
983
|
-
tail -100 "${DOCKERD_LOG_FILE}" || true
|
|
984
|
-
fi
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
run_smoke() {
|
|
988
|
-
local auth_mode=""
|
|
989
|
-
local validator_code=""
|
|
990
|
-
local ledger_code=""
|
|
991
|
-
|
|
992
|
-
log "Running localnet smoke checks..."
|
|
993
|
-
auth_mode="$(current_auth_mode)"
|
|
994
|
-
|
|
995
|
-
if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then
|
|
996
|
-
log "Keycloak is reachable."
|
|
997
|
-
elif [[ "${auth_mode}" == "oauth2" ]]; then
|
|
998
|
-
log "Keycloak is not reachable."
|
|
999
|
-
exit 1
|
|
1000
|
-
else
|
|
1001
|
-
log "Keycloak not detected (expected in ${auth_mode} mode)."
|
|
1002
|
-
fi
|
|
1003
|
-
|
|
1004
|
-
validator_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)"
|
|
1005
|
-
if [[ "${validator_code}" != "200" && "${validator_code}" != "401" ]]; then
|
|
1006
|
-
log "Validator API is not reachable (HTTP ${validator_code})."
|
|
1007
|
-
exit 1
|
|
1008
|
-
fi
|
|
1009
|
-
|
|
1010
|
-
if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then
|
|
1011
|
-
log "Scan API is not reachable."
|
|
1012
|
-
exit 1
|
|
1013
|
-
fi
|
|
1014
|
-
|
|
1015
|
-
ledger_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3975/v2/version || true)"
|
|
1016
|
-
if [[ "${ledger_code}" != "200" && "${ledger_code}" != "401" ]]; then
|
|
1017
|
-
log "Ledger JSON API is not reachable (HTTP ${ledger_code})."
|
|
1018
|
-
exit 1
|
|
1019
|
-
fi
|
|
1020
|
-
|
|
1021
|
-
log "Smoke checks passed."
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
read_npm_script() {
|
|
1025
|
-
local target_dir="$1"
|
|
1026
|
-
local script_name="$2"
|
|
1027
|
-
|
|
1028
|
-
if [[ ! -f "${target_dir}/package.json" ]]; then
|
|
1029
|
-
return
|
|
1030
|
-
fi
|
|
1031
|
-
|
|
1032
|
-
node -e 'const fs=require("fs"); const pkgPath=process.argv[1]; const scriptName=process.argv[2]; try { const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); const script = pkg?.scripts?.[scriptName]; if (typeof script === "string") process.stdout.write(script); } catch {}' "${target_dir}/package.json" "${script_name}"
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
script_is_recursive_localnet_test() {
|
|
1036
|
-
local script_value="$1"
|
|
1037
|
-
echo "${script_value}" | grep -Eq '(^|[[:space:]])(canton-localnet[[:space:]]+test|[^[:space:]]*localnet-cloud\.sh[[:space:]]+test)([[:space:]]|$)'
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
run_integration_tests() {
|
|
1041
|
-
local script_value=""
|
|
1042
|
-
|
|
1043
|
-
if [[ -n "${CANTON_LOCALNET_TEST_CMD:-}" ]]; then
|
|
1044
|
-
log "Running custom integration command from CANTON_LOCALNET_TEST_CMD..."
|
|
1045
|
-
(
|
|
1046
|
-
cd "${PROJECT_ROOT}"
|
|
1047
|
-
bash -lc "${CANTON_LOCALNET_TEST_CMD}"
|
|
1048
|
-
)
|
|
1049
|
-
return
|
|
1050
|
-
fi
|
|
1051
|
-
|
|
1052
|
-
script_value="$(read_npm_script "${PROJECT_ROOT}" "test:integration")"
|
|
1053
|
-
if [[ -n "${script_value}" ]]; then
|
|
1054
|
-
if script_is_recursive_localnet_test "${script_value}"; then
|
|
1055
|
-
log "Skipping recursive test:integration script."
|
|
1056
|
-
else
|
|
1057
|
-
log "Running project integration tests: npm run test:integration"
|
|
1058
|
-
(
|
|
1059
|
-
cd "${PROJECT_ROOT}"
|
|
1060
|
-
npm run test:integration
|
|
1061
|
-
)
|
|
1062
|
-
return
|
|
1063
|
-
fi
|
|
1064
|
-
fi
|
|
1065
|
-
|
|
1066
|
-
script_value="$(read_npm_script "${PROJECT_ROOT}" "test:localnet")"
|
|
1067
|
-
if [[ -n "${script_value}" ]]; then
|
|
1068
|
-
if script_is_recursive_localnet_test "${script_value}"; then
|
|
1069
|
-
log "Skipping recursive test:localnet script."
|
|
1070
|
-
else
|
|
1071
|
-
log "Running project integration tests: npm run test:localnet"
|
|
1072
|
-
(
|
|
1073
|
-
cd "${PROJECT_ROOT}"
|
|
1074
|
-
npm run test:localnet
|
|
1075
|
-
)
|
|
1076
|
-
return
|
|
1077
|
-
fi
|
|
1078
|
-
fi
|
|
1079
|
-
|
|
1080
|
-
if [[ "${PROJECT_ROOT}" == "${REPO_ROOT}" && -d "${REPO_ROOT}/test/integration/localnet" && -d "${REPO_ROOT}/node_modules" ]]; then
|
|
1081
|
-
log "Running bundled SDK localnet integration tests..."
|
|
1082
|
-
(
|
|
1083
|
-
cd "${REPO_ROOT}"
|
|
1084
|
-
npm test -- test/integration/localnet
|
|
1085
|
-
)
|
|
1086
|
-
return
|
|
1087
|
-
fi
|
|
1088
|
-
|
|
1089
|
-
log "No integration test command configured; skipping test step."
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
usage() {
|
|
1093
|
-
cat <<'USAGE'
|
|
1094
|
-
Usage: scripts/localnet/localnet-cloud.sh <command>
|
|
1095
|
-
|
|
1096
|
-
Commands:
|
|
1097
|
-
setup Install prerequisites, init submodules, configure quickstart
|
|
1098
|
-
start Start localnet and wait for ready endpoints
|
|
1099
|
-
stop Stop localnet services
|
|
1100
|
-
logs Show localnet diagnostic logs
|
|
1101
|
-
status Show docker + endpoint status
|
|
1102
|
-
smoke Run endpoint smoke checks
|
|
1103
|
-
test Run project integration tests (if configured)
|
|
1104
|
-
verify Run setup + start + smoke + test
|
|
1105
|
-
|
|
1106
|
-
Environment:
|
|
1107
|
-
CANTON_LOCALNET_FAST_START=true|false Enable fast startup path (default: true)
|
|
1108
|
-
CANTON_LOCALNET_FORCE_FULL_START=true|false Force full startup with rebuild
|
|
1109
|
-
CANTON_LOCALNET_INFRA_ONLY=true|false Start only LocalNet + Keycloak infrastructure
|
|
1110
|
-
CANTON_LOCALNET_AUTH_MODE=oauth2|shared-secret
|
|
1111
|
-
CANTON_LOCALNET_QUICKSTART_DIR=<path> Use an existing cn-quickstart/quickstart directory
|
|
1112
|
-
USAGE
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
main() {
|
|
1116
|
-
if [[ "${1:-}" == "" ]]; then
|
|
1117
|
-
usage
|
|
1118
|
-
exit 1
|
|
1119
|
-
fi
|
|
1120
|
-
|
|
1121
|
-
case "$1" in
|
|
1122
|
-
setup)
|
|
1123
|
-
validate_configuration
|
|
1124
|
-
ensure_docker_packages
|
|
1125
|
-
start_docker_daemon
|
|
1126
|
-
ensure_submodules
|
|
1127
|
-
ensure_hosts_entries
|
|
1128
|
-
quickstart_setup
|
|
1129
|
-
;;
|
|
1130
|
-
start)
|
|
1131
|
-
validate_configuration
|
|
1132
|
-
require_command curl
|
|
1133
|
-
ensure_docker_packages
|
|
1134
|
-
start_docker_daemon
|
|
1135
|
-
ensure_submodules
|
|
1136
|
-
ensure_hosts_entries
|
|
1137
|
-
quickstart_setup
|
|
1138
|
-
start_localnet
|
|
1139
|
-
;;
|
|
1140
|
-
stop)
|
|
1141
|
-
stop_localnet
|
|
1142
|
-
;;
|
|
1143
|
-
logs)
|
|
1144
|
-
show_localnet_logs
|
|
1145
|
-
;;
|
|
1146
|
-
status)
|
|
1147
|
-
require_command curl
|
|
1148
|
-
status_localnet
|
|
1149
|
-
;;
|
|
1150
|
-
smoke)
|
|
1151
|
-
require_command curl
|
|
1152
|
-
run_smoke
|
|
1153
|
-
;;
|
|
1154
|
-
test)
|
|
1155
|
-
run_integration_tests
|
|
1156
|
-
;;
|
|
1157
|
-
verify)
|
|
1158
|
-
validate_configuration
|
|
1159
|
-
require_command curl
|
|
1160
|
-
ensure_docker_packages
|
|
1161
|
-
start_docker_daemon
|
|
1162
|
-
ensure_submodules
|
|
1163
|
-
ensure_hosts_entries
|
|
1164
|
-
quickstart_setup
|
|
1165
|
-
start_localnet
|
|
1166
|
-
run_smoke
|
|
1167
|
-
run_integration_tests
|
|
1168
|
-
;;
|
|
1169
|
-
*)
|
|
1170
|
-
usage
|
|
1171
|
-
exit 1
|
|
1172
|
-
;;
|
|
1173
|
-
esac
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
1177
|
-
main "$@"
|
|
1178
|
-
fi
|