Перейти к содержанию

Работа с провайдерами в блокчейн-модуле Saga

Общая архитектура работы с провайдерами

В системе Saga реализован модуль для работы с различными блокчейн-провайдерами через ProviderManager, который является составной частью BlockchainService. ProviderManager отвечает за управление подключениями к различным блокчейн-сетям через настраиваемые RPC-провайдеры.

Структура ProviderManager

ProviderManager определен в файле backend/blockchain/service/blockchain_service.go и имеет следующую структуру:

// ProviderManager управляет блокчейн-провайдерами
type ProviderManager struct {
    repo *repository.BlockchainRepository
    clients map[string]*ethclient.Client
}

Где:

  • repo - ссылка на репозиторий блокчейн-данных
  • clients - карта клиентов Ethereum, где ключ - имя блокчейн-сети, а значение - клиент для взаимодействия с этой сетью

Хранение информации о провайдерах

Информация о блокчейн-провайдерах хранится в базе данных PostgreSQL в таблице blockchain_providers. Каждый провайдер представлен структурой BlockchainProvider в models/blockchain_models.go:

// BlockchainProvider представляет информацию о провайдере блокчейн-сети
type BlockchainProvider struct {
    ID                string    `json:"id" db:"id"`
    Name              string    `json:"name" db:"name"`
    BlockchainNetwork string    `json:"blockchain_network" db:"blockchain_network"`
    ProviderType      string    `json:"provider_type" db:"provider_type"`
    URL               string    `json:"url" db:"url"`
    APIKey            string    `json:"api_key,omitempty" db:"-"`
    Priority          int       `json:"priority" db:"priority"`
    Status            string    `json:"status" db:"status"`
    HealthCheckURL    string    `json:"health_check_url" db:"health_check_url"`
    TimeoutMS         int       `json:"timeout_ms" db:"timeout_ms"`
    HealthStatus      string    `json:"health_status" db:"health_status"`
    LastHealthCheck   time.Time `json:"last_health_check" db:"last_health_check"`
    CreatedAt         time.Time `json:"created_at" db:"created_at"`
    UpdatedAt         time.Time `json:"updated_at" db:"updated_at"`
}

Инициализация провайдеров

Инициализация подключений к провайдерам происходит в методе Initialize сервиса BlockchainService:

// Initialize инициализирует сервис блокчейна, создавая соединения с провайдерами
func (s *BlockchainService) Initialize(ctx context.Context) error {
    // Получаем все активные сети
    networks, err := s.repo.GetBlockchainNetworks(ctx)
    if err != nil {
        return fmt.Errorf("ошибка при получении списка блокчейн-сетей: %w", err)
    }

    for _, network := range networks {
        if network.Status != models.NetworkStatusActive {
            continue
        }

        // Получаем провайдеров для этой сети
        providers, err := s.repo.GetProviders(ctx, network.Name)
        if err != nil {
            return fmt.Errorf("ошибка при получении провайдеров для сети %s: %w", network.Name, err)
        }

        // Пытаемся подключиться к первому доступному провайдеру
        for _, provider := range providers {
            if provider.Status != "active" {
                continue
            }

            client, err := ethclient.Dial(provider.URL)
            if err != nil {
                continue // Пробуем следующего провайдера
            }

            // Сохраняем клиент для всех компонентов
            s.walletManager.clients[network.Name] = client
            s.txManager.clients[network.Name] = client
            s.monitorService.clients[network.Name] = client
            s.providerManager.clients[network.Name] = client
            break
        }
    }

    return nil
}

Процесс инициализации включает следующие шаги:

  1. Получение списка всех активных блокчейн-сетей
  2. Для каждой сети получение списка провайдеров (отсортированных по приоритету)
  3. Попытка подключиться к первому доступному провайдеру
  4. Сохранение клиента для использования в различных компонентах системы

Получение провайдеров из репозитория

За получение провайдеров из базы данных отвечает метод GetProviders в BlockchainRepository:

// GetProviders возвращает список всех блокчейн-провайдеров
func (r *BlockchainRepository) GetProviders(ctx context.Context, networkName string) ([]models.BlockchainProvider, error) {
    var providers []models.BlockchainProvider

    query := `
        SELECT id, name, blockchain_network, provider_type, url, priority,
               status, health_check_url, timeout_ms, health_status, last_health_check,
               created_at, updated_at
        FROM blockchain_providers
        WHERE blockchain_network = $1 AND status != 'deleted'
        ORDER BY priority ASC
    `

    err := r.db.SelectContext(ctx, &providers, query, networkName)
    if err != nil {
        return nil, fmt.Errorf("ошибка при получении списка провайдеров для сети %s: %w", networkName, err)
    }

    return providers, nil
}

Провайдеры сортируются по полю priority для обеспечения выбора наиболее предпочтительного провайдера.

Использование провайдеров в системе

ProviderManager используется для взаимодействия с блокчейн-сетями в различных компонентах системы:

1. Проверка статуса сети

// GetNetworkStatus получает статус блокчейн-сети
func (s *BlockchainService) GetNetworkStatus(ctx context.Context, blockchain string) (*models.BlockchainNetwork, error) {
    // Получаем информацию о сети из БД
    network, err := s.repo.GetBlockchainNetwork(ctx, blockchain)
    if err != nil {
        return nil, fmt.Errorf("ошибка при получении информации о сети: %w", err)
    }

    // Проверяем наличие клиента для сети
    client, ok := s.providerManager.clients[blockchain]
    if !ok {
        network.Status = models.NetworkStatusInactive
        return network, nil
    }

    // Пробуем получить номер последнего блока для проверки соединения
    _, err = client.BlockNumber(ctx)
    if err != nil {
        network.Status = models.NetworkStatusDegraded
        return network, nil
    }

    // Обновляем статус на активный
    network.Status = models.NetworkStatusActive
    return network, nil
}

2. Получение цены газа

// GetGasPrice получает текущую цену газа для указанной блокчейн-сети
func (s *BlockchainService) GetGasPrice(ctx context.Context, blockchain string) (map[string]interface{}, error) {
    // Проверяем наличие клиента для сети
    client, ok := s.providerManager.clients[blockchain]
    if !ok {
        return nil, ErrProviderNotAvailable
    }

    // Получаем цену газа из блокчейна
    gasPrice, err := client.SuggestGasPrice(ctx)
    if err != nil {
        return nil, fmt.Errorf("ошибка при получении цены газа: %w", err)
    }

    // Формируем ответ
    result := map[string]interface{}{
        "network":   blockchain,
        "gas_price": gasPrice.String(),
        "timestamp": time.Now().Unix(),
    }

    return result, nil
}

Обработка ошибок провайдеров

В системе определены специальные ошибки для обработки проблем с провайдерами:

var (
    ErrProviderNotAvailable = errors.New("провайдер недоступен")
)

Эта ошибка возвращается, когда провайдер для запрошенной сети недоступен или отсутствует.

Поддерживаемые блокчейн-сети

Система использует ЕДИНУЮ VPS блокчейн-сеть согласно архитектурному принципу VPS-FIRST:

// Единая Saga VPS production конфигурация
var sagaVPSConfig = map[string]string{
    "network_id": "1337",                                    // Saga VPS Production
    "rpc_url":    "http://188.42.218.164:8545",             // VPS нода
    "usdc_token": "0x5FbDB2315678afecb367f032d93F642f64180aa3", // USDC контракт (детерминистический)
    "name":       "Saga VPS Production",                    // Единое название сети
}

Резервирование и отказоустойчивость

Текущая реализация выбирает первый доступный провайдер для каждой сети. При недоступности этого провайдера система помечает сеть как degraded или inactive.

В будущих версиях может быть реализована более сложная стратегия с автоматическим переключением между провайдерами в случае отказа.

API для работы с провайдерами

API модуля блокчейн содержит следующие методы для работы с провайдерами:

// BlockchainGetStatus получает текущий статус блокчейн-сети
func (api *BlockchainAPI) BlockchainGetStatus(r *http.Request, args *BlockchainStatusArgs, reply *BlockchainStatusResponse) error {
    // ...
}

Заключение

Архитектура работы с провайдерами в системе Saga построена на базе модуля ProviderManager, который обеспечивает управление подключением к единой VPS блокчейн-сети. Провайдеры хранятся в базе данных и инициализируются при запуске системы.

Система использует исключительно Saga VPS Production (Network ID: 1337) согласно архитектурному принципу VPS-FIRST.