Skip to main content

Sign a Bitcoin Message

Produce a standard Bitcoin signed-message hash (the same format used by Bitcoin Core's signmessage) and sign it with an MPC key share. Useful for proof-of-ownership, off-chain authentication challenges, and OP_RETURN attestations.

Components used:


Code

import { WorkspaceClient, ComponentModule } from 'caller-sdk';

const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });

const KEY_ID = 'your-key-id';
const DERIV = [2147532436, 2147483648, 2147483648, 0, 0]; // m/84'/0'/0'/0/0

async function signBitcoinMessage(message: string) {
// 1. Hash the message in Bitcoin signed-message format
// Prepends "\x18Bitcoin Signed Message:\n" + varint length before double-SHA256
const { messageHash } = await workspace
.call(ComponentModule.COMPUTE_BITCOIN_SIGNATURE_HASH, {
message,
})
.promise();

console.log('Message hash:', messageHash); // 64-char hex

// 2. MPC sign the hash
const { signature } = await workspace
.call(ComponentModule.SIGN_WITH_KEY_SHARE, {
keyId: KEY_ID,
derivationPath: DERIV,
messageHash,
})
.promise();

console.log('Signature:', signature);
return { message, messageHash, signature };
}

const result = await signBitcoinMessage('I own this address — 2024-01-15');
console.log(result);

Raw SHA-256 variant

Use format: 'raw' to compute a plain double-SHA256 hash instead of the Bitcoin message prefix format. This is useful for signing raw data (e.g., commitment hashes stored in OP_RETURN outputs).

const { messageHash } = await workspace
.call(ComponentModule.COMPUTE_BITCOIN_SIGNATURE_HASH, {
message: 'raw-data-to-hash',
})
.promise();
// format is configured at the component level in the workflow canvas

In a workflow

[Trigger: message]


COMPUTE_BITCOIN_SIGNATURE_HASH ← format: 'bitcoin-message' (config)
│ messageHash

SIGN_WITH_KEY_SHARE ← keyId, derivationPath (constants)
│ signature

[Output: { message, messageHash, signature }]
Verifying the signature

The resulting signature can be verified by any standard Bitcoin message verification tool (e.g., Bitcoin Core's verifymessage RPC, or libraries like bitcoinjs-message). Pass the address derived from the same key, the original message, and the signature.