因为它,中心化交易所要慌(黄)了吗?
作者 | Gnosis
译者 | 臣天
自门头沟事件以来,中心化交易所屡屡出现安全问题,造成大量金钱丢失,由于区块链技术的去中心化特征,去中心化交易所大势所趋,被认为更加安全。
Snark 是以太坊下一个侧链解决方案,由以太坊研究者 Barry Whitehat、Harry R、Alex Gluchowski 等人共同提出,理论上可以使以太坊网络实现 17000TPS 的吞吐量。
本文我们将着重介绍基于 Snark 的去中心化交易所 dFusion。基于代码,它真的可以实现上万TPS吞吐量吗?如果实现,能否真正干掉中心化交易所?
跟随作者,一探究竟!
dFusion是一个完全去中心化的交易所,基于Snark方案,能后将网络交易能力大大扩展。这项扩展技术使得信息通过Snark技术后仅存储在默克尔树根,并且只能通过预先留下的逻辑门CP进行操作与处理,从而加快了交易速度。
为了允许更多的Snark参数,我们计划采用DIZK(分发零知识证明系统)中的想法。对于批量交易的处理,我们采用了Gnosis开发的无套利价格清算技术。下面让我们详细地去了解一下其架构。
现实中大多数交易所并不是去中心化的,交易所收下用户的币和钱,将数额记录在用户的账户上,交易只是双方账户数字上的增减,记录在交易所的数据库里,不写在区块上。在我们设想的交易所中,用户通过N种预定义的ERC20代币(代表N种数字货币)进行限价交易。
对于每种代币,都会有一棵默克尔树作为交易集与之对应,默克尔树根的散列值为:balanceTRH_I (for 0<I<=N)。在每棵默克尔树中,每一个叶子节点只会存储某个用户的单一类代币余额。
交易所账户的地址和交易轮次信息存储在另一颗默克尔树中,这棵交易树根散列值为accountsRH。
balanceTRH_Token与accountsRH是一一对应的,balanceTRH_Token中第i个叶子节点的余额属于accountsRH中第i个叶子节点的账户。
所有的交易都被编码为以下交易格式:
(accountLeafIndex,fromTokenIndex,toTokenIndex,limitPrice,amount,nounce,signature)。
交易的处理逻辑为:
叶子索引为 accountLeafIndex 的用户,希望从 fromTokenIndex 账户中的代币向 toTokenIndex 账户转移 amount 的代币,当且仅当 amount < limitPrice 时执行。
对于所有树根的散列值[accountsRH, balanceRH_1, ..., balanceRH_N]将会被合并后一起散列。balance树按默克尔树规则散列出balanceRH_,再与accountsRH合并处理,得到最终散列值stateTRH。最终散列值“stateTRH”存储在“锚定的”智能合约链上,合约将保存一切与交易相关的信息。
存储系统架构
交易的工作流如下:
1. 获取交易列表(获得交易通过后所得散列值)
2. 过渡处理:将SHA散列值转换为Pederson散列值
3. 挖矿:寻找最优清算价格
4. 上链:更新余额
5. 处理待处理的存取操作
6. 从第一步重新开始下一轮交易
我们将对工作流逐一介绍:
1、获取交易信息
通过调用以太坊上智能合约编程内置的Kecca256函数:
这个函数能够轻松地更新orderHashSHA变量,这个变量随着新交易的产生而改变。这个函数是public的,可以被任一方调用,从而达到去中心化的目的。按照我们的预计,“执行者” 会将所有用户的交易捆绑到一起,然后再一同进行散列。因为是在Snark侧链上操作,所有交易只会作为事件来进行发送和请求,并不会存储到以太坊虚拟机中。
2、过渡处理
在上一步中,我们利用SHA算法对交易进行散列处理。这是因为在EVM中SHA算法是十分“廉价”的(消耗gas量小),而在Snark中,SHA算法十分昂贵。为此,我们必须更改计算方法,将原hash转化为Pederson散列值。
我们利用Snark做以下的工作:
Snark – TransitionHashes & Validation snark 将执行以下操作:
通过比较Private input所有交易的SHA散列值与原public input是否一致来判断Private input是否合法,不合法则终止
重新迭代所有交易,并按顺序进行Pederson散列,将散列值作为输出
请注意,我们允许零元交易(交易不携带发起人的代币),这些订单将在结算后进行整理并上链。
在智能合约中,我们提供了以下功能:
任何人都可以通过提供代币所有权与签名来提出更新状态机的请求:
如果发现更新请求的信息不正确,任何人都有权利通过提供代币所有权与签名发送“质询请求”:
在默认情况下,除非最终解决方案者在规定时间内提交正确的Snark证明,任何有签名的“质询请求”都是合法的,并一定会在有限时间内执行。
通过newState码,我们可以在EVM中对证明进行验证,使用以下函数,当且仅当证明是有效的时候,系统会更新这个默克尔树根:
3、挖矿:寻找最优清算价格
在上一步之后,这一轮次的交易已经整理完毕。现在我们需要用一种方式去选出这一轮交易的记录者,这种方法就是通过计算统一清算价格选出。
统一清算价格是指能够最大化交易对之间的交易盈余的一个数字(交易盈余=(限价价格 - 统一清算价格)x订单量),这一步类似于公链上挖矿的过程。这种计算是一个NP-hard问题,短时间内很难找到最优解。
经过测试,我们认为3-10分钟是比较合理的选择。虽然找到的解有可能并不是全局最优解,但每个人都是公平地去计算并提交自己的最佳解决方案,程序仍然保证了公平与去中心化。程序最后会选择最大化交易盈余的清算价格作为最终解决方案。
这种算法意味着统一清算价格是以一种去中心化的方式计算出的,每个解决方案提交到合约时都会伴有提交者的签名。若一位提交者被选中,他还必须在下一个步中进行更新余额信息服务,并且需要回应来自任何人的质询请求。
4、上链:更新余额
在竞价期后,合约会选出所有提交解决方案中的最优解,此解决方案的提交者(上文的最终解决方案者,下文中通过用户A代替)需要执行两个步骤:
a) 将整个解决方案发布至以太坊公链上,整个解决方案包含一个价格向量P,一个新的balanceRootHash,还有每个交易的交易盈余(VV)向量;
注:P只是所有代币相对于参考代币Token_1的价格向量,由于没有套汇的存在,Token_i:Token_k的值等同于(Token_i:Token_1):( Token_1:Token_k)。
b) 在实际中,并非所有低于限价的交易都能被成功上链。有些时候发起交易的账户并没有足够的余额去支付。我们将这些交易称为”uncovered orders",这些交易需要被挑出并排除。
因此,用户A需要给每个交易都分一小部分交易盈余作为补偿。
这两部分构成了解决方案:VV和P作为合约数据的有效载体,记录交易数据,再一同通过SHA算法散列到hashBatchInfo中。
到此为止,上链的部分告一段落了。
现在可以开启检查通道了,如上文所说网络中每个人都可以去检查这个解决方案是否合法,如果非法,每个人都可以发起质询请求,当用户A面对质询请求时,他必须提交正确的Snark证明自己的行为合法。
需要进行以下内容:
priceMatrix/orderVolume:hashBatchInfo通过SHA算法散列出的值;
检查向量[balanceTRH_i for 0<i<=N]散列值是否为balanceRH;
检查[VV]是否合法;
对于每一笔交易:
检查余额树种账户余额;检查账户权限;检查轮次是否有效并更新至最新状态;if FollowUpOrderOfAccount == 0;
检查余额是否为正(否则,检查与后续交易账户有关的其它交易的收款账户和其余额);
通过转移所有权来更新余额;检查每个代币的总销售盈余、总购买盈余、总销售量和总购买量是否正确;
对于每一种代币,检查出入量是否相等;
检查交易损差的计算值是否与交易总资产相同。
4、存取款操作处理
存取款的操作也需要按顺序记录在“balanceRH”中,我们再次使用Snarks侧链技术和“质询请求”的设计。
如果用户想要存款到公链上,可以通过执行以下代码实现:
有关存款操作的所有信息都会被存储在一个32位比特串depositHash中。每当有20个区块生成就会产生一个新的depositHash
通过调用以下函数,可以完成存款整合:
这个函数会通过整合blockNr至blockNr+19这20个块中的存款量来更新stateRH
和上文所说的相似,任何人都有权利检查stateRH是否正确更新,任何人也有权利对错误的行为提交“质询请求”。
这个存款的质询请求格式如下:
这个需要检查以下内容:
通过对[deposit information]队列进行散列得到depositHash,检查是否一致
对于每一笔存款:检查存款账户当前余额;检查deposit.sender是否为accountLeaf.address;更新余额;计算新的stateRH
取款的操作与存款相似,但也有不同之处。在取款中我们只需要小心一件事情:取款的函数一定要在交易所账本数目减少好再被调用,需要保证足够的处理与同步时间,否则很有可能遭到双重支付的问题。
下面是取款的智能合约:
网络中的任何人都可以调用以下函数来更新stateRH:
Function incorporateWithdrawals(uint blockNr, bytes32 oldBalanceHash, bytes32 newBalanceHash, bytes32 withdrawalAmounts)
和上面存款操作类似,连续20块的取款请求会放在一起进行处理。
通过oldBalanceHash和withdrawalAmounts(合法取款数量)来获取newBalanceHash。
取款的质询请求格式如下:
需要检查以下内容:
通过对[exitRequest informaiton]队列进行散列得到exitRequestHash,检查是否一致
对于每一笔取款:检查存款账户当前余额;检查deposit.sender是否为accountLeaf.address&&账户余额是否大于提取金额(更新余额、计算新的stateRH、withdrawalAmounts+=withdrawal.amount)
如果未收到质询请求,用户将会在一天后提款到账。withdrawalAmounts会被存储到余额树中withdrawalAmounts[blockNr]中。
5、系统可行性分析
这个系统受信息通过以太坊公链的燃料费与对Snark参数数量这两个因素限制:
a) 燃料费计算
对于每个交易 (accountLeafIndex, fromTokenIndex, toTokenIndex, limitprice, amount, signature),有以下规定:
最多在交易中使用2^6个不同的代币
最多有2^16个不同的子叶
价格被编码为64位浮点数(61位有效位数)
数字签名为一对(s,r,v),其中s和r大小与椭圆曲线素数相近,这代表(s,r)大致有512比特大小。综上,我很可以用三个bytes32来存储任何交易。
K个交易的燃料费计算公式为:
transaction initiation costs + k* order as payload costs + k* signature verification cost + k* hashing costs + updating the orderHashSHA+21000+ 21000+k*(6+16+16+64+64+512)*68/8+k*3000+k*60+5000
经过测试,1000个交易大概只需要880万gas来进行处理。
b) Snark参数计算
按照DIZK论文的架构,我们理论上可以达到数十亿的逻辑门运算能力,这种并行化的方法仅适用于特定的椭圆曲线,以太坊所有的alt-bn128曲线满足其条件。理论上可以达到2.6亿—26亿的逻辑门运算能力。
很明显,最大的成本来源于检查操作。我们通过散列频率来估计状态机的情况,按照网络的构造,这种估计所得值理论上合理。
按照Snark-applyAuction操作的计算,每个pedersonHash可以拥有1.1k个参数,也就是说,我们可以用1M大小的账户来处理大约5K比交易。
目前我们所能想到的最大的制约条件在于计算能力可能不足导致交易无法得到及时处理。
为了保证去中心化,我们还需要考虑价格操控的问题。
如果一笔有利润剩余的交易(低于限价)发生后,不法者通过增补价格来破坏市场的平衡,从而操控了代币之间的转换价格。在这种情况下人们无法提交自己的合法交易,从而导致交易网络瘫痪。不法者去可以通过恶意炒币来进行低买高卖,从中获利。
有两种方法可以防止这种情况发生:
对交易加密:将交易用分布式秘钥进行加密,当且仅当交易完成后才能解密,这样不法者就无法获得正常的市场价格,无法去操作币价。
将部分交易转换为期货:98%的交易用普通的方式进行处理,剩余2%与GNO / OWl或其它数字货币绑定,这样,不法者就更难去控制所有交易,从而更难去操控价格。
随着对数字货币感兴趣的人数增多以及数字货币的普及,去中心化的交易所是未来的交易所发展方向和趋势。希望在技术的进步之下,值得信任的,去中心化的交易网络能赶快到来。
原文链接:
https://github.com/gnosis/dex-research/blob/cc9cdb9ebed2d27732aa512bc649ebbffd5fed91/dFusion/dFusionSpec.md#finding-batch-price-optimization-of-batch-trading-surplus
--【完】--
公众号又又又改版了,为不错过最新技术干货和行业信息,建议你按照图片提示,将【区块链大本营】设为星标(安卓用户设为“置顶”),看大图更爽哟!
推荐阅读
大力戳↑↑↑ 加入区块链大本营读者⑦群
(内容转载请联系微信:171075719)
(商务合作请联系微信:fengyan-1101)