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

# 0x12 BLS12-381 Map Fp to G1

> Deterministic hash-to-curve mapping from base field element to G1 point

<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

**Address:** `0x0000000000000000000000000000000000000012`
**Introduced:** Prague (EIP-2537)
**EIP:** [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537)

The BLS12-381 Map Fp to G1 precompile maps a field element from the base field Fp to a point on the G1 curve. This is a core building block for hash-to-curve operations, enabling deterministic point generation for BLS signatures, VRFs, and other cryptographic protocols.

Hash-to-curve provides a way to hash arbitrary messages to curve points in a way that is:

* **Deterministic:** Same input always produces same output
* **Uniform:** Output distribution is indistinguishable from random
* **One-way:** Cannot reverse the mapping
* **Collision-resistant:** Hard to find different inputs mapping to same point

## Hash-to-Curve Overview

The complete hash-to-curve process typically involves:

1. **Hash message to field elements** using hash\_to\_field (external)
2. **Map field elements to curve points** using this precompile (0x12)
3. **Clear cofactor** to ensure point is in correct subgroup (if needed)

This precompile implements step 2: the deterministic mapping from a field element to a G1 curve point.

## Gas Cost

**Fixed cost:** `5,500` gas (constant, independent of input)

Much cheaper than elliptic curve operations since it's a single mapping operation without scalar multiplication.

## Input Format

```
Offset | Length | Description
-------|--------|-------------
0      | 64     | Field element in Fp (big-endian, padded)
```

Total input length: **64 bytes** (exactly)

**Field element constraints:**

* Must be \< field modulus p
* Big-endian encoding
* Left-padded with zeros to 64 bytes
* All values 0 to p-1 are valid inputs

**BLS12-381 base field modulus p:**

```
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
```

(381-bit prime, \~48 bytes, padded to 64)

## Output Format

```
Offset | Length | Description
-------|--------|-------------
0      | 64     | x coordinate (big-endian, padded)
64     | 64     | y coordinate (big-endian, padded)
```

Total output length: **128 bytes** (G1 point in uncompressed form)

Output is always a valid G1 point on the curve. The mapping ensures:

* Point is on curve: y² = x³ + 4
* Point is in correct subgroup (after cofactor clearing if protocol requires)
* Mapping is deterministic and injective

## Usage Examples

### TypeScript

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

// Map a hash to a G1 point for signature schemes
function hashToG1Point(message: Uint8Array): Uint8Array {
  // Step 1: Hash message to field element
  const hash = Keccak256.hash(message);

  // Pad to 64 bytes (field element size)
  const fpElement = Bytes64(hash); // Automatically handles padding

  // Step 2: Map to G1 using precompile
  const result = execute(
    PrecompileAddress.BLS12_MAP_FP_TO_G1,
    fpElement,
    5500n,
    Hardfork.PRAGUE
  );

  if (!result.success) {
    throw new Error(`Map failed: ${result.error}`);
  }

  return result.output; // 128-byte G1 point
}

// Use in BLS signature scheme
const message = new TextEncoder().encode("Sign this message");
const messagePoint = hashToG1Point(message);
console.log('Message mapped to G1 point:', messagePoint);
```

### Zig

```zig theme={null}
const std = @import("std");
const precompiles = @import("precompiles");
const crypto = @import("crypto");

/// Hash message to G1 point for BLS signatures
pub fn hashToG1(
    allocator: std.mem.Allocator,
    message: []const u8,
) ![]u8 {
    // Step 1: Hash to field element
    var fp_element: [64]u8 = undefined;
    @memset(&fp_element, 0);

    const hash = crypto.Crypto.keccak256(message);
    // Right-align hash in 64-byte buffer
    @memcpy(fp_element[32..64], &hash);

    // Step 2: Map to G1
    const result = try precompiles.bls12_map_fp_to_g1.execute(
        allocator,
        &fp_element,
        5500,
    );

    return result.output; // Caller owns memory
}

test "hash message to G1" {
    const msg = "Hello BLS12-381";
    const point = try hashToG1(std.testing.allocator, msg);
    defer std.testing.allocator.free(point);

    try std.testing.expectEqual(@as(usize, 128), point.len);
    // Point should not be point at infinity
    const is_zero = for (point) |byte| {
        if (byte != 0) break false;
    } else true;
    try std.testing.expect(!is_zero);
}
```

## Error Conditions

* **Out of gas:** Gas limit less than 5,500
* **Invalid input length:** Input not exactly 64 bytes
* **Field element overflow:** Input value >= field modulus p
* **Invalid encoding:** Malformed field element

Note: All field elements in range \[0, p-1] are valid inputs and will successfully map to G1 points.

## Use Cases

### BLS Signature Hash-to-Curve

BLS signatures require hashing messages to curve points:

```typescript theme={null}
// BLS signature: sig = H(m)^sk where H maps to G2
// For G1 variant: sig = sk * H(m) where H maps to G1

function signMessage(secretKey: bigint, message: Uint8Array): Uint8Array {
  // Hash message to G1 point
  const messagePoint = hashToG1(message);

  // Multiply by secret key (use G1_MUL precompile 0x0c)
  // signature = secretKey * messagePoint
  // ...
  return signature;
}
```

### Verifiable Random Functions (VRF)

VRFs use hash-to-curve for deterministic randomness:

```typescript theme={null}
// VRF: Prove you know secret key that produces output
// Gamma = H(alpha)^sk
// Proof = NIZK that discrete logs match

function vrfProve(secretKey: bigint, alpha: Uint8Array) {
  const h = hashToG1(alpha); // H(alpha)
  // Gamma = sk * h (use G1_MUL precompile)
  // Generate NIZK proof...
}
```

### Identity-Based Encryption

Map identities (email addresses, etc.) to public keys:

```typescript theme={null}
function identityToPublicKey(identity: string): Uint8Array {
  const identityBytes = new TextEncoder().encode(identity);
  return hashToG1(identityBytes);
}

// Now can encrypt to "alice@example.com" without prior key exchange
const alicePubKey = identityToPublicKey("alice@example.com");
```

### Threshold Cryptography

Deterministic point generation for distributed key generation:

```typescript theme={null}
function generateCommitment(coefficientIndex: number): Uint8Array {
  const input = Bytes64();
  new DataView(input.buffer).setBigUint64(56, BigInt(coefficientIndex), false);

  const result = execute(
    PrecompileAddress.BLS12_MAP_FP_TO_G1,
    input,
    5500n,
    Hardfork.PRAGUE
  );

  return result.output;
}
```

## Implementation Details

* **Algorithm:** Simplified SWU (Shallue-van de Woestijne-Ulas) map
* **Curve:** BLS12-381 G1 over Fp (equation: y² = x³ + 4)
* **Properties:** Deterministic, injective (one-to-one), constant-time
* **Zig:** Uses blst library implementation via C FFI
* **TypeScript:** Uses @noble/curves BLS12-381 hash-to-curve

### Simplified SWU Method

The map uses an isogeny-based approach:

1. Map Fp element to point on isogenous curve E'
2. Evaluate isogeny map to get point on target curve E
3. Result is valid G1 point

This provides better distribution properties than older try-and-increment methods.

## Hash-to-Curve Standards

This precompile implements the mapping function from:

* **RFC:** [draft-irtf-cfrg-hash-to-curve](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve)
* **Suite:** BLS12381G1\_XMD:SHA-256\_SSWU\_RO\_
* **Method:** Simplified Shallue-van de Woestijne-Ulas (SSWU)

For complete hash-to-curve:

1. Use hash\_to\_field to get two field elements u₀, u₁
2. Map both to curve: Q₀ = map(u₀), Q₁ = map(u₁)
3. Add points: Q = Q₀ + Q₁
4. Clear cofactor: P = clear\_cofactor(Q)

This precompile handles step 2. Steps 1, 3, 4 done in application code.

## Security Considerations

### Constant-Time Execution

The mapping must be constant-time to prevent timing side-channels:

* No branches based on input value
* Uniform execution path for all inputs
* Protects secret keys in signature schemes

### Distribution Uniformity

The map produces points with distribution indistinguishable from random:

* Important for VRF security
* Prevents bias in cryptographic protocols
* Two-map approach (u₀, u₁) improves uniformity

### Domain Separation

Different protocols should use different domain separation tags:

```typescript theme={null}
const DST = "MY_PROTOCOL_V1_HASH_TO_G1";
// Include DST in hash_to_field step before calling this precompile
```

## Comparison with Other Approaches

### Try-and-Increment (Legacy)

* Hash + point validation loop
* Variable time (security risk)
* Non-uniform distribution
* Not recommended

### Simplified SWU (This Precompile)

* Constant time
* Uniform distribution
* Standards-compliant
* Recommended

## Test Vectors

### Zero Element

```typescript theme={null}
const input = Bytes64(); // All zeros
const result = execute(
  PrecompileAddress.BLS12_MAP_FP_TO_G1,
  input,
  5500n,
  Hardfork.PRAGUE
);
// Should succeed with valid G1 point
console.log('Mapped point:', result.output);
```

### Maximum Field Element

```typescript theme={null}
// p-1 is maximum valid input
const input = Bytes64();
input.set([
  0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a,
  0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7,
  0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf,
  0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24,
  0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff,
  0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa,
], 16); // Right-align in 64 bytes

const result = execute(
  PrecompileAddress.BLS12_MAP_FP_TO_G1,
  input,
  5500n,
  Hardfork.PRAGUE
);
// Should succeed
```

### Determinism Test

```typescript theme={null}
const input = Bytes64();
input[63] = 42;

const result1 = execute(PrecompileAddress.BLS12_MAP_FP_TO_G1, input, 5500n, Hardfork.PRAGUE);
const result2 = execute(PrecompileAddress.BLS12_MAP_FP_TO_G1, input, 5500n, Hardfork.PRAGUE);

// Same input always produces same output
console.assert(result1.output.every((b, i) => b === result2.output[i]));
```

## Related

* [Precompile: BLS12-381 Pairing](/evm/precompiles/bls12-pairing)
* [Precompile: BLS12-381 Map Fp2 to G2](/evm/precompiles/bls12-map-fp2-to-g2)
* [Precompile: BLS12-381 G1 Mul](/evm/precompiles/bls12-g1-mul)
* [Precompile: BLS12-381 G1 Add](/evm/precompiles/bls12-g1-add)
* [EIP-2537: Precompiles for BLS12-381 Curve Operations](https://eips.ethereum.org/EIPS/eip-2537)
* [Hash to Curve RFC](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve)
* [BLS Signatures Spec](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature)
