Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Multi-Signature (MultiSig)

Multi-signature is an advanced Bitcoin feature that requires M signatures to spend funds controlled by N public keys (M-of-N).

Overview

What is Multi-Signature?

A multi-signature address requires multiple private keys to jointly sign before funds can be spent, rather than a single private key as in traditional setups.

Examples:

  • 2-of-3: Requires any 2 of 3 keys
  • 3-of-5: Requires any 3 of 5 keys
  • 2-of-2: Requires both keys to agree

Why Use MultiSig?

1. Improved Security

  • No single point of failure
  • A stolen private key does not immediately result in fund loss
  • Risk is distributed

2. Distributed Trust

  • Corporate governance: prevents misuse by a single person
  • Escrow services: buyer + seller + arbitrator
  • Joint family management: shared management by spouses

3. Flexibility

  • Different M-N combinations meet different needs
  • Emergency recovery mechanisms can be set up
  • Supports complex business logic

Technical Implementation

MultiSig Address Structure

#![allow(unused)]
fn main() {
pub struct MultiSigAddress {
    pub address: String,            // Multi-sig address (starts with "3")
    pub required_sigs: usize,       // M (number of required signatures)
    pub total_keys: usize,          // N (total number of keys)
    pub public_keys: Vec<String>,   // Public keys of all participants
    pub script: String,             // Locking script
}
}

Creating a Multi-Sig Address

#![allow(unused)]
fn main() {
use bitcoin_simulation::{multisig::MultiSigAddress, wallet::Wallet};

// Create participants
let ceo = Wallet::new();
let cfo = Wallet::new();
let cto = Wallet::new();

// Collect public keys
let public_keys = vec![
    ceo.public_key.clone(),
    cfo.public_key.clone(),
    cto.public_key.clone(),
];

// Create a 2-of-3 multi-sig address
let multisig = MultiSigAddress::new(2, public_keys)?;

println!("Multi-sig address: {}", multisig.address);
println!("Required signatures: {}/{}", multisig.required_sigs, multisig.total_keys);
}

MultiSig Types

SimpleBTC provides preset common multi-sig types:

#![allow(unused)]
fn main() {
use bitcoin_simulation::multisig::MultiSigType;

// 2-of-2: both parties must agree
let two_of_two = MultiSigAddress::from_type(
    MultiSigType::TwoOfTwo,
    vec![alice.public_key, bob.public_key]
)?;

// 2-of-3: any two parties suffice (most common)
let two_of_three = MultiSigAddress::from_type(
    MultiSigType::TwoOfThree,
    vec![party1.public_key, party2.public_key, party3.public_key]
)?;

// 3-of-5: high-security scenarios
let three_of_five = MultiSigAddress::from_type(
    MultiSigType::ThreeOfFive,
    vec![pk1, pk2, pk3, pk4, pk5]
)?;
}

Use Cases

Use Case 1: Corporate Financial Management

Requirement: Company funds require joint approval from multiple executives

Solution: 2-of-3 MultiSig (CEO + CFO + CTO)

#![allow(unused)]
fn main() {
fn setup_corporate_wallet() -> Result<MultiSigAddress, String> {
    // 1. Create executive wallets
    let ceo = Wallet::new();
    let cfo = Wallet::new();
    let cto = Wallet::new();

    println!("=== Corporate Multi-Sig Wallet ===");
    println!("CEO: {}", &ceo.address[..16]);
    println!("CFO: {}", &cfo.address[..16]);
    println!("CTO: {}", &cto.address[..16]);

    // 2. Create multi-sig address
    let company_wallet = MultiSigAddress::new(
        2,  // Requires 2 signatures
        vec![
            ceo.public_key.clone(),
            cfo.public_key.clone(),
            cto.public_key.clone(),
        ]
    )?;

    println!("\nCompany multi-sig address: {}", company_wallet.address);
    println!("Rule: any 2 executives can authorize a transfer\n");

    Ok(company_wallet)
}

// Transfer scenario
fn corporate_payment(
    multisig: &MultiSigAddress,
    ceo: &Wallet,
    cfo: &Wallet,
    recipient: &str,
    amount: u64
) -> Result<(), String> {
    println!("Transferring {} satoshi to {}", amount, &recipient[..16]);

    // 1. CEO signs
    let ceo_sig = ceo.sign(&format!("{}{}", multisig.address, amount));
    println!("✓ CEO has signed");

    // 2. CFO signs
    let cfo_sig = cfo.sign(&format!("{}{}", multisig.address, amount));
    println!("✓ CFO has signed");

    // 3. Collect signatures
    let signatures = vec![ceo_sig, cfo_sig];

    // 4. Verify signature count
    if signatures.len() >= multisig.required_sigs {
        println!("✅ Signature count meets requirement; transaction can be executed");
        // Create and broadcast transaction...
        Ok(())
    } else {
        Err("Insufficient signatures".to_string())
    }
}
}

Advantages:

  • ✅ Prevents misuse of funds by a single person
  • ✅ CFO + CTO can still operate when CEO is traveling
  • ✅ Even if one person is compromised, funds remain safe

Use Case 2: Escrow Service

Requirement: Buyer and seller do not trust each other; a third-party arbitrator is needed

Solution: 2-of-3 MultiSig (buyer + seller + arbitrator)

#![allow(unused)]
fn main() {
fn escrow_service() -> Result<(), String> {
    // Participants
    let buyer = Wallet::new();
    let seller = Wallet::new();
    let arbitrator = Wallet::new();

    println!("=== Escrow Service ===");
    println!("Buyer: {}", &buyer.address[..16]);
    println!("Seller: {}", &seller.address[..16]);
    println!("Arbitrator: {}", &arbitrator.address[..16]);

    // Create escrow multi-sig address
    let escrow = MultiSigAddress::new(
        2,
        vec![
            buyer.public_key.clone(),
            seller.public_key.clone(),
            arbitrator.public_key.clone(),
        ]
    )?;

    println!("\nEscrow address: {}", escrow.address);

    // Scenario 1: Normal transaction (buyer + seller)
    println!("\n--- Scenario 1: Transaction completed smoothly ---");
    println!("Buyer received goods; satisfied");
    println!("Buyer signs: ✓");
    println!("Seller signs: ✓");
    println!("✅ 2/3 signatures; funds released to seller");

    // Scenario 2: Dispute (buyer + arbitrator or seller + arbitrator)
    println!("\n--- Scenario 2: Dispute arises ---");
    println!("Buyer: goods are defective");
    println!("Seller: goods are fine");
    println!("Arbitrator investigates...");
    println!("Arbitrator: buyer is right");
    println!("Buyer signs: ✓");
    println!("Arbitrator signs: ✓");
    println!("✅ 2/3 signatures; funds refunded to buyer");

    Ok(())
}
}

Advantages:

  • ✅ Buyer protection: refund if goods don’t match description
  • ✅ Seller protection: funds released automatically for normal transactions
  • ✅ Fair: arbitrator cannot control funds alone

Use Case 3: Personal Asset Protection

Requirement: Prevent loss due to a single private key being lost or stolen

Solution: 2-of-3 MultiSig (primary key + backup key + custodian key)

#![allow(unused)]
fn main() {
fn personal_security_setup() -> Result<(), String> {
    // Key assignment
    let main_key = Wallet::new();      // Daily use
    let backup_key = Wallet::new();    // Safe deposit box
    let custodian_key = Wallet::new(); // Lawyer / trust company

    println!("=== Personal Asset Protection ===");
    println!("Primary key (daily): {}", &main_key.address[..16]);
    println!("Backup key (safe): {}", &backup_key.address[..16]);
    println!("Custodian key (lawyer): {}", &custodian_key.address[..16]);

    let secure_wallet = MultiSigAddress::new(
        2,
        vec![
            main_key.public_key,
            backup_key.public_key,
            custodian_key.public_key,
        ]
    )?;

    println!("\nSecure wallet: {}", secure_wallet.address);

    // Usage scenarios
    println!("\n--- Usage Scenarios ---");
    println!("Daily transfers: primary key + backup key");
    println!("Primary key lost: backup key + custodian key");
    println!("Theft risk: requires 2 keys; single key theft poses no risk");

    Ok(())
}
}

Use Case 4: Cold-Hot Wallet Combination

Requirement: Security for large storage + convenience for small amounts

Solution: 2-of-3 (hot wallet + cold wallet 1 + cold wallet 2)

#![allow(unused)]
fn main() {
fn cold_hot_wallet_setup() -> Result<(), String> {
    let hot_wallet = Wallet::new();    // Online device
    let cold_wallet_1 = Wallet::new(); // Hardware wallet 1
    let cold_wallet_2 = Wallet::new(); // Paper wallet

    println!("=== Cold-Hot Wallet Combination ===");
    println!("Hot wallet (phone): {}", &hot_wallet.address[..16]);
    println!("Cold wallet 1 (Ledger): {}", &cold_wallet_1.address[..16]);
    println!("Cold wallet 2 (paper wallet): {}", &cold_wallet_2.address[..16]);

    let vault = MultiSigAddress::new(
        2,
        vec![
            hot_wallet.public_key,
            cold_wallet_1.public_key,
            cold_wallet_2.public_key,
        ]
    )?;

    println!("\nVault address: {}", vault.address);

    println!("\n--- Usage Strategy ---");
    println!("Daily small amounts: hot wallet + cold wallet 1 (convenient)");
    println!("Large transfers: cold wallet 1 + cold wallet 2 (most secure)");
    println!("Hot wallet hacked: still requires cold wallet cooperation; funds safe");

    Ok(())
}
}

Advanced Usage

TimeLock + MultiSig

Combining time locks for inheritance planning:

#![allow(unused)]
fn main() {
use bitcoin_simulation::advanced_tx::TimeLock;

fn inheritance_setup() -> Result<(), String> {
    let owner = Wallet::new();
    let heir = Wallet::new();
    let lawyer = Wallet::new();

    // Normal: 2-of-2 (owner + heir; protects privacy)
    let normal_multisig = MultiSigAddress::new(
        2,
        vec![owner.public_key.clone(), heir.public_key.clone()]
    )?;

    // Time lock: 1 year later
    let one_year = 365 * 24 * 60 * 60;
    let unlock_time = current_timestamp() + one_year;
    let timelock = TimeLock::new_time_based(unlock_time);

    println!("=== Inheritance Plan ===");
    println!("Normal period: requires owner + heir (2-of-2)");
    println!("After 1 year: heir can operate independently");

    // Or use 3-of-3, downgraded to 2-of-3 after 1 year
    let emergency_multisig = MultiSigAddress::new(
        2,  // Only 2 required after 1 year
        vec![owner.public_key, heir.public_key, lawyer.public_key]
    )?;

    Ok(())
}
}

Hierarchical MultiSig

Multi-level multi-sig structure for large organizations:

#![allow(unused)]
fn main() {
// Board of directors: 5-of-9
let board = MultiSigAddress::new(5, board_members)?;

// Executive committee: 3-of-5
let exec_committee = MultiSigAddress::new(3, executives)?;

// Petty cash: 2-of-3
let petty_cash = MultiSigAddress::new(2, managers)?;

println!("Permission levels:");
println!("< 10 BTC: manager level 2-of-3");
println!("10-100 BTC: executive level 3-of-5");
println!("> 100 BTC: board level 5-of-9");
}

Security Considerations

⚠️ Important Notes

  1. Key Management

    • Store keys in dispersed locations; do not keep them together
    • Use hardware wallets for cold keys
    • Regularly test backup recovery
  2. Choosing M

    • M too small: security is reduced
    • M too large: usability is reduced
    • Recommended: M = (N+1)/2 or N-1
  3. Choosing N

    • N=2: simple but has a single point of failure
    • N=3: balances security and convenience (most common)
    • N=5+: high security but complex
  4. Choosing Participants

    • Geographically dispersed
    • Trusted but mutually independent
    • Have emergency contact information

Best Practices

#![allow(unused)]
fn main() {
// ✅ Good practice
let multisig = MultiSigAddress::new(
    2,  // Reasonable M value
    vec![key1, key2, key3]  // 3 independent keys
)?;

// Dispersed storage
// key1 -> mobile hot wallet
// key2 -> hardware wallet (safe)
// key3 -> paper wallet (bank safe deposit box)

// ❌ Bad practice
// All keys stored on the same computer
// M=N (loses fault tolerance)
// Multiple keys derived from the same mnemonic
}

Complete Example

#![allow(unused)]
fn main() {
use bitcoin_simulation::{
    blockchain::Blockchain,
    wallet::Wallet,
    multisig::MultiSigAddress,
};

fn complete_multisig_demo() -> Result<(), String> {
    let mut blockchain = Blockchain::new();

    // Create participants
    let alice = Wallet::new();
    let bob = Wallet::new();
    let charlie = Wallet::new();

    // Create 2-of-3 multi-sig
    let multisig = MultiSigAddress::new(
        2,
        vec![
            alice.public_key.clone(),
            bob.public_key.clone(),
            charlie.public_key.clone(),
        ]
    )?;

    println!("Multi-sig address: {}", multisig.address);

    // 1. Deposit funds
    let funding_tx = blockchain.create_transaction(
        &Wallet::from_address("funder".to_string()),
        multisig.address.clone(),
        10000,
        0,
    )?;
    blockchain.add_transaction(funding_tx)?;
    blockchain.mine_pending_transactions(alice.address.clone())?;

    println!("Multi-sig balance: {}", blockchain.get_balance(&multisig.address));

    // 2. Multi-sig transfer (requires 2 signatures)
    let recipient = Wallet::new();

    // Alice signs
    let alice_sig = alice.sign(&format!("{}{}",
        multisig.address, recipient.address));

    // Bob signs
    let bob_sig = bob.sign(&format!("{}{}",
        multisig.address, recipient.address));

    // Verify signatures
    println!("\nCollecting signatures:");
    println!("Alice: ✓");
    println!("Bob: ✓");

    if vec![alice_sig, bob_sig].len() >= multisig.required_sigs {
        println!("✅ Signatures meet requirement; transfer can proceed");

        // Create transfer transaction
        // Note: actual implementation requires multi-sig transaction building logic
        println!("Transaction created and broadcast");
    }

    Ok(())
}
}

References


Next: TimeLock Tutorial | RBF Mechanism

Back to Advanced Features