Citrea
WebsiteBlogJoin The Community
  • 👋Welcome
    • Getting started
  • ⛓️Technical Specs
    • TL;DR
    • Technical Introduction
    • Characteristics
      • Execution Environment
      • Block Production
        • Mempool
        • Sequencer
        • Sequencer Commitments
        • Soft Confirmations
      • Proof Generation
      • Nodes
      • Bitcoin Settlement: Trust-minimized BTC Bridge
        • BitVM
        • Optimistic Verification
    • Security Properties
      • Validity
      • Data Availability
      • Re-org Resistance
      • Censorship Resistance and Force Transactions
        • Escape Hatch
  • 👤User Guide
    • Run Citrea Full Node
      • Bitcoin Testnet4
        • Testnet4 Docker Setup
        • Build Testnet4 from Source
      • Citrea Full Node
        • Citrea Binary Executable
        • Build Citrea from Source
    • Use Citrea Testnet Faucet
    • Installing an EVM Wallet
    • Taproot Recovery Address
  • 📖Developer Documentation
    • Kickstart
    • Deployment Guide
      • Deploy a Smart Contract Using Remix
      • Deploy a Token
      • Configure Hardhat
    • System Contracts
      • Bitcoin Light Client
      • Bridge
      • Fee Vaults
    • Chain Information
    • Deploy a Bitcoin Appchain (L3)
    • RPC Documentation
    • secp256r1 & Schnorr Precompiles
  • 🔎Future Research
    • Decentralized Sequencer Network
    • Lightning Integration
    • Multi Prover
    • Multi VM Approach
    • Trustless Atomic Swaps
    • Trustless Settlement
    • Volition Model
  • 🌐Community
    • Citrea Meetups
      • Meetup Guide
      • Resources
      • Code of Conduct
Powered by GitBook
On this page
  • secp256r1 Curve Support (RIP 7212)
  • Example Smart Contract
  • Schnorr Signature Verification
  • Example Smart Contract

Was this helpful?

  1. Developer Documentation

secp256r1 & Schnorr Precompiles

PreviousRPC DocumentationNextDecentralized Sequencer Network

Last updated 8 days ago

Was this helpful?

The secp256r1 and Schnorr precompiles are enabled with the upgrade of Citrea. In this document we will go over the details of these precompiles and how to use them.

secp256r1 Curve Support ()

RIP 7212 is an improvement proposal that introduces a precompile support for the secp256r1 curve. In Citrea, like other EVM rollups, this precompile is available at address 0x0000000000000000000000000000000000000100.

This precompile enables a whole new set of applications and use cases, such as:

  • Hardware & Biometric Authentication: Native support of secp256r1 allows smart contracts to directly validate the signatures from secure enclaves and passkey devices, such as Apple's , Android's , Yubikeys, and WebAuthn authenticators.

  • Account Abstraction & Smart Wallets: Combining features above with account abstraction & smart wallets, self-custodial platforms can be built much more easily and efficiently, such as . It also improves the security and the UX perspective of applications massively.

  • Gas-efficient signature verification: With this precompile, secp256r1 signature verification comes down to 3450 gas, which is significantly cheaper than any other existing smart contract verification method.

Technical Details

The precompile accepts a 160-byte input, which is a concatenation of:

  1. Message Hash (32 bytes): The 32-byte hash of the message that was signed.

  2. Signature r value (32 bytes): The r component of the ECDSA signature.

  3. Signature s value (32 bytes): The s component of the ECDSA signature.

  4. Public Key X-coordinate (32 bytes): The X-coordinate of the uncompressed secp256r1 public key.

  5. Public Key Y-coordinate (32 bytes): The Y-coordinate of the uncompressed secp256r1 public key.

Upon successful verification, the precompile returns a 32-byte value 0x00...01. On failure (e.g., invalid signature, malformed input), it returns empty bytes: 0x.

Example Smart Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

/// @title secp256r1 Precompile Example
/// @notice Calls the secp256r1 precompile to verify secp256r1 ECDSA signatures
contract P256R1VerifyCaller {
    address constant P256R1_VERIFY_PRECOMPILE = 0x0000000000000000000000000000000000000100;

    /**
     * @notice Verifies a secp256r1 (RIP-7212) ECDSA signature.
     * @dev All inputs must be 32-byte big-endian values.
     * @param messageHash 32-byte message hash
     * @param r 32-byte signature r value
     * @param s 32-byte signature s value
     * @param pubKeyX 32-byte public key X coordinate
     * @param pubKeyY 32-byte public key Y coordinate 
     * @return isValid True if signature is valid, false otherwise.
     */
    function callP256R1Verify(
        bytes32 messageHash,
        bytes32 r,
        bytes32 s,
        bytes32 pubKeyX,
        bytes32 pubKeyY
    ) external view returns (bool isValid) {
        // Concatenate inputs in correct order: hash | r | s | pubKeyX | pubKeyY
        bytes memory input = abi.encodePacked(
            messageHash,
            r,
            s,
            pubKeyX,
            pubKeyY
        );
        (bool ok, bytes memory output) = P256R1_VERIFY_PRECOMPILE.staticcall(input);
        // 32-byte return, last byte == 0x01 means success
        return ok && output.length == 32 && output[31] == 0x01;
    }
}

Schnorr Signature Verification

Schnorr precompile enables an interesting set of developments, such as:

  • Scriptless cross-chain atomic swaps: With Schnorr adaptor signatures it is possible to build BTC <> cBTC atomic swaps without HTLCs or any other third-party custody.

  • Bitcoin-aware oracles & bridges: A smart contract on Citrea can now prove that a Taproot key signed some data without any external verifiers.

Technical Details

The precompile accepts a 128-byte input, concatenated as follows:

  1. Public Key X-coordinate (32 bytes): The 32-byte x-coordinate of the Schnorr public key. The y-coordinate is implicitly assumed to be even as per BIP340.

  2. Message Hash (32 bytes): The 32-byte hash of the message that was signed.

  3. Signature (64 bytes): The 64-byte Schnorr signature (typically r and s components, each 32 bytes).

If the signature is valid for the given message hash and public key, the precompile returns a 32-byte value 0x00...01. If verification fails, it returns empty bytes: 0x.

Base gas cost of Schnorr precompile is set to 4600.

Example Smart Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

/// @title Schnorr Precompile Example
/// @notice Calls the Schnorr precompile to verify BIP340 secp256k1 signatures
contract SchnorrVerifyCaller {
    address constant SCHNORR_VERIFY_PRECOMPILE = 0x0000000000000000000000000000000000000200;

    /**
     * @notice Verifies a BIP340 Schnorr signature.
     * @dev All inputs must be big-endian byte sequences.
     * @param pubKeyX 32-byte public key X coordinate (big-endian, Y is implicitly even per BIP340)
     * @param messageHash 32-byte hash of the signed message
     * @param signature 64-byte Schnorr signature (r || s), both 32-byte values concatenated
     * @return isValid True if signature is valid, false otherwise.
     */
    function schnorrVerify(
        bytes32 pubKeyX,
        bytes32 messageHash,
        bytes calldata signature // must be 64 bytes
    ) external view returns (bool isValid) {
        require(signature.length == 64, "Invalid signature length");
        // Concatenate inputs in correct order: pubKeyX | messageHash | signature
        bytes memory input = abi.encodePacked(
            pubKeyX,
            messageHash,
            signature
        );
        (bool ok, bytes memory output) = SCHNORR_VERIFY_PRECOMPILE.staticcall(input);
        // 32-byte return, last byte == 0x01 means success
        return ok && output.length == 32 && output[31] == 0x01;
    }
}

Schnorr signature is a Bitcoin-native, linear, and aggregation-friendly signature scheme. It is a key component of Bitcoin's Taproot upgrade () and also offers advantages for multi-signature schemes like . In Citrea, this precompile is available at the address 0x0000000000000000000000000000000000000200.

📖
Tangerine
RIP 7212
Secure Enclave
Keystore
Tanari
BIP 340
MuSig2