@distrohelena/canton-typescript-sdk 0.1.30 → 0.1.31

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 CHANGED
@@ -81,6 +81,11 @@ not an exact Numeric value.
81
81
 
82
82
  The repository also supports a live SDK validation suite against an already-running CN quickstart localnet.
83
83
 
84
+ The current party-creation flows were developed and live-tested against Canton
85
+ Participant 3.5.7. In particular, external-party allocation signs the generated
86
+ combined topology hash (`multi_hash`) rather than each topology transaction
87
+ individually.
88
+
84
89
  ## Localnet launchers
85
90
 
86
91
  The published package includes launchers for an existing CN Quickstart checkout.
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PartyManagementServiceClient = void 0;
4
- const external_party_onboarding_transaction_js_1 = require("../../core/types/external-party/external-party-onboarding-transaction.js");
5
4
  const external_party_signature_js_1 = require("../../core/types/external-party/external-party-signature.js");
6
5
  const validation_error_js_1 = require("../../core/errors/validation-error.js");
7
6
  const decentralized_party_lifecycle_js_1 = require("./decentralized-party-lifecycle.js");
@@ -132,21 +131,12 @@ class PartyManagementServiceClient {
132
131
  confirmationThreshold: request.confirmationThreshold,
133
132
  observingParticipantUids: [...request.observingParticipantUids],
134
133
  }), options);
135
- const onboardingTransactions = [];
136
- for (const transaction of generated.topologyTransactions) {
137
- onboardingTransactions.push(new external_party_onboarding_transaction_js_1.ExternalPartyOnboardingTransaction({
138
- transaction,
139
- signatures: [
140
- await this.signExternalPartyPayloadAsync(request, transaction, "topology-transaction", generated.partyId, generated.publicKeyFingerprint),
141
- ],
142
- }));
143
- }
144
134
  const multiHashSignature = await this.signExternalPartyPayloadAsync(request, generated.multiHash, "multi-hash", generated.partyId, generated.publicKeyFingerprint);
145
135
  return this.allocateExternalPartyAsync(party_management_service_js_1.AllocateExternalPartyRequest.create({
146
136
  synchronizer: request.synchronizer,
147
- onboardingTransactions: onboardingTransactions.map((transaction) => ({
148
- transaction: transaction.transaction,
149
- signatures: transaction.signatures.map(toGeneratedSignature),
137
+ onboardingTransactions: generated.topologyTransactions.map((transaction) => ({
138
+ transaction,
139
+ signatures: [],
150
140
  })),
151
141
  multiHashSignatures: [toGeneratedSignature(multiHashSignature)],
152
142
  identityProviderId: request.identityProviderId,
@@ -1,4 +1,3 @@
1
- import { ExternalPartyOnboardingTransaction } from "../../core/types/external-party/external-party-onboarding-transaction.js";
2
1
  import { ExternalPartySignature } from "../../core/types/external-party/external-party-signature.js";
3
2
  import { ValidationError } from "../../core/errors/validation-error.js";
4
3
  import { prepareDecentralizedPartyAsync } from "./decentralized-party-lifecycle.js";
@@ -129,21 +128,12 @@ export class PartyManagementServiceClient {
129
128
  confirmationThreshold: request.confirmationThreshold,
130
129
  observingParticipantUids: [...request.observingParticipantUids],
131
130
  }), options);
132
- const onboardingTransactions = [];
133
- for (const transaction of generated.topologyTransactions) {
134
- onboardingTransactions.push(new ExternalPartyOnboardingTransaction({
135
- transaction,
136
- signatures: [
137
- await this.signExternalPartyPayloadAsync(request, transaction, "topology-transaction", generated.partyId, generated.publicKeyFingerprint),
138
- ],
139
- }));
140
- }
141
131
  const multiHashSignature = await this.signExternalPartyPayloadAsync(request, generated.multiHash, "multi-hash", generated.partyId, generated.publicKeyFingerprint);
142
132
  return this.allocateExternalPartyAsync(AllocateExternalPartyRequest.create({
143
133
  synchronizer: request.synchronizer,
144
- onboardingTransactions: onboardingTransactions.map((transaction) => ({
145
- transaction: transaction.transaction,
146
- signatures: transaction.signatures.map(toGeneratedSignature),
134
+ onboardingTransactions: generated.topologyTransactions.map((transaction) => ({
135
+ transaction,
136
+ signatures: [],
147
137
  })),
148
138
  multiHashSignatures: [toGeneratedSignature(multiHashSignature)],
149
139
  identityProviderId: request.identityProviderId,
@@ -958,40 +958,87 @@ extra_pqs_services() {
958
958
  done
959
959
  }
960
960
 
961
- wait_for_container_health() {
961
+ container_readiness_state() {
962
962
  local container="$1"
963
- local attempts="${2:-30}"
964
- local delay_seconds="${3:-2}"
965
- local status
963
+ docker inspect --format '{{.State.Status}} {{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$container" \
964
+ 2>/dev/null | tail -n 1 || true
965
+ }
966
966
 
967
- for ((i = 1; i <= attempts; i++)); do
968
- status="$(docker inspect --format '{{.State.Health.Status}}' "$container" 2>/dev/null | tail -n 1 || true)"
969
- if [[ "$status" == "healthy" ]]; then
970
- return 0
971
- fi
972
- sleep "$delay_seconds"
973
- done
967
+ container_health_is_ready() {
968
+ local _container="$1"
969
+ local _lifecycle_state="$2"
970
+ local health_status="$3"
971
+ [[ "$health_status" == "healthy" ]]
972
+ }
974
973
 
975
- echo "$container did not become healthy after $((attempts * delay_seconds)) seconds." >&2
976
- return 1
974
+ canton_and_extras_are_ready() {
975
+ local _container="$1"
976
+ local _lifecycle_state="$2"
977
+ local health_status="$3"
978
+ local extra_participants="$4"
979
+ [[ "$health_status" == "healthy" ]] && extra_participants_healthy "$extra_participants"
977
980
  }
978
981
 
979
- wait_for_canton_health() {
980
- local extra_participants="${1:-0}"
981
- local attempts=30
982
- local delay_seconds=2
983
- local status
982
+ print_container_logs() {
983
+ local container="$1"
984
+ echo "Recent logs for $container:" >&2
985
+ docker logs --tail 40 "$container" >&2 || true
986
+ }
987
+
988
+ readiness_elapsed_seconds() {
989
+ if [[ -n "${START_LOCAL_TEST_READINESS_CLOCK_FILE:-}" ]]; then
990
+ cat "$START_LOCAL_TEST_READINESS_CLOCK_FILE"
991
+ return 0
992
+ fi
993
+ printf '%s\n' "$SECONDS"
994
+ }
984
995
 
985
- for ((i = 1; i <= attempts; i++)); do
986
- status="$(docker inspect --format '{{.State.Health.Status}}' canton 2>/dev/null | tail -n 1 || true)"
987
- if [[ "$status" == "healthy" ]] && extra_participants_healthy "$extra_participants"; then
996
+ wait_for_container_readiness() {
997
+ local container="$1"
998
+ local readiness_predicate="$2"
999
+ shift 2
1000
+
1001
+ local last_readiness_state=""
1002
+ local last_diagnostic_at
1003
+ last_diagnostic_at="$(readiness_elapsed_seconds)"
1004
+
1005
+ while true; do
1006
+ local lifecycle_state health_status readiness_state
1007
+ read -r lifecycle_state health_status <<< "$(container_readiness_state "$container")"
1008
+ lifecycle_state="${lifecycle_state:-missing}"
1009
+ health_status="${health_status:-unknown}"
1010
+
1011
+ if [[ "$lifecycle_state" != "running" ]]; then
1012
+ echo "$container is not running (state: $lifecycle_state)." >&2
1013
+ print_container_logs "$container"
1014
+ return 1
1015
+ fi
1016
+
1017
+ readiness_state="$lifecycle_state/$health_status"
1018
+ if [[ "$readiness_state" != "$last_readiness_state" ]]; then
1019
+ echo "$container readiness: $readiness_state"
1020
+ last_readiness_state="$readiness_state"
1021
+ fi
1022
+
1023
+ if "$readiness_predicate" "$container" "$lifecycle_state" "$health_status" "$@"; then
988
1024
  return 0
989
1025
  fi
990
- sleep "$delay_seconds"
1026
+
1027
+ local now
1028
+ now="$(readiness_elapsed_seconds)"
1029
+ if (( now - last_diagnostic_at >= 30 )); then
1030
+ echo "$container is still not ready (state: $lifecycle_state, health: $health_status)." >&2
1031
+ print_container_logs "$container"
1032
+ last_diagnostic_at="$now"
1033
+ fi
1034
+
1035
+ sleep 2
991
1036
  done
1037
+ }
992
1038
 
993
- echo "canton did not become healthy after $((attempts * delay_seconds)) seconds." >&2
994
- return 1
1039
+ wait_for_canton_health() {
1040
+ local extra_participants="${1:-0}"
1041
+ wait_for_container_readiness canton canton_and_extras_are_ready "$extra_participants"
995
1042
  }
996
1043
 
997
1044
  provision_extra_pqs() {
@@ -1190,8 +1237,8 @@ start_ledger_stack() {
1190
1237
  docker_compose "${compose_args[@]}" up -d --no-recreate "${followup_services[@]}"
1191
1238
 
1192
1239
  if (( extra_participants > 0 )); then
1193
- wait_for_container_health splice 60 2
1194
- wait_for_container_health splice-onboarding 60 2
1240
+ wait_for_container_readiness splice container_health_is_ready
1241
+ wait_for_container_readiness splice-onboarding container_health_is_ready
1195
1242
  if [[ "$auth_mode" == "shared-secret" ]]; then
1196
1243
  grant_localnet_validator_read_rights "$extra_participants"
1197
1244
  fi
@@ -42,8 +42,9 @@ run_case() {
42
42
  local quickstart_root_dir="$tmpdir/cn-quickstart"
43
43
  local stubbin="$tmpdir/bin"
44
44
  local generated_dir="$tmpdir/generated"
45
+ local docker_state_dir="$tmpdir/docker-state"
45
46
 
46
- rm -rf "$quickstart_dir" "$quickstart_root_dir" "$stubbin" "$generated_dir" "$tmpdir/tls"
47
+ rm -rf "$quickstart_dir" "$quickstart_root_dir" "$stubbin" "$generated_dir" "$docker_state_dir" "$tmpdir/tls"
47
48
  mkdir -p \
48
49
  "$quickstart_root_dir" \
49
50
  "$quickstart_dir/docker/modules/localnet/env" \
@@ -51,7 +52,9 @@ run_case() {
51
52
  "$quickstart_dir/docker/modules/splice-onboarding" \
52
53
  "$quickstart_dir/docker/modules/keycloak" \
53
54
  "$stubbin" \
54
- "$generated_dir"
55
+ "$generated_dir" \
56
+ "$docker_state_dir"
57
+ printf '0\n' > "$docker_state_dir/readiness-seconds"
55
58
  ln -s "$quickstart_dir" "$quickstart_root_dir/quickstart"
56
59
  printf '%s\n' "$makefile_targets" > "$quickstart_dir/Makefile"
57
60
  printf '%s\n' \
@@ -132,7 +135,39 @@ printf 'stub env SV_PROFILE=%s\n' "${SV_PROFILE:-}"
132
135
  printf 'stub env PQS_SV_PROFILE=%s\n' "${PQS_SV_PROFILE:-}"
133
136
  printf 'stub env AUTH_MODE=%s\n' "${AUTH_MODE:-}"
134
137
  if [[ "${1:-}" == "inspect" ]]; then
135
- printf 'healthy\n'
138
+ container="${!#}"
139
+ state_file="${START_LOCAL_TEST_DOCKER_STATE_DIR}/${docker_mode}-${container}.count"
140
+ count=0
141
+ if [[ -f "$state_file" ]]; then
142
+ count="$(<"$state_file")"
143
+ fi
144
+ count=$((count + 1))
145
+ printf '%s\n' "$count" > "$state_file"
146
+
147
+ lifecycle=running
148
+ health=healthy
149
+ case "$docker_mode:$container" in
150
+ slow-canton:canton)
151
+ if (( count <= 31 )); then health=starting; fi
152
+ ;;
153
+ slow-splice:splice)
154
+ if (( count <= 61 )); then health=starting; fi
155
+ ;;
156
+ exited-canton:canton)
157
+ lifecycle=exited
158
+ health=unhealthy
159
+ ;;
160
+ esac
161
+
162
+ if [[ "$*" == *'.State.Status'* ]]; then
163
+ printf '%s %s\n' "$lifecycle" "$health"
164
+ else
165
+ printf '%s\n' "$health"
166
+ fi
167
+ exit 0
168
+ fi
169
+ if [[ "${1:-}" == "logs" ]]; then
170
+ printf 'stub log tail for %s\n' "${!#}"
136
171
  exit 0
137
172
  fi
138
173
  if [[ "${1:-}" == "exec" ]]; then
@@ -160,6 +195,9 @@ if [[ "${1:-}" == "compose" && "${2:-}" == "version" ]]; then
160
195
  exit 0
161
196
  fi
162
197
  printf 'stub docker %s\n' "$*"
198
+ if [[ "$*" == *" up -d --no-recreate pqs-extra-1"* ]]; then
199
+ printf 'stub extra pqs startup\n'
200
+ fi
163
201
  if [[ "$docker_mode" == "staged-start" ]]; then
164
202
  if [[ "${1:-}" == "compose" && "$*" == *" down -v --remove-orphans"* ]]; then
165
203
  exit 0
@@ -189,6 +227,16 @@ EOF
189
227
  sed -i "s|__DOCKER_MODE__|$docker_mode|" "$stubbin/docker"
190
228
  chmod +x "$stubbin/docker"
191
229
 
230
+ cat > "$stubbin/sleep" <<'EOF'
231
+ #!/usr/bin/env bash
232
+ set -euo pipefail
233
+ if [[ -n "${START_LOCAL_TEST_READINESS_CLOCK_FILE:-}" ]]; then
234
+ current="$(<"$START_LOCAL_TEST_READINESS_CLOCK_FILE")"
235
+ printf '%s\n' "$((current + ${1:-0}))" > "$START_LOCAL_TEST_READINESS_CLOCK_FILE"
236
+ fi
237
+ EOF
238
+ chmod +x "$stubbin/sleep"
239
+
192
240
  cat > "$stubbin/docker-compose" <<'EOF'
193
241
  #!/usr/bin/env bash
194
242
  set -euo pipefail
@@ -226,6 +274,8 @@ EOF
226
274
  LOCALNET_TLS_CERT_CHAIN_PATH="$tls_cert_chain_path" \
227
275
  LOCALNET_TLS_PRIVATE_KEY_PATH="$tls_private_key_path" \
228
276
  LOCALNET_TLS_CA_CERT_PATH="$tls_ca_cert_path" \
277
+ START_LOCAL_TEST_DOCKER_STATE_DIR="$docker_state_dir" \
278
+ START_LOCAL_TEST_READINESS_CLOCK_FILE="$docker_state_dir/readiness-seconds" \
229
279
  EXTRA_PARTICIPANTS="$extra_participants" \
230
280
  PATH="$stubbin:$PATH" \
231
281
  bash ./start-local.sh 2>&1
@@ -240,6 +290,8 @@ EOF
240
290
  LOCALNET_TLS_CERT_CHAIN_PATH="$tls_cert_chain_path" \
241
291
  LOCALNET_TLS_PRIVATE_KEY_PATH="$tls_private_key_path" \
242
292
  LOCALNET_TLS_CA_CERT_PATH="$tls_ca_cert_path" \
293
+ START_LOCAL_TEST_DOCKER_STATE_DIR="$docker_state_dir" \
294
+ START_LOCAL_TEST_READINESS_CLOCK_FILE="$docker_state_dir/readiness-seconds" \
243
295
  EXTRA_PARTICIPANTS="$extra_participants" \
244
296
  PATH="$stubbin:$PATH" \
245
297
  bash ./start-local.sh 2>&1
@@ -353,7 +405,11 @@ printf 'stub env SV_PROFILE=%s\n' "${SV_PROFILE:-}"
353
405
  printf 'stub env PQS_SV_PROFILE=%s\n' "${PQS_SV_PROFILE:-}"
354
406
  printf 'stub env AUTH_MODE=%s\n' "${AUTH_MODE:-}"
355
407
  if [[ "${1:-}" == "inspect" ]]; then
356
- printf 'healthy\n'
408
+ if [[ "$*" == *'.State.Status'* ]]; then
409
+ printf 'running healthy\n'
410
+ else
411
+ printf 'healthy\n'
412
+ fi
357
413
  exit 0
358
414
  fi
359
415
  if [[ "${1:-}" == "exec" ]]; then
@@ -390,6 +446,13 @@ run_case $'.PHONY: start\nstart:\n' 'stub env APP_USER_PROFILE=off'
390
446
  run_case $'.PHONY: start\nstart:\n' 'stub env SV_PROFILE=on'
391
447
  run_case $'.PHONY: start\nstart:\n' 'stub env PQS_SV_PROFILE=on'
392
448
  run_case $'.PHONY: start\nstart:\n' 'stub env AUTH_MODE=oauth2' oauth2
449
+ run_case $'.PHONY: start\nstart:\n' 'canton readiness: running/starting' shared-secret slow-canton
450
+ run_case $'.PHONY: start\nstart:\n' 'canton is still not ready (state: running, health: starting).' shared-secret slow-canton
451
+ run_case $'.PHONY: start\nstart:\n' 'stub log tail for canton' shared-secret slow-canton
452
+ run_case $'.PHONY: start\nstart:\n' 'canton is not running (state: exited).' shared-secret exited-canton '' '' '' 0 0 '' '' '' 1
453
+ run_case $'.PHONY: start\nstart:\n' 'stub log tail for canton' shared-secret exited-canton '' '' '' 0 0 '' '' '' 1
454
+ run_case $'.PHONY: start\nstart:\n' 'splice readiness: running/starting' shared-secret slow-splice 1
455
+ run_case $'.PHONY: start\nstart:\n' 'stub extra pqs startup' shared-secret slow-splice 1
393
456
  run_case $'.PHONY: start\nstart:\n' 'stub docker compose -f compose.yaml -f '"$tmpdir"'/quickstart/docker/modules/localnet/compose.yaml -f '"$tmpdir"'/quickstart/docker/modules/splice-onboarding/compose.yaml -f '"$tmpdir"'/quickstart/docker/modules/pqs/compose.yaml --env-file .env --env-file .env.local --env-file '"$tmpdir"'/quickstart/docker/modules/localnet/compose.env --env-file '"$tmpdir"'/quickstart/docker/modules/localnet/env/common.env --env-file '"$tmpdir"'/quickstart/docker/modules/pqs/compose.env --profile app-provider --profile pqs-app-provider --profile pqs-sv down -v --remove-orphans'
394
457
  run_case $'.PHONY: start\nstart:\n' 'stub docker compose -f compose.yaml -f '"$tmpdir"'/quickstart/docker/modules/localnet/compose.yaml -f '"$tmpdir"'/quickstart/docker/modules/splice-onboarding/compose.yaml -f '"$tmpdir"'/quickstart/docker/modules/pqs/compose.yaml --env-file .env --env-file .env.local --env-file '"$tmpdir"'/quickstart/docker/modules/localnet/compose.env --env-file '"$tmpdir"'/quickstart/docker/modules/localnet/env/common.env --env-file '"$tmpdir"'/quickstart/docker/modules/pqs/compose.env --profile app-provider --profile pqs-app-provider --profile pqs-sv up -d --no-recreate postgres canton'
395
458
  run_case $'.PHONY: start\nstart:\n' 'stub docker compose -f compose.yaml -f '"$tmpdir"'/quickstart/docker/modules/localnet/compose.yaml -f '"$tmpdir"'/quickstart/docker/modules/splice-onboarding/compose.yaml -f '"$tmpdir"'/quickstart/docker/modules/pqs/compose.yaml --env-file .env --env-file .env.local --env-file '"$tmpdir"'/quickstart/docker/modules/localnet/compose.env --env-file '"$tmpdir"'/quickstart/docker/modules/localnet/env/common.env --env-file '"$tmpdir"'/quickstart/docker/modules/pqs/compose.env --profile app-provider --profile pqs-app-provider --profile pqs-sv -f '"$tmpdir"'/quickstart/docker/modules/keycloak/compose.yaml --env-file '"$tmpdir"'/quickstart/docker/modules/keycloak/compose.env --profile keycloak down -v --remove-orphans' oauth2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@distrohelena/canton-typescript-sdk",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -79,6 +79,13 @@
79
79
  "build": "node ./scripts/clean-dist.mjs && tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node ./scripts/write-cjs-package.mjs",
80
80
  "generate:daml-interface": "node ./dist/daml-interface/cli/daml-interface-cli-main.js",
81
81
  "generate:grpc": "node ./scripts/generate-grpc-bindings.mjs",
82
+ "examples:check": "npm run build && tsc -p tsconfig.examples.json --noEmit",
83
+ "example:init": "npm run build && node --loader ts-node/esm examples/01-client-initialization.ts",
84
+ "example:tls": "npm run build && node --loader ts-node/esm examples/02-tls-connection.ts",
85
+ "example:jwt": "npm run build && node --loader ts-node/esm examples/03-jwt-authentication.ts",
86
+ "example:party:hosted": "npm run build && node --loader ts-node/esm examples/10-hosted-party.ts",
87
+ "example:party:external": "npm run build && node --loader ts-node/esm examples/20-external-party-ed25519.ts",
88
+ "example:party:decentralized": "npm run build && node --loader ts-node/esm examples/30-decentralized-party-ed25519.ts",
82
89
  "lint": "eslint . --max-warnings=0",
83
90
  "lint:fix": "eslint . --fix --max-warnings=0",
84
91
  "start:local-ledger": "bash node/start-local.sh",