> ## 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 Precompiles (EIP-2537)

> Nine precompiles for BLS12-381 curve operations enabling efficient signature aggregation

<Warning>
  **This page is a placeholder.** All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
</Warning>

## Overview

**Addresses:** `0x0B` - `0x13`
**Introduced:** Prague (planned)
**EIP:** [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537)

BLS12-381 is a pairing-friendly elliptic curve enabling efficient signature aggregation for Ethereum 2.0 consensus. These 9 precompiles provide gas-efficient operations on the BLS12-381 curve, supporting:

* BLS signature aggregation for Ethereum 2.0 validators
* Threshold signatures (t-of-n signing schemes)
* Blind signatures and anonymous credentials
* Verifiable random functions (VRFs)
* Advanced cryptographic protocols requiring pairings

## Curve Overview

**BLS12-381** is a pairing-friendly elliptic curve with:

* **Field modulus (p):** 381-bit prime
* **Curve order (r):** 255-bit prime subgroup
* **Embedding degree:** 12 (enables efficient pairings)
* **Security level:** 128-bit (comparable to AES-128)

The curve has two groups:

* **G1:** Points over the base field Fp (128 bytes uncompressed)
* **G2:** Points over the extension field Fp2 (256 bytes uncompressed)

**Pairing:** A bilinear map `e: G1 × G2 → GT` that enables verification of complex cryptographic relationships.

## Precompile Summary

| Address | Name             | Gas            | Input Size | Output Size | Description                       |
| ------- | ---------------- | -------------- | ---------- | ----------- | --------------------------------- |
| 0x0B    | G1\_ADD          | 500            | 256        | 128         | Add two G1 points                 |
| 0x0C    | G1\_MUL          | 12000          | 160        | 128         | Multiply G1 point by scalar       |
| 0x0D    | G1\_MSM          | variable       | 160k       | 128         | Multi-scalar multiplication on G1 |
| 0x0E    | G2\_ADD          | 800            | 512        | 256         | Add two G2 points                 |
| 0x0F    | G2\_MUL          | 45000          | 288        | 256         | Multiply G2 point by scalar       |
| 0x10    | G2\_MSM          | variable       | 288k       | 256         | Multi-scalar multiplication on G2 |
| 0x11    | PAIRING          | 65000 + 43000k | 384k       | 32          | Pairing check (k pairs)           |
| 0x12    | MAP\_FP\_TO\_G1  | 5500           | 64         | 128         | Hash to G1 (map field element)    |
| 0x13    | MAP\_FP2\_TO\_G2 | 75000          | 128        | 256         | Hash to G2 (map Fp2 element)      |

## Detailed Specifications

### 0x0B: G1\_ADD

Add two points on the G1 curve.

**Gas Cost:** 500

**Input:** 256 bytes

* Bytes 0-127: First G1 point (x₁, y₁)
  * Bytes 0-63: x-coordinate (big-endian, 48-byte field element padded to 64)
  * Bytes 64-127: y-coordinate (big-endian, 48-byte field element padded to 64)
* Bytes 128-255: Second G1 point (x₂, y₂)

**Output:** 128 bytes

* G1 point representing P₁ + P₂

**Operation:** Elliptic curve point addition on G1. Point at infinity encoded as (0, 0).

**Errors:**

* Invalid input length (not 256 bytes)
* Points not on curve
* Out of gas

***

### 0x0C: G1\_MUL

Multiply a G1 point by a scalar.

**Gas Cost:** 12000

**Input:** 160 bytes

* Bytes 0-127: G1 point (x, y)
  * Bytes 0-63: x-coordinate
  * Bytes 64-127: y-coordinate
* Bytes 128-159: Scalar (32 bytes, big-endian)

**Output:** 128 bytes

* G1 point representing scalar × P

**Operation:** Scalar multiplication on G1. Scalar is reduced modulo curve order.

**Errors:**

* Invalid input length (not 160 bytes)
* Point not on curve
* Out of gas

***

### 0x0D: G1\_MSM (Multi-Scalar Multiplication)

Compute a linear combination of G1 points: s₁P₁ + s₂P₂ + ... + sₖPₖ

**Gas Cost:** Variable with discount

```
gas = (12000 × k × discount) / 1000
```

where `k` = number of point-scalar pairs, and discount per tier:

| Pairs (k) | Discount | Gas per pair |   | Pairs (k) | Discount | Gas per pair |
| --------- | -------- | ------------ | - | --------- | -------- | ------------ |
| 1         | 1000     | 12000        |   | 16        | 320      | 3840         |
| 2         | 820      | 4920         |   | 32        | 250      | 3000         |
| 4         | 580      | 3480         |   | 64        | 200      | 2400         |
| 8         | 430      | 2580         |   | 128+      | 174      | 2088         |

**Input:** 160k bytes (k point-scalar pairs)

* Each pair: 160 bytes
  * Bytes 0-127: G1 point
  * Bytes 128-159: Scalar (32 bytes)

**Output:** 128 bytes

* G1 point representing the sum

**Operation:** Optimized batch scalar multiplication with Pippenger's algorithm. Discount reflects batch efficiency.

**Errors:**

* Invalid input length (not multiple of 160)
* Empty input
* Point not on curve
* Out of gas

***

### 0x0E: G2\_ADD

Add two points on the G2 curve.

**Gas Cost:** 800

**Input:** 512 bytes

* Bytes 0-255: First G2 point (x₁, y₁)
  * Bytes 0-127: x-coordinate (Fp2 element: c0 || c1, each 64 bytes)
  * Bytes 128-255: y-coordinate (Fp2 element: c0 || c1, each 64 bytes)
* Bytes 256-511: Second G2 point (x₂, y₂)

**Output:** 256 bytes

* G2 point representing P₁ + P₂

**Operation:** Elliptic curve point addition on G2. Point at infinity encoded as all zeros.

**Errors:**

* Invalid input length (not 512 bytes)
* Points not on curve
* Out of gas

***

### 0x0F: G2\_MUL

Multiply a G2 point by a scalar.

**Gas Cost:** 45000

**Input:** 288 bytes

* Bytes 0-255: G2 point (x, y)
  * Bytes 0-127: x-coordinate (Fp2)
  * Bytes 128-255: y-coordinate (Fp2)
* Bytes 256-287: Scalar (32 bytes, big-endian)

**Output:** 256 bytes

* G2 point representing scalar × P

**Operation:** Scalar multiplication on G2. More expensive than G1 due to Fp2 arithmetic.

**Errors:**

* Invalid input length (not 288 bytes)
* Point not on curve
* Out of gas

***

### 0x10: G2\_MSM (Multi-Scalar Multiplication)

Compute a linear combination of G2 points: s₁P₁ + s₂P₂ + ... + sₖPₖ

**Gas Cost:** Variable with discount

```
gas = (45000 × k × discount) / 1000
```

Uses same discount table as G1\_MSM. Base cost is 45000 (G2\_MUL cost).

**Input:** 288k bytes (k point-scalar pairs)

* Each pair: 288 bytes
  * Bytes 0-255: G2 point
  * Bytes 256-287: Scalar (32 bytes)

**Output:** 256 bytes

* G2 point representing the sum

**Operation:** Optimized batch scalar multiplication on G2.

**Errors:**

* Invalid input length (not multiple of 288)
* Empty input
* Point not on curve
* Out of gas

***

### 0x11: BLS12\_PAIRING

Verify a pairing equation: e(P₁, Q₁) × e(P₂, Q₂) × ... × e(Pₖ, Qₖ) = 1

**Gas Cost:** 65000 + 43000k

* Base: 65000
* Per pair: 43000

**Input:** 384k bytes (k pairs, k ≥ 0)

* Each pair: 384 bytes
  * Bytes 0-127: G1 point (128 bytes)
  * Bytes 128-383: G2 point (256 bytes)
* Empty input (k=0) is valid and returns success

**Output:** 32 bytes

* Byte 31: 1 if pairing check succeeds, 0 otherwise
* Bytes 0-30: Zero padding

**Operation:** Compute optimal Ate pairing for each (G1, G2) pair, multiply results, check if product equals 1.

**Use Case:** BLS signature verification

* Verify: e(pubkey, H(msg)) = e(G1\_generator, signature)
* Input: \[G1\_generator, signature, -pubkey, H(msg)]
* Rearranged: e(G1, sig) × e(-pub, H) = 1

**Errors:**

* Invalid input length (not multiple of 384)
* Points not on curve
* Out of gas

***

### 0x12: BLS12\_MAP\_FP\_TO\_G1

Map a field element to a G1 point (hash-to-curve).

**Gas Cost:** 5500

**Input:** 64 bytes

* Field element in Fp (48-byte big-endian, padded to 64 bytes)

**Output:** 128 bytes

* G1 point

**Operation:** Deterministic hash-to-curve mapping using simplified SWU (Shallue-van de Woestijne-Ulas) method. Maps arbitrary field elements to valid curve points.

**Use Case:** Hash-to-curve for BLS signatures

* H(message) → G1 point for signing

**Errors:**

* Invalid input length (not 64 bytes)
* Field element ≥ field modulus
* Out of gas

***

### 0x13: BLS12\_MAP\_FP2\_TO\_G2

Map an Fp2 element to a G2 point (hash-to-curve).

**Gas Cost:** 75000

**Input:** 128 bytes

* Fp2 element (c0 || c1, each 64 bytes)
  * Bytes 0-63: c0 component (48-byte field element padded to 64)
  * Bytes 64-127: c1 component (48-byte field element padded to 64)

**Output:** 256 bytes

* G2 point

**Operation:** Deterministic hash-to-curve mapping for G2 using simplified SWU over Fp2.

**Use Case:** Hash messages to G2 for signature schemes where public keys are in G1.

**Errors:**

* Invalid input length (not 128 bytes)
* Field elements ≥ field modulus
* Out of gas

## Point Encoding

### G1 Point (128 bytes)

```
[x-coordinate (64 bytes)][y-coordinate (64 bytes)]
```

* Each coordinate: 48-byte big-endian field element, left-padded with 16 zero bytes
* Point at infinity: all zeros

### G2 Point (256 bytes)

```
[x.c0 (64)][x.c1 (64)][y.c0 (64)][y.c1 (64)]
```

* Each Fp2 element: two 48-byte field elements (c0, c1), each padded to 64 bytes
* Point at infinity: all zeros

### Field Element (Fp)

* 48-byte big-endian integer \< field modulus p
* Padded to 64 bytes with leading zeros
* p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab

## Complete BLS Signature Workflow

This example shows how multiple BLS12-381 precompiles work together for a complete signature verification:

```typescript theme={null}
import { execute, PrecompileAddress, Hardfork } from '@tevm/voltaire/precompiles';
import { Keccak256 } from '@tevm/voltaire/Keccak256';

/**
 * Complete BLS signature verification workflow
 * Uses 5 precompiles: MAP_FP2_TO_G2, G1_MUL, G2_MUL, G1_ADD (optional), PAIRING
 */

// Step 1: Hash message to G2 point using MAP_FP2_TO_G2
function hashMessageToG2(message: Uint8Array): Uint8Array {
  // Hash message to field elements (simplified - real impl uses hash_to_field)
  const hash1 = Keccak256.hash(message);
  const hash2 = Keccak256.hash(hash1);

  // Create two Fp2 elements
  const u0 = new Uint8Array(128);
  u0.set(hash1, 96); // Place hash in lower 32 bytes of c0

  const u1 = new Uint8Array(128);
  u1.set(hash2, 96);

  // Map both to G2 points
  const q0 = execute(PrecompileAddress.BLS12_MAP_FP2_TO_G2, u0, 75000n, Hardfork.PRAGUE);
  const q1 = execute(PrecompileAddress.BLS12_MAP_FP2_TO_G2, u1, 75000n, Hardfork.PRAGUE);

  if (!q0.success || !q1.success) throw new Error('Hash-to-curve failed');

  // Add points: H(m) = Q0 + Q1
  const addInput = new Uint8Array(512);
  addInput.set(q0.output, 0);
  addInput.set(q1.output, 256);

  const result = execute(PrecompileAddress.BLS12_G2_ADD, addInput, 800n, Hardfork.PRAGUE);
  if (!result.success) throw new Error('G2 addition failed');

  return result.output; // 256-byte G2 point: H(m)
}

// Step 2: Generate BLS signature: sig = sk * H(m)
function signMessage(secretKey: bigint, messageHash: Uint8Array): Uint8Array {
  // messageHash is G2 point from hashMessageToG2
  const input = new Uint8Array(288);
  input.set(messageHash, 0); // G2 point (256 bytes)

  // Encode scalar at offset 256
  const scalarBytes = Bytes32();
  for (let i = 0; i < 32; i++) {
    scalarBytes[31 - i] = Number((secretKey >> BigInt(i * 8)) & 0xFFn);
  }
  input.set(scalarBytes, 256);

  const result = execute(PrecompileAddress.BLS12_G2_MUL, input, 45000n, Hardfork.PRAGUE);
  if (!result.success) throw new Error('Signing failed');

  return result.output; // 256-byte signature in G2
}

// Step 3: Derive public key: PK = sk * G1
function derivePublicKey(secretKey: bigint): Uint8Array {
  // G1 generator point (these are the actual BLS12-381 generator coordinates)
  const g1Generator = new Uint8Array(128);
  // x-coordinate (48 bytes, left-padded to 64)
  g1Generator.set([
    0x17, 0xf1, 0xd3, 0xa7, 0x31, 0x97, 0xd7, 0x94,
    0x26, 0x95, 0x63, 0x8c, 0x4f, 0xa9, 0xac, 0x0f,
    0xc3, 0x68, 0x8c, 0x4f, 0x97, 0x74, 0xb9, 0x05,
    0xa1, 0x4e, 0x3a, 0x3f, 0x17, 0x1b, 0xac, 0x58,
    0x6c, 0x55, 0xe8, 0x3f, 0xf9, 0x7a, 0x1a, 0xef,
    0xfb, 0x3a, 0xf0, 0x0a, 0xdb, 0x22, 0xc6, 0xbb,
  ], 16);
  // y-coordinate (48 bytes, left-padded to 64)
  g1Generator.set([
    0x08, 0xb3, 0xf4, 0x81, 0xe3, 0xaa, 0xa0, 0xf1,
    0xa0, 0x9e, 0x30, 0xed, 0x74, 0x1d, 0x8a, 0xe4,
    0xfc, 0xf5, 0xe0, 0x95, 0xd5, 0xd0, 0x0a, 0xf6,
    0x00, 0xdb, 0x18, 0xcb, 0x2c, 0x04, 0xb3, 0xed,
    0xd0, 0x3c, 0xc7, 0x44, 0xa2, 0x88, 0x8a, 0xe4,
    0x0c, 0xaa, 0x23, 0x29, 0x46, 0xc5, 0xe7, 0xe1,
  ], 80);

  const input = new Uint8Array(160);
  input.set(g1Generator, 0);

  // Encode scalar
  const scalarBytes = Bytes32();
  for (let i = 0; i < 32; i++) {
    scalarBytes[31 - i] = Number((secretKey >> BigInt(i * 8)) & 0xFFn);
  }
  input.set(scalarBytes, 128);

  const result = execute(PrecompileAddress.BLS12_G1_MUL, input, 12000n, Hardfork.PRAGUE);
  if (!result.success) throw new Error('Public key derivation failed');

  return result.output; // 128-byte public key in G1
}

// Step 4: Verify BLS signature using pairing check
// Check: e(PK, H(m)) = e(G1, sig)
// Rearranged: e(PK, H(m)) * e(-G1, sig) = 1
function verifySignature(
  publicKey: Uint8Array,    // 128 bytes (G1)
  message: Uint8Array,
  signature: Uint8Array     // 256 bytes (G2)
): boolean {
  // Hash message to G2
  const messageHash = hashMessageToG2(message);

  // Get negated G1 generator
  const g1Generator = new Uint8Array(128);
  g1Generator.set([
    0x17, 0xf1, 0xd3, 0xa7, 0x31, 0x97, 0xd7, 0x94,
    0x26, 0x95, 0x63, 0x8c, 0x4f, 0xa9, 0xac, 0x0f,
    0xc3, 0x68, 0x8c, 0x4f, 0x97, 0x74, 0xb9, 0x05,
    0xa1, 0x4e, 0x3a, 0x3f, 0x17, 0x1b, 0xac, 0x58,
    0x6c, 0x55, 0xe8, 0x3f, 0xf9, 0x7a, 0x1a, 0xef,
    0xfb, 0x3a, 0xf0, 0x0a, 0xdb, 0x22, 0xc6, 0xbb,
  ], 16);
  // Negated y-coordinate (would need actual negation - simplified here)
  g1Generator.set([
    0x08, 0xb3, 0xf4, 0x81, 0xe3, 0xaa, 0xa0, 0xf1,
    0xa0, 0x9e, 0x30, 0xed, 0x74, 0x1d, 0x8a, 0xe4,
    0xfc, 0xf5, 0xe0, 0x95, 0xd5, 0xd0, 0x0a, 0xf6,
    0x00, 0xdb, 0x18, 0xcb, 0x2c, 0x04, 0xb3, 0xed,
    0xd0, 0x3c, 0xc7, 0x44, 0xa2, 0x88, 0x8a, 0xe4,
    0x0c, 0xaa, 0x23, 0x29, 0x46, 0xc5, 0xe7, 0xe1,
  ], 80);

  // Construct pairing input: 2 pairs (768 bytes)
  const pairingInput = new Uint8Array(768);

  // Pair 1: (PK, H(m))
  pairingInput.set(publicKey, 0);        // G1 point (128 bytes)
  pairingInput.set(messageHash, 128);    // G2 point (256 bytes)

  // Pair 2: (-G1, sig)
  pairingInput.set(g1Generator, 384);    // G1 point (128 bytes)
  pairingInput.set(signature, 512);      // G2 point (256 bytes)

  // Pairing check gas: 115,000 + 23,000 * 2 = 161,000
  const result = execute(
    PrecompileAddress.BLS12_PAIRING,
    pairingInput,
    161000n,
    Hardfork.PRAGUE
  );

  if (!result.success) throw new Error('Pairing check failed');

  // Check if pairing result is 1 (success)
  return result.output[31] === 1;
}

// Complete workflow example
const secretKey = 12345678901234567890n;
const message = new TextEncoder().encode("Hello, BLS12-381!");

console.log("=== BLS Signature Workflow ===");

// 1. Derive public key
const publicKey = derivePublicKey(secretKey);
console.log("Public key generated (G1, 128 bytes)");

// 2. Hash message
const messageHash = hashMessageToG2(message);
console.log("Message hashed to G2 (256 bytes)");

// 3. Sign message
const signature = signMessage(secretKey, messageHash);
console.log("Signature generated (G2, 256 bytes)");

// 4. Verify signature
const isValid = verifySignature(publicKey, message, signature);
console.log("Signature valid:", isValid);

// Total gas used:
// - Hash to G2: 2 * 75,000 + 800 = 150,800
// - Derive PK: 12,000
// - Sign: 45,000
// - Verify (pairing): 161,000
// Total: ~368,800 gas for complete workflow
```

## Usage Examples

### TypeScript

```typescript theme={null}
import { execute, PrecompileAddress, Hardfork } from '@tevm/voltaire/precompiles';

// G1 Addition
const p1 = new Uint8Array(128); // First G1 point
const p2 = new Uint8Array(128); // Second G1 point
const addInput = new Uint8Array(256);
addInput.set(p1, 0);
addInput.set(p2, 128);

const result = execute(
  PrecompileAddress.BLS12_G1_ADD,
  addInput,
  10000n,
  Hardfork.PRAGUE
);

if (result.success) {
  console.log('Sum:', result.output); // 128 bytes
  console.log('Gas used:', result.gasUsed); // 500
}

// Multi-scalar multiplication (G1)
const k = 4; // 4 point-scalar pairs
const msmInput = new Uint8Array(160 * k);
// Fill with points and scalars...

const msmResult = execute(
  PrecompileAddress.BLS12_G1_MSM,
  msmInput,
  50000n,
  Hardfork.PRAGUE
);
// Gas used: (12000 × 4 × 580) / 1000 = 27840

// BLS Signature Verification via Pairing
// Verify: e(G1, sig) × e(-pubkey, H(msg)) = 1
const G1_gen = new Uint8Array(128); // Generator point
const signature = new Uint8Array(256); // G2 signature
const negPubkey = new Uint8Array(128); // Negated public key (G1)
const msgHash = new Uint8Array(256); // H(message) in G2

const pairingInput = new Uint8Array(384 * 2);
pairingInput.set(G1_gen, 0);
pairingInput.set(signature, 128);
pairingInput.set(negPubkey, 384);
pairingInput.set(msgHash, 512);

const pairingResult = execute(
  PrecompileAddress.BLS12_PAIRING,
  pairingInput,
  200000n,
  Hardfork.PRAGUE
);

const isValid = pairingResult.output[31] === 1;
console.log('Signature valid:', isValid);
```

## Implementation Status

### Zig: Complete

All 9 precompiles fully implemented in `/Users/williamcory/tevm/src/precompiles/`:

* `bls12_g1_add.zig`
* `bls12_g1_mul.zig`
* `bls12_g1_msm.zig`
* `bls12_g2_add.zig`
* `bls12_g2_mul.zig`
* `bls12_g2_msm.zig`
* `bls12_pairing.zig`
* `bls12_map_fp_to_g1.zig`
* `bls12_map_fp2_to_g2.zig`

Delegates to `crypto.Crypto.bls12_381.*` functions which wrap the audited **blst** C library.

### TypeScript: Stubs Only

**Warning:** TypeScript implementations in `src/precompiles/precompiles.ts` are currently stubs that:

* Return correctly sized zero-filled outputs
* Calculate gas costs accurately
* Provide type safety and interfaces

**No actual cryptographic computation is performed.** For production use, call Zig/WASM implementations.

### WASM: Available

BLS12-381 operations available via compiled Zig implementation.

## Use Cases

### Ethereum 2.0 Consensus

**BLS signature aggregation** enables efficient validator consensus:

* Aggregate 1000s of validator signatures into single 96-byte signature
* Single pairing check verifies all signatures
* Massively reduces bandwidth and verification time

### Threshold Signatures

**t-of-n signing schemes:**

* Distribute key shares to n parties
* Any t parties can jointly sign
* Applications: multisig wallets, distributed custody, governance

### Blind Signatures

**Anonymous credentials:**

* Signer signs message without seeing content
* User unblinds signature
* Applications: anonymous voting, privacy-preserving authentication

### Verifiable Random Functions (VRFs)

**Provable randomness:**

* Generate random value with cryptographic proof
* Anyone can verify randomness is correct
* Applications: lotteries, random leader election, proof-of-stake

### SNARKs and zkSNARKs

**Zero-knowledge proofs:**

* Prove statement without revealing witness
* Pairing-based SNARKs (like Groth16) require BN254 and BLS12-381
* Applications: privacy, scalability (rollups)

## Gas Optimization

### MSM Discount Strategies

Multi-scalar multiplication benefits from batch discounts:

```typescript theme={null}
// Bad: Individual multiplications
let sum = pointAtInfinity;
for (const [point, scalar] of pairs) {
  sum = execute(PrecompileAddress.BLS12_G1_MUL, ...); // 12000 gas each
}
// Total for 16 pairs: 16 × 12000 = 192000 gas

// Good: Batch MSM
const msmInput = concatenate(pairs); // 160 bytes × 16 = 2560 bytes
const result = execute(PrecompileAddress.BLS12_G1_MSM, msmInput, ...);
// Total: (12000 × 16 × 320) / 1000 = 61440 gas
// Savings: 68% reduction
```

### Signature Aggregation

Aggregate before verification:

```typescript theme={null}
// Bad: Verify 10 signatures individually
for (const sig of signatures) {
  pairing(pubkey, msg, sig); // 108000 gas each
}
// Total: 1080000 gas

// Good: Aggregate then verify once
const aggSig = aggregateSignatures(signatures);
pairing(aggPubkey, msg, aggSig); // 108000 gas
// Savings: 90% reduction
```

## Security Considerations

### Subgroup Checks

All operations enforce subgroup membership:

* Points must be in prime-order subgroup
* Prevents small subgroup attacks
* Performed automatically by blst library

### Point Validation

Input points are validated:

* Must satisfy curve equation: y² = x³ + 4
* Coordinates must be in field (\< field modulus)
* Invalid points return error (no result)

### Side-Channel Resistance

**blst** library provides:

* Constant-time scalar multiplication
* Protection against timing attacks
* Hardware-optimized assembly for major platforms

### Known Limitations

**TypeScript stubs:** Do not use TS implementations for security-critical operations. Always use Zig/WASM for actual cryptography.

**WASM:** BLS12-381 operations are available in WASM builds but inherit platform security constraints (no hardware acceleration).

## Performance

### Hardware Optimization

**blst** library features:

* Assembly implementations for x86\_64, ARM64
* AVX2/AVX512 optimizations when available
* Fallback portable C implementation

### Benchmarks

Approximate gas costs and execution times (hardware-dependent):

| Operation    | Gas    | Approx. Time | Throughput   |
| ------------ | ------ | ------------ | ------------ |
| G1\_ADD      | 500    | \~10 μs      | 100K ops/s   |
| G1\_MUL      | 12000  | \~200 μs     | 5K ops/s     |
| G1\_MSM (16) | 61440  | \~1 ms       | 16K points/s |
| G2\_ADD      | 800    | \~20 μs      | 50K ops/s    |
| G2\_MUL      | 45000  | \~800 μs     | 1.2K ops/s   |
| PAIRING (2)  | 151000 | \~5 ms       | 400 pairs/s  |

## Implementation Details

### Zig → blst C Library

All precompiles delegate to `src/crypto/crypto.zig`:

```zig theme={null}
pub const bls12_381 = struct {
    pub fn g1Add(input: []const u8, output: []u8) !void;
    pub fn g1Mul(input: []const u8, output: []u8) !void;
    pub fn g1Msm(input: []const u8, output: []u8) !void;
    pub fn g2Add(input: []const u8, output: []u8) !void;
    pub fn g2Mul(input: []const u8, output: []u8) !void;
    pub fn g2Msm(input: []const u8, output: []u8) !void;
    pub fn pairingCheck(input: []const u8) !bool;
    pub fn mapFpToG1(input: []const u8, output: []u8) !void;
    pub fn mapFp2ToG2(input: []const u8, output: []u8) !void;
};
```

These wrap **blst** (lib/blst/), a production-grade BLS12-381 library:

* Audited by NCC Group and Trail of Bits
* Used by Ethereum 2.0 clients (Prysm, Lighthouse)
* Constant-time operations, side-channel resistant

## Related

* [Crypto: BLS12-381](/crypto/bls12-381) - Underlying cryptographic primitives
* [Precompiles: BN254](/crypto/bn254) - Alternative pairing-friendly curve (cheaper, less secure)
* [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) - Official specification
* [blst library](https://github.com/supranational/blst) - C implementation
* [BLS Signatures](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature) - IETF specification

## References

* **EIP-2537:** BLS12-381 curve operations
  [https://eips.ethereum.org/EIPS/eip-2537](https://eips.ethereum.org/EIPS/eip-2537)

* **BLS12-381 Spec:**
  [https://hackmd.io/@benjaminion/bls12-381](https://hackmd.io/@benjaminion/bls12-381)

* **Hash-to-Curve:**
  [https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve)

* **blst Documentation:**
  [https://github.com/supranational/blst](https://github.com/supranational/blst)

* **Ethereum 2.0 BLS:**
  [https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#bls-signatures](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#bls-signatures)
