Multi-Signature
Overview
2-of-2 Multisig
import { Transaction, PrivateKey, PublicKey, Script, OP } from '@bsv/sdk'
/**
* 2-of-2 Multisig
*
* Both parties must sign to spend the funds
*/
class TwoOfTwoMultisig {
/**
* Create 2-of-2 multisig locking script
*/
createLockingScript(pubKey1: PublicKey, pubKey2: PublicKey): Script {
const script = new Script()
script.writeOpCode(OP.OP_2) // Require 2 signatures
script.writeBin(pubKey1.encode())
script.writeBin(pubKey2.encode())
script.writeOpCode(OP.OP_2) // Out of 2 keys
script.writeOpCode(OP.OP_CHECKMULTISIG)
return script
}
/**
* Create transaction with 2-of-2 multisig output
*/
async createMultisigOutput(
senderKey: PrivateKey,
recipientKey1: PublicKey,
recipientKey2: PublicKey,
amount: number,
utxo: { txid: string; vout: number; satoshis: number; script: Script }
): Promise<Transaction> {
const tx = new Transaction()
// Add input
tx.addInput({
sourceTXID: utxo.txid,
sourceOutputIndex: utxo.vout,
unlockingScriptTemplate: new P2PKH().unlock(senderKey),
sequence: 0xffffffff
})
// Add 2-of-2 multisig output
tx.addOutput({
satoshis: amount,
lockingScript: this.createLockingScript(recipientKey1, recipientKey2)
})
// Add change output
const fee = 500
const change = utxo.satoshis - amount - fee
if (change > 546) {
tx.addOutput({
satoshis: change,
lockingScript: new P2PKH().lock(senderKey.toPublicKey().toHash())
})
}
await tx.sign()
return tx
}
/**
* Spend from 2-of-2 multisig (both signatures required)
*/
async spendMultisigOutput(
key1: PrivateKey,
key2: PrivateKey,
multisigUtxo: { txid: string; vout: number; satoshis: number; script: Script },
toAddress: string,
amount: number
): Promise<Transaction> {
const tx = new Transaction()
// Add multisig input
tx.addInput({
sourceTXID: multisigUtxo.txid,
sourceOutputIndex: multisigUtxo.vout,
unlockingScript: new Script(), // Will be filled after signing
sequence: 0xffffffff
})
// Add output
tx.addOutput({
satoshis: amount,
lockingScript: Script.fromAddress(toAddress)
})
// Create signatures
const preimage = tx.inputs[0].getPreimage(multisigUtxo.script)
const sig1 = key1.sign(preimage)
const sig2 = key2.sign(preimage)
// Build unlocking script
const unlockingScript = new Script()
unlockingScript.writeOpCode(OP.OP_0) // Bug workaround for CHECKMULTISIG
unlockingScript.writeBin(sig1.toDER())
unlockingScript.writeBin(sig2.toDER())
tx.inputs[0].unlockingScript = unlockingScript
return tx
}
}
/**
* Usage Example
*/
async function twoOfTwoExample() {
const multisig = new TwoOfTwoMultisig()
// Generate keys
const sender = PrivateKey.fromRandom()
const key1 = PrivateKey.fromRandom()
const key2 = PrivateKey.fromRandom()
// Create multisig output
const utxo = {
txid: 'abc123...',
vout: 0,
satoshis: 100000,
script: new P2PKH().lock(sender.toPublicKey().toHash())
}
const createTx = await multisig.createMultisigOutput(
sender,
key1.toPublicKey(),
key2.toPublicKey(),
50000,
utxo
)
console.log('Created multisig output:', createTx.id('hex'))
// Spend from multisig (requires both keys)
const multisigUtxo = {
txid: createTx.id('hex'),
vout: 0,
satoshis: 50000,
script: multisig.createLockingScript(
key1.toPublicKey(),
key2.toPublicKey()
)
}
const spendTx = await multisig.spendMultisigOutput(
key1,
key2,
multisigUtxo,
'1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
49000
)
console.log('Spent from multisig:', spendTx.id('hex'))
}2-of-3 Multisig
3-of-5 Multisig
Custom M-of-N Multisig
Related Examples
See Also
Last updated
