Boneh-Lynn-Shacham (BLS) multi-signatures are a powerful cryptographic tool that enable multiple participants to jointly produce a single, compact signature. These signatures are particularly valuable in consensus mechanisms, such as Ethereum, where they help reduce signature size and enhance efficiency.
In this blog post, we delve into the implementation of BLS multi-signatures within EigenLayer’s Actively Validated Services (AVS) contracts. EigenLayer represents an important protocol in the blockchain ecosystem, extending Ethereum’s functionality through restaking. By using EigenLayer AVS as an example, we move beyond theoretical cryptographic constructions to demonstrating how BLS multi-signatures have been used to solve real-world challenges. We highlight in this post some key technical details that are often overlooked or not documented. We also present an important pitfall to avoid when using BLS multi-signatures, how EigenLayer solves this challenge, and an alternative approach that may be explored in the future.
Understanding EigenLayer’s AVS
EigenLayer is a protocol built on Ethereum that enables restaking, a process where Ethereum stakers can reuse or restake their staked ETH to secure additional services beyond the Ethereum blockchain itself. Those additional services are called in EigenLayer, Actively Validated Services (AVSs). Ethereum stakers can delegate some of their staked tokens to the operators that will be in charge to validate some tasks given by the AVSs. Some AVSs are already running different services, and a list of deployed AVSs is available here↗.
EigenLayer created a repository called the Incredible Squaring AVS↗, implementing a toy example of AVSs to show how it works in practice. In this simple example, a task is simply a number and the set of operators are in charge of computing and signing the square of the number. Here is an image from this repository showing how it works:
The task generator is in charge of submitting tasks to the AVS contracts. In this example, the task is composed of a number, the block number when the task was created, and a threshold indicating the percentage of operator validation, which is necessary to validate the task. The AVS contracts are developed by EigenLayer and are available here↗. However, it is up to each AVS to use those contracts or not according to their own needs.
Then the operators that opted in to the AVS are able to get those tasks, to compute the task answer — in the previous example, the square of the number — and to send the answer with their BLS signature of the task to the aggregator.
As soon as the threshold of identical answers is reached, the aggregator merges all the BLS signatures into a unique aggregate signature and sends it back to the AVS contract. The contract verifies that the signature is correct and opens a challenging period when a challenger can give proof that the validation was incorrect, and if so, the misbehaving operators are slashed.
The following part of the post is about the details of BLS signature aggregation and how it is implemented in the AVS contracts.
Defining Boneh-Lynn-Shacham
We recall briefly how the Boneh-Lynn-Shacham (BLS) signature works. For a more detailed definition, see the reference paper↗. The signature works with two different groups with respective generators .
Then the secret key is random number between and (the order). The corresponding public key is . To sign a message, we need a hash function . This function maps arbitrary messages to elements of the first group. The signature of a message is given by the following:
Given a pairing , to verify that a signature is correct, the verifier checks the following equality holds:
From the bilinearity of the pairing , if a signature is correct, we have
To learn more about pairings and their properties, see our previous blog post↗.
There are few remarks regarding the previous definitions. First, since the pairing verification is symmetric, the signature scheme can be defined the other way around with the signatures in and the public key in . Most of the time is defined over the quadratic extension of the field and the storage of element is larger. In addition, the arithmetic in is more resource-intensive than in . It may be an advantage depending on the implementation constraints to switch the public key group. If the implementation needs to store all the public keys that signed a message, then it would be more interesting to have them in to reduce storage. If it is possible to keep only the aggregate public key, but all the signatures must be stored, then the signature should be in and the public keys in .
Another point is that, since the verification involves a pairing operation that is very complex, the verification is more time-consuming than other elliptic-curve signature verification like Schnorr or EdDSA. However, the BLS signature allows direct signature aggregation, which is not as straightforward for other signature algorithms.
In Solidity contracts, most of the time, the groups used are subgroups of the BN254 curves↗. This allows using the precompiled contracts as defined in EIP-196↗ and EIP-197↗ to save a large amount of gas. Nevertheless, the precompiled contracts impose some constraints since they provide only operations in and not in . As we will see later, this forced smart contract developers to find some optimizations to avoid computation in .
Another problem for long-term security is that the BN254 curve was found to have a security level around 100 bits↗ instead of 128, as believed previously. For this reason, some projects like Zcash↗ moved to another curve called BLS12-381 with a higher security level. Don’t be confused with the name “BLS”; here, this curve is a member of the Barreto-Lynn-Scott curves↗. The Ethereum Pectra upgrade↗ now also includes precompiled contracts for the BLS12-381 curve with EIP-2537↗. The main improvement regarding BLS signatures is that operations in for this curve are included.
Building Aggregation and Multi-signatures
The concept of signature aggregation is when participants have issued signatures of different messages, and they build an aggregate signature . When valid, this aggregate signature convinces a verifier that all the participants signed their message.
For BLS signatures, there is a natural way to build aggregate signatures. Suppose we have signatures of messages. Instead of verifying them independently, we can compute an aggregate signature with
Then the verification algorithm becomes
The aggregate signature is small, a single group element, and it saved pairing evaluations for verification.
The concept of multi-signatures is similar to aggregation, but all the signers sign the same message . For BLS, it further optimizes the verification. If we have signatures of the same message , the verification becomes
We only need two pairing evaluations, whatever the number of signatures. In addition, we can define an aggregate public key
to be reused for each signature verification.
This efficient scheme is used a lot in practice. For example, it is used in Ethereum↗ to aggregate validator votes into a single signature. As mentioned earlier, it is also at the heart of AVS. This reduces the verification time and the signature storage. A good source of information for implementing BLS signature aggregation or multi-signatures is the IETF draft↗, which details the primitive to use and some pitfalls to avoid.
One question could arise at this point: Is it possible to build signature aggregation with Schnorr signatures? The answer is yes. Schnorr signatures are linear; thus, we can also aggregate them. However, since a nonce needs to be generated for each Schnorr signature, multi-signatures need at least two rounds of communication between participants compared to a single round for BLS. It makes the protocol more complex to implement for Schnorr and more error-prone. MuSig2↗ is one of the Schnorr multi-signature protocols used in practice. We have covered more on the topics of Schnorr multi-signatures and threshold signatures in our previous blog post↗ about Bitcoin.
What BLS Signatures Look Like in EigenLayer
As mentioned earlier, EigenLayer also uses BLS aggregation in their AVS implementation. The aggregator receives operators’ BLS signatures of the current task; as soon as it receives enough signatures, it aggregates them into a single signature and sends it to the BLSSignatureChecker contract together with the public keys that have signed the message. To verify the signature, the contract adds all the public keys together and verifies the aggregate signature as described earlier. In the contract, the verification happens in the trySignatureAndApkVerification
function:
function trySignatureAndApkVerification(
bytes32 msgHash,
BN254.G1Point memory apk,
BN254.G2Point memory apkG2,
BN254.G1Point memory sigma
) public view returns(bool pairingSuccessful, bool siganatureIsValid) {
// gamma = keccak256(abi.encodePacked(msgHash, apk, apkG2, sigma))
uint256 gamma = uint256(keccak256(abi.encodePacked(msgHash, apk.X, apk.Y, apkG2.X[0], apkG2.X[1], apkG2.Y[0], apkG2.Y[1], sigma.X, sigma.Y))) % BN254.FR_MODULUS;
// verify the signature
(pairingSuccessful, siganatureIsValid) = BN254.safePairing(
sigma.plus(apk.scalar_mul(gamma)),
BN254.negGeneratorG2(),
BN254.hashToG1(msgHash).plus(BN254.generatorG1().scalar_mul(gamma)),
apkG2,
PAIRING_EQUALITY_CHECK_GAS
);
}
It is noticeable that the verification differs a bit compared to what we have introduced previously. First, it takes two public keys, and , and the pairing check is modified to the following:
After rearranging the terms, we can split the check in two:
We recognize in the first check the BLS verification. The second check with the factor is an optimization described in a previous paper↗. As we have seen, in Solidity, the EIP-196 precompiled contract allows performing operations in , but there is no precompiled contract for operations. If the aggregator had sent only a list of a signer’s public keys in , then the aggregate public key would have to be computed in , which may be very gas-expensive. Instead, the aggregator sends a list of the signer’s public keys but in and the aggregate key already . Finally, the check in (2) is simply to ensure that matches the value computed from the signer’s keys . As we have seen previously, with EIP-2537, this trick is not needed anymore if the operations are done on BLS12-381.
The is the delineation factor. It is computed over all the public values to prevent a malicious aggregator from forging signatures. For example, if does not include the signature and the public key, then an attacker can choose the following:
With being the point at infinity. Then, the signature would verify for any message . In the previous function, is computed in the contract with
preventing the signature forgery since the depends on all the public parameters. Similarly to the Fiat–Shamir heuristic↗, must depend on all the verifying arguments to prevent a malicious aggregator from forging a signature.
There is another threat, as mentioned in the EigenLayer documentation:
msgHash
is the hash being signed by the apk. Note that the caller is responsible for ensuringmsgHash
is a hash! If someone can provide arbitrary input, it may be possible to tamper with signature verification.
Indeed, if the hash value can be freely chosen by a malicious operator, by setting the following —
again, the signature would be accepted for any message . Fortunately, in the previous example, the task message is hashed before being passed to for signature verification:
/* CHECKING SIGNATURES & WHETHER THRESHOLD IS MET OR NOT */
// calculate message which operators signed
bytes32 message = keccak256(abi.encode(taskResponse));
// check the BLS signature
(QuorumStakeTotals memory quorumStakeTotals, bytes32 hashOfNonSigners) =
checkSignatures(message, quorumNumbers, taskCreatedBlock, nonSignerStakesAndSignature);
Nevertheless, it is the AVS contracts’ responsibility to properly call the verification, and thus it is important to check.
The Pitfall of Multi-signatures
Multi-signatures come with a serious potential pitfall called rogue-key attacks. Let’s illustrate how this kind of attack works.
Let’s suppose an honest user has a public key . Then, an attacker who has previously seen can choose their public key as . The attacker would not know the private key associated to the public key. However, the multi-signature verification would give the following:
Only is needed to sign a message resulting in a valid multi-signature, even though the first user may not have signed it. This is easily generalized to any number of honest users by choosing the rogue key, being
This is a dangerous threat since, in our previous AVS example, a malicious aggregator that would have previously registered a rogue key could send aggregate signatures that were not signed by the validators but still will be accepted by the contract. This would lead to having validators being slashed even if they did not misbehave.
Preventing the Pitfall
Proof of Possession
To prevent a rogue-key attack, a common method is to request users to prove they know the private key matching their public key. Thus, in a first registration step, the user is requested to register their public key together with a proof of possession such that