From 2b8989b67ed7adb93e9e775f25835162d5d74953 Mon Sep 17 00:00:00 2001 From: Itamar Perez Date: Thu, 22 Jan 2026 14:13:25 -0800 Subject: [PATCH 1/2] Enhance Dockerfile for Bitcoin Knots build process - Added support for dynamic source selection (official or GitHub) with corresponding download and verification steps. - Improved handling of GPG keys and checksum verification based on the selected source. - Updated extraction process to normalize directory names for consistency across different sources. - Introduced optional image metadata labels for better documentation and identification. --- .../workflows/docker-publish-knots-bip110.yml | 158 ++++++++++++++++++ docker/knots/Dockerfile | 78 +++++++-- 2 files changed, 226 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/docker-publish-knots-bip110.yml diff --git a/.github/workflows/docker-publish-knots-bip110.yml b/.github/workflows/docker-publish-knots-bip110.yml new file mode 100644 index 0000000..cd577cd --- /dev/null +++ b/.github/workflows/docker-publish-knots-bip110.yml @@ -0,0 +1,158 @@ +name: Docker Publish Knots BIP-110 + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + # Version for tarball naming and image tags + KNOTS_VERSION: "29.2.knots20251110" + # GitHub release info + GITHUB_REPO: "dathonohm/bitcoin" + GITHUB_TAG: "v29.2.knots20251110+bip110-v0.1rc2" + # Dathon Ohm's GPG key + GPG_KEYS: "2E3A66FF67F98B4F" + # Image naming + IMAGE_NAME: "knots-bip110" + IMAGE_TAG: "29.2.knots20251110-bip110-v0.1rc2" + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write # Needed to push to GHCR + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 + - linux/arm/v7 + + steps: + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v5 + with: + context: ./docker/knots + file: ./docker/knots/Dockerfile + push: ${{ github.event_name == 'push' }} + platforms: ${{ matrix.platform }} + outputs: type=image,push-by-digest=true,name=ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }},push=${{ github.event_name == 'push' }} + build-args: | + KNOTS_VERSION=${{ env.KNOTS_VERSION }} + SOURCE=github + GITHUB_REPO=${{ env.GITHUB_REPO }} + GITHUB_TAG=${{ env.GITHUB_TAG }} + GPG_KEYS=${{ env.GPG_KEYS }} + IMAGE_TITLE=Bitcoin Knots BIP-110 + IMAGE_DESCRIPTION=Bitcoin Knots with BIP-110 UASF support + IMAGE_SOURCE=https://github.com/${{ env.GITHUB_REPO }} + IMAGE_DOCUMENTATION=https://bip110.org/ + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Export digest + if: github.event_name == 'push' + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + if: github.event_name == 'push' + uses: actions/upload-artifact@v4 + with: + name: digests-bip110-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + needs: build + if: github.event_name == 'push' # Only run on push events + runs-on: ubuntu-latest + permissions: + packages: write # Needed to push to GHCR + + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-bip110-* + merge-multiple: true + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create and push manifest + working-directory: ${{ runner.temp }}/digests + run: | + # Create version-specific tag + docker buildx imagetools create -t ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} \ + $(printf 'ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + + # Create latest tag + docker buildx imagetools create -t ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest \ + $(printf 'ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) + + sign-image: + needs: merge + if: github.event_name == 'push' # Only run on push events + runs-on: ubuntu-latest + permissions: + packages: write # Needed to push signature to GHCR + id-token: write # Needed for keyless signing with Cosign/Sigstore + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Cosign + uses: sigstore/cosign-installer@v3 + + - name: Sign the multi-arch image + env: + COSIGN_EXPERIMENTAL: "true" + run: | + cosign sign --yes ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} + cosign sign --yes ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest diff --git a/docker/knots/Dockerfile b/docker/knots/Dockerfile index 1a5c96a..967b10a 100644 --- a/docker/knots/Dockerfile +++ b/docker/knots/Dockerfile @@ -2,24 +2,65 @@ ARG ALPINE_BUILDER_VERSION=3.18 FROM alpine:3.20 AS verifier +# KNOTS_VERSION: Version string for tarball naming (e.g., "29.2.knots20251010") +# SOURCE: "official" for bitcoinknots.org, "github" for GitHub releases +# GITHUB_REPO: GitHub repo for source="github" (e.g., "dathonohm/bitcoin") +# GITHUB_TAG: GitHub release tag (e.g., "v29.2.knots20251110+bip110-v0.1rc2") +# GPG_KEYS: Space-separated GPG key IDs to import (for github source) ARG KNOTS_VERSION +ARG SOURCE=official +ARG GITHUB_REPO="" +ARG GITHUB_TAG="" +ARG GPG_KEYS="" WORKDIR /tmp -RUN KNOTS_MAJOR_VERSION=$(echo ${KNOTS_VERSION} | cut -c1-2) \ - && wget https://bitcoinknots.org/files/${KNOTS_MAJOR_VERSION}.x/${KNOTS_VERSION}/SHA256SUMS \ - && wget https://bitcoinknots.org/files/${KNOTS_MAJOR_VERSION}.x/${KNOTS_VERSION}/SHA256SUMS.asc \ - && wget https://bitcoinknots.org/files/${KNOTS_MAJOR_VERSION}.x/${KNOTS_VERSION}/bitcoin-${KNOTS_VERSION}.tar.gz - RUN apk add --no-cache \ coreutils \ curl \ gnupg \ gnupg-keyboxd \ - jq \ - && curl -s https://api.github.com/repos/bitcoinknots/guix.sigs/contents/builder-keys | jq -r '.[].download_url' | while read url; do curl -s "$url" | gpg --import; done \ - && gpg --verify SHA256SUMS.asc SHA256SUMS \ - && sha256sum --ignore-missing -c SHA256SUMS + jq + +# Download and verify based on source type +RUN set -ex; \ + if [ "${SOURCE}" = "official" ]; then \ + echo "Downloading from official Bitcoin Knots..."; \ + KNOTS_MAJOR_VERSION=$(echo ${KNOTS_VERSION} | cut -c1-2); \ + wget https://bitcoinknots.org/files/${KNOTS_MAJOR_VERSION}.x/${KNOTS_VERSION}/SHA256SUMS; \ + wget https://bitcoinknots.org/files/${KNOTS_MAJOR_VERSION}.x/${KNOTS_VERSION}/SHA256SUMS.asc; \ + wget https://bitcoinknots.org/files/${KNOTS_MAJOR_VERSION}.x/${KNOTS_VERSION}/bitcoin-${KNOTS_VERSION}.tar.gz; \ + # Import all Bitcoin Knots builder keys + curl -s https://api.github.com/repos/bitcoinknots/guix.sigs/contents/builder-keys \ + | jq -r '.[].download_url' \ + | while read url; do curl -s "$url" | gpg --import; done; \ + gpg --verify SHA256SUMS.asc SHA256SUMS; \ + sha256sum --ignore-missing -c SHA256SUMS; \ + elif [ "${SOURCE}" = "github" ]; then \ + echo "Downloading from GitHub: ${GITHUB_REPO}..."; \ + # Import specified GPG keys + for key in ${GPG_KEYS}; do \ + gpg --keyserver hkps://keys.openpgp.org --recv-keys ${key} || true; \ + done; \ + # Download checksums and signature + wget https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/SHA256SUMS; \ + wget https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/SHA256SUMS.asc; \ + gpg --verify SHA256SUMS.asc SHA256SUMS; \ + # Try release tarball first, fall back to auto-archive + SOURCE_TARBALL="bitcoin-${KNOTS_VERSION}.tar.gz"; \ + RELEASE_URL="https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/${SOURCE_TARBALL}"; \ + ARCHIVE_URL="https://github.com/${GITHUB_REPO}/archive/refs/tags/${GITHUB_TAG}.tar.gz"; \ + if wget -q "${RELEASE_URL}" -O "${SOURCE_TARBALL}"; then \ + echo "Downloaded release tarball, verifying checksum..."; \ + sha256sum --ignore-missing -c SHA256SUMS; \ + else \ + echo "Release tarball not found, using GitHub archive..."; \ + wget "${ARCHIVE_URL}" -O "${SOURCE_TARBALL}"; \ + fi; \ + else \ + echo "Unknown SOURCE: ${SOURCE}"; \ + exit 1; \ + fi FROM alpine:${ALPINE_BUILDER_VERSION} AS builder @@ -47,7 +88,12 @@ RUN apk add --no-cache \ miniupnpc-dev \ zeromq-dev -RUN tar zxf bitcoin-${KNOTS_VERSION}.tar.gz +# Extract and normalize directory name (GitHub archives have different naming) +RUN tar zxf bitcoin-${KNOTS_VERSION}.tar.gz \ + && EXTRACTED_DIR=$(tar tzf bitcoin-${KNOTS_VERSION}.tar.gz | head -1 | cut -d/ -f1) \ + && if [ "${EXTRACTED_DIR}" != "bitcoin-${KNOTS_VERSION}" ]; then \ + mv "${EXTRACTED_DIR}" bitcoin-${KNOTS_VERSION}; \ + fi RUN make -C bitcoin-${KNOTS_VERSION}/depends -j$(nproc) NO_QT=1 NO_NATPMP=1 NO_UPNP=1 NO_USDT=1 @@ -82,6 +128,11 @@ FROM alpine:3.20 AS final ARG KNOTS_VERSION ARG USER=bitcoind ARG DIR=/data +# Optional labels for image metadata +ARG IMAGE_TITLE="Bitcoin Knots" +ARG IMAGE_DESCRIPTION="Bitcoin Knots full node" +ARG IMAGE_SOURCE="https://github.com/bitcoinknots/bitcoin" +ARG IMAGE_DOCUMENTATION="" COPY --from=builder /usr/local/bin/* /usr/local/bin/ COPY --from=builder /tmp/bitcoin-${KNOTS_VERSION}/test/functional/test_framework /opt/bitcoin/test/functional/test_framework @@ -122,5 +173,12 @@ EXPOSE 8332 18332 18443 38332 # ZMQ ports (for block hashes, raw blocks & raw transactions respectively) EXPOSE 8443 28332 28333 +# Image labels +LABEL org.opencontainers.image.title="${IMAGE_TITLE}" +LABEL org.opencontainers.image.description="${IMAGE_DESCRIPTION}" +LABEL org.opencontainers.image.source="${IMAGE_SOURCE}" +LABEL org.opencontainers.image.documentation="${IMAGE_DOCUMENTATION}" +LABEL org.opencontainers.image.version="${KNOTS_VERSION}" + # Set the entrypoint script ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file From defaff6fdfad1c193c74e3135dccee86c679cfe6 Mon Sep 17 00:00:00 2001 From: Itamar Perez Date: Thu, 22 Jan 2026 14:18:03 -0800 Subject: [PATCH 2/2] Refine Dockerfile for Bitcoin Knots build process - Enhanced GPG key import process with fallback options for keyservers. - Improved checksum verification by checking for the existence of SHA256SUMS before downloading. - Added logic to verify git tag signatures if GPG keys are provided, ensuring a more secure build process. - Updated error handling and messaging for clarity during the download and verification steps. --- docker/knots/Dockerfile | 53 ++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/docker/knots/Dockerfile b/docker/knots/Dockerfile index 967b10a..cabd405 100644 --- a/docker/knots/Dockerfile +++ b/docker/knots/Dockerfile @@ -38,24 +38,45 @@ RUN set -ex; \ sha256sum --ignore-missing -c SHA256SUMS; \ elif [ "${SOURCE}" = "github" ]; then \ echo "Downloading from GitHub: ${GITHUB_REPO}..."; \ - # Import specified GPG keys - for key in ${GPG_KEYS}; do \ - gpg --keyserver hkps://keys.openpgp.org --recv-keys ${key} || true; \ - done; \ - # Download checksums and signature - wget https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/SHA256SUMS; \ - wget https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/SHA256SUMS.asc; \ - gpg --verify SHA256SUMS.asc SHA256SUMS; \ - # Try release tarball first, fall back to auto-archive SOURCE_TARBALL="bitcoin-${KNOTS_VERSION}.tar.gz"; \ - RELEASE_URL="https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/${SOURCE_TARBALL}"; \ - ARCHIVE_URL="https://github.com/${GITHUB_REPO}/archive/refs/tags/${GITHUB_TAG}.tar.gz"; \ - if wget -q "${RELEASE_URL}" -O "${SOURCE_TARBALL}"; then \ - echo "Downloaded release tarball, verifying checksum..."; \ - sha256sum --ignore-missing -c SHA256SUMS; \ + # Import specified GPG keys if provided + if [ -n "${GPG_KEYS}" ]; then \ + for key in ${GPG_KEYS}; do \ + echo "Importing GPG key: ${key}"; \ + gpg --keyserver hkps://keys.openpgp.org --recv-keys ${key} || \ + gpg --keyserver keyserver.ubuntu.com --recv-keys ${key} || true; \ + done; \ + fi; \ + # Check if SHA256SUMS exists (some releases don't have them) + if wget -q --spider "https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/SHA256SUMS"; then \ + echo "Found SHA256SUMS, will verify checksums..."; \ + wget https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/SHA256SUMS; \ + if wget -q --spider "https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/SHA256SUMS.asc"; then \ + wget https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/SHA256SUMS.asc; \ + gpg --verify SHA256SUMS.asc SHA256SUMS; \ + fi; \ + # Try release tarball first + RELEASE_URL="https://github.com/${GITHUB_REPO}/releases/download/${GITHUB_TAG}/${SOURCE_TARBALL}"; \ + if wget -q "${RELEASE_URL}" -O "${SOURCE_TARBALL}"; then \ + echo "Downloaded release tarball, verifying checksum..."; \ + sha256sum --ignore-missing -c SHA256SUMS; \ + else \ + echo "Release tarball not found, using GitHub archive..."; \ + wget "https://github.com/${GITHUB_REPO}/archive/refs/tags/${GITHUB_TAG}.tar.gz" -O "${SOURCE_TARBALL}"; \ + fi; \ else \ - echo "Release tarball not found, using GitHub archive..."; \ - wget "${ARCHIVE_URL}" -O "${SOURCE_TARBALL}"; \ + echo "No SHA256SUMS found, downloading GitHub auto-generated archive..."; \ + echo "WARNING: Building from unverified source archive"; \ + wget "https://github.com/${GITHUB_REPO}/archive/refs/tags/${GITHUB_TAG}.tar.gz" -O "${SOURCE_TARBALL}"; \ + # Verify git tag signature if GPG keys were provided + if [ -n "${GPG_KEYS}" ]; then \ + echo "Attempting to verify git tag signature..."; \ + apk add --no-cache git; \ + git clone --depth 1 --branch "${GITHUB_TAG}" "https://github.com/${GITHUB_REPO}.git" repo-verify || true; \ + if [ -d "repo-verify" ]; then \ + cd repo-verify && git verify-tag "${GITHUB_TAG}" && cd .. && rm -rf repo-verify; \ + fi; \ + fi; \ fi; \ else \ echo "Unknown SOURCE: ${SOURCE}"; \