CS251 Fall 2023
(cs251.stanford.edu)
Bitcoin Scripts and Wallets
Dan Boneh
Note: HW#1 is posted on the course web site. Due Tue, Oct. 10.
Recap: the Bitcoin blockchain
genesis
block BH1 BH2 BH3
H version (4 bytes) H prev H prev
prev (32 bytes)
time (4 bytes) …
bits (4 bytes)
nonce (4 bytes)
Tx (32 bytes) Tx root Tx root
root 80 bytes
coinbase Tx
coinbase Tx
Tx sequence
View the blockchain as a sequence of Tx (append-only)
coinbase Tx
Tx cannot be erased: mistaken Tx ⇒ locked or lost of funds
Tx structure (non-coinbase)
input[0] input: TxID 32 byte hash
inputs input[1] out-index 4 byte index
input[2] ScriptSig program
seq ignore
outputs output[0] TxID = H(Tx)
output[1] (excluding witnesses)
value 8 bytes
(segwit) witnesses output:
(part of input) ScriptPK program
(4 bytes)
locktime
#BTC = value/108
earliest block # that can include Tx
Example
null locktime
Tx1: input 2 ScriptPK 5 ScriptPK 0
(funding Tx) value
value
UTXO2
UTXO1
UTXO: unspent Tx output
Tx2: TxID 1 ScriptSig output output 0
(spending Tx) identifies UTXO3 UTXO4
a UTXO
Example
☓
null locktime
Tx1: input 2 ScriptPK 5 ScriptPK 0
(funding Tx)
UTXO2
UTXO1
UTXO: unspent Tx output
Tx2: TxID 1 ScriptSig output output 0
(spending Tx) identifies UTXO3 UTXO4
a UTXO
Validating
Tx2 program from funding
Tx: under what
conditions can UTXO be
spent
Miners check (for each input):
1. The program ScriptSig | ScriptPK returns true
2. TxID | index is in the current UTXO program from spending Tx:
proof that conditions
are met
set
3. sum input values ≥ sum output values
After Tx2 is posted, miners remove UTXO2 from UTXO set
Transaction types: (1) P2PKH
pay to public key hash
Alice want to pay Bob 5 BTC:
• step 1: Bob generates sig key pair (pkB, skB) ⇽ Gen()
• step 2: Bob computes his Bitcoin address as addrB ⇽ H(pkB)
• step 3: Bob sends addrB to Alice
• step 4: Alice posts Tx:
UTXOB for Bob UTXOA for Alice (change)
Point to input
Alice’s 7 BTC 5 2 0
UTXO ScriptPKB ScriptPKA
ScriptPKB:
DUP
HASH256
<addrB>
EQVERIFY
CHECKSIG
Transaction types: (1) P2PKH
pay to public key hash
“input” contains ScriptSig that authorizes spending Alice’s UTXO
• example: ScriptSig contains Alice’s signature on Tx
⟹ miners cannot change ScriptPKB (will invalidate Alice’s signature)
UTXOB for UTXOA for Alice (change)
Point to
Bob
Alice’s
input 2 ScriptPKA 0
UTXO 5
7 BTC
ScriptPKB: DUP HASH256 <addrB> EQVERIFY CHECKSIG
Transaction types: (1) P2PKH
Later, when Bob wants to spend his UTXO: create a Txspend
Txspend: TxID 0 ScriptSigB output output 0
points
to
UTXOB <sig> <pkB> (authorizes spending UTXOB)
<sig> = Sign(skB, Tx) where Tx = (Txspend excluding all ScriptSigs) (SIGHASH_ALL)
Miners validate that ScriptSigB | ScriptPKB returns true
P2PKH: comments
• Alice specifies recipient’s pk in UTXOB
• Recipient’s pk is not revealed until UTXO is spent
(some security against attacks on pk)
• Miner cannot change <AddrB> and steal funds:
invalidates Alice’s signature that created
UTXOB
Segregated Witness
ECDSA malleability:
Given (m, sig) anyone can create (m, sig’) with sig ≠ sig’
⇒ miner can change sig in Tx and change TxID = SHA256(Tx)
⇒ Tx issuer cannot tell what TxID is, until Tx is posted
⇒ leads to problems and attacks
Segregated witness: signature is moved to witness field in
Tx TxID = Hash(Tx without witnesses)
Transaction types: (2) P2SH: pay to script hash
(pre SegWit in 2017)
Payer specifies a redeem script (instead of just pkhash)
Usage: (1) Bob publishes hash(redeem script) ⟵ Bitcoint
(2) Alice sends funds to that address in funding Tx
(3) Bob can spend UTXO if he can satisfy the
script
ScriptPK in UTXO: HASH160 <H(redeem script)> EQUAL
ScriptSig to spend: <sig1> <sig2> … <sign> <redeem script>
payer can specify complex conditions for when UTXO can be spent
P2SH
Miner verifies:
(1) <ScriptSig> ScriptPK = true ⟵ spending Tx gave correct script
(2) ScriptSig = true ⟵ script is satisfied
Example P2SH: multisig
Goal: spending a UTXO requires t-out-of-n signatures
Redeem script for 2-out-of-3: (chosen by payer)
<2> <PK1> <PK2> <PK3> <3> CHECKMULTISIG
threshold
hash gives P2SH address
ScriptSig to spend: (by payee) <0> <sig1> <sig3> <redeem script>
(in the clear)
Abstractly …
Multisig address: addr = H(PK1, PK2, PK3, 2-of-3)
Tx1: input 5 addr 2 ScriptPKA 0
(funding Tx) 7 BTC UTXOB for Bob UTXOA for Alice
(change)
Tx2: input: UTXO, sig1, sig3, PK1, PK2, PK3, 2-of-3 output 0
(spending Tx)
Example Bitcoin scripts
Protecting assets with a co-signatory
Alice stores her funds in UTXOs for addr = 2-of-2(PKA, PKS)
PKA spending Tx PKS
Alice custody
SKA is this Alice server
yep, it’s me SKS
post Tx with <sigA> <sigS>
<sigS> on Tx
⇒ theft of Alice’s SKA does not compromise BTC
Escrow service
Alice wants to buy a backpack for 0.1₿ from merchant Bob
Goal: Alice only pays after backpack arrives, but can’t not pay
addr = 2-of-3(PKA, PKB, PKJ)
post want backpack for 0.1₿
payment Alice Bob Judge
of 0.11₿ once see Tx on chain
to addr PKA mail backpack PKB PKJ
(creates backpack arrives redeem using
UTXOA𝖾 (PKB:0.1,
UTXOA) send <sigA> on Tx: <sigA> <sigB>
PKA:0
.01) on Tx
Escrow service: a dispute
(1) Backpack never arrives: (Bob at fault)
Alice gets her funds back with help of Judge and a
Tx: Tx: ( UTXOA 𝖾 PKA , sigA,
sigJudge ) [2-out-of-3]
(2) Alice never sends sigA: (Alice at fault)
Bob gets paid with help of Judge and a Tx:
Tx: ( UTXOA 𝖾 PKB , sigB, sigJudge ) [2-out-of-3]
(3) Both are at fault: Judge publishes <sigJudge> on Tx:
Tx: ( UTXOA 𝖾 PKA: 0.05, PKB: 0.05, PKJ: 0.01
)
Now either Alice or Bob can execute this Tx.
Cross Chain Atomic Swap
Alice has 5 BTC, Bob has 2 LTC (LiteCoin). They want to
swap. Want a sequence of Tx on the Bitcoin and Litecoin chains
s.t.:
• either success: Alice has 2 LTC and Bob has 5 BTX,
• or failure: no funds move.
Swap cannot get stuck
halfway.
Goal: design a sequence of Tx to do
this. solution: programming proj #1
ex 4.
Managing crypto assets: Wallets
Managing secret keys
Users can have many PK/SK:
• one per Bitcoin address, Ethereum address, …
Wallets:
• Generates PK/SK, and stores SK,
• Post and verify Tx,
• Show balances
Managing lots of secret keys
Types of wallets:
• cloud (e.g., Coinbase): cloud holds secret keys … like a bank.
• laptop/phone: Electrum, MetaMask, …client stores secret
• hardware: Trezor, Ledger, Keystone, … keys
• paper: print all sk on paper
• brain: memorize sk (bad idea)
• Hybrid: non-custodial cloud wallet (using threshold signatures)
Not your keys, not your coins … but lose key ⇒ lose funds
Simplified Payment Verification (SPV)
How does a client wallet display Alice’s current balances?
• Laptop/phone wallet needs to verify an incoming payment
• Goal: do so w/o downloading entire blockchain (366 GB)
SPV: (1) download all block headers (60 MB)
block header
• wallet 𝖾 server: list of my wallet addrs (Bloom filter)
(2) Tx download:
• server 𝖾 wallet: Tx involving addresses +
Tx root
Merkle proof to block header.
Simplified Payment Verification (SPV)
Problems:
(1) Security: are BH the ones on the blockchain? Can server omit Tx?
• Electrum: download block headers from ten random
servers, optionally, also from a trusted full node.
List of servers: electrum.org/#community
(2) Privacy: remote server can test if an addr belongs to wallet
We will see better light client designs later in the course (e.g. Celo)
Hardware wallet: Ledger, Trezor, …
End user can have lots of secret keys. How to store them ???
Hardware wallet (e.g., Ledger Nano X)
- connects to laptop or phone wallet using Bluetooth or USB
- manages many secret keys
- Bolos OS: each coin type is an app on top of OS
- PIN to unlock HW (up to 48 digits)
- screen and buttons to verify and confirm Tx
Hardware wallet: backup
Lose hardware wallet ⇒ loss of funds. What to do?
Idea 1: generate a secret seed k0 ∈ {0,1}256 ECDSA public key
for i=1,2,…: sk ⇽ HMAC(k , i) , pk ⇽
𝑔𝑠𝑘𝑖
i 0 i
𝑝𝑘1, 𝑝𝑘2, 𝑝𝑘3, … : random unlinkable addresses (without
k0 )
k0 is stored on HW device and in offline storage (as 24 words)
⇒ in case of loss, buy new device, restore k0, recompute keys
On Ledger
When initializing ledger:
• user asked to write down the 24 words
• each word encodes 11 bits (24 ×11 = 268 bits)
• list of 2048 words in different languages (BIP 39)
Example: English word list
⋮
save list
of 24
words
Crypto Steel
Careful with unused letters …
On Ledger
When initializing ledger:
• user asked to write down the 24 words
• each word encodes 11 bits (24 ×11 = 268 bits)
• list of 2048 words in different languages (BIP 39)
Beware of “pre-initialized HW wallet”
• 2018: funds transferred to wallet promptly stolen
How to securely check balances?
With Idea1: need k0 just to check my balance:
• k0 needed to generate my addresses (𝑝𝑘1, 𝑝𝑘2, 𝑝𝑘3, … )
… but k0 can also be used to spend funds
• Can we check balances without the spending key ??
Goal: two seeds
• k0 lives on Ledger: can generate all secret keys (and addresses)
• kpub: lives on laptop/phone wallet: can only generate
addresses (for checking balance)
Idea 2: (used in HD wallets)
secret seed: k0 ∈ {0,1}256 ; (𝑘1, 𝑘2) ⇽ HMAC(k0, “init”)
balance seed: kpub = (𝑘2, ℎ = 𝑔𝑘1 )
for all i=1,2,…: ski ⇽ 𝑘1 + HMAC(𝑘2, i)
pki ⇽ 𝑔𝑠𝑘𝑖 = 𝑔𝑘1 3 𝑔𝐻𝑀𝐴𝐶 𝑘2,𝑖
=ℎ3
𝑔𝐻𝑀𝐴𝐶 𝑘2,𝑖
kpub does not reveal sk1, sk2, … computed from kpub
kpub: on laptop/phone, generates unlinkable addresses 𝑝𝑘1, 𝑝𝑘2,
…
k0: on ledger
Paper wallet (be careful when generating)
Bitcoin address = base58(hash(PK)) signing key (cleartext)
base58 = a-zA-Z0-9 without {0,O,l,1}
Managing crypto assets in the cloud
How exchanges store assets
Hot/cold storage
Coinbase: holds customer assets
Design: 98% of assets (SK) are held in cold storage
cold storage (98%) 𝑘0
𝑘
(1
)0
𝑘0
𝑘
(2
0
customers
)
𝑘0
(3
t-out-of-n secret sharing of
hot wallet (2%)
ℎ, 𝑘2 SKhot
used to 2% of
verify assets
cold
storage
balances
Problems
Can’t prove ownership of assets in cold storage,
without accessing cold storage:
• To prove ownership (e.g., in audit or in a proof of solvency)
• To participate in proof-of-stake consensus
Solutions:
• Keep everything in hot wallet (e.g, Anchorage)
• Proxy keys: keys that prove ownership of
assets, but cannot spend assets
END OF LECTURE
Next lecture: consensus