Documentation Index Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
Use this file to discover all available pages before exploring further.
Try it Live Run BLS12-381 examples in the interactive playground
BLS12-381
Pairing-friendly elliptic curve implementation for Ethereum 2.0 consensus layer signatures and EIP-2537 precompiled contracts.
Overview
BLS12-381 is a Barreto-Lynn-Scott pairing-friendly curve designed for optimal security and performance in blockchain applications. It provides 128-bit security, efficient pairing operations, and signature aggregation capabilities essential for proof-of-stake consensus.
Ethereum Use Cases:
Ethereum 2.0 Consensus : Validator signature aggregation
BLS Signatures : Short signatures with efficient batch verification
EIP-2537 : Precompiled contracts for curve operations
Light clients : Compact sync committee proofs
Cross-chain bridges : Trustless interoperability proofs
Security Level : 128-bit (comparable to 3072-bit RSA or 256-bit ECC)
Quick Start
import * as BLS12381 from '@tevm/voltaire/crypto' ;
// G1 operations (signatures)
const g1Point1 = new Uint8Array ( 128 ); // G1 point input
const g1Point2 = new Uint8Array ( 128 );
const g1Output = new Uint8Array ( 128 );
await BLS12381 . bls12_381 . g1Add ([ ... g1Point1 , ... g1Point2 ], g1Output );
// G2 operations (public keys)
const g2Point1 = new Uint8Array ( 256 );
const g2Scalar = Bytes32 ();
const g2Output = new Uint8Array ( 256 );
await BLS12381 . bls12_381 . g2Mul ([ ... g2Point1 , ... g2Scalar ], g2Output );
// Pairing check (signature verification)
const g1_128 = new Uint8Array ( 128 );
const g2_256 = new Uint8Array ( 256 );
const pairingInput = new Uint8Array ([ ... g1_128 , ... g2_256 ]); // 384 bytes per pair
const pairingOutput = Bytes32 ();
await BLS12381 . bls12_381 . pairing ( pairingInput , pairingOutput );
Elliptic Curve Pairing Basics
BLS12-381 is a Barreto-Lynn-Scott curve with embedding degree 12, providing:
Efficient Pairings : Optimal ate pairing computable in ~1-2ms
Signature Aggregation : Combine multiple signatures into one
Batch Verification : Verify many signatures in one pairing check
Short Signatures : G1 signatures (48 bytes) with G2 public keys (96 bytes)
Pairing Map : e: G1 × G2 → GT where:
G1 : Points over base field Fp (48-byte compressed, 96-byte uncompressed)
G2 : Points over Fp2 extension (96-byte compressed, 192-byte uncompressed)
GT : Elements in Fp12 (multiplicative group)
Properties :
Bilinearity: e(aP, bQ) = e(P, Q)^(ab)
Non-degeneracy: e(G1, G2) ≠ 1
Computability: Polynomial time optimal ate pairing
API Reference
G1 Operations
G1 points are in the base field Fp (381-bit prime).
G1 Addition
import { bls12_381 } from '@tevm/voltaire/crypto' ;
// Add two G1 points
const input = new Uint8Array ( 256 ); // p1 (128 bytes) || p2 (128 bytes)
const output = new Uint8Array ( 128 );
// Each G1 point: 128 bytes
// - x coordinate: 64 bytes (Fp, padded big-endian)
// - y coordinate: 64 bytes (Fp, padded big-endian)
await bls12_381 . g1Add ( input , output );
Input Format : 256 bytes
Bytes 0-63: p1.x (Fp, padded to 64 bytes)
Bytes 64-127: p1.y (Fp)
Bytes 128-191: p2.x (Fp)
Bytes 192-255: p2.y (Fp)
Output Format : 128 bytes (result point)
G1 Scalar Multiplication
// Multiply G1 point by scalar
const input = new Uint8Array ( 160 ); // point (128) || scalar (32)
const output = new Uint8Array ( 128 );
// Point: 128 bytes (x || y, each 64 bytes padded)
// Scalar: 32 bytes (Fr element, big-endian)
await bls12_381 . g1Mul ( input , output );
Input Format : 160 bytes
Bytes 0-127: G1 point (x || y)
Bytes 128-159: Scalar (32-byte big-endian)
G1 Multi-Scalar Multiplication (MSM)
// Multi-scalar multiplication: sum(scalar_i * point_i)
const numPoints = 10 ;
const input = new Uint8Array ( 160 * numPoints );
const output = new Uint8Array ( 128 );
// Input: concatenated (point || scalar) pairs
await bls12_381 . g1Msm ( input , output );
Use case : Efficient batch operations (validators, proof aggregation)
G2 Operations
G2 points are over Fp2 extension field (complex numbers over Fp).
G2 Addition
// Add two G2 points
const input = new Uint8Array ( 512 ); // p1 (256) || p2 (256)
const output = new Uint8Array ( 256 );
// Each G2 point: 256 bytes
// - x.c0: 64 bytes (Fp, padded)
// - x.c1: 64 bytes (Fp)
// - y.c0: 64 bytes (Fp)
// - y.c1: 64 bytes (Fp)
await bls12_381 . g2Add ( input , output );
Input Format : 512 bytes (two G2 points)
Output Format : 256 bytes (result G2 point)
G2 Scalar Multiplication
// Multiply G2 point by scalar
const input = new Uint8Array ( 288 ); // point (256) || scalar (32)
const output = new Uint8Array ( 256 );
await bls12_381 . g2Mul ( input , output );
Input Format : 288 bytes
Bytes 0-255: G2 point (x.c0 || x.c1 || y.c0 || y.c1)
Bytes 256-287: Scalar (32-byte big-endian)
G2 Multi-Scalar Multiplication
// MSM for G2 points
const numPoints = 5 ;
const input = new Uint8Array ( 288 * numPoints );
const output = new Uint8Array ( 256 );
await bls12_381 . g2Msm ( input , output );
Pairing Operations
Optimal Ate Pairing
// Compute pairing(s) and check if product equals 1
const pairs = 2 ; // Number of (G1, G2) pairs
const input = new Uint8Array ( 384 * pairs );
const output = Bytes32 ();
// Each pair: 384 bytes
// - G1 point: 128 bytes (x || y, each 64 bytes padded)
// - G2 point: 256 bytes (x.c0 || x.c1 || y.c0 || y.c1)
await bls12_381 . pairing ( input , output );
// Output interpretation:
// - 0x00...01: Pairing product equals 1 (valid)
// - 0x00...00: Pairing product not equal to 1 (invalid)
Input Format : Multiple of 384 bytes
Each pair: G1 (128 bytes) || G2 (256 bytes)
Output Format : 32 bytes
Last byte 0x01: Pairing check passed
Last byte 0x00: Pairing check failed
Pairing Check (BLS Signature Verification)
// Verify BLS signature
async function verifyBLSSignature (
signature : Uint8Array , // G1 point (128 bytes)
publicKey : Uint8Array , // G2 point (256 bytes)
message : Uint8Array , // Hashed to G1 (128 bytes)
generator : Uint8Array // G2 generator (256 bytes)
) : Promise < boolean > {
// Check: e(signature, G2) = e(H(msg), pubkey)
// Equivalent: e(signature, G2) * e(-H(msg), pubkey) = 1
const negatedMessage = negateG1 ( message );
const input = new Uint8Array ( 768 ); // 2 pairs * 384 bytes
input . set ( signature , 0 ); // Pair 1: signature, G2 gen
input . set ( generator , 128 );
input . set ( negatedMessage , 384 ); // Pair 2: -H(msg), pubkey
input . set ( publicKey , 512 );
const output = Bytes32 ();
await bls12_381 . pairing ( input , output );
return output [ 31 ] === 0x01 ;
}
Point Mapping
Map Field Element to G1
import { bls12_381 } from '@tevm/voltaire/crypto' ;
// Hash to curve: map Fp element to G1 point
const fpElement = Bytes64 (); // Padded field element
const g1Point = new Uint8Array ( 128 );
await bls12_381 . mapFpToG1 ( fpElement , g1Point );
Use case : Hash-to-curve for deterministic point generation
Map Field Element to G2
// Map Fp2 element to G2 point
const fp2Element = new Uint8Array ( 128 ); // c0 (64) || c1 (64)
const g2Point = new Uint8Array ( 256 );
await bls12_381 . mapFp2ToG2 ( fp2Element , g2Point );
Use Cases
BLS Signature Aggregation
// Aggregate multiple signatures
async function aggregateSignatures ( signatures : Uint8Array []) : Promise < Uint8Array > {
let aggregated = signatures [ 0 ];
for ( let i = 1 ; i < signatures . length ; i ++ ) {
const input = new Uint8Array ( 256 );
input . set ( aggregated , 0 );
input . set ( signatures [ i ], 128 );
const output = new Uint8Array ( 128 );
await bls12_381 . g1Add ( input , output );
aggregated = output ;
}
return aggregated ;
}
// Batch verify aggregated signature
async function batchVerifyAggregated (
aggregatedSignature : Uint8Array ,
publicKeys : Uint8Array [],
messages : Uint8Array []
) : Promise < boolean > {
// Aggregate public keys
const aggregatedPubKey = await aggregateG2Points ( publicKeys );
// Aggregate messages (hash to curve)
const aggregatedMessage = await aggregateG1Points ( messages );
// Single pairing check
return verifyBLSSignature (
aggregatedSignature ,
aggregatedPubKey ,
aggregatedMessage ,
G2_GENERATOR
);
}
Ethereum 2.0 Validator Signatures
// Verify sync committee aggregate signature
async function verifySyncCommitteeSignature (
signature : Uint8Array , // Aggregated BLS signature
publicKeys : Uint8Array [], // Validator public keys
signingRoot : Uint8Array // Block root being signed
) : Promise < boolean > {
// Map signing root to G1
const message = await hashToG1 ( signingRoot );
// Aggregate validator public keys
const aggregatedPubKey = await aggregateG2Points ( publicKeys );
// Verify aggregated signature
return verifyBLSSignature ( signature , aggregatedPubKey , message , G2_GENERATOR );
}
Implementation Details
C Library (BLST - Production)
Library : BLST (Supranational)
Location : lib/blst/ (git submodule)
Status : Audited, production-grade
Performance : Assembly-optimized for x86_64, ARM64
Features :
Constant-time operations
Side-channel resistant
Multi-scalar multiplication (Pippenger)
Compressed point support
Why BLST?
Official Ethereum Foundation recommendation
Used in all major Ethereum clients (Prysm, Lighthouse, Teku)
Extensive security audits (Trail of Bits, NCC Group)
Performance leader in benchmarks
Zig FFI Wrapper
Location : src/crypto/crypto.zig
Purpose : Safe Zig bindings to BLST C library
Features :
Error handling wrapper
Memory safety
Type-safe point validation
// Zig wrapper for BLS12-381 operations
pub const bls12_381 = struct {
pub fn g1Add ( input : [] const u8 , output : [] u8 ) Error ! void { ... }
pub fn g1Mul ( input : [] const u8 , output : [] u8 ) Error ! void { ... }
pub fn pairing ( input : [] const u8 , output : [] u8 ) Error ! void { ... }
// ...
};
TypeScript API
Location : src/crypto/crypto.zig (exported via FFI)
Runtime : Node.js native, Bun FFI, WASM
Validation : Automatic point validation on all operations
WASM Limitations
BLST unavailable in WASM - C library requires native compilation.
Alternatives :
noble/curves : Pure TS implementation (slower, ~10x)
Stub implementations : Return errors for unsupported platforms
// WASM builds may not support BLS12-381
import { bls12_381 } from '@tevm/voltaire/crypto' ;
try {
await bls12_381 . g1Add ( input , output );
} catch ( error ) {
console . error ( "BLS12-381 not available in WASM" );
}
Error Handling
BLS12-381 operations throw typed errors that extend CryptoError:
import { Bls12381 } from '@tevm/voltaire/crypto' ;
import {
InvalidScalarError ,
SignatureError ,
InvalidFieldElementError ,
InvalidPointError ,
PairingError
} from '@tevm/voltaire/crypto/Bls12381/errors' ;
// Private key validation
try {
const publicKey = Bls12381 . derivePublicKey ( new Uint8Array ( 32 )); // Zero key
} catch ( e ) {
if ( e instanceof InvalidScalarError ) {
console . log ( e . name ); // "InvalidScalarError"
console . log ( e . code ); // "BLS12381_INVALID_SCALAR"
console . log ( e . context ); // { ... }
}
}
// Signature operations
try {
const aggSig = Bls12381 . aggregate ([]); // Empty array
} catch ( e ) {
if ( e instanceof SignatureError ) {
console . log ( e . name ); // "SignatureError"
}
}
// Field operations
try {
const inv = Bls12381 . Fp . inv ( 0 n ); // Division by zero
} catch ( e ) {
if ( e instanceof InvalidFieldElementError ) {
console . log ( e . name ); // "InvalidFieldElementError"
}
}
Error Types :
Bls12381Error - Base error for BLS12-381 operations
InvalidScalarError - Invalid private key (extends InvalidPrivateKeyError)
SignatureError - Signature operation failed (extends InvalidSignatureError)
InvalidFieldElementError - Invalid field element
InvalidPointError - Point not on curve
InvalidSubgroupError - Point not in correct subgroup
PairingError - Pairing operation failed
Security Considerations
Production Requirements :
Use BLST library (audited, constant-time)
Validate all deserialized points
Check subgroup membership (especially G2)
Verify scalar range [1, r-1]
Point Validation :
// BLST performs automatic validation:
// - Point on curve check
// - Subgroup membership check (G2)
// - Infinity point handling
// Invalid points will throw typed errors
try {
await bls12_381 . g1Add ( input , output );
} catch ( error ) {
if ( error instanceof InvalidPointError ) {
// Handle invalid point
}
}
Signature Security :
Rogue key attacks : Prevented by proof-of-possession
Signature malleability : Use canonical point representations
Domain separation : Hash with context string for different message types
Timing Side-Channels :
BLST uses constant-time operations
No branching on secret data
Resistant to cache-timing attacks
Native (BLST on x86_64) :
G1 addition: ~0.015ms
G1 multiplication: ~0.08ms
G2 addition: ~0.025ms
G2 multiplication: ~0.2ms
Pairing: ~1.2ms
Pairing check (2 pairs): ~2ms
G1 MSM (100 points): ~8ms
Optimization Tips :
Batch operations with MSM
Precompute static points
Use compressed point formats
Aggregate signatures before verification
Constants
// Curve order (scalar field modulus)
const FR_MOD = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 n ;
// Base field modulus (381 bits)
const FP_MOD = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab n ;
// Embedding degree
const EMBEDDING_DEGREE = 12 ;
// Security level
const SECURITY_BITS = 128 ;
// G1 generator (compressed)
const G1_GENERATOR_COMPRESSED = new Uint8Array ([
0x97 , 0xf1 , 0xd3 , 0xa7 , /* ... 48 bytes total */
]);
// G2 generator (compressed)
const G2_GENERATOR_COMPRESSED = new Uint8Array ([
0x93 , 0xe0 , 0x2b , 0x6c , /* ... 96 bytes total */
]);
EIP-2537 Precompiles
Status : Proposed (not yet activated on mainnet)
Precompile Addresses :
0x0b: BLS12_G1ADD
0x0c: BLS12_G1MUL
0x0d: BLS12_G1MULTIEXP
0x0e: BLS12_G2ADD
0x0f: BLS12_G2MUL
0x10: BLS12_G2MULTIEXP
0x11: BLS12_PAIRING
0x12: BLS12_MAP_FP_TO_G1
0x13: BLS12_MAP_FP2_TO_G2
Gas Costs (EIP-2537):
G1 addition: 500 gas
G1 multiplication: 12,000 gas
Pairing (base): 115,000 gas
Pairing (per pair): 23,000 gas
References