Skip to content
Draft
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
2,963 changes: 2,741 additions & 222 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = ["src/rust"]
members = ["src/rust", "overlay"]
exclude = ["overlay-stub"]

[profile.release]
codegen-units = 1
Expand Down
41 changes: 41 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,47 @@ tracy-csvexport: $(TRACY_CSVEXPORT_BUILD)/csvexport-release
bin_PROGRAMS += tracy-csvexport
endif # USE_TRACY_CSVEXPORT

# Rust overlay build
if USE_RUST_OVERLAY
RUST_OVERLAY_DIR=$(top_srcdir)/overlay
RUST_OVERLAY_TARGET=$(top_srcdir)/target

# Determine build profile based on configure options
if ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION
RUST_BUILD_PROFILE=debug
RUST_PROFILE_FLAG=
RUST_PROFILE_DIR=debug
else
RUST_BUILD_PROFILE=release
RUST_PROFILE_FLAG=--release
RUST_PROFILE_DIR=release
endif

RUST_OVERLAY_BINARY=$(RUST_OVERLAY_TARGET)/$(RUST_PROFILE_DIR)/stellar-overlay

$(RUST_OVERLAY_BINARY): $(wildcard $(RUST_OVERLAY_DIR)/src/*.rs) $(wildcard $(RUST_OVERLAY_DIR)/src/**/*.rs) $(RUST_OVERLAY_DIR)/Cargo.toml
@echo "Building Rust overlay ($(RUST_BUILD_PROFILE))..."
cd $(RUST_OVERLAY_DIR) && $(CARGO) build $(RUST_PROFILE_FLAG)

stellar-overlay: $(RUST_OVERLAY_BINARY)
cp -v $< $@

bin_PROGRAMS += stellar-overlay

# Clean Rust build artifacts
clean-rust:
cd $(RUST_OVERLAY_DIR) && $(CARGO) clean

clean-local: clean-rust

# Run Rust tests
check-rust:
cd $(RUST_OVERLAY_DIR) && $(CARGO) test

check-local: check-rust

endif # USE_RUST_OVERLAY

EXTRA_DIST = stellar-core.supp test/testnet/multitail.conf \
test/testnet/run-test.sh README.md make-mks

Expand Down
5 changes: 5 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,11 @@ AC_ARG_ENABLE(next-protocol-version-unsafe-for-production,
AM_CONDITIONAL(ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION,
[test x$enable_next_protocol_version_unsafe_for_production = xyes])

AC_ARG_ENABLE(rust-overlay,
AS_HELP_STRING([--disable-rust-overlay],
[Disable building the Rust overlay (libp2p-based P2P networking)]))
AM_CONDITIONAL(USE_RUST_OVERLAY, [test x$enable_rust_overlay != xno])

AC_ARG_ENABLE(libunwind,
AS_HELP_STRING([--disable-libunwind],
[Disable backtraces using libunwind]))
Expand Down
2 changes: 2 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ MAINTAINER Siddharth Suresh <siddharth@stellar.org>

EXPOSE 11625
EXPOSE 11626
# QUIC port for Rust overlay (peer_port + 1000, UDP)
EXPOSE 12625/udp

VOLUME /data
VOLUME /postgresql-unix-sockets
Expand Down
3 changes: 3 additions & 0 deletions docker/Dockerfile.testing
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ RUN apt-get update && \
apt-get -y install libunwind-20 postgresql curl sqlite3 iproute2 libc++abi1-20 libc++1-20

COPY --from=buildstage /usr/local/bin/stellar-core /usr/local/bin/stellar-core
COPY --from=buildstage /usr/local/bin/stellar-overlay /usr/local/bin/stellar-overlay
EXPOSE 11625
EXPOSE 11626
# QUIC port for Rust overlay (peer_port + 1000, UDP)
EXPOSE 12625/udp
CMD stellar-core
216 changes: 216 additions & 0 deletions docs/RUST_OVERLAY_DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Rust Overlay Design

**Status**: Prototype.

The Rust overlay is a **separate process** that handles all peer-to-peer
networking for `stellar-core`. It communicates with the C++ core via a
Unix domain socket.

This document is the high-level overview. For each subsystem there is a
dedicated doc under [`docs/rust-overlay/`](rust-overlay/):

- [Transport](rust-overlay/transport.md) — QUIC, libp2p, stream framing.
- [Peer connections](rust-overlay/peer-connections.md) — connection
strategy, DNS, reconnect.
- [SCP flooding](rust-overlay/scp-flooding.md) — push-based SCP
propagation.
- [TX propagation](rust-overlay/tx-propagation.md) — pull-based
INV/GETDATA TX flooding.
- [TX-set fetching](rust-overlay/txset-fetching.md) — fetching and
caching nominated TX sets.
- [Mempool](rust-overlay/mempool.md) — fee-ordered pending-TX store.
- [Core ↔ Overlay IPC](rust-overlay/ipc.md) — Unix socket protocol.

## Why a separate process

- **Process isolation**: a panic, parse bug, or memory corruption in the
network-facing code cannot crash the consensus state machine. Restart
/ upgrade the overlay independently of Core.
- **Smaller blast radius for CVEs**: untrusted-input parsing (peer
messages, DNS, TLS handshake) lives outside the Core process.
- **Memory safety**: Rust eliminates the use-after-free / data-race
classes that have historically bitten the C++ overlay.

## Architecture

```
┌──────────────────────────────────────────────────────────────────┐
│ stellar-core (C++) │
│ │
│ ┌────────────────┐ ┌──────────────────────────────────────┐ │
│ │ HerderImpl │───▶│ RustOverlayManager │ │
│ │ (SCP logic) │ │ └─ OverlayIPC (Unix socket client) │ │
│ └────────────────┘ └──────────────────────────────────────┘ │
│ │ │
└──────────────────────────────────────│───────────────────────────┘
│ Unix domain socket IPC
│ length-prefixed binary frames
┌──────────────────────────────────────│───────────────────────────┐
│ stellar-overlay (Rust) │
│ │ │
│ ┌───────────────────────────────────▼──────────────────────────┐│
│ │ Main event loop ││
│ │ • Reads IPC messages from Core ││
│ │ • Consumes libp2p events (SCP / TxSet / peer / TX) ││
│ │ • Drives reconnect timer ││
│ └──────────┬──────────────────────────────────┬────────────────┘│
│ │ │ │
│ ┌──────────▼──────────┐ ┌────────────▼───────────────┐ │
│ │ Mempool + flood │ │ libp2p Swarm (QUIC) │ │
│ │ (integrated.rs, │ │ │ │
│ │ flood/) │ │ Behaviours: │ │
│ │ │ │ • libp2p-stream (3 protos)│ │
│ │ • Fee-ordered pool │ │ • Identify (informational)│ │
│ │ • INV batching │ │ │ │
│ │ • GETDATA tracking │ │ Transport: QUIC over UDP │ │
│ │ • TX set cache │ │ │ │
│ └─────────────────────┘ └────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ [Peer 1] [Peer 2] [Peer N] │
│ SCP stream SCP stream SCP stream │
│ TX stream TX stream TX stream │
│ TxSet stream TxSet stream TxSet str. │
└──────────────────────────────────────────────────────────────────┘
```

## Properties at a glance

- **Transport**: QUIC over UDP, via libp2p. TLS 1.3, 0-RTT reconnects,
per-stream flow control. Listen port = `peer_port + 1000`. See
[transport.md](rust-overlay/transport.md).
- **Stream independence**: SCP, TX, and TxSet each get their own libp2p
stream (`/stellar/scp/1.0.0`, `/stellar/tx/1.0.0`,
`/stellar/txset/1.0.0`). A multi-MB TxSet write cannot stall a
500-byte SCP envelope. This is the single biggest design win over the
legacy single-TCP-stream overlay.
- **Peer membership is Core-driven**. There is no peer-discovery
protocol — no Kademlia, no peer exchange, no gossip. The overlay
connects to addresses Core sends via `SetPeerConfig` and accepts any
inbound dial. Reconnects to configured peers are handled with
exponential backoff; targeted reconnect on disconnect plus a 30-second
safety-net sweep. See
[peer-connections.md](rust-overlay/peer-connections.md).
- **SCP is pushed; TX is pulled**. SCP envelopes flood immediately on
receipt. TXs use a three-phase INV/GETDATA protocol with batched
announcements and per-peer/total timeouts. See
[scp-flooding.md](rust-overlay/scp-flooding.md) and
[tx-propagation.md](rust-overlay/tx-propagation.md).
- **Mempool lives in the overlay**. Fee-ordered, capacity 100,000,
300-second max age. Core queries it for nomination via `GetTopTxs`.
See [mempool.md](rust-overlay/mempool.md).
- **Backpressure asymmetry**. SCP and TxSet events to Core are on an
unbounded channel and never drop. TX events are on a bounded channel
(10,000) and may drop under load — TXs are re-fetchable via the same
INV/GETDATA protocol, so this is acceptable. See
[ipc.md](rust-overlay/ipc.md#channel-discipline).

## What changed vs. the legacy C++ overlay

| Property | Legacy (C++) | Rust overlay |
|-----------------------------|-------------------------------------------|-------------------------------------------------------|
| Process boundary | In-process with consensus | Separate process, IPC over Unix socket |
| Transport | TCP + custom auth | QUIC (TLS 1.3 + multiplexing) via libp2p |
| Stream isolation | Single TCP connection per peer | Three logical streams per peer over one QUIC conn |
| Memory-safety class | C++ | Safe Rust |
| TX flooding | Pull-based (existing INV/GETDATA scheme) | Pull-based (INV/GETDATA), reimplemented |
| SCP flooding | Push-based | Push-based |
| Peer discovery | Built-in PEERS gossip | None — Core-driven via `SetPeerConfig` |
| Mempool location | In Core | In overlay process |

## Configuration

TOML, parsed at startup (`config.rs`):

| Field | Type | Default | Description |
|---------------------|---------------|-------------------------------|-----------------------------------------------------|
| `core_socket` | `PathBuf` | `/tmp/stellar-overlay.sock` | IPC socket path |
| `listen_addr` | `SocketAddr` | `0.0.0.0:11625` | Legacy peer-port placeholder |
| `libp2p_listen_ip` | `String` | `0.0.0.0` | QUIC bind interface |
| `peer_port` | `u16` | `11625` | Base port. QUIC listens on `peer_port + 1000` |
| `target_outbound_peers` | `usize` | `8` | **Defined but unused** |
| `max_inbound_peers` | `usize` | `64` | **Defined but unused** |
| `preferred_peers` | `Vec<SocketAddr>` | `[]` | Static preferred peers (see note below) |
| `known_peers` | `Vec<SocketAddr>` | `[]` | Static known peers (see note below) |
| `tx_push_peer_count`| `usize` | `8` | **Defined but unused** in network code |
| `max_mempool_size` | `usize` | `100_000` | **Defined but unused** — mempool is hardcoded |
| `http_addr` | `Option<SocketAddr>` | `127.0.0.1:11626` | HTTP server (TX submission, status) |
| `log_level` | `String` | `info` | `tracing` log level |

> **About `known_peers` / `preferred_peers` in the file**: peer
> membership at runtime is set by Core via the `SetPeerConfig` IPC
> message, *not* by the values in this file. The static fields here are
> kept for tooling and tests but are not the source of truth.

## Code structure

```
overlay/
├── Cargo.toml
├── src/
│ ├── main.rs # Entry point, event loop, IPC handlers,
│ │ # SetPeerConfig, reconnect, DNS retry
│ ├── lib.rs # Public module exports
│ ├── config.rs # TOML configuration parsing
│ ├── libp2p_overlay.rs # libp2p swarm, stream handling,
│ │ # SCP/TX/TxSet send paths, housekeeping
│ ├── integrated.rs # Mempool manager + high-level overlay API
│ ├── metrics.rs # Atomic-counter metrics for /metrics
│ ├── ipc/
│ │ ├── mod.rs
│ │ ├── messages.rs # IPC message types and codec
│ │ └── transport.rs # Unix-socket read/write + dispatcher
│ ├── flood/
│ │ ├── mod.rs
│ │ ├── mempool.rs # Fee-ordered TX mempool
│ │ ├── txset.rs # TX set cache + XDR builder
│ │ ├── inv_messages.rs # INV/GETDATA wire format
│ │ ├── inv_batcher.rs # Per-peer INV batching
│ │ ├── inv_tracker.rs # Peer→TX advertisement tracking
│ │ ├── pending_requests.rs # GETDATA timeout/retry
│ │ └── tx_buffer.rs # TX storage for GETDATA responses
│ └── http/
│ └── mod.rs # HTTP server (TX submission, status)
└── tests/
└── e2e_binary.rs # Binary integration tests
```

## Known issues and TODOs

These limitations live across multiple subsystems; details are in the
relevant subsystem doc.

- **Network-received TXs land in the mempool with `fee=0, num_ops=1,
account=0, sequence=0`** — XDR parsing of `TransactionEnvelope` is not
yet implemented (`main.rs:748`, `integrated.rs:144`). This breaks
fee ordering, account grouping, and the `fee_per_op` carried in
outbound INV entries for relayed TXs.
- **TX-set fetch retry is commented out** (`libp2p_overlay.rs:1829-1916`).
A pending fetch to a silent peer leaks until the peer disconnects;
Core's own retry policy is the only safety net.
- **`max_mempool_size`, `tx_push_peer_count`, `target_outbound_peers`,
and `max_inbound_peers` are unused**. The mempool is hardcoded to
100,000 entries; the overlay does not enforce inbound or outbound
connection limits.
- **TxSet cache eviction under capacity pressure is non-deterministic**
(`HashMap::keys().next()`, `txset.rs:48`). Per-ledger
`evict_before(seq-12)` keeps the cache bounded in practice.
- **`evict_expired` on the mempool is implemented but not scheduled** —
old TXs sit in the mempool until pushed out by capacity-based
eviction.
- **No DoS scoring or peer banning** at the overlay layer. A peer
flooding INVs is not throttled.
- **No survey support**. Legacy network-survey messages are not
implemented.
- **No fee overflow guard** in mempool ordering: `fee * num_ops` is
computed in `u64` and can overflow for pathological values
(`mempool.rs:64`).

## Hash functions

| Usage | Algorithm | Output bytes |
|-----------------------------|-----------|--------------|
| SCP / TX network dedup | Blake2b | 32 |
| TX content hash (mempool) | SHA-256 | 32 |
| TX set hash | SHA-256 | 32 |
1 change: 1 addition & 0 deletions docs/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ history.publish.time | timer | time to successfully pub
history.get.throughput | meter | bytes per second of history archive retrieval
history.get.failure | meter | history archive downloads failed
ledger.age.closed | bucket | time between ledgers
ledger.age.closed-histogram | histogram | time between ledgers
ledger.age.current-seconds | counter | gap between last close ledger time and current time
ledger.apply.success | counter | count of successfully applied transactions
ledger.apply.failure | counter | count of failed applied transactions
Expand Down
20 changes: 20 additions & 0 deletions docs/rust-overlay/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Rust Overlay — Subsystem Docs

This directory documents the Rust overlay process, one subsystem per
file. Start with [the high-level design doc](../RUST_OVERLAY_DESIGN.md)
for the overall architecture and rationale.

| Doc | Covers |
|-------------------------------------------|-----------------------------------------------------------------|
| [transport.md](transport.md) | QUIC over UDP, libp2p, stream protocols, frame formats |
| [peer-connections.md](peer-connections.md)| Peer membership, dialing, DNS, reconnect logic |
| [scp-flooding.md](scp-flooding.md) | Push-based SCP propagation and dedup |
| [tx-propagation.md](tx-propagation.md) | Pull-based INV/GETDATA TX flooding |
| [txset-fetching.md](txset-fetching.md) | Fetching nominated TX sets, cache lifecycle |
| [mempool.md](mempool.md) | Fee-ordered pending-TX store |
| [ipc.md](ipc.md) | Core ↔ Overlay Unix-socket protocol |

All file:line references use paths relative to the repo root (e.g.
`overlay/src/libp2p_overlay.rs:672`). When the implementation changes,
update both the relevant subsystem doc and any high-level claims in
`RUST_OVERLAY_DESIGN.md`.
Loading