Разработка системы синхронизации состояния между сетями

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка системы синхронизации состояния между сетями
Сложная
от 1 недели до 3 месяцев
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1060
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка системы синхронизации состояния между сетями

Синхронизация состояния — это более широкая задача, чем bridge токенов. Речь о том, чтобы смарт-контракт на chain B «знал» о состоянии смарт-контракта на chain A и реагировал на его изменения. Примеры: governance vote на Ethereum применяется к протоколу на Arbitrum; NFT купленный на Polygon разблокирует контент на Solana; позиция в lending на Optimism используется как collateral на Base.

Это требует general purpose cross-chain messaging — передачу произвольных данных, а не просто токенов. И это требует решения проблемы finality: когда chain B может доверять состоянию, которое ему передали с chain A?

Проблема finality и её решения

Confirmation depth

Разные chain имеют разную модель finality:

Chain Тип finality Время
Ethereum Probabilistic → Absolute (LMD-GHOST + Casper) ~12 сек (slot), absolute ~12 мин
Arbitrum Soft finality от sequencer ~250 мс; hard (L1 finality) ~10 мин
Polygon PoS Checkpoint на Ethereum ~30 мин для full finality
Solana ~1.5 сек (400ms slots × ~4) ~1.5 сек
Bitcoin Probabilistic, ~6 blocks ~60 мин

Для синхронизации состояния важно: при каком уровне finality source chain вы считаете данные валидными для обновления destination state?

Оптимистичный подход: принять после soft finality sequencer'а (~секунды), но иметь challenge window. Если state окажется неверным — rollback. Модель работает для некритичных данных (score в игре, non-financial state).

Консервативный подход: ждать hard finality (L1-anchored). 10–30 минут для L2. Подходит для финансовых данных (collateral ratio, governance decisions).

ZK-верификация заголовков как trustless решение

Наиболее интересный технический подход: destination chain верифицирует ZK proof о состоянии source chain без внешних validator-ов.

Storage Proof через Herodotus

Storage proof: доказательство о значении конкретного слота в storage смарт-контракта на другой chain, верифицируемое on-chain.

Структура Ethereum storage:

State trie → Account (contract) → Storage trie → Slot value

Merkle-Patricia proof позволяет доказать: "в блоке #N на Ethereum, у контракта 0x..., в storage slot 5, значение = X". Proof верифицируется через block hash.

Herodotus предоставляет storage proofs между EVM chains:

// Интерфейс Herodotus Storage Proof Verifier
interface IStorageProofVerifier {
    function verifyStorageSlot(
        uint256 blockNumber,
        address account,
        bytes32 storageKey,
        bytes calldata proof
    ) external view returns (bytes32 value);
}

contract CrossChainStateSync {
    IStorageProofVerifier public immutable prover;
    
    // Маппинг: ethereum_block → verified_value
    mapping(uint256 => mapping(bytes32 => bytes32)) public verifiedState;
    
    // Синхронизировать значение из Ethereum governance контракта
    function syncGovernanceDecision(
        uint256 ethereumBlock,
        bytes32 proposalKey,
        bytes calldata storageProof
    ) external {
        // Верифицируем storage proof on-chain
        bytes32 value = prover.verifyStorageSlot(
            ethereumBlock,
            ETHEREUM_GOVERNANCE_CONTRACT,
            proposalKey,
            storageProof
        );
        
        // Сохраняем верифицированное состояние
        verifiedState[ethereumBlock][proposalKey] = value;
        
        // Применяем к локальной логике
        if (uint256(value) > QUORUM_THRESHOLD) {
            _executeGovernanceDecision(proposalKey, value);
        }
    }
}

Недостаток: proof generation — off-chain задача (Herodotus API или собственный prover). On-chain верификация proof стоит ~200k–500k gas. Для частых обновлений — дорого.

Succinct Labs: ZK Light Client

Telepathy (Succinct) — ZK light client для Ethereum Beacon Chain на других chain. Верифицирует Ethereum блок заголовки через BLS signature aggregation proof.

// Telepathy Light Client: получаем верифицированный Ethereum state root
interface ITelepathy {
    function consistent(uint64 slot) external view returns (bool);
    function headers(uint64 slot) external view returns (bytes32 headerRoot);
    function executionStateRoots(uint64 slot) external view returns (bytes32 stateRoot);
}

contract TrustyStateSync {
    ITelepathy public telepathy;
    
    function getVerifiedEthereumState(
        uint64 slot,
        address contractAddress,
        uint256 storageSlot,
        bytes calldata accountProof,
        bytes calldata storageProof
    ) external view returns (bytes32 value) {
        require(telepathy.consistent(slot), "Slot not finalized");
        
        bytes32 stateRoot = telepathy.executionStateRoots(slot);
        
        // Верифицируем account proof относительно state root
        bytes32 accountRoot = verifyAccountProof(
            stateRoot,
            contractAddress,
            accountProof
        );
        
        // Верифицируем storage proof относительно account storage root
        value = verifyStorageProof(
            accountRoot,
            storageSlot,
            storageProof
        );
    }
}

Это полностью trustless: Ethereum validator set верифицируется криптографически, не через внешних oracle.

Практическая архитектура: General Message Passing

Для большинства проектов ZK light client избыточен по latency и стоимости. Практическое решение — General Message Passing через Axelar, LayerZero или Wormhole с разумными security параметрами.

Axelar GMP (General Message Passing)

Axelar — proof-of-stake network из validator-ов, которые наблюдают за несколькими chain и подписывают cross-chain сообщения.

// Source chain: отправляем произвольное состояние
import { IAxelarGateway } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol";
import { IAxelarGasService } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol";

contract StateSender {
    IAxelarGateway public immutable gateway;
    IAxelarGasService public immutable gasService;
    
    struct GameState {
        address player;
        uint256 score;
        uint256 level;
        uint256 timestamp;
    }
    
    function syncPlayerState(
        string calldata destinationChain,
        string calldata destinationAddress,
        address player
    ) external payable {
        GameState memory state = GameState({
            player: player,
            score: playerScores[player],
            level: playerLevels[player],
            timestamp: block.timestamp
        });
        
        bytes memory payload = abi.encode(state);
        
        // Оплата gas на destination chain
        gasService.payNativeGasForContractCall{value: msg.value}(
            address(this),
            destinationChain,
            destinationAddress,
            payload,
            msg.sender
        );
        
        // Отправляем через Axelar
        gateway.callContract(
            destinationChain,
            destinationAddress,
            payload
        );
    }
}
// Destination chain: принимаем и применяем состояние
import { AxelarExecutable } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol";

contract StateReceiver is AxelarExecutable {
    mapping(address => GameState) public syncedPlayerState;
    
    // Вызывается Axelar relayer после верификации
    function _execute(
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) internal override {
        // Проверяем source
        require(
            keccak256(abi.encodePacked(sourceAddress)) ==
            keccak256(abi.encodePacked(authorizedSender[sourceChain])),
            "Unauthorized source"
        );
        
        GameState memory state = abi.decode(payload, (GameState));
        
        // Проверяем актуальность (не принимаем старые данные)
        require(
            state.timestamp > syncedPlayerState[state.player].timestamp,
            "Stale state"
        );
        
        syncedPlayerState[state.player] = state;
        emit StateSynced(sourceChain, state.player, state.score, state.level);
    }
}

Idempotency и упорядоченность сообщений

Критическая проблема: сообщения могут доставляться не в порядке отправки, или дублироваться (при retry).

contract OrderedStateSync {
    // Sequence number для каждого источника
    mapping(string => mapping(address => uint256)) public lastSyncedSequence;
    
    // Набор обработанных message ID
    mapping(bytes32 => bool) public processedMessages;
    
    function _execute(
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) internal override {
        bytes32 messageId = keccak256(abi.encodePacked(sourceChain, sourceAddress, payload));
        
        // Защита от дублирования
        require(!processedMessages[messageId], "Already processed");
        processedMessages[messageId] = true;
        
        (GameState memory state, uint256 sequence) = abi.decode(payload, (GameState, uint256));
        
        // Принимаем только строго последовательные обновления
        // (или с flexible ordering если порядок не критичен)
        uint256 lastSeq = lastSyncedSequence[sourceChain][state.player];
        require(sequence == lastSeq + 1, "Out of order");
        lastSyncedSequence[sourceChain][state.player] = sequence;
        
        _applyState(state);
    }
}

Off-chain оркестрация: State Sync Worker

Для высокочастотных обновлений (игры, trading) — синхронизировать каждое изменение on-chain неэффективно. Правильная архитектура: batching.

// State Sync Worker (Node.js)
class StateSyncWorker {
  private pendingUpdates: Map<string, PlayerState> = new Map();
  private syncInterval = 30_000; // 30 секунд
  
  // Накапливаем обновления
  queueUpdate(playerId: string, state: PlayerState): void {
    // Если уже есть pending — перезаписываем (берём последнее)
    this.pendingUpdates.set(playerId, state);
  }
  
  // Батчевая отправка
  async flushBatch(): Promise<void> {
    if (this.pendingUpdates.size === 0) return;
    
    const batch = Array.from(this.pendingUpdates.entries());
    this.pendingUpdates.clear();
    
    // Строим Merkle дерево из всех обновлений
    const leaves = batch.map(([id, state]) =>
      keccak256(abi.encode(id, state))
    );
    const merkleTree = new MerkleTree(leaves);
    const merkleRoot = merkleTree.getRoot();
    
    // Отправляем только Merkle root cross-chain
    await stateSyncContract.submitBatch(
      merkleRoot,
      batch.length,
      timestamp,
    );
    
    // Храним batch данные off-chain для proof generation
    await batchStore.save(merkleRoot, batch);
  }
  
  // Пользователь запрашивает верификацию своего состояния
  async generateProof(playerId: string, merkleRoot: string): Promise<MerkleProof> {
    const batch = await batchStore.load(merkleRoot);
    const leaf = keccak256(abi.encode(playerId, batch.get(playerId)));
    return merkleTree.getProof(leaf);
  }
}

На destination chain хранится только Merkle root (один bytes32). Отдельные состояния верифицируются по запросу через Merkle proof — gas экономия в десятки раз.

Паттерны для конкретных use cases

NFT cross-chain metadata sync

NFT владелец на chain A получает привилегии на chain B. Подход: ownership proof через cross-chain message, кэшируется на destination с TTL.

// NFT Ownership Bridge
mapping(address => mapping(uint256 => uint256)) public ownershipCache; // owner => tokenId => expiry

function verifyAndCacheOwnership(
    address claimedOwner,
    uint256 tokenId,
    bytes calldata ownershipProof // cross-chain message или ZK proof
) external {
    bool valid = _verifyOwnership(claimedOwner, tokenId, ownershipProof);
    require(valid, "Invalid ownership proof");
    
    // Кэшируем на 1 час (для non-financial use cases допустимо)
    ownershipCache[claimedOwner][tokenId] = block.timestamp + 3600;
    emit OwnershipCached(claimedOwner, tokenId);
}

function hasVerifiedOwnership(address user, uint256 tokenId) public view returns (bool) {
    return ownershipCache[user][tokenId] > block.timestamp;
}

Cross-chain governance

DAO принимает решение на Ethereum (основная governance chain), применяется на всех chain где развёрнут протокол.

// Governance Executor на L2
contract CrossChainGovernanceExecutor is AxelarExecutable {
    address public constant ETHEREUM_GOVERNANCE = 0x...;
    uint256 public constant MIN_EXECUTION_DELAY = 2 days;
    
    struct QueuedAction {
        bytes callData;
        address target;
        uint256 executeAfter;
        bool executed;
    }
    
    mapping(bytes32 => QueuedAction) public queuedActions;
    
    function _execute(
        string calldata sourceChain,
        string calldata sourceAddress,
        bytes calldata payload
    ) internal override {
        require(
            keccak256(bytes(sourceChain)) == keccak256(bytes("ethereum")),
            "Only ethereum governance"
        );
        
        (bytes32 proposalId, address target, bytes memory callData) =
            abi.decode(payload, (bytes32, address, bytes));
        
        // Timelock: обязательная задержка перед исполнением
        queuedActions[proposalId] = QueuedAction({
            callData: callData,
            target: target,
            executeAfter: block.timestamp + MIN_EXECUTION_DELAY,
            executed: false
        });
        
        emit ActionQueued(proposalId, target, block.timestamp + MIN_EXECUTION_DELAY);
    }
}

Инструментарий

Messaging: LayerZero V2, Axelar GMP, Wormhole (для Solana + EVM). ZK proofs: Herodotus (storage proofs), Succinct Telepathy (light client). Indexing: The Graph (multi-chain subgraph). Monitoring: собственный worker + alerting на message delivery failures. Testing: Foundry с fork + mock messaging.

Ориентиры по срокам

Базовая синхронизация состояния (два chain, Axelar messaging, ordered delivery): 3–4 недели. Merkle-batched sync с off-chain worker и ZK storage proof верификацией: 8–12 недель. Trustless ZK light client интеграция (Succinct/Herodotus): 12–20 недель.