# 合约设计文档大纲(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. 用户可以提交默克尔证明,直接从一层提现。