Cardano Improvement Proposals

CIP 0381 - Plutus support for Pairings over BLS12_381



This CIP proposes an extension of the current plutus functions to provide support for basic operations over BLS12_381 curve to the plutus language. We expose a candidate implementation, and describe clearly the benefits that this would bring. In a nutshell, pairing friendly curves will enable a large number of cryptographic primitives that will be essential for the scalability of Cardano.


Pairing Friendly Curves are a type of curves that provide the functionality of computing pairings. A pairing is a binary function that maps two points from two groups to a third element in a third target group. For a more in-depth introduction to pairings, we recommend reading Pairings for Beginners or Pairings for Cryptographers. For level of detail required in this document, it is sufficient to understand that a pairing is a map: e:G1 X G2 -> GT, which satisfies the following properties:

where G1, G2 and GT are three distinct groups of order a prime q. Given that all three groups have the same order, we can refer to the scalars of each group using the same type/structure. Pairing computation is an expensive operation. However, they enable a range of interesting cryptographic primitives which can be used for scaling Cardano and many other use cases. We now provide a list of use cases of pairings as well as an estimate operation count to understand the number of 'expensive' operations that need to be computed for each of them (a preliminary benchmark can be found in Section 'Costing of function').


We now provide the technical specification.

Names and types/kinds for the new functions or types

The added types will be the following, all of which can be represented as a byte array. Even if these types are equivalent to byte arrays of a given size, it makes sense to include these types, to enforce deserialization, and therefore some checks on the data used by the smart contract. In particular, all three types can only be achieved with byte arrays that represent a point which is part of the prime order subgroup.

We need to support the binary operation of G1 and G2 (which are additive groups), as well as the binary operation over GT (which is represented as a multiplicative group). We also want to enable hashing to G1 and G2. In particular, we expose the hash to curve (which we denote with H2G1 and H2G2 for G1 and G2 respectively) algorithm as described in hash to curve draft, using BLS12381G1_XMD:SHA-256_SSWU_RO_ and BLS12381G2_XMD:SHA-256_SSWU_RO_ for G1 and G2 respectively. We do not include the type of the Scalar, nor operations related to it. These type of operations can already be performed with the Integral type. In particular, we need the following functions:

On top of the elliptic curve operations, we also need to include deserialization functions, and equality definitions among the G1 and G2 types.

This makes a total of 18 new functions and three new types.

We follow the ZCash Bls12-381 specification for the serialization of elements:

The most-significant three bits of a G1 or G2 encoding should be masked away before the coordinate(s) are interpreted. These bits are used to unambiguously represent the underlying element:

We include the serialisation of the generator of G1 and the generator of G2:

[151, 241, 211, 167, 49, 151, 215, 148, 38, 149, 99, 140, 79, 169, 172, 15, 195, 104, 140, 79, 151, 116, 185, 
    5, 161,78, 58, 63, 23, 27, 172, 88, 108, 85, 232, 63, 249, 122, 26, 239, 251, 58, 240, 10, 219, 34, 198, 187]
[147, 224, 43, 96, 82, 113, 159, 96, 125, 172, 211, 160, 136, 39, 79, 101, 89, 107, 208, 208, 153, 32, 182, 
    26, 181, 218, 97, 187, 220, 127, 80, 73, 51, 76, 241, 18, 19, 148, 93, 87, 229, 172, 125, 5, 93, 4, 43, 126, 
    2, 74, 162, 178, 240, 143, 10, 145, 38, 8, 5, 39, 45, 197, 16, 81, 198, 228, 122, 212, 250, 64, 59, 2, 180, 
    81, 11, 100, 122, 227, 209, 119, 11, 172, 3, 38, 168, 5, 187, 239, 212, 128, 86, 200, 193, 33, 189, 184]

An important note on GT elements

We intentionally limit what can be done with the GT element. In fact, the only way that one can generate a GT element is using the BLS12_381_ppairing_ml function. Then these elements can only be used for operations among them (mult) and a final equality check (denote final_verify). In other words, GT elements are only there to eventually perform some equality checks. We thought of including the inverse function to allow division, but given that we simply allow for equality checks, if one needs to divide by A, then A can simply move to the other side of the equality sign. These limitations allow us for a performance trick, which is also used for the verification of BLS signatures. In a nutshell, a pairing is divided into two operations: (i) Miller loop, and (ii) final exponentiation. Both are expensive, but what we can do is first compute the miller loop, which already maps two points from G1 and G2 to GT. Then we can use this result to perform the operations over these points (mults). Finally, when we want to check for equality, we invert one of the two points (or equivalently in this case we compute the conjugate), and multiply the result by the other point. Only now we compute the final exponentiation and verify that the result is the identity element. In other words:

Using the estimates exposed in the section Costing of function, we can see how this representation of GT elements is beneficial. Assume that we want to check the following relation: e(A,B) * e(C,D) =? e(E,F) * e(G,H). The miller look takes around 330us, and the final exponentiation around 370us. A full pairing would be 700us, and therefore checking this relation would cost around 2,8ms. If, instead, we break down the pairing into a miller loop and a final exponentiation, and only compute the latter once, the cost of the relation above would be 330 * 4 + 370 = 1,7ms.

For this reason it is important to limit what can be done with GTElements, as the pairing really is not the full pairing operation, but only the miller loop.

Source implementation

Other libraries of interest

Comparison with existing function

We present what would be the alternatives of using pairings in the different use cases presented above.

Reason for exposing curve operations API

One might be concerned of why we are exposing such low-level primitives, instead of exposing higher level protocol functions, such as VerifyBlsSignature or VerifyZKP. The motivation behind that is because pairings can enable a big number of use cases, and covering all of those can considerably extend the list of required functions.

Curve specifications

BLS12 curve is fully defined by the following set of parameters (coefficient A=0 for all BLS12 curves). Taken from EIP 2537:

Base field modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
    B coefficient = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004
    Main subgroup order = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
    Extension tower
    Fp2 construction:
    Fp quadratic non-residue = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa
    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
    X = 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb
    Y = 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1
    X c0 = 0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8
    X c1 = 0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e
    Y c0 = 0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801
    Y c1 = 0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be
    Pairing parameters:
    |x| (miller loop scalar) = 0xd201000000010000
    x is negative = true

One should note that base field modulus is equal to 3 mod 4 that allows an efficient square root extraction.


The reason for choosing BLS12_381 over BN256 curve is that the former is claimed to provide 128 bits of security, while the latter was reduced to 100 bits of security after the extended number field sieve (a new algorithm to compute the discrete logarithm) was shown to reduce the security of these curves.

An EIP for precompiles of curve BLS12381 already exists, but has been stagnant for a while. Nonetheless, Zcash, MatterLabs and Consensys support BLS12381 curve, so it is certainly widely used in the space.

Further reading regarding curve BLS12_381 can be found here and the references thereof cited.

Path to Proposed

To move this draft to proposed, we need to argue the trustworthiness of blst library, cost all functions and include these functions to Plutus. Furthermore, before proceeding to Proposed, we will provide a Haskell implementation of one of the algorithms that we want to do on-chain, implemented in terms of the primitives we are going to provide. This will help in benchmarking the viability of using these primitives on main-net.

Trustworthiness of implementations

Costing of function

We performed some benchmarks on the curve operations that can be used to cost each of the functions. We performed the benchmarks on a 2,7 GHz Quad-Core Intel Core i7, using the blst library. Further benchmarks are required to provide final costings of functions. Deserialization functions check that elements are part of the prime order subgroup. The pairing evaluation only computes the miller loop, and the final verify of GTs computes an inversion in GT, a multiplication, a final exponentiation and a check wrt the identity element (more info in section An important note on GT elements).

Plutus implementor

IOHK internal. We currently have implemented the FFI binding in cardano-base.

Haskell implementation of ATMS

We will provide a Haskell implementation of ATMS verification to understand the complexity of such a procedure.

Path to Active

Release in upcoming update.