跳到主要内容

运行 OpenFiat 节点

节点运营者维护位于区块链之上的去中心化市场。Solana 验证者负责保障链的安全并执行 OpenFiat 程序;节点则承载挂单、信誉、通讯与发现。

以下命令基于参考部署方案:服务名为 openfiat-node,配置位于 /etc/openfiat,数据位于 /var/lib/openfiat。

硬件要求

最低配置

4 个 CPU 核心 · 16 GB 内存 · 250 GB NVMe 固态硬盘 · 稳定的宽带连接

生产环境推荐配置

8–16 个 CPU 核心 · 32 GB 或更多内存 · 1 TB NVMe 固态硬盘 · 高带宽、低延迟网络 · UPS 电源保护 · 冗余互联网线路

节点是一个独立的 Rust 二进制文件。libp2p(QUIC、Noise、Yamux)与 RocksDB 已编译在内,无需单独安装。

在哪里运行节点

AllenHark 主导协议的早期开发,同时提供运行节点所需的两样东西:服务器与 Solana RPC 接入。作为 OpenFiat 计划的一部分,节点运营者可享两者 50% 的折扣。

服务器托管

NVMe 存储、10 Gbps 上行,位于法兰克福、阿姆斯特丹与芝加哥。请按上方硬件要求选型——存储是全节点增长最快的部分,说明你要运行的内容即可获得配置建议。

查看价格

Solana RPC 与 gRPC

提供质押连接,你的节点提交的交易不必与其他人排在同一队列中。接入通过 IP 白名单授权,而非 API 密钥。如需流式获取账户与区块槽更新,也提供 Yellowstone gRPC。

查看价格

如何获得折扣

无需输入优惠码。在 Discord 或 allenhark.com 的在线聊天中说明你正在运行 OpenFiat 节点,折扣即会应用到你的订单。

服务网络,而不只是服务自己

只做读取的节点是客人;能被公众访问的节点才是基础设施——钱包、浏览器和网页应用总要连上*某个人*,而今天这样的「某个人」实在太少。每一位把节点放到 TLS 之后并设置 --public-rpc-url 的运营者,都在扩大这个集合,这决定了网络究竟是拥有多个彼此独立的入口,还是所有人都默默依赖着同一个单点。代价不过是一张证书和一层反向代理,而且它是有回报的:可被访问的节点才能被对等节点挑战与奖励,OpenFiat 自己的应用也会和其他节点一样使用你的节点。

动手之前有两点值得了解。对公众提供服务意味着真实流量,请据此配置机器并关注 metrics 端点。另外,公开节点是一种公开承诺——人们会基于你发布的 URL 进行开发,因此若要下线,请有计划地进行,而不是悄然关停。

安装方式

预编译二进制——用于生产环境

提供适用于 x86-64 与 arm64 Linux 的静态构建。放入 /usr/local/bin,再交由 systemd 运行。这是运行一个供他人依赖的节点时受支持的方式:服务会在崩溃或重启后自动恢复,并有足够长的停机宽限期让数据库干净地落盘。

GitHub Releases 页面
shell
useradd --system --create-home --home-dir /var/lib/openfiat --shell /usr/sbin/nologin openfiat
install -m 755 openfiat-node /usr/local/bin/openfiat-node
mkdir -p /etc/openfiat
chown -R openfiat:openfiat /var/lib/openfiat

systemctl daemon-reload
systemctl enable --now openfiat-node
journalctl -u openfiat-node -f

Docker——仅用于测试

每个版本都会发布官方镜像。可用于在本地试用节点,或搭建一个用完即弃的多节点集群——但不要用它运行网络所依赖的节点。该镜像的用途是可复现的本地测试,本页所记录的参考部署方案是上面的 systemd 方式。

compose.yaml
# /opt/openfiat/compose.yaml
# TESTING ONLY. Use this to try a node locally or to bring up a throwaway
# cluster — run a production node from the binary under systemd instead
# (see the unit file further down this page).
services:
  node:
    image: ghcr.io/openfiat-org/openfiat-node:latest
    restart: unless-stopped
    # QUIC needs UDP; the same TCP port serves JSON-RPC, WebSocket, REST,
    # health, and metrics together — there is no separate metrics port.
    ports:
      - "4001:4001/udp"
      - "7080:7080/tcp"
    volumes:
      - /var/lib/openfiat:/data
      - /etc/openfiat/wallet.json:/data/wallet.json:ro
    command:
      - --ledger=/data
      - --identity=/data/wallet.json
      - --rpc-bind-address=0.0.0.0:7080
      - --gossip-bind-address=/ip4/0.0.0.0/udp/4001/quic-v1
      # Static multiaddr — DNS bootstrap does not resolve (see this
      # file's own top comment). Repeat the flag for several.
      - --entrypoint=/ip4/84.32.223.111/udp/4001/quic-v1/p2p/12D3KooWK9hQ7TwbfvFiaAxUbRFCkdhS7iEpAJDnewNL1anyREQ1
      # Omit to stay GossipOnly, the safe default. Any value opts into
      # real Solana devnet connectivity (OFS-4300 §4).
      - --solana-rpc-url=https://api.devnet.solana.com
    stop_grace_period: 60s
    ulimits:
      nofile: 65536
shell
install -d -m 0750 /var/lib/openfiat
docker compose -f /opt/openfiat/compose.yaml up -d
docker compose -f /opt/openfiat/compose.yaml logs -f node

从源码构建

需要 Rust 工具链,以及用于编译 RocksDB 的 C 工具链。适用于运行未发布的提交、为未支持的平台构建,或审计你所运行的代码。

shell
# Rust toolchain, then build the node binary
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
apt install -y build-essential pkg-config libssl-dev clang cmake

git clone https://github.com/OpenFiat-org/openfiat-core.git
cd openfiat-core
cargo build --release --bin openfiat-node

install -m 0755 target/release/openfiat-node /usr/local/bin/

端口

  • 4001/UDP点对点流量。QUIC 是主要传输方式,因此该 UDP 端口必须可达——也是最容易被遗漏的端口。必须可达
  • 7080/TCPJSON-RPC、WebSocket、REST、健康检查与指标共用同一个真实端口,并非各自独立的端口。必须可达
  • 7080/TCP与上面的 API 同一端口(GET /metrics)。如不希望公开,请仅在回环地址或内网上开放。保持内部可见

让节点上线

请按顺序完成以下步骤,每一步都附有所需的命令。

  1. 01

    准备服务器

    一台较新的 Debian 或 Ubuntu 服务器、一个防火墙,以及准确的系统时钟。时钟比看起来更重要:记录都带有时间戳与有效期,时钟漂移会让节点拒绝本来有效的数据。

    shell
    # Debian 12 / Ubuntu 24.04, as root
    apt update && apt install -y ca-certificates curl gnupg ufw chrony
    
    # Clock drift breaks signature and expiry checks — keep NTP running.
    systemctl enable --now chrony
  2. 02

    安装节点

    如果这个节点会被他人依赖,请安装二进制文件并交由 systemd 运行——本手册后续步骤均以此为前提。在本地测试或搭建用完即弃的集群时可以使用 Docker;当你需要运行未发布的提交,或想审计自己实际运行的代码时,则从源码构建。

  3. 03

    生成节点的钱包

    并不存在单独的“节点身份”格式——节点的身份就是一个真实的 Solana CLI 格式 wallet.json,与 solana-keygen 生成的文件相同。它的种子会同时用作节点的 gossip/对等身份与 Solana 签名密钥。请务必保留该文件——一旦丢失,节点将以陌生身份重新加入,信誉也要从零重新积累。

    shell
    # The node's identity IS a Solana CLI-format wallet.json — the same
    # file "solana-keygen new" produces (its own seed is reused for the
    # node's gossip/P2P keypair too, see crates/cli's load_or_generate_wallet).
    # There is no separate "node identity" format or command.
    solana-keygen new --outfile /etc/openfiat/wallet.json
    chmod 600 /etc/openfiat/wallet.json
    solana-keygen pubkey /etc/openfiat/wallet.json
  4. 04

    设置环境变量

    openfiat-node 没有自己的配置文件——每一项设置都是启动时读取一次的环境变量:数据存放位置、监听地址、启动时拨号的对等节点,以及(可选的)使用哪个 Solana RPC 端点。引导节点必须是静态的 multiaddr/IP,而不是主机名——DNS 引导地址无法解析。

    shell
    # There is no config file and no environment variables. Every
    # setting is a flag on the systemd unit's ExecStart below, so
    # "systemctl cat openfiat-node" shows exactly what a running node was
    # given, and "openfiat-node --help" is the whole surface.
    #
    # Try it in the foreground first — the node prints the addresses it is
    # reachable at and whether it reached Solana:
    
    openfiat-node \
      --ledger /var/lib/openfiat \
      --identity /etc/openfiat/wallet.json \
      --rpc-bind-address 0.0.0.0:7080 \
      --gossip-bind-address /ip4/0.0.0.0/udp/4001/quic-v1 \
      --entrypoint /ip4/84.32.223.111/udp/4001/quic-v1/p2p/12D3KooWK9hQ7TwbfvFiaAxUbRFCkdhS7iEpAJDnewNL1anyREQ1 \
      --solana-rpc-url https://api.devnet.solana.com
    
    # Omit --solana-rpc-url to stay GossipOnly, the safe default: the node
    # still serves the marketplace, but its on-chain answers come
    # second-hand from peers. Never put a real endpoint or API key anywhere
    # version controlled — it belongs in the unit file on the server.
    #
    # Add --ipfs-api-url http://127.0.0.1:5001 to pin protocol content and
    # earn the full reward share, and --retention archival to keep the whole
    # history instead of a rolling 30 days.
  5. 05

    开放必要端口

    由于 QUIC 是主要传输方式,对等节点通过 UDP 访问节点——这也是最容易被遗漏的端口。JSON-RPC、WebSocket、REST、健康检查与指标共用同一个 TCP 端口;如不希望公开对外提供服务,请将其保持私有。

    shell
    ufw default deny incoming
    ufw allow 22/tcp                 # keep your own access
    ufw allow 4001/udp               # libp2p, QUIC — the port people most often forget
    ufw allow 443/tcp                # once nginx terminates TLS in front (see below)
    ufw allow 7080/tcp               # only if the node is exposed directly, without a proxy
    ufw enable && ufw status verbose
  6. 06

    服务网络:让你的节点可被访问

    这一步会把节点从旁观网络的角色,变成真正承载网络的一环。在前面架好 nginx 并申请证书,然后用 --public-rpc-url 告诉节点它的公开地址,它就会对外通告自己,让钱包、浏览器以及 OpenFiat 自己的网页应用都能使用它。顺序很关键:先用纯 HTTP 配好 nginx,再由 certbot 添加 TLS——一份已经指向证书文件的配置根本无法启动,certbot 会在签发那份证书之前就失败。证书并非可有可无的装饰:通过 HTTPS 提供的页面无法建立纯 HTTP 连接,因此没有证书的节点,无论多健康,对所有浏览器而言都是不可见的。

    /etc/nginx/sites-available/openfiat-node
    # /etc/nginx/sites-available/openfiat-node
    #
    # HTTP only at this stage, with NO TLS directives. certbot --nginx works
    # by running "nginx -t" and editing this file, so a config that already
    # points at a certificate cannot load, nginx will not start, and certbot
    # fails before it can issue the certificate that would have fixed it.
    server {
        listen 80;
        listen [::]:80;
        server_name openfiat.example.com;
    
        location / {
            proxy_pass http://127.0.0.1:7080;
            proxy_http_version 1.1;
    
            # The node serves its WebSocket event stream on the same port;
            # without these the stream downgrades to a plain request and
            # subscriptions silently never deliver.
            proxy_set_header Upgrade    $http_upgrade;
            proxy_set_header Connection "upgrade";
    
            proxy_set_header Host              $host;
            proxy_set_header X-Real-IP         $remote_addr;
            proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    
            # Do NOT add CORS headers here — the node already sets them, and
            # two Access-Control-Allow-Origin headers on one response make
            # browsers reject it while curl sees a clean 200.
            proxy_read_timeout 300s;
        }
    }
    shell
    # The A record must already resolve for the world — certbot proves
    # control of the name over port 80. Check a public resolver, not your
    # own machine, whose cache can be stale:
    dig +short @8.8.8.8 openfiat.example.com
    
    ln -s /etc/nginx/sites-available/openfiat-node /etc/nginx/sites-enabled/
    nginx -t && systemctl reload nginx
    curl -s http://openfiat.example.com/health    # ok, before any certificate
    
    apt install -y certbot python3-certbot-nginx
    certbot --nginx -d openfiat.example.com
    
    # certbot edits the server block in place: it adds listen 443 ssl, the
    # certificate paths, and a redirect from :80. The location block and its
    # proxy headers carry over. Renewal installs itself as a systemd timer.
    # Do not hand-write a 443 block afterwards — certbot manages that one.
    
    # Then tell the network this node can be reached directly:
    #   openfiat-node --public-rpc-url https://openfiat.example.com
  7. 07

    以系统服务方式运行

    交由 systemd 管理后,节点在崩溃或重启后会自动恢复,并获得足够长的停机宽限时间,使数据库能够完整落盘,而不是在写入中途被强制终止。

    openfiat-node.service
    # /etc/systemd/system/openfiat-node.service
    [Unit]
    Description=OpenFiat node
    Documentation=https://docs.openfiat.network
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=simple
    User=openfiat
    Group=openfiat
    ExecStart=/usr/local/bin/openfiat-node \
        --ledger /var/lib/openfiat \
        --identity /etc/openfiat/wallet.json \
        --rpc-bind-address 0.0.0.0:7080 \
        --gossip-bind-address /ip4/0.0.0.0/udp/4001/quic-v1 \
        --entrypoint /ip4/84.32.223.111/udp/4001/quic-v1/p2p/12D3KooWK9hQ7TwbfvFiaAxUbRFCkdhS7iEpAJDnewNL1anyREQ1 \
        --solana-rpc-url https://api.devnet.solana.com
    WorkingDirectory=/var/lib/openfiat
    Restart=on-failure
    RestartSec=5s
    
    # openfiat-node handles SIGTERM itself (graceful shutdown) rather than
    # needing systemd to force-kill it; this gives that shutdown path a
    # reasonable window before systemd escalates to SIGKILL.
    TimeoutStopSec=30s
    
    NoNewPrivileges=true
    ProtectSystem=strict
    ProtectHome=true
    ReadWritePaths=/var/lib/openfiat
    PrivateTmp=true
    
    # AF_NETLINK is required, not optional: binding a wildcard address makes
    # libp2p enumerate this host's interfaces, and that goes over a netlink
    # socket. Without it the QUIC listener fails and the gossip actor panics
    # while the HTTP thread survives — so systemd reports the unit active
    # and the node looks healthy while serving nothing.
    RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
    
    [Install]
    WantedBy=multi-user.target
    shell
    useradd --system --create-home --home-dir /var/lib/openfiat --shell /usr/sbin/nologin openfiat
    install -m 755 openfiat-node /usr/local/bin/openfiat-node
    mkdir -p /etc/openfiat
    chown -R openfiat:openfiat /var/lib/openfiat
    
    systemctl daemon-reload
    systemctl enable --now openfiat-node
    journalctl -u openfiat-node -f
  8. 08

    让它完成追赶

    新节点不会重放全部历史,而是可以发现并导入对等节点公告的市场状态快照(OFS-1300)——这是真实的 JSON-RPC 方法,而非独立工具:getLatestSnapshot、getCheckpointHeight。签名、协议版本与状态根都必须匹配,才会被信任。

    shell
    # Snapshot sync (OFS-1300) is real JSON-RPC, not a separate CLI —
    # a new node discovers and imports a peer-announced snapshot instead of
    # replaying all history:
    curl -s -X POST http://localhost:7080/rpc -H 'content-type: application/json' \
      -d '{"jsonrpc":"2.0","id":1,"method":"getLatestSnapshot","params":{}}'
    curl -s -X POST http://localhost:7080/rpc -H 'content-type: application/json' \
      -d '{"jsonrpc":"2.0","id":1,"method":"getCheckpointHeight","params":{}}'
  9. 09

    确认运行健康

    GET /health 确认进程已启动;通过 JSON-RPC 调用 getChainStatus 可以看到它处于 GossipOnly 还是 RpcConnected,以及(若为后者)当前的区块哈希。

    shell
    # Is it up, and which mode is it in?
    curl -s http://localhost:7080/health
    # ok
    
    curl -s -X POST http://localhost:7080/rpc -H 'content-type: application/json' \
      -d '{"jsonrpc":"2.0","id":1,"method":"getChainStatus","params":{}}'
    # {"jsonrpc":"2.0","id":1,"result":{"mode":"GossipOnly","blockhash":null,"slot":null,"age_ms":null}}
    # ("RpcConnected" with a real blockhash once --solana-rpc-url is set)
    
    # The addresses your node is reachable at are in its own log — give one
    # of these to another operator as their --entrypoint:
    journalctl -u openfiat-node | grep "reachable at a new address"
  10. 10

    它已经是网络的一部分

    并不存在单独的“注册”步骤——只要节点配置了引导节点,就会自动进行 gossip 广播与被广播,无需任何人批准。质押、发布服务注册元数据、加入争议仲裁与参与治理投票,都是客户端针对运行中节点执行的独立、由钱包驱动的操作——请参阅各自对应的参与指南。

    节点需质押 OPEN 才能成为活跃参与者。但质押买不到信誉:表现不佳的运营者无法靠多质押来弥补。有效优先级由信誉、质押与实测网络表现共同决定。

  11. 11

    持续监控

    真正能预警问题的信号是:已连接对等节点数、链路模式(GossipOnly 还是 RpcConnected)与区块哈希的新鲜度。为这些配置告警,你就能比用户更早发现异常。

    prometheus.yml
    # /etc/prometheus/prometheus.yml — same port as everything else,
    # no separate metrics listener to configure on the node's own side.
    scrape_configs:
      - job_name: openfiat-node
        static_configs:
          - targets: ["127.0.0.1:7080"]
  12. 12

    保持版本更新

    停止、替换二进制文件、再启动。节点各自独立升级,网络无需协同停机,期间缺失的 gossip 事件会在启动时自动重放。

    shell
    systemctl stop openfiat-node        # SIGTERM, lets RocksDB flush
    # docker: docker compose pull node && docker compose up -d node
    install -m 0755 ./openfiat-node /usr/local/bin/
    systemctl start openfiat-node
    
    # Missed gossip events replay automatically on start.
    journalctl -u openfiat-node -f
  13. 13

    备份无法再生成的内容

    市场状态随时可以通过快照重新同步,而钱包无法再生成。余额与履约托管存放在 Solana 上,而不在你的磁盘中。

    shell
    # Back up what cannot be re-derived. Marketplace state can always be
    # re-synced from a snapshot; the wallet cannot be regenerated.
    tar czf openfiat-wallet-$(date +%F).tar.gz /etc/openfiat/wallet.json
    
    # Authoritative financial state lives on Solana, not on the node's own
    # RocksDB volume.

出现问题时

没有对等节点连接

UDP 4001 被拦截,或 --entrypoint 指向了对等节点实际无法访问的地址——它必须是静态的 multiaddr/IP,而不是主机名(DNS 引导地址无法解析)。节点开始监听后会在日志中打印它自己可被访问的地址。

shell
ss -lunp | grep 4001 && journalctl -u openfiat-node -n 100

同步始终无法完成

快照导入(OFS-1300)尚未完成,或还没有对等节点公告足够新的快照——可检查 getLatestSnapshot / getCheckpointHeight。

shell
journalctl -u openfiat-node -n 200 --no-pager | grep -i snapshot

磁盘占用持续增长

RocksDB 尚未压缩。

shell
du -sh /var/lib/openfiat/*

签名或有效期被拒绝

时钟漂移。记录带有时间戳与有效期,因此主机时钟必须准确。

shell
chronyc tracking

节点应当暴露的信息

这就是节点目前导出的全部指标——已连接对等节点数、链路模式与同步进度尚未成为 Prometheus 指标,只能通过 JSON-RPC 轮询获得(getChainStatus、getLatestSnapshot、getCheckpointHeight)。

基础设施

CPU · 内存 · 磁盘 · 网络吞吐

协议(GET /metrics)

rpc_requests_total · rpc_errors_total

自行托管用户界面

OpenFiat 将协议与界面分离。任何人都可以托管网页界面、移动网关、企业门户或区域市场,而它们访问的都是同一个市场。

无论使用哪个界面,每位参与者面对的都是同一个去中心化市场。

官方网页界面 · 社区界面 · 商户界面 · 区域市场 · 企业门户

界面连接的对象

界面并不直接与链通讯。它连接到一个或多个邻近的 OpenFiat 节点,并在某个节点不可用时自动切换到另一个。

节点 API 范围

  • 市场搜索挂单 · 创建挂单 · 更新挂单 · 下架挂单
  • 交易预订挂单 · 同步交易会话 · 提交付款确认
  • 基础设施节点发现 · 网关发现 · 快照信息 · 风险情报
  • 治理提案发现 · 提交投票 · 国库信息

所有合规节点暴露的 API 完全一致,因此针对某个节点开发的界面同样适用于其他节点。

节点从不托管用户资金。所有资金操作始终由 Solana 智能合约控制。