diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9328d3f9f..f75ae2c2d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,8 +15,10 @@ on: - main - dev env: - DOCKERFILE_PATH: crates/support/Dockerfile - IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/e3-support + SUPPORT_DOCKERFILE_PATH: crates/support/Dockerfile + CIPHERNODE_DOCKERFILE_PATH: crates/Dockerfile + SUPPORT_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/e3-support + CIPHERNODE_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/ciphernode HARDHAT_VAR_MNEMONIC: 'test test test test test test test test test test test junk' HARDHAT_VAR_INFURA_API_KEY: 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' PRIVATE_KEY: '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' @@ -143,44 +145,78 @@ jobs: image_tag: ${{ steps.version.outputs.version }} steps: - uses: actions/checkout@v4 - - name: Generate version tag - id: version - run: | - echo "version=$(git rev-parse --short=9 HEAD)" >> $GITHUB_OUTPUT - name: Generate tags id: tags run: | - VERSION=$(git rev-parse --short=9 HEAD) - TAGS="${{ env.IMAGE_NAME }}:$VERSION" - if [ "${{ github.ref }}" = "refs/heads/main" ]; then - TAGS="$TAGS,${{ env.IMAGE_NAME }}:latest" + SHORT_SHA=$(git rev-parse --short=9 HEAD) + TAGS="${SUPPORT_IMAGE_NAME}:${SHORT_SHA}" + + if [ "${GITHUB_REF}" = "refs/heads/main" ]; then + TAGS="$TAGS,${SUPPORT_IMAGE_NAME}:main" fi + echo "tags=$TAGS" >> $GITHUB_OUTPUT - name: Set up BuildKit uses: docker/setup-buildx-action@v3 + - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Build image uses: docker/build-push-action@v5 with: context: ./crates/support - file: ${{ env.DOCKERFILE_PATH }} - push: true + file: ${{ env.SUPPORT_DOCKERFILE_PATH }} + push: ${{ github.ref == 'refs/heads/main' }} + tags: ${{ steps.tags.outputs.tags }} + cache-from: | + type=gha,scope=e3-support + cache-to: | + type=gha,mode=max,scope=e3-support + + build_ciphernode_image: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Generate tags + id: tags + run: | + SHORT_SHA=$(git rev-parse --short=9 HEAD) + TAGS="${CIPHERNODE_IMAGE_NAME}:${SHORT_SHA}" + + if [ "${GITHUB_REF}" = "refs/heads/main" ]; then + TAGS="$TAGS,${CIPHERNODE_IMAGE_NAME}:main" + fi + + echo "tags=$TAGS" >> $GITHUB_OUTPUT + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.ref == 'refs/heads/main' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build ciphernode image + uses: docker/build-push-action@v5 + with: + context: . + file: ${{ env.CIPHERNODE_DOCKERFILE_PATH }} + push: ${{ github.ref == 'refs/heads/main' }} tags: ${{ steps.tags.outputs.tags }} cache-from: | - type=gha,scope=cargo-registry - type=gha,scope=cargo-git - type=gha,scope=cargo-target - type=gha,scope=buildcache + type=gha,scope=ciphernode cache-to: | - type=gha,mode=max,scope=cargo-registry - type=gha,mode=max,scope=cargo-git - type=gha,mode=max,scope=cargo-target - type=gha,mode=max,scope=buildcache + type=gha,mode=max,scope=ciphernode test_contracts: runs-on: 'ubuntu-latest' diff --git a/.github/workflows/ec2-deployment.yml b/.github/workflows/ec2-deployment.yml deleted file mode 100644 index cd2ed28d4e..0000000000 --- a/.github/workflows/ec2-deployment.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Build and Deploy Ciphernode -on: - push: - branches: - - dev - - main - paths: - - 'Cargo.*' - - 'crates/**' - - 'packages/enclave-contracts/contracts/**' - pull_request: - branches: - - dev - - main - paths: - - 'Cargo.*' - - 'crates/**' - - 'packages/enclave-contracts/contracts/**' - -env: - DOCKERFILE_PATH: crates/Dockerfile - IMAGE_NAME: ghcr.io/gnosisguild/ciphernode - -permissions: - contents: read - packages: write - -jobs: - build: - name: Build & Push Image - runs-on: ubuntu-latest - outputs: - image_tag: ${{ steps.version.outputs.version }} - steps: - - uses: actions/checkout@v3 - - - name: Generate version tag - id: version - run: echo "version=$(date +'%Y%m%d')-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build image - env: - IMAGE_TAG: ${{ steps.version.outputs.version }} - run: | - docker build -t $IMAGE_NAME:${{ steps.version.outputs.version }} -f $DOCKERFILE_PATH . - docker push $IMAGE_NAME:$IMAGE_TAG - - - name: Push to GHCR - if: github.ref == 'refs/heads/release' - env: - IMAGE_TAG: ${{ steps.version.outputs.version }} - run: | - docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest - docker push $IMAGE_NAME:latest - - deploy: - name: Deploy to Production - needs: build - runs-on: ubuntu-latest - environment: - name: production - if: github.ref == 'refs/heads/release' - - steps: - - name: Deploy to EC2 - uses: appleboy/ssh-action@v1.2.0 - with: - host: ${{ secrets.EC2_HOST }} - username: ${{ secrets.EC2_USERNAME }} - key: ${{ secrets.EC2_KEY }} - script: | - IMAGE_TAG="${{ needs.build.outputs.image_tag }}" - echo "Deploying version: $IMAGE_TAG" - docker pull $IMAGE_NAME:$IMAGE_TAG - - cd /home/ec2-user/enclave - git pull - - ./deploy/deploy.sh enclave $IMAGE_NAME:$IMAGE_TAG diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 70e3230f72..2b2227cdb2 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -11,6 +11,10 @@ on: - 'v*.*.*-*' # Pre-release tags like v1.0.0-beta.1 env: + CIPHERNODE_DOCKERFILE_PATH: crates/Dockerfile + SUPPORT_DOCKERFILE_PATH: crates/support/Dockerfile + CIPHERNODE_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/ciphernode + SUPPORT_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/e3-support MNEMONIC: 'test test test test test test test test test test test junk' INFURA_API_KEY: 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' ETHERSCAN_API_KEY: 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' @@ -67,6 +71,72 @@ jobs: echo "✅ All versions match: $VERSION" + build-ciphernode-image-release: + name: Build & Push ciphernode (release) + runs-on: ubuntu-latest + needs: validate-and-prepare + env: + CIPHERNODE_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/ciphernode + steps: + - uses: actions/checkout@v4 + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build & push ciphernode release image + uses: docker/build-push-action@v5 + with: + context: . + file: crates/Dockerfile + push: true + tags: | + ${{ env.CIPHERNODE_IMAGE_NAME }}:${{ needs.validate-and-prepare.outputs.version }} + ${{ env.CIPHERNODE_IMAGE_NAME }}:latest + cache-from: | + type=gha,scope=ciphernode + cache-to: | + type=gha,mode=max,scope=ciphernode + + build-e3-support-release: + name: Build & Push e3-support (release) + runs-on: ubuntu-latest + needs: validate-and-prepare + env: + SUPPORT_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/e3-support + steps: + - uses: actions/checkout@v4 + + - name: Set up Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build & push e3-support release image + uses: docker/build-push-action@v5 + with: + context: ./crates/support + file: ${{ env.SUPPORT_DOCKERFILE_PATH }} + push: true + tags: | + ${{ env.SUPPORT_IMAGE_NAME }}:${{ needs.validate-and-prepare.outputs.version }} + ${{ env.SUPPORT_IMAGE_NAME }}:latest + cache-from: | + type=gha,scope=e3-support + cache-to: | + type=gha,mode=max,scope=e3-support + build-binaries: name: Build Binaries (${{ matrix.os_name }}-${{ matrix.arch }}) runs-on: ${{ matrix.os }} @@ -217,7 +287,15 @@ jobs: create-github-release: name: Create GitHub Release runs-on: ubuntu-latest - needs: [validate-and-prepare, build-binaries, publish-rust-crates, publish-npm-packages] + needs: + [ + validate-and-prepare, + build-ciphernode-image-release, + build-e3-support-release, + build-binaries, + publish-rust-crates, + publish-npm-packages, + ] if: always() && needs.validate-and-prepare.result == 'success' && needs.build-binaries.result == 'success' steps: - name: Checkout diff --git a/dappnode/.gitignore b/dappnode/.gitignore new file mode 100644 index 0000000000..537867f288 --- /dev/null +++ b/dappnode/.gitignore @@ -0,0 +1,33 @@ +# DAppNode Package - Git Ignore + +# Environment files with secrets +.env +*.env.local + +# Local test data +data/ + +# Build artifacts +build_*/ +*.tar.xz +*.txz + +# DAppNode SDK output +releases/ + +# Node modules (if any) +node_modules/ + +# OS files +.DS_Store +Thumbs.db + +# IDE files +.vscode/ +.idea/ +*.swp +*.swo + +# Logs +*.log +logs/ diff --git a/dappnode/Dockerfile b/dappnode/Dockerfile new file mode 100644 index 0000000000..e5f3865f2f --- /dev/null +++ b/dappnode/Dockerfile @@ -0,0 +1,43 @@ +# DAppNode Enclave Ciphernode Dockerfile +# Uses official upstream image from GitHub Container Registry + +ARG UPSTREAM_VERSION +FROM ghcr.io/gnosisguild/ciphernode:${UPSTREAM_VERSION} AS upstream + +# Runtime image +FROM debian:stable-slim + +# Install runtime dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + procps \ + gettext-base \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Create non-root user +RUN useradd -m -u 1000 -s /bin/bash ciphernode + +# Create data directory +RUN mkdir -p /data && chown ciphernode:ciphernode /data + +# Copy binary from upstream +COPY --from=upstream /usr/local/bin/enclave /usr/local/bin/enclave +RUN chmod +x /usr/local/bin/enclave + +# Copy entrypoint and config template +COPY --chmod=755 entrypoint.sh /usr/local/bin/entrypoint.sh +COPY --chmod=644 config.template.yaml /opt/config.template.yaml + +# Labels +LABEL maintainer="Gnosis Guild " +LABEL org.opencontainers.image.source="https://github.com/gnosisguild/enclave" +LABEL org.opencontainers.image.description="Enclave Ciphernode for DAppNode" + +USER ciphernode +WORKDIR /data + +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD pgrep -f enclave || exit 1 + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/dappnode/README.md b/dappnode/README.md new file mode 100644 index 0000000000..370ba5c532 --- /dev/null +++ b/dappnode/README.md @@ -0,0 +1,240 @@ +# Enclave Ciphernode – DAppNode Package + +Run an Enclave ciphernode on DAppNode. + +This package wraps the `enclave` CLI in a DAppNode service so users can run a ciphernode with a +simple UI form (setup wizard) instead of hand-crafting configs and Docker commands. + +## Networks + +This is a **single-configuration** package: the same package can be pointed at different networks by +changing the config in the DAppNode UI. + +You choose: + +- `NETWORK` (e.g. `sepolia`, `mainnet`, `localhost`) +- The RPC URL [remote procedure call endpoint] +- Contract addresses and deploy blocks + +All of these are set in the setup wizard or in the package config after installation. + +## Files + +Package layout (from the `dappnode/` directory): + +```text +dappnode/ +├── Dockerfile # Builds the DAppNode image from the upstream ciphernode image +├── docker-compose.yml # DAppNode service definition (single variant) +├── dappnode_package.json # Package metadata (name, version, links, backup, etc.) +├── setup-wizard.yml # DAppNode UI form -> environment variables +├── entrypoint.sh # Startup script (validates env, renders config, runs enclave) +├── config.template.yaml # Enclave config template (filled via envsubst) +├── releases.json # Release metadata used by DAppNode +└── avatar-default.png # Icon shown in the DAppNode UI +``` + +All configuration is done via **environment variables**, wired through `docker-compose.yml` and +`setup-wizard.yml`. + +## Quick Start + +### For DAppNode Users + +Once this package is published to the DAppStore: + +1. Open your DAppNode UI (`http://my.dappnode`). +2. Search for **“Enclave Ciphernode”** and install the package. +3. The **setup wizard** will prompt you for: + - `RPC_URL` – WebSocket RPC endpoint (e.g. `wss://ethereum-sepolia-rpc.publicnode.com`) + - `NETWORK` – e.g. `sepolia`, `mainnet`, `localhost` + - Contract addresses + deploy blocks + - Node role (`ciphernode` or `aggregator`) + - Optional keys and peers + +4. Confirm and finish the installation. +5. Go to **Packages → enclave-ciphernode.public.dappnode.eth → Logs** to verify the node started + correctly. + +Until it’s in the public store, you can install it by IPFS hash: + +- Build it with the SDK (see “For Developers”). +- Paste the resulting `/ipfs/...` hash into the DAppNode installer UI (“Install from IPFS hash”). + +--- + +### For Developers + +You’ll typically: + +- Build the package with the DAppNode SDK. +- Install it on a DAppNode box (device or VM) from the resulting IPFS hash. +- Iterate on the entrypoint, config template, and setup wizard. + +#### 1. Build the package + +From the `dappnode/` directory: + +```bash +cd dappnode +npx @dappnode/dappnodesdk@latest build -p remote +``` + +This will: + +- Validate `docker-compose.yml`, `setup-wizard.yml`, and `dappnode_package.json` +- Build a multi-arch Docker image for `ciphernode.enclave-ciphernode.public.dappnode.eth` +- Upload the release to the DAppNode IPFS node +- Print an `/ipfs/` you can use to install the package + +#### 2. Install on your DAppNode instance + +In your browser (connected to your DAppNode): + +- Open the installer URL that the SDK prints, **or** +- Go to the DAppNode UI → Installer → “Install from IPFS hash” and paste the `/ipfs/`. + +Fill in the wizard fields, then install. + +#### 3. Debugging and iteration + +- Use the package **Logs** tab to inspect `entrypoint.sh` and `enclave` output. + +- If something is wrong in the generated config, `docker exec` into the container and inspect: + + ```bash + docker exec -it cat /data/config.yaml + ``` + +- Edit `entrypoint.sh`, `config.template.yaml`, or `setup-wizard.yml` locally, then rebuild with: + + ```bash + npx @dappnode/dappnodesdk@latest build -p remote + ``` + +- Reinstall with the new IPFS hash. + +## Configuration + +All runtime configuration is done via environment variables. They are: + +### Core + +- **`RPC_URL`** (required) WebSocket RPC endpoint for the chain (e.g. + `wss://ethereum-sepolia-rpc.publicnode.com`). + +- **`NETWORK`** Logical network name written into the Enclave config (e.g. `sepolia`, `mainnet`, + `localhost`). + +- **`NODE_ROLE`** + - `ciphernode` – participate in threshold decryption. + - `aggregator` – coordinate operations, requires a wallet key. + +- **`ETH_ADDRESS`** Optional Ethereum address to bind the node to. Leave empty to let Enclave handle + it. + +- **`QUIC_PORT`** Internal UDP port used for QUIC [Quick UDP Internet Connections] P2P networking. + Default in this package: `37173`. + +- **`LOG_LEVEL`** One of `info`, `debug`, `trace`. Mapped internally to `-v`, `-vv`, or `-vvv` when + calling `enclave start`. + +- **`EXTRA_OPTS`** Extra flags appended to the `enclave start` CLI. + +### Contracts + +Used to populate the `chains[0].contracts` section in `config.yaml`: + +- `ENCLAVE_CONTRACT` +- `CIPHERNODE_REGISTRY_CONTRACT` +- `BONDING_REGISTRY_CONTRACT` +- `ENCLAVE_DEPLOY_BLOCK` +- `CIPHERNODE_REGISTRY_DEPLOY_BLOCK` +- `BONDING_REGISTRY_DEPLOY_BLOCK` + +These are all required in the setup wizard so that the node can index chain events from the correct +block heights. + +### Secrets and keys + +- **`ENCRYPTION_PASSWORD`** Optional local encryption password. If set, `entrypoint.sh` calls: + - `enclave password set --config /data/config.yaml` + +- **`NETWORK_PRIVATE_KEY`** Optional libp2p network key. If set, `entrypoint.sh` calls: + - `enclave net set-key --config /data/config.yaml --net-keypair "$NETWORK_PRIVATE_KEY"` + +- **`PRIVATE_KEY`** Optional Ethereum private key (hex). Only needed for aggregator mode. If set, + `entrypoint.sh` calls: + - `enclave wallet set --config /data/config.yaml --private-key "$PRIVATE_KEY"` + +### Peers + +- **`PEERS`** Comma-separated list of peer multiaddresses, for example: + + ```text + /dns4/cn1/udp/37173/quic-v1,/dns4/cn2/udp/37173/quic-v1 + ``` + + The entrypoint splits this on commas, trims spaces, and turns each into a `--peer` flag: + + ```bash + enclave start ... --peer /dns4/cn1/udp/37173/quic-v1 --peer /dns4/cn2/udp/37173/quic-v1 + ``` + +If a variable is not set in the wizard, it still appears (with its default) in the package config +screen after installation, as per DAppNode’s env behavior. + +## How It Works Internally + +At container startup, `entrypoint.sh`: + +1. Validates `RPC_URL` is non-empty and starts with `ws://` or `wss://`. +2. Applies sensible defaults for `NETWORK`, `QUIC_PORT`, `NODE_ROLE`, and `LOG_LEVEL`. +3. Uses `envsubst` to render `config.template.yaml` into `/data/config.yaml`, substituting: + - node address, role, ports + - network name and RPC URL + - contract addresses and deploy blocks + +4. Optionally programs password, network key, and wallet key via the `enclave` CLI. +5. Builds CLI args, including verbosity and `--peer` flags from `PEERS`. +6. Executes: + + ```bash + enclave start --config /data/config.yaml ... + ``` + +The state and databases live under `/data` inside the container, which is backed by the +`ciphernode_data` Docker volume and listed as a backup target in `dappnode_package.json`. + +## Data & Ports + +- **Data volume**: `ciphernode_data` → `/data` This is where Enclave stores its databases and state. + +- **Ports**: + - **UDP 37173** – QUIC P2P networking (host and container). + +If you change `QUIC_PORT` in the config, you must also adjust the `ports:` mapping in +`docker-compose.yml` in a derived package. + +## Publishing + +To publish this package to the public DAppStore so others can install it: + +```bash +npx @dappnode/dappnodesdk@latest publish \ + --type= \ + --eth-provider= \ + --content-provider= \ + --developer-address= +``` + +The SDK will guide you through signing and broadcasting the on-chain transaction that registers the +new package version. + +## Links + +- [Enclave Docs](https://docs.enclave.gg) +- [DAppNode Package Development – Single Configuration](https://docs.dappnode.io/docs/dev/package-development/single-configuration/) +- [DAppNode Docker Compose Reference](https://docs.dappnode.io/docs/dev/references/docker-compose/) +- [DAppNode Setup Wizard Reference](https://docs.dappnode.io/docs/dev/references/setup-wizard/) +- [Enclave GitHub Repository](https://github.com/gnosisguild/enclave) diff --git a/dappnode/avatar-default.png b/dappnode/avatar-default.png new file mode 100644 index 0000000000..2365b0e440 Binary files /dev/null and b/dappnode/avatar-default.png differ diff --git a/dappnode/config.template.yaml b/dappnode/config.template.yaml new file mode 100644 index 0000000000..a5f5703542 --- /dev/null +++ b/dappnode/config.template.yaml @@ -0,0 +1,25 @@ +# Enclave Ciphernode Configuration +# Generated by DAppNode entrypoint + +node: + address: '${NODE_ADDRESS}' + quic_port: ${QUIC_PORT} + role: + type: ${NODE_ROLE} + autonetkey: true + autopassword: true + autowallet: true + +chains: + - name: '${NETWORK}' + rpc_url: '${RPC_URL}' + contracts: + enclave: + address: '${ENCLAVE_CONTRACT}' + deploy_block: ${ENCLAVE_DEPLOY_BLOCK} + ciphernode_registry: + address: '${CIPHERNODE_REGISTRY_CONTRACT}' + deploy_block: ${CIPHERNODE_REGISTRY_DEPLOY_BLOCK} + bonding_registry: + address: '${BONDING_REGISTRY_CONTRACT}' + deploy_block: ${BONDING_REGISTRY_DEPLOY_BLOCK} diff --git a/dappnode/dappnode_package.json b/dappnode/dappnode_package.json new file mode 100644 index 0000000000..426f623735 --- /dev/null +++ b/dappnode/dappnode_package.json @@ -0,0 +1,38 @@ +{ + "name": "enclave-ciphernode.public.dappnode.eth", + "version": "0.1.0", + "upstreamVersion": "0.1.5", + "upstreamArg": "UPSTREAM_VERSION", + "upstreamRepo": "gnosisguild/enclave", + "shortDescription": "Enclave Ciphernode - Run a node for confidential computing", + "description": "Run an Enclave ciphernode to participate in the E3 (Encrypted Execution Environments) network.", + "type": "service", + "mainService": "ciphernode", + "author": "Gnosis Guild ", + "categories": ["Blockchain", "Developer tools"], + "keywords": ["enclave", "fhe", "mpc", "ciphernode", "threshold", "encryption"], + "links": { + "homepage": "https://enclave.gg", + "docs": "https://docs.enclave.gg", + "repository": "https://github.com/gnosisguild/enclave" + }, + "repository": { + "type": "git", + "url": "https://github.com/gnosisguild/enclave.git", + "directory": "dappnode" + }, + "bugs": { + "url": "https://github.com/gnosisguild/enclave/issues" + }, + "license": "LGPL-3.0-only", + "requirements": { + "minimumDappnodeVersion": "0.2.50" + }, + "backup": [ + { + "name": "ciphernode-data", + "path": "/data", + "service": "ciphernode" + } + ] +} diff --git a/dappnode/docker-compose.yml b/dappnode/docker-compose.yml new file mode 100644 index 0000000000..50bae2a0f9 --- /dev/null +++ b/dappnode/docker-compose.yml @@ -0,0 +1,46 @@ +version: '3.5' +services: + ciphernode: + build: + context: . + args: + UPSTREAM_VERSION: 0.1.5 + image: 'ciphernode.enclave-ciphernode.public.dappnode.eth:0.1.0' + restart: unless-stopped + volumes: + - 'ciphernode_data:/data' + ports: + - 37173:37173/udp + environment: + # Required + RPC_URL: '' + # Network & node config + NETWORK: 'sepolia' + NODE_ROLE: 'ciphernode' + NODE_ADDRESS: '' + QUIC_PORT: '37173' + PEERS: '' + LOG_LEVEL: 'info' + EXTRA_OPTS: '' + # Secrets + ENCRYPTION_PASSWORD: '' + NETWORK_PRIVATE_KEY: '' + PRIVATE_KEY: '' + # Contracts (variants override these) + ENCLAVE_CONTRACT: '' + CIPHERNODE_REGISTRY_CONTRACT: '' + BONDING_REGISTRY_CONTRACT: '' + ENCLAVE_DEPLOY_BLOCK: '0' + CIPHERNODE_REGISTRY_DEPLOY_BLOCK: '0' + BONDING_REGISTRY_DEPLOY_BLOCK: '0' + security_opt: + - no-new-privileges:true + healthcheck: + test: ['CMD', 'pgrep', '-f', 'enclave'] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + +volumes: + ciphernode_data: {} diff --git a/dappnode/entrypoint.sh b/dappnode/entrypoint.sh new file mode 100644 index 0000000000..e3ecdb7568 --- /dev/null +++ b/dappnode/entrypoint.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# DAppNode Enclave Ciphernode Entrypoint +set -e + +CONFIG_DIR="/data" +CONFIG_FILE="$CONFIG_DIR/config.yaml" +TEMPLATE_FILE="/opt/config.template.yaml" + +log() { echo "[$(date '+%H:%M:%S')] $1"; } + +echo "==========================================" +echo " Enclave Ciphernode - ${NETWORK:-sepolia}" +echo "==========================================" + +# Validate RPC URL (required) +if [ -z "$RPC_URL" ]; then + log "ERROR: RPC_URL is required!" + log "Set it in the DAppNode package configuration." + exit 1 +fi + +if [[ ! "$RPC_URL" =~ ^wss?:// ]]; then + log "ERROR: RPC_URL must be a WebSocket URL (ws:// or wss://)" + exit 1 +fi + +# Set defaults +export NETWORK="${NETWORK:-sepolia}" +export QUIC_PORT="${QUIC_PORT:-37173}" +export NODE_ROLE="${NODE_ROLE:-ciphernode}" +export NODE_ADDRESS="${NODE_ADDRESS:-}" +export LOG_LEVEL="${LOG_LEVEL:-info}" + +# Contract addresses are set by package variants or environment variables +# No need for default placeholders here as variants handle network-specific values + +# Generate config from template +log "Generating configuration..." +envsubst < "$TEMPLATE_FILE" > "$CONFIG_FILE" + +# Setup secrets if provided +if [ -n "$ENCRYPTION_PASSWORD" ]; then + log "Setting encryption password..." + echo "$ENCRYPTION_PASSWORD" | enclave password set --config "$CONFIG_FILE" 2>/dev/null || true +fi + +if [ -n "$NETWORK_PRIVATE_KEY" ]; then + log "Setting network key..." + enclave net set-key --config "$CONFIG_FILE" --net-keypair "$NETWORK_PRIVATE_KEY" 2>/dev/null || true +fi + +if [ -n "$PRIVATE_KEY" ]; then + log "Setting wallet key..." + enclave wallet set --config "$CONFIG_FILE" --private-key "$PRIVATE_KEY" 2>/dev/null || true +fi + +# Build CLI args +CLI_ARGS="--config $CONFIG_FILE" + +case "$LOG_LEVEL" in + trace) CLI_ARGS="-vvv $CLI_ARGS" ;; + debug) CLI_ARGS="-vv $CLI_ARGS" ;; + info) CLI_ARGS="-v $CLI_ARGS" ;; +esac + +# Add peers if provided +if [ -n "$PEERS" ]; then + IFS=',' read -ra PEER_ARRAY <<< "$PEERS" + for peer in "${PEER_ARRAY[@]}"; do + peer=$(echo "$peer" | xargs) + [ -n "$peer" ] && CLI_ARGS="$CLI_ARGS --peer $peer" + done +fi + +[ -n "$EXTRA_OPTS" ] && CLI_ARGS="$CLI_ARGS $EXTRA_OPTS" + +# Start +log "Starting: enclave start $CLI_ARGS" +exec enclave start $CLI_ARGS \ No newline at end of file diff --git a/dappnode/releases.json b/dappnode/releases.json new file mode 100644 index 0000000000..1b6a59c96f --- /dev/null +++ b/dappnode/releases.json @@ -0,0 +1,8 @@ +{ + "0.1.0": { + "hash": "/ipfs/QmeX7jxDFcwbW7kAbs8Tgn5T4vonYxe4WmemUQsaca8xDQ", + "uploadedTo": { + "remote": "Mon, 01 Dec 2025 15:29:59 GMT" + } + } +} diff --git a/dappnode/setup-wizard.yml b/dappnode/setup-wizard.yml new file mode 100644 index 0000000000..183fb8b259 --- /dev/null +++ b/dappnode/setup-wizard.yml @@ -0,0 +1,194 @@ +# DAppNode Setup Wizard - Enclave Ciphernode +version: '2' +fields: + - id: NETWORK + target: + type: environment + name: NETWORK + service: ciphernode + title: 'Network' + description: 'Select the network to connect to.' + enum: + - sepolia + - mainnet + - localhost + required: true + + - id: RPC_URL + target: + type: environment + name: RPC_URL + service: ciphernode + title: 'RPC URL' + description: | + WebSocket RPC endpoint for the blockchain. + + Examples: + - Sepolia: wss://ethereum-sepolia-rpc.publicnode.com + - Mainnet: wss://ethereum.publicnode.com + - Localhost: ws://localhost:8545 + required: true + pattern: '^wss?://.*' + patternErrorMessage: 'Must be a WebSocket URL (wss:// or ws://)' + + - id: NODE_ADDRESS + target: + type: environment + name: NODE_ADDRESS + service: ciphernode + title: 'Node Address' + description: 'Public address for this node (must match the private key below if set).' + required: true + pattern: '^0x[a-fA-F0-9]{40}$' + patternErrorMessage: 'Must be a valid 20-byte hex address (0x...)' + + - id: PRIVATE_KEY + target: + type: environment + name: PRIVATE_KEY + service: ciphernode + title: 'Private Key' + description: 'Private key for this node. Must correspond to the Node Address above.' + secret: true + required: false + pattern: '^(0x[a-fA-F0-9]{64})?$' + + # Node settings + - id: NODE_ROLE + target: + type: environment + name: NODE_ROLE + service: ciphernode + title: 'Node Role' + default: 'ciphernode' + description: | + - **ciphernode**: Participate in threshold decryption + - **aggregator**: Coordinate operations + enum: + - ciphernode + - aggregator + required: false + + # Contracts + - id: ENCLAVE_CONTRACT + target: + type: environment + name: ENCLAVE_CONTRACT + service: ciphernode + title: 'Enclave Contract Address' + description: 'Address of the Enclave contract on the selected network.' + required: true + pattern: '^0x[a-fA-F0-9]{40}$' + patternErrorMessage: 'Must be a valid Ethereum address (0x...)' + + - id: CIPHERNODE_REGISTRY_CONTRACT + target: + type: environment + name: CIPHERNODE_REGISTRY_CONTRACT + service: ciphernode + title: 'Ciphernode Registry Contract' + description: 'Address of the CiphernodeRegistry contract.' + required: true + pattern: '^0x[a-fA-F0-9]{40}$' + patternErrorMessage: 'Must be a valid Ethereum address (0x...)' + + - id: BONDING_REGISTRY_CONTRACT + target: + type: environment + name: BONDING_REGISTRY_CONTRACT + service: ciphernode + title: 'Bonding Registry Contract' + description: 'Address of the BondingRegistry contract.' + required: true + pattern: '^0x[a-fA-F0-9]{40}$' + patternErrorMessage: 'Must be a valid Ethereum address (0x...)' + + - id: ENCLAVE_DEPLOY_BLOCK + target: + type: environment + name: ENCLAVE_DEPLOY_BLOCK + service: ciphernode + title: 'Enclave Deploy Block' + description: 'Block number where the Enclave contract was deployed.' + required: true + pattern: '^[0-9]+$' + patternErrorMessage: 'Must be a block number (integer).' + + - id: CIPHERNODE_REGISTRY_DEPLOY_BLOCK + target: + type: environment + name: CIPHERNODE_REGISTRY_DEPLOY_BLOCK + service: ciphernode + title: 'Ciphernode Registry Deploy Block' + description: 'Block number where the CiphernodeRegistry contract was deployed.' + required: true + pattern: '^[0-9]+$' + patternErrorMessage: 'Must be a block number (integer).' + + - id: BONDING_REGISTRY_DEPLOY_BLOCK + target: + type: environment + name: BONDING_REGISTRY_DEPLOY_BLOCK + service: ciphernode + title: 'Bonding Registry Deploy Block' + description: 'Block number where the BondingRegistry contract was deployed.' + required: true + pattern: '^[0-9]+$' + patternErrorMessage: 'Must be a block number (integer).' + + - id: PEERS + target: + type: environment + name: PEERS + service: ciphernode + title: 'Bootstrap Peers' + description: | + Comma-separated peer addresses, e.g.: + /dns4/cn1/udp/9091/quic-v1, /dns4/cn2/udp/9092/quic-v1 + Leave empty for auto-discovery. + required: false + + # Secrets + - id: ENCRYPTION_PASSWORD + target: + type: environment + name: ENCRYPTION_PASSWORD + service: ciphernode + title: 'Encryption Password' + description: 'Password for local encryption. Leave empty to auto-generate.' + secret: true + required: false + + - id: NETWORK_PRIVATE_KEY + target: + type: environment + name: NETWORK_PRIVATE_KEY + service: ciphernode + title: 'Network Private Key' + description: 'libp2p network key for consistent peer ID across restarts. Leave empty to auto-generate.' + secret: true + required: false + pattern: '^(0x[a-fA-F0-9]{64})?$' + + # Advanced + - id: LOG_LEVEL + target: + type: environment + name: LOG_LEVEL + service: ciphernode + title: 'Log Level' + description: 'Log level for the ciphernode.' + enum: + - info + - debug + - trace + required: false + + - id: EXTRA_OPTS + target: + type: environment + name: EXTRA_OPTS + service: ciphernode + title: 'Extra Options' + description: 'Additional CLI arguments passed to `enclave start`.' + required: false diff --git a/docs/public/site.webmanifest b/docs/public/site.webmanifest index a1553eb86b..161c642e0b 100644 --- a/docs/public/site.webmanifest +++ b/docs/public/site.webmanifest @@ -1,19 +1,19 @@ { - "name": "", - "short_name": "", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-384x384.png", - "sizes": "384x384", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-384x384.png", + "sizes": "384x384", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" } diff --git a/templates/default/client/index.html b/templates/default/client/index.html index e9dc495af7..b4ec575bc7 100644 --- a/templates/default/client/index.html +++ b/templates/default/client/index.html @@ -1,48 +1,54 @@ + + Enclave + + + + + + - - Enclave - - - - - - + + + + - - - - + + + + + + - - - - - - + + + + + + + + + + - - - - - - - - - - - - -
- -
-
- - - - \ No newline at end of file + +
+ +
+
+ + +