diff --git a/ch04.asciidoc b/ch04.asciidoc index 60e2044d..40cc8106 100644 --- a/ch04.asciidoc +++ b/ch04.asciidoc @@ -1311,7 +1311,7 @@ using SHA256 without RIPEMD160 is that P2WSH commitments are 32 bytes For the Pay-to-Taproot (P2TR) output, the witness program is a point on the secp256k1 curve. It may be a simple public key, but in most cases it should be a public key that commits to some additional data. We'll -learn more about that commitment in <>. +learn more about that commitment in <>. For the example of a future segwit version, we simply use the highest possible segwit version number (16) and the smallest allowed witness diff --git a/ch05.asciidoc b/ch05.asciidoc index 81a7f258..7c3f999e 100644 --- a/ch05.asciidoc +++ b/ch05.asciidoc @@ -114,6 +114,7 @@ possible to store private keys more securely than public keys. .Deterministic key generation: a deterministic sequence of keys derived from a seed for a wallet database image::images/mbc2_0502.png["Deterministic Wallet"] +[[public_child_key_derivation]] ==== Public Child Key Derivation In <>, we learned how to create a public key from a private key diff --git a/chapters/authorization-authentication.adoc b/chapters/authorization-authentication.adoc index 40479366..f0a1f555 100644 --- a/chapters/authorization-authentication.adoc +++ b/chapters/authorization-authentication.adoc @@ -1245,6 +1245,7 @@ account directly. Here's the redeemScript that Mohammed designs to achieve this (line number prefixed as XX): +[[variable_timelock_multisig]] .Variable Multi-Signature with Timelock ---- 01 OP_IF @@ -1626,7 +1627,7 @@ Even though Alice's wallet has no support for segwit, the payment it creates can be spent by Bob with a segwit transaction.((("", startref="aliced"))) -===== Pay-to-Witness-Script-Hash inside Pay-to-Script-Hash +===== Nested Pay-to-Witness-Script-Hash Similarly, a P2WSH witness program for a multisig script or other complicated script can be embedded inside a P2SH script and address, @@ -1697,3 +1698,367 @@ OP_HASH160 86762607e8fe87c0c37740cddee880988b9455b2 OP_EQUAL Mohammed's company can then construct segwit transactions to spend these payments, taking advantage of segwit features including lower transaction fees. + +=== Merklized Alternative Script Trees (MAST) + +Using +OP_IF+, you can authorize multiple different spending conditions, +but this approach has several undesirable aspects: + +- _Weight (cost):_ every condition you add increases the size of the + script, increasing the weight of the transaction and the amount of fee + that will need to be paid in order to spend bitcoins protected by + that script. + +- _Limited size:_ even if you're willing to pay for extra conditions, + there's a limit to the maximum number you can put in a script. For + example, legacy script is limited to 10,000 bytes, practically + limiting you to a few hundred conditional branches at most. Even if + you could create a script as large as an entire block, it could still + only contain about 20,000 useful branches. That's a lot for simple + payments but tiny compared to some imagined uses of Bitcoin. + +- _Lack of privacy:_ every condition you add to your script becomes + public knowledge when you spend bitcoins protected by that script. + For example, Mohammed's lawyer and business partners will be able to + see the entire script in <> whenever + anyone spends from it. That means their lawyer, even if he's not + needed for signing, will be able to track all of their transactions. + +However, Bitcoin already uses a data structure known as a merkle tree +that allows verifying an element is a member of a set without +needing to identify every other member of the set. + +We'll learn more about merkle trees in <>, but the +essential information is that members of the set of information we want +(e.g. authorization conditions of any length) can be passed into a hash +function to create a short commitment (called a _leaf_ of the merkle +tree). Each of those leaves is then paired with another leaf +and hashed again, creating a commitment to the leaves, called a +_branch_ commitment. A commitment to a pair of branches can be created +the same way. This step is repeated for the branches until only one +identifier remains, called the _merkle root_. Using our example script +from <>, we construct a merkle tree for each +of the three authorization conditions in <>. + +[[diagram_mast1]] +.A MAST with three sub-scripts +image::../images/mast1.dot.png["A MAST with three sub-scripts"] + +We can now create a compact membership proof that proves a particular +authorization condition is a member of the merkle tree without +disclosing any details about the other members of the merkle tree. See +<> and note that the nodes with diagonal corners can be +computed from other data provided by the user, so they don't need to be +specified at spend time. + +[[diagram_mast2]] +.A MAST membership proof for one of the sub-scripts +image::../images/mast2.dot.png["A MAST membership proof for one of the sub-scripts"] + +The hash digests used to create the commitments are each 32-bytes, so +proving the above spend is authorized (using a merkle tree and the +particular conditions) and authenticated (using signatures) uses 383 +bytes. By comparison, the same spend without a merkle tree (i.e. +providing all possible authorization conditions) uses 412 bytes. + +Saving 29 bytes (7%) in this example doesn't fully +capture the potential savings. The binary-tree nature of a merkle tree +means that you only need an additional 32-byte commitment every time +you double the number of members in the set (in this case, authorization +conditions). In this case, with three conditions, we need to use three +commitments (one of them being the merkle root, which will need to be +included in the authorization data); we could also have four +commitments for the same cost. An extra commitment would give us up to +eight conditions. With just 16 commitments--512 bytes of commitments--we could have +over 32,000 authorization conditions, far more that could be effectively +used in an entire block of +OP_IF+ statements. With 128 commitments +(4,096 bytes), the number of conditions we could create in theory far +exceeds the number of conditions that all the computers in the world +could create. + +It's commonly the case that not every authorization condition is equally +as likely to be used. In the our example case, we expect Mohammed and +his partners to spend their money frequently; the time delayed +conditions only exist in case something goes wrong. We can re-structure +our tree with this knowledge in <>. + +[[diagram_mast3]] +.A MAST with the most-expected script in the best position +image::../images/mast3.dot.png["A MAST with the most-expected script in the best position"] + +Now we only need to provide two commitments for the common case (saving 32 +bytes), although we still need three commitments for the less common cases. +If you know (or can guess) the probabilities of +using the different authorization conditions, you can use the Huffman +algorithm to place them into a maximally-efficient tree; see BIP341 for +details. + +Regardless of how the tree is constructed, we can see in the above +examples that we're only revealing the actual authorization conditions +that get used. The other conditions remain private. It even remains +private the number of conditions: a tree could have a single condition +or a trillion conditions--there's no way for someone looking only at the +onchain data for a single transaction to tell. + +Except for increasing the complexity of Bitcoin slightly, there are no +significant downsides of MAST for Bitcoin and there were two solid +proposals for it, BIP114 and BIP116, before an improved approach was +discovered, which we'll see in <>. + +[[pay_to_contract]] +=== Pay to Contract (P2C) + +As we saw in <>, the math of Elliptic Curve +Cryptography (ECC) allows Alice to use a private key to derive a public +key that she gives to Bob. He can add an arbitrary value to that public +key create a derived public key. If he gives that arbitrary value to Alice, she can +add it to her private key to derive the private key for the derived +public key. In short, Bob can create child public keys for which only +Alice can create the corresponding private keys. This is useful for +BIP32-style Hierarchical Deterministic (HD) wallet recovery, but it can +also serve another use. + +Let's imagine Bob wants to buy something from Alice but he also wants to +be able prove later what he paid for in case there's any dispute. Alice +and Bob agree on the name of the item or service being sold, e.g. +"Alice's podcast episode #123", and transform that description into a +number by hashing it and interpreting the hash digest as a number. Bob +adds that number to Alice's public key and pays it. The process is +called _key tweaking_ and the number is known as a _tweak_. + +Alice can spend the funds by tweaking her private key using the same +number (tweak). + +Later, Bob can prove to anyone what he paid Alice by revealing her +underlying key and the description they used. Anyone can verify that +the public key which was paid equals the underlying key plus the +hash commitment to the description. If Alice admits that key is hers, +then she received the payment. If Alice spent the funds, this further +proves she knew the description at the time she signed the spending +transaction, since she could only create a valid signature for the +tweaked public key if she knew the tweak (the description). + +If neither Alice or Bob decided to publicly reveal the description they +use, the payment between them looks like any other payment. There's no +privacy loss. + +Because P2C is private by default, we can't know how often it is used +for its original purpose--in theory every payment could be using it, +although we consider that unlikely. However, P2C is widely used today +in a slightly different form, which we'll see in <>. + +=== Scriptless Multisignature + +In <>, we looked at scripts which require signatures from +multiple keys. However, there's another way to require cooperation from +multiple keys, which is also confusingly called _multisignature_. To +distinguish between the two types in this section, we'll call the +version involving `OP_CHECKSIG`-style opcodes _script multisignatures_ +and the other version _scriptless multisignatures_. + +Scriptless multisignatures involves each participant creating their own +secret the same way they create a private key. We'll call this secret a +_partial private key_, although we should note that it's the same length +as a regular full private key. From the partial private key, each +participant derives a partial public key using the same algorithm used +for regular public keys we described in <>. Each +participant shares their partial public keys with all the other +participants and then combines all of the keys together to create the +scriptless multisignature public key. + +This combined public key looks the same as any other Bitcoin public key. +A third party can't distinguish between a multi-party public key and an +ordinary key generated by a single user. + +To spend bitcoins protected by the scriptless multisignature public key, +each participant generates a partial signature. The partial signatures +are then combined to create a regular full signature. There are +many known methods for creating and combining the partial signatures; +we'll look at this topic more in <>. Similar to the public +keys for scriptless multisignatures, the signatures generated by this +process look the same as any other Bitcoin signature. Third parties +can't determine whether a signature was created by a single person or a +million people cooperating with each other. + +Scriptless multisignatures are smaller and more private than scripted +multisignatures. For scripted multisignatures, the number of bytes +placed in a transaction increases for every key and signature involved. +For scriptless multisignatures, the size is constant--a million +participants each providing their own partial key and partial signature +puts exactly the same amount of data in a transaction as an individual +using a single key and signature. The story is the same for privacy: +because each new key or signature adds data to a transaction, scripted +multisignatures disclose data about how many keys and signatures are +being used--which may make it easy to figure out which transactions were +created by which group of participants. But, because every scriptless +multisignatures looks identical to every other scriptless +multisignature and every single-signature, no privacy-reducing data is +leaked. + +There are two downsides of scriptless multisignatures. The first is +that all known secure algorithms for creating them for Bitcoin require more +rounds of interaction or more careful management of state (or both) than +scripted multisignature. This can be challenging in cases where +signatures are being generated by nearly stateless hardware signing +devices and the keys are physically distributed. For example, if you +keep a hardware signing device in a bank safe deposit box, you would +need to visit that box once to create a scripted multisignature but +possibly two or three times for a scriptless multisignature. + +The other downside is that threshold signing doesn't reveal who signed. +In scripted threshold signing, Alice, Bob, and Carol agree (for example) +that any two of them signing will be sufficient to spend the funds. +If Alice and Bob sign, this requires putting signatures from each of +them on chain, proving to anyone who knows their keys that they signed +and Carol didn't. In scriptless threshold signing, a signature from +Alice and Bob is indistinguishable from a signature between Alice and +Carol or Bob and Carol. This is beneficial for privacy, but it means +that, even if Carol claims she didn't sign, she can't +prove that she didn't, which may be bad for accountability and +auditability. + +For many users and use cases, the always reduced sized and increased +privacy of multisignatures outweighs its occasional challenges for +creating and auditing signatures. + +[[taproot]] +=== Taproot + +One reason people choose to use Bitcoin is that it's possible to create +contracts with highly predictable outcomes. Legal contracts enforced by +a court of law depend in part on decisions by the judges and jurors +involved in the case. By contrast, Bitcoin contracts often require +actions by their participants but are otherwise enforced by thousands of +full nodes all running functionally identical code. When given the same +contract and the same input, every full node will always produce the +same result. Any deviation would mean that Bitcoin was broken. +Human judges and juries can be much more flexible than software, but +when that flexibility isn't wanted or needed, the predictability of +Bitcoin contracts is a major asset. + +If all of the participants in a contract recognize that its outcome has +become completely predictable, there's not actually any need for them to +continue using the contract. They could just do whatever the contract +compels them to do and then terminate the contract. In society, this +is how most contracts terminate: if the interested parties are +satisfied, they never take the contract before a judge or jury. In +Bitcoin, it means that any contract which will use a significant amount +of block space to settle should also provide a clause that allows it to +instead be settled by mutual satisfaction. + +In MAST and with scriptless multisignatures, a mutual satisfaction +clause is easy to design. We simply make one of the top leaves of the +script tree a scriptless multisignature between all interested parties. +We already saw a complex contract between several parties with a +simple mutual satisfaction clause in <>. We could make +that more optimized by switching from scripted multisignature to +scriptless multisignature. + +That's reasonably efficient and private. If the mutual satisfaction +clause is used, we only need to provide a single merkle branch and all +we reveal is that a signature was involved (it could be from one person +or it could be from thousands of different participants). But +developers in 2018 realized that we could do better if we also used +pay-to-contract. + +In our previous description of pay-to-contract in <>, +we tweaked a public key to commit to the text of an agreement between +Alice and Bob. We can instead commit to the program code of a contract +by committing to the root of a MAST. The public key we tweak +is a regular Bitcoin public key, meaning it could require a signature +from a single person or it could require a signature from multiple +people (or it could be created in a special way to make it impossible to +generate a signature for it). That means we can satisfy the contract +either with a single signature from all interested parties or by +revealing the MAST branch we want to use. That commitment tree +involving both a public key and a MAST is shown in [[diagram_taproot]]. + +[[diagram_taproot1]] +.A taproot with the public key committing to a merkle root +image::../images/taproot1.dot.png["A taproot with the public key committing to a merkle root"] + +This makes the mutual satisfaction clause using a multisignature +extremely efficient and very private. It's even more private than it +may appear because any transaction created by a single user who wants it +to be satisfied by a single signature (or a multisignature generated by +multiple different wallets they control) looks identical onchain to a +mutual-satisfaction spend. + +When spending is possible using just the key, such for a single signature +or scriptless multisignature, that is called _keypath spending_. When +the tree of scripts is used, that is called _scriptpath spending_. +For keypath spending, the data that gets put onchain is the public key +(in a witness program) and the signature (in the witness data). + +For scriptpath spending, the onchain data also includes the public key, +which is placed in a witness program and called the _taproot output key_ +in this context. The witness data includes the following information: + +1. A version number + +2. The underlying key--the key which existed before being tweaked by the + merkle root to produce the taproot output key. This underlying key + is called the _taproot internal key_ + +3. The script to execute + +4. One 32-byte hash for each merkle branch connecting the script to the merkle root + +5. Any data necessary to satisfy the script (such as signatures or hash preimages) + +// Source for 33 bytes: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-February/017622.html + +We're only aware of one significant described downside of taproot: +contracts whose participants want to use MAST but who don't want a +mutual satisfaction clause have to include a taproot internal key on the +blockchain, adding about 33 bytes of overhead. Given that almost +all contracts are expected to benefit from a mutual satisfaction clause, +or other multisignature clause that uses the top-level public key, and +all users benefit from the increased anonymity set of outputs looking +similar to each other, that rare overhead was not considered important +by most users who participated in taproot's activation. + +Support for taproot was added to Bitcoin in a soft fork which activated +in November 2021. + +=== Tapscript + +Taproot enables MAST but only with a slightly different version of the +Bitcoin Script language than previously used, the new version being +called _tapscript_. The major differences include: + +Scripted multisignature changes:: + The old +OP_CHECKMULTSIG+ and +OP_CHECKMULTISIGVERIFY+ opcodes are + removed. Those opcodes don't combine well with one of the other + changes in the taproot soft fork, the ability to use schnorr signatures + with batch validation. A new +OP_CHECKSIGADD+ opcode is provided + instead. When it successfully verifies a signature, this new opcode + increments a counter by one, making it possible to conveniently count + how many signatures passed, which can be compared against the desired number + of successful signatures to reimplement the same behavior as + +OP_CHECKMULTISIG+. + +Changes to all signatures:: + All signature operations in tapscript use the schnorr signature + algorithm as defined in BIP340. We'll explore schnorr signatures more + in <>. ++ +Additionally, any signature-checking operation which is not expected + to succeed must be fed the value +OP_FALSE+ (also called +OP_0+) + instead of an actual signature. Providing anything else to a failed + signature-checking operation will cause the entire script to fail. + This also helps support batch validation of schnorr signatures. + +OP_SUCCESSx opcodes:: + Opcodes in previous versions of script which were unusable are now + redefined to be cause an entire script to succeed if they are used. + This allows future soft forks to redefine them as not succeeding, which + is a restriction and so is possible to do in a soft fork. (The + opposite, to define a not-succeeding operation as a success can only + be done in a hard fork, which is a much more challenging upgrade + path.) + +Although we've looked at authorization and authentication in depth in +this chapter, we've skipped over one very important part of how Bitcoin +authenticates spenders: its signatures. We'll look at that next in +<>. diff --git a/images/mast1.dot b/images/mast1.dot new file mode 100644 index 00000000..ebdbbd41 --- /dev/null +++ b/images/mast1.dot @@ -0,0 +1,21 @@ +digraph merkle_tree { + splines=ortho; + node [shape=box, style="filled", color="black", fontcolor="black", fillcolor="white"]; + + "Merkle Root" -> "Hash AB"; + "Merkle Root" -> "Hash C"; + "Hash AB" -> "Hash A"; + "Hash AB" -> "Hash B"; + "Hash A" -> "A"; + "Hash B" -> "B"; + "Hash C" -> "C" [minlen = 2]; + + "Merkle Root" [label="Merkle Root"]; + "Hash AB" [label="Hash AB"]; + "Hash A" [label="Hash A"]; + "Hash B" [label="Hash B"]; + "Hash C" [label="Hash C"]; + "A" [label="2 3 OP_CHECKMULTISIG"]; + "B" [label="<30 days> OP_CSV OP_DROP\n OP_CHECKSIGVERIFY\n1 3 OP_CHECKMULTISIG"]; + "C" [label="<90 days> OP_CSV OP_DROP\n OP_CHECKSIG"]; +} diff --git a/images/mast1.dot.png b/images/mast1.dot.png new file mode 100644 index 00000000..7b4f8953 Binary files /dev/null and b/images/mast1.dot.png differ diff --git a/images/mast2.dot b/images/mast2.dot new file mode 100644 index 00000000..8613d650 --- /dev/null +++ b/images/mast2.dot @@ -0,0 +1,21 @@ +digraph merkle_tree { + splines=ortho; + node [shape=box, style="filled", color="black", fontcolor="black", fillcolor="white"]; + + "Merkle Root" -> "Hash AB"; + "Merkle Root" -> "Hash C"; + "Hash AB" -> "Hash A"; + "Hash AB" -> "Hash B"; + "Hash A" -> "A"; + //"Hash B" -> "B"; + //"Hash C" -> "C" [minlen = 2]; + + "Merkle Root" [label="Merkle Root", style = "diagonals"]; + "Hash AB" [label="Hash AB", style = "diagonals" ]; + "Hash A" [label="Hash A" style = "diagonals"]; + "Hash B" [label="Hash B"]; + "Hash C" [label="Hash C"]; + "A" [label="2 3 OP_CHECKMULTISIG"]; + //"B" [label="<30 days> OP_CSV OP_DROP\n OP_CHECKSIGVERIFY\n1 3 OP_CHECKMULTISIG"]; + //"C" [label="<90 days> OP_CSV OP_DROP\n OP_CHECKSIG"]; +} diff --git a/images/mast2.dot.png b/images/mast2.dot.png new file mode 100644 index 00000000..ba4dd840 Binary files /dev/null and b/images/mast2.dot.png differ diff --git a/images/mast3.dot b/images/mast3.dot new file mode 100644 index 00000000..d996ee4f --- /dev/null +++ b/images/mast3.dot @@ -0,0 +1,21 @@ +digraph merkle_tree { + splines=ortho; + node [shape=box, style="filled", color="black", fontcolor="black", fillcolor="white"]; + + "Merkle Root" -> "Hash AB"; + "Merkle Root" -> "Hash C"; + "Hash AB" -> "Hash A"; + "Hash AB" -> "Hash B"; + "Hash A" -> "A"; + "Hash B" -> "B"; + "Hash C" -> "C" [minlen = 2]; + + "Merkle Root" [label="Merkle Root"]; + "Hash AB" [label="Hash AB"]; + "Hash A" [label="Hash A"]; + "Hash B" [label="Hash B"]; + "Hash C" [label="Hash C"]; + "C" [label="2 3 OP_CHECKMULTISIG", style="filled", fillcolor="silver"]; + "B" [label="<30 days> OP_CSV OP_DROP\n OP_CHECKSIGVERIFY\n1 3 OP_CHECKMULTISIG"]; + "A" [label="<90 days> OP_CSV OP_DROP\n OP_CHECKSIG"]; +} diff --git a/images/mast3.dot.png b/images/mast3.dot.png new file mode 100644 index 00000000..908f8fbe Binary files /dev/null and b/images/mast3.dot.png differ diff --git a/images/taproot1.dot b/images/taproot1.dot new file mode 100644 index 00000000..8e0047ba --- /dev/null +++ b/images/taproot1.dot @@ -0,0 +1,16 @@ +digraph merkle_tree { + splines=ortho; + node [shape=box, style="filled", color="black", fontcolor="black", fillcolor="white"]; + + "Public Key" -> "Merkle Root" + "Merkle Root" -> "Hash A"; + "Merkle Root" -> "Hash B"; + "Hash A" -> "A"; + "Hash B" -> "B"; + + "Merkle Root" [label="Merkle Root"]; + "Hash A" [label="Hash A"]; + "Hash B" [label="Hash B"]; + "B" [label="Script 0"]; + "A" [label="Script 1"]; +} diff --git a/images/taproot1.dot.png b/images/taproot1.dot.png new file mode 100644 index 00000000..39895f66 Binary files /dev/null and b/images/taproot1.dot.png differ