In-Detail: Multi-Chain State Synchronization

Overview

zkLink Nova is powered by zkLink Nexus technology for multi-chain settlement. In zkLink's Nexus, users can deposit and withdraw assets on all connected networks (L1 and L2s). Users' assets are locked in smart contracts on the connected networks and enter the Nova network via the canonical rollup bridge. Nexus boasts Ethereum-grade security, achieved through multi-chain state synchronization by transmitting the sync hashes of on-chain transactions via the canonical roll-up message service.

The connected networks (L1 and L2s) of Nova can be classified into two types serving different roles:

  • Primary Chain: ZK-proofs and data commitments for transaction batches on Nova (L3) are submitted to the primary chain (Linea, L2). The primary chain is responsible for ZKP verification and checking on-chain data consistency by sync hashes.

  • Secondary Chain: Secondary chains send sync hashes to the primary chain via the canonical roll-up message service. Upon successful verification on the primary chain, the confirmed batch root is relayed back to secondary chains, and withdrawal requests on secondary chains can be executed.

Among the secondary chains, Ethereum (L1) holds a special position. Message transmission between the primary chain and other L2 secondary chains occurs via Ethereum, where the arbitrator contract facilitates the forwarding of cross-chain messages."

On-chain transaction synchronization

A user could deposit token assets by initiating an on-chain transaction. Additionally, users can submit other types of on-chain transactions via the connected networks, which the sequencer is force to process.

As shown in the figure above, if a user sends a transaction (e.g., depositing ETH) to the zkLink contract on a secondary chain (step 1), the transaction will be forwarded in real-time by the Nova sequencer to the zkLink contract on the primary chain (step 2). A user can also send a transaction directly to the zkLink contract on the primary chain (step 3).

Please note that only the sequencer is authorized to relay transactions from the secondary chains to the primary chain. To achieve low-latency synchronization, this process does not rely on a canonical rollup bridge. However, we have a mechanism in place to prevent the sequencer from sending fake information, which will be discussed in a later part of this article."

Step 2:

https://github.com/zkLinkProtocol/era-contracts/blob/zklink_testnet/l1-contracts/contracts/zksync/facets/Mailbox.sol

function forwardRequestL2Transaction( ForwardL2Request calldata _request )

The sequencer monitors the transactions received in the zkLink contract on the primary chain and forwards these transactions (in step 4) to the Nova network (L3) for execution. In the case that a user deposits an ERC-20 token on a secondary chain, the token will be locked in the ERC-20 bridge, and the message will be transmitted to the zkLink contract (see the figure below).

Multi-Chain State Synchronization

The main challenge of building a rollup network deployed across various networks is the risk of deposit fraud. For example, a bad sequencer may falsely inform the primary chain about a fake deposit on one secondary chain that does not exist. In such scenarios, without effective verification mechanisms, it could lead to the loss of user funds.

To prevent this risk, in the state synchronization phase, we add another layer of transaction consistency verification in addition to ZKP verification in a typical ZK-Rollup. This additional layer ensures that the data relayed to the primary chain in real-time is consistent with the sync hashes periodically transmitted via the canonical rollup message service.

For each transaction on a secondary chain, the zkLink contract will calculate the sync hash of this transaction based on the previous sync hash.

syncHashn=hash(tx,syncHashnāˆ’1)syncHash_{n} = hash(tx, syncHash_{n-1})

A relayer will periodically trigger secondary chains to send the latest sync hash to the arbitrator contract on Ethereum via the canonical message service (in steps 1, 2, 3), and the arbitrator contract will forward all sync hashes of secondary chains to the zkLink Contract via the canonical message service (in steps 4, 5, 6).

Step 1: Trigger the zkLink contract on secondary chains to send the latest sync hash to the L2 Gateway.

https://github.com/zkLinkProtocol/zklink-evm-contracts/blob/main/contracts/ZkLink.sol

function syncL2Requests(uint256 _newTotalSyncedPriorityTxs)

Step 2: Transmitting message (sync hash) via canonical rollup bridge.

Step 3: Receive message (sync hash) from canonical message service and forward it to the arbitrator contract. For example:

https://github.com/zkLinkProtocol/zklink-evm-contracts/blob/main/contracts/gateway/zksync/ZkSyncL1Gateway.sol

function finalizeMessage( uint256 _l2BatchNumber, uint256 _l2MessageIndex, uint16 _l2TxNumberInBatch, bytes memory _message, bytes32[] calldata _merkleProof )

Step 4: Forward message (sync hash) from the arbitrator contract to the L1 gateway of primary chain.

https://github.com/zkLinkProtocol/zklink-evm-contracts/blob/main/contracts/Arbitrator.sol

function forwardMessage( IL1Gateway _gateway, uint256 _value, bytes memory _callData, bytes memory _adapterParams )

Step 5: Transmitting message (sync hash) via canonical rollup bridge.

Step 6.1: Receive message (sync hash) from canonical message service of the primary chain and forward it to the zkLink contract on the primary chain.

https://github.com/zkLinkProtocol/zklink-evm-contracts/blob/main/contracts/gateway/linea/LineaL2Gateway.sol

function claimMessageCallback( uint256 _value, bytes calldata _callData )

Step 6.2: Once the primary chain receives the sync hash from secondary chains, zkLink contract will verify if it's consistent with the transactions that were previously relayed by the sequencer to the primary chain.

https://github.com/zkLinkProtocol/era-contracts/blob/zklink_testnet/l1-contracts/contracts/zksync/facets/Mailbox.sol

function syncL2Requests( address _secondaryChainGateway, uint256 _newTotalSyncedPriorityTxs, bytes32 _syncHash, uint256 _forwardEthAmount )

Step 7: After the zkLink contract on the primary chain executes ZKP verification for a transaction batch, it checks if all the on-chain transactions in that batch have already been verified based on sync hashes.

https://github.com/zkLinkProtocol/era-contracts/blob/zklink_testnet/l1-contracts/contracts/zksync/facets/Executor.sol

function _collectOperationsFromPriorityQueue(uint256 _nPriorityOps)

Upon successful verification of ZKP and on-chain transaction consistency, settlement on the primary chain can be approved. At this stage, Nova achieves soft finalization on the primary chain (Linea). Afterward, the batch root will be sent to all secondary chains via the canonical message service (in steps 8-13).

Step 8: Trigger the zkLink contract on the primary chain to send the batch root to the gateway.

https://github.com/zkLinkProtocol/era-contracts/blob/zklink_testnet/l1-contracts/contracts/zksync/facets/Mailbox.sol

function syncBatchRoot(address _secondaryChainGateway, uint256 _batchNumber, uint256 _forwardEthAmount)

Step 9: Transmitting message (batch root) via canonical message bridge. Once the message reaches Ethereum, zkLink Nova achieved hard finalization.

Step 10: Receive message (batch root) from canonical message service and forward it to the arbitrator contract.

https://github.com/zkLinkProtocol/zklink-evm-contracts/blob/main/contracts/Arbitrator.sol

function receiveMessage(uint256 _value, bytes memory _callData)

Step 11: Forward message (batch root) from the arbitrator contract to the L1 gateway of secondary chains.

https://github.com/zkLinkProtocol/zklink-evm-contracts/blob/main/contracts/Arbitrator.sol

function forwardMessage( IL1Gateway _gateway, uint256 _value, bytes memory _callData, bytes memory _adapterParams )

Step 12: Transmitting message (batch root) via canonical rollup bridge.

Step 13: Receive message from canonical message service of the secondary chain and forward it to the zkLink contract. For example:

https://github.com/zkLinkProtocol/zklink-evm-contracts/blob/main/contracts/gateway/zksync/ZkSyncL2Gateway.sol

function claimMessageCallback(uint256 _value, bytes memory _callData)

After receiving the batch root, withdrawal requests can be executed in the zkLink contract on the secondary chains.

For further detailed information, please refer to our verified smart contracts.

Last updated