Skip to content
Merged
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
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ jobs:
tool: nextest

- name: Run unit tests
run: cargo nextest run --locked --workspace
run: cargo nextest run --locked --workspace --exclude arc-test-integration

rust-integration-test:
name: Rust Integration Tests
Expand Down Expand Up @@ -129,11 +129,14 @@ jobs:
with:
tool: nextest

- name: Run node integration tests
run: cargo nextest run --locked -p arc-test-integration --test-threads 1

- name: Start integration services
run: make up

- name: Run integration tests
run: cargo nextest run --locked --workspace --features integration
run: cargo nextest run --locked --workspace --exclude arc-test-integration --features integration

# ---------------------------------------------------------------------------
# Protocol Buffers (standalone, no deps)
Expand Down
266 changes: 266 additions & 0 deletions docs/running-an-arc-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,272 @@ address and port, exposing the _protected_ RPC endpoint.

---

## RPC Provider Nodes

RPC provider nodes (relay nodes) serve public JSON-RPC traffic on behalf of the
network. They differ from follow nodes in two key ways:

1. **Direct peering** — they connect to network sentries via devp2p (EL) and
libp2p (CL), rather than using follow-mode HTTP/WebSocket endpoints.
2. **Public exposure** — they expose RPC endpoints to the public internet,
requiring a more restrictive configuration.

This section covers the configuration specific to RPC provider nodes. It assumes
you have completed the initial setup steps from [Binaries](#binaries) (paths,
directories, snapshots, consensus init). For EL ↔ CL communication options (IPC
vs RPC), see the [Binaries](#binaries) and [Separated hosts](#separated-hosts)
sections.

### RPC namespaces

Only the following namespaces may be exposed:

```text
eth,net,web3,rpc
```

All other namespaces **must not** be enabled. Specifically, the following are
prohibited: `txpool`, `debug`, `trace`, `admin`, `flashbots`, `mev`, `ots`.

```bash
--http.api eth,net,web3,rpc
--ws.api eth,net,web3,rpc
--public-api
```

The `--public-api` flag enforces these restrictions at runtime: it hides
pending-transaction RPCs and warns at startup if `--http.api` or `--ws.api`
exposes namespaces outside the safe set.

To verify, call `rpc_modules`:

```bash
curl -s -X POST http://localhost:8545 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"rpc_modules","params":[],"id":1}' | jq .result
```

Expected output (the `arc` namespace is added automatically by `--enable-arc-rpc`):

```json
{
"arc": "1.0",
"net": "1.0",
"rpc": "1.0",
"eth": "1.0",
"web3": "1.0"
}
```

If you see `txpool`, `debug`, `trace`, `admin`, or any other unexpected
namespace, the node is misconfigured.

### Arc RPC extension

The `--enable-arc-rpc` flag adds a custom `arc` JSON-RPC namespace with two
methods:

- `arc_getCertificate(height)` — returns the BFT commit certificate for a given
block height
- `arc_getVersion()` — returns build and version information

The EL proxies certificate requests to the co-located CL's REST API, defaulting
to `http://127.0.0.1:31000`. Override with `--arc-rpc-upstream-url` or the
`ARC_RPC_UPSTREAM_URL` environment variable if the CL listens on a different
address. Your load balancer must allow `arc_getCertificate` on the EL's RPC port
(8545).

`arc_getCertificate` responses are safe to cache by height at the edge — once a
block is finalized, any valid certificate for that height is valid indefinitely.
The same applies to `eth_getBlockByNumber` — finalized block payloads are
immutable and safe to cache by block number.

### Transaction propagation

Transactions must only be propagated to trusted peers:

```bash
--tx-propagation-policy Trusted
```

### Peering

Use `--trusted-peers` with the enode URLs provided during onboarding:

```bash
--trusted-peers <ENODE_URLS>
```

RPC nodes run as full nodes:

```bash
--full
```

### Prohibited configuration

Do **not** use any of the following:

| Flag / Feature | Reason |
| --------------------------------- | ----------------------------------------------------- |
| `--rpc.forwarder` | Request forwarding to upstream must not be configured |
| Bundle APIs | Bundle submission endpoints must not be exposed |
| `--http.api all` / `--ws.api all` | Exposes prohibited namespaces |

### Firewall and network

| Port | Service | Protocol | Exposure |
| ----- | ----------------------- | --------- | --------------------------------------------------------------------- |
| 8545 | EL HTTP RPC | TCP | **Public** — serves end users and permissionless nodes |
| 8546 | EL WebSocket RPC | TCP | **Public** — serves end users and permissionless nodes |
| 30303 | EL P2P (devp2p) | TCP + UDP | **Public** — allows permissionless nodes to gossip transactions |
| 27000 | CL P2P (libp2p) | TCP | **Restricted** — allow only IPs of peers provided during onboarding |
| 8551 | EL Engine API (authrpc) | TCP | **Internal only** — CL-to-EL communication, never expose externally |
| 31000 | CL RPC | TCP | **Internal only** — required for CL operation, never expose externally|

Key rules:

- RPC ports (8545, 8546) and EL P2P port (30303) are the **only** ports that
should be open to the public internet.
- CL P2P port (27000) must be reachable by network sentries but **not** by the
general public. Use IP allowlisting or the `--p2p.persistent-peers-only` flag.
- Engine API (8551) and CL RPC (31000) must **never** be exposed outside the
host. Bind to `127.0.0.1` if EL and CL are colocated, or use a private
network interface.
- If you enable metrics (`--metrics` for both EL and CL), restrict the metrics
ports accordingly.
- Deploy a reverse proxy or load balancer in front of ports 8545 and 8546 with
rate limiting, request size limits, and connection throttling. The node itself
does not enforce per-client request limits, so without an external layer,
a single client can saturate the RPC interface.

### Consensus layer configuration

The CL on an RPC node syncs decided blocks from sentry endpoints rather than
using follow mode:

```bash
--p2p.persistent-peers <SENTRY_MULTIADDRS>
```

Replace `<SENTRY_MULTIADDRS>` with the peer multiaddrs provided during onboarding.

To restrict CL P2P to only the configured persistent peers (recommended, since
RPC nodes should only communicate with sentries):

```bash
--p2p.persistent-peers-only
```

The `--rpc.addr` flag is **required** when `--enable-arc-rpc` is enabled on the
EL:

```bash
--rpc.addr=127.0.0.1:31000
```

#### Sync-only mode

By default the CL participates in the consensus protocol. To run a node that
only syncs blocks without participating in consensus (recommended for RPC
nodes):

```bash
--no-consensus
```

When set, the node only runs the synchronization protocol and does not subscribe
to consensus-related gossip topics.

### Complete example

This example uses IPC for EL ↔ CL communication (recommended when colocated).
See [Separated hosts](#separated-hosts) for the RPC alternative.

```bash
# Execution Layer (start first)
arc-node-execution node \
--chain=arc-testnet \
--datadir=$ARC_EXECUTION \
--trusted-peers <ENODE_URLS> \
--full \
--http --http.addr=0.0.0.0 --http.port=8545 \
--http.api eth,net,web3,rpc \
--ws --ws.addr=0.0.0.0 --ws.port=8546 \
--ws.api eth,net,web3,rpc \
--enable-arc-rpc \
--public-api \
--tx-propagation-policy Trusted \
--metrics 127.0.0.1:9001 \
--ipcpath=$ARC_RUN/reth.ipc \
--auth-ipc \
--auth-ipc.path=$ARC_RUN/auth.ipc

# Consensus Layer (start after EL is healthy)
arc-node-consensus start \
--home=$ARC_CONSENSUS \
--p2p.persistent-peers <SENTRY_MULTIADDRS> \
--p2p.persistent-peers-only \
--rpc.addr=127.0.0.1:31000 \
--eth-socket=$ARC_RUN/reth.ipc \
--execution-socket=$ARC_RUN/auth.ipc \
--no-consensus \
--metrics 127.0.0.1:29000
```

**Important notes:**

- Start the EL first. The CL connects to the EL on startup, so it will fail if
the EL is not running.
- The `--rpc.addr` flag on the CL is **required** because of the EL
`--enable-arc-rpc` flag.
- When using IPC, both processes must have read/write access to the socket
directory. If running in containers, mount the same directory into both.

### Checklist

**General:**

- [ ] `arc-node-consensus init` has been run
- [ ] `--http.api` and `--ws.api` set to `eth,net,web3,rpc` only
- [ ] Prohibited namespaces (`txpool`, `debug`, `trace`, `admin`, `flashbots`, `mev`, `ots`) return "Method not found"
- [ ] `--enable-arc-rpc` is set and `arc_getCertificate` is accessible on port 8545
- [ ] `--public-api` is set on the EL
- [ ] Pending-transaction RPCs are hidden — verify with:
```bash
curl -s -X POST http://localhost:8545 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_newPendingTransactionFilter","params":[],"id":1}' \
| jq .error
```
This should return an error (code `-32001`), not a filter ID.
- [ ] `--tx-propagation-policy Trusted` is set
- [ ] `--rpc.forwarder` is **not** configured
- [ ] No bundle APIs are exposed
- [ ] `--trusted-peers` is set to the enode URLs provided during onboarding
- [ ] `--full` is set on the EL
- [ ] Ports 8545, 8546 and 30303 are open to the public internet
- [ ] Port 27000 (CL P2P) is restricted to peer IPs provided during onboarding
- [ ] Engine API (8551, if using RPC) and CL RPC (31000) are not externally accessible
- [ ] CL has `--rpc.addr` set
- [ ] CL has `--no-consensus` set (recommended)

**If using IPC:**

- [ ] JWT secret is **not** configured (mutually exclusive with IPC)
- [ ] EL has `--ipcpath`, `--auth-ipc`, and `--auth-ipc.path` set
- [ ] CL has `--eth-socket` and `--execution-socket` set
- [ ] Both processes can read/write the socket directory

**If using RPC:**

- [ ] JWT secret file has been generated and is accessible to both EL and CL
- [ ] EL exposes auth RPC via `--authrpc.addr`, `--authrpc.port`, and `--authrpc.jwtsecret`
- [ ] CL connects to EL via `--eth-rpc-endpoint`, `--execution-endpoint`, and `--execution-jwt`
- [ ] `--arc-rpc-upstream-url` points to the CL's RPC address (the default `http://127.0.0.1:31000` only works when colocated)

---

## Operational Guide

### System Requirements
Expand Down
Loading