Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ test-suite/gateway-stress/Dockerfile @zama-ai/fhevm-devs
/host-contracts/ @zama-ai/fhevm-gateway
/library-solidity/ @zama-ai/fhevm-gateway
/kms-connector/ @zama-ai/mpc-devs @dartdart26
/relayer/ @zama-ai/fhevm-gateway

# Coprocessor Team ownership
/coprocessor/ @zama-ai/fhevm-coprocessor
Expand Down
75 changes: 75 additions & 0 deletions .github/workflows/relayer-dependency-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: relayer-dependency-analysis

permissions: {}

on:
pull_request:

concurrency:
group: fhevm-relayer-deps-analysis-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
check-changes:
name: relayer-dependency-analysis/check-changes
permissions:
actions: 'read' # Required to read workflow run information
contents: 'read' # Required to checkout repository code
pull-requests: 'read' # Required to read pull request information
runs-on: ubuntu-latest
outputs:
changes-rust-files: ${{ steps.filter.outputs.rust-files }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: 'false'
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
rust-files:
- .github/workflows/relayer-dependency-analysis.yml
- relayer/**
dependencies-check:
name: relayer-dependency-analysis/dependencies-check (bpr)
needs: check-changes
if: ${{ needs.check-changes.outputs.changes-rust-files == 'true' }}
permissions:
contents: 'read' # Required to checkout repository code
checks: 'write' # Required to create GitHub checks for test results
runs-on: ubuntu-latest
steps:

- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: 'false'

- name: Setup Rust toolchain file
run: cp relayer/rust-toolchain.toml .

- name: Rust setup
uses: dsherret/rust-toolchain-file@3551321aa44dd44a0393eb3b6bdfbc5d25ecf621 # v1

- name: Install cargo-binstall
uses: cargo-bins/cargo-binstall@84ca29d5c1719e79e23b6af147555a8f4dac79d6 # v1.10.14

- name: Install cargo tools
run: |
cargo binstall --no-confirm --force \
cargo-audit@0.22.0 \
cargo-deny@0.16.2
- name: Check that Cargo.lock is the source of truth
run: |
cd relayer
cargo update -w --locked || (echo "Error: Cargo.lock is out of sync. Please run 'cargo update' locally and commit changes" && exit 1)
- name: License whitelist
run: |
cd relayer
cargo-deny deny check license --deny license-not-encountered
- name: Security issue whitelist
run: |
cd relayer
cargo-audit audit
187 changes: 187 additions & 0 deletions .github/workflows/relayer-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
name: relayer-test

# NOTE: could be parametrized with which package to test when we have a specific transaction manager different from the relayer itself.

on:
pull_request:

permissions: {}

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
check-changes:
name: relayer-test/check-changes
permissions:
actions: 'read' # Required to read workflow run information
contents: 'read' # Required to checkout repository code
pull-requests: 'read' # Required to read pull request information
runs-on: ubuntu-latest
outputs:
changes-rust-files: ${{ steps.filter.outputs.rust-files }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: 'false'
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
rust-files:
- .github/workflows/relayer-tests.yaml
- relayer/**

unit-test:
name: relayer-test/unit-test (bpr)
needs: check-changes
if: ${{ needs.check-changes.outputs.changes-rust-files == 'true' }}
permissions:
contents: 'read' # Required to checkout repository code
runs-on: ubuntu-latest
steps:
- name: Checkout Project
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
token: ${{ secrets.BLOCKCHAIN_ACTIONS_TOKEN }}
submodules: recursive

- name: Setup Rust toolchain file
run: cp relayer/rust-toolchain.toml .

- name: Rust setup
uses: dsherret/rust-toolchain-file@3551321aa44dd44a0393eb3b6bdfbc5d25ecf621 # v1

- name: Cache Rust dependencies and build artifacts
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
workspaces: relayer
key: rust-build-relayer
cache-targets: true
cache-all-crates: true

- name: Format
working-directory: relayer
run: cargo fmt --all -- --check

- name: Build for tests (no rpc mock)
working-directory: relayer
run: cargo test --no-run --lib

- name: Unit tests
working-directory: relayer
run: RUST_BACKTRACE=full make test-unit

clippy:
name: relayer-test/clippy (bpr)
needs: check-changes
if: ${{ needs.check-changes.outputs.changes-rust-files == 'true' }}
permissions:
contents: 'read' # Required to checkout repository code
runs-on: ubuntu-latest
steps:
- name: Checkout Project
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
token: ${{ secrets.BLOCKCHAIN_ACTIONS_TOKEN }}
submodules: recursive

- name: Setup Rust toolchain file
run: cp relayer/rust-toolchain.toml .

- name: Setup Rust
uses: dsherret/rust-toolchain-file@3551321aa44dd44a0393eb3b6bdfbc5d25ecf621 # v1

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
workspaces: relayer
key: rust-build-relayer

- name: Clippy
working-directory: relayer
run: cargo clippy -- -D warnings

integration-test:
name: relayer-test/integration-test (bpr)
needs: check-changes
if: ${{ needs.check-changes.outputs.changes-rust-files == 'true' }}
permissions:
contents: 'read' # Required to checkout repository code
runs-on: ubuntu-latest
services:
postgres:
image: cgr.dev/zama.ai/postgres:17.9@sha256:d42847b7c5f9ece73655164588b2387dcd5fadd618ea64761c19d3484498cef0
credentials:
username: ${{ secrets.CGR_USERNAME }}
password: ${{ secrets.CGR_PASSWORD }}
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: relayer_db
ports:
- 5432
options: >-
--health-cmd "pg_isready -U postgres -d relayer_db"
--health-interval 10s
--health-timeout 5s
--health-retries 5

env:
SQLX_OFFLINE: true # Use cached queries for compilation (speeds up CI)
APP_GLOBAL__MOCK_TEST: true
DB_USER: postgres
DB_PASSWORD: postgres
DB_NAME: relayer_db

steps:
- name: Checkout Project
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
token: ${{ secrets.BLOCKCHAIN_ACTIONS_TOKEN }}
submodules: recursive

- name: Set database URLs
env:
DB_PORT: ${{ job.services.postgres.ports['5432'] }}
run: |
{
echo "DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME}"
echo "APP_STORAGE__SQL_DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME}"
echo "TEST_DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME}"
} >> "$GITHUB_ENV"

- name: Setup Rust toolchain file
run: cp relayer/rust-toolchain.toml .

- name: Setup Rust
uses: dsherret/rust-toolchain-file@3551321aa44dd44a0393eb3b6bdfbc5d25ecf621 # v1

- name: Cache Rust dependencies and build artifacts
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
workspaces: relayer
key: rust-build-relayer
cache-targets: true
cache-all-crates: true

- name: Install SQLx CLI
run: |
if ! command -v sqlx &> /dev/null; then
echo "Installing sqlx-cli..."
cargo install sqlx-cli --no-default-features --features postgres
else
echo "sqlx-cli found in cache."
fi

- name: Migrate Database
working-directory: relayer/relayer-migrate
run: sqlx migrate run

- name: Run tests
working-directory: relayer
run: RUST_BACKTRACE=full make test-run
12 changes: 7 additions & 5 deletions gateway-contracts/tasks/utils/loadVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ import path from "path";
import { ADDRESSES_DIR, GATEWAY_ADDRESSES_ENV_FILE_NAME } from "../../hardhat.config";
import { pascalCaseToAddressEnvVar } from "../utils";

// Get the required environment variable, throw an error if it's not set
// We only check if the variable is set, not if it's empty
// Get the required environment variable, throw an error if it's not set or empty
export function getRequiredEnvVar(name: string): string {
if (!(name in process.env)) {
throw new Error(`"${name}" env variable is not set`);
}
return process.env[name]!;
const value = process.env[name]!;
if (value.trim() === "") {
throw new Error(`"${name}" env variable is set but empty`);
}
return value;
}

// Get the required address from the environment variable, throw an error if it's not set
// We only check if the variable is set, not if it's empty
// Get the required address from the environment variable, throw an error if it's not set or empty
export function getRequiredAddressEnvVar(name: string): string {
const addressEnvVarName = pascalCaseToAddressEnvVar(name);
return getRequiredEnvVar(addressEnvVarName);
Expand Down
9 changes: 6 additions & 3 deletions host-contracts/tasks/utils/loadVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import path from 'path';

import { ADDRESSES_DIR, HOST_ADDRESSES_ENV_FILE_NAME } from '../../hardhat.config';

// Get the required environment variable, throw an error if it's not set
// We only check if the variable is set, not if it's empty
// Get the required environment variable, throw an error if it's not set or empty
export function getRequiredEnvVar(name: string): string {
if (!(name in process.env)) {
throw new Error(`"${name}" env variable is not set`);
}
return process.env[name]!;
const value = process.env[name]!;
if (value.trim() === '') {
throw new Error(`"${name}" env variable is set but empty`);
}
return value;
}

// Load the addresses as environment variables from the env file
Expand Down
9 changes: 6 additions & 3 deletions library-solidity/tasks/utils/loadVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import path from 'path';
const ADDRESSES_DIR = path.resolve(__dirname, '../../fhevmTemp/addresses');
const HOST_ADDRESSES_ENV_FILE_NAME = '.env.host';

// Get the required environment variable, throw an error if it's not set
// We only check if the variable is set, not if it's empty
// Get the required environment variable, throw an error if it's not set or empty
export function getRequiredEnvVar(name: string): string {
if (!(name in process.env)) {
throw new Error(`"${name}" env variable is not set`);
}
return process.env[name]!;
const value = process.env[name]!;
if (value.trim() === '') {
throw new Error(`"${name}" env variable is set but empty`);
}
return value;
}

// Load the addresses as environment variables from the env file
Expand Down
20 changes: 10 additions & 10 deletions relayer/LOGGING_POLICY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ How we handle logging and correlation in the relayer's event-driven architecture

The relayer processes jobs through async flows: HTTP requests trigger blockchain transactions, then gateway events complete the cycle. Correlation IDs track these flows end-to-end.

**Applies to:** `apps/relayer/src/`
**Applies to:** `src/`

**Notation:**
- **For developers** - Sections for human understanding and implementation
Expand Down Expand Up @@ -309,18 +309,18 @@ subscription active) remain **INFO**. Reconnects and dropped subscriptions remai

```bash
# 1. No ERROR in non-boundaries (should find nothing)
rg 'error!\(' apps/relayer/src/core/
rg 'error!\(' apps/relayer/src/store/sql/repositories/ --glob '!cron_task.rs'
rg 'error!\(' apps/relayer/src/orchestrator/
rg 'error!\(' src/core/
rg 'error!\(' src/store/sql/repositories/ --glob '!cron_task.rs'
rg 'error!\(' src/orchestrator/

# 2. ERROR only in boundaries (should only find here)
rg 'error!\(' apps/relayer/src/http/endpoints/
rg 'error!\(' apps/relayer/src/gateway/ --glob '*_handler.rs'
rg 'error!\(' apps/relayer/src/gateway/arbitrum/listener.rs
rg 'error!\(' apps/relayer/src/store/sql/repositories/cron_task.rs
rg 'error!\(' src/http/endpoints/
rg 'error!\(' src/gateway/ --glob '*_handler.rs'
rg 'error!\(' src/gateway/arbitrum/listener.rs
rg 'error!\(' src/store/sql/repositories/cron_task.rs

# 3. No ext_job_id in gateway handlers (should find nothing)
rg 'ext_job_id' apps/relayer/src/gateway/ --glob '*_handler.rs'
rg 'ext_job_id' src/gateway/ --glob '*_handler.rs'

# 4. Structured logging - check for string interpolation in messages (code smell)
# Look for patterns like: info!("Message {}", var) or error!("Error: {}", e)
Expand All @@ -346,4 +346,4 @@ When reviewing logs:

---

**Reference:** Logging primitives in `src/logging.rs` - Database schema in `apps/relayer/migrations/`
**Reference:** Logging primitives in `src/logging.rs` - Database schema in `relayer-migrate/migrations/`
2 changes: 1 addition & 1 deletion relayer/docs/SELF_HOSTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Running your own relayer gives you permissionless, independent access to the FHE

### Step-by-step

All commands run from `apps/relayer/`.
All commands run from `relayer/`.

#### 1. Start the database

Expand Down
3 changes: 3 additions & 0 deletions relayer/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[toolchain]
channel = "1.91.1"
components = ["rustfmt", "clippy"]
Loading