# 合约设计文档大纲(old) # 文档目的 介绍合约的功能设计和具体实现。 # 合约功能概述 DeGate合约负责处理出块节点提交上链的zkBlock区块,并使用zkSnark零知识证明来验证zkBlock数据的正确性。zkBlock验证通过后,合约会进行状态的变更。 普通用户通过充值/提现/代币注册等接口,和合约进行交互。 ## zkBlock的处理和验证 出块节点的Postman账户调用合约的`submitBlocks`接口提交zkBlock,合约需要处理和验证如下信息 ### 验证完整树与资产树 DeGate的系统中,维护了用户完整信息的完整树和资产信息的资产树。zkBlock的header保存了父区块和当前区块的两棵树的根,合约通过验证父区块的根和当前状态一致,从而可以由一个旧状态更新到新状态。 资产树的另一个作用是保证系统的trustless,用户可以提交默克尔证明来和资产树对比验证,直接从一层提现。 ### 验证时间戳 zkBlock的header中包含了一个电路时间戳(TimestampInCircuits),这个时间戳作为电路的输入,并参与到电路中交易的验证(交易是否超时),电路无法验证这个时间戳的正确性。DeGate系统要求operator将电路时间戳作为区块的一部分,提交到合约中,进行验证。由于operator无法准确预估上链时间,合约会使用zkBlock上链时一层的时间戳(TimestampL1)进行校验,确保电路时间戳在有效范围内, TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS(7 days). 有效范围: [TimestampL1 - TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS, TimestampL1 + TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS] ### 验证和处理部分交易 zkBlock包含所有的用户交易,其中一部分交易需要在合约中处理,比如充值(Depoist),提现(Withdraw)和账户更新(AccountUpdate)。这部分交易也被称为conditional交易。 这部分交易数据的正确性,由电路零知识证明和合约共同来确保。 合约会按照一定的顺序处理这些交易,具体参见实现章节。如果任何交易处理失败,则整个zkBlock提交失败。 ### 零知识证明key 零知识证明key,包括证明key(proving key)和验证key(verification keys)。这两种key,分别用来生成proof和验证proof。(DeGate是如何生成零知识证明key的,参考Trusted Setup文档。) 在出块节点侧,使用电路程序和证明key来生成zkBlock对应的proofs。 在合约侧,合约根据区块的size信息,选择对应的验证key来验证proofs的正确性。 不同的电路size,会对应不同的零知识证明key。DeGate合约在部署时,会配置一组电路size对应的验证key。(电路size的选择,参考电路章节。) 验证key是通过硬编码的方式注册到合约中的。为了保证系统的trustless,DeGate合约在部署后,不允许更新验证key。 ### 验证零知识证明 zkBlock包含零知识证明`proof`,合约使用硬编码的验证key,来验证`proof`的正确性。 证明由出块节点生成,并作为zkBlock的一部分提交上链。 ## 充值功能 用户可以使用如下方式进行代币充值。 ### 合约充值 默认充值方式,支持任意币种。 通过调用DeGate合约的deposit接口,进行充值。如果存入ERC20代币,用户需要先发送交易到ERC20代币合约,授权一定额度给DeGate合约。 出块节点需要在`MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE`(15 days)内, 处理用户的充值。否则,用户可以调用`withdrawFromDepositRequest` 直接从合约中提取充值金额。 ### 转账充值 仅支持白名单币种。 通过直接转账代币到DeGate合约的方式进行充值,也被称为`gas saving deposit`。相比合约接口充值,用户不需要发送授权交易。 为了验证这类充值,DeGate合约中未确认所有权的代币余额需要大于充值金额。出块节点的Operator账户有权限消费这部分余额,进行转账充值。在合约中,这部分余额可以通过`getUnconfirmedBalance`接口查询。 ### 充值手续费 正常情况下,充值均免费。如果短时间内充值交易的笔数过多,则对充值进行收费。 免费可用的充值交易笔数,可以通过`getFreeDepositRemained` 接口查询。(默认配置,免费可用5000笔,上限5000笔,同时每个区块恢复2笔) 对于收费的充值,用户需要在发起调用deposit的交易里转入一定的ETH(默认配置0.01ETH),才会被处理,否则直接拒绝。 ## 提现功能 用户可以通过如下方式进行提现。 ### 普通提现 用户通过发送签名到DeGate服务端的方式,进行普通提现,而不需要调用合约接口。 这部分提现,在出块节点的Postman账户提交区块上链时,进行处理。 ### 强制提现 强制提现支持用户强制出块节点允许其提取对应账户下的余额,调用`forceWithdraw`合约接口。 出块节点必须在规定时间`MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE`(15 days)内上链处理,否则一旦超时,则DeGate会进入逃离模式。 强制提现需要用户支付一定的ETH手续费(默认配置0.01ETH,最大0.25ETH)。 强制提现要求用户提供账户地址,代币地址,和账户对应的AccountID。数据由用户提供,因此存在输入参数不匹配的可能。由于合约并不记录用户的账户与余额等信息,需要由出块节点的Postman账户将数据和证明提交到链上,并由合约删除错误的强制提现记录。 为了防止恶意用户发送过多强制提现交易攻击DeGate系统, 合约中设置了最大可用笔数`MAX_OPEN_FORCED_REQUESTS`(1000000笔), 定义了排队处理中的最大强制提现笔数。 同时为了最大限度的保护用户,该笔数基于operator的最大处理能力和最低的强制提现费用进行设定。 ### 逃离模式提现 进入逃离模式后,DeGate交易所的基本功能都会停止,出块节点也不能再提交区块。 用户只能够基于最后一个区块的状态提取资金。需要调用合约的`withdrawFromMerkleTree`接口,提交默克尔证明进行提现,一次只能提取一个币种的所有余额。 默克尔证明包含了用户某个币种的资产树的枝杈树信息,合约通过比较计算出的资产树和最新状态的资产树,来验证交易正确。 ## 代币注册 在DeGate中使用代币之前,需要进行注册。任何人都可以调用`registerToken`接口,进行代币注册。 合约会确保代币只能注册一次,ETH和DG代币在合约部署时注册。 在代币充值时,如果代币没有注册,合约会进行自动注册。 ID为0到31的代币由DeGate交易所预留,只能由交易所admin进行注册,这些币种可以用来支付矿工费。 # 合约的实现 ## 合约实现概述 合约的实现包括如下几个模块 - Exchange合约,处理zkBlock上链和充值提现请求等。 - Deposit合约,托管用户资产,提供代币的转入转出功能。 - Loopring合约,Exchange合约参数配置。 - BlockVerifier合约,电路零知识证明key注册和proof验证功能。 ## Exchange合约 ### zkBlock数据定义 #### 1. zkBlock的数据定义如下 ``` struct Block { uint8 blockType; uint16 blockSize; uint8 blockVersion; bytes data; uint256[8] proof; bool storeBlockInfoOnchain; // not used bytes auxiliaryData; bytes offchainData; // not used } ``` - blockType, zkBlock类型,目前恒定为0,仅支持一种类型。 - blockSize, zkBlock包含的交易数量,对应了电路size。 - blockVersion,zkBlock的版本号,默认为0。 - data, 是zkBlock的具体数据,并且作为input输入给电路,来生成证明proof。data由BlockHeader和交易的Calldata数据两部分组成。 - proof, zkBlock的零知识证明proof。 - auxiliaryData, 合约需要处理的交易的另一部分数据。这部分数据不会作为input输入给电路。每个conditional交易会有一个对应的AuxiliaryData,具体定义参考各交易的Auxdata数据部分。 #### 2. zkBlock的data中的BlockHeader定义 BlockHeader序列化为167 bytes,定义如下 ``` struct BlockHeader { address exchange; bytes32 merkleRootBefore; bytes32 merkleRootAfter; bytes32 merkleAssetRootBefore; bytes32 merkleAssetRootAfter; uint32 timestamp; uint8 protocolFeeBips; uint32 numConditionalTransactions; uint32 operatorAccountID; uint16 depositSize; uint16 accountUpdateSize; uint16 withdrawSize; } ``` - exchange, DeGate系统的exchange合约地址。 - merkleRootBefore, zkBlock出块前,完整树根哈希 - merkleRootAfter, zkBlock出块后,完整树根哈希 - merkleAssetRootBefore, zkBlock出块前,资产树根哈希 - merkleAssetRootAfter, zkBlock出块后,资产树根哈希 - timestamp, 电路输入的时间戳 - protocolFeeBips, 现货交易最大手续费,所有现货交易的手续费收费比例均不能大于此费率. - numConditionalTransactions, 合约需要处理的交易数量 - operatorAccountID, 出块账号的AccountID - depositSize, zkBlock中充值交易的数量 - accountUpdateSize, zkBlock中账户更新交易的数量 - withdrawSize, zkBlock中提现交易的数量 #### 3. zkBlock的data中的Calldata定义 交易的Calldata数据, 是合约需要处理的交易(Conditional交易)的序列化汇总。需要按照约定的数量和顺序,进行解析处理。 - BlockHeader中的depositSize/accountUpdateSize/withdrawSize 分别定义了这3种交易的数量 - 交易按照类型排列,Deposit/AccountUpdate排在最前面,Withdraw排在最后。 [Deposit transactions][AccountUpdate transactions][…others…][Withdraw transactions] - 每个交易的calldata为83 bytes,具体定义参考各交易的Calldata部分。 ### Conditional交易说明 需要在合约中处理的交易,比如充值(Depoist),提现(Withdraw)和账户更新(AccountUpdate)。这部分交易被称为conditional交易。 每个交易的数据,包括Calldata和Auxdata两部分。 #### Calldata数据 每个交易的calldata数据固定为83 bytes,不足部分用0 bytes填充。 为了节省gas,calldata切分为80 bytes(part1)和3 bytes(part2)两部分。在Block的data中,所有交易的part1部分聚合之后放到前面,part2部分聚合之后放到后面。 因此,读取交易的Calldata数据,需要分别读取part1和part2,再拼接到一起,合计83 bytes。 #### AuxData数据 每个交易的Auxdata数据为AuxiliaryData对象。所有交易的Auxdata数据存储到AuxiliaryData[] 数组中,序列化为bytes后,存到Block中的auxiliaryData。 ``` struct AuxiliaryData { bytes data; } ``` 读取交易的Auxdata数据,即按照顺序从数组中读取。 Deposit交易不需要AuxData,为了统一处理,Deposit交易的AuxData的data字段为空数据"0x"。 合约中,会验证AuxiliaryData[] 数组长度和Conditional交易数量一致。 ### Deposit交易定义和处理 Deposit交易用来处理用户的充值请求。 Deposit交易的Calldata定义 ``` - Type: 1 byte (0 for calling `deposit`, 1 for gas saving deposit) - Owner: 20 bytes - Account ID: 4 bytes - Token ID: 4 bytes - Amount: 31 bytes ``` - Type,合约充值 Type =0,转账充值 Type =1. - Owner,用户的一层账户地址 - Account ID,用户账户在DeGate系统中的账户ID - Token ID,充值代币在DeGate系统中的代币ID - Amount 充值数量, uint248类型。 合约根据交易的calldata定义decode出交易的具体字段,并根据Type 分别处理合约充值和转账充值。 - 合约充值 未处理的充值请求`pendingDeposit` 存在, 并且未处理的amount数量大于或等于充值的amount数量。 如果充值请求没有消费所有的pending余额,则`pendingDeposit` 需要更新余额并保留。 未处理的充值的数量, 可以通过 `getPendingDepositAmount` 接口查询。 - 转账充值 未确认所有权的代币余额`unconfirmedBalance` 需要大于充值的amount数量。 直接转账充值,合约中无法记录发起充值的用户,因此合约中只需确认数量合法。 未确认所有权的代币余额可以通过`getUnconfirmedBalance`接口查询。 ### AccountUpdate交易定义和处理 AccountUpdate允许用户添加更新高权限秘钥。 AccountUpdate交易的Calldata定义 ``` - Type: 1 byte (type = 1 for a conditional transaction) - Account owner: 20 bytes - Account ID: 4 bytes (for contract verification) - Fee token ID: 4 bytes - Fee amount: 2 bytes (16 bits, 11 bits for the mantissa part and 5 for the exponent part) - Public key: 32 bytes - Nonce: 4 bytes - Account ID: 4 bytes (appointed accountID by DeGate, used for crawling blocks to restore the Asset Merkle Tree) ``` - Type, 只支持ECDSA签名类型的AccountUpdate交易,Type字段必须为1。 - Account owner,用户的一层账户地址 - Account ID, 用户账户在DeGate系统中的账户ID - Fee token ID, Fee代币在DeGate系统中的代币ID - Fee amount,Fee数量 - Public key,高权限秘钥的公钥,EdDSA格式 - Nonce, 防重放字段,nonce每次使用后递增。 - Account ID,预分配的账户ID,上链,可以用来恢复资产树。 Auxdata定义 ``` struct AccountUpdateAuxiliaryData { bytes signature; uint96 maxFee; uint32 validUntil; } ``` - signature 是用户的ECDSA签名, 签名使用EIP712方式。签名由用户使用一层账户的私钥签署。 签名内容: ``` AccountUpdate { owner (address) accountID (uint32) feeTokenID (uint32) maxFee (uint96) publicKeyX (uint256) validUntil (uint32) nonce (uint32) } ``` - maxFee,最大支付的fee,用户签名的一部分。 - validUntil,交易的有效期,用户签名的一部分。 合约根据交易的calldata定义和auxData定义,decode出交易的具体字段。 合约需要做如下验证: 1. 验证validUntil,交易的有效期,需要小于电路时间戳。 2. 实际支付fee必须小于等于maxFee。 3. 验证用户签名signature正确。 ### Withdraw交易定义和处理 Withdraw交易允许用户将资产从L2提现到一层。 Withdraw交易的Calldata定义 ``` - Type: 1 bytes (type > 0 for conditional withdrawals, type == 2 for a valid forced withdrawal, type == 3 when invalid) - Owner: 20 bytes - Account ID: 4 bytes - Token ID: 4 bytes - Fee token ID: 4 bytes - Fee amount: 2 bytes (16 bits, 11 bits for the mantissa part and 5 for the exponent part) - StorageID: 4 bytes - OnchainDataHash: 20 bytes ``` - Type, withdraw包括4种type类型,合约负责处理1/2/3类型,具体在处理中描述。 - Owner,用户的一层账户地址 - Account ID, 用户账户在DeGate系统中的账户ID - Token ID,提现代币在DeGate系统中的代币ID - Fee token ID, Fee代币在DeGate系统中的代币ID - Fee amount,Fee数量 - StorageID,做为签名一部分防重放,并且记录提现已经完成 - OnchainDataHash,minGas,to和amount3个字段不直接传递给电路,而是以hash的形式。目的是可以减少calldata长度。 Auxdata定义 ``` struct WithdrawalAuxiliaryData { bool storeRecipient; uint gasLimit; bytes signature; uint248 minGas; address to; uint96 maxFee; uint32 validUntil; uint248 amount; } ``` - storeRecipient, 链上保存提现记录。 - gasLimit,withdraw提币支付的gas上限。 - signature, 用户的ECDSA签名。 - minGas,withdraw提币支付的gas最低值。 - to, withdraw收币地址 - maxFee,最大支付的fee,用户签名的一部分。实际支付fee必须小于等于maxFee。 - validUntil,交易的有效期,用户签名的一部分。 - amount,withdraw提币数量。 合约根据交易的calldata定义和auxData定义,decode出交易的具体字段。处理流程如下 1. 验证onchainDataHash正确. ``` bytes20 onchainDataHash = bytes20( abi.encodePacked( minGas, to, amount ).fastSHA256() ); ``` 2. 验证withdraw交易类型 - type=0:EdDSA签名模式。合约不验证。 - type=1:ECDSA签名模式。合约验证: - validUntil 小于电路时间戳 - fee 小于或者等于 maxFee - 验证用户签名signature正确 - type=2:有效的强制提现。合约验证: - withdraw的from和to必须相等 - fee 必须是0 - forcedWithdrawal请求存在,并且请求的owner和from地址一致 - forcedWithdrawal请求不存在,必须是关停模式,operator替用户强制提现 - type=3:无效的强制提现。 - withdraw的from和to必须相等 - fee 必须是0 - forcedWithdrawal的请求存在,并且请求的owner和from地址不一致 - amount必须是0。不处理提币,只清理合约中的forcedWithdrawal无效请求 3. 处理提币 - 合约验证auxData中gasLimit需要大于或者等于minGas。 - 调用deposit合约的提币接口,尝试向用户地址转币。 - 允许提币接口执行失败,而withdraw交易成功,不影响submitBlocks的交易上链。如果提币失败的话,则将提币请求记录到`amountWithdrawable`表中。用户可以调用`withdrawFromApprovedWithdrawals`直接从一层提币。 ### 交易最大手续费的校验和更新 不同的交易对,存在不同的交易手续费,而电路并不约束手续费的具体数值,这部分校验由合约执行。 在出块节点Postman账户提交zkBlock时,会验证和更新交易最大手续费。 - 校验机制 每个提交的zkBlock的BlockHeader中,`protocolFeeBips`就是现货交易最大手续费。电路中,会约束该zkBlock中的所有现货交易的手续费均不大于此费率。 合约中,会比较BlockHeader中的`protocolFeeBips`和合约中设置的费率一致,否则上链失败。 - 延时更新 合约中设置的费率采用延时更新的机制进行修改,延时为`MIN_AGE_PROTOCOL_FEES_UNTIL_UPDATED`(7天)。 延时更新, 需要admin调用Loopring合约`updateProtocolFeeSettings`接口设置需要改的数值。 延时期限过后,在提交zkBlock时会调用`validateAndSyncProtocolFees`进行更新。 - 最大值保护 为了保护用户,最大手续费的范围限制为 [0%, 1%], 默认值0.18%。 ### zkBlock上链接口实现 Postman通过调用 `submitBlocks` 接口提交zkBlock上链。 ``` function submitBlocks(Block[] zkBlocks) ``` 1. 验证zkBlock中完整树与资产树root正确 2. 验证zkBlock中电路时间戳合法 3. 处理Conditional交易 4. 交易最大手续费的校验和更新 5. 调用BlockVerifier合约的verifyProofs接口,验证零知识证明 ### 合约充值接口实现 合约充值接口,允许用户发起合约充值请求 ``` function deposit(from,to,tokenAddress,amount, extraData) ``` 1. 检查token地址,如果没有注册,调用`registerToken`进行注册。 2. 检查是否需要对充值进行收费,如果需要,从转入的eth中扣除手续费`depositFeeETH` 3. 调用Deposit合约的`deposit`接口执行充值转币流程 4. 记录用户的充值请求到 `pendingDeposits`列表 用户的充值,需要等待operator构建`Deposit交易`并提交上链后,才能够完成。 如果operator一直没有处理,则用户可以调用`withdrawFromDepositRequest` 直接从合约中提取充值金额 ### 强制提现接口实现 强制提现接口,允许用户发起强制提现请求 ``` function forceWithdraw(from, token, accountID) ``` 1. 检查强制提现存在可用的slots 2. 检查转入的eth足够支付强制提现手续费 3. 记录用户的强制提现请求到`pendingForcedWithdrawals`列表 用户的强制提现,需要等待operator构建`Withdraw交易`并提交上链后,才能够完成。 如果operator一直没有处理,则用户可以触发交易所进入逃离模式。 ### 逃离模式实现 #### 逃离模式的触发接口 ``` function notifyForcedRequestTooOld(accountID,token) ``` 1. 检查accountID和token对应的强制提现请求存在 2. 检查该请求已经超时,`MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE`(15 days) 3. 设置逃离模式启动时间为当前一层的时间戳。可以调用`isInWithdrawalMode`查询,当前是否是逃离模式 (`withdrawalModeStartTime` > 0) #### 逃离模式的提现接口 ``` function withdrawFromMerkleTree(merkleProof) ``` 1. 检查必须是逃离模式 2. 每个用户的每个币种,只允许处理一次提现 3. 验证默克尔证明正确 4. 处理提币,提现的币种会全额提现,并标记该币种已经处理。 #### 默克尔证明MerkleProof的验证 MerkleProof的数据定义 ``` struct AccountLeaf { uint32 accountID; address owner; uint pubKeyX; uint pubKeyY; uint32 nonce; } struct BalanceLeaf { uint32 tokenID; uint248 balance; } struct MerkleProof { AccountLeaf accountLeaf; BalanceLeaf balanceLeaf; uint[48] accountMerkleProof; uint[48] balanceMerkleProof; } ``` - accountLeaf, 账户叶子节点 - balanceLeaf, 代币叶子节点 - accountMerkleProof, 从叶子节点到account根节点的proof, 一共是3*16层 = 48个。account根节点,即资产树的根。 - balanceMerkleProof, 从叶子节点到balance根节点的proof, 一共是3*16层 = 48个。balance根节点,是账户叶子节点的一部分,参与账户节点的计算。 MerkleProof的验证 ``` function verifyAccountBalance(merkleRoot, merkleProof) ``` 1. 计算代币叶子节点哈希, hash(balance) 2. 将balanceMerkleProof分为16组,做为兄弟节点。循环计算出balance根节点 balancesRoot 3. 计算账户叶子节点哈希, hash(owner, pubKeyX, pubKeyY, nonce, balancesRoot) 4. 将accountMerkleProof分为16组,做为兄弟节点。循环计算出account根节点 accountsRoot 5. 验证资产树的根和accountsRoot是一致的 ### 关停模式实现 交易所admin可以选择在任何时候,调用`shutdown` 进入关停模式。 即使在shutdown模式,用户仍可以通过普通提现的方式来取走他的资金, Operator也可以正常出块。 进入shutdown模式后,如果用户没有进行withdraw操作,operator有权限替代用户发起forceWithdraw,并将资金提取到用户的一层地址。 在这个模式,operator只会处理withdraw交易,而不会再处理其他类型交易。 ``` function shutdown() ``` 1. 如果交易所是逃离模式,则无法进入关停模式 2. 设置关停模式启动时间为当前一层的时间戳。 可以调用`isShutdown` 查询,当前是否是关停模式((`shutdownModeStartTime` > 0) ## Deposit合约 Deposit合约是存储用户代币的合约,Exchange合约调用Deposit合约进行充值和提现。Deposit合约,只提供接口来转账ETH和ERC20,确保用户资金安全。 ### 充提接口 ``` function deposit(from, token, amount, /*extraData*/) ``` 1. 只能由Exchange合约调用 2. 充值ETH,检查转入数额大于等于amount,多的部分退款。 3. 充值ERC20,需要调用TransferFrom将代币从用户账户转入到Deposit合约,因此用户需要预先授权。 4. `转账充值`, 是直接转账到Deposit合约,而不会调用到`deposit`接口。 ``` function withdraw(/*from*/, to, token, amount) ``` 1. 只能由Exchange合约调用 2. 将代币转账给`to`地址。 ### 非标准代币 余额会自动通胀/通缩的rebase类型币种(比如ampl),DeGate不支持。 余额在转账时变化的burn类型币种(比如cult DAO),DeGate 默认情况不支持。可以通过添加白名单的方式来支持。 白名单接口 ``` function setCheckBalance(token, checkBalance) ``` 1. 添加token到白名单列表 2. 充值时,需要计算合约实际的余额变化,来确定充值金额。 ## Loopring合约 Loopring合约, 用来配置强制提现手续费和交易最大手续费。(这是沿用的路印合约模块,用来管理多个Exchanges和参数配置。在DeGate中只会有一个Exchange合约。) 强制提现手续费接口 ``` function updateSettings (forcedWithdrawalFee) ``` 1. 检查手续费不能大于`MAX_FORCED_WITHDRAWAL_FEE` (0.25 ETH) 交易最大手续费接口 ``` function updateProtocolFeeSettings(protocolFeeBips) ``` 1. 检查费率不能大于`MAX_PROTOCOL_FEE_BIPS` (1%) 2. 交易最大手续费延时(7 days)更新。 ## BlockVerifier合约 BlockVerifier合约,负责电路零知识证明key注册和proof验证功能。 ### 电路验证key的注册 电路验证key通过硬编码的方式,直接将一组keys写入到 `VerificationKeys.sol` 合约。 这组keys,对应的zkBlock的size为 [320, 170, 85, 42, 20, 10, 5] 合约部署后,DeGate中不提供接口新增和修改验证key。 ### 零知识证明proof验证 验证接口verifyProofs ``` function verifyProofs(blockType, blockSize, blockVersion, publicInputs,proofs) ``` - blockType, zkBlock类型,目前恒定为0,仅支持一种类型。 - blockSize, zkBlock包含的交易数量,对应了电路size。 - blockVersion,zkBlock的版本号,默认为0。 - publicInputs, zkBlock的data数据的哈希 - proofs, zkBlock的零知识证明proof 验证流程 1. 根据blockType/blockSize/blockVersion,选择验证key 2. 根据zkBlock数量,调用第三方library的verify接口验证proof。 - library Verifier, 单个proof验证。 https://github.com/HarryR/ethsnarks/blob/master/contracts/Verifier.sol - library BatchVerifier, 批量proof验证。https://github.com/matter-labs-archive/Groth16BatchVerifier/blob/master/BatchedSnarkVerifier/contracts/BatchVerifier.sol 3. 批量验证有固定的开销,对于单个proof的验证,使用Verifier合约单个验证更高效。 ## 合约的Trustless说明 用户可以无需信任的和DeGate合约交互,合约的Trustless由以下几点保证 1. DeGate合约没有使用proxy更新模式,部署后不允许更新合约代码。 2. BlockVerifier合约中,不允许新增和修改电路验证key。 3. 用户可以使用强制提现,来约束operator处理自己的提现交易。 4. 用户可以提交默克尔证明,直接从一层提现。