> ## 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.

# BLS12-381

> Pairing-friendly curve for Ethereum 2.0 consensus signatures and EIP-2537 precompiles

<Card title="Try it Live" icon="play" href="https://playground.tevm.sh?example=crypto/bls12381.ts">
  Run BLS12-381 examples in the interactive playground
</Card>

<Info>
  Source: [bls12\_381.zig](https://github.com/evmts/voltaire/blob/main/src/crypto/bls12_381.zig)

  Tests: [bls12\_g2\_operations.test.ts](https://github.com/evmts/voltaire/blob/main/src/precompiles/bls12_g2_operations.test.ts)
</Info>

# 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

```typescript theme={null}
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:

1. **Efficient Pairings**: Optimal ate pairing computable in \~1-2ms
2. **Signature Aggregation**: Combine multiple signatures into one
3. **Batch Verification**: Verify many signatures in one pairing check
4. **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

```typescript theme={null}
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

```typescript theme={null}
// 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)

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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)

```typescript theme={null}
// 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

```typescript theme={null}
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

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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 theme={null}
// 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**:

1. **noble/curves**: Pure TS implementation (slower, \~10x)
2. **Stub implementations**: Return errors for unsupported platforms

```typescript theme={null}
// 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`:

```typescript theme={null}
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(0n); // 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**:

```typescript theme={null}
// 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

## Performance

**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

```typescript theme={null}
// Curve order (scalar field modulus)
const FR_MOD = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n;

// Base field modulus (381 bits)
const FP_MOD = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;

// 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

## Related

* [Precompiles: BLS12-381 Operations](/evm/precompiles) - EIP-2537 implementation
* [BN254](/crypto/bn254) - Alternative pairing curve for zkSNARKs
* [KZG Commitments](/crypto/kzg) - Polynomial commitments using BLS12-381

## References

* [EIP-2537: Precompile for BLS12-381 curve operations](https://eips.ethereum.org/EIPS/eip-2537)
* [BLST Library](https://github.com/supranational/blst) - Production implementation
* [BLS Signatures Spec](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#bls-signatures)
* [Hash to Curve (draft-irtf-cfrg-hash-to-curve)](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
