BIP-141: Segregated Witness
SegWit specification. Separating signatures from transaction data for scalability and malleability fixes.
| Type | Bitcoin Improvement Proposal |
| Number | bip-141 |
| Status | Final |
| Authors | Eric Lombrozo, Johnson Lau, Pieter Wuille |
| Original | https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki |
BIP-141: Segregated Witness (Consensus Layer)
Segregated Witness (SegWit) is a soft fork that separates signature data (witness) from the transaction structure. Activated in August 2017.
Motivation
Transaction Malleability
Before SegWit, transaction IDs could be changed without invalidating signatures:
- Signatures could be modified (same key, different encoding)
- txid changed → dependent transactions broke
Problem for Layer 2: Lightning channels need stable txids.
Scalability
Signature data is ~60% of transaction size. By segregating:
- More transactions per block
- Reduced bandwidth for SPV clients
How SegWit Works
Traditional Transaction
┌──────────────────────────────┐
│ Version │
│ Input Count │
│ Inputs (with scriptSig) │ ← Signatures here
│ Output Count │
│ Outputs │
│ Locktime │
└──────────────────────────────┘
↓
txid = hash(all of this)
SegWit Transaction
┌──────────────────────────────┐
│ Version │
│ Marker (0x00) │ ← SegWit flag
│ Flag (0x01) │
│ Input Count │
│ Inputs (empty scriptSig) │
│ Output Count │
│ Outputs │
│ Witness │ ← Signatures here (separate)
│ Locktime │
└──────────────────────────────┘
↓
txid = hash(without witness)
wtxid = hash(with witness)
Witness Programs
Structure
<version byte> <witness program>
| Version | Length | Type |
|---|---|---|
| 0 | 20 bytes | P2WPKH |
| 0 | 32 bytes | P2WSH |
| 1 | 32 bytes | P2TR (Taproot) |
P2WPKH (Pay-to-Witness-Public-Key-Hash)
scriptPubKey:
OP_0 <20-byte-pubkey-hash>
Witness:
<signature> <public-key>
Equivalent to P2PKH but with witness data.
P2WSH (Pay-to-Witness-Script-Hash)
scriptPubKey:
OP_0 <32-byte-script-hash>
Witness:
<script arguments> <witness script>
Equivalent to P2SH for complex scripts.
Block Weight
SegWit introduces “weight” instead of byte size:
weight = base_size × 3 + total_size
| Component | Weight Factor |
|---|---|
| Non-witness data | 4 |
| Witness data | 1 |
Max block weight: 4,000,000 WU (effectively ~2-4 MB)
Virtual Size
For fee calculation:
vsize = ⌈weight / 4⌉
Commitment Structure
Witness data is committed in coinbase:
Coinbase output:
OP_RETURN <witness commitment>
Commitment = SHA256(SHA256(witness_root || witness_reserved))
This ensures witness data integrity without changing block header.
Benefits
1. Malleability Fix
txid no longer includes signatures:
- Stable txids for unconfirmed transactions
- Enables Lightning Network and other L2
2. Capacity Increase
~1.8x effective capacity for typical transactions.
3. Script Versioning
Witness version byte allows future upgrades:
- Version 0: P2WPKH, P2WSH
- Version 1: Taproot (BIP-341)
- Versions 2-16: Reserved for future
4. Linear Signature Hashing
Fixes quadratic hashing vulnerability:
# Old: O(n²) for n inputs
# SegWit: O(n) via cached midstate
Address Formats
Native SegWit (Bech32)
bc1q... (P2WPKH, 42 chars)
bc1q... (P2WSH, 62 chars)
Wrapped SegWit (P2SH)
For compatibility with old software:
3... (P2SH-P2WPKH)
3... (P2SH-P2WSH)
Implementation
Creating P2WPKH Output
const bitcoin = require('bitcoinjs-lib');
// From public key
const pubkey = Buffer.from('02...', 'hex');
const { address } = bitcoin.payments.p2wpkh({ pubkey });
// bc1q...
Spending P2WPKH
const psbt = new bitcoin.Psbt();
psbt.addInput({
hash: txid,
index: vout,
witnessUtxo: {
script: Buffer.from('0014...', 'hex'), // scriptPubKey
value: 100000
}
});
psbt.addOutput({
address: recipient,
value: 90000
});
psbt.signInput(0, keyPair);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
Signature Hash Algorithm
BIP-143 defines new sighash for SegWit:
hashPrevouts = SHA256(SHA256(all input outpoints))
hashSequence = SHA256(SHA256(all input sequences))
hashOutputs = SHA256(SHA256(all outputs))
preimage =
nVersion ||
hashPrevouts ||
hashSequence ||
outpoint ||
scriptCode ||
amount ||
nSequence ||
hashOutputs ||
nLockTime ||
sighashType
sighash = SHA256(SHA256(preimage))
Benefits:
- O(n) complexity instead of O(n²)
- Commits to input amounts (prevents fee attacks)
Migration Path
From Legacy to SegWit
- Generate new SegWit addresses
- Move funds when convenient
- Use SegWit for all new receiving
Wrapped SegWit
For sending to recipients who don’t support native:
// P2SH-wrapped P2WPKH
const { address } = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({ pubkey })
});
// 3...
For Agents
Always Use Native SegWit
- Lower fees than legacy
- Better than wrapped SegWit
- Wide support (2024+)
Fee Estimation
// SegWit transaction vsize
const vsize = Math.ceil((
10 + // overhead
inputs.length * 68 + // P2WPKH inputs
outputs.length * 31 // P2WPKH outputs
));
const fee = vsize * feeRate;
Verify SegWit Support
# Check if address is SegWit
if [[ $address == bc1* ]]; then
echo "Native SegWit"
elif [[ $address == 3* ]]; then
echo "Could be wrapped SegWit or P2SH"
fi
Machine-Readable Summary
{
"bip": 141,
"title": "Segregated Witness",
"status": "final",
"activated": "2017-08-24",
"block_height": 481824,
"max_block_weight": 4000000,
"witness_versions": {
"0": ["p2wpkh", "p2wsh"],
"1": ["p2tr"]
},
"benefits": ["malleability-fix", "capacity-increase", "script-versioning"]
}