Skip to main content

Overview

Address: 0x000000000000000000000000000000000000000f Introduced: Prague (EIP-2537) EIP: EIP-2537 The BLS12-381 G2 Mul precompile performs scalar multiplication on the BLS12-381 curve’s G2 group. It multiplies a G2 point by a scalar, computing scalar * point. This operation is fundamental for BLS signature schemes, zero-knowledge proofs, and cryptographic protocols requiring operations over extension fields. BLS12-381 provides 128 bits of security and is the foundation of Ethereum 2.0’s consensus layer signature scheme.

Gas Cost

Fixed: 45000 gas

G2 vs G1

G2 scalar multiplication operates on points over the Fp2 extension field:
  • G1 points: 128 bytes (2 Fp coordinates)
  • G2 points: 256 bytes (2 Fp2 coordinates)
  • G1 mul gas: 12,000
  • G2 mul gas: 45,000 (3.75x more expensive)
  • Cost driver: Extension field arithmetic is significantly more complex

Input Format

Offset | Length | Description
-------|--------|-------------
0      | 64     | x.c0 (point x-coordinate c0 component, big-endian)
64     | 64     | x.c1 (point x-coordinate c1 component, big-endian)
128    | 64     | y.c0 (point y-coordinate c0 component, big-endian)
192    | 64     | y.c1 (point y-coordinate c1 component, big-endian)
256    | 32     | scalar (multiplier, big-endian)
Total input length: 288 bytes (256 bytes G2 point + 32 bytes scalar) G2 point must satisfy curve equation: y^2 = x^3 + 4(1 + u) over Fp2. Scalar can be any 256-bit value (automatically reduced modulo curve order).

Output Format

Offset | Length | Description
-------|--------|-------------
0      | 64     | x.c0 (result point x-coordinate c0 component, big-endian)
64     | 64     | x.c1 (result point x-coordinate c1 component, big-endian)
128    | 64     | y.c0 (result point y-coordinate c0 component, big-endian)
192    | 64     | y.c1 (result point y-coordinate c1 component, big-endian)
Total output length: 256 bytes

Usage Example

TypeScript

import { execute, PrecompileAddress } from '@tevm/voltaire/precompiles';
import { Hardfork } from '@tevm/voltaire/primitives/Hardfork';

// Multiply G2 point by scalar
// Using point at infinity (valid edge case: O * k = O)
const point = new Uint8Array(256); // All zeros = point at infinity

const scalar = Bytes32('0x0000000000000000000000000000000000000000000000000000000000000005'); // multiply by 5

const input = new Uint8Array(288);
input.set(point, 0);
input.set(scalar, 256);

const result = execute(
  PrecompileAddress.BLS12_G2_MUL,
  input,
  50000n,
  Hardfork.PRAGUE
);

if (result.success) {
  const resultPoint = result.output; // 256 bytes
  const xc0 = result.output.slice(0, 64);
  const xc1 = result.output.slice(64, 128);
  const yc0 = result.output.slice(128, 192);
  const yc1 = result.output.slice(192, 256);
  console.log('Result G2 point:', { xc0, xc1, yc0, yc1 });
  console.log('Gas used:', result.gasUsed); // 45000
} else {
  console.error('Error:', result.error);
}

Zig

const std = @import("std");
const precompiles = @import("precompiles");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Create input: G2 point (256 bytes) + scalar (32 bytes)
    var input = [_]u8{0} ** 288;
    // ... populate G2 point coordinates
    input[287] = 5; // scalar = 5

    // Execute G2 multiplication
    const result = try precompiles.bls12_g2_mul.execute(
        allocator,
        &input,
        100000
    );
    defer result.deinit(allocator);

    std.debug.print("Gas used: {}\n", .{result.gas_used}); // 45000
    std.debug.print("Output length: {}\n", .{result.output.len}); // 256
}

Error Conditions

  • Out of gas: gasLimit < 45,000
  • Invalid input length: input.len != 288
  • Point not on curve: coordinates don’t satisfy G2 curve equation
  • Invalid field element: coordinate component >= field modulus
  • Invalid point: point not in correct subgroup

Use Cases

  • BLS signature verification: Key derivation and signature operations
  • Threshold signatures: Generate signature shares
  • Key generation: Derive public keys from private scalars
  • Commitment schemes: Pedersen-like commitments over G2
  • Zero-knowledge proofs: zkSNARKs and zkSTARKs on BLS12-381
  • Ethereum 2.0: Validator key operations

Implementation Details

  • Zig: Uses BLST library optimized for BLS12-381
  • TypeScript: Wraps @noble/curves bls12-381 G2 operations
  • Algorithm: Windowed scalar multiplication for efficiency
  • Security: Constant-time execution prevents timing attacks
  • Optimization: Double-and-add with precomputed tables

Special Cases

  • Scalar = 0: Returns point at infinity (256 bytes of zeros)
  • Scalar = 1: Returns input point unchanged
  • Scalar = group order: Returns point at infinity (r*P = O)
  • Point at infinity input: Returns point at infinity regardless of scalar
  • Scalar > group order: Automatically reduced modulo group order

Scalar Arithmetic

Scalars are elements of F_r where r is the curve order:
  • Group order (r): Same as BLS12-381 scalar field order
  • Modular reduction: Scalars wrap around modulo r
  • Zero scalar: Always produces point at infinity
  • Negative scalars: Equivalent to positive via modular arithmetic

Extension Field Operations

G2 scalar multiplication involves Fp2 arithmetic:
  • Field elements: a = a.c0 + a.c1*u where u^2 + 1 = 0
  • Point doubling: Requires Fp2 squaring and multiplication
  • Point addition: Complex formula over extension field
  • Cost: Each Fp2 operation is ~3-4x more expensive than Fp
This complexity explains why G2 mul is 3.75x more expensive than G1 mul.

Gas Comparison

OperationG1 GasG2 GasRatio
Addition5008001.6x
Multiplication12,00045,0003.75x
MSM (base)12,00045,0003.75x
The multiplication cost ratio reflects the increased complexity of extension field arithmetic.

Performance Considerations

  • Expensive operation: 45,000 gas is substantial
  • Batch with MSM: For multiple scalar muls, use G2 MSM with discount
  • Precomputation: Cache commonly used multiples when possible
  • G1 vs G2 choice: Use G1 operations when either group works
  • Signature verification: Typically requires 1-2 G2 muls

Test Vectors

// Generator * 0 = Identity
const scalar = 0n;
const result = bls12G2Mul(G2_GENERATOR, scalar);
// result = point at infinity (256 bytes of zeros)

// Generator * 1 = Generator
const result = bls12G2Mul(G2_GENERATOR, 1n);
// result = G2_GENERATOR

// Point * group_order = Identity
const result = bls12G2Mul(somePoint, groupOrder);
// result = point at infinity

BLS Signature Context

In BLS signature schemes:
  • Public keys: Often G2 points derived via scalar multiplication
  • Signature verification: Requires G2 scalar operations
  • Key aggregation: Combine public keys via G2 addition
  • Threshold schemes: Generate key shares with G2 mul

Security Considerations

  • 128-bit security: BLS12-381 provides quantum-resistant classical security
  • Side-channel resistance: Constant-time implementation prevents timing attacks
  • Subgroup checks: Implementation validates points are in correct subgroup
  • Field validation: Coordinates must be valid field elements

Gas Cost Justification

The 45,000 gas cost reflects:
  1. Extension field arithmetic: Fp2 operations are computationally intensive
  2. Security overhead: Subgroup and validity checks
  3. Scalar multiplication: Requires ~255 point operations on average
  4. Memory operations: 256-byte point representation
Compared to BN254 mul (6,000 gas), the higher cost accounts for stronger security and extension field complexity.