路印智能合约由一些列以太智能合约组成,这些智能合约实现了路印协议。本文档描述了路印协议提供的基本功能,功能目录信息如下所示:
实现上述功能的代码是开源的,你可以在 github 上获取源代码。
在本篇文档中,用符号 LSC
指代路印智能合约.读者如果想阅读更多关于路印协议的数学验证逻辑,可以查看白皮书 and 其它相关文档 {da447m@yahoo.com} 路印协议备注说明.
⚠️️ 注意:当前代码中对价格模型的实现方式与文档中描述的信息一致,但是手续费模型有所不同。
想要知道 LSC 能够做什么,我们首先需要学习一下订单是如何定义的、用户可执行的操作以及如何追踪当前订单状态.
订单是一组数据的集合,这些数据描述了用户在市场上的交易意图,为了保证数据信息的完整性(防止数据被篡改),使用用户的私钥信息对订单参数进行签名,这样可以对数据完整性进行校验。签名信息随着订单一起在网络上进行发布。这使得订单在整个交易的生命周期内需要保持一定程度上的不可变,以便验证发送方的地址信息。
签名方式如下公式所示:
Signature = ECDSA(SHA3(order_params))
即使订单数据保持不变,LSC 也能够识别出当前订单状态.
一个订单的相关参数如下所示(如果想一个订单的完整参数信息,可以查看项目源码):
参数 | 简介 |
---|---|
owner | 交易发起方地址 |
tokenS | 售出的代币种类 |
tokenB | 买入的代币种类 |
amountS | 售出的代币数量 |
amountB | 买入的代币数量 |
buyNoMoreThanAmountB | 描述见下文 |
ttl | 订单有效时间,单位秒 |
lrcFee | 用于支付手续费的 LRC 数量 |
marginSplitPercentage | 当实际兑换率小于当前兑换率时,矿工得到的利润分成比例 |
上述模型被称为 单向订单模型, 简称 UDOM. 如果想学习更多关于 UDOM 的信息, 可以查看 medium 发文.
订单的兑换率 r
计算方式如下所示 r = amountS/amountB
. 矿工对你的订单进行环路匹配时,有可能会找到一组匹配订单,使得交易兑换率小于订单原始兑换率,在这种情况下,你可以得到更多数量代币 tokenB,但是当上述参数列表中的 buyNoMoreThanAmountB
值为真时,LSC 将会确保你只得到确定数量 amountB
的代币 tokenB
。
例子:有这样一个订单,出售
amountS = 10
个数量代币 tokenS, 用于兑换amountB = 2
个代币 tokenB,订单兑换率r = 10/2 = 5
. 这个兑换率表示用5个代币tokenS 可以兑换一个tokenB
. 当矿工对你的订单进行环路匹配时,发现了兑换率为4的环路订单
,如果兑换amountS = 10
个数量的代币,那么实际得到2.5个代币tokenB, 而不是订单中给出的2个代币
,如果你只想逃 2 个代币 tokeB,可以设置订单参数buyNoMoreThanAmountB = true
. LSC 将会按照你的要求,只买入 2 个代币 tokenB,花费8 个代币tokenS,得到2个代币tokeS
,节约了订单成本2个代币tokenS
.请注意,上述的过程没有包含矿工手续费,手续费模型将在后续文章中进行详细阐述。
用户可以通过与 LSC 之间的交互来执行部分或者全部取消操作,交互数据包括,订单详情,需要取消的交易数量。LSC 得到上述数据扣将会存储取消的数量,然后发送一个 OrderCancelled
事件来通知相关角色(矿工、中继节点).
LSC 会存储用户对订单的操作数据,订单 hash 值为 key,key-value 形式进行存储,通过追踪订单中的数值信息可以计算出订单的状态信息,这些数据的都是公开的,不需要授权访问,任何人都可以访问,当数据发生变化时,对应的 OrderCancelled
/OrderFilled
事件将会发布到网络上。
这种数据可追踪机制有利于 LSC 的环路结算
这一章节将会介绍 LSC 期望接收的矿工数据,以及如何对这些数据进行验证。
LSC 期望从矿工那边接收有效的订单环路数据,订单环路由多个订单组成,前一个订单的买入代币是后一个订单的卖出,最后一个订单的买入是第一个订单的卖出,这样子就形成了一个订单环路,这个环路还必须满足实际兑换率小于等于订单原始兑换率,下图描述了订单环路结构
一个有效的订单环路,可以确保所有的订单都能够按照等于或者小于原始订单兑换率进行兑换,如果想验证订单环路是否有效,只需要验证所有实际兑换率的乘积是否大于等于 1.
例子:对上述图片中的订单环路进行校验.
0.2 * 14 * 0.5 = 1.4
, 计算结果大于 1,因此环路数据是有效的.对于上述说的有效订单环路的兑换率乘积大于等于 1 的结论很容易验证:Sax表示订单 x 卖出代币 a 的数量,Bbx 表示订单 x 买入代币 b 的数量,兑换率:Rx = Sax / Bbx, Orderx = {Sax, Bbx, Rx}. 假如存在两个订单,构成了订单环路 Orderx = {Sax, Bbx, Rx}、Ordery = {Sby, Bay, Ry}. 兑换率乘积:Rx * Ry ==> Sax / Bbx * Sby / Bay, 当乘积等于 1 时,表示订单 y 平价购买 订单 x,订单环路有效,当乘积小于 1 时,相当于在等于 1 的基础上两边乘以变量 θ ==> Sax / Bbx * Sby / Bay * θ = θ * 1, 当 θ < 1 时,上述乘积小于 1,而且 Sby / Bay * θ < Sby / Bay,可以理解为订单 y 想以低于平价的价格进行兑换,这对于订单 x 来说是不能接受的,因此订单环路将是无效的,如果 θ > 1 时, 订单 y 想以高于平价的价格进行兑换,这对订单 x 来说是可以接受的,因此订单环路是有效的。
LSC 不执行兑换率以及金额计算,但是必须对矿工提交的数据进行校验,这些工作由矿工处理,由两方面原因让矿工来处理:一方面合约开发语言 solidity 不支持浮点数计算,特别是 pow(x, 1/n)
,另一方面希望将这些工作转移到链下进行。
以下章节讨论如何对订单环路进行数学验证。在阅读下面这些章节以前,建议读者先阅读相关文档(在本篇文章开始时已经阐述了相关文档)
子环路校验可以杜绝平台无风险套利行为。 当矿工匹配到了有效的订单环路,矿工可以试图将另外一个订单放入到当前订单环路中,从而达到无风险套利。路印协议规定这是不公平行为。
下图描述上述阐述的环路嵌套订单的问题(在原先订单环路中嵌套订单 E,D,构成新的环路):
在路印协议中规定,一个有效的订单环路中不能包含子环路。对于订单环路是否存在子环路的验证方式极其简单:一个代币A 位于订单环路中买入和卖出位置的次数不能出现两次及以上
,在上图中,可以发现 ARK 在买入和卖出位置出现了两次,因此可以判断出存在子环路。
在一个交易事务中,矿工负责兑换率计算,此操作的原因在上述章节中已给出解释。因此 LSC 需要验证数据的正确性。
首先需要验证的是,矿工计算出的兑换率需要等于或者是小于用户设置的兑换率,这能保证用户的利益,意味着用户得到的兑换率至少是他设置或者是更好的。
为了保证公平公正,当实际兑换率确定之后,我们需要保证每个参与订单环路匹配的订单获得的优惠折扣是一致的。
订单按比例缩放的数量由以下因素决定:订单兑换以及取消数量,当前参与兑换账户的余额数据。
依据上述特性来查找订单环路最小兑换数量(没个订单兑换的代币数量有所不同,需要找出最小兑换数量),然后以此为参考计算出所有交易订单的兑换比例。
例子:查找后发现最小兑换数量只是原来订单兑换数量的 5%,那么订单环路中的订单都需要按比例缩小到原先的 5%。当交易完成后,至少有一个订单已经完成交易(拥有最小兑换数量的订单)
当订单环路通过了上述校验时,表明订单环路可以执行结算。
LSC 调用 TokenTransferDelegate
智能会约执行交易操作,引入这样的委托模式,将会使得未来升级智能合同变得很容易,这种模式下,所有订单只需要授权这个委托而不依赖于不同版本的协议。
对于订单环路中的订单,代币 tokenS 用于支付给订单环路中的先前订单.矿工的手续费收取情况取决于矿工选择的手续费模型,如果矿工选择了路印作为手续费,那么订单交易之后剩余的代币将会返回给订单发起账户。最后将会触发一个 OrderFilled
事件.
当环路中所有订单完成交易之后,将会触发一个 RingMined
事件
这一章节将会介绍路印当前手续费模型,如果想了解更多关于手续费模型的知识,建议阅读 Daniel 的 medium 文章。
在当前手续费模型中,矿工有两种可选的手续费方案。当用户创建订单时,可以设置一定数量的 LRC 作为矿工的手续费,同时也可以设置利润百分比作为矿工的手续费。上述利润百分比称作利润分割,矿工可以选择其中一种方案作为他的手续费。
下图描述了利润分割:
当矿工发现订单环路的利润太少时,可以选择 LRC 作为其手续费,当订单环路的利润与设置的路印价值相同或者更高时,将会选择利润分割作为手续费。
当选择利润分割时将会出现这样的情形,矿工需要支付一定数量的手续费用于支付订单创建者支付利润分割时创建的交易的网络手续费(有点绕,其实就是用户要将利润支付给矿工时,需要创建一个网络转账交易,这个交易需要网络确认手续费,这笔手续费需要矿工支付),由于这笔费用的关系,将会导致用户优先选择 LRC,当利润分割价值是 LRC 的两倍或者更多时,才会选择利润分割。
从矿工的角度来看,这能保护矿工,让他在低利润的订单环路中也能获得稳定的收益,同时在高利润的订单环路中获得更高的收益。随着市场的发展,交易模式将会日益成熟,高利润订单环路将会日益减少,趋于稳定,路印的手续费模型就是基于这种情形进行设计的。
接下来讨论一下利润分割以及 LRC 手续费:
- f 表示 LRC 费用
- x 表示利润分割
- y 表示矿工收入
假设 f 为 LRC 费用,x 是利润分割,y 时矿工收入,y=max(f, x-f),用蓝色线条表示。
当设置 LRC 费用为 0 时,等式 y = max(0, x- 0),简化后为 y=x,用黄色线条表示。
因此有以下结论:
- 当利润分割为 0 时,矿工可以得到一定数量的 LRC 作为手续费,这是稳定的收入,因此有一定的激励效果。
- 当 LRC 费用为 0 时,黄色线条显示收入与利润分割相关,这将激励矿工寻找最优的订单环路。
- 当利润分割的价值是 LRC 费用的两倍时,矿工才会选择利润分割作为手续费。
值得注意的是,当 LRC 费用为非 0 时,无论矿工选择什么哪种手续费方案,创建订单用户与矿工之间都会存在一笔转账交易,要么将剩余的 LRC 费用发送会用户地址,要么支付一定数量的 LRC 费用,以便获得利润分割。
当前的手续费模型仍处于草案阶段,社区人员可以参与讨论,提出建设性意见,社区人员可以加入 slack 参与讨论,也可以在 LIPs 提出自己的意见。
在本篇文章中,你应该接触到了一系列 LSC 发出的事件。中继器或者是订单浏览器以及其它组件通过接收事件获得信息来更新他们的订单数据库信息。
有以下事件:OrderCancelled
OrderFilled
RingMined
攻击者可以监控网络上没有确认的订单环路数据,然后攻击者使用自己的数据进行签名,广播到网络中。为了防止撮合窃取,路印允许矿工分步提交撮合数据
- 首先提交撮合数据 HASH 值,然后等待确认
- 确认之后提交撮合数据
这种保护机制有明确的时间限制,在 LSC 中用 blocksToLive
表示这段时间值。超过保护期,撮合数据还没有提交,其它矿工就可以认领这个任务。
我们允许节点通过设置自己的标准有选择地处理订单,并且它们可以选择隐藏或显示它们。因此,我们不认为拒绝服务是不道德行为的一种形式。
用户可以发送大量的微小指令来攻击环节点。然而,由于我们允许节点根据自己的标准来拒绝订单,所以大多数订单都将被拒绝,因为它们在匹配时不会产生令人满意的利润。因此,大规模的小订单攻击是不可行的。
恶意用户可能会广播订单价值不为 0,但是实际地址余额为 0 的订单,不过节点可以监控地址余额,更新订单状态,忽略这些订单。
为了监控订单数据,更新订单信息,节点需要花费一点时间,但是可以通过建立一些机制来优化这项工作,例如建立黑名单然后丢弃相关订单数据。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于