diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index e2e2e15e84..2f84ea3662 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -13,15 +13,28 @@ permissions: jobs: docker-build: runs-on: Linux-ARM64-Runner + strategy: + matrix: + include: + - component: node + bin: miden-node + port: 57291 + - component: validator + bin: miden-validator + port: 50101 + name: Build ${{ matrix.component }} steps: - name: Set up Docker Buildx uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - - name: Build and push + - name: Build uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 with: push: false - file: ./bin/node/Dockerfile + file: ./Dockerfile + build-args: | + BIN=${{ matrix.bin }} + PORT=${{ matrix.port }} cache-from: type=gha # Only save cache on push into next cache-to: ${{ github.event_name == 'push' && github.ref == 'refs/heads/next' && 'type=gha,mode=max' || '' }} diff --git a/.github/workflows/publish-debian-all.yml b/.github/workflows/publish-debian-all.yml index 201412e39b..9ed73569f1 100644 --- a/.github/workflows/publish-debian-all.yml +++ b/.github/workflows/publish-debian-all.yml @@ -72,6 +72,34 @@ jobs: crate: miden-remote-prover arch: ${{ matrix.arch }} + publish-validator: + name: Publish Validator ${{ matrix.arch }} Debian + permissions: + contents: write + strategy: + matrix: + arch: [amd64, arm64] + runs-on: + labels: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + steps: + - name: Checkout repo + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + fetch-depth: 0 + persist-credentials: false + - uses: ./.github/actions/install-rocksdb + - uses: ./.github/actions/install-protobuf-compiler + - name: Build and Publish Validator + uses: ./.github/actions/debian + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + gitref: ${{ env.version }} + crate_dir: validator + package: miden-validator + packaging_dir: validator + crate: miden-validator + arch: ${{ matrix.arch }} + publish-network-monitor: name: Publish Network Monitor ${{ matrix.arch }} Debian permissions: diff --git a/.github/workflows/publish-debian.yml b/.github/workflows/publish-debian.yml index a9550ff33b..478ea46d96 100644 --- a/.github/workflows/publish-debian.yml +++ b/.github/workflows/publish-debian.yml @@ -3,40 +3,15 @@ name: Publish Debian Package on: workflow_dispatch: inputs: - package: - description: "Name of package to publish" + component: + description: "Component to publish" required: true type: choice - options: - - miden-network-monitor - - miden-node - - miden-prover - - miden-prover-proxy - crate_dir: - required: true - description: "Name of crate directory" - type: choice - options: - - network-monitor - - node - - remote-prover - packaging_dir: - required: true - description: "Name of packaging directory" - type: choice options: - network-monitor - node - prover - - prover-proxy - crate: - description: "Name of the binary crate to publish" - required: true - type: choice - options: - - miden-network-monitor - - miden-node - - miden-remote-prover + - validator version: description: "Version to release (E.G. v0.10.0-rc.1, v0.10.0). Corresponding git tag must already exist." required: true @@ -47,7 +22,7 @@ permissions: jobs: publish: - name: Publish ${{ inputs.package }} ${{ matrix.arch }} Debian + name: Publish ${{ inputs.component }} ${{ matrix.arch }} Debian permissions: contents: write strategy: @@ -62,6 +37,29 @@ jobs: fetch-depth: 0 persist-credentials: false + - name: Resolve component inputs + id: resolve + shell: bash + env: + COMPONENT: ${{ inputs.component }} + run: | + package="miden-${COMPONENT}" + packaging_dir="${COMPONENT}" + + # prover's binary crate lives in bin/remote-prover and is named miden-remote-prover + if [ "${COMPONENT}" = "prover" ]; then + crate_dir="remote-prover" + crate="miden-remote-prover" + else + crate_dir="${COMPONENT}" + crate="miden-${COMPONENT}" + fi + + echo "package=${package}" >> "$GITHUB_OUTPUT" + echo "crate_dir=${crate_dir}" >> "$GITHUB_OUTPUT" + echo "packaging_dir=${packaging_dir}" >> "$GITHUB_OUTPUT" + echo "crate=${crate}" >> "$GITHUB_OUTPUT" + - uses: ./.github/actions/install-rocksdb - uses: ./.github/actions/install-protobuf-compiler @@ -70,8 +68,8 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} gitref: ${{ inputs.version }} - crate_dir: ${{ inputs.crate_dir }} - package: ${{ inputs.package }} - packaging_dir: ${{ inputs.packaging_dir }} - crate: ${{ inputs.crate }} + crate_dir: ${{ steps.resolve.outputs.crate_dir }} + package: ${{ steps.resolve.outputs.package }} + packaging_dir: ${{ steps.resolve.outputs.packaging_dir }} + crate: ${{ steps.resolve.outputs.crate }} arch: ${{ matrix.arch }} diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index c23d5a5a1d..c82fdf5c88 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -28,7 +28,13 @@ jobs: environment: publish-docker strategy: matrix: - component: [node] + include: + - component: node + bin: miden-node + port: 57291 + - component: validator + bin: miden-validator + port: 50101 name: Publish ${{ matrix.component }} ${{ inputs.version }} steps: - name: Checkout repo @@ -62,7 +68,10 @@ jobs: uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 with: push: true - file: ./bin/${{ matrix.component }}/Dockerfile + file: ./Dockerfile + build-args: | + BIN=${{ matrix.bin }} + PORT=${{ matrix.port }} tags: ${{ env.registry }}/0xmiden/miden-${{ matrix.component }}:${{ env.version }} cache-from: type=s3,region=${{ secrets.AWS_REGION }},bucket=${{ secrets.AWS_CACHE_BUCKET }},name=miden-${{ matrix.component }} cache-to: type=s3,region=${{ secrets.AWS_REGION }},bucket=${{ secrets.AWS_CACHE_BUCKET }},name=miden-${{ matrix.component }},mode=max diff --git a/CHANGELOG.md b/CHANGELOG.md index bd1fdb784c..c9f6a2aea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Replaced the network monitor's JavaScript dashboard with a server-rendered Maud + HTMX frontend ([#2024](https://github.com/0xMiden/node/pull/2024)). - [BREAKING] Removed `CheckNullifiers` endpoint ([#2049](https://github.com/0xMiden/node/pull/2049)). - [BREAKING] `BlockRange.block_to` is now required for all RPC endpoints ([#2056](https://github.com/0xMiden/node/pull/2056)). +- [BREAKING] Removed `miden-node validator` subcommand and created a separate `miden-validator` binary ([#2053](https://github.com/0xMiden/node/pull/2053)). ## v0.14.10 (2026-05-29) diff --git a/Cargo.lock b/Cargo.lock index eb5f37ee88..1f3ea6aa44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3230,14 +3230,12 @@ dependencies = [ "anyhow", "clap", "fs-err", - "hex", "humantime", "miden-node-block-producer", "miden-node-ntx-builder", "miden-node-rpc", "miden-node-store", "miden-node-utils", - "miden-node-validator", "miden-protocol", "tokio", "url", @@ -3255,11 +3253,11 @@ dependencies = [ "miden-node-proto-build", "miden-node-store", "miden-node-utils", - "miden-node-validator", "miden-protocol", "miden-remote-prover-client", "miden-standards", "miden-tx-batch-prover", + "miden-validator-core", "pretty_assertions", "rand 0.9.2", "rand_chacha", @@ -3508,33 +3506,6 @@ dependencies = [ "url", ] -[[package]] -name = "miden-node-validator" -version = "0.15.0" -dependencies = [ - "anyhow", - "aws-config", - "aws-sdk-kms", - "build-rs", - "diesel", - "diesel_migrations", - "miden-node-db", - "miden-node-proto", - "miden-node-proto-build", - "miden-node-store", - "miden-node-utils", - "miden-protocol", - "miden-tx", - "tempfile", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tonic", - "tonic-reflection", - "tower-http", - "tracing", -] - [[package]] name = "miden-package-registry" version = "0.22.1" @@ -3803,6 +3774,49 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "miden-validator" +version = "0.15.0" +dependencies = [ + "anyhow", + "clap", + "fs-err", + "hex", + "miden-node-store", + "miden-node-utils", + "miden-protocol", + "miden-validator-core", + "tokio", + "url", +] + +[[package]] +name = "miden-validator-core" +version = "0.15.0" +dependencies = [ + "anyhow", + "aws-config", + "aws-sdk-kms", + "build-rs", + "diesel", + "diesel_migrations", + "miden-node-db", + "miden-node-proto", + "miden-node-proto-build", + "miden-node-store", + "miden-node-utils", + "miden-protocol", + "miden-tx", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tonic", + "tonic-reflection", + "tower-http", + "tracing", +] + [[package]] name = "miden-verifier" version = "0.22.1" diff --git a/Cargo.toml b/Cargo.toml index b9741a73ba..c68092f1b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "bin/node", "bin/remote-prover", "bin/stress-test", + "bin/validator", "crates/block-producer", "crates/db", "crates/grpc-error-macro", @@ -54,8 +55,8 @@ miden-node-rpc = { path = "crates/rpc", version = "0.15" } miden-node-store = { path = "crates/store", version = "0.15" } miden-node-test-macro = { path = "crates/test-macro" } miden-node-utils = { path = "crates/utils", version = "0.15" } -miden-node-validator = { path = "crates/validator", version = "0.15" } miden-remote-prover-client = { path = "crates/remote-prover-client", version = "0.15" } +miden-validator-core = { path = "crates/validator", version = "0.15" } # Temporary workaround until # is part of `rocksdb-rust` release miden-node-rocksdb-cxx-linkage-fix = { path = "crates/rocksdb-cxx-linkage-fix", version = "0.15" } diff --git a/bin/node/Dockerfile b/Dockerfile similarity index 83% rename from bin/node/Dockerfile rename to Dockerfile index 89560f0891..21605c1325 100644 --- a/bin/node/Dockerfile +++ b/Dockerfile @@ -1,3 +1,6 @@ +ARG BIN +ARG PORT + FROM rust:1.93-slim-bookworm AS chef # Install build dependencies. RocksDB is compiled from source by librocksdb-sys. RUN apt-get update && \ @@ -20,12 +23,13 @@ COPY . . RUN cargo chef prepare --recipe-path recipe.json FROM chef AS builder +ARG BIN COPY --from=planner /app/recipe.json recipe.json # Build dependencies - this is the caching Docker layer! RUN cargo chef cook --release --recipe-path recipe.json # Build application COPY . . -RUN cargo build --release --locked --bin miden-node +RUN cargo build --release --locked --bin ${BIN} # Base line runtime image with runtime dependencies installed. FROM debian:bookworm-slim AS runtime-base @@ -35,7 +39,9 @@ RUN apt-get update && \ && rm -rf /var/lib/apt/lists/* FROM runtime-base AS runtime -COPY --from=builder /app/target/release/miden-node /usr/local/bin/miden-node +ARG BIN +ARG PORT +COPY --from=builder /app/target/release/${BIN} /usr/local/bin/${BIN} LABEL org.opencontainers.image.authors=devops@miden.team \ org.opencontainers.image.url=https://0xMiden.github.io/ \ org.opencontainers.image.documentation=https://github.com/0xMiden/node \ @@ -48,8 +54,7 @@ ARG COMMIT LABEL org.opencontainers.image.created=$CREATED \ org.opencontainers.image.version=$VERSION \ org.opencontainers.image.revision=$COMMIT - -# Expose RPC port -EXPOSE 57291 -# Miden node does not spawn sub-processes, so it can be used as the PID1. -CMD ["miden-node"] +EXPOSE ${PORT} +# Use exec to replace the shell so the binary runs as PID 1. +ENV MIDEN_BIN=${BIN} +CMD ["/bin/sh", "-c", "exec /usr/local/bin/$MIDEN_BIN"] diff --git a/Makefile b/Makefile index ca2481d1ee..aa6a035b47 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,6 @@ help: # -- variables ------------------------------------------------------------------------------------ WARNINGS=RUSTDOCFLAGS="-D warnings" -CONTAINER_RUNTIME ?= docker STRESS_TEST_DATA_DIR ?= stress-test-store-$(shell date +%Y%m%d-%H%M%S) COMPOSE_FILES = -f docker-compose.yml -f compose/telemetry.yml -f compose/monitor.yml @@ -105,6 +104,10 @@ build: ## Builds all crates and re-builds protobuf bindings for proto crates install-node: ## Installs node cargo install --path bin/node --locked +.PHONY: install-validator +install-validator: ## Installs validator + cargo install --path bin/validator --locked + .PHONY: install-remote-prover install-remote-prover: ## Install remote prover's CLI cargo install --path bin/remote-prover --bin miden-remote-prover --locked @@ -130,46 +133,63 @@ install-network-monitor: ## Installs network monitor binary .PHONY: compose-genesis compose-genesis: ## Wipes node volumes and creates a fresh genesis block - $(CONTAINER_RUNTIME) compose $(COMPOSE_FILES) down --volumes --remove-orphans - $(CONTAINER_RUNTIME) volume rm -f miden-node_node-data - $(CONTAINER_RUNTIME) compose $(COMPOSE_FILES) --profile genesis run --rm genesis + docker compose $(COMPOSE_FILES) down --volumes --remove-orphans + docker volume rm -f miden-node_node-data + docker compose $(COMPOSE_FILES) --profile genesis run --rm genesis-store + docker compose $(COMPOSE_FILES) --profile genesis rm -f .PHONY: compose-up compose-up: ## Starts all node components, telemetry, and monitor via docker compose - $(CONTAINER_RUNTIME) compose $(COMPOSE_FILES) up -d + docker compose $(COMPOSE_FILES) up -d .PHONY: compose-down compose-down: ## Stops and removes all containers via docker compose - $(CONTAINER_RUNTIME) compose $(COMPOSE_FILES) down + docker compose $(COMPOSE_FILES) down .PHONY: compose-logs compose-logs: ## Follows logs for all components via docker compose - $(CONTAINER_RUNTIME) compose $(COMPOSE_FILES) logs -f + docker compose $(COMPOSE_FILES) logs -f + +.PHONY: docker-build +docker-build: docker-build-node docker-build-validator ## Builds all Docker images .PHONY: docker-build-node -docker-build-node: ## Builds the Miden node using Docker (override with CONTAINER_RUNTIME=podman) +docker-build-node: ## Builds the Miden node using Docker @CREATED=$$(date) && \ VERSION=$$(cat bin/node/Cargo.toml | grep -m 1 '^version' | cut -d '"' -f 2) && \ COMMIT=$$(git rev-parse HEAD) && \ - $(CONTAINER_RUNTIME) build --build-arg CREATED="$$CREATED" \ - --build-arg VERSION="$$VERSION" \ - --build-arg COMMIT="$$COMMIT" \ - -f bin/node/Dockerfile \ - -t miden-node-image . + docker build --build-arg CREATED="$$CREATED" \ + --build-arg VERSION="$$VERSION" \ + --build-arg COMMIT="$$COMMIT" \ + --build-arg BIN=miden-node \ + --build-arg PORT=57291 \ + -t miden-node . + +.PHONY: docker-build-validator +docker-build-validator: ## Builds the Miden validator using Docker + @CREATED=$$(date) && \ + VERSION=$$(cat bin/validator/Cargo.toml | grep -m 1 '^version' | cut -d '"' -f 2) && \ + COMMIT=$$(git rev-parse HEAD) && \ + docker build --build-arg CREATED="$$CREATED" \ + --build-arg VERSION="$$VERSION" \ + --build-arg COMMIT="$$COMMIT" \ + --build-arg BIN=miden-validator \ + --build-arg PORT=50101 \ + -t miden-validator . .PHONY: docker-build-monitor -docker-build-monitor: ## Builds the network monitor using Docker (override with CONTAINER_RUNTIME=podman) - $(CONTAINER_RUNTIME) build \ +docker-build-monitor: ## Builds the network monitor using Docker + docker build \ -f bin/network-monitor/Dockerfile \ -t miden-network-monitor-image . .PHONY: docker-run-node -docker-run-node: ## Runs the Miden node as a Docker container (override with CONTAINER_RUNTIME=podman) - $(CONTAINER_RUNTIME) volume create miden-db - $(CONTAINER_RUNTIME) run --name miden-node \ +docker-run-node: ## Runs the Miden node as a Docker container + docker volume create miden-db + docker run --name miden-node \ -p 57291:57291 \ -v miden-db:/db \ - -d miden-node-image + -d miden-node ## --- setup -------------------------------------------------------------------------------------- diff --git a/bin/genesis/README.md b/bin/genesis/README.md index ddd9f0a5fc..8b570b77f6 100644 --- a/bin/genesis/README.md +++ b/bin/genesis/README.md @@ -38,7 +38,7 @@ The bridge account always uses NoAuth and has no secret keys. miden-genesis --output-dir ./genesis # 2. Bootstrap the genesis block -miden-node validator bootstrap \ +miden-validator bootstrap \ --genesis-block-directory ./data \ --accounts-directory ./accounts \ --genesis-config-file ./genesis/genesis.toml \ diff --git a/bin/node/.env b/bin/node/.env index dbb6355216..16b3c1bbdd 100644 --- a/bin/node/.env +++ b/bin/node/.env @@ -28,12 +28,6 @@ MIDEN_NODE_RPC_BLOCK_PRODUCER_URL= MIDEN_NODE_RPC_VALIDATOR_URL= MIDEN_NODE_RPC_NTX_BUILDER_URL= -# Validator -MIDEN_NODE_VALIDATOR_URL= -MIDEN_NODE_VALIDATOR_GENESIS_CONFIG_FILE= -MIDEN_NODE_VALIDATOR_KEY= -MIDEN_NODE_VALIDATOR_KMS_KEY_ID= - # NTX Builder MIDEN_NODE_NTX_BUILDER_URL= MIDEN_NODE_NTX_BUILDER_STORE_URL= diff --git a/bin/node/Cargo.toml b/bin/node/Cargo.toml index fd242c4386..cba7708418 100644 --- a/bin/node/Cargo.toml +++ b/bin/node/Cargo.toml @@ -21,14 +21,12 @@ tracing-forest = ["miden-node-block-producer/tracing-forest"] anyhow = { workspace = true } clap = { features = ["env", "string"], workspace = true } fs-err = { workspace = true } -hex = { workspace = true } humantime = { workspace = true } miden-node-block-producer = { workspace = true } miden-node-ntx-builder = { workspace = true } miden-node-rpc = { workspace = true } miden-node-store = { workspace = true } miden-node-utils = { workspace = true } -miden-node-validator = { workspace = true } miden-protocol = { workspace = true } tokio = { features = ["macros", "net", "rt-multi-thread"], workspace = true } url = { workspace = true } diff --git a/bin/node/src/commands/mod.rs b/bin/node/src/commands/mod.rs index 876e19af55..95f7e14b65 100644 --- a/bin/node/src/commands/mod.rs +++ b/bin/node/src/commands/mod.rs @@ -2,7 +2,6 @@ pub mod block_producer; pub mod ntx_builder; pub mod rpc; pub mod store; -pub mod validator; const ENV_DATA_DIRECTORY: &str = "MIDEN_NODE_DATA_DIRECTORY"; const ENV_ENABLE_OTEL: &str = "MIDEN_NODE_ENABLE_OTEL"; diff --git a/bin/node/src/commands/store.rs b/bin/node/src/commands/store.rs index 53ce600548..1f740f8756 100644 --- a/bin/node/src/commands/store.rs +++ b/bin/node/src/commands/store.rs @@ -24,7 +24,7 @@ const ENV_BLOCK_PROVER_URL: &str = "MIDEN_NODE_STORE_BLOCK_PROVER_URL"; pub enum StoreCommand { /// Bootstraps the blockchain database with a pre-existing genesis block. /// - /// The genesis block file should be produced by `miden-node validator bootstrap`. + /// The genesis block file should be produced by `miden-validator bootstrap`. Bootstrap { /// Directory in which to store the database and raw block data. #[arg(long, env = ENV_DATA_DIRECTORY, value_name = "DIR")] diff --git a/bin/node/src/main.rs b/bin/node/src/main.rs index 93789b97e9..f35414d22b 100644 --- a/bin/node/src/main.rs +++ b/bin/node/src/main.rs @@ -33,10 +33,6 @@ pub enum Command { #[command(subcommand)] BlockProducer(commands::block_producer::BlockProducerCommand), - /// Commands related to the node's validator component. - #[command(subcommand)] - Validator(commands::validator::ValidatorCommand), - /// Commands related to the node's network transaction builder component. #[command(subcommand)] NtxBuilder(commands::ntx_builder::NtxBuilderCommand), @@ -51,7 +47,6 @@ impl Command { Command::Store(subcommand) => subcommand.is_open_telemetry_enabled(), Command::Rpc(subcommand) => subcommand.is_open_telemetry_enabled(), Command::BlockProducer(subcommand) => subcommand.is_open_telemetry_enabled(), - Command::Validator(subcommand) => subcommand.is_open_telemetry_enabled(), Command::NtxBuilder(subcommand) => subcommand.is_open_telemetry_enabled(), } { OpenTelemetry::Enabled @@ -65,7 +60,6 @@ impl Command { Command::Rpc(rpc_command) => rpc_command.handle().await, Command::Store(store_command) => store_command.handle().await, Command::BlockProducer(block_producer_command) => block_producer_command.handle().await, - Command::Validator(validator) => validator.handle().await, Command::NtxBuilder(ntx_builder) => ntx_builder.handle().await, } } diff --git a/bin/node/src/tests.rs b/bin/node/src/tests.rs index d5e1b00570..7b6e3f5e30 100644 --- a/bin/node/src/tests.rs +++ b/bin/node/src/tests.rs @@ -16,16 +16,6 @@ fn block_producer_start_parses() { let _ = parse(&["block-producer", "start"]); } -#[test] -fn validator_bootstrap_parses() { - let _ = parse(&["validator", "bootstrap"]); -} - -#[test] -fn validator_start_parses() { - let _ = parse(&["validator", "start"]); -} - #[test] fn bundled_bootstrap_parses() { let _ = parse(&["bundled", "bootstrap"]); diff --git a/bin/validator/.env b/bin/validator/.env new file mode 100644 index 0000000000..c6eddf29bb --- /dev/null +++ b/bin/validator/.env @@ -0,0 +1,9 @@ +# For more info use -h on the relevant commands: +# miden-validator start -h + +MIDEN_NODE_ENABLE_OTEL=true +MIDEN_NODE_DATA_DIRECTORY= +MIDEN_NODE_VALIDATOR_URL= +MIDEN_NODE_VALIDATOR_GENESIS_CONFIG_FILE= +MIDEN_NODE_VALIDATOR_KEY= +MIDEN_NODE_VALIDATOR_KMS_KEY_ID= diff --git a/bin/validator/Cargo.toml b/bin/validator/Cargo.toml new file mode 100644 index 0000000000..32bc69fc1a --- /dev/null +++ b/bin/validator/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors.workspace = true +description = "Miden validator binary" +edition.workspace = true +homepage.workspace = true +keywords = ["miden", "validator"] +license.workspace = true +name = "miden-validator" +readme.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lints] +workspace = true + +[dependencies] +anyhow = { workspace = true } +clap = { features = ["env", "string"], workspace = true } +fs-err = { workspace = true } +hex = { workspace = true } +miden-node-store = { workspace = true } +miden-node-utils = { workspace = true } +miden-protocol = { workspace = true } +miden-validator-core = { workspace = true } +tokio = { features = ["macros", "rt-multi-thread"], workspace = true } +url = { workspace = true } diff --git a/bin/validator/src/commands/bootstrap.rs b/bin/validator/src/commands/bootstrap.rs new file mode 100644 index 0000000000..3eb5a287a9 --- /dev/null +++ b/bin/validator/src/commands/bootstrap.rs @@ -0,0 +1,101 @@ +use std::path::{Path, PathBuf}; + +use anyhow::Context; +use miden_node_store::genesis::config::{AccountFileWithName, GenesisConfig}; +use miden_node_utils::fs::ensure_empty_directory; +use miden_node_utils::signer::BlockSigner; +use miden_protocol::utils::serde::Serializable; +use miden_validator_core::ValidatorSigner; + +use super::ValidatorKey; + +const GENESIS_BLOCK_FILENAME: &str = "genesis.dat"; + +// Bootstraps the validator component. +pub async fn bootstrap( + genesis_block_directory: &Path, + accounts_directory: &Path, + data_directory: &Path, + genesis_config: Option<&PathBuf>, + validator_key: ValidatorKey, +) -> anyhow::Result<()> { + let config = genesis_config + .map(|file_path| { + GenesisConfig::read_toml_file(file_path).with_context(|| { + format!("failed to parse genesis config from file {}", file_path.display()) + }) + }) + .transpose()? + .unwrap_or_default(); + + for directory in [accounts_directory, genesis_block_directory] { + ensure_empty_directory(directory)?; + } + + let signer = validator_key.into_signer().await?; + match signer { + ValidatorSigner::Kms(signer) => { + build_and_write_genesis( + config, + signer, + accounts_directory, + genesis_block_directory, + data_directory, + ) + .await + }, + ValidatorSigner::Local(signer) => { + build_and_write_genesis( + config, + signer, + accounts_directory, + genesis_block_directory, + data_directory, + ) + .await + }, + } +} + +/// Builds the genesis state, writes account secret files, signs the genesis block, writes it +/// to disk, and initializes the validator's database with the genesis block as the chain tip. +async fn build_and_write_genesis( + config: GenesisConfig, + signer: impl BlockSigner, + accounts_directory: &Path, + genesis_block_directory: &Path, + data_directory: &Path, +) -> anyhow::Result<()> { + let (genesis_state, secrets) = config.into_state(signer)?; + + for item in secrets.as_account_files(&genesis_state) { + let AccountFileWithName { account_file, name } = item?; + let account_path = accounts_directory.join(name); + // Do not override existing keys. + fs_err::OpenOptions::new() + .create_new(true) + .write(true) + .open(&account_path) + .context("key file already exists")?; + account_file.write(account_path)?; + } + + let genesis_block = + genesis_state.into_block().await.context("failed to build the genesis block")?; + + let block_bytes = genesis_block.inner().to_bytes(); + let genesis_block_path = genesis_block_directory.join(GENESIS_BLOCK_FILENAME); + fs_err::write(&genesis_block_path, block_bytes).context("failed to write genesis block")?; + + let (genesis_header, ..) = genesis_block.into_inner().into_parts(); + let db = miden_validator_core::db::load(data_directory.join("validator.sqlite3")) + .await + .context("failed to initialize validator database during bootstrap")?; + db.transact("upsert_block_header", move |conn| { + miden_validator_core::db::upsert_block_header(conn, &genesis_header) + }) + .await + .context("failed to persist genesis block header as chain tip")?; + + Ok(()) +} diff --git a/bin/node/src/commands/validator.rs b/bin/validator/src/commands/mod.rs similarity index 50% rename from bin/node/src/commands/validator.rs rename to bin/validator/src/commands/mod.rs index 807f5fa129..ea81923d69 100644 --- a/bin/node/src/commands/validator.rs +++ b/bin/validator/src/commands/mod.rs @@ -1,33 +1,33 @@ -use std::net::SocketAddr; -use std::path::{Path, PathBuf}; +mod bootstrap; +mod start; + +use std::path::PathBuf; use anyhow::Context; -use miden_node_store::genesis::config::{AccountFileWithName, GenesisConfig}; +use clap::Parser; use miden_node_utils::clap::GrpcOptionsInternal; -use miden_node_utils::fs::ensure_empty_directory; use miden_node_utils::grpc::UrlExt; -use miden_node_utils::signer::BlockSigner; -use miden_node_validator::{Validator, ValidatorSigner}; use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey; -use miden_protocol::utils::serde::{Deserializable, Serializable}; +use miden_protocol::utils::serde::Deserializable; +use miden_validator_core::ValidatorSigner; use url::Url; -use crate::commands::{ENV_DATA_DIRECTORY, ENV_ENABLE_OTEL}; - +pub(crate) const ENV_DATA_DIRECTORY: &str = "MIDEN_NODE_DATA_DIRECTORY"; +const ENV_ENABLE_OTEL: &str = "MIDEN_NODE_ENABLE_OTEL"; const ENV_URL: &str = "MIDEN_NODE_VALIDATOR_URL"; const ENV_GENESIS_CONFIG_FILE: &str = "MIDEN_NODE_VALIDATOR_GENESIS_CONFIG_FILE"; -const ENV_KEY: &str = "MIDEN_NODE_VALIDATOR_KEY"; -const ENV_KMS_KEY_ID: &str = "MIDEN_NODE_VALIDATOR_KMS_KEY_ID"; +pub(crate) const ENV_KEY: &str = "MIDEN_NODE_VALIDATOR_KEY"; +pub(crate) const ENV_KMS_KEY_ID: &str = "MIDEN_NODE_VALIDATOR_KMS_KEY_ID"; /// A predefined, insecure validator key for development purposes. -const INSECURE_KEY_HEX: &str = "0101010101010101010101010101010101010101010101010101010101010101"; -/// The filename used for the genesis block file. -pub const GENESIS_BLOCK_FILENAME: &str = "genesis.dat"; +pub(crate) const INSECURE_KEY_HEX: &str = + "0101010101010101010101010101010101010101010101010101010101010101"; // VALIDATOR COMMAND // ================================================================================================ -#[derive(clap::Subcommand)] +#[derive(Parser)] +#[command(version, about, long_about = None)] pub enum ValidatorCommand { /// Bootstraps the genesis block. /// @@ -100,7 +100,6 @@ pub enum ValidatorCommand { } impl ValidatorCommand { - /// Runs the validator command. pub async fn handle(self) -> anyhow::Result<()> { match self { Self::Bootstrap { @@ -110,7 +109,7 @@ impl ValidatorCommand { genesis_config_file, validator_key, } => { - Self::bootstrap_genesis( + bootstrap::bootstrap( &genesis_block_directory, &accounts_directory, &data_directory, @@ -131,149 +130,30 @@ impl ValidatorCommand { .to_socket() .context("failed to extract socket address from validator URL")?; - // Run validator with KMS key backend if key id provided. if let Some(kms_key_id) = kms_key_id { let signer = ValidatorSigner::new_kms(kms_key_id).await?; - Self::serve(address, grpc_options, signer, data_directory).await + start::start(address, grpc_options, signer, data_directory).await } else { let signer = SecretKey::read_from_bytes(hex::decode(validator_key)?.as_ref())?; let signer = ValidatorSigner::new_local(signer); - Self::serve(address, grpc_options, signer, data_directory).await + start::start(address, grpc_options, signer, data_directory).await } }, } } - /// Runs the validator component until failure. - async fn serve( - address: SocketAddr, - grpc_options: GrpcOptionsInternal, - signer: ValidatorSigner, - data_directory: PathBuf, - ) -> anyhow::Result<()> { - Validator { - address, - grpc_options, - signer, - data_directory, - } - .serve() - .await - .context("failed while serving validator component") - } - pub fn is_open_telemetry_enabled(&self) -> bool { match self { Self::Start { enable_otel, .. } => *enable_otel, Self::Bootstrap { .. } => false, } } - - /// Bootstraps the genesis block: creates accounts, signs the block, and writes artifacts to - /// disk. - async fn bootstrap_genesis( - genesis_block_directory: &Path, - accounts_directory: &Path, - data_directory: &Path, - genesis_config: Option<&PathBuf>, - validator_key: ValidatorKey, - ) -> anyhow::Result<()> { - // Parse genesis config (or default if not given). - let config = genesis_config - .map(|file_path| { - GenesisConfig::read_toml_file(file_path).with_context(|| { - format!("failed to parse genesis config from file {}", file_path.display()) - }) - }) - .transpose()? - .unwrap_or_default(); - - // Create directories if they do not already exist. - for directory in [accounts_directory, genesis_block_directory] { - ensure_empty_directory(directory)?; - } - - // Bootstrap with KMS key or local key. - let signer = validator_key.into_signer().await?; - match signer { - ValidatorSigner::Kms(signer) => { - build_and_write_genesis( - config, - signer, - accounts_directory, - genesis_block_directory, - data_directory, - ) - .await - }, - ValidatorSigner::Local(signer) => { - build_and_write_genesis( - config, - signer, - accounts_directory, - genesis_block_directory, - data_directory, - ) - .await - }, - } - } -} - -/// Builds the genesis state, writes account secret files, signs the genesis block, writes it -/// to disk, and initializes the validator's database with the genesis block as the chain tip. -async fn build_and_write_genesis( - config: GenesisConfig, - signer: impl BlockSigner, - accounts_directory: &Path, - genesis_block_directory: &Path, - data_directory: &Path, -) -> anyhow::Result<()> { - // Build genesis state with the provided signer. - let (genesis_state, secrets) = config.into_state(signer)?; - - // Write account secret files. - for item in secrets.as_account_files(&genesis_state) { - let AccountFileWithName { account_file, name } = item?; - let accountpath = accounts_directory.join(name); - // Do not override existing keys. - fs_err::OpenOptions::new() - .create_new(true) - .write(true) - .open(&accountpath) - .context("key file already exists")?; - account_file.write(accountpath)?; - } - - // Build the signed genesis block. - let genesis_block = - genesis_state.into_block().await.context("failed to build the genesis block")?; - - // Serialize and write the genesis block to disk. - let block_bytes = genesis_block.inner().to_bytes(); - let genesis_block_path = genesis_block_directory.join(GENESIS_BLOCK_FILENAME); - fs_err::write(&genesis_block_path, block_bytes).context("failed to write genesis block")?; - - // Initialize the validator database and persist the genesis block header as the chain tip. - let (genesis_header, ..) = genesis_block.into_inner().into_parts(); - let db = miden_node_validator::db::load(data_directory.join("validator.sqlite3")) - .await - .context("failed to initialize validator database during bootstrap")?; - db.transact("upsert_block_header", move |conn| { - miden_node_validator::db::upsert_block_header(conn, &genesis_header) - }) - .await - .context("failed to persist genesis block header as chain tip")?; - - Ok(()) } // VALIDATOR KEY // ================================================================================================ /// Configuration for the Validator key used to sign blocks. -/// -/// Used by the Validator command and the genesis bootstrap command. #[derive(clap::Args)] #[group(required = false, multiple = false)] pub struct ValidatorKey { @@ -288,7 +168,7 @@ pub struct ValidatorKey { value_name = "VALIDATOR_KEY", default_value = INSECURE_KEY_HEX, )] - validator_key: String, + pub validator_key: String, /// Key ID for the KMS key used by validator to sign blocks. /// /// Cannot be used with `validator.key.hex`. @@ -297,22 +177,16 @@ pub struct ValidatorKey { env = ENV_KMS_KEY_ID, value_name = "VALIDATOR_KMS_KEY_ID", )] - validator_kms_key_id: Option, + pub validator_kms_key_id: Option, } impl ValidatorKey { - /// Consumes the validator key configuration and returns a KMS or local key signer depending on - /// the supplied configuration. pub async fn into_signer(self) -> anyhow::Result { if let Some(kms_key_id) = self.validator_kms_key_id { - // Use KMS key ID to create a ValidatorSigner. - let signer = ValidatorSigner::new_kms(kms_key_id).await?; - Ok(signer) + Ok(ValidatorSigner::new_kms(kms_key_id).await?) } else { - // Use hex-encoded key to create a ValidatorSigner. let signer = SecretKey::read_from_bytes(hex::decode(self.validator_key)?.as_ref())?; - let signer = ValidatorSigner::new_local(signer); - Ok(signer) + Ok(ValidatorSigner::new_local(signer)) } } } diff --git a/bin/validator/src/commands/start.rs b/bin/validator/src/commands/start.rs new file mode 100644 index 0000000000..994f478937 --- /dev/null +++ b/bin/validator/src/commands/start.rs @@ -0,0 +1,24 @@ +use std::net::SocketAddr; +use std::path::PathBuf; + +use anyhow::Context; +use miden_node_utils::clap::GrpcOptionsInternal; +use miden_validator_core::{Validator, ValidatorSigner}; + +// Starts the validator component. +pub async fn start( + address: SocketAddr, + grpc_options: GrpcOptionsInternal, + signer: ValidatorSigner, + data_directory: PathBuf, +) -> anyhow::Result<()> { + Validator { + address, + grpc_options, + signer, + data_directory, + } + .serve() + .await + .context("failed while serving validator component") +} diff --git a/bin/validator/src/main.rs b/bin/validator/src/main.rs new file mode 100644 index 0000000000..6f27a578cc --- /dev/null +++ b/bin/validator/src/main.rs @@ -0,0 +1,22 @@ +use clap::Parser; +use miden_node_utils::logging::OpenTelemetry; + +mod commands; + +// MAIN +// ================================================================================================ + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let command = commands::ValidatorCommand::parse(); + + let otel = if command.is_open_telemetry_enabled() { + OpenTelemetry::Enabled + } else { + OpenTelemetry::Disabled + }; + + let _otel_guard = miden_node_utils::logging::setup_tracing(otel)?; + + command.handle().await +} diff --git a/crates/block-producer/Cargo.toml b/crates/block-producer/Cargo.toml index d3349bf7cc..9e589834b5 100644 --- a/crates/block-producer/Cargo.toml +++ b/crates/block-producer/Cargo.toml @@ -46,9 +46,9 @@ url = { workspace = true } assert_matches = { workspace = true } miden-node-store = { workspace = true } miden-node-utils = { features = ["testing"], workspace = true } -miden-node-validator = { workspace = true } miden-protocol = { default-features = true, features = ["testing"], workspace = true } miden-standards = { features = ["testing"], workspace = true } +miden-validator-core = { workspace = true } pretty_assertions = { workspace = true } rand_chacha = { default-features = false, workspace = true } serial_test = { workspace = true } diff --git a/crates/block-producer/src/server/tests.rs b/crates/block-producer/src/server/tests.rs index c3b1d40039..b5b981621a 100644 --- a/crates/block-producer/src/server/tests.rs +++ b/crates/block-producer/src/server/tests.rs @@ -5,8 +5,8 @@ use miden_node_proto::generated::block_producer::api_client as block_producer_cl use miden_node_store::{DEFAULT_MAX_CONCURRENT_PROOFS, GenesisState, Store, StoreMode}; use miden_node_utils::clap::{GrpcOptionsInternal, StorageOptions}; use miden_node_utils::fee::test_fee_params; -use miden_node_validator::{Validator, ValidatorSigner}; use miden_protocol::testing::random_secret_key::random_secret_key; +use miden_validator_core::{Validator, ValidatorSigner}; use tokio::net::TcpListener; use tokio::time::sleep; use tokio::{runtime, task}; diff --git a/crates/validator/Cargo.toml b/crates/validator/Cargo.toml index bbe4d4df0a..65ed5d8204 100644 --- a/crates/validator/Cargo.toml +++ b/crates/validator/Cargo.toml @@ -5,7 +5,7 @@ edition.workspace = true homepage.workspace = true keywords = ["miden", "node", "validator"] license.workspace = true -name = "miden-node-validator" +name = "miden-validator-core" readme = "README.md" repository.workspace = true rust-version.workspace = true diff --git a/docker-compose.yml b/docker-compose.yml index cd5594507b..45b00d310d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: - genesis: - image: miden-node-image + genesis-validator: + image: miden-validator pull_policy: if_not_present profiles: - genesis @@ -12,17 +12,32 @@ services: set -e mkdir -p /data/genesis /data/store /data/validator /data/accounts /data/ntx-builder echo "Bootstrapping validator (creating genesis block)..." - miden-node validator bootstrap \ + miden-validator bootstrap \ --data-directory /data/validator \ --genesis-block-directory /data/genesis \ --accounts-directory /data/accounts + + genesis-store: + image: miden-node + pull_policy: if_not_present + profiles: + - genesis + volumes: + - node-data:/data + entrypoint: ["/bin/sh", "-c"] + depends_on: + genesis-validator: + condition: service_completed_successfully + command: + - | + set -e echo "Bootstrapping store..." miden-node store bootstrap \ --data-directory /data/store \ --genesis-block /data/genesis/genesis.dat store: - image: miden-node-image + image: miden-node pull_policy: if_not_present volumes: - node-data:/data @@ -48,7 +63,7 @@ services: - "50003:50003" validator: - image: miden-node-image + image: miden-validator pull_policy: if_not_present volumes: - node-data:/data @@ -57,8 +72,7 @@ services: - OTEL_EXPORTER_OTLP_ENDPOINT=http://tempo:4317 - OTEL_SERVICE_NAME=validator command: - - miden-node - - validator + - miden-validator - start - http://0.0.0.0:50101 - --data-directory=/data/validator @@ -66,7 +80,7 @@ services: - "50101:50101" block-producer: - image: miden-node-image + image: miden-node pull_policy: if_not_present environment: - MIDEN_NODE_ENABLE_OTEL=true @@ -83,7 +97,7 @@ services: - "50201:50201" rpc: - image: miden-node-image + image: miden-node pull_policy: if_not_present environment: - MIDEN_NODE_ENABLE_OTEL=true @@ -101,7 +115,7 @@ services: - "57291:57291" ntx-builder: - image: miden-node-image + image: miden-node pull_policy: if_not_present volumes: - node-data:/data diff --git a/docs/external/src/operator/usage.md b/docs/external/src/operator/usage.md index 62d05b31a8..041461b5f2 100644 --- a/docs/external/src/operator/usage.md +++ b/docs/external/src/operator/usage.md @@ -20,7 +20,7 @@ its database from that block. By default the genesis block will contain a single ```sh # Step 1: Validator bootstrap — create the signed genesis block and account files. -miden-node validator bootstrap \ +miden-validator bootstrap \ --genesis-block-directory genesis-data \ --accounts-directory accounts @@ -36,7 +36,7 @@ transactions to achieve the desired state. Any account secrets will be written t the provided `--accounts-directory` path in the process. ```sh -miden-node validator bootstrap \ +miden-validator bootstrap \ --genesis-block-directory genesis-data \ --accounts-directory accounts \ --genesis-config-file genesis.toml @@ -110,7 +110,7 @@ The default `compose-up` target starts all node components along with a telemetr a network monitor: ```sh -make docker-build-node +make docker-build make docker-build-monitor make compose-genesis make compose-up @@ -166,7 +166,7 @@ miden-node store start \ --data-directory /tmp/store # Start the validator -miden-node validator start http://0.0.0.0:50101 \ +miden-validator start http://0.0.0.0:50101 \ --data-directory /tmp/validator # Start the block producer diff --git a/packaging/node/postinst b/packaging/node/postinst index 036b2d112a..a6365a7ac6 100644 --- a/packaging/node/postinst +++ b/packaging/node/postinst @@ -2,28 +2,27 @@ # # This is a postinstallation script so the service can be configured and started when requested. -for svc in miden-node miden-validator; do - # user is expected by the systemd service file and `/opt/` is its working directory, - sudo adduser --disabled-password --disabled-login --shell /usr/sbin/nologin --quiet --system --no-create-home --home /nonexistent "$svc" +svc=miden-node - # Working folder. - if [ -d "/opt/$svc" ] - then - echo "Directory /opt/$svc exists." - else - mkdir -p "/opt/$svc" - fi - sudo chown -R "$svc" "/opt/$svc" +# user is expected by the systemd service file and `/opt/` is its working directory, +sudo adduser --disabled-password --disabled-login --shell /usr/sbin/nologin --quiet --system --no-create-home --home /nonexistent "$svc" - # Configuration folder - if [ -d "/etc/opt/$svc" ] - then - echo "Directory /etc/opt/$svc exists." - else - mkdir -p "/etc/opt/$svc" - fi - sudo chown -R "$svc" "/etc/opt/$svc" +# Working folder. +if [ -d "/opt/$svc" ] +then + echo "Directory /opt/$svc exists." +else + mkdir -p "/opt/$svc" +fi +sudo chown -R "$svc" "/opt/$svc" -done +# Configuration folder +if [ -d "/etc/opt/$svc" ] +then + echo "Directory /etc/opt/$svc exists." +else + mkdir -p "/etc/opt/$svc" +fi +sudo chown -R "$svc" "/etc/opt/$svc" sudo systemctl daemon-reload diff --git a/packaging/node/postrm b/packaging/node/postrm index 86a9846a25..e87c22c55f 100644 --- a/packaging/node/postrm +++ b/packaging/node/postrm @@ -1,12 +1,12 @@ #!/bin/bash # ############### -# Remove miden-node installs +# Remove miden-node install ############## -for svc in miden-node miden-validator; do - sudo rm -rf "/lib/systemd/system/$svc.service" - sudo rm -rf "/etc/opt/$svc" - sudo deluser "$svc" -done +svc=miden-node + +sudo rm -rf "/lib/systemd/system/$svc.service" +sudo rm -rf "/etc/opt/$svc" +sudo deluser "$svc" sudo systemctl daemon-reload diff --git a/packaging/node/miden-validator.service b/packaging/validator/miden-validator.service similarity index 87% rename from packaging/node/miden-validator.service rename to packaging/validator/miden-validator.service index 7b6c5de874..68ebe5cb01 100644 --- a/packaging/node/miden-validator.service +++ b/packaging/validator/miden-validator.service @@ -9,7 +9,7 @@ WantedBy=multi-user.target Type=exec Environment="OTEL_SERVICE_NAME=miden-validator" EnvironmentFile=/lib/systemd/system/miden-validator.env -ExecStart=/usr/bin/miden-node validator start +ExecStart=/usr/bin/miden-validator start WorkingDirectory=/opt/miden-validator User=miden-validator RestartSec=5 diff --git a/packaging/validator/postinst b/packaging/validator/postinst new file mode 100644 index 0000000000..53d185ca3d --- /dev/null +++ b/packaging/validator/postinst @@ -0,0 +1,28 @@ +#!/bin/bash +# +# This is a postinstallation script so the service can be configured and started when requested. + +svc=miden-validator + +# user is expected by the systemd service file and `/opt/` is its working directory, +sudo adduser --disabled-password --disabled-login --shell /usr/sbin/nologin --quiet --system --no-create-home --home /nonexistent "$svc" + +# Working folder. +if [ -d "/opt/$svc" ] +then + echo "Directory /opt/$svc exists." +else + mkdir -p "/opt/$svc" +fi +sudo chown -R "$svc" "/opt/$svc" + +# Configuration folder +if [ -d "/etc/opt/$svc" ] +then + echo "Directory /etc/opt/$svc exists." +else + mkdir -p "/etc/opt/$svc" +fi +sudo chown -R "$svc" "/etc/opt/$svc" + +sudo systemctl daemon-reload diff --git a/packaging/validator/postrm b/packaging/validator/postrm new file mode 100644 index 0000000000..d801ef8dd6 --- /dev/null +++ b/packaging/validator/postrm @@ -0,0 +1,12 @@ +#!/bin/bash +# +############### +# Remove miden-validator install +############## +svc=miden-validator + +sudo rm -rf "/lib/systemd/system/$svc.service" +sudo rm -rf "/etc/opt/$svc" +sudo deluser "$svc" + +sudo systemctl daemon-reload diff --git a/scripts/run-node.sh b/scripts/run-node.sh index a9621917ac..7414c6c2ea 100755 --- a/scripts/run-node.sh +++ b/scripts/run-node.sh @@ -4,6 +4,7 @@ set -euo pipefail # Configuration SKIP_BOOTSTRAP="${SKIP_BOOTSTRAP:-false}" BINARY="${MIDEN_NODE_BIN:-./target/debug/miden-node}" +VALIDATOR_BINARY="${MIDEN_VALIDATOR_BIN:-./target/debug/miden-validator}" KMS_KEY_ID="${KMS_KEY_ID:-}" if [[ -n "$KMS_KEY_ID" ]]; then AWS_REGION="${AWS_REGION:?error: AWS_REGION environment variable must be set when KMS_KEY_ID is set}" @@ -75,7 +76,7 @@ if [[ "$SKIP_BOOTSTRAP" != "true" ]]; then KMS_BOOTSTRAP_ARGS+=(--validator.key.kms-id "$KMS_KEY_ID") fi - $BINARY validator bootstrap \ + $VALIDATOR_BINARY bootstrap \ --data-directory "$VALIDATOR_DIR" \ --genesis-block-directory "$VALIDATOR_DIR" \ --accounts-directory "$ACCOUNTS_DIR" \ @@ -118,7 +119,7 @@ if [[ -n "$KMS_KEY_ID" ]]; then fi echo "Starting validator..." -$BINARY validator start "$VALIDATOR_URL" \ +$VALIDATOR_BINARY start "$VALIDATOR_URL" \ --data-directory "$VALIDATOR_DIR" \ "${KMS_START_ARGS[@]+"${KMS_START_ARGS[@]}"}" & PIDS+=($!)