Thank Onur, Jannik, and Mikerah for the review and the advice
published version https://ethresear.ch/t/bls-signatures-in-solidity/7919
This article targets developers who want to perform BLS signature verification in Eth1 contracts.
For readers interested in the BLS signature in Eth2, I highly recommend "BLS12-381 For The Rest Of Us"[1] by Ben Edgington, which provides long but not too long answers to common questions.
The article also assumes the readers heard of the terms like
When we talk about the BLS signature, we are talking about the signature aggregation technique. The BLS here is the name of three authors Boneh, Lynn, and Shacham. [2]
BLS signature aggregation works on pairing friendly curves. We have to choose which of the following curves to use.
At the time of writing this document (2020-Aug), we can only use alt_bn128 curve in contracts. The alt_bn128 curve is specified in EIP-196[5] (curve point addition and multiplication) and EIP-197[6] (curve pairing) and introduced in Byzantium hardfork October 16, 2017[7].
The cost is further reduced in EIP-1108[8], and was live on mainnet in Istanbul-fork[9].
The BLS12-381 curve is specified in EIP-2537[10] and expected to go live in the Berlin hardfork[11].
The alt_bn128(BN256) is shown in 2016 to have less than 100-bits of security[12]. To target 128-bits security use BLS12-381 whenever possible.
We use alt_bn128 in the examples hereafter.
alt_bn128 | BLS12-381 | Note | |
---|---|---|---|
254 bits (32 bytes) | 381 bits (48 bytes) | has leading zero bits | |
64 bytes | 96 bytes | ||
64 bytes | 96 bytes | has x and y coordinates as |
|
128 bytes | 192 bytes | has x and y coordinates as |
A curve point's y coordinate can be compressed to 1 bit and recovered by x.
alt_bn128 | BLS12-381 | |
---|---|---|
32 bytes | 48 bytes | |
64 bytes | 96 bytes |
This is a decision you'll face in the project.
Choose either:
uint256[4]
in Solidity) and uint256[2]
in Solidity). Also field/curve operations on
The option saves more gas in your project is better.
We'll use the big public keys in the examples hereafter.
We have a curve. The points on the curve can form a subgroup. We define
Pairing function has bilinear property, which means for
It maps the message to an element of
Secret key:
Public key:
Developers usually work with a wrapped contract that has clear function names and function signatures. However, the core of the implementation could be confusing. Here we provide a simple walkthrough.
Below shows an example Solidity function that verifies a single signature. It is the small signature and big public key setup. A signature is a 64 bytes
EIP 197 defined a pairing precompile contract at address 0x8
and requires input to a multiple of 192. This assembly code calls the precompile contract at address 0x8
with inputs.
function verifySingle(
uint256[2] memory signature, \\ small signature
uint256[4] memory pubkey, \\ big public key: 96 bytes
uint256[2] memory message
) internal view returns (bool) {
uint256[12] memory input = [
signature[0],
signature[1],
nG2x1,
nG2x0,
nG2y1,
nG2y0,
message[0],
message[1],
pubkey[1],
pubkey[0],
pubkey[3],
pubkey[2]
];
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, input, 384, out, 0x20)
switch success
case 0 {
invalid()
}
}
require(success, "");
return out[0] != 0;
}
We translate the above code into math formula. Where the nG2
is the negative "curve" operation of the
If the above formula is not straight forward, let's derive from the pairing check we usually see. The message here is the raw message hashed to
We can choose from Hash and pray approach or Fouque Tibouchi approach:
Avg 30k gas for hash and pray https://github.com/thehubbleproject/RedditHubble/runs/1011657548#step:7:35
A sqrt iteration takes 14k gas due to the call to modexp precompile.
Attempt to replace modexp with a series of modmul. optimized cost is 6.7k
https://github.com/ChihChengLiang/modexp
Kobi has a proposal that user provides outputs of modexp and onchain we use 1 modmul to verify. https://gist.github.com/kobigurk/b9142a4755691bb12df59fbe999c2a1f#file-bls_with_help-sol-L129-L154
Post EIP-1108, k pairings cost 34 000 * k + 45 000
gas.
So as the above example, to validate a single signature takes 2 pairings and thus 113000 gas.
Validating n different messages takes n + 1 pairings, which costs 80 000 + 34000*n
gas.
In comparison, ECDSA takes 3000 gas, see ecrecover
[15].
Here are cases to consider the aggregate signature:
https://github.com/kilic/evmbls
https://github.com/ChihChengLiang/bls_solidity_python
TODO