该文档详细介绍了在数字结果的 DLC (Discreet Log Contract) 中,如何对赔付曲线进行序列化和反序列化。针对一般的赔付曲线,文档提出了"General Payout Curves"的概念,并详细说明了其序列化方法和函数评估过程。此外,对于特定的双曲线形状的赔付曲线,文档也提供了相应的序列化和评估方法,最后强调了这些设计是为了提高效率和紧凑性,从而更方便地应用到实际场景中。
当为一个数值结果构建 DLC 时,通常会有数量非常多的 可能结果,以至于实际上无法在要约中全部枚举出来,以及它们相关的赔付。
通常情况下,存在一些宏观结构,其中一些参数决定了 所有可能结果的赔付,使得这些参数成为数值结果 DLC 赔付曲线的更简洁的序列化方式。
本文档首先详细说明了所谓的 通用赔付曲线的序列化和反序列化(又名评估),这应该足以满足任何简单的赔付曲线,例如那些 由一些线条组合而成的曲线,以及不需要创建自己的类型的自定义赔付曲线。
本文档还详细说明了双曲线形状的赔付曲线片段的序列化和反序列化,
这对于许多反向合约(例如差价合约 (CFD))很有用,其中赔付
曲线的形式为 constant/outcome
(其中 outcome
是输入值)。
本规范的目标是高效、紧凑地实现通用赔付曲线形状,同时也确保 更简单、更常见的赔付曲线(例如远期合约的直线)在符合通用结构的同时不会变得复杂。
具体来说,通用赔付曲线规范支持分段函数集(片段之间没有连续性 要求),其中片段是多项式或双曲线。
如果你希望支持的某些赔付曲线“形状”不能有效地表示为分段多项式
函数,那么你应该提出一个新的 payout_curve_piece
类型,该类型可以有效地指定最少的参数集,
从中可以确定整个赔付曲线片段。
例如,“形状” 1/outcome
仅由几个参数完全确定,但当使用多项式插值进行逼近时,可能需要数千个插值点,
因此引入了特定于双曲线的类型。
请注意,赔付曲线是协议级别的抽象,在应用程序层,用户很可能会 与某些合约模板上的一组参数进行交互。 他们不会直接与通用赔付曲线及其序列化进行交互,这些序列化旨在实现核心 DLC 逻辑实现之间的有效 序列化和确定性再现。 通用赔付曲线还处理大量常见应用程序用例的插值逻辑, 也就是说,应用程序可能不必计算所有结果点来序列化为此格式, 而是通常可以使用仅用户提供的参数来直接计算少量相关的插值点。
在本节中,我们将详细介绍通用 payout_function
的 TLV 序列化。
1. 数据:
* [`bigsize`:`num_pieces`]
* [`u64`:`endpoint_0`]
* [`u64`:`endpoint_payout_0`]
* [`u16`:`extra_precision_0`]
* [`payout_curve_piece`:`piece_1`]
* [`u64`:`endpoint_1`]
* ...
* [`payout_curve_piece`:`piece_num_pieces`]
* [`u64`:`endpoint_num_pieces`]
* [`u64`:`endpoint_payout_num_pieces`]
* [`u16`:`extra_precision_num_pieces`]
num_pieces
是构成赔付曲线的 payout_curve_pieces
的数量及其端点。
每个端点由两个 u64
和一个 u16
组成。
第一个整数称为 endpoint
,包含实际的 event_outcome
,它对应于赔付曲线上的 x 坐标,
该坐标是曲线片段之间的边界。
第二个整数称为 endpoint_payout
,如果签署了 endpoint
的值,则将其设置为等于本地方的赔付,
此赔付对应于赔付曲线上的 y 坐标。
第三个整数 u16
称为 extra_precision
,并设置为二进制点之后四舍五入的赔付的前 16 位。
这种额外的精度确保了插值不会因
于舍入而导致的 endpoint_payout
中的错误而包含较大的误差。
准确地说,用于插值的点应该是:
(endpoint
, endpoint_payout + double(extra_precision) >> 16
)。
对于本文档的其余部分,值 endpoint_payout + double(extra_precision) >> 16
称为 endpoint_payout
。
请注意,此 payout_function
是从提供者的角度来看的。
要评估接受者的 payout_function
,你必须在给定的
event_outcome
处评估提供者的 payout_function
,并从 total_collateral
中减去结果赔付。
重要的是,你不要通过将提供者的 payout_function
中的所有 payout
字段替换为 total_collateral - payout
并插值resulting 点来构造接受者的 payout_function
。
这是行不通的,因为由于舍入,双方的 payout_function
的输出之和可能是 total_collateral - 1
,包括检查此情况(任一结果都缺少一个 satoshi)到验证算法
可能会使验证时间最多慢四倍,并通过破坏一个合理的假设来增加协议的复杂性。
num_pieces
必须至少为 1
。endpoint
必须严格增加。
如果需要不连续性,则应使用“空” polynomial_curve_piece,从而导致
连续 endpoint
值之间的线。
这样做是为了避免对不连续点的值产生歧义。给定一个潜在的 event_outcome
,按如下方式计算 outcome_payout
:
event_outcome
二分查找 endpoint
endpoint_payout
。endpoint
之间(包括之前和之后)的 payout_curve_piece
上评估 event_outcome
。当重复且顺序地评估插值(如在 CET 计算期间所做的那样)时,可以对这个分段插值函数进 行许多优化。
为顺序输入计算时,可以避免二分查找。
在评估多项式片段时,可以为多项式片段中的每个 i
缓存值 points(i).outcome_payout / PROD(j = 0, j < points.length && j != i, points(i).event_outcome - points(j).event_outcome)
,称之为 coef_i
。
给定一个 event_outcome
,令 all_prod = PROD(i = 0, i < points.length, event_outcome - points(i).event_outcome)
。
然后,可以将总和计算为 SUM(i = 0, i < points.length, coef_i * all_prod / (event_outcome - points(i).event_outcome))
。
由于直线是多项式,因此当以分段多项式函数的语言表示时,简单曲线仍然很简单。 任何有趣的(例如,非随机的)赔付曲线都可以使用巧妙构造的多项式插值紧密逼近。 最后,序列化这些函数可以通过仅提供每个多项式片段的几个点来紧凑地完成,以便 使通信中的接收方能够从这最少的信息中插值多项式。
但重要的是要注意,由于 Runge 现象, 客户端通常最好使用某些样条插值来构造其赔付曲线,而不是直接使用多项式插值(除非线性逼近就足够了)。 样条由多项式片段组成,因此可以将结果插值写为分段多项式。
1. 实现:`payout_curve_piece`
2. 类型:0
3. 数据:
* [`bigsize`:`num_pts`]
* [`u64`:`event_outcome_1`]
* [`u64`:`outcome_payout_1`]
* [`u16`:`extra_precision_1`]
* ...
* [`u64`:`event_outcome_num_pts`]
* [`u64`:`outcome_payout_num_pts`]
* [`u16`:`extra_precision_num_pts`]
num_pts
是此曲线片段中指定的中间点的数量,这些中间点将与周围的 endpoint
一起用于执行插值。
每个点由两个 bigsize
整数和一个 u16
组成,它们的解释方式与
通用赔付曲线中的 endpoint
的解释方式完全相同,用作 x
和 y
坐标。
在 num_pts
为 0
的特殊情况下,仅使用端点,这意味着在端点之间插值一条直线。
有很多方法可以计算由某些插值点集确定的唯一多项式。 我选择在此处详细介绍 拉格朗日插值,因为它相对简单,但任何算法都应该有效,只要它不会导致近似误差太大,以至于无法通过验证。 仅举几个其他算法,如果你对替代方案感兴趣,你可能希望使用范德蒙矩阵或 另一种替代方案,除差法 。
请注意,虽然以下内容可能看起来很复杂,但它应该归结为非常少的代码行。 此外,这个问题是众所周知的,大多数语言的解决方案可能存在于 Stack Overflow 等站点上, 你可以从中复制它们,以便只需要进行细微的审美修改。
给定一个潜在的 event_outcome
,按如下方式计算 outcome_payout
(其中 points 是包含 endpoint
的列表):
lagrange_line(i, j) = (event_outcome - points(j).event_outcome)/(points(i).event_outcome - points(j).event_outcome)
lagrange(i) := PROD(j = 0, j < points.length && j != i, lagrange_line(i, j))
SUM(i = 0, i < points.length, points(i).outcome_payout * lagrange(i))
本规范的目标是高效、紧凑地实现一般的双曲线形状,同时也确保更简单、更常见的赔付曲线(例如constant/outcome
)在符合通用结构的同时不会变得复杂。
这是通过总共使用 7 个参数来完成的,这些参数可以(几乎)唯一地表达曲线 1/x
的每个仿射变换。
具体来说,我们有 (f_1, f_2) + ((a, b), (c, d))*(x, 1/x)
,它是点 (f_1, f_2)
的某种平移,添加到由矩阵 ((a, b), (c, d))
变换的曲线 (x, 1/x)
,其中 a*d =/= b*c
。
最后一个参数是一个布尔值,用于在存在歧义时指定要使用的曲线片段。
这个方案使简单曲线保持简单,为了表示任何形式为 constant/x + constant'
的曲线,我们只需
设置 f_1 = b = c = 0, a = 1, d = constant, f_2 = constant'
。
1. 实现:`payout_curve_piece`
2. 类型:1
3. 数据:
* [`bool`:`use_positive_piece`]
* [`bool`:`translate_outcome_sign`]
* [`u64`:`translate_outcome`]
* [`u16`:`translate_outcome_extra_precision`]
* [`bool`:`translate_payout_sign`]
* [`u64`:`translate_payout`]
* [`u16`:`translate_payout_extra_precision`]
* [`bool`:`a_sign`]
* [`u64`:`a`]
* [`u16`:`a_extra_precision`]
* [`bool`:`b_sign`]
* [`u64`:`b`]
* [`u16`:`b_extra_precision`]
* [`bool`:`c_sign`]
* [`u64`:`c`]
* [`u16`:`c_extra_precision`]
* [`bool`:`d_sign`]
* [`u64`:`d`]
* [`u16`:`d_extra_precision`]
如果将 use_positive_piece
设置为 true,则使用 y_1
,否则使用 y_2
。
然后有六个数值,表示为一个 bool
符号(对于正数设置为 true,对于负数设置为 false)、一个 u64
整数和一个 u16
额外精度。
准确地说,使用的数字应该是:(num_sign
)( num + double(num_extra_precision) >> 16
)。
字段 translate_outcome
和 translate_payout
分别对应于值 f_1
和 f_2
。
请注意,此 payout_function
是从提供者的角度来看的。
要评估接受者的 payout_function
,你必须在给定的
event_outcome
处评估提供者的 payout_function
,并从 total_collateral
中减去结果赔付。
a*d
必须不等于 b*c
。event_outcome
定义(没有零除)。双曲线的两个曲线片段可以通过一个布尔标志选择出来,这两个曲线片段都仅仅是对以上表达式中 y坐标
的参数化,当它的表达式展开后:
y_1 = c * (x - f_1 + sqrt((x - f_1)^2 - 4*a*b))/(2*a) + 2*a*d/(x - f_1 + sqrt((x - f_1)^2 - 4*a*b)) + f_2
y_2 = c * (x - f_1 - sqrt((x - f_1)^2 - 4*a*b))/(2*a) + 2*a*d/(x - f_1 - sqrt((x - f_1)^2 - 4*a*b)) + f_2
我们将 y_1
称为正片段,将 y_2
称为负片段,仅仅因为它们分别使用正平方根
和负平方根。
Nadav Kohen <nadavk25@gmail.com>
<br>
本作品已获得 知识共享署名 4.0 国际许可协议 的许可。
- 原文链接: github.com/discreetlogco...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!