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
11 changes: 10 additions & 1 deletion .github/workflows/bridge-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,19 @@ jobs:
load: true
tags: linera-bridge:latest

- name: Build example Wasm for bridge tests
- name: Push bridge image to registry
if: steps.build-bridge.outcome == 'success'
run: |
BRANCH="${{ github.base_ref || github.ref_name }}"
docker push "${GCP_REGISTRY}/linera-bridge:${BRANCH}"

- name: Build example Wasm modules for bridge tests
run: cargo build --release --target wasm32-unknown-unknown -p wrapped-fungible -p evm-bridge
working-directory: examples

- name: Build linera-bridge binary for local relay tests
run: cargo build -p linera-bridge --features linera-bridge/relay

- name: Run bridge E2E test
working-directory: linera-bridge/tests/e2e
env:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -213,14 +213,14 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Compile Wasm test modules for Witty integration tests
run: |
cargo build -p linera-witty-test-modules --target wasm32-unknown-unknown
cargo build -p linera-witty-test-modules --target wasm32-unknown-unknown --locked
- name: Run all tests using the default features (except storage-service)
run: |
# TODO(#2764): Actually link this to the default features
cargo test --no-default-features --features fs,macros,wasmer,rocksdb --locked
- name: Run Witty integration tests
run: |
cargo test -p linera-witty --features wasmer,wasmtime
cargo test -p linera-witty --features wasmer,wasmtime --locked

check-outdated-cli-md:
needs: changed-files
Expand Down
1 change: 1 addition & 0 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,7 @@ Start a Local Linera Network
* `--exporter-port <EXPORTER_PORT>` — The port on which to run the block exporter

Default value: `8081`
* `--http-request-allow-list <HTTP_REQUEST_ALLOW_LIST>` — Set the list of hosts that contracts and services can send HTTP requests to



Expand Down
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions docker/Dockerfile.bridge
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,40 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

COPY --from=builder /usr/local/bin/linera-bridge /usr/local/bin/linera-bridge
COPY docker/bridge-entrypoint.sh /usr/local/bin/bridge-entrypoint.sh
RUN chmod +x /usr/local/bin/bridge-entrypoint.sh

# ── Required (no defaults — must be set at runtime) ──
# RPC_URL EVM JSON-RPC endpoint
# EVM_BRIDGE_ADDRESS FungibleBridge contract address on EVM
# LINERA_BRIDGE_APP evm-bridge Linera ApplicationId (hex)
# LINERA_FUNGIBLE_APP wrapped-fungible Linera ApplicationId (hex)
# EVM_PRIVATE_KEY EVM private key for signing transactions

# ── Conditionally required ──
# FAUCET_URL Linera faucet URL (required when wallet doesn't exist or chain ID not provided)

# ── Optional (have defaults) ──
ENV PORT=3001
ENV MONITOR_SCAN_INTERVAL=30
ENV MONITOR_START_BLOCK=0
ENV MAX_RETRIES=10
ENV BLOB_CACHE_SIZE=1000
ENV CONFIRMED_BLOCK_CACHE_SIZE=1000
ENV LITE_CERTIFICATE_CACHE_SIZE=1000
ENV CERTIFICATE_RAW_CACHE_SIZE=1000
ENV EVENT_CACHE_SIZE=1000

# ── Optional Linera client paths (clap reads these directly) ──
# LINERA_WALLET Path to wallet state file
# LINERA_KEYSTORE Path to keystore file
# LINERA_STORAGE Storage config (e.g. rocksdb:/data/client.db)

# ── Optional ──
# LINERA_BRIDGE_CHAIN_ID Linera bridge chain ID (claims new if omitted)
# LINERA_BRIDGE_CHAIN_OWNER Owner for the bridge chain (required with LINERA_BRIDGE_CHAIN_ID)

EXPOSE 3001

ENTRYPOINT ["bridge-entrypoint.sh"]
CMD ["serve"]
43 changes: 43 additions & 0 deletions docker/bridge-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/sh
set -e

# If the first argument is "serve" with no other args, build CLI from env vars.
# Otherwise, pass everything through as-is (supports "sh -c ...", direct CLI usage, etc).
if [ "$1" != "serve" ]; then
exec "$@"
fi

# Build the CLI invocation from environment variables.
shift # consume "serve"

set -- linera-bridge serve \
--rpc-url="${RPC_URL:?RPC_URL is required}" \
--evm-bridge-address="${EVM_BRIDGE_ADDRESS:?EVM_BRIDGE_ADDRESS is required}" \
--linera-bridge-address="${LINERA_BRIDGE_APP:?LINERA_BRIDGE_APP is required}" \
--linera-fungible-address="${LINERA_FUNGIBLE_APP:?LINERA_FUNGIBLE_APP is required}" \
--evm-private-key="${EVM_PRIVATE_KEY:?EVM_PRIVATE_KEY is required}" \
--port="${PORT:-3001}" \
--monitor-scan-interval="${MONITOR_SCAN_INTERVAL:-30}" \
--monitor-start-block="${MONITOR_START_BLOCK:-0}" \
--max-retries="${MAX_RETRIES:-10}" \
--blob-cache-size="${BLOB_CACHE_SIZE:-1000}" \
--confirmed-block-cache-size="${CONFIRMED_BLOCK_CACHE_SIZE:-1000}" \
--lite-certificate-cache-size="${LITE_CERTIFICATE_CACHE_SIZE:-1000}" \
--certificate-raw-cache-size="${CERTIFICATE_RAW_CACHE_SIZE:-1000}" \
--event-cache-size="${EVENT_CACHE_SIZE:-1000}"

# Optional: faucet URL (required when wallet doesn't exist or chain ID not provided)
if [ -n "$FAUCET_URL" ]; then
set -- "$@" --faucet-url="$FAUCET_URL"
fi

# Optional: bridge chain ID + owner (owner is required when chain ID is provided)
if [ -n "$LINERA_BRIDGE_CHAIN_ID" ]; then
set -- "$@" --linera-bridge-chain-id="$LINERA_BRIDGE_CHAIN_ID"
set -- "$@" --linera-bridge-chain-owner="${LINERA_BRIDGE_CHAIN_OWNER:?LINERA_BRIDGE_CHAIN_OWNER is required when LINERA_BRIDGE_CHAIN_ID is set}"
fi

# LINERA_WALLET, LINERA_KEYSTORE, LINERA_STORAGE are read directly by clap
# via `env = "..."`, so they don't need explicit --flags here.

exec "$@"
117 changes: 93 additions & 24 deletions docker/docker-compose.bridge-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ services:
# 0. Anvil - local EVM node for LightClient contract
anvil:
image: ghcr.io/foundry-rs/foundry:stable
entrypoint: ["anvil", "--host", "0.0.0.0", "--code-size-limit", "300000", "--hardfork", "shanghai"]
entrypoint: ["anvil", "--host", "0.0.0.0", "--code-size-limit", "300000", "--hardfork", "shanghai", "--slots-in-an-epoch", "1", "--block-time", "1"]
ports:
- "${ANVIL_PORT:-8545}:8545"
healthcheck:
Expand Down Expand Up @@ -70,22 +70,27 @@ services:
- "13001:13001"
- "9090:9090"
- "${RELAY_PORT:-3001}:${RELAY_PORT:-3001}"
command: >
sh -c "mkdir -p ${LINERA_NET_PATH:-/tmp/wallet} &&
./linera net
--storage scylladb:tcp:scylla:9042:table_default
up
--path ${LINERA_NET_PATH:-/tmp/wallet}
--with-faucet
--faucet-port ${FAUCET_PORT:-8080}
--with-block-exporter
--exporter-address linera-block-exporter
--exporter-port ${BLOCK_EXPORTER_PORT:-8882}"
command:
- sh
- -c
- |
mkdir -p ${LINERA_NET_PATH:-/tmp/wallet} &&
./linera net \
--storage scylladb:tcp:scylla:9042:table_default \
up \
--path ${LINERA_NET_PATH:-/tmp/wallet} \
--with-faucet \
--faucet-port ${FAUCET_PORT:-8080} \
--with-block-exporter \
--exporter-address linera-block-exporter \
--exporter-port ${BLOCK_EXPORTER_PORT:-8882} \
$${HTTP_REQUEST_ALLOW_LIST:+--http-request-allow-list $$HTTP_REQUEST_ALLOW_LIST}
environment:
- RUST_LOG=linera=info
- FAUCET_PORT=${FAUCET_PORT:-8080}
- BLOCK_EXPORTER_PORT=${BLOCK_EXPORTER_PORT:-8882}
- LINERA_NET_PATH=${LINERA_NET_PATH:-/tmp/wallet}
- HTTP_REQUEST_ALLOW_LIST=${HTTP_REQUEST_ALLOW_LIST:-anvil}
healthcheck:
test: ["CMD-SHELL", "bash -c 'cat < /dev/null > /dev/tcp/localhost/'${FAUCET_PORT:-8080}"]
interval: 5s
Expand Down Expand Up @@ -217,6 +222,7 @@ services:
image: "${LINERA_EXPORTER_IMAGE:-linera-exporter}"
ports:
- "${BLOCK_EXPORTER_PORT:-8882}:${BLOCK_EXPORTER_PORT:-8882}"
- "${EXPORTER_METRICS_PORT:-9091}:9091"
command:
- sh
- -c
Expand Down Expand Up @@ -258,36 +264,99 @@ services:
bridge-init:
condition: service_completed_successfully
healthcheck:
test: ["CMD-SHELL", "nc -z localhost ${BLOCK_EXPORTER_PORT:-8882}"]
test: ["CMD-SHELL", "wget -qO- http://localhost:9091/health || exit 1"]
interval: 5s
timeout: 3s
retries: 10
start_period: 20s
networks:
- linera-network

# 6. Relay server - claims bridge chain, processes inbox, forwards blocks to EVM
# Bridge address is resolved from /shared/bridge-address (written by setup script).
# 6a. Bridge chain init - claims a chain for the relay before the relay starts.
# Writes bridge-chain-id and relay-owner to /shared/ so the setup script
# and the relay can use them.
bridge-chain-init:
image: "${LINERA_NETWORK_IMAGE:-linera-test}"
network_mode: "service:linera-network"
command:
- sh
- -c
- |
set -e
FAUCET=http://localhost:${FAUCET_PORT:-8080}
RELAY_WALLET=/shared/relay-wallet

echo "Initializing relay wallet..."
mkdir -p $$RELAY_WALLET
LINERA_WALLET=$$RELAY_WALLET/wallet.json \
LINERA_KEYSTORE=$$RELAY_WALLET/keystore.json \
LINERA_STORAGE=rocksdb:$$RELAY_WALLET/client.db \
./linera wallet init --faucet "$$FAUCET"

echo "Claiming bridge chain..."
OUTPUT=$$(LINERA_WALLET=$$RELAY_WALLET/wallet.json \
LINERA_KEYSTORE=$$RELAY_WALLET/keystore.json \
LINERA_STORAGE=rocksdb:$$RELAY_WALLET/client.db \
./linera wallet request-chain --faucet "$$FAUCET" 2>&1)
echo "$$OUTPUT"

# Extract chain ID (64-char hex on its own line)
CHAIN_ID=$$(echo "$$OUTPUT" | grep -oE '^[a-f0-9]{64}$$' | tail -1)
# Extract owner (the AccountOwner printed by request-chain)
OWNER=$$(echo "$$OUTPUT" | grep -oE '0x[a-f0-9]{64}' | tail -1)

echo "Bridge chain: $$CHAIN_ID"
echo "Relay owner: $$OWNER"
echo "$$CHAIN_ID" > /shared/bridge-chain-id
echo "$$OWNER" > /shared/relay-owner
environment:
- FAUCET_PORT=${FAUCET_PORT:-8080}
volumes:
- bridge-shared:/shared
depends_on:
linera-network:
condition: service_healthy

# 6b. Relay server - processes inbox, forwards blocks to EVM.
# Uses the chain claimed by bridge-chain-init.
# Bridge address is resolved from /shared/bridge-address (written by setup script).
# Uses network_mode: service:linera-network so that localhost:13001 reaches the
# validator (whose address is hardcoded as "localhost" in the genesis config).
linera-relay:
image: "${LINERA_BRIDGE_IMAGE:-linera-bridge}"
network_mode: "service:linera-network"
entrypoint: ["sh", "-c"]
command:
- linera-bridge
- serve
- --rpc-url=http://anvil:8545
- --faucet-url=http://localhost:${FAUCET_PORT:-8080}
- --bridge-address-file=/shared/bridge-address
- --evm-private-key=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
- --port=${RELAY_PORT:-3001}
- |
rm -f /shared/setup-complete
echo "Waiting for setup to complete..."
while [ ! -f /shared/setup-complete ]; do sleep 1; done

export RPC_URL=http://anvil:8545
export FAUCET_URL=http://localhost:${FAUCET_PORT:-8080}
export EVM_BRIDGE_ADDRESS=$$(cat /shared/bridge-address | tr -d '[:space:]')
export LINERA_BRIDGE_APP=$$(cat /shared/bridge-app-id | tr -d '[:space:]')
export LINERA_FUNGIBLE_APP=$$(cat /shared/wrapped-app-id | tr -d '[:space:]')
export LINERA_BRIDGE_CHAIN_ID=$$(cat /shared/bridge-chain-id | tr -d '[:space:]')
export LINERA_BRIDGE_CHAIN_OWNER=$$(cat /shared/relay-owner | tr -d '[:space:]')
export EVM_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
export LINERA_WALLET=/shared/relay-wallet/wallet.json
export LINERA_KEYSTORE=/shared/relay-wallet/keystore.json
export LINERA_STORAGE=rocksdb:/shared/relay-wallet/client.db

echo "Starting relay: chain=$$LINERA_BRIDGE_CHAIN_ID bridge=$$EVM_BRIDGE_ADDRESS"
exec bridge-entrypoint.sh serve
environment:
- RUST_LOG=linera=info,linera_bridge=debug
- PORT=${RELAY_PORT:-3001}
- MONITOR_SCAN_INTERVAL=${MONITOR_SCAN_INTERVAL:-5}
- MONITOR_START_BLOCK=${MONITOR_START_BLOCK:-0}
- MAX_RETRIES=${MAX_RETRIES:-10}
volumes:
- bridge-shared:/shared
depends_on:
linera-network:
condition: service_healthy
bridge-chain-init:
condition: service_completed_successfully
anvil:
condition: service_healthy

Expand Down
3 changes: 3 additions & 0 deletions examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ getrandom = { version = "0.2.12", default-features = false, features = [
"custom",
] }
hex = "0.4.3"
insta = { version = "1.36.1", features = ["yaml"] }
linera-ethereum = { path = "../linera-ethereum" }
linera-sdk = { path = "../linera-sdk" }
log = "0.4.20"
num-bigint = "0.4.3"
Expand Down
Loading
Loading