diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c17599a0d..7942977b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,6 +60,7 @@ jobs: target/release/orchestrator target/release/cdn-broker target/release/cdn-marshal + target/release/cdn-whitelist target/release/state-relay-server target/release/state-prover target/release/sequencer @@ -102,6 +103,7 @@ jobs: target/release/orchestrator target/release/cdn-broker target/release/cdn-marshal + target/release/cdn-whitelist target/release/state-relay-server target/release/state-prover target/release/sequencer @@ -121,6 +123,7 @@ jobs: sequencer-tag: ${{ steps.sequencer.outputs.tags }} cdn-broker-tag: ${{ steps.cdn-broker.outputs.tags }} cdn-marshal-tag: ${{ steps.cdn-marshal.outputs.tags }} + cdn-whitelist-tag: ${{ steps.cdn-whitelist.outputs.tags }} state-relay-server-tag: ${{ steps.state-relay-server.outputs.tags }} prover-service-tag: ${{ steps.prover-service.outputs.tags }} orchestrator-tag: ${{ steps.orchestrator.outputs.tags }} @@ -176,6 +179,12 @@ jobs: with: images: ghcr.io/espressosystems/espresso-sequencer/cdn-marshal + - name: Generate cdn-whitelist docker metadata + uses: docker/metadata-action@v5 + id: cdn-whitelist + with: + images: ghcr.io/espressosystems/espresso-sequencer/cdn-whitelist + - name: Generate state-relay-server docker metadata uses: docker/metadata-action@v5 id: state-relay-server @@ -254,6 +263,16 @@ jobs: tags: ${{ steps.cdn-marshal.outputs.tags }} labels: ${{ steps.cdn-marshal.outputs.labels }} + - name: Build and push cdn-whitelist docker + uses: docker/build-push-action@v5 + with: + context: ./ + file: ./docker/cdn-whitelist.Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.cdn-whitelist.outputs.tags }} + labels: ${{ steps.cdn-whitelist.outputs.labels }} + - name: Build and push state-relay-server docker uses: docker/build-push-action@v5 with: @@ -352,6 +371,7 @@ jobs: docker pull ${{ needs.build-dockers.outputs.sequencer-tag }} docker pull ${{ needs.build-dockers.outputs.cdn-broker-tag }} docker pull ${{ needs.build-dockers.outputs.cdn-marshal-tag }} + docker pull ${{ needs.build-dockers.outputs.cdn-whitelist-tag }} docker pull ${{ needs.build-dockers.outputs.state-relay-server-tag }} docker pull ${{ needs.build-dockers.outputs.prover-service-tag }} docker pull ${{ needs.build-dockers.outputs.orchestrator-tag }} @@ -365,6 +385,7 @@ jobs: docker tag ${{ needs.build-dockers.outputs.sequencer-tag }} ghcr.io/espressosystems/espresso-sequencer/sequencer:main docker tag ${{ needs.build-dockers.outputs.cdn-broker-tag }} ghcr.io/espressosystems/espresso-sequencer/cdn-broker:main docker tag ${{ needs.build-dockers.outputs.cdn-marshal-tag }} ghcr.io/espressosystems/espresso-sequencer/cdn-marshal:main + docker tag ${{ needs.build-dockers.outputs.cdn-whitelist-tag }} ghcr.io/espressosystems/espresso-sequencer/cdn-whitelist:main docker tag ${{ needs.build-dockers.outputs.state-relay-server-tag }} ghcr.io/espressosystems/espresso-sequencer/state-relay-server:main docker tag ${{ needs.build-dockers.outputs.prover-service-tag }} ghcr.io/espressosystems/espresso-sequencer/prover-service:main docker tag ${{ needs.build-dockers.outputs.orchestrator-tag }} ghcr.io/espressosystems/espresso-sequencer/orchestrator:main diff --git a/.github/workflows/build_static.yml b/.github/workflows/build_static.yml index b5f203200..61d6a4eff 100644 --- a/.github/workflows/build_static.yml +++ b/.github/workflows/build_static.yml @@ -78,6 +78,7 @@ jobs: ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/orchestrator ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/cdn-broker ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/cdn-marshal + ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/cdn-whitelist ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/state-relay-server ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/state-prover ${{ env.CARGO_TARGET_DIR }}/${{ env.TARGET_TRIPLET }}/release/sequencer @@ -141,6 +142,13 @@ jobs: images: ghcr.io/espressosystems/espresso-sequencer/cdn-marshal flavor: suffix=musl + - name: Generate cdn-whitelist docker metadata + uses: docker/metadata-action@v5 + id: cdn-whitelist + with: + images: ghcr.io/espressosystems/espresso-sequencer/cdn-whitelist + flavor: suffix=musl + - name: Generate state-relay-server docker metadata uses: docker/metadata-action@v5 id: state-relay-server @@ -213,6 +221,16 @@ jobs: tags: ${{ steps.cdn-marshal.outputs.tags }} labels: ${{ steps.cdn-marshal.outputs.labels }} + - name: Build and push cdn-whitelist docker + uses: docker/build-push-action@v5 + with: + context: ./ + file: ./docker/cdn-whitelist.Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.cdn-whitelist.outputs.tags }} + labels: ${{ steps.cdn-whitelist.outputs.labels }} + - name: Build and push state-relay-server docker uses: docker/build-push-action@v5 with: diff --git a/docker-compose.yaml b/docker-compose.yaml index 0ee69ffa1..2e62038c8 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -117,6 +117,23 @@ services: keydb: condition: service_healthy + # A service to whitelist specific public keys for the CDN + cdn-whitelist: + restart: no + environment: + - RUST_LOG + - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL + image: ghcr.io/espressosystems/espresso-sequencer/cdn-whitelist:main + command: + - cdn-whitelist + - -d + - redis://:changemeplease!!@keydb:6379 + depends_on: + keydb: + condition: service_healthy + orchestrator: + condition: service_healthy + state-relay-server: image: ghcr.io/espressosystems/espresso-sequencer/state-relay-server:main ports: diff --git a/docker/cdn-whitelist.Dockerfile b/docker/cdn-whitelist.Dockerfile new file mode 100644 index 000000000..551de1cbf --- /dev/null +++ b/docker/cdn-whitelist.Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:jammy + +ARG TARGETARCH + +RUN apt-get update \ + && apt-get install -y curl libcurl4 wait-for-it tini \ + && rm -rf /var/lib/apt/lists/* +ENTRYPOINT ["tini", "--"] + +COPY target/$TARGETARCH/release/cdn-whitelist /bin/cdn-whitelist +RUN chmod +x /bin/cdn-whitelist + +ENV RUST_LOG="info" + +CMD ["cdn-whitelist"] \ No newline at end of file diff --git a/process-compose.yaml b/process-compose.yaml index 451207f50..60584d538 100644 --- a/process-compose.yaml +++ b/process-compose.yaml @@ -274,7 +274,10 @@ processes: # The CDN system's main entry point; where users contact first. marshal_0: - command: cdn-marshal -d "redis://:changeme!@localhost:6379" -b $ESPRESSO_CDN_SERVER_PORT -m 127.0.0.1:9093 + command: cdn-marshal + -d "redis://:changeme!@localhost:6379" + -b $ESPRESSO_CDN_SERVER_PORT + -m 127.0.0.1:9093 depends_on: keydb: condition: process_healthy @@ -322,6 +325,16 @@ processes: path: /metrics failure_threshold: 100 + cdn-whitelist: + command: cdn-whitelist + -d redis://:changeme!@localhost:6379 + -o http://localhost:$ESPRESSO_ORCHESTRATOR_PORT + depends_on: + orchestrator: + condition: process_healthy + keydb: + condition: process_healthy + commitment-task: command: commitment-task depends_on: diff --git a/scripts/build-docker-images b/scripts/build-docker-images index c45814f36..4d15eb023 100755 --- a/scripts/build-docker-images +++ b/scripts/build-docker-images @@ -41,6 +41,7 @@ export DOCKER_BUILDKIT=1 docker build -t ghcr.io/espressosystems/espresso-sequencer/orchestrator:main -f docker/orchestrator.Dockerfile ${WORKDIR} docker build -t ghcr.io/espressosystems/espresso-sequencer/cdn-broker:main -f docker/cdn-broker.Dockerfile ${WORKDIR} docker build -t ghcr.io/espressosystems/espresso-sequencer/cdn-marshal:main -f docker/cdn-marshal.Dockerfile ${WORKDIR} +docker build -t ghcr.io/espressosystems/espresso-sequencer/cdn-whitelist:main -f docker/cdn-whitelist.Dockerfile ${WORKDIR} docker build -t ghcr.io/espressosystems/espresso-sequencer/state-relay-server:main -f docker/state-relay-server.Dockerfile ${WORKDIR} docker build -t ghcr.io/espressosystems/espresso-sequencer/prover-service:main -f docker/prover-service.Dockerfile ${WORKDIR} docker build -t ghcr.io/espressosystems/espresso-sequencer/sequencer:main -f docker/sequencer.Dockerfile ${WORKDIR} diff --git a/scripts/build-docker-images-native b/scripts/build-docker-images-native index db70cb10e..e834053db 100755 --- a/scripts/build-docker-images-native +++ b/scripts/build-docker-images-native @@ -95,6 +95,7 @@ export DOCKER_BUILDKIT=1 docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/orchestrator:main -f docker/orchestrator.Dockerfile ${WORKDIR} docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/cdn-broker:main -f docker/cdn-broker.Dockerfile ${WORKDIR} docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/cdn-marshal:main -f docker/cdn-marshal.Dockerfile ${WORKDIR} +docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/cdn-whitelist:main -f docker/cdn-whitelist.Dockerfile ${WORKDIR} docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/state-relay-server:main -f docker/state-relay-server.Dockerfile ${WORKDIR} docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/prover-service:main -f docker/prover-service.Dockerfile ${WORKDIR} docker build --platform $PLATFORM -t ghcr.io/espressosystems/espresso-sequencer/sequencer:main -f docker/sequencer.Dockerfile ${WORKDIR} diff --git a/sequencer/src/bin/cdn-whitelist.rs b/sequencer/src/bin/cdn-whitelist.rs new file mode 100644 index 000000000..537a307bd --- /dev/null +++ b/sequencer/src/bin/cdn-whitelist.rs @@ -0,0 +1,90 @@ +//! The whitelist is an adaptor that is able to update the allowed public keys for +//! all brokers. Right now, we do this by asking the orchestrator for the list of +//! allowed public keys. In the future, we will pull the stake table from the L1. + +use std::{str::FromStr, sync::Arc}; + +use anyhow::{Context, Result}; +use cdn_broker::reexports::discovery::{DiscoveryClient, Embedded, Redis}; +use clap::Parser; +use hotshot_orchestrator::{ + client::{OrchestratorClient, ValidatorArgs}, + config::NetworkConfig, +}; +use hotshot_types::traits::{node_implementation::NodeType, signature_key::SignatureKey}; +use sequencer::SeqTypes; +use surf_disco::Url; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +/// Whitelist is a service that updates the allowed public keys for the CDN. +struct Args { + /// The discovery client endpoint (including scheme) to connect to. + /// With the local discovery feature, this is a file path. + /// With the remote (redis) discovery feature, this is a redis URL (e.g. `redis://127.0.0.1:6789`). + #[arg(short, long, env = "ESPRESSO_CDN_WHITELIST_DISCOVERY_ENDPOINT")] + discovery_endpoint: String, + + /// The URL the orchestrator is running on. This should be something like `http://localhost:5555` + #[arg(short, long, env = "ESPRESSO_SEQUENCER_ORCHESTRATOR_URL")] + orchestrator_url: String, + + /// Whether or not to use the local discovery client + #[arg(short, long)] + local_discovery: bool, +} + +#[cfg_attr(async_executor_impl = "tokio", tokio::main)] +#[cfg_attr(async_executor_impl = "async-std", async_std::main)] +async fn main() -> Result<()> { + // Parse the command line arguments + let args = Args::parse(); + + // Initialize tracing + tracing_subscriber::fmt::init(); + + // Create a new `OrchestratorClient` from the supplied URL + let orchestrator_client = OrchestratorClient::new(ValidatorArgs { + url: Url::from_str(&args.orchestrator_url).with_context(|| "Invalid URL")?, + advertise_address: None, + network_config_file: None, + }); + + tracing::info!( + "Waiting for config from orchestrator on {}", + args.orchestrator_url + ); + + // Attempt to get the config from the orchestrator. + // Loops internally until the config is received. + let config: NetworkConfig< + ::SignatureKey, + ::ElectionConfigType, + > = orchestrator_client.get_config_after_collection().await; + + tracing::info!("Received config from orchestrator"); + + // Extrapolate the state_ver_keys from the config and convert them to a compatible format + let whitelist = config + .config + .known_nodes_with_stake + .iter() + .map(|k| Arc::from(k.stake_table_entry.stake_key.to_bytes())) + .collect(); + + if args.local_discovery { + ::new(args.discovery_endpoint, None) + .await? + .set_whitelist(whitelist) + .await?; + } else { + ::new(args.discovery_endpoint, None) + .await? + .set_whitelist(whitelist) + .await?; + } + + tracing::info!("Posted config to discovery endpoint"); + + Ok(()) +}