Using wallets like XVerse, users can sign a message using their ordinal btc address, let's say bc1XXX, the message is hashed based on BIP0322.

So I have:

  • User wallet: bc1XXX
  • Message hash: YYY
  • Signature signed by bc1XXX: ZZZ

So this data is sent to my backend server, and I want to verify that ZZZ was indeed signed by bc1XXX (and contains YYY as message).

I'm using this so far:

    const msgHash = bip0322Hash(message);
    const signatureBuffer = Buffer.from(signatureStr, 'base64');

    const decodedSignature = signatureBuffer.slice(2, 66);
    const recoveryId = signatureBuffer[0];

    // Extract public key from the signature
    const recoveredPublicKeyBuffer = secp.recoverPublicKey(
        recoveryId, // Recovery ID (0 or 1)
    console.log(publicKeyToTaprootAddress(recoveredPublicKeyBuffer)); //no match with my original pubkey that signed the message

But I have a hard time getting the correct address from recoveredPublicKeyBuffer which I can't match with the public key address of my test set.

I'm trying to use this function, but the output doesn't match my pubkey:

    function publicKeyToTaprootAddress(publicKey: Uint8Array) {
        // Compute the SHA-256 hash of the public key
        const hash = sha256(Buffer.from(publicKey));

        // Construct the human-readable part and the data part of the Bech32m string
        const hrp = 'bc';
        const data = sha256(Buffer.from([0x01].concat(Array.from(hash))));
        const data2 = bech32m.toWords(Buffer.from(data));

        // Encode the Bech32m string
        return bech32m.encode(hrp, data2);

1 Answer 1


Assuming that recoverPublicKey is performing the ECDSA key recovery, this does not apply to BIP340 Schnorr Signatures. If you want to use BIP322 with Taproot addresses, you need the original public key.

  • so there's no way to get that information from the signature itself? How can I verify that an address has signed a message in that case, knowing that I have the address 'bc1p...', the signature and the message?
    – toto
    May 19, 2023 at 17:55
  • 1
    The address has the public key, encoded in base 32. Decode the address and the witness program part (what comes after the version) is a 32-byte vector containing an x coordinate for an x-only public key. Look for a public key parsing in the lib you're using May 19, 2023 at 18:11
  • ok thanks for the help
    – toto
    May 19, 2023 at 22:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.