From 8db0a526e949c7e6380cc25b527317a1b0969d1d Mon Sep 17 00:00:00 2001 From: Hubert Bugaj Date: Tue, 23 Jul 2024 17:48:47 +0200 Subject: [PATCH] feat: bootstrapping tests --- .github/workflows/forest.yml | 19 ++ scripts/tests/bootstrapper/.env | 11 + scripts/tests/bootstrapper/README.md | 32 +++ scripts/tests/bootstrapper/docker-compose.yml | 201 ++++++++++++++++++ scripts/tests/bootstrapper/lotus.dockerfile | 53 +++++ scripts/tests/bootstrapper/patch-lotus.diff | 12 ++ scripts/tests/bootstrapper/setup.sh | 21 ++ 7 files changed, 349 insertions(+) create mode 100644 scripts/tests/bootstrapper/.env create mode 100644 scripts/tests/bootstrapper/README.md create mode 100644 scripts/tests/bootstrapper/docker-compose.yml create mode 100644 scripts/tests/bootstrapper/lotus.dockerfile create mode 100644 scripts/tests/bootstrapper/patch-lotus.diff create mode 100755 scripts/tests/bootstrapper/setup.sh diff --git a/.github/workflows/forest.yml b/.github/workflows/forest.yml index c4bda0139cef..90fda87e4111 100644 --- a/.github/workflows/forest.yml +++ b/.github/workflows/forest.yml @@ -412,6 +412,25 @@ jobs: - name: Dump docker logs if: always() uses: jwalton/gh-docker-logs@v2 + bootstrap-checks: + needs: + - build-ubuntu + name: Bootstrap checks + runs-on: ubuntu-latest + env: + # We use a custom Dockerfile for CI to speed up the build process. + FOREST_DOCKERFILE_OVERRIDE: scripts/devnet/forest_ci.dockerfile + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: 'forest-${{ runner.os }}' + - name: Run bootstrap tests + run: ./scripts/tests/bootstrapper/setup.sh + timeout-minutes: '${{ fromJSON(env.SCRIPT_TIMEOUT_MINUTES) }}' + - name: Dump docker logs + if: always() + uses: jwalton/gh-docker-logs@v2 snapshot-parity-checks: needs: - build-ubuntu diff --git a/scripts/tests/bootstrapper/.env b/scripts/tests/bootstrapper/.env new file mode 100644 index 000000000000..37570ef1d18a --- /dev/null +++ b/scripts/tests/bootstrapper/.env @@ -0,0 +1,11 @@ +# Note: this should be a `fat` image so that it contains the pre-downloaded filecoin proof parameters +LOTUS_VERSION=v1.28.0-rc5 +FIL_PROOFS_PARAMETER_CACHE=/var/tmp/filecoin-proof-parameters +LOTUS_RPC_PORT=1234 +FOREST_RPC_PORT=2345 +FOREST_P2P_PORT=12345 +# Pre-generated keypair for the forest node. This is required to easily connect to the forest node from the lotus node. +FOREST_PEER_KEYPAIR=7PCBrDPUebd7Pj+DqhbzNuKBWmldP9r2K5eEnbYelUoK4xd+ng8c6C9gDa/q31/U5b6FIlNnHDQLQ4WSop1y6w== +# The PeerID is derived from the `FOREST_PEER_KEYPAIR`. +FOREST_BOOTSTRAPPER_ADDRESS=/dns/forest-bootstrapper/tcp/12345/p2p/12D3KooWAYs5zbzniHaL9RnnH2RKdNvibuj3BCS4b3bHtYvC81yL +CHAIN=calibnet diff --git a/scripts/tests/bootstrapper/README.md b/scripts/tests/bootstrapper/README.md new file mode 100644 index 000000000000..463927827db4 --- /dev/null +++ b/scripts/tests/bootstrapper/README.md @@ -0,0 +1,32 @@ +# Forest as a bootstrapper test + +The setup here creates a single Forest bootstrap peer with a well-known peer id +and p2p listening port. Then, a secondary Forest and Lotus are created and +connected to that peer. The assertion succeeds if both secondary peers are able +to sync the chain from the bootstrap peer and have multiple peers in their +peerstores. + +This is illustrated in the following flowchart: + +```mermaid +flowchart TD + A[Init] -->|Download proofs and snapshot| B(Start the Forest bootstrapper) + B --> C(Start Forest-Peer) + B --> D(Start Lotus) + C -->|Wait for sync| E(Assert peer store populated) + D -->|Wait for sync| F(Assert peer store populated) + F --> G + E --> G(Finish) +``` + +## Usage + +```bash +./setup.sh +``` + +## Teardown + +```bash +docker compose down -v --rmi all +``` diff --git a/scripts/tests/bootstrapper/docker-compose.yml b/scripts/tests/bootstrapper/docker-compose.yml new file mode 100644 index 000000000000..71f496b8a03b --- /dev/null +++ b/scripts/tests/bootstrapper/docker-compose.yml @@ -0,0 +1,201 @@ +# Docker compose file to run Forest bootstrap tests. + +services: + init: + build: + context: ../../../. + dockerfile: ${FOREST_DOCKERFILE_OVERRIDE:-Dockerfile} + volumes: + - node-data:/data + - filecoin-proofs:${FIL_PROOFS_PARAMETER_CACHE} + networks: + - bootstrap-tests + environment: + - FIL_PROOFS_PARAMETER_CACHE=${FIL_PROOFS_PARAMETER_CACHE} + entrypoint: [ "/bin/bash", "-c" ] + user: 0:0 + command: + - | + set -euxo pipefail + # fetch parameter files + forest-tool fetch-params --keys + # if there are some files in the data directory, then we don't need to fetch the snapshot + if [ "$$(ls -A /data/*.car.zst)" ]; then + echo "Snapshot already fetched" + else + forest-tool snapshot fetch --chain ${CHAIN} -d /data + fi + forest-bootstrapper: + depends_on: + init: + condition: service_completed_successfully + build: + context: ../../../. + dockerfile: ${FOREST_DOCKERFILE_OVERRIDE:-Dockerfile} + volumes: + - node-data:/data + - filecoin-proofs:${FIL_PROOFS_PARAMETER_CACHE} + networks: + - bootstrap-tests + environment: + - FIL_PROOFS_PARAMETER_CACHE=${FIL_PROOFS_PARAMETER_CACHE} + - FULLNODE_API_INFO=/dns/forest/tcp/${FOREST_RPC_PORT}/http + entrypoint: [ "/bin/bash", "-c" ] + user: 0:0 + command: + - | + set -euxo pipefail + # Import the P2P keypair + mkdir -p /root/.local/share/forest/libp2p + echo ${FOREST_PEER_KEYPAIR} | base64 --decode > /root/.local/share/forest/libp2p/keypair + + forest --chain ${CHAIN} --encrypt-keystore false --no-gc \ + --p2p-listen-address /ip4/0.0.0.0/tcp/$FOREST_P2P_PORT \ + --import-snapshot $(ls /data/*.car.zst | tail -n 1) + forest-peer: + depends_on: + init: + condition: service_completed_successfully + build: + context: ../../../. + dockerfile: ${FOREST_DOCKERFILE_OVERRIDE:-Dockerfile} + volumes: + - node-data:/data + - filecoin-proofs:${FIL_PROOFS_PARAMETER_CACHE} + networks: + - bootstrap-tests + environment: + - FIL_PROOFS_PARAMETER_CACHE=${FIL_PROOFS_PARAMETER_CACHE} + entrypoint: [ "/bin/bash", "-c" ] + user: 0:0 + command: + - | + set -euxo pipefail + base64 --decode <<< ${FOREST_PEER_KEYPAIR} > keypair + PEER_ID=$(forest-tool shed peer-id-from-key-pair keypair) + + # Make sure to use the Forest bootstrapper as the only bootstrap peer + cat > config.toml < build/bootstrap/calibnet.pi + +# Apply a patch to Lotus so that local IPs are not discarded +COPY patch-lotus.diff . +RUN git apply patch-lotus.diff + +# https://github.com/Filecoin-project/filecoin-ffi?tab=readme-ov-file#building-from-source +RUN CGO_CFLAGS_ALLOW="-D__BLST_PORTABLE__" \ + CGO_CFLAGS="-D__BLST_PORTABLE__" \ + FFI_USE_BLST_PORTABLE="1" \ + FFI_USE_GPU="0" \ + make calibnet && strip lotus* + +FROM ubuntu:22.04 + +# Need to copy the relevant shared libraries from the builder image. +# See https://github.com/filecoin-project/lotus/blob/master/Dockerfile +COPY --from=lotus-builder /etc/ssl/certs /etc/ssl/certs +COPY --from=lotus-builder /lib/*/libdl.so.2 /lib/ +COPY --from=lotus-builder /lib/*/librt.so.1 /lib/ +COPY --from=lotus-builder /lib/*/libgcc_s.so.1 /lib/ +COPY --from=lotus-builder /lib/*/libutil.so.1 /lib/ +COPY --from=lotus-builder /usr/lib/*/libltdl.so.7 /lib/ +COPY --from=lotus-builder /usr/lib/*/libnuma.so.1 /lib/ +COPY --from=lotus-builder /usr/lib/*/libhwloc.so.* /lib/ +COPY --from=lotus-builder /usr/lib/*/libOpenCL.so.1 /lib/ + +# Copy only the binaries relevant for the bootstrap test +COPY --from=lotus-builder /lotus/lotus /usr/local/bin/ + +WORKDIR /lotus + +# Basic verification of dynamically linked dependencies +RUN lotus -v + +CMD ["/bin/bash"] diff --git a/scripts/tests/bootstrapper/patch-lotus.diff b/scripts/tests/bootstrapper/patch-lotus.diff new file mode 100644 index 000000000000..78990e2de09c --- /dev/null +++ b/scripts/tests/bootstrapper/patch-lotus.diff @@ -0,0 +1,12 @@ +diff --git a/node/modules/lp2p/host.go b/node/modules/lp2p/host.go +index 66256df52..3625b9a31 100644 +--- a/node/modules/lp2p/host.go ++++ b/node/modules/lp2p/host.go +@@ -93,7 +93,6 @@ func DHTRouting(mode dht.ModeOpt) interface{} { + dht.Validator(validator), + dht.ProtocolPrefix(build.DhtProtocolName(nn)), + dht.QueryFilter(dht.PublicQueryFilter), +- dht.RoutingTableFilter(dht.PublicRoutingTableFilter), + dht.DisableProviders(), + dht.DisableValues()} + d, err := dht.New( diff --git a/scripts/tests/bootstrapper/setup.sh b/scripts/tests/bootstrapper/setup.sh new file mode 100755 index 000000000000..f7d3de159bfc --- /dev/null +++ b/scripts/tests/bootstrapper/setup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# This script is used to set up clean environment for the bootstrapper tests. + +set -euxo pipefail + +# Path to the directory containing this script. +PARENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +pushd "${PARENT_PATH}" +source .env + +# This should not be needed in GH. It is useful for running locally. +docker compose down --remove-orphans +docker compose rm -f + +# Run it in the background so we can perform checks on it. +# Ideally, we could use `--wait` and `--wait-timeout` to wait for services +# to be up. However, `compose` does not distinct between services and +# init containers. See more: https://github.com/docker/compose/issues/10596 +docker compose up --build --force-recreate --detach --timestamps + +popd