rocky rocky
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # 合约设计文档大纲 # ***术语表*** | Terms | Description | | :--------:|:-------------------| |资产树|资产树是保存了全部DeGate账户和资产中重要数据的默克尔树。| |资产私钥|资产私钥指的是资产公私钥对中的私钥。| |资产公钥|资产公钥指的是资产公私钥对中的公钥。| |区块提交|区块提交是Operator通过Postman向DeGate智能合约提交zkBlock的零知识证明的过程。| |出块节点|出块节点负责完成DeGate链下交易的ZK-Rollup流程,包括交易排序、打包、出块、生产零知识证明、数据上链。| |Calldata|Calldata是调用智能合约方法时填写到data字段的内容。DeGate链下账户和资产数据都通过calldata传给智能合约。| |电路|电路是描述需要进行零知识证明事件的系统模块,以zkBlock作为输入,输出相应的零知识证明。| |DeGate账户|DeGate账户用来在DeGate协议内管理资产、下单、充值、提现和转账。| |充值|充值功能允许将代币或ETH从钱包地址转移到DeGate账户。| |高级充值|用户调用DeGate智能合约的Deposit方法进行充值。| |标准充值|用户直接发送资产到DeGate智能合约地址进行充值。| |完整树|完整树是保存了全部DeGate账户、资产和订单数据的默克尔树。| |逃离模式|逃亡模式允许用户不通过任何第三方,自主从DeGate账户取回资产。当逃亡模式被启动,DeGate智能合约将拒绝接收新的zkBlock数据, 只支持用户取回资产到钱包地址。| |强制提现|强制提现是DeGate智能合约功能,允许用户发起提现请求,要求DeGate节点在规定的时间范围内执行请求。| |矿工费|矿工费是在以太坊网络上执行操作所需的计算工作量成本。目前DeGate协议中可以使用 ETH、USDC 和 USDT 来支付矿工费。| |链下交易|DeGate节点处理部分用户请求时,会产生链下交易。这些交易最终会以ZK-Rollup发到链上。| |链上撤单|链上撤单旨在增强DeGate协议的无需信任。对于还在有效期内的已取消订单,可以发起链上取消的请求,使该订单不会再被执行。| |Operator|Operator是出块节点的核心角色,负责交易排序、打包和出块,并调用其他模块完成ZK-Rollup流程。| |Postman|Postman负责调用DeGate智能合约,将calldata传到智能合约,完成数据上链。| |付费入账|付费入账是一种仅在用户用完免费充值额度(由DeGate协议补贴)时才会发生的动作,此时用户需要支付矿工费来完成充值请求。| |关停模式|DeGate可以主动激活关停模式,将所有资产退回给用户。| |手续费|每次成交时都会收取手续费,由Taker全额支付。 一个订单如果产生多次成交,会收取多笔手续费。 手续费是DeGate协议的主要收入来源。| |转账|转账功能实现了2个DeGate账户之间的资产转账。| |提现|提现功能允许将资产从用户的DeGate账户转移回钱包地址。| |ZKP-Worker|ZKP-Worker根据Operator传入的数据,通过电路来生成零知识证明。| # 文档目的 介绍合约的功能设计和具体实现。 # 合约功能概述 DeGate合约负责处理出块节点提交上链的zkBlock区块,并使用***zk-SNARKs***零知识证明来验证zkBlock数据的正确性。zkBlock验证通过后,合约会进行状态的变更。 普通用户通过充值/提现/代币注册等接口,和合约进行交互。 ## zkBlock的处理和验证 Postman账户调用合约的submitBlocks接口提交zkBlock, 合约需要处理和验证如下信息 ### 验证完整树与资产树 DeGate的系统中,维护了用户完整信息的完整树和资产信息的资产树。zkBlock的header保存了父区块和当前区块的两棵树的根。***合约通过验证父区块的根和合约中当前保存的根是一致的,从而可以更新到当前区块的根。*** 资产树的另一个作用是保证系统的trustless,在逃离模式下, 用户可以提交默克尔证明来和资产树对比验证,直接从一层提现。 ### 验证时间戳 zkBlock的header中包含了一个电路时间戳,这个时间戳作为电路的输入,并参与到电路中交易的验证(交易是否超时),电路无法验证这个时间戳的正确性。DeGate系统要求Operator将电路时间戳作为zkBlock区块的一部分,在合约中进行验证。 合约会对比zkBlock上链时一层的时间戳(TimestampL1)和zkBlock中的电路时间戳,进行校验,确保电路时间戳在有效范围内。 电路时间戳有效范围 (TIMESTAMP_HALF_WINDOW_SIZE_IN_SECONDS 配置为7days): [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(verifying key)。这两种key,分别用来生成proof和验证proof。DeGate是如何生成零知识证明key的,参考Trusted-Setup的说明文档。 在出块节点侧,使用电路程序和证明key来生成zkBlock对应的proof。 在合约侧,合约根据zkBlock的size信息,选择对应的验证key来验证proof的正确性。 不同的zkBlock的size,会对应不同的零知识证明key。DeGate合约在部署时,会配置一组不同size对应的验证key。size的选择,参考 电路设计文档-zkBlock大小 验证key通过硬编码的方式注册到合约中。 每一对零知识证明key(证明key/验证key)唯一对应一个电路逻辑,更换key即更换电路逻辑。为了确保系统完全地trustless,代码逻辑不允许更改,所以不允许更新和添加验证key。 ### 验证零知识证明 zkBlock包含零知识证明,合约使用硬编码的验证key,来验证证明的正确性。 证明proof由出块节点生成,并作为zkBlock的一部分提交上链。 DeGate采用***zk-SNARKs***协议的Groth16算法来实现零知识证明,Groth16采用ALT_BN128曲线。 以太坊在EIP196和EIP197中添加了这条曲线上计算的预编译指令, ecAdd/ecMul/ecPairing, 可以便宜高效的执行验证。 验证的输入参数,包括验证key、证明proof和PublicInputDataHash, 其中PublicInputDataHash是zkBlock数据的哈希。验证过程中, 根据输入的参数,构建一组pair。执行配对检查函数ecPairing,确认pair正确。 在DeGate合约的实现中,使用了***ethsnarks***的Verifier合约,进行零知识证明验证。 ## 充值功能 用户可以使用如下方式进行代币充值。 ### 标准充值 支持白名单币种。 通过直接转账代币到DeGate合约的方式进行充值,也被称为标准充值。相比合约接口充值,用户不需要发送授权交易。 合约侧为了验证这类充值,合约中未确认所有权的代币余额需要大于充值金额。出块节点的Operator账户有权使用这部分余额,充值给任意账户。在合约中,这部分余额可以通过getUnconfirmedBalance接口查询。 ### 高级充值 支持任意币种。 通过调用DeGate合约的deposit接口,进行充值。如果存入ERC20代币,用户需要先发送交易到ERC20代币合约,授权一定额度给DeGate合约。 出块节点需要在 MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE(15 days)内, 处理用户的充值。否则,用户可以调用withdrawFromDepositRequest 直接从合约中提取充值金额。 ### 充值手续费 正常情况下,充值均免费。如果短时间内充值交易的笔数过多,则对充值进行收费。 免费可用的充值交易笔数,可以通过getFreeDepositRemained 接口查询。(默认配置,免费可用5000笔,上限5000笔,同时每个区块恢复2笔) 对于收费的合约充值,用户需要在发起调用deposit的交易里转入一定的ETH(默认配置0.01ETH),才会被处理,否则直接拒绝。 ### ***用户充值流程*** 用户链上发起一笔充值交易,交易入块并通过区块确认后,DeGate节点会把充值数量计入账户可用余额,用户就能立即使用这笔资产。同时,Operator会发起一笔确认充值的链下交易,经过打包、出块、生成证明流程、更新默克尔树资产一系列操作后,最终rollup到链上,完成充值。 充值流程和方法对比,详情参考DeGate产品功能文档 https://docs.degate.com/v/product_zh/main-features/deposit ## 提现功能 用户可以通过如下方式进行提现。 ### 普通提现 用户通过发送签名到DeGate服务端的方式,进行普通提现,而不需要调用合约接口。 这部分提现,在出块节点的Postman账户提交区块上链时,进行处理。 ### 强制提现 强制提现支持用户强制出块节点允许其提取对应账户下的余额,调用forceWithdraw合约接口。 出块节点必须在规定时间MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE(15 days)内上链处理。否则一旦超时,任何人可以调用合约notifyForcedRequestTooOld接口,使得DeGate进入逃离模式。 强制提现需要用户支付一定的ETH手续费(默认配置0.01ETH,最大0.25ETH)。 强制提现要求用户提供账户地址,代币地址,和账户对应的AccountID。数据由用户提供,因此存在输入参数不匹配的可能。由于合约并不记录用户的账户与余额等信息,需要由出块节点的Postman账户将数据和证明提交到链上,并由合约删除错误的强制提现记录。 为了防止恶意用户发送过多强制提现交易攻击DeGate系统, 合约中设置了最大可用笔数MAX_OPEN_FORCED_REQUESTS(1000000笔), 定义了排队处理中的最大强制提现笔数。 ***如果强制提现交易过多,DeGate系统将来不及处理,被迫进入逃离模式。1000000笔的设定,是评估DeGate处理能力后的合理设置。 针对恶意用户消耗过多笔数,导致普通用户无法使用的情况,DeGate系统会根据实际情况,调整合理的强制提现费用来增加攻击者成本,使得攻击在经济上不可行。*** ### 逃离模式提现 进入逃离模式后,DeGate交易所的基本功能都会停止,出块节点也不能再提交区块。 用户只能够基于最后一个区块的状态提取资金。需要调用合约的`withdrawFromMerkleTree`接口,提交默克尔证明进行提现,一次只能提取一个币种的所有余额。 默克尔证明包含了用户某个币种的资产树的枝杈树信息,合约通过比较计算出的资产树和最新状态的资产树,来验证交易正确。 ### ***用户提现流程*** 用户可随时发出提现链下请求,从DeGate合约中取回资产。用户签名发起提现请求,节点验证签名通过后,先从该资产的可用余额中锁定需要提现的部分数量,然后交给Operator进行rollup操作。当包含了提现请求的rollup交易入块后,用户会收到提现的资产。假如节点拒接处理提现申请,用户可通过强制提现和逃离模式来取回资产。 提现流程和方法对比,详情参考DeGate产品功能文档 https://docs.degate.com/v/product_zh/main-features/withdrawal ## 代币注册 在DeGate中使用代币之前,需要进行注册。任何人都可以调用registerToken接口,进行代币注册。 合约会确保代币只能注册一次,ETH和DG代币在合约部署时注册。 在代币充值时,如果代币没有注册,合约会进行自动注册。 ID为0到31的代币由DeGate交易所预留,只能由交易所admin进行注册。这些币种称之为BindToken,聚合交易时费用更低。 ## ***协议费用*** 使用DeGate时,根据不同场景的操作,用户可能需要支付不同的费用。 操作请求与费用的说明,参考DeGate产品功能文档 https://docs.degate.com/v/product_zh/concepts/protocol-fees # 合约的实现 ## 合约实现概述 合约的实现包括如下几个模块 * Exchange合约,处理zkBlock和充值提现请求等。 * Deposit合约,托管用户资产,提供代币的转入转出功能。 * Loopring合约,Exchange合约参数配置。 * BlockVerifier合约,电路零知识证明key注册和proof验证功能。 ## Exchange合约 ### zkBlock数据定义 出块节点Postman账户通过调用 submitBlocks 接口提交zkBlock上链。 ``` function submitBlocks(Block[] zkBlocks) ``` #### 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包含的交易数量,数量范围是 [320, 170, 85, 42, 20, 10, 5] * blockVersion,zkBlock的版本号,默认为0。 * data, 是zkBlock的具体数据,也被称为PublicInputData,data执行sha256哈希后生成的PublicInputDataHash作为input输入给电路,来生成证明proof。data由BlockHeader和交易的PublicInputData数据两部分组成。 * 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, exchange合约地址。 * merkleRootBefore, zkBlock出块前,完整树根哈希 * merkleRootafter, zkBlock出块后,完整树根哈希 * merkleAssetRootBefore, zkBlock出块前,资产树根哈希 * merkleAssetRootAfter, zkBlock出块后,资产树根哈希 * timestamp, 电路输入的时间戳 * protocolFeeBips, 现货交易最大手续费,所有现货交易的手续费收费比例均不能大于此费率. 费率的单位为万分之一,比如18的话,就是万分之18. * numConditionalTransactions, 合约需要处理的交易数量, 即 zkBlock中充值交易的数量 + zkBlock中账户更新交易的数量 + zkBlock中提现交易的数量。 * operatorAccountID, 出块账号的AccountID * depositSize, zkBlock中充值交易的数量 * accountUpdateSize, zkBlock中账户更新交易的数量 * withdrawSize, zkBlock中提现交易的数量 #### 3. zkBlock的data中的PublicInputData定义 交易的PublicInputData数据, 是合约需要处理的交易(Conditional交易)的序列化汇总。需要按照约定的数量和顺序,进行解析处理。 * BlockHeader中的depositSize/accountUpdateSize/withdrawSize 分别定义了这3种交易的数量 * 交易按照类型排列,Deposit/AccountUpdate排在最前面,Withdraw排在最后。按照顺序和交易的数量信息,合约可以推导出交易的类型。 [Deposit transactions][AccountUpdate transactions][…others…][Withdraw transactions] * 每个交易的PublicInputData为83 bytes,具体定义参考各交易的PublicInputData部分。 ### Conditional交易说明 需要在合约中处理的交易,比如充值(Depoist),提现(Withdraw)和账户更新(AccountUpdate)。这部分交易被称为conditional交易。 每个交易的数据,包括PublicInputData和Auxdata两部分。 #### PublicInputData数据 每个交易的PublicInputData数据固定为83 bytes,不足部分用0 bytes填充。 为了节省gas,PublicInputData切分为80 bytes(part1)和3 bytes(part2)两部分。在Block的data中,所有交易的part1部分聚合之后放到前面,part2部分聚合之后放到后面。 因此,读取交易的PublicInputData数据,需要分别读取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交易的PublicInputData定义 ``` - Type: 1 byte (0 for Advanced Deposit, 1 for Standard 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类型。 合约根据交易的PublicInputData定义decode出交易的具体字段,并根据Type 分别处理合约充值和转账充值。 合约充值处理: 1. 未处理的充值请求pendingDeposit 存在 2. 未处理的amount数量大于或等于充值的amount数量。 3. 如果充值请求没有扣除所有的pending余额,则pendingDeposit 需要更新余额并保留。否则的话,则会删除 pendingDeposit请求。 4. 未处理的充值的数量, 可以通过 getPendingDepositAmount 接口查询。 转账充值处理: 1. 未确认所有权的代币余额unconfirmedBalance 需要大于充值的amount数量。 2. 直接转账充值,合约中无法记录发起充值的用户,因此合约中只需确认数量合法。 3. 未确认所有权的代币余额可以通过getUnconfirmedBalance接口查询。 ### AccountUpdate交易定义和处理 AccountUpdate允许用户添加更新资产公钥。 AccountUpdate交易的PublicInputData定义 ``` - 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,交易的有效期,用户签名的一部分。 合约根据交易的PublicInputData定义和AuxData定义,decode出交易的具体字段。 合约处理流程: 1. 验证交易的有效期validUntil,电路时间戳需要小于validUntil。 2. 实际支付fee必须小于等于maxFee。 3. 验证用户签名signature正确。 ### Withdraw交易定义和处理 Withdraw交易允许用户将资产从L2提现到一层。 Withdraw交易的PublicInputData定义 ``` - 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。 由于83bytes的限制,所以这3个字段没有放到publicInput中(而是以hash的方式),最终通过auxData的方式去传递给合约。合约端要去验证这3个字段的hash与OnchainDataHash相等。 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提币数量。 合约根据交易的PublicInputData定义和AuxData定义,decode出交易的具体字段。 合约处理流程: 1. 验证onchainDataHash正确. ``` bytes20 onchainDataHash = bytes20( abi.encodePacked( minGas, to, amount ).fastSHA256() ); ``` 2. 验证withdraw交易类型 - type=0:EdDSA签名模式。合约不验证。 - type=1:ECDSA签名模式。合约验证: - 验证交易的有效期validUntil,电路时间戳需要小于validUntil。 - fee 小于或者等于 maxFee - 验证用户签名signature正确 - type=2:有效的强制提现。合约验证: - withdraw的from和to必须相等。***from是发起强制提现交易到链上的地址,to是Auxdata中的to地址。强制提现交易只能提现给from地址,因此from和to地址需要一致。*** - fee 必须是0 - forcedWithdrawal请求存在,并且请求的owner和from地址一致 - forcedWithdrawal请求不存在,必须是关停模式,operator替用户强制提现 - type=3:无效的强制提现,***无效是指用户输入了错误的账户ID进行强制提现***。 - withdraw的from和to必须相等。***from是发起强制提现交易到链上的地址,to是Auxdata中的to地址。强制提现交易只能提现给from地址,因此from和to地址需要一致。*** - fee 必须是0 - forcedWithdrawal的请求存在,并且请求的owner和from地址不一致 - amount必须是0。不处理提币,只清理合约中的forcedWithdrawal无效请求 3. 处理提币 - 合约验证auxData中gasLimit需要大于或者等于minGas。 - 调用deposit合约的提币接口,尝试向用户地址转币。 - 允许提币接口执行失败,而withdraw交易成功,不影响submitBlocks的交易上链。如果提币失败的话,则将提币请求记录到amountWithdrawable表中。用户可以调用withdrawFromApprovedWithdrawals直接从一层提币。 withdrawFromApprovedWithdrawals提币接口,允许用户手动提币,处理withdraw交易中提币失败的情况。 ``` function withdrawFromApprovedWithdrawals(address[] owners, address[] tokens) ``` 合约处理流程: 1. 任何人可以调用,并传递一组用户地址owners 和一组代币地址tokens做为输入参数 2. 合约按照参数,依次处理,检查amountWithdrawable表中存在owner和token的提币记录 3. 根据amountWithdrawable表中记录的余额,向用户地址转币 4. 删除amountWithdrawable表中的提币记录 ### 交易最大手续费的校验和更新 不同的交易对,存在不同的交易手续费,而电路并不约束手续费的具体数值,这部分校验由合约执行。 在出块节点Postman账户提交zkBlock时,会验证和更新交易最大手续费。 - 校验机制 每个提交的zkBlock的BlockHeader中,protocolFeeBips就是现货交易最大手续费。电路中,会约束该zkBlock中的所有现货交易的手续费均不大于此费率。 合约中,会比较BlockHeader中的protocolFeeBips和合约中设置的费率一致,否则上链失败。 - 延时更新 合约中设置的费率采用延时更新的机制进行修改,延时为MIN_AGE_PROTOCOL_FEES_UNTIL_UPDATED(7天)。 延时更新, 需要admin调用Loopring合约updateProtocolFeeSettings接口设置需要改的数值。 延时期限过后,在提交zkBlock时会调用validateAndSyncProtocolFees进行更新。 ### zkBlock上链接口实现 Postman通过调用 submitBlocks 接口提交zkBlock上链。 ``` function submitBlocks(Block[] zkBlocks) ``` 合约处理流程: 1. 验证zkBlock中完整树与资产树root正确 2. 验证zkBlock中电路时间戳合法 3. 处理Conditional交易 4. 交易最大手续费的校验和更新 5. 调用BlockVerifier合约的verifyProofs接口,验证零知识证明 ### 合约充值接口实现 合约充值接口,允许用户发起合约充值请求 ``` function deposit(address from,address to,address tokenAddress,uint248 amount, bytes extraData) ``` 1. 检查token地址,如果没有注册,调用registerToken进行注册。 2. 检查是否需要对充值进行收费。 如果需要,从转入的eth中扣除手续费depositFeeETH。如果eth不足,则充值交易失败。 3. 调用Deposit合约的deposit接口执行充值转币流程 4. 记录用户的充值请求到 pendingDeposits列表, 用户的相同币种的多笔充值会合并到一条充值请求记录。 用户的充值,需要等待DeGate构建Deposit交易并提交上链后,才能够完成。 如果Operator一直没有处理,则用户可以调用withdrawFromDepositRequest 直接从合约中提取充值金额。 withdrawFromDepositRequest提币接口 ``` function withdrawFromDepositRequest(address owner, address token) ``` 合约处理流程: 1. 任何人可以调用,传递用户地址owner和代币地址token作为参数 2. 检查pendingDeposits列表中存在充值请求记录 3. 检查充值请求超时没有处理(大于 MAX_AGE_DEPOSIT_UNTIL_WITHDRAWABLE), 或者进入了逃离模式 4. 根据pendingDeposits表中记录的余额,向用户地址转币 5. 删除充值请求记录 ### 强制提现接口实现 强制提现接口,允许用户发起强制提现请求 ``` function forceWithdraw(address from, address token, uint32 accountID) ``` 1. 检查强制提现存在可用的笔数 2. 检查转入的eth足够支付强制提现手续费 3. 记录用户的强制提现请求到pendingForcedWithdrawals列表 用户的强制提现,需要等待DeGate构建Withdraw交易并提交上链后,才能够完成。 如果Operator一直没有处理,则用户可以触发交易所进入逃离模式。 ### 逃离模式实现 #### 逃离模式的触发接口 ``` function notifyForcedRequestTooOld(uint32 accountID, address token) ``` 1. 检查accountID和token对应的强制提现请求存在 2. 检查该请求已经超时,MAX_AGE_FORCED_REQUEST_UNTIL_WITHDRAW_MODE(15 days) 3. 设置逃离模式启动时间为当前一层的时间戳。 4. 可以调用isInWithdrawalMode查询,当前是否是逃离模式 (withdrawalModeStartTime > 0) #### 逃离模式的提现接口 ``` function withdrawFromMerkleTree(MerkleProof merkleProof) ``` 1. 检查必须是逃离模式 2. 每个用户的每个币种,只允许处理一次提现 3. 验证默克尔证明正确 4. 处理提币,提现的币种会全额提现,并标记该币种已经处理。 5. withdrawFromMerkleTree的开销,约为300w gas。 #### 默克尔证明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, 从叶子节点到 资产树的根节点的proof。这是一个4叉树,一共是3*16层 = 48个。account根节点,即资产树的根。 - balanceMerkleProof, 从叶子节点到余额子树的根节点的proof。这是一个4叉树, 一共是3*16层 = 48个。balance根节点,是账户叶子节点的一部分,参与账户节点的计算。 MerkleProof的验证 ``` function verifyAccountBalance(uint merkleRoot, MerkleProof merkleProof) ``` 1. 计算代币叶子节点哈希, hash(balance) 2. 将balanceMerkleProof按照顺序分为每组3个,一共16组(层)。从最底层,使用计算出的叶子节点和每组的3个节点,计算出上一层的根节点。依次递归,从而计算出余额子树的根节点 balancesRoot 3. 计算账户叶子节点哈希, hash(owner, pubKeyX, pubKeyY, nonce, balancesRoot) 4. 将accountMerkleProof按照顺序分为每组3个,一共16组(层)。从最底层,使用计算出的叶子节点和每组的3个节点,计算出上一层的根节点。依次递归,从而计算出资产树的根节点 accountsRoot 5. 验证计算出的accountsRoot和合约中存储的资产树的根是一致的。 ### 关停模式实现 交易所admin可以选择在任何时候,调用shutdown 进入关停模式。 即使在shutdown模式,用户仍可以通过普通提现的方式来取走他的资金, Operator也可以正常出块。 进入shutdown模式后,如果用户没有进行withdraw操作,Operator有权限替代用户发起forceWithdraw,并将资金提取到用户的一层地址。 在这个模式,Operator只会处理withdraw交易,而不会再处理其他类型交易。 关停模式的触发接口 ``` function shutdown() ``` 1. 如果交易所是逃离模式,则无法进入关停模式 2. 设置关停模式启动时间为当前一层的时间戳。 3. 可以调用isShutdown 查询,当前是否是关停模式((shutdownModeStartTime > 0) ## Deposit合约 Deposit合约是存储用户代币的合约,Exchange合约调用Deposit合约进行充值和提现。Deposit合约,只提供接口来转账ETH和ERC20,确保用户资金安全。 ### 充提接口 ``` function deposit(address from, address token, uint248 amount) ``` 合约处理流程: 1. 只能由Exchange合约调用 2. 充值ETH,检查转入数额大于等于amount,多的部分退款。 3. 充值ERC20,需要调用TransferFrom将代币从用户账户转入到Deposit合约,因此用户需要预先授权。 4. 转账充值, 是直接转账到Deposit合约,而不会调用到deposit接口。 ``` function withdraw(address to, address token, uint amount) ``` 合约处理流程: 1. 只能由Exchange合约调用 2. 将代币转账给to地址。 ### 非标准代币 余额会自动通胀/通缩的rebase类型币种(比如ampl),DeGate不支持。 余额在转账时变化的burn类型币种(比如cult DAO),DeGate 默认情况不支持, 但可以通过添加白名单的方式来支持。 白名单接口 ``` function setCheckBalance(address token, bool checkBalance) ``` 合约处理流程: 1. 添加token到白名单列表 2. 充值时,需要计算合约实际的余额变化,来确定充值金额。 ## Loopring合约 Loopring合约, 用来配置强制提现手续费和交易最大手续费。这是沿用的路印合约模块,用来管理多个Exchanges和参数配置。在DeGate中只会有一个Exchange合约。 强制提现手续费接口 ``` function updateSettings (uint forcedWithdrawalFee) ``` 1. 检查手续费不能大于MAX_FORCED_WITHDRAWAL_FEE (0.25 ETH) 交易最大手续费接口 ``` function updateProtocolFeeSettings(uint8 protocolFeeBips) ``` 1. 交易最大手续费延时(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(uint8 blockType, uint16 blockSize, uint8 blockVersion, uint[] publicInputs, uint[] 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 - 单个proof,调用[Verifier合约](https://github.com/HarryR/ethsnarks/blob/master/contracts/Verifier.sol)验证。 * 批量proof,调用[BatchVerifier合约](https://github.com/matter-labs-archive/Groth16BatchVerifier/blob/master/BatchedSnarkVerifier/contracts/BatchVerifier.sol)验证。 3. 验证的gas开销 * 单个proof,由于批量验证有固定的开销,使用Verifier合约单个验证更高效。 * 多个proof, 使用批量验证更为高效。 * 单个proof的证明消耗 大约18.7w gas,批量验证的开销 与proof的数量n成正比, 大约 4.6w*n + 15.3w gas。 ## 合约的Trustless说明 用户可以无需信任的和DeGate合约交互,合约的Trustless由以下几点保证 1. 不允许更新合约代码: DeGate合约没有使用proxy更新模式,部署后不允许更新合约代码。 2. 不允许更新电路代码: BlockVerifier合约中,不允许新增和修改电路验证key。 3. 用户可以使用强制提现,来约束Operator处理自己的提现交易或者促使交易所进入逃离模式。 4. 用户可以通过爬块获取所有的calldata 并基于这些calldata可以恢复出完整的资产树 5. 逃离模式下,用户可以使用资产树的节点哈希信息提交默克尔证明,直接从一层自主提现。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully