Skip to content

Add secp256k1-decompress? function #7153

@jbencin-stacks

Description

@jbencin-stacks

The Problem

There is an issue in current Wormhole Core contract where we need to provide the uncompressed public keys when performing a guardian set update. We are the only chain that needs to do this, and getting these uncompressed keys can be difficult. Last time we had to wait until the new guardian set went live and manually recover the keys from VAA signatures, which led to downtime for protocols which depended on Wormhole

The need to do this is due to a limitation in Clarity, there is currently no built-in function to uncompress a secp256k1 public key

  • On other chains, they can store and compare ETH addresses directly since there are built-in functions to recover from signature → compressed_pubkey → uncompressed_pubkey → ETH_address
  • On Stacks, we are missing the compressed_pubkey → uncompressed_pubkey component. To compensate, we provide the uncompressed pubkeys, and do the following instead:
    • On guardian set update, we validate the uncompressed key against the ETH addresses, derive the compressed key, and store both keys
    • When validating a VAA, we recover the compressed pubkey from the message signature and compare it against the stored compressed key for the guardian

Can this be done already in Clarity?

I don't know enough about cryptography to know if this is feasible to do in Clarity, but according to Claude, it is not:

Can this be solved within existing Clarity?

No, not feasibly. Decompressing a public key requires computing a modular square root on the secp256k1 curve:

y² = x³ + 7  (mod p)
y  = (x³ + 7)^((p+1)/4)  (mod p)

where p is the 256-bit secp256k1 prime. This requires:

  1. 256-bit modular arithmetic — Clarity's largest integer is u128, so you'd need to implement big-number math by splitting values into two u128 halves and manually handling carries/overflow
  2. Modular exponentiation with a ~256-bit exponent — roughly 256 iterations of modular squaring + multiplication
  3. Each modular multiplication of 256-bit numbers composed of u128 pairs would itself be many operations

Proposed Solution

I propose adding a new Clarity built-in function, secp256k1-decompress?, which:

  • Accepts a compressed secp256k1 public key: (buff 33)
  • Returns an uncompressed public key if successful: (response (buff 64) uint)

From there, we can derive the ETH address with keccak256 and slice?

Metadata

Metadata

Assignees

No one assigned

    Labels

    clarity-new-builtinThe issue describes a new Clarity builtin (function or variable)clarity-next-candidateChanges that are considered to be included in the next version of Clarity

    Type

    No type

    Projects

    Status

    Status: 🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions