Перейти к содержимому

Запуск узла OpenFiat

Операторы узлов поддерживают децентрализованный рынок, лежащий поверх блокчейна. Валидаторы Solana защищают цепочку и исполняют программы OpenFiat; узлы несут объявления, репутацию, сообщения и поиск.

Команды ниже используют эталонное развёртывание: сервис openfiat-node, /etc/openfiat для конфигурации и /var/lib/openfiat для данных.

Оборудование

Минимум

4 ядра CPU · 16 ГБ ОЗУ · SSD NVMe 250 ГБ · Стабильное широкополосное соединение

Рекомендуется для продакшена

8–16 ядер CPU · 32 ГБ ОЗУ или больше · SSD NVMe 1 ТБ · Сеть с высокой пропускной способностью и низкой задержкой · Защита питания через ИБП (UPS) · Резервное интернет-подключение

Узел — единый бинарник на Rust. libp2p (QUIC, Noise, Yamux) и RocksDB скомпилированы внутри — отдельно устанавливать нечего.

Где его запускать

AllenHark ведёт первоначальную разработку протокола и продаёт две вещи, которые нужны узлу: сервер и RPC-доступ к Solana. Операторы узлов получают 50% скидки на оба в рамках программы OpenFiat.

Хостинг серверов

Хранилище NVMe на каналах 10 Гбит/с во Франкфурте, Амстердаме и Чикаго. Рассчитывайте по оборудованию выше — хранилище это то, что полноценный узел постепенно требует, поэтому скажите, что вы запускаете, и конфигурацию подберут для вас.

Смотреть тарифы

RPC и gRPC для Solana

Соединения со стейком, чтобы транзакции, отправляемые вашим узлом, не стояли в очереди за чужими. Доступ выдаётся по списку разрешённых IP, а не по API-ключу. Yellowstone gRPC доступен для стриминга обновлений аккаунтов и слотов.

Смотреть тарифы

Как получить скидку

Никакого кода вводить не нужно. Спросите в Discord или в чате на allenhark.com, скажите, что запускаете узел OpenFiat, и скидка применяется к вашему заказу.

Служите сети, а не только себе

Узел, который только читает, — гость. Узел, до которого может достучаться публика, — это инфраструктура: кошелькам, эксплорерам и веб-приложениям нужно с *кем-то* разговаривать, а таких «кого-то» сегодня немного. Каждый оператор, который ставит узел за TLS и задаёт --public-rpc-url, расширяет этот набор, а это и есть разница между сетью с несколькими независимыми точками входа и сетью с единой точкой отказа, от которой все молча зависят. Это стоит сертификата и обратного прокси. И это вознаграждается: достижимый узел — тот, которому пиры могут бросить вызов и вознаградить, и собственные приложения OpenFiat будут использовать ваш наравне со всеми.

Две вещи, которые стоит знать заранее. Обслуживание публики означает реальный трафик, так что рассчитайте машину под это и следите за эндпоинтом метрик. И публичный узел — это публичное обязательство: люди будут строить против URL, который вы публикуете, поэтому выводите его из работы обдуманно, а не молча.

Установка

Сборка из исходников — для продакшена, сегодня

Ни одна версия ещё не помечена тегом, так что сейчас это единственный способ получить бинарник узла. Нужны тулчейн Rust и тулчейн C для RocksDB. Соберите, положите в /usr/local/bin и запустите под systemd — это то развёртывание, которое документирует эта страница, и то, что следует использовать для узла, от которого зависят другие.

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/

Готовый бинарник — как только версия будет помечена тегом

Пуш тега v* запускает workflow релиза, который собирает openfiat-node на нативных раннерах и публикует архивы linux-x86_64 и windows-x86_64 в GitHub Releases. Пока ничего не помечено тегом, поэтому эта страница сейчас пуста. Учтите, что это обычные релизные сборки, не статические и не подписанные — проверяйте то, что скачиваете, против workflow, который это произвёл.

Релизы на GitHub
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

Порты

  • 4001/UDPТрафик между пирами. QUIC — основной транспорт, поэтому этот UDP-порт должен быть достижим — тот, что чаще всего забывают открыть.Должен быть достижим
  • 7080/TCPJSON-RPC, WebSocket, REST, health и метрики — всё обслуживает один реальный порт, отдельного порта на каждую поверхность нет.Должен быть достижим
  • 7080/TCPТот же порт, что и API выше (GET /metrics). Ограничьте фаерволом до loopback/приватной сети, если не хотите, чтобы он был публичным.Держите закрытым

Вывод узла в онлайн

Выполняйте эти шаги по порядку. Каждый шаг ниже приводит нужную команду.

  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

    Сгенерируйте кошелёк узла

    Отдельного формата «идентичности узла» нет — идентичность узла это настоящий wallet.json из CLI Solana, тот же файл, что производит solana-keygen. Его seed переиспользуется и для 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 нет собственного файла конфигурации — каждая настройка это переменная окружения, читаемая один раз при запуске: где лежат данные, на каком адресе слушать, к каким пирам подключаться на старте и (по желанию) какой RPC-эндпоинт Solana использовать. Bootstrap-пиры должны быть статическим multiaddr/IP, а не именем хоста — bootstrap по 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

    Откройте нужные порты

    Пиры достигают узла по UDP, потому что QUIC — основной транспорт — это тот порт, который чаще всего забывают. Один TCP-порт обслуживает JSON-RPC, WebSocket, REST, health и метрики вместе; держите его закрытым, если не хотите обслуживать клиентов публично.

    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 впереди и получите сертификат, затем сообщите узлу его публичный URL через --public-rpc-url, и он анонсирует себя, чтобы кошельки, эксплореры и само веб-приложение OpenFiat могли им пользоваться. Порядок важен: сначала nginx по простому HTTP, затем 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 подтверждает, что процесс работает; getChainStatus по JSON-RPC говорит, GossipOnly он или RpcConnected, и его текущий blockhash, если он последний.

    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

    Он уже часть сети

    Отдельного шага «анонсировать» нет — как только у узла есть bootstrap-пиры, он распространяется через gossip и распространяется автоматически; никто его не одобряет. Стейкинг, публикация метаданных в реестре сервисов, присоединение к спорам и подача голосов управления — это отдельные действия, управляемые кошельком, которые клиент выполняет против работающего узла — смотрите соответствующее руководство по участию для каждого.

    Узел стейкает OPEN, чтобы стать активным участником. Но стейк не покупает репутацию: оператор с плохими показателями не может это компенсировать бо́льшим стейком. Эффективный приоритет сочетает репутацию, стейк и измеренную сетевую производительность.

  11. 11

    Следите за ним

    Сигналы, которые действительно предсказывают проблемы, — это подключённые пиры, режим цепочки (GossipOnly или RpcConnected) и возраст blockhash. Настройте оповещения по ним, и вы узна́ете раньше своих пользователей.

    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, а не имя хоста (bootstrap по 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.