diff --git a/.github/workflows/docker-images.yml b/.github/workflows/docker-images.yml index 87025908506..c030b6e8af2 100644 --- a/.github/workflows/docker-images.yml +++ b/.github/workflows/docker-images.yml @@ -348,3 +348,58 @@ jobs: TARGET_BASE_IMAGE=alpine:3.22 TARGETOS=linux TARGETARCH=amd64 + + build-op-batcher-tee: + needs: prepare-deployment + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download deployment artifacts + uses: actions/download-artifact@v4 + with: + name: deployment-artifacts + + - name: Copy config for op-batcher + run: | + mkdir -p packages/contracts-bedrock/lib/superchain-registry/ops/testdata/monorepo + # Copy any required config files here, or create placeholder + echo "Config prepared for op-batcher" + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_PREFIX }}/op-batcher-tee + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix={{branch}}-,enable={{is_default_branch}} + type=raw,value=latest,enable={{is_default_branch}} + type=raw,value=pr-${{ github.event.number }},enable=${{ github.event_name == 'pull_request' }} + + - name: Build and push OP Batcher TEE + uses: docker/build-push-action@v5 + with: + context: . + file: espresso/docker/op-stack/Dockerfile + target: op-batcher-enclave-target + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + TARGET_BASE_IMAGE=alpine:3.22 + TARGETOS=linux + TARGETARCH=amd64 diff --git a/README_ESPRESSO.md b/README_ESPRESSO.md index 149e1304988..681d2131ece 100644 --- a/README_ESPRESSO.md +++ b/README_ESPRESSO.md @@ -298,6 +298,10 @@ docker compose down -v --remove-orphans ```console docker compose up --build -d ``` +If you're on a machine with [AWS Nitro Enclaves enabled](#guide-setting-up-an-enclave-enabled-nitro-ec2-instance), use the `tee` profile instead to start the enclave batcher. +```console +COMPOSE_PROFILES=tee docker compose up --build -d +``` * Run the services and check the log. ```console diff --git a/espresso/.env b/espresso/.env index d0b0d91360e..2f7f1164279 100644 --- a/espresso/.env +++ b/espresso/.env @@ -36,3 +36,5 @@ OPERATOR_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 L1_CHAIN_ID=11155111 L2_CHAIN_ID=22266222 + +COMPOSE_PROFILES=default diff --git a/espresso/docker-compose-op-geth.yml b/espresso/docker-compose-op-geth.yml index cdffe30c2bf..af82c1b1009 100644 --- a/espresso/docker-compose-op-geth.yml +++ b/espresso/docker-compose-op-geth.yml @@ -20,6 +20,3 @@ services: L1_RPC: http://l1-geth:${L1_HTTP_PORT:?err} OP_HTTP_PORT: ${OP_HTTP_PORT:?err} OP_ENGINE_PORT: ${OP_ENGINE_PORT:?err} - ports: - - "${OP_HTTP_PORT}" - - "${OP_ENGINE_PORT}" diff --git a/espresso/docker-compose.yml b/espresso/docker-compose.yml index 36e9318063b..e9f205422db 100644 --- a/espresso/docker-compose.yml +++ b/espresso/docker-compose.yml @@ -142,6 +142,9 @@ services: service: op-geth volumes: - op-data-seq:/data + ports: + - "${OP_HTTP_PORT}:${OP_HTTP_PORT}" + - "${OP_ENGINE_PORT}:${OP_ENGINE_PORT}" op-geth-verifier: extends: @@ -149,6 +152,9 @@ services: service: op-geth volumes: - op-data-verifier:/data + ports: + - "8547:${OP_HTTP_PORT}" + - "8553:${OP_ENGINE_PORT}" op-geth-caff-node: extends: @@ -156,6 +162,9 @@ services: service: op-geth volumes: - op-data-caff-node:/data + ports: + - "8548:${OP_HTTP_PORT}" + - "8554:${OP_ENGINE_PORT}" op-node-sequencer: build: @@ -269,6 +278,7 @@ services: restart: "no" op-batcher: + profiles: ["default"] build: context: ../ dockerfile: espresso/docker/op-stack/Dockerfile @@ -304,7 +314,82 @@ services: - --max-channel-duration=1 - --target-num-frames=1 + # HTTP proxy for enclave Odyn proxy requirement + http-proxy: + image: alpine:latest + command: > + sh -c " + apk add --no-cache tinyproxy && + echo 'Allow 127.0.0.1' >> /etc/tinyproxy/tinyproxy.conf && + echo 'Allow 0.0.0.0/0' >> /etc/tinyproxy/tinyproxy.conf && + echo 'DisableViaHeader Yes' >> /etc/tinyproxy/tinyproxy.conf && + tinyproxy -d + " + ports: + - "3128:8888" + networks: + default: + aliases: + - proxy + + op-batcher-tee: + profiles: ["tee"] + build: + context: ../ + dockerfile: espresso/docker/op-stack/Dockerfile + target: op-batcher-enclave-target + image: op-batcher-tee:espresso + healthcheck: + test: ["CMD-SHELL", "test -f /tmp/enclave-tools.pid && kill -0 $(cat /tmp/enclave-tools.pid) 2>/dev/null || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + depends_on: + l1-geth: + condition: service_healthy + op-geth-sequencer: + condition: service_started + op-node-sequencer: + condition: service_started + espresso-dev-node: + condition: service_started + l2-genesis: + condition: service_completed_successfully + http-proxy: + condition: service_started + network_mode: "host" + environment: + http_proxy: http://127.0.0.1:3128 + HTTP_PROXY: http://127.0.0.1:3128 + OPERATOR_PRIVATE_KEY: ${OPERATOR_PRIVATE_KEY} + ENCLAVE_DEBUG: ${ENCLAVE_DEBUG:-false} + CONTAINER_MONITOR_INTERVAL: ${CONTAINER_MONITOR_INTERVAL:-1} + ENCLAVE_MEMORY_MB: ${ENCLAVE_MEMORY_MB:-4096} + ENCLAVE_CPU_COUNT: ${ENCLAVE_CPU_COUNT:-2} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ..:/source:ro + - ./scripts/batcher-enclave-tool-image.sh:/app/espresso/scripts/batcher-enclave-tool-image.sh:ro + - /tmp:/tmp + privileged: true + devices: + - /dev/nitro_enclaves:/dev/nitro_enclaves + restart: "no" + command: + - sh + - -c + - | + export DEPLOYMENT_MODE=local + export L1_RPC_URL="http://127.0.0.1:${L1_HTTP_PORT}" + export L2_RPC_URL="http://127.0.0.1:${OP_HTTP_PORT}" + export ROLLUP_RPC_URL="http://127.0.0.1:${ROLLUP_PORT}" + export ESPRESSO_URL1="http://127.0.0.1:${ESPRESSO_SEQUENCER_API_PORT}" + /source/espresso/docker/op-batcher-tee/run-enclave.sh + + op-proposer: + profiles: ["default"] build: context: ../ dockerfile: espresso/docker/op-stack/Dockerfile diff --git a/espresso/docker/op-batcher-tee/run-enclave.sh b/espresso/docker/op-batcher-tee/run-enclave.sh new file mode 100755 index 00000000000..9ba86cab307 --- /dev/null +++ b/espresso/docker/op-batcher-tee/run-enclave.sh @@ -0,0 +1,299 @@ +#!/bin/bash +# Enclave Batcher Runner Script +# Supports both local (docker-compose) and AWS ECS deployments + +set -e + +# Required environment variables - will fail if not set +: ${L1_RPC_URL:?Error: L1_RPC_URL is required} +: ${L2_RPC_URL:?Error: L2_RPC_URL is required} +: ${ROLLUP_RPC_URL:?Error: ROLLUP_RPC_URL is required} +: ${ESPRESSO_URL1:?Error: ESPRESSO_URL1 is required} +: ${OPERATOR_PRIVATE_KEY:?Error: OPERATOR_PRIVATE_KEY is required} + +# Optional configuration with defaults +TAG="${TAG:-op-batcher-enclavetool}" +ESPRESSO_URL2="${ESPRESSO_URL2:-$ESPRESSO_URL1}" # Default to same as URL1 if not set +ENCLAVE_DEBUG="${ENCLAVE_DEBUG:-false}" +MONITOR_INTERVAL="${MONITOR_INTERVAL:-30}" +MEMORY_MB="${ENCLAVE_MEMORY_MB:-4096}" +CPU_COUNT="${ENCLAVE_CPU_COUNT:-2}" + +# Deployment mode detection +DEPLOYMENT_MODE="${DEPLOYMENT_MODE:-aws}" # 'local' or 'aws' + +echo "=== Enclave Batcher Configuration ===" +echo "Deployment Mode: $DEPLOYMENT_MODE" +echo "L1 RPC URL: $L1_RPC_URL" +echo "L2 RPC URL: $L2_RPC_URL" +echo "Rollup RPC URL: $ROLLUP_RPC_URL" +echo "Espresso URLs: $ESPRESSO_URL1, $ESPRESSO_URL2" +echo "Debug Mode: $ENCLAVE_DEBUG" +echo "Monitor Interval: $MONITOR_INTERVAL seconds" +echo "Memory: ${MEMORY_MB}MB" +echo "CPU Count: $CPU_COUNT" +echo "=====================================" + +# Batcher arguments +BATCHER_ARGS="--l1-eth-rpc=$L1_RPC_URL" +BATCHER_ARGS="$BATCHER_ARGS,--l2-eth-rpc=$L2_RPC_URL" +BATCHER_ARGS="$BATCHER_ARGS,--rollup-rpc=$ROLLUP_RPC_URL" +BATCHER_ARGS="$BATCHER_ARGS,--espresso-url=$ESPRESSO_URL1" +BATCHER_ARGS="$BATCHER_ARGS,--espresso-url=$ESPRESSO_URL2" +BATCHER_ARGS="$BATCHER_ARGS,--testing-espresso-batcher-private-key=$OPERATOR_PRIVATE_KEY" +BATCHER_ARGS="$BATCHER_ARGS,--mnemonic=test test test test test test test test test test test junk" +BATCHER_ARGS="$BATCHER_ARGS,--hd-path=m/44'/60'/0'/0/0" +BATCHER_ARGS="$BATCHER_ARGS,--throttle-threshold=0" +BATCHER_ARGS="$BATCHER_ARGS,--max-channel-duration=1" +BATCHER_ARGS="$BATCHER_ARGS,--target-num-frames=1" +BATCHER_ARGS="$BATCHER_ARGS,--espresso-light-client-addr=0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797" + +# Add debug arguments if enabled +if [ "$ENCLAVE_DEBUG" = "true" ]; then + BATCHER_ARGS="$BATCHER_ARGS,--log.level=debug" + echo "Debug logging enabled" +fi + +# Build the enclave image +echo "Building enclave image with tag: $TAG" +cd /source + +if ! enclave-tools build --op-root /source --tag "$TAG" 2>&1 | tee /tmp/build_output.log; then + echo "ERROR: Failed to build enclave image" + echo "Build output was:" + cat /tmp/build_output.log + exit 1 +fi + +echo "Build completed successfully" + +# Extract PCR0 from build output +PCR0=$(grep "PCR0:" /tmp/build_output.log | sed 's/.*PCR0: //') + +# Get batch authenticator address from deployment state +BATCH_AUTHENTICATOR_ADDRESS=$(jq -r '.opChainDeployments[0].batchAuthenticatorAddress' /source/espresso/deployment/deployer/state.json 2>/dev/null || echo "") + +# Register PCR0 if all required values are present +if [ -n "$PCR0" ] && [ -n "$BATCH_AUTHENTICATOR_ADDRESS" ] && [ -n "$OPERATOR_PRIVATE_KEY" ]; then + echo "Registering PCR0: $PCR0 with authenticator: $BATCH_AUTHENTICATOR_ADDRESS" + enclave-tools register \ + --authenticator "$BATCH_AUTHENTICATOR_ADDRESS" \ + --l1-url "$L1_RPC_URL" \ + --private-key "$OPERATOR_PRIVATE_KEY" \ + --pcr0 "$PCR0" + + if [ $? -ne 0 ]; then + echo "WARNING: Failed to register PCR0, continuing anyway..." + else + echo "PCR0 registration successful" + fi +else + echo "Skipping PCR0 registration - missing required values:" + echo " PCR0: ${PCR0:-[missing]}" + echo " BATCH_AUTHENTICATOR_ADDRESS: ${BATCH_AUTHENTICATOR_ADDRESS:-[missing]}" + echo " OPERATOR_PRIVATE_KEY: ${OPERATOR_PRIVATE_KEY:+[set]}" +fi + +# Setup tracking files for local deployment +if [ "$DEPLOYMENT_MODE" = "local" ]; then + PID_FILE="/tmp/enclave-tools.pid" + CONTAINER_TRACKER_FILE="/tmp/enclave-containers.txt" + STATUS_FILE="/tmp/enclave-status.json" + + # Cleanup function for local deployment + cleanup() { + echo "Cleaning up enclave resources..." + if [ -f "$PID_FILE" ]; then + STORED_PID=$(cat "$PID_FILE") + if kill -0 "$STORED_PID" 2>/dev/null; then + echo "Terminating enclave-tools process (PID: $STORED_PID)" + kill -TERM "$STORED_PID" 2>/dev/null || true + sleep 5 + kill -KILL "$STORED_PID" 2>/dev/null || true + fi + rm -f "$PID_FILE" + fi + + # Clean up any remaining enclave containers + if [ -f "$CONTAINER_TRACKER_FILE" ]; then + while IFS= read -r container_id; do + if [ -n "$container_id" ] && docker ps -q --filter id="$container_id" | grep -q "$container_id"; then + echo "Stopping tracked enclave container: $container_id" + docker stop "$container_id" 2>/dev/null || true + docker rm "$container_id" 2>/dev/null || true + fi + done < "$CONTAINER_TRACKER_FILE" + rm -f "$CONTAINER_TRACKER_FILE" + fi + + rm -f "$STATUS_FILE" + exit 0 + } + + # Setup signal handlers for local deployment + trap cleanup SIGTERM SIGINT EXIT + + # Get Docker network for local deployment + DOCKER_NETWORK=$(docker network ls --filter name=espresso --format "{{.Name}}" | head -1) + if [ -z "$DOCKER_NETWORK" ]; then + DOCKER_NETWORK="espresso_default" + fi + echo "Using Docker network: $DOCKER_NETWORK" + export DOCKER_DEFAULT_NETWORK="$DOCKER_NETWORK" + export ENCLAVE_DOCKER_NETWORK="$DOCKER_NETWORK" +fi + +# Run the enclave +echo "Starting enclave with command:" +echo " enclave-tools run --image \"$TAG\" --args \"$BATCHER_ARGS\"" + +enclave-tools run --image "$TAG" --args "$BATCHER_ARGS" & +ENCLAVE_TOOLS_PID=$! + +if [ "$DEPLOYMENT_MODE" = "local" ]; then + echo "$ENCLAVE_TOOLS_PID" > "$PID_FILE" + echo "Enclave-tools started with PID: $ENCLAVE_TOOLS_PID (stored in $PID_FILE)" +else + echo "Enclave-tools started with PID: $ENCLAVE_TOOLS_PID" +fi + +# Wait for enclave-tools to finish starting the enclave container +echo "Waiting for enclave-tools to complete startup..." +wait $ENCLAVE_TOOLS_PID +ENCLAVE_TOOLS_EXIT_CODE=$? +echo "Enclave-tools process completed with exit code: $ENCLAVE_TOOLS_EXIT_CODE" + +if [ "$DEPLOYMENT_MODE" = "local" ]; then + rm -f "$PID_FILE" +fi + +# Check if enclave-tools failed +if [ $ENCLAVE_TOOLS_EXIT_CODE -ne 0 ]; then + echo "ERROR: enclave-tools failed with exit code $ENCLAVE_TOOLS_EXIT_CODE" + exit $ENCLAVE_TOOLS_EXIT_CODE +fi + +# Wait for container to fully initialize +sleep 5 + +# Find the enclave container that was started +echo "Looking for running enclave container..." +CONTAINER_NAME=$(docker ps --format "table {{.Names}}" | grep "batcher-enclaver-" | head -1) + +if [ -z "$CONTAINER_NAME" ]; then + echo "ERROR: No enclave container found after waiting." + echo "Checking all Docker containers:" + docker ps -a + exit 1 +fi + +echo "Found enclave container: $CONTAINER_NAME" + +# Get container details +CONTAINER_ID=$(docker ps --filter "name=$CONTAINER_NAME" --format "{{.ID}}" | head -1) +CONTAINER_IMAGE=$(docker inspect "$CONTAINER_NAME" --format '{{.Config.Image}}' 2>/dev/null) +STARTED_AT=$(docker inspect "$CONTAINER_NAME" --format '{{.State.StartedAt}}' 2>/dev/null) + +echo "Container Details:" +echo " ID: $CONTAINER_ID" +echo " Image: $CONTAINER_IMAGE" +echo " Started: $STARTED_AT" + +# Setup status tracking for local deployment +if [ "$DEPLOYMENT_MODE" = "local" ]; then + echo "$CONTAINER_NAME" >> "$CONTAINER_TRACKER_FILE" + + # Create initial status file + cat > "$STATUS_FILE" <&1 | while read line; do + echo "[ENCLAVE] $line" + done +) & +LOG_PID=$! +echo "Log capture started with PID: $LOG_PID" + +# Monitor the container +echo "Monitoring enclave container $CONTAINER_NAME..." +MONITOR_COUNT=0 + +while true; do + # Check if the container is still running + CONTAINER_STATUS=$(docker inspect "$CONTAINER_NAME" 2>/dev/null | jq -r '.[0].State.Status' 2>/dev/null || echo "") + + if [ -z "$CONTAINER_STATUS" ] || [ "$CONTAINER_STATUS" != "running" ]; then + echo "$(date): Container $CONTAINER_NAME is no longer running (status: $CONTAINER_STATUS)" + + # Get exit code if available + EXIT_CODE=$(docker inspect "$CONTAINER_NAME" 2>/dev/null | jq -r '.[0].State.ExitCode' 2>/dev/null || echo "unknown") + echo "Container exit code: $EXIT_CODE" + + # Update status file for local deployment + if [ "$DEPLOYMENT_MODE" = "local" ] && [ -n "$STATUS_FILE" ]; then + cat > "$STATUS_FILE" </dev/null || echo "Could not get container stats" + + # Update status file for local deployment + if [ "$DEPLOYMENT_MODE" = "local" ] && [ -n "$STATUS_FILE" ]; then + cat > "$STATUS_FILE" </dev/null; then + echo "Stopping log capture..." + kill $LOG_PID 2>/dev/null || true +fi + +echo "Script exiting..." \ No newline at end of file diff --git a/espresso/docker/op-stack/Dockerfile b/espresso/docker/op-stack/Dockerfile index c871c87264b..aa0b959394a 100644 --- a/espresso/docker/op-stack/Dockerfile +++ b/espresso/docker/op-stack/Dockerfile @@ -91,6 +91,13 @@ WORKDIR /app/op-batcher ENV GOOS=$TARGETOS GOARCH=$TARGETARCH GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$OP_BATCHER_VERSION" RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build just op-batcher +# Build enclave-tools +FROM op-cgo-builder AS enclave-tools-builder +ARG ENCLAVE_TOOLS_VERSION=v0.0.0 +WORKDIR /app/op-batcher +ENV GOOS=$TARGETOS GOARCH=$TARGETARCH GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$ENCLAVE_TOOLS_VERSION" +RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build just enclave-tools + # Build op-proposer FROM builder AS op-proposer-builder ARG OP_PROPOSER_VERSION=v0.0.0 @@ -125,6 +132,25 @@ ADD "https://github.com/EspressoSystems/ark-srs/releases/download/v0.2.0/kzg10-a COPY --from=op-batcher-builder /app/op-batcher/bin/op-batcher /usr/local/bin/ CMD ["op-batcher"] +FROM $TARGET_BASE_IMAGE AS op-batcher-enclave-target +RUN apk add gcc docker bash jq curl wget +# Install enclaver for EIF creation +RUN curl -L https://github.com/enclaver-io/enclaver/releases/download/v0.5.0/enclaver-linux-x86_64-v0.5.0.tar.gz | tar xz --strip-components=1 -C /usr/local/bin enclaver-linux-x86_64-v0.5.0/enclaver +# Copy source code +COPY --from=op-cgo-builder /app /source +WORKDIR /source +# Copy pre-built forge-artifacts from host (faster for development) +COPY packages/contracts-bedrock/forge-artifacts /source/packages/contracts-bedrock/forge-artifacts +# Include the deployment state for contract addresses +COPY espresso/deployment/ /source/espresso/deployment/ +# Copy the run-enclave.sh script +COPY espresso/docker/op-batcher-tee/run-enclave.sh ./espresso/docker/op-batcher-tee/run-enclave.sh +RUN chmod +x ./espresso/docker/op-batcher-tee/run-enclave.sh +ENV AZTEC_SRS_PATH /aztec/kzg10-aztec20-srs-1048584.bin +ADD "https://github.com/EspressoSystems/ark-srs/releases/download/v0.2.0/kzg10-aztec20-srs-1048584.bin" /aztec/kzg10-aztec20-srs-1048584.bin +COPY --from=enclave-tools-builder /app/op-batcher/bin/enclave-tools /usr/local/bin/ +CMD ["enclave-tools"] + FROM $TARGET_BASE_IMAGE AS op-proposer-target RUN apk add jq COPY --from=op-proposer-builder /app/op-proposer/bin/op-proposer /usr/local/bin/ diff --git a/op-batcher/enclave-entrypoint.bash b/op-batcher/enclave-entrypoint.bash index 58e86c3199e..50523dfa3c2 100644 --- a/op-batcher/enclave-entrypoint.bash +++ b/op-batcher/enclave-entrypoint.bash @@ -6,7 +6,7 @@ # to directly pass commandline arguments when starting EIF images) # We will need to start a proxy for each of those urls -URL_ARG="^(--altda\.da-server|--espresso-url|--l1-eth-rpc|--l2-eth-rpc|--rollup-rpc|--signer\.endpoint)$" +URL_ARG_RE='^(--altda\.da-server|--espresso-url|--l1-eth-rpc|--l2-eth-rpc|--rollup-rpc|--signer\.endpoint)(=|$)' # Re-populate the arguments passed through the environment if [ -n "$ENCLAVE_BATCHER_ARGS" ]; then @@ -108,36 +108,49 @@ filtered_args=() url_args=() SOCAT_PORT=10001 +echo "Arguments: $@" # Process all arguments while [ $# -gt 0 ]; do + echo "Processing argument: $1" # Check if the argument matches the URL pattern - if [[ $1 =~ $URL_ARG ]]; then - # Extract the flag part and possible value part - flag=${BASH_REMATCH[1]} - - if [ $# -gt 1 ]; then - shift - value="$1" - else - echo "$flag doesn't have a value" - exit 1 - fi + if [[ $1 =~ $URL_ARG_RE ]]; then + echo "Found URL argument: $1" + # Extract the flag part and possible value part + flag=${BASH_REMATCH[1]} + + # extract value from "--flag=value" or "--flag value" + if [[ "$1" == *=* ]]; then + value="${1#*=}" + else + shift || { echo "$flag missing value"; exit 1; } + value="$1" + fi - echo "Rewriting $flag=$value" + # Handle comma-separated values for any flag + if [[ "$value" == *","* ]]; then + IFS=',' read -r -a parts <<< "$value" + for part in "${parts[@]}"; do + if ! new_url=$(launch_socat "$part" "$SOCAT_PORT"); then + echo "Failed to launch socat for $flag=$part"; exit 1 + fi + echo "Rewritten: $new_url" + url_args+=("${flag}=${new_url}") + ((SOCAT_PORT++)) + done + else if ! new_url=$(launch_socat "$value" "$SOCAT_PORT"); then - echo "Failed to launch socat for $flag=$value" - exit 1 + echo "Failed to launch socat for $flag=$value"; exit 1 fi echo "Rewritten: $new_url" url_args+=("$flag" "$new_url") - ((SOCAT_PORT++)) + fi else - # This is not a URL argument, add it to filtered args - filtered_args+=("$1") + filtered_args+=("$1") fi shift -done + done + # Combine the rewritten URL arguments with the other arguments all_args=("${filtered_args[@]}" "${url_args[@]}")