Files
zeekayandHanzo Dev 9ababce758 A precompile's config key names the precompile
Forty-five of the fifty-six suite modules keyed off <name>Config and eleven did
not, in three other spellings: Verify, Threshold, Encapsulate. The LP-4200
unified PQCrypto block carried four of those conventions across its eight
addresses — two KEMs named differently (mlkemConfig beside hqcEncapsulate) and
two threshold schemes named differently (pulsarVerify beside coronaThreshold) —
so the block that exists to be one thing did not read as one thing.

The deviations also named an OPERATION where a precompile belongs. HQC
decapsulates as well as encapsulates and the Verify precompiles do more than
verify, so hqcEncapsulate was the name of one entry point standing in for the
whole module. A config key selects a precompile; it is not a method selector.

All fifty-six are <name>Config now. There is no compatibility shim: an
upgrade.json naming an old key is naming something that does not exist, which
luxd already refuses loudly, and every chain config in the estate moves with this.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-10 19:27:25 -07:00
..

Pulsar Threshold Signature Precompile

Post-quantum threshold signature verification precompile for Lux EVM, implementing the LWE-based two-round threshold signature scheme from Pulsar (ePrint 2024/1113).

Overview

The Pulsar Threshold precompile enables verification of lattice-based threshold signatures on-chain, providing post-quantum security for multi-party consensus protocols. This is a critical component of the Quasar quantum consensus mechanism.

Precompile Address: 0x020000000000000000000000000000000000000B

Features

  • Post-Quantum Security: Based on Learning With Errors (LWE) lattice problem
  • Threshold Signatures: Supports t-of-n threshold schemes
  • Two-Round Protocol: Efficient distributed signing
  • Quasar Integration: Native support for quantum consensus validators
  • Flexible Thresholds: Configurable from 1-of-n to n-of-n

Algorithm

Pulsar implements a threshold signature scheme based on:

  • Lattice Cryptography: LWE problem hardness
  • Module Learning With Errors: Polynomial ring operations
  • Threshold Secret Sharing: Shamir's secret sharing over rings
  • Two-Round Protocol: Efficient distributed key generation and signing

Parameters

From corona/sign/config.go:

Parameter Value Description
M 8 Matrix rows
N 7 Matrix columns
Dbar 48 Signature dimension
Q 0x1000000004A01 Prime modulus (48-bit)
LogN 8 Ring dimension log (256)
SigmaE 6.108 Error distribution
SigmaStar 1.73e11 Masking distribution

Security Level

  • Classical: >128-bit security
  • Quantum: Resistant to Shor's algorithm
  • Hardness: Based on LWE with modulus Q and dimension 256

Gas Costs

Operation Formula Example
Base Cost 150,000 Fixed cost
Per Party 10,000 × n Cost per party
2 parties 150,000 + 20,000 170,000 gas
3 parties 150,000 + 30,000 180,000 gas
5 parties 150,000 + 50,000 200,000 gas
10 parties 150,000 + 100,000 250,000 gas

Input Format

Offset  | Size | Field
--------|------|------------------
0-4     | 4    | threshold (uint32)
4-8     | 4    | totalParties (uint32)
8-40    | 32   | messageHash (bytes32)
40-...  | ~4KB | signature (variable)

Signature Components

The signature consists of serialized lattice elements:

  • c: Challenge polynomial (256 bytes)
  • z: Response vector (N × 256 bytes = 1,792 bytes)
  • Delta: Difference vector (M × 256 bytes = 2,048 bytes)
  • A: Public matrix (M × N × 256 bytes = 14,336 bytes)
  • bTilde: Rounded vector (M × 256 bytes = 2,048 bytes)

Total: ~20KB per signature

Usage

Solidity

import "./ICoronaThreshold.sol";

contract MyContract is CoronaThresholdVerifier {
    function verifyConsensus(
        bytes32 messageHash,
        bytes calldata signature
    ) external view returns (bool) {
        // 2-of-3 threshold
        return verifyThresholdSignature(
            2,  // threshold
            3,  // total parties
            messageHash,
            signature
        );
    }

    function processSignedData(
        bytes32 messageHash,
        bytes calldata signature
    ) external {
        // Revert if invalid
        CoronaThresholdLib.verifyOrRevert(
            2, 3, messageHash, signature
        );

        // Process data - signature is valid
    }
}

TypeScript/ethers.js

import { ethers } from 'ethers';

const CORONA_THRESHOLD = '0x020000000000000000000000000000000000000B';

const abi = [
    'function verifyThreshold(uint32,uint32,bytes32,bytes) view returns(bool)'
];

const precompile = new ethers.Contract(CORONA_THRESHOLD, abi, provider);

// Verify 2-of-3 threshold signature
const isValid = await precompile.verifyThreshold(
    2,                    // threshold
    3,                    // total parties
    messageHash,          // bytes32
    signatureBytes        // bytes
);

Go

import (
    "corona/sign"
    "github.com/luxfi/lattice/v6/ring"
)

// Generate threshold signature (off-chain)
threshold := 2
totalParties := 3

// Initialize rings
r, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.Q})
r_xi, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.QXi})
r_nu, _ := ring.NewRing(1<<sign.LogN, []uint64{sign.QNu})

// Run threshold signing protocol
// ... (see corona/main.go for full example)

// Verify signature
valid := sign.Verify(r, r_xi, r_nu, z, A, mu, bTilde, c, Delta)

Protocol Flow

Setup Phase

  1. Trusted dealer generates keys using sign.Gen()
  2. Secret shares distributed to n parties
  3. Public parameters (A, b) published

Signing Phase (Two Rounds)

Round 1: Each party generates masking polynomials

D_i, MACs_i := party.SignRound1(A, sid, PRFKey, T)

Round 2 Preprocess: Verify MACs and combine D matrices

valid, DSum, hash := party.SignRound2Preprocess(A, b, D, MACs, sid, T)

Round 2: Each party generates signature share

z_i := party.SignRound2(A, bTilde, DSum, sid, mu, T, PRFKey, hash)

Finalize: Combiner aggregates shares

c, z_sum, Delta := party.SignFinalize(z, A, bTilde)

Verification

valid := sign.Verify(r, r_xi, r_nu, z, A, mu, bTilde, c, Delta)

Quasar Consensus Integration

The Pulsar threshold precompile is designed for Quasar consensus:

contract QuasarConsensus {
    uint32 constant VALIDATORS = 5;
    uint32 constant THRESHOLD = 4; // 4-of-5 for finality

    function finalizeBlock(
        bytes32 blockHash,
        bytes calldata validatorSignature
    ) external {
        // Verify 4-of-5 validators signed
        CoronaThresholdLib.verifyOrRevert(
            THRESHOLD,
            VALIDATORS,
            blockHash,
            validatorSignature
        );

        // Block is finalized with quantum-resistant proof
        emit BlockFinalized(blockHash);
    }
}

Security Considerations

Post-Quantum Security

  • Quantum Attacks: Resistant to Shor's algorithm
  • Classical Attacks: >128-bit security under LWE assumption
  • Side Channels: Constant-time operations in lattice library

Threshold Properties

  • Unforgeability: Cannot forge signature without t parties
  • Robustness: Works with any subset of size t
  • Non-interactivity: After setup, signing is non-interactive per party

Network Security

  • MAC Protection: Message authentication codes prevent tampering
  • Replay Protection: Session IDs prevent signature replay
  • Rank Checks: Full rank verification prevents malicious matrices

Performance

Benchmarks (Apple M1 Max)

Operation Time Gas
2-of-3 verify ~2.5ms 170,000
3-of-5 verify ~3.8ms 200,000
5-of-7 verify ~5.2ms 220,000

Comparison with Other Schemes

Scheme Signature Size Verify Time Quantum Secure
ECDSA (secp256k1) 65 bytes ~88μs ❌
BLS 96 bytes ~2.1ms ❌
Pulsar ~20KB ~3.8ms ✅
ML-DSA-65 3.3KB ~108μs ✅
SLH-DSA 7.9KB ~4.2ms ✅

Trade-offs:

  • ✅ Post-quantum security
  • ✅ Threshold signatures
  • ✅ Two-round protocol
  • ⚠️ Large signature size (~20KB)
  • ⚠️ Higher verification time

Testing

# Run tests
cd /Users/z/work/lux/standard/src/precompiles/corona-threshold
go test -v

# Run benchmarks
go test -bench=. -benchmem

# Test with specific threshold
go test -run TestCoronaThresholdVerify_3of5

Test Coverage

  • ✅ 2-of-3 threshold signature
  • ✅ 3-of-5 threshold signature
  • ✅ Full threshold (n-of-n)
  • ✅ Invalid signature rejection
  • ✅ Wrong message rejection
  • ✅ Threshold not met rejection
  • ✅ Input validation
  • ✅ Gas cost calculation

Migration from Classical Signatures

From ECDSA

// Before (ECDSA)
function verify(bytes32 hash, bytes memory sig) public view {
    address signer = ECDSA.recover(hash, sig);
    require(isAuthorized(signer), "Unauthorized");
}

// After (Pulsar Threshold)
function verify(bytes32 hash, bytes memory sig) public view {
    CoronaThresholdLib.verifyOrRevert(
        THRESHOLD,
        TOTAL_SIGNERS,
        hash,
        sig
    );
}

From BLS

// Before (BLS aggregate)
function verify(bytes32 hash, bytes memory sig) public view {
    require(BLS.verify(aggregateKey, hash, sig), "Invalid");
}

// After (Pulsar Threshold)
function verify(bytes32 hash, bytes memory sig) public view {
    require(
        verifyThresholdSignature(THRESHOLD, N, hash, sig),
        "Invalid"
    );
}

References

License

Copyright (C) 2025, Lux Industries, Inc. All rights reserved.