zkSync 基本原理

1. 引言

zkSync为以太坊的扩容和隐私引擎,当前支持或即将支持的功能有:

  • 当前支持 low gas transfers of ETH and ERC20 tokens in the Ethereum network。
  • 计划在2021年,通过Zinc语言或者现有的Solidity语言,实现可编程的隐私智能合约。
  • 对交易所友好,即将支持交易所协议的关键功能——Atomic swap:https://github.com/matter-labs/atomic-swaps

2. zkSync中的基本概念

2.1 zkSync中的operation

zkSync中有2种类型的operation操作:

  • Priority operation:又可分为Deposit和FullExit两种类型。
  • Transaction:又可分为ChangePubKey、Transfer、Withdraw、ForcedExit 四种类型。

(1)Priority operation
Priority operation已直接在以太坊主网上启动。
如,用户创建一个存款交易,将资金由以太坊转移到zkSync。priority operation可通过numeric ID 或 hash of the ethereum transaction that created it 来区分。
有2种类型的priority operation:

  • Deposit:将资金由以太坊转移至zkSync中的指定账号。若zkSync中的该账号不存在,则会创建该账号,同时为该地址赋予一个numeric ID。
  • FullExit:在不与zkSync server交互的情况下,将资金由zkSync提取到以太坊。当检测到来自zkSync服务器节点的审查时 或 无法设置zkSync账号的signing key时(如,某地址对应的是智能合约),该操作可用于紧急退出。

(2)Transaction
transaction 必须由zkSync operation通过API提交。
transactions通过the hash of their serialized representation 来区分。
有4种类型的transaction:

  • ChangePubKey:设置或修改某账号的signing key。若无相应的signing key,该账号无法做出priority operation之外的任何操作。
  • Transfer:将资金由一个zkSync账号交易至另一个zkSync账号。若zkSync上的接收账号不存在,则会创建新账号并为其赋予一个numeric ID。
  • Withdraw:将资金由zkSync提取至以太坊。
  • ForcedExit:提取资金,若该“target” L2账号没有相应的signing key时。(如,某地址对应的是智能合约)

2.2 zkSync中的block

zkSync中的所有操作都是按block排列的。
当zkSync operator创建好区块之后,通过Commit transaction,会将该区块推送到以太坊主网上的zkSync智能合约。此时,其状态并未final。几分钟之后,证明该区块正确性的ZK proof将生成。该proof通过Verify transaction 发布到以太坊。只有在该Verify tx被挖出后,该新状态才可认定为是final的。

存在多个区块已commit但未verify的状态。为了让用户不等待区块finalization,可将交易分组为“mini-blocks”,相应的延时会小很多。因此,区块可以更小的间隔部分提交,也能以更短的间隔收到交易并更新相应的状态。

这就意味着,发送交易后,用户既不用等待block commit也不用等待block verify,在交易执行后,相应的资金即立即可用。

2.3 zkSync中的flow

zkSync中的主要流程有:

  • 创建账号
  • 设置signing key
  • 交易资金
  • 交易费用
  • 提取资金

(1)创建账号
zkSync的账号可通过从 以太坊存入资金 或 在zkSync中交易资金至指定的账号。若相应账号不存在,以上操作会创建新的账号。
但是,新创建的账号无法授权任意交易,必须由该账号的owner设置相应的signing key。

(2)设置signing key
默认,每个账号的signing key都是0值,以标识该账号是“unowned”,主要基于以下考虑:

  • 若在以太坊中交易至某地址是有效的,则其在zkSync中也是有效的。
  • 并不是每一个地址都有相应的private key(如某些智能合约)
  • 可交易至用户账号,即使其当前并不对zkSync感兴趣

为了让某账号能发起L2交易,用户必须通过ChangePubKey交易为其设置signing key。
该交易中必须包含2个签名:

  • zkSync signature of the transaction data,使得其无法更改交易内容。zkSync signature on all transaction fields must correspond to the public key provided in the transaction。
  • Ethereum signature,用于证明account ownership。Ethereum signature应为a signature of some pre-defined message。ownership of account将由zkSync server和smart contract共同check,以保证更好的安全性。

(3)交易资金
任何在以太坊上valid的交易,在zkSync中也为valid。
用户可交易任意数额的Ether或任意支持的ERC-20 token。
当前zkSync支持的token有:

  • https://zkscan.io/tokens

相应的api见:

  • https://zksync.io/api/

若交易至某个不存在的账号,则在发送给智能合约时需再稍微多一点的数据,因为需提供新账号的信息,相应的交易费用也比给existing账号的费用略高。

(4)交易费用
zkSync需要交易费用来覆盖网络维护费用。
每种交易类型的费用主要从以下3方面来计算:

  • 发送到以太坊的数据量
  • 当前的gas price
  • 为包含该交易的区块生成proof所需的计算资源的成本

由于在一个区块中包含了很多交易,该成本会分摊至区块中所包含的所有交易,所以每笔交易的费用将很低。
此外,为所有数据进行费用计算,相应的API接口为:

  • https://zksync.io/api/v0.1.html#get-tx-fee

(5)提取资金
从zkSync将资金提取到以太坊账号,当前支持3种方式:

  • 通过Withdraw transaction:为L2交易,用于request a withdrawal from your zkSync account to any Ethereum address。与交易类似,需要由正确的zkSync private key签名。
  • 通过ForceExit transaction:为L2交易,用于request from any unowned account (one that does not have a signing key set)。在该类交易中,既不能指定以太坊地址,也不能指定交易金额。仅支持将target L2地址某种特定token的所有可用金额都提取至target L1 address。与Withdraw transaction类似,该交易的发起方将支付相应的费用、
  • 通过FullExit priority operation:其由L1发起,智能合约可保证该request将以合理的时间间隔执行,或当网络死机或被攻陷时,合约将进入出逃模式。This method is preferred if the user will ever experience censorship from the network operator.

3. 向zkSync网络发送交易基本原则

向zkSync网络发送交易,主要考虑以下3种类型:

  • send priority operation
  • send transaction
  • send transactions batch

3.1 send priority operation

通过调用相应智能合约的方法来触发priority operation。

(1)deposit存款操作,可分为:

  • 存ETH到Layer2。
  • 存ERC20 token到Layer2。在存ERC20资金之前,必须批准合约相应数量的金额,即:
    erc20contract.approve(zkSyncContractAddress, deposit_amount);
// @notice Deposit ETH to Layer 2 - transfer ether from user into contract, validate it, register deposit
/// @param _franklinAddr The receiver Layer 2 address
function depositETH(address _franklinAddr) external payable nonReentrant;

/// @notice Deposit ERC20 token to Layer 2 - transfer ERC20 tokens from user into contract, validate it, register deposit
/// @param _token Token address
/// @param _amount Token amount
/// @param _franklinAddr Receiver Layer 2 address
function depositERC20(IERC20 _token, uint104 _amount, address _franklinAddr) external nonReentrant;

(2)fullExit操作:
用户必须通过依次调用fullExit来注册exit request,然后调用completeWithdrawals来complete this request。

/// @notice Register full exit request - pack pubdata, add priority request
/// @param _accountId Numerical id of the account in the zkSync network
/// @param _token Token address, 0 address for ether
function fullExit (uint32 _accountId, address _token) external nonReentrant;

/// @notice executes pending withdrawals
/// @param _n The number of withdrawals to complete starting from oldest
function completeWithdrawals(uint32 _n) external nonReentrant;

3.2 send transaction

在发送交易之前,用户需做如下操作:

  • 准备交易数据
  • 将交易数据encode为a byte sequence
  • 用zkSync私钥对该byte sequece进行签名
  • 为transaction description生成Ethereum签名 或 提供EIP-1271签名
  • 通过相应的 JSON RPC方法 发送交易

详细的交易数据和encode细节可参见:

  • zkSync Rollup Protocol

zkSync中采用的cryptographic primitive与以太坊中的不同,zkSync中生成私钥和签名交易的接口具体见:

  • Cryptographic libraries

交易类型的不同,相应的Ethereum signature message也不同:(transaction分为ChangePubKey、Transfer、Withdraw、ForcedExit 四种类型)

// Amount and fee must be encoded into formatted string, e.g. by `ethers.utils.formatUnits` method
// with respect to the token decimals value.
// Token must be represented as a token symbol, e.g. `ETH` or `DAI`.

// For Transfer:
const transferEthMessage =
  `Transfer ${stringAmount} ${stringToken}\n` +
  `To: ${transfer.to.toLowerCase()}\n` +
  `Nonce: ${transfer.nonce}\n` +
  `Fee: ${stringFee} ${stringToken}\n` +
  `Account Id: ${this.accountId}`;

// For Withdraw:
const withdrawEthMessage =
  `Withdraw ${stringAmount} ${stringToken}\n` +
  `To: ${withdraw.ethAddress.toLowerCase()}\n` +
  `Nonce: ${withdraw.nonce}\n` +
  `Fee: ${stringFee} ${stringToken}\n` +
  `Account Id: ${this.accountId}`;

// For ChangePubKey (assuming it is a stand-alone transaction, for batch see details below):
const msgNonce = utils.hexlify(serializeNonce(nonce));
const msgAccId = utils.hexlify(serializeAccountId(accountId));
const pubKeyHashHex = pubKeyHash.replace("sync:", "").toLowerCase();
const changePubKeyEthMessage =
  `Register zkSync pubkey:\n\n` + `${pubKeyHashHex}\n` + `nonce: ${msgNonce}\n` + `account id: ${msgAccId}\n\n` + `Only sign this message for a trusted client!`;

注意,某些以太坊签名者会在签名消息前加\x19Ethereum Signed Message:\n${messageBytes.length} 前缀,因此若签名者未自动加该前缀,可能需要手动加上。

3.3 send transactions batch

transactions batch 是指 a set of transactions that should succeed all together。若其中的一笔交易失败,则该batch中的所有交易也将失败。当前一个transactions batch最多支持50笔交易。且在transaction batch中,没必要为每笔交易单独设置费用,仅要求所有交易的费用之和 大于等于 单独发送交易的费用之和。

(1)使用transaction batch模式,支持以其他的token来支付交易费用,具体实现方式为,在一个batch中创建2笔交易:

  • transfer to the recipient in token FOO 的fee set设为0
  • transfer to the own account in token BAR (想用BAR来支付token)的amount set设为0,而将其fee set设为足够的金额(足以覆盖这两笔交易的费用)

server将check that sum of fees(0 in the first transaction and 2x expected fee in the second one) is enough to cover processing of two transfers and will execute the batch。

(2)使用transaction batch模式,无需为每笔交易单独提供an Ethereum signature,可为每个batch提供一个签名就足够了。【仍然需要类似 \x19Ethereum Signed Message:\n${messageBytes.length} 的前缀】
可通过https://zksync.io/api/v0.1/#submit-txs-batch JSON RPC接口来发送batch和签名。

为batch的签名规则为:

// Assuming that `transactions` variable holds an array of batch transactions, and
// `serializeTx(...)` encodes transaction into bytes as per zkSync protocol.

// Obtain concatenated byte representations of each transaction.
const bytes = concat(transactions.map((tx) => serializeTx(tx)));
// Calculate `keccak256` hash of this byte sequence.
const hash = ethers.utils.keccak256(bytes).slice(2);
// Decode it into a byte sequence.
const message = Uint8Array.from(Buffer.from(hash, "hex"));

注意:当前transactions batch仅支持server-side abstraction。会在cricuit之前进行successful execution check,information about batch不会传入circuit中。若想以不同的token来支付交易费用,则推荐在最后设置the fee payment transaction。未来,transations batch将在circuit内enforced,这样可提升整体的安全性。

4. zkSync中的智能合约

zkSync有望推出高效、安全、图灵完备的多语言智能合约。

zkSync的智能合约编程模型将继承以太坊的。

Zinc和Solidity语言都是图灵完备的,因此你可用无限循环、递归、向量和maps of arbitrary length等。局部变量存储在栈上或堆上,而contract storage则是全局可访问的。智能合约可通过强类型接口相互调用,且可访问public storage fields。

4.1 zkSync智能合约的composability

zkSync智能合约可像以太坊智能合约那样相互调用。无论包含了多少个合约,每个call transaction tree为atomic的。
任何DeFi项目都可迁移至zkSync,现有的大多数solidity代码可无需修改直接部署。

4.2 zkSync的VM

zkSync的VM为高效、图灵完备、SNARK友好的虚拟机,可运行zkSync智能合约。

对智能合约bytecode和虚拟机本身都进行了优化,使得交易执行速度很快。

zkSync的虚拟机是SNARK友好的,其execution trance可proven in SNARK。不需要为每个program定义一个circuit。

zkSync虚拟机的proof system是PLONK方案。

4.2 zkSync智能合约语言——Zinc

Zinc 是一个新兴的框架,用于在zkSync平台上开发智能合约和SNARK电路。

现有的ZKP框架缺乏特定于智能合约的功能。由于智能合约涉及有价值的金融资产,安全和保障至关重要。这就是为什么现代智能合约语言(如Simplicity或Libra’s move)的设计者们更倾向于代码的安全性和形式验证性,而不是表现力。

Zinc填补这两个世界之间的差距,提供了一个简单,可靠的智能合约语言,专为ZKP电路优化,易于开发人员学习。
该框架包括一个简单的、图灵完备的、以安全为中心的通用语言,专门用于开发智能合约和具有平坦学习曲线的零知识证明电路。语法和语义与Rust非常相似。
Zinc编译器使用LLVM作为其中端和后端,这为代码优化提供了一套强大的解决方案。
Zinc语言正在大力发展;因此,它的许多方面最终将得到改进或改变。但是,安全性和简单性等基本原则永远不会受到质疑。

4.3 zkSync智能合约语言——Solidity

Solidity 是一种通用语言,拥有庞大的代码库和无数的DeFi项目,一种被来自世界各地的数千名区块链开发人员采用的语言。
大多数的Solidity项目可无需修改直接部署,但是,有些功能可能会被进制或忽略,以保证代码兼容性:

  • 带内存访问的ASM blocks
  • 利用溢出来方便计算
  • ABI合约调用
  • 未定义行为的一般情况

此外,matter labs团队也在研究由 Solidity 到 Zinc的transpiler,以简化迁移过程。

4.4 zkSync智能合约的路线图

zkSync测试网络智能合约已于2020年10月启动,Curve Finance为其第一个常驻dapp。
计划于2021年上主网,届时将同时支持Zinc和Solidity语言。

可以使用Zandbox server运行任意智能合约。
如何在Rinkeby testnet部署zkSync智能合约,可参看教程:

  • https://zinc.zksync.io/07-smart-contracts/02-minimal-example.html

5. zkSync 1.x中的NFTs

NFT addresses将按如下规则来encode NFT content和metadata:

address = truncate_to_20_bytes(rescue_hash(creator_account_id || serial_id || content_hash))

这种加密方法确保了2个不变量:

  • NFT addresses可看成是unique commitment to the creator、serial number of the NFT及其content hash;
  • NFT addresses不受任何人控制 或 在主网上拥有smart contract code。

注意,在zkSync 1.x中,多个NFTs可被minted with the same content hash。

可在Rinkeby 测试网上调用 SDK接口 来mint和transfer NFTs。

5.1 mint NFT

mint NFT,需使用new opcode MINT_NFT,相应的参数为:

  • creator_account_id
  • content_hash
  • recipient_account_id

通过recipient_account_id参数,creator可选择是mint to 自己还是直接mint to 其他人。

参考资料

[1] zkSync Mainnet Block Explorer
[2] zkSync project architecture
[3] Introduction to zkSync for Developers
[4] zkSync Rollup Protocol
[5] Zinc说明书

你可能感兴趣的:(隐私应用)