EIP-: ```md
Authors |
---|
Table of Contents
---
eip: 2537
title: BLS12-381曲线操作的预编译合约
description: 添加 BLS12-381 曲线上的操作作为一个预编译合约集合,以便高效地执行诸如 BLS 签名验证之类的操作。
author: Alex Vlasov (@shamatar), Kelly Olson (@ineffectualproperty), Alex Stokes (@ralexstokes), Antonio Sanso (@asanso)
discussions-to: https://ethereum-magicians.org/t/eip2537-bls12-precompile-discussion-thread/4187
status: Final
type: Standards Track
category: Core
created: 2020-02-21
---
## 摘要
添加高效执行 BLS12-381 曲线操作的功能,包括 BLS 签名验证所需的操作。
除了曲线算术,还包括多标量乘法操作,以便在 BLS 签名验证期间高效地聚合公钥或单个签名者的签名。
## 动机
此预编译合约的动机是添加一种密码学原语,与仅提供 80 位安全性的现有 BN254 预编译合约相比,它允许对配对友好曲线上的操作获得 120+ 位的安全性。
## 规范
### 常量
| 名称 | 值 | 注释 |
|---------------------|------|--------------------|
| BLS12_G1ADD | 0x0b | 预编译合约地址 |
| BLS12_G1MSM | 0x0c | 预编译合约地址 |
| BLS12_G2ADD | 0x0d | 预编译合约地址 |
| BLS12_G2MSM | 0x0e | 预编译合约地址 |
| BLS12_PAIRING_CHECK | 0x0f | 预编译合约地址 |
| BLS12_MAP_FP_TO_G1 | 0x10 | 预编译合约地址 |
| BLS12_MAP_FP2_TO_G2 | 0x11 | 预编译合约地址 |
我们引入 *七个* 独立的预编译合约来执行以下操作:
- BLS12_G1ADD - 用于在 G1 中执行点加法(基素数域上的曲线),gas 消耗为 `375` gas
- BLS12_G1MSM - 用于在 G1 中执行多标量乘法(MSM)(基素数域上的曲线),gas 消耗公式在相应部分定义
- BLS12_G2ADD - 用于在 G2 中执行点加法(基素数域的二次扩张上的曲线),gas 消耗为 `600` gas
- BLS12_G2MSM - 用于在 G2 中执行多标量乘法(MSM)(基素数域的二次扩张上的曲线),gas 消耗公式在相应部分定义
- BLS12_PAIRING_CHECK - 用于在一组 (G1, G2) 点的 *pair* 之间执行配对操作,gas 消耗公式在相应部分定义
- BLS12_MAP_FP_TO_G1 - 将基础域元素映射到 G1 点,gas 消耗为 `5500` gas
- BLS12_MAP_FP2_TO_G2 - 将扩张域元素映射到 G2 点,gas 消耗为 `23800` gas
映射函数的规范包含在单独的[文档](/docs/eips/assets/eip-2537/field_to_curve.html)中。此映射函数不执行将字节字符串映射到域元素的操作(因为它可以通过许多不同的方式实现并在 EVM 中高效执行),而仅执行域算术以将域元素映射到曲线点。此功能是签名方案所必需的。
### 曲线参数
BLS12 曲线由以下参数集完全定义(对于所有 BLS12 曲线,系数 `A=0`):
Base field modulus = p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab Fp - 大小为 p 的有限域 Curve Fp equation: Y^2 = X^3+B (mod p) B coefficient = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 Main subgroup order = q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 Extension tower Fp2 construction: Fp quadratic non-residue = nr2 = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa Fp2 is Fp[X]/(X^2-nr2) Curve Fp2 equation: Y^2 = X^3 + B*(v+1) where v is the square root of nr2 Fp6/Fp12 construction: Fp2 cubic non-residue c0 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 Fp2 cubic non-residue c1 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 Twist parameters: Twist type: M B coefficient for twist c0 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 B coefficient for twist c1 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 Generators: H1: X = 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb Y = 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1 H2: X c0 = 0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8 X c1 = 0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e Y c0 = 0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801 Y c1 = 0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be Pairing parameters: |x| (miller loop scalar) = 0xd201000000010000 x is negative = true
应该注意的是,基域模数 `p` 等于 `3 mod 4`,这允许高效的平方根提取,尽管如下所述,解压缩的 gas 成本高于在 `calldata` 中提供解压缩点数据的 gas 成本。
### 域和群
域 Fp 定义为大小为 `p` 的有限域,其元素表示为 0 到 p-1 之间的整数(包括 0 和 p-1)。
域 Fp2 定义为 `Fp[X]/(X^2-nr2)`,其元素 `el = c0 + c1 * v`,其中 `v` 是 `nr2` 的形式平方根,表示为整数对 `(c0,c1)`。
群 G1 定义为 Fp 对(点)`(x,y)` 的集合,使得 `(x,y)` 是 `(0,0)` 或 `x,y` 满足曲线 Fp 方程。
群 G2 定义为 Fp2 对(点)`(x',y')` 的集合,使得 `(x,y)` 是 `(0,0)` 或 `(x',y')` 满足曲线 Fp2 方程。
### 基本元素的细节和编码
#### 域元素编码:
为了生成操作的输入,需要对基域和扩张域的元素进行编码。
基域元素 (Fp) 被编码为 `64` 字节,方法是对相应的(无符号)整数执行 BigEndian 编码。由于 `p` 的大小,前 `16` 个字节始终为零。选择 `64` 字节是为了具有 `32` 字节对齐的 ABI(可以表示为例如 `bytes32[2]` 或 `uint256[2]`,后者假设 BigEndian 编码)。相应的整数**必须**小于域模数。
对于二次扩张域 (Fp2) 的元素,编码是各个系数编码的字节串联,总共为 `128` 字节。对于形式为 `el = c0 + c1 * v` 的 Fp2 元素,其中 `v` 是二次非残差的形式平方根,`c0` 和 `c1` 是 Fp 元素,则相应的字节编码将是 `encode(c0) || encode(c1)`,其中 `||` 表示字节串联(或者可以使用 Solidity 类型中的 `bytes32[4]` 或 `uint256[4]`)。
*关于前 `16` 个字节为零的说明*:要求编码的元素“在域中”,这意味着严格 `< 模数`。在 BigEndian 编码中,这自动意味着对于仅 `381` 位长的模数,`64` 字节编码中的前 `16` 个字节为零,并且即使仅使用输入数据的子切片进行实际解码,也**必须**检查这一点。
对于不能作为域元素的有效编码的输入,预编译合约*必须*返回错误。
#### G1/G2 中点的编码:
G1 和 G2 的点被编码为 `x` 和 `y` 坐标的各自编码的字节串联。因此,G1 点的总编码长度为 `128` 字节,G2 点的总编码长度为 `256` 字节。
#### 无穷远点的编码:
也称为“零点”。对于 BLS12 曲线,坐标为 `(0, 0)` 的点(Fp 或 Fp2 中的零)*不在*曲线上,因此 `128` 字节或 `256` 字节的零序列,如果天真地解码为 `(0, 0)`,则按照惯例用于编码 G1 或 G2 的无穷远点。
#### 用于乘法运算的标量的编码:
乘法运算的标量被编码为 `32` 字节,方法是对相应的(无符号)整数执行 BigEndian 编码。不要求相应的整数小于或等于主子群阶 `q`。
#### 空输入的行为:
某些操作具有可变长度的输入,例如 MSM(采用 `(point, scalar)` 对的列表)或配对(采用 `(G1, G2)` 点的列表)。虽然它们在空输入上的行为是明确定义的(从算术角度来看),但此 EIP 不鼓励此类用例,如果输入为空,则可变输入长度操作必须返回错误。
### 操作的 ABI
#### G1 加法的 ABI
G1 加法调用期望 `256` 字节作为输入,该输入被解释为两个 G1 点(每个 `128` 字节)的字节串联。输出是加法运算结果的编码 - 单个 G1 点(`128` 字节)。
错误情况:
- 无效的坐标编码
- 输入既不是 G1 椭圆曲线上的点,也不是无穷远点
- 输入长度无效
注意:
G1 加法预编译合约没有子群检查。
#### G1 MSM 的 ABI
G1 MSM 调用期望 `160*k`(`k` 是一个**正**整数)字节作为输入,该输入被解释为 `k` 个切片的字节串联,每个切片都是 G1 点(`128` 字节)的编码和标量值(`32` 字节)的编码的字节串联。输出是 MSM 运算结果的编码 - 单个 G1 点(`128` 字节)。
错误情况:
- 无效的坐标编码
- 输入既不是 G1 椭圆曲线上的点,也不是无穷远点
- 输入在 G1 椭圆曲线上,但不在正确的子群中
- 输入长度无效
#### G2 加法的 ABI
G2 加法调用期望 `512` 字节作为输入,该输入被解释为两个 G2 点(每个 `256` 字节)的字节串联。输出是加法运算结果的编码 - 单个 G2 点(`256` 字节)。
错误情况:
- 无效的坐标编码
- 输入既不是 G2 椭圆曲线上的点,也不是无穷远点
- 输入长度无效
注意:
G2 加法预编译合约没有子群检查。
#### G2 MSM 的 ABI
G2 MSM 调用期望 `288*k`(`k` 是一个**正**整数)字节作为输入,该输入被解释为 `k` 个切片的字节串联,每个切片都是 G2 点(`256` 字节)的编码和标量值(`32` 字节)的编码的字节串联。输出是 MSM 运算结果的编码 - 单个 G2 点(`256` 字节)。
错误情况:
- 无效的坐标编码
- 输入既不是 G2 椭圆曲线上的点,也不是无穷远点
- 输入在 G2 椭圆曲线上,但不在正确的子群中
- 输入长度无效
#### 配对检查的 ABI
配对检查调用期望 `384*k`(`k` 是一个**正**整数)字节作为输入,该输入被解释为 `k` 个切片的字节串联。每个切片具有以下结构:
- `128` 字节的 G1 点编码
- `256` 字节的 G2 点编码
预计每个点都在阶数为 `q` 的子群中。
它检查配对目标域中的方程 `e(P1, Q1) * e(P2, Q2) * ... * e(Pk, Qk) == 1`,其中 `e` 是配对运算。输出是 `32` 字节,其中前 `31` 字节等于 `0x00`,最后一个字节是 `0x00`(false)或 `0x01`(true)。
错误情况:
- 无效的坐标编码
- 输入既不是其各自椭圆曲线上的点,也不是无穷远点
- 输入在其各自椭圆曲线上,但不在正确的子群中
- 输入长度无效
#### 将 Fp 元素映射到 G1 点的 ABI
域到曲线的调用期望 `64` 字节作为输入,该输入被解释为 Fp 的一个元素。此调用的输出为 `128` 字节,并且是编码的 G1 点。
错误情况:
- 输入长度无效
- 输入未正确编码
#### 将 Fp2 元素映射到 G2 点的 ABI
域到曲线的调用期望 `128` 字节作为输入,该输入被解释为 Fp2 的一个元素。此调用的输出为 `256` 字节,并且是编码的 G2 点。
错误情况:
- 输入长度无效
- 输入未正确编码
### 错误时的 Gas 消耗
按照所有其他预编译合约的当前状态,如果对此 EIP 中的预编译合约之一的调用导致错误,则与 `CALL` 或 `STATICCALL` 一起提供的所有 gas 都会被消耗掉。
### DDoS 保护
此 EIP 的合理实现*不应*包含潜在的无限循环(无需 `while` 循环即可实现所有功能,这并非不可能,甚至不难),并且 gas 计划准确地反映了相应功能计算所花费的时间(预编译合约定价反映了在存在此类情况下的最坏情况下消耗的 gas 量)。
### Gas 计划
假设 `EcRecover` 预编译合约作为基准。
#### G1 加法
`375` gas
#### G1 乘法
`12000` gas
#### G2 加法
`600` gas
#### G2 乘法
`22500` gas
#### G1/G2 MSM
MSM 预计通过 Pippenger 算法执行(我们也可以说它**必须**通过 Pippenger 算法执行,才能获得加速,从而降低了通过单独乘以每个对并添加结果的朴素实现的成本)。对于这种情况,如果 MSM 中有 `k <= 128` 个点,则准备了一个表格来计算折扣,`k > 128` 的折扣上限为 `max_discount`。
调用成本计算为 `(k * multiplication_cost * discount) // multiplier`,其中 `multiplier = 1000`,`k` 是调用的 (标量,点) 对的数量,`multiplication_cost` 是上面介绍的相应的 G1/G2 乘法成本,`//` 是整数除法。
G1 和 G2 的定价是分开的,每个都有自己的折扣表和 `max_discount`。
##### G1 折扣
G1 MSM 的折扣表,作为 pairs `[k, discount]` 的向量:
[[1, 1000], [2, 949], [3, 848], [4, 797], [5, 764], [6, 750], [7, 738], [8, 728], [9, 719], [10, 712], [11, 705], [12, 698], [13, 692], [14, 687], [15, 682], [16, 677], [17, 673], [18, 669], [19, 665], [20, 661], [21, 658], [22, 654], [23, 651], [24, 648], [25, 645], [26, 642], [27, 640], [28, 637], [29, 635], [30, 632], [31, 630], [32, 627], [33, 625], [34, 623], [35, 621], [36, 619], [37, 617], [38, 615], [39, 613], [40, 611], [41, 609], [42, 608], [43, 606], [44, 604], [45, 603], [46, 601], [47, 599], [48, 598], [49, 596], [50, 595], [51, 593], [52, 592], [53, 591], [54, 589], [55, 588], [56, 586], [57, 585], [58, 584], [59, 582], [60, 581], [61, 580], [62, 579], [63, 577], [64, 576], [65, 575], [66, 574], [67, 573], [68, 572], [69, 570], [70, 569], [71, 568], [72, 567], [73, 566], [74, 565], [75, 564], [76, 563], [77, 562], [78, 561], [79, 560], [80, 559], [81, 558], [82, 557], [83, 556], [84, 555], [85, 554], [86, 553], [87, 552], [88, 551], [89, 550], [90, 549], [91, 548], [92, 547], [93, 547], [94, 546], [95, 545], [96, 544], [97, 543], [98, 542], [99, 541], [100, 540], [101, 540], [102, 539], [103, 538], [104, 537], [105, 536], [106, 536], [107, 535], [108, 534], [109, 533], [110, 532], [111, 532], [112, 531], [113, 530], [114, 529], [115, 528], [116, 528], [117, 527], [118, 526], [119, 525], [120, 525], [121, 524], [122, 523], [123, 522], [124, 522], [125, 521], [126, 520], [127, 520], [128, 519]]
`max_discount = 519`
##### G2 折扣
G2 MSM 的折扣表,作为 pairs `[k, discount]` 的向量:
[[1, 1000], [2, 1000], [3, 923], [4, 884], [5, 855], [6, 832], [7, 812], [8, 796], [9, 782], [10, 770], [11, 759], [12, 749], [13, 740], [14, 732], [15, 724], [16, 717], [17, 711], [18, 704], [19, 699], [20, 693], [21, 688], [22, 683], [23, 679], [24, 674], [25, 670], [26, 666], [27, 663], [28, 659], [29, 655], [30, 652], [31, 649], [32, 646], [33, 643], [34, 640], [35, 637], [36, 634], [37, 632], [38, 629], [39, 627], [40, 624], [41, 622], [42, 620], [43, 618], [44, 615], [45, 613], [46, 611], [47, 609], [48, 607], [49, 606], [50, 604], [51, 602], [52, 600], [53, 598], [54, 597], [55, 595], [56, 593], [57, 592], [58, 590], [59, 589], [60, 587], [61, 586], [62, 584], [63, 583], [64, 582], [65, 580], [66, 579], [67, 578], [68, 576], [69, 575], [70, 574], [71, 573], [72, 571], [73, 570], [74, 569], [75, 568], [76, 567], [77, 566], [78, 565], [79, 563], [80, 562], [81, 561], [82, 560], [83, 559], [84, 558], [85, 557], [86, 556], [87, 555], [88, 554], [89, 553], [90, 552], [91, 552], [92, 551], [93, 550], [94, 549], [95, 548], [96, 547], [97, 546], [98, 545], [99, 545], [100, 544], [101, 543], [102, 542], [103, 541], [104, 541], [105, 540], [106, 539], [107, 538], [108, 537], [109, 537], [110, 536], [111, 535], [112, 535], [113, 534], [114, 533], [115, 532], [116, 532], [117, 531], [118, 530], [119, 530], [120, 529], [121, 528], [122, 528], [123, 527], [124, 526], [125, 526], [126, 525], [127, 524], [128, 524]]
`max_discount = 524`
#### 配对检查操作
配对检查操作的成本为 `32600*k + 37700`,其中 `k` 是 pairs 的数量。
#### Fp 到 G1 映射操作
Fp -> G1 映射是 `5500` gas。
#### Fp2 到 G2 映射操作
Fp2 -> G2 映射是 `23800` gas
#### 变长输入的 Gas 计划说明
对于 MSM 和配对函数,gas 成本取决于输入长度。目前主要客户端(在撰写本文时)中 gas 计划的实现方式是 gas 成本函数*不*执行任何输入长度验证,并且永远不会返回错误。因此,我们提供一个规则列表,说明如何**必须**实现 gas 成本函数,以确保客户端之间的一致性和安全性。
##### G1/G2 MSM 的 Gas 计划说明
定义一个常量 `LEN_PER_PAIR`,对于 G1 操作,该常量等于 `160`,对于 G2 操作,该常量等于 `288`。 定义一个函数 `discount(k)`,该函数遵循相应部分中的规则,其中 `k` 是 pairs 的数量。
以下伪函数反映了 gas 的计算方式:
k = floor(len(input) / LEN_PER_PAIR); if k == 0 { return 0; } gas_cost = k * multiplication_cost * discount(k) // multiplier; return gas_cost;
我们使用向下取整除法来获取 pairs 的数量。如果输入长度不能被 `LEN_PER_PAIR` 整除,我们仍然会产生*一些*结果,但稍后预编译合约将返回错误。此外,`k = 0` 的情况是安全的:`CALL` 或 `STATICCALL` 成本非零,并且形式 gas 成本为零的情况已经用于 `Blake2f` 预编译合约中。无论如何,主预编译合约例程**必须**在此类输入上产生错误,因为它违反了编码规则。
##### 配对的 Gas 计划说明
定义一个常量 `LEN_PER_PAIR = 384`;
以下伪函数反映了 gas 的计算方式:
k = floor(len(input) / LEN_PER_PAIR); gas_cost = 32600*k + 37700; return gas_cost; ```
我们使用向下取整除法来获取 pairs 的数量。如果输入长度不能被 LEN_PER_PAIR
整除,我们仍然会产生一些结果,但稍后预编译合约将返回错误(预编译合约例程必须在此类输入上产生错误,因为它违反了编码规则)。
理由
动机部分涵盖了对 BLS12-381 曲线上的操作的可用性的总体动机。我们还扩展了关于更具体细节的理由。
MSM 作为单独的调用
显式单独的 MSM 操作允许通过所使用的算法(即 Pippenger 算法)以及(通常被遗忘的)以太坊中 CALL
操作很昂贵的事实来节省执行时间(因此节省 gas)(在撰写本文时),因此如果例如对于 100
个点的 MSM,则必须调用乘法预编译合约 100
次,并调用加法 99
次,则必须支付不可忽略的开销(大约可以节省 138600
)。
没有专用的 MUL 调用
执行单个 G1/G2 点与标量乘法的专用 MUL 预编译合约与 k == 1
的 MSM 具有完全相同的 ABI。
MSM 必须检查输入长度以拒绝无效长度的输入。因此,它应该识别 k == 1
的情况,并调用单点乘法的底层实现,以避免更复杂的多标量乘法算法的开销。
向后兼容性
没有向后兼容性问题。
子群检查
MSM 和配对必须执行子群检查。 实现应该使用专用文档中详述的优化子群检查方法。 对于任何未通过子群检查的输入,预编译合约必须返回错误。 由于自同态加速要求正确的子群中的输入,因此实现者可以使用自同态加速。
域到曲线的映射
SWU 映射方法的算法和参数集由单独的文档提供
##
Citation
Please cite this document as:
, "EIP-: ```md," Ethereum Improvement Proposals, no. , . [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-.