summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.mediawiki56
-rw-r--r--bip-0002.mediawiki13
-rw-r--r--bip-0010.mediawiki4
-rw-r--r--bip-0012.mediawiki4
-rw-r--r--bip-0021.mediawiki2
-rw-r--r--bip-0032.mediawiki10
-rw-r--r--bip-0038.mediawiki2
-rw-r--r--bip-0039.mediawiki7
-rw-r--r--bip-0043.mediawiki2
-rw-r--r--bip-0044.mediawiki2
-rw-r--r--bip-0047.mediawiki18
-rw-r--r--bip-0049.mediawiki2
-rw-r--r--bip-0060.mediawiki4
-rw-r--r--bip-0084.mediawiki4
-rw-r--r--bip-0085.mediawiki82
-rw-r--r--bip-0086.mediawiki2
-rw-r--r--bip-0087.mediawiki2
-rw-r--r--bip-0088.mediawiki6
-rw-r--r--bip-0119.mediawiki13
-rw-r--r--bip-0126.mediawiki4
-rw-r--r--bip-0127.mediawiki1
-rw-r--r--bip-0129.mediawiki2
-rw-r--r--bip-0137.mediawiki2
-rw-r--r--bip-0141.mediawiki6
-rw-r--r--bip-0143.mediawiki4
-rw-r--r--bip-0152.mediawiki2
-rw-r--r--bip-0158.mediawiki2
-rw-r--r--bip-0174.mediawiki2
-rw-r--r--bip-0176.mediawiki2
-rw-r--r--bip-0300.mediawiki299
-rw-r--r--bip-0324.mediawiki94
-rw-r--r--bip-0327.mediawiki8
-rw-r--r--bip-0329.mediawiki22
-rw-r--r--bip-0330.mediawiki2
-rw-r--r--bip-0340.mediawiki55
-rw-r--r--bip-0340/reference.py4
-rw-r--r--bip-0340/test-vectors.csv4
-rw-r--r--bip-0340/test-vectors.py20
-rw-r--r--bip-0341.mediawiki2
-rw-r--r--bip-0345.mediawiki688
-rw-r--r--bip-0345/opvault.drawio.pngbin0 -> 92563 bytes
-rw-r--r--bip-0345/vaults-Basic.pngbin0 -> 18595 bytes
-rw-r--r--bip-0345/vaults.drawio1113
-rw-r--r--bip-0345/withdrawal-comparison.drawio.pngbin0 -> 20720 bytes
-rw-r--r--bip-0350.mediawiki2
-rw-r--r--bip-0370.mediawiki2
-rw-r--r--bip-0371.mediawiki2
-rw-r--r--bip-0380.mediawiki61
-rw-r--r--bip-0381.mediawiki46
-rw-r--r--bip-0382.mediawiki49
-rw-r--r--bip-0383.mediawiki34
-rw-r--r--bip-0384.mediawiki35
-rw-r--r--bip-0385.mediawiki22
-rw-r--r--bip-0386.mediawiki25
-rw-r--r--bip-0389.mediawiki109
55 files changed, 2739 insertions, 221 deletions
diff --git a/README.mediawiki b/README.mediawiki
index bc174cf..b5ae7bb 100644
--- a/README.mediawiki
+++ b/README.mediawiki
@@ -1,4 +1,4 @@
-People wishing to submit BIPs, first should propose their idea or document to the [https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev bitcoin-dev@lists.linuxfoundation.org] mailing list (do <em>not</em> assign a number - read <a href="bip-0002.mediawiki">BIP 2</a> for the full process). After discussion, please open a PR. After copy-editing and acceptance, it will be published here.
+People wishing to submit BIPs, first should propose their idea or document to the [https://groups.google.com/g/bitcoindev bitcoindev@googlegroups.com] mailing list (do <em>not</em> assign a number - read <a href="bip-0002.mediawiki">BIP 2</a> for the full process). After discussion, please open a PR. After copy-editing and acceptance, it will be published here.
We are fairly liberal with approving BIPs, and try not to be too involved in decision making on behalf of the community. The exception is in very rare cases of dispute resolution when a decision is contentious and cannot be agreed upon. In those cases, the conservative option will always be preferred.
@@ -235,7 +235,7 @@ Those proposing changes should consider that ultimately consent may rest with th
| Applications
| Purpose Field for Deterministic Wallets
| Marek Palatinus, Pavol Rusnak
-| Informational
+| Standard
| Final
|- style="background-color: #ffffcf"
| [[bip-0044.mediawiki|44]]
@@ -243,7 +243,7 @@ Those proposing changes should consider that ultimately consent may rest with th
| Multi-Account Hierarchy for Deterministic Wallets
| Marek Palatinus, Pavol Rusnak
| Standard
-| Proposed
+| Final
|- style="background-color: #ffffcf"
| [[bip-0045.mediawiki|45]]
| Applications
@@ -270,7 +270,7 @@ Those proposing changes should consider that ultimately consent may rest with th
| Applications
| Derivation scheme for P2WPKH-nested-in-P2SH based accounts
| Daniel Weigl
-| Informational
+| Standard
| Final
|- style="background-color: #cfffcf"
| [[bip-0050.mediawiki|50]]
@@ -434,13 +434,13 @@ Those proposing changes should consider that ultimately consent may rest with th
| Eric Lombrozo
| Standard
| Rejected
-|-
+|- style="background-color: #cfffcf"
| [[bip-0084.mediawiki|84]]
| Applications
| Derivation scheme for P2WPKH based accounts
| Pavol Rusnak
-| Informational
-| Draft
+| Standard
+| Final
|-
| [[bip-0085.mediawiki|85]]
| Applications
@@ -452,7 +452,7 @@ Those proposing changes should consider that ultimately consent may rest with th
| [[bip-0086.mediawiki|86]]
| Applications
| Key Derivation for Single Key P2TR Outputs
-| Andrew Chow
+| Ava Chow
| Standard
| Draft
|- style="background-color: #ffffcf"
@@ -487,7 +487,7 @@ Those proposing changes should consider that ultimately consent may rest with th
| [[bip-0093.mediawiki|93]]
| Applications
| codex32: Checksummed SSSS-aware BIP32 seeds
-| Leon Olsson Curr, Pearlwort Sneed
+| Leon Olsson Curr, Pearlwort Sneed, Andrew Poelstra
| Informational
| Draft
|-
@@ -627,7 +627,7 @@ Those proposing changes should consider that ultimately consent may rest with th
| [[bip-0119.mediawiki|119]]
| Consensus (soft fork)
| CHECKTEMPLATEVERIFY
-| Jeremy Rubin
+| Jeremy Rubin, James O'Beirne
| Standard
| Draft
|- style="background-color: #ffcfcf"
@@ -900,7 +900,7 @@ Those proposing changes should consider that ultimately consent may rest with th
| [[bip-0174.mediawiki|174]]
| Applications
| Partially Signed Bitcoin Transaction Format
-| Andrew Chow
+| Ava Chow
| Standard
| Final
|- style="background-color: #ffcfcf"
@@ -1079,12 +1079,19 @@ Those proposing changes should consider that ultimately consent may rest with th
| Standard
| Final
|-
+| [[bip-0345.mediawiki|345]]
+| Consensus (soft fork)
+| OP_VAULT
+| James O'Beirne, Greg Sanders, Anthony Towns
+| Standard
+| Draft
+|- style="background-color: #cfffcf"
| [[bip-0350.mediawiki|350]]
| Applications
| Bech32m format for v1+ witness addresses
| Pieter Wuille
| Standard
-| Draft
+| Final
|-
| [[bip-0351.mediawiki|351]]
| Applications
@@ -1096,14 +1103,14 @@ Those proposing changes should consider that ultimately consent may rest with th
| [[bip-0370.mediawiki|370]]
| Applications
| PSBT Version 2
-| Andrew Chow
+| Ava Chow
| Standard
| Draft
|-
| [[bip-0371.mediawiki|371]]
| Applications
| Taproot Fields for PSBT
-| Andrew Chow
+| Ava Chow
| Standard
| Draft
|-
@@ -1117,49 +1124,56 @@ Those proposing changes should consider that ultimately consent may rest with th
| [[bip-0380.mediawiki|380]]
| Applications
| Output Script Descriptors General Operation
-| Pieter Wuille, Andrew Chow
+| Pieter Wuille, Ava Chow
| Informational
| Draft
|-
| [[bip-0381.mediawiki|381]]
| Applications
| Non-Segwit Output Script Descriptors
-| Pieter Wuille, Andrew Chow
+| Pieter Wuille, Ava Chow
| Informational
| Draft
|-
| [[bip-0382.mediawiki|382]]
| Applications
| Segwit Output Script Descriptors
-| Pieter Wuille, Andrew Chow
+| Pieter Wuille, Ava Chow
| Informational
| Draft
|-
| [[bip-0383.mediawiki|383]]
| Applications
| Multisig Output Script Descriptors
-| Pieter Wuille, Andrew Chow
+| Pieter Wuille, Ava Chow
| Informational
| Draft
|-
| [[bip-0384.mediawiki|384]]
| Applications
| combo() Output Script Descriptors
-| Pieter Wuille, Andrew Chow
+| Pieter Wuille, Ava Chow
| Informational
| Draft
|-
| [[bip-0385.mediawiki|385]]
| Applications
| raw() and addr() Output Script Descriptors
-| Pieter Wuille, Andrew Chow
+| Pieter Wuille, Ava Chow
| Informational
| Draft
|-
| [[bip-0386.mediawiki|386]]
| Applications
| tr() Output Script Descriptors
-| Pieter Wuille, Andrew Chow
+| Pieter Wuille, Ava Chow
+| Informational
+| Draft
+|-
+| [[bip-0389.mediawiki|389]]
+| Applications
+| Multipath Descriptor Key Expressions
+| Ava Chow
| Informational
| Draft
|}
diff --git a/bip-0002.mediawiki b/bip-0002.mediawiki
index c6eb950..0462a00 100644
--- a/bip-0002.mediawiki
+++ b/bip-0002.mediawiki
@@ -32,13 +32,13 @@ The BIP process begins with a new idea for Bitcoin. Each potential BIP must have
Small enhancements or patches to a particular piece of software often don't require standardisation between multiple projects; these don't need a BIP and should be injected into the relevant project-specific development workflow with a patch submission to the applicable issue tracker.
Additionally, many ideas have been brought forward for changing Bitcoin that have been rejected for various reasons.
The first step should be to search past discussions to see if an idea has been considered before, and if so, what issues arose in its progression.
-After investigating past work, the best way to proceed is by posting about the new idea to the [https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev Bitcoin development mailing list].
+After investigating past work, the best way to proceed is by posting about the new idea to the [https://groups.google.com/g/bitcoindev Bitcoin development mailing list].
Vetting an idea publicly before going as far as writing a BIP is meant to save both the potential author and the wider community time.
Asking the Bitcoin community first if an idea is original helps prevent too much time being spent on something that is guaranteed to be rejected based on prior discussions (searching the internet does not always do the trick).
It also helps to make sure the idea is applicable to the entire community and not just the author. Just because an idea sounds good to the author does not mean it will work for most people in most areas where Bitcoin is used.
-Once the champion has asked the Bitcoin community as to whether an idea has any chance of acceptance, a draft BIP should be presented to the [https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev Bitcoin development mailing list].
+Once the champion has asked the Bitcoin community as to whether an idea has any chance of acceptance, a draft BIP should be presented to the [https://groups.google.com/g/bitcoindev Bitcoin development mailing list].
This gives the author a chance to flesh out the draft BIP to make it properly formatted, of high quality, and to address additional concerns about the proposal.
Following a discussion, the proposal should be submitted to the [https://github.com/bitcoin/bips BIPs git repository] as a pull request.
This draft must be written in BIP style as described below, and named with an alias such as "bip-johndoe-infinitebitcoins" until an editor has assigned it a BIP number (authors MUST NOT self-assign BIP numbers).
@@ -67,8 +67,12 @@ If you are interested in assuming ownership of a BIP, send a message asking to t
The current BIP editors are:
+* Bryan Bishop ([[mailto:kanzure@gmail.com|kanzure@gmail.com]])
+* Jon Atack ([[mailto:jon@atack.com|jon@atack.com]])
* Luke Dashjr ([[mailto:luke_bipeditor@dashjr.org|luke_bipeditor@dashjr.org]])
-* Kalle Alm ([[mailto:karljohan-alm@garage.co.jp|karljohan-alm@garage.co.jp]])
+* Mark "Murch" Erhardt ([[mailto:murch@murch.one|murch@murch.one]])
+* Olaoluwa Osuntokun ([[mailto:laolu32@gmail.com|laolu32@gmail.com]])
+* Ruben Somsen ([[mailto:rsomsen@gmail.com|rsomsen@gmail.com]])
===BIP Editor Responsibilities & Workflow===
@@ -102,7 +106,7 @@ The BIP editors are intended to fulfill administrative and editorial responsibil
===Specification===
-BIPs should be written in mediawiki format.
+BIPs should be written in mediawiki or markdown format.
Each BIP should have the following parts:
@@ -409,7 +413,6 @@ Why is Public Domain no longer acceptable for new BIPs?
* Non-image auxiliary files are permitted in the bip-XXXX subdirectory.
* Email addresses are now required for authors.
* The Post-History header may be provided as a link instead of a simple date.
-* Markdown format is no longer permitted for BIPs.
* The Resolution header has been dropped, as it is not applicable to a decentralised system where no authority exists to make final decisions.
==See Also==
diff --git a/bip-0010.mediawiki b/bip-0010.mediawiki
index 42071f3..289e3b0 100644
--- a/bip-0010.mediawiki
+++ b/bip-0010.mediawiki
@@ -93,10 +93,10 @@ The following is an example TxDP from Armory, produced while running on the test
In this transaction, there are two inputs, one of 150 BTC and the other of 12 BTC. This transaction combines 162 BTC to create two outputs, one of 160 BTC, one 1.9995 BTC, and a tx fee of 0.0005. In this TxDP, both inputs have been signed, and thus could broadcast immediately.
-The style of communication is taken directly from PGP/GPG, which uses blocks of ASCII like this to communicate encrypted messages and signatures. This serialization is compact, and will be interpretted the same in all character encodings. It can be copied inline into an email, or saved in a text file. The advantage over the analogous PGP encoding is that there are some human readable elements to it, for users that wish to examine the TxDP packet manually, instead of requiring a program to parse the core elements of the TxDP.
+The style of communication is taken directly from PGP/GPG, which uses blocks of ASCII like this to communicate encrypted messages and signatures. This serialization is compact, and will be interpreted the same in all character encodings. It can be copied inline into an email, or saved in a text file. The advantage over the analogous PGP encoding is that there are some human readable elements to it, for users that wish to examine the TxDP packet manually, instead of requiring a program to parse the core elements of the TxDP.
A party receiving this TxDP can simply add their signature to the appropriate _TXINPUT_ line. If that is the last signature required, they can broadcast it themselves. Any software that implements this standard should be able to combine multiple TxDPs into a single TxDP. However, even without the programmatic support, a user could manually combine them by copying the appropriate _TXSIGS_ lines between serializations, though it is not the recommended method for combining TxDPs.
== Reference Implementation ==
-This proposal was implemented and tested in the older versions of ''Armory'' Bitcoin software for use in offline-wallet transaction signing (as a 1-of-1 transaction). Implementation can be found in https://github.com/etotheipi/BitcoinArmory/blob/v0.91-beta/armoryengine/Transaction.py under the class PyTxDistProposal. However, as of verion 0.92 released in July 2014, Armory no longer uses this proposal for offline wallet transaction signing and has moved on to a new format.
+This proposal was implemented and tested in the older versions of ''Armory'' Bitcoin software for use in offline-wallet transaction signing (as a 1-of-1 transaction). Implementation can be found in https://github.com/etotheipi/BitcoinArmory/blob/v0.91-beta/armoryengine/Transaction.py under the class PyTxDistProposal. However, as of version 0.92 released in July 2014, Armory no longer uses this proposal for offline wallet transaction signing and has moved on to a new format.
diff --git a/bip-0012.mediawiki b/bip-0012.mediawiki
index 70069d6..bd3d88c 100644
--- a/bip-0012.mediawiki
+++ b/bip-0012.mediawiki
@@ -43,11 +43,11 @@ OP_EVAL allows the receiver of bitcoins to specify how they can be spent when th
If ''serialized script'' is a large or complicated multi-signature script, then the burden of paying for it (in increased transaction fees due to more signature operations or transaction size) is shifted from the sender to the receiver.
-The main objection to OP_EVAL is that it adds complexity, and complexity is the enemy of security. Also, evaluating data as code has a long record of being a source of security vulnerabilties.
+The main objection to OP_EVAL is that it adds complexity, and complexity is the enemy of security. Also, evaluating data as code has a long record of being a source of security vulnerabilities.
That same argument can be applied to the existing Bitcoin 'scripting' system; scriptPubKeys are transmit as data across the network and are then interpreted by every bitcoin implementation. OP_EVAL just moves the data that will be interpreted. It is debatable whether or not the entire idea of putting a little interpreted expression evaluation language at the core of Bitcoin was brilliant or stupid, but the existence of OP_EVAL does not make the expression language less secure.
-There is a 1-confirmation attack on old clients that interepret OP_EVAL as a no-op, but it is expensive and difficult in practice. The attack is:
+There is a 1-confirmation attack on old clients that interpret OP_EVAL as a no-op, but it is expensive and difficult in practice. The attack is:
# Attacker creates an OP_EVAL transaction that is valid as seen by old clients, but invalid for new clients.
# Attacker also creates a standard transaction that spends the OP_EVAL transaction, and pays the victim.
diff --git a/bip-0021.mediawiki b/bip-0021.mediawiki
index 0fba9bc..cdc37ba 100644
--- a/bip-0021.mediawiki
+++ b/bip-0021.mediawiki
@@ -37,7 +37,7 @@ Elements of the query component may contain characters outside the valid range.
=== ABNF grammar ===
-(See also [[#Simpler syntax|a simpler representation of syntax]])
+(See also [[#simpler-syntax|a simpler representation of syntax]])
bitcoinurn = "bitcoin:" bitcoinaddress [ "?" bitcoinparams ]
bitcoinaddress = *base58
diff --git a/bip-0032.mediawiki b/bip-0032.mediawiki
index b441658..0e6df24 100644
--- a/bip-0032.mediawiki
+++ b/bip-0032.mediawiki
@@ -25,7 +25,7 @@ This document describes hierarchical deterministic wallets (or "HD Wallets"): wa
The specification is intended to set a standard for deterministic wallets that can be interchanged between different clients. Although the wallets described here have many features, not all are required by supporting clients.
-The specification consists of two parts. In a first part, a system for deriving a tree of keypairs from a single seed is presented. The second part demonstrates how to build a wallet structure on top of such a tree.
+The specification consists of two parts. In the first part, a system for deriving a tree of keypairs from a single seed is presented. The second part demonstrates how to build a wallet structure on top of such a tree.
==Copyright==
@@ -37,7 +37,7 @@ The Bitcoin reference client uses randomly generated keys. In order to avoid the
Deterministic wallets do not require such frequent backups, and elliptic curve mathematics permit schemes where one can calculate the public keys without revealing the private keys. This permits for example a webshop business to let its webserver generate fresh addresses (public key hashes) for each order or for each customer, without giving the webserver access to the corresponding private keys (which are required for spending the received funds).
-However, deterministic wallets typically consist of a single "chain" of keypairs. The fact that there is only one chain means that sharing a wallet happens on an all-or-nothing basis. However, in some cases one only wants some (public) keys to be shared and recoverable. In the example of a webshop, the webserver does not need access to all public keys of the merchant's wallet; only to those addresses which are used to receive customer's payments, and not for example the change addresses that are generated when the merchant spends money. Hierarchical deterministic wallets allow such selective sharing by supporting multiple keypair chains, derived from a single root.
+However, deterministic wallets typically consist of a single "chain" of keypairs. The fact that there is only one chain means that sharing a wallet happens on an all-or-nothing basis. However, in some cases one only wants some (public) keys to be shared and recoverable. In the example of a webshop, the webserver does not need access to all public keys of the merchant's wallet; only to those addresses which are used to receive customers' payments, and not for example the change addresses that are generated when the merchant spends money. Hierarchical deterministic wallets allow such selective sharing by supporting multiple keypair chains, derived from a single root.
==Specification: Key derivation==
@@ -104,7 +104,7 @@ The function N((k, c)) &rarr; (K, c) computes the extended public key correspond
To compute the public child key of a parent private key:
* N(CKDpriv((k<sub>par</sub>, c<sub>par</sub>), i)) (works always).
* CKDpub(N(k<sub>par</sub>, c<sub>par</sub>), i) (works only for non-hardened child keys).
-The fact that they are equivalent is what makes non-hardened keys useful (one can derive child public keys of a given parent key without knowing any private key), and also what distinguishes them from hardened keys. The reason for not always using non-hardened keys (which are more useful) is security; see further for more information.
+The fact that they are equivalent is what makes non-hardened keys useful (one can derive child public keys of a given parent key without knowing any private key), and also what distinguishes them from hardened keys. The reason for not always using non-hardened keys (which are more useful) is security; see further below for more information.
====Public parent key &rarr; private child key====
@@ -184,7 +184,7 @@ When a business has several independent offices, they can all use wallets derive
====Recurrent business-to-business transactions: N(m/i<sub>H</sub>/0)====
In case two business partners often transfer money, one can use the extended public key for the external chain of a specific account (M/i h/0) as a sort of "super address", allowing frequent transactions that cannot (easily) be associated, but without needing to request a new address for each payment.
-Such a mechanism could also be used by mining pool operators as variable payout address.
+Such a mechanism could also be used by mining pool operators as a variable payout address.
====Unsecure money receiver: N(m/i<sub>H</sub>/0)====
@@ -212,7 +212,7 @@ Private and public keys must be kept safe as usual. Leaking a private key means
Somewhat more care must be taken regarding extended keys, as these correspond to an entire (sub)tree of keys.
One weakness that may not be immediately obvious, is that knowledge of a parent extended public key plus any non-hardened private key descending from it is equivalent to knowing the parent extended private key (and thus every private and public key descending from it). This means that extended public keys must be treated more carefully than regular public keys.
-It is also the reason for the existence of hardened keys, and why they are used for the account level in the tree. This way, a leak of account-specific (or below) private key never risks compromising the master or other accounts.
+It is also the reason for the existence of hardened keys, and why they are used for the account level in the tree. This way, a leak of account-specific (or below) private keys never risks compromising the master or other accounts.
==Test Vectors==
diff --git a/bip-0038.mediawiki b/bip-0038.mediawiki
index 511b55a..7f99b1a 100644
--- a/bip-0038.mediawiki
+++ b/bip-0038.mediawiki
@@ -170,7 +170,7 @@ To recalculate the address:
# Derive ''passfactor'' using scrypt with ''ownerentropy'' and the user's passphrase and use it to recompute ''passpoint''
# Derive decryption key for ''pointb'' using scrypt with ''passpoint'', ''addresshash'', and ''ownerentropy''
# Decrypt ''encryptedpointb'' to yield ''pointb''
-# ECMultiply ''pointb'' by ''passfactor''. Use the resulting EC point as a public key and hash it into ''address'' using either compressed or uncompressed public key methodology as specifid in ''flagbyte''.
+# ECMultiply ''pointb'' by ''passfactor''. Use the resulting EC point as a public key and hash it into ''address'' using either compressed or uncompressed public key methodology as specified in ''flagbyte''.
=====Decryption=====
# Collect encrypted private key and passphrase from user.
diff --git a/bip-0039.mediawiki b/bip-0039.mediawiki
index d8a4d25..1c4845e 100644
--- a/bip-0039.mediawiki
+++ b/bip-0039.mediawiki
@@ -39,7 +39,7 @@ security is improved but the sentence length increases. We refer to the
initial entropy length as ENT. The allowed size of ENT is 128-256 bits.
First, an initial entropy of ENT bits is generated. A checksum is generated by
-taking the first <pre>ENT / 32</pre> bits of its SHA256 hash. This checksum is
+taking the first <code>ENT / 32</code> bits of its SHA256 hash. This checksum is
appended to the end of the initial entropy. Next, these concatenated bits
are split into groups of 11 bits, each encoding a number from 0-2047, serving
as an index into a wordlist. Finally, we convert these numbers into words and
@@ -168,8 +168,12 @@ Haskell:
JavaScript:
* https://github.com/bitpay/bitcore/tree/master/packages/bitcore-mnemonic
* https://github.com/bitcoinjs/bip39 (used by [[https://github.com/blockchain/My-Wallet-V3/blob/v3.8.0/src/hd-wallet.js#L121-L146|blockchain.info]])
+* https://github.com/dashhive/DashPhrase.js
* https://github.com/hujiulong/web-bip39
+TypeScript:
+* https://github.com/bitauth/libauth
+
Java:
* https://github.com/bitcoinj/bitcoinj/blob/master/core/src/main/java/org/bitcoinj/crypto/MnemonicCode.java
@@ -190,6 +194,7 @@ Swift:
* https://github.com/matter-labs/web3swift/blob/develop/Sources/web3swift/KeystoreManager/BIP39.swift
* https://github.com/zcash-hackworks/MnemonicSwift
* https://github.com/ShenghaiWang/BIP39
+* https://github.com/anquii/BIP39
C++:
* https://github.com/libbitcoin/libbitcoin-system/blob/master/include/bitcoin/system/wallet/mnemonic.hpp
diff --git a/bip-0043.mediawiki b/bip-0043.mediawiki
index 32e02b1..f07c94a 100644
--- a/bip-0043.mediawiki
+++ b/bip-0043.mediawiki
@@ -7,7 +7,7 @@
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0043
Status: Final
- Type: Informational
+ Type: Standards Track
Created: 2014-04-24
</pre>
diff --git a/bip-0044.mediawiki b/bip-0044.mediawiki
index 4ddd56b..5db540c 100644
--- a/bip-0044.mediawiki
+++ b/bip-0044.mediawiki
@@ -6,7 +6,7 @@
Pavol Rusnak <stick@satoshilabs.com>
Comments-Summary: Mixed review (one person)
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0044
- Status: Proposed
+ Status: Final
Type: Standards Track
Created: 2014-04-24
</pre>
diff --git a/bip-0047.mediawiki b/bip-0047.mediawiki
index af801f9..dc1f588 100644
--- a/bip-0047.mediawiki
+++ b/bip-0047.mediawiki
@@ -1,7 +1,7 @@
RECENT CHANGES:
+* (15 Feb 2021) Finalize specification
+* (28 Sep 2017) Adjust text to match test vectors
* (19 Apr 2016) Define version 2 payment codes
-* (17 Apr 2016) Clarify usage of outpoints in notification transactions
-* (18 Dec 2015) Update explanations to resolve FAQs
<pre>
BIP: 47
@@ -10,11 +10,17 @@ RECENT CHANGES:
Author: Justus Ranvier <justus@openbitcoinprivacyproject.org>
Comments-Summary: Unanimously Discourage for implementation
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0047
- Status: Draft
+ Status: Final
Type: Informational
Created: 2015-04-24
</pre>
+==Status==
+
+This BIP can be be considered final in terms of enabling compatibility with wallets that implement version 1 and version 2 reusable payment codes, however future developments of the reusable payment codes specification will not be distributed via the BIP process.
+
+The Open Bitcoin Privacy Project RFC repo should be consulted for specifications related to version 3 or higher payment codes: https://github.com/OpenBitcoinPrivacyProject/rfc
+
==Abstract==
This BIP defines a technique for creating a payment code which can be publicly advertised and associated with a real-life identity without creating the loss of security or privacy inherent to P2PKH address reuse.
@@ -150,7 +156,7 @@ It is assumed that Alice can easily obtain Bob's payment code via a suitable met
Prior to the first time Alice initiates a transaction to Bob, Alice MUST inform Bob of her payment code via the following procedure:
-Note: this procedure is used if Bob uses a version 1 payment code (regardless of the the version of Alice's payment code). If Bob's payment code is not version 1, see the appropriate section in this specification.
+Note: this procedure is used if Bob uses a version 1 payment code (regardless of the version of Alice's payment code). If Bob's payment code is not version 1, see the appropriate section in this specification.
# Alice constructs a transaction which sends a small quantity of bitcoins to Bob's notification address (notification transaction)
## The inputs selected for this transaction MUST NOT be easily associated with Alice's notification address
@@ -158,7 +164,7 @@ Note: this procedure is used if Bob uses a version 1 payment code (regardless of
## Alice selects the private key corresponding to the designated pubkey: <pre>a</pre>
## Alice selects the public key associated with Bob's notification address: <pre>B, where B = bG</pre>
## Alice calculates a secret point: <pre>S = aB</pre>
-## Alice calculates a 64 byte blinding factor: <pre>s = HMAC-SHA512(x, o)</pre>
+## Alice calculates a 64 byte blinding factor: <pre>s = HMAC-SHA512(o, x)</pre>
### "x" is the x value of the secret point
### "o" is the outpoint being spent by the designated input
# Alice serializes her payment code in binary form.
@@ -229,7 +235,7 @@ The following actions are recommended to reduce this risk:
<img src="bip-0047/reusable_payment_codes-04.png" />
<img src="bip-0047/reusable_payment_codes-05.png" />
# Bob is watching for incoming payments on B' ever since he received the notification transaction from Alice.
-## Bob calculates n shared secrets with Alice, using the 0<sup>th</sup> public key derived Alice's payment code, and private keys 0 - n derived from Bob's payment code, where n is his desired lookahead window.
+## Bob calculates n shared secrets with Alice, using the 0<sup>th</sup> public key derived from Alice's payment code, and private keys 0 - n derived from Bob's payment code, where n is his desired lookahead window.
## Bob calculates the ephemeral deposit addresses using the same procedure as Alice: <pre>B' = B + sG</pre>
## Bob calculate the private key for each ephemeral address as: <pre>b' = b + s</pre>
<img src="bip-0047/reusable_payment_codes-02.png" />
diff --git a/bip-0049.mediawiki b/bip-0049.mediawiki
index 7d8d2c7..a13b437 100644
--- a/bip-0049.mediawiki
+++ b/bip-0049.mediawiki
@@ -6,7 +6,7 @@
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0049
Status: Final
- Type: Informational
+ Type: Standards Track
Created: 2016-05-19
License: PD
</pre>
diff --git a/bip-0060.mediawiki b/bip-0060.mediawiki
index 8e9f289..626a039 100644
--- a/bip-0060.mediawiki
+++ b/bip-0060.mediawiki
@@ -23,14 +23,14 @@ The implementation is problematic because the RelayTransactions flag is an optio
One property of Bitcoin messages is their fixed number of fields. This keeps the format simple and easily understood. Adding optional fields to messages will cause deserialisation issues when other fields come after the optional one.
-As an example, the length of version messages might be checked to ensure the byte stream is consistent. With optional fields, this checking is no longer possible. This is desirable to check for consistency inside internal deserialization code, and proper formatting of version messages originating from other nodes. In the future with diversification of the Bitcoin network, it will become desirable to enforce this kind of strict adherance to standard messages with field length compliance with every protocol version.
+As an example, the length of version messages might be checked to ensure the byte stream is consistent. With optional fields, this checking is no longer possible. This is desirable to check for consistency inside internal deserialization code, and proper formatting of version messages originating from other nodes. In the future with diversification of the Bitcoin network, it will become desirable to enforce this kind of strict adherence to standard messages with field length compliance with every protocol version.
Another property of fixed-length field messages is the ability to pass stream operators around for deserialization. This property is also lost, as now the deserialisation code must know the remaining length of bytes to parse. The parser now requires an additional piece of information (remaining size of the stream) for parsing instead of being a dumb reader.
==Specification==
=== version ===
-When a node creates an outgoing connection, it will immediately advertise its version. The remote node will respond with its version. No futher communication is possible until both peers have exchanged their version.
+When a node creates an outgoing connection, it will immediately advertise its version. The remote node will respond with its version. No further communication is possible until both peers have exchanged their version.
Payload:
diff --git a/bip-0084.mediawiki b/bip-0084.mediawiki
index dc5a05d..e1e458c 100644
--- a/bip-0084.mediawiki
+++ b/bip-0084.mediawiki
@@ -5,8 +5,8 @@
Author: Pavol Rusnak <stick@satoshilabs.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0084
- Status: Draft
- Type: Informational
+ Status: Final
+ Type: Standards Track
Created: 2017-12-28
License: CC0-1.0
</pre>
diff --git a/bip-0085.mediawiki b/bip-0085.mediawiki
index 6e7dd0e..d5557fb 100644
--- a/bip-0085.mediawiki
+++ b/bip-0085.mediawiki
@@ -106,7 +106,7 @@ OUTPUT
* AirGap Vault: [https://github.com/airgap-it/airgap-vault/commit/d64332fc2f332be622a1229acb27f621e23774d6]
-btc_hd_wallet: [https://github.com/scgbckbone/btc-hd-wallet]
+* btc_hd_wallet: [https://github.com/scgbckbone/btc-hd-wallet]
==Applications==
@@ -244,7 +244,7 @@ INPUT:
OUTPUT
* DERIVED ENTROPY=ead0b33988a616cf6a497f1c169d9e92562604e38305ccd3fc96f2252c177682
-* DERIVED WIF=xprv9s21ZrQH143K2srSbCSg4m4kLvPMzcWydgmKEnMmoZUurYuBuYG46c6P71UGXMzmriLzCCBvKQWBUv3vPB3m1SATMhp3uEjXHJ42jFg7myX
+* DERIVED XPRV=xprv9s21ZrQH143K2srSbCSg4m4kLvPMzcWydgmKEnMmoZUurYuBuYG46c6P71UGXMzmriLzCCBvKQWBUv3vPB3m1SATMhp3uEjXHJ42jFg7myX
===HEX===
Application number: 128169'
@@ -262,6 +262,82 @@ INPUT:
OUTPUT
* DERIVED ENTROPY=492db4698cf3b73a5a24998aa3e9d7fa96275d85724a91e71aa2d645442f878555d078fd1f1f67e368976f04137b1f7a0d19232136ca50c44614af72b5582a5c
+===PWD BASE64===
+Application number: 707764'
+
+The derivation path format is: <code>m/83696968'/707764'/{pwd_len}'/{index}'</code>
+
+`20 <= pwd_len <= 86`
+
+[https://datatracker.ietf.org/doc/html/rfc4648 Base64] encode the all 64 bytes of entropy.
+Remove any spaces or new lines inserted by Base64 encoding process. Slice base64 result string
+on index 0 to `pwd_len`. This slice is the password. As `pwd_len` is limited to 86, passwords will not contain padding.
+
+Entropy calculation:<br>
+R = 64 (base64 - do not count padding)<br>
+L = pwd_len<br>
+Entropy = log2(R ** L)<br>
+
+{| class="wikitable" style="margin:auto"
+! pwd_length !! (cca) entropy
+|-
+| 20 || 120.0
+|-
+| 24 || 144.0
+|-
+| 32 || 192.0
+|-
+| 64 || 384.0
+|-
+| 86 || 516.0
+|}
+
+INPUT:
+* MASTER BIP32 ROOT KEY: xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb
+* PATH: m/83696968'/707764'/21'/0'
+
+OUTPUT
+* DERIVED ENTROPY=d7ad61d4a76575c5bad773feeb40299490b224e8e5df6c8ad8fe3d0a6eed7b85ead9fef7bcca8160f0ee48dc6e92b311fc71f2146623cc6952c03ce82c7b63fe
+* DERIVED PWD=dKLoepugzdVJvdL56ogNV
+
+===PWD BASE85===
+Application number: 707785'
+
+The derivation path format is: <code>m/83696968'/707785'/{pwd_len}'/{index}'</code>
+
+`10 <= pwd_len <= 80`
+
+Base85 encode the all 64 bytes of entropy.
+Remove any spaces or new lines inserted by Base64 encoding process. Slice base85 result string
+on index 0 to `pwd_len`. This slice is the password. `pwd_len` is limited to 80 characters.
+
+Entropy calculation:<br>
+R = 85<br>
+L = pwd_len<br>
+Entropy = log2(R ** L)<br>
+
+{| class="wikitable" style="margin:auto"
+! pwd_length !! (cca) entropy
+|-
+| 10 || 64.0
+|-
+| 15 || 96.0
+|-
+| 20 || 128.0
+|-
+| 30 || 192.0
+|-
+| 20 || 512.0
+|}
+
+INPUT:
+* MASTER BIP32 ROOT KEY: xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb
+* PATH: m/83696968'/707785'/12'/0'
+
+OUTPUT
+* DERIVED ENTROPY=f7cfe56f63dca2490f65fcbf9ee63dcd85d18f751b6b5e1c1b8733af6459c904a75e82b4a22efff9b9e69de2144b293aa8714319a054b6cb55826a8e51425209
+* DERIVED PWD=_s`{TW89)i4`
+
===RSA===
Application number: 828365'
@@ -288,7 +364,7 @@ The resulting RSA key can be used to create a GPG key where the creation date MU
Note on GPG key capabilities on smartcard/hardware devices:
-GPG capable smart-cards SHOULD be be loaded as follows: The encryption slot SHOULD be loaded with the ENCRYPTION capable key; the authentication slot SHOULD be loaded with the AUTHENTICATION capable key. The signature capable slot SHOULD be loaded with the SIGNATURE capable key.
+GPG capable smart-cards SHOULD be loaded as follows: The encryption slot SHOULD be loaded with the ENCRYPTION capable key; the authentication slot SHOULD be loaded with the AUTHENTICATION capable key. The signature capable slot SHOULD be loaded with the SIGNATURE capable key.
However, depending on available slots on the smart-card, and preferred policy, the CERTIFY capable key MAY be flagged with CERTIFY and SIGNATURE capabilities and loaded into the SIGNATURE capable slot (for example where the smart-card has only three slots and the CERTIFY capability is required on the same card). In this case, the SIGNATURE capable sub-key would be disregarded because the CERTIFY capable key serves a dual purpose.
diff --git a/bip-0086.mediawiki b/bip-0086.mediawiki
index f724884..529f094 100644
--- a/bip-0086.mediawiki
+++ b/bip-0086.mediawiki
@@ -2,7 +2,7 @@
BIP: 86
Layer: Applications
Title: Key Derivation for Single Key P2TR Outputs
- Author: Andrew Chow <andrew@achow101.com>
+ Author: Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0086
Status: Draft
diff --git a/bip-0087.mediawiki b/bip-0087.mediawiki
index d270027..308e852 100644
--- a/bip-0087.mediawiki
+++ b/bip-0087.mediawiki
@@ -40,7 +40,7 @@ A modern standardization is needed for multisig derivation paths. There are som
m / purpose' / cosigner_index / change / address_index
</pre>
-BIP45 unecessarily demands a single script type (here, P2SH). In addition, BIP45 sets <code>cosigner_index</code> in order to sort the <code>purpose'</code> public keys of each cosigner. This too is redundant, as descriptors can set the order of the public keys with <code>multi</code> or have them sorted lexicographically (as described in [https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki BIP67]) with <code>sortedmulti</code>. Sorting public keys between cosigners in order to create the full derivation path, prior to sending the key record to the coordinator to create the descriptor, merely adds additional unnecessary communication rounds.
+BIP45 unnecessarily demands a single script type (here, P2SH). In addition, BIP45 sets <code>cosigner_index</code> in order to sort the <code>purpose'</code> public keys of each cosigner. This too is redundant, as descriptors can set the order of the public keys with <code>multi</code> or have them sorted lexicographically (as described in [https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki BIP67]) with <code>sortedmulti</code>. Sorting public keys between cosigners in order to create the full derivation path, prior to sending the key record to the coordinator to create the descriptor, merely adds additional unnecessary communication rounds.
The second multisignature "standard" in use is m/48', which specifies:
diff --git a/bip-0088.mediawiki b/bip-0088.mediawiki
index 936f2ca..49be7db 100644
--- a/bip-0088.mediawiki
+++ b/bip-0088.mediawiki
@@ -89,7 +89,7 @@ installation of malicious or incorrect profiles, though.
==Specification==
-The format for the template was choosen to make it easy to read, convenient and visually unambigous.
+The format for the template was chosen to make it easy to read, convenient and visually unambigous.
Template starts with optional prefix <code>m/</code>, and then one or more sections delimited by the slash character (<code>/</code>).
@@ -127,13 +127,13 @@ Constraints:
# To avoid ambiguity, an index range that matches a single value MUST be specified as Unit range.
# To avoid ambiguity, an index range <code>0-2147483647</code> is not allowed, and MUST be specified as Wildcard index template instead
# For Non-unit range, range_end MUST be larger than range_start.
-# If there is more than one index range within the Ranged index template, range_start of the second and any subsequent range MUST be larger than the range_end of the preceeding range.
+# If there is more than one index range within the Ranged index template, range_start of the second and any subsequent range MUST be larger than the range_end of the preceding range.
# To avoid ambiguity, all representations of integer values larger than 0 MUST NOT start with character <code>0</code> (no leading zeroes allowed).
# If hardened marker appears within any section in the path template, all preceding sections MUST also specify hardened matching.
# To avoid ambiguity, if a hardened marker appears within any section in the path template, all preceding sections MUST also use the same hardened marker (either <code>h</code> or <code>'</code>).
# To avoid ambiguity, trailing slashes (for example, <code>1/2/</code>) and duplicate slashes (for example, <code>0//1</code>) MUST NOT appear in the template.
-It may be desireable to have fully unambiguous encoding, where for each valid path template string, there is no other valid template string that matches the exact same set of paths. This would enable someone to compare templates for equality through a simple string equality check, without any parsing.
+It may be desirable to have fully unambiguous encoding, where for each valid path template string, there is no other valid template string that matches the exact same set of paths. This would enable someone to compare templates for equality through a simple string equality check, without any parsing.
To achieve this, two extra rules are needed:
diff --git a/bip-0119.mediawiki b/bip-0119.mediawiki
index aa226d0..d661f4c 100644
--- a/bip-0119.mediawiki
+++ b/bip-0119.mediawiki
@@ -3,6 +3,7 @@
Layer: Consensus (soft fork)
Title: CHECKTEMPLATEVERIFY
Author: Jeremy Rubin <j@rubin.io>
+ James O'Beirne <vaults@au92.org>
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0119
Status: Draft
Type: Standards Track
@@ -61,7 +62,7 @@ references.
==Detailed Specification==
The below code is the main logic for verifying CHECKTEMPLATEVERIFY, described
-in pythonic pseduocode. The canonical specification for the semantics of
+in pythonic pseudocode. The canonical specification for the semantics of
OP_CHECKTEMPLATEVERIFY as implemented in C++ in the context of Bitcoin Core can
be seen in the reference implementation.
@@ -88,7 +89,7 @@ def execute_bip_119(self):
self.context.precomputed_ctv_data = self.context.tx.get_default_check_template_precomputed_data()
# If the hashes do not match, return error
- if stack[-1] != self.context.tx.get_default_check_template_hash(self.context.nIn, self.context.precomputed_ctv_data)
+ if stack[-1] != self.context.tx.get_default_check_template_hash(self.context.nIn, self.context.precomputed_ctv_data):
return self.errors_with(errors.script_err_template_mismatch)
return self.return_as_nop()
@@ -225,12 +226,12 @@ A recent commit hash in that PR including tests and vectors can be found here ht
Once the PR is merged, this BIP should be updated to point to the specific code released.
Test vectors are available in [/bip-0119/vectors the bip-0119/vectors
-directory] for checking compatibility with the refrence implementation and BIP.
+directory] for checking compatibility with the reference implementation and BIP.
==Rationale==
The goal of CHECKTEMPLATEVERIFY is to be minimal impact on the existing codebase -- in the
-future, as we become aware of more complex but shown to be safe use cases new template types can be added.
+future, as we become aware of more complex but shown to be safe use cases, new template types can be added.
Below we'll discuss the rules one-by-one:
@@ -250,7 +251,7 @@ Were these values not committed, it would be possible to delay the spending of
an output arbitrarily as well as possible to change the TXID.
Committing these values, rather than restricting them to specific values, is
-more flexible as it permits users of CHECKTEMPLATEVERIFY the set the version and
+more flexible as it permits users of CHECKTEMPLATEVERIFY to set the version and
locktime as they please.
=====Committing to the ScriptSigs Hash=====
@@ -258,7 +259,7 @@ locktime as they please.
The scriptsig in a segwit transaction must be exactly empty, unless it is a P2SH
segwit transaction in which case it must be only the exact redeemscript. P2SH is incompatible
(unless the P2SH hash is broken) with CHECKTEMPLATEVERIFY because the template hash must commit
-to the ScriptSig, which must contain the redeemscript, which is a hash cycle.
+to the ScriptSig, which must contain the redeemscript, which is a hash cycle.
To prevent malleability when not using a segwit input, we also commit to the
scriptsig. This makes it possible to use a 2 input CHECKTEMPLATEVERIFY with a legacy pre-signed
diff --git a/bip-0126.mediawiki b/bip-0126.mediawiki
index f498b1c..4cfa292 100644
--- a/bip-0126.mediawiki
+++ b/bip-0126.mediawiki
@@ -50,7 +50,7 @@ Applications which wish to comply both with this procedure and BIP69 should appl
A HIT is Standard form if it adheres to all of the following rules:
-# The number of unique output scripts must be equal to the number of unique inputs scripts (irrespective of the number of inputs and outputs).
+# The number of unique output scripts must be equal to the number of unique input scripts (irrespective of the number of inputs and outputs).
# All output scripts must be unique.
# At least one pair of outputs must be of equal value.
# The largest output in the transaction is a member of a set containing at least two identically-sized outputs.
@@ -88,7 +88,7 @@ Clients which create intentional HITs must have the capability to form alternate
An HIT formed via the preceding procedure will adhere to the following conditions:
-# The number of unique inputs scripts must exceed the number of output scripts.
+# The number of unique input scripts must exceed the number of output scripts.
# All output scripts must be unique.
# At least one pair of outputs must be of equal value.
## "Standard outputs" refers to the set of outputs with equal value
diff --git a/bip-0127.mediawiki b/bip-0127.mediawiki
index 15c7755..44a90d7 100644
--- a/bip-0127.mediawiki
+++ b/bip-0127.mediawiki
@@ -219,6 +219,7 @@ A work-in-progress implementation of a tool that produces and verifies proofs
in the described format can be found here:
https://github.com/stevenroose/reserves
+An implementation of the custom proof PSBTs is part of the [https://bitcoindevkit.org/ BDK], and can be found here: https://crates.io/crates/bdk-reserves
== Footnotes ==
diff --git a/bip-0129.mediawiki b/bip-0129.mediawiki
index 8719fe4..608c724 100644
--- a/bip-0129.mediawiki
+++ b/bip-0129.mediawiki
@@ -452,7 +452,7 @@ sh(wsh(multi(2,[793cc70b/48'/0'/0'/1']xpub6ErVmcYYHmavsMgxEcTZyzN5sqth1ZyRpFNJC2
==Acknowledgement==
-Special thanks to Pavol Rusnak, Dmitry Petukhov, Christopher Allen, Craig Raw, Robert Spigler, Gregory Sanders, Ta Tat Tai, Michael Flaxman, Pieter Wuille, Salvatore Ingala, Andrew Chow and others for their feedback on the specification.
+Special thanks to Pavol Rusnak, Dmitry Petukhov, Christopher Allen, Craig Raw, Robert Spigler, Gregory Sanders, Ta Tat Tai, Michael Flaxman, Pieter Wuille, Salvatore Ingala, Ava Chow and others for their feedback on the specification.
==References==
diff --git a/bip-0137.mediawiki b/bip-0137.mediawiki
index 19dd536..43addba 100644
--- a/bip-0137.mediawiki
+++ b/bip-0137.mediawiki
@@ -25,7 +25,7 @@ This BIP is licensed under the 2-clause BSD license.
==Motivation==
-Since Bitcoin private keys can not only be used to sign Bitcoin transactions, but also any other message, it has become customary to use them to sign various messages for differing purposes. Some applications of signing messages with a Bitcoin private key are as follows: proof of funds for collateral, credit worthiness, enterence to events, airdrops, audits as well as other applications. While there was no BIP written for how to digitally sign messages with Bitcoin private keys with P2PKH addresses it is a fairly well understood process, however with the introduction of Segwit (both in the form of P2SH and bech32) addresses, it is unclear how to distinguish a P2PKH, P2SH, or bech32 address from one another. This BIP proposes a standard signature format that will allow clients to distinguish between the different address formats.
+Since Bitcoin private keys can not only be used to sign Bitcoin transactions, but also any other message, it has become customary to use them to sign various messages for differing purposes. Some applications of signing messages with a Bitcoin private key are as follows: proof of funds for collateral, credit worthiness, entrance to events, airdrops, audits as well as other applications. While there was no BIP written for how to digitally sign messages with Bitcoin private keys with P2PKH addresses it is a fairly well understood process, however with the introduction of Segwit (both in the form of P2SH and bech32) addresses, it is unclear how to distinguish a P2PKH, P2SH, or bech32 address from one another. This BIP proposes a standard signature format that will allow clients to distinguish between the different address formats.
==Specification==
diff --git a/bip-0141.mediawiki b/bip-0141.mediawiki
index efdd9c9..eb2fc74 100644
--- a/bip-0141.mediawiki
+++ b/bip-0141.mediawiki
@@ -83,7 +83,7 @@ If all transactions in a block do not have witness data, the commitment is optio
=== Witness program ===
-A <code>scriptPubKey</code> (or <code>redeemScript</code> as defined in BIP16/P2SH) that consists of a 1-byte push opcode (for 0 to 16) followed by a data push between 2 and 40 bytes gets a new special meaning. The value of the first push is called the "version byte". The following byte vector pushed is called the "witness program".
+A <code>scriptPubKey</code> (or <code>redeemScript</code> as defined in BIP16/P2SH) that consists of a select subset of opcodes (<code>OP_0,OP_1,OP_2,...,OP_16</code>) followed by a data push between 2 and 40 bytes gets a new special meaning. The value of the first push is called the "version byte". The following byte vector pushed is called the "witness program".
There are two cases in which witness validation logic are triggered. Each case determines the location of the witness version byte and program, as well as the form of the scriptSig:
# Triggered by a <code>scriptPubKey</code> that is exactly a push of a version byte, plus a push of a witness program. The scriptSig must be exactly empty or validation fails. (''"native witness program"'')
@@ -276,7 +276,7 @@ These commitments could be included in the extensible commitment structure throu
Since a version byte is pushed before a witness program, and programs with unknown versions are always considered as anyone-can-spend script, it is possible to introduce any new script system with a soft fork. The witness as a structure is not restricted by any existing script semantics and constraints, the 520-byte push limit in particular, and therefore allows arbitrarily large scripts and signatures.
-Examples of new script system include Schnorr signatures which reduce the size of multisig transactions dramatically, Lamport signature which is quantum computing resistance, and Merklized abstract syntax trees which allow very compact witness for conditional scripts with extreme complexity.
+Examples of new script systems include Schnorr signatures, which reduce the size of multisig transactions dramatically; Lamport signatures, which are quantum computing resistant; and Merklized abstract syntax trees, which allow very compact witnesses for conditional scripts with extreme complexity.
=== Per-input lock-time and relative-lock-time ===
@@ -303,7 +303,7 @@ As a soft fork, older software will continue to operate without modification. N
This BIP will be deployed by "version bits" BIP9 with the name "segwit" and using bit 1.
-For Bitcoin mainnet, the BIP9 starttime will be midnight 15 november 2016 UTC (Epoch timestamp 1479168000) and BIP9 timeout will be midnight 15 november 2017 UTC (Epoch timestamp 1510704000).
+For Bitcoin mainnet, the BIP9 starttime will be midnight 15 November 2016 UTC (Epoch timestamp 1479168000) and BIP9 timeout will be midnight 15 November 2017 UTC (Epoch timestamp 1510704000).
For Bitcoin testnet, the BIP9 starttime will be midnight 1 May 2016 UTC (Epoch timestamp 1462060800) and BIP9 timeout will be midnight 1 May 2017 UTC (Epoch timestamp 1493596800).
diff --git a/bip-0143.mediawiki b/bip-0143.mediawiki
index 81763a0..ebf0d7c 100644
--- a/bip-0143.mediawiki
+++ b/bip-0143.mediawiki
@@ -187,7 +187,7 @@ To ensure consistency in consensus-critical behaviour, developers should test th
nHashType: 01000000
sigHash: c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670
- signature: 304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee
+ signature: 304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01
The serialized signed transaction is: 01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000
@@ -551,7 +551,7 @@ These examples show that <code>FindAndDelete</code> for the signature is not app
nLockTime: 00000000
The input comes from a P2WSH witness program:
- scriptPubKey : 00209e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19, value: 200000
+ scriptPubKey : 00209e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19, value: 0.00200000
redeemScript : OP_CHECKSIGVERIFY <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01>
ad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01
diff --git a/bip-0152.mediawiki b/bip-0152.mediawiki
index 8200714..fad1746 100644
--- a/bip-0152.mediawiki
+++ b/bip-0152.mediawiki
@@ -211,7 +211,7 @@ There are several design goals for the Short ID calculation:
SipHash is a secure, fast, and simple 64-bit MAC designed for network traffic authentication and collision-resistant hash tables. We truncate the output from SipHash-2-4 to 48 bits (see next section) in order to minimize space. The resulting 48-bit hash is certainly not large enough to avoid intentionally created individual collisons, but by using the block hash as a key to SipHash, an attacker cannot predict what keys will be used once their transactions are actually included in a relayed block. We mix in a per-connection 64-bit nonce to obtain independent short IDs on every connection, so that even block creators cannot control where collisions occur, and random collisions only ever affect a small number of connections at any given time. The mixing is done using SHA256(block_header || nonce), which is slow compared to SipHash, but only done once per block. It also adds the ability for nodes to choose the nonce in a better than random way to minimize collisions, though that is not necessary for correct behaviour. Conversely, nodes can also abuse this ability to increase their ability to introduce collisions in the blocks they relay themselves. However, they can already cause more problems by simply refusing to relay blocks. That is inevitable, and this design only seeks to prevent network-wide misbehavior.
-====Random collision probabilty====
+====Random collision probability====
Thanks to the block-header-based SipHash keys, we can assume that the only collisions on links between honest nodes are random ones.
diff --git a/bip-0158.mediawiki b/bip-0158.mediawiki
index 8887d32..054aa1f 100644
--- a/bip-0158.mediawiki
+++ b/bip-0158.mediawiki
@@ -312,6 +312,8 @@ complete serialization of a filter is:
* <code>N</code>, encoded as a <code>CompactSize</code>
* The bytes of the compressed filter itself
+A zero element filter MUST be written as one byte containing zeroes.
+
==== Signaling ====
This BIP allocates a new service bit:
diff --git a/bip-0174.mediawiki b/bip-0174.mediawiki
index f4c0807..5e70a11 100644
--- a/bip-0174.mediawiki
+++ b/bip-0174.mediawiki
@@ -2,7 +2,7 @@
BIP: 174
Layer: Applications
Title: Partially Signed Bitcoin Transaction Format
- Author: Andrew Chow <achow101@gmail.com>
+ Author: Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0174
Status: Final
diff --git a/bip-0176.mediawiki b/bip-0176.mediawiki
index 60311c4..2f5ee9f 100644
--- a/bip-0176.mediawiki
+++ b/bip-0176.mediawiki
@@ -16,7 +16,7 @@ Bits is presented here as the standard term for 100 (one hundred) satoshis or 1/
== Motivation ==
The bitcoin price has grown over the years and once the price is past $10,000 USD or so, bitcoin amounts under $10 USD start having enough decimal places that it's difficult to tell whether the user is off by a factor of 10 or not. Switching the denomination to "bits" makes comprehension easier. For example, when BTC is $15,000 USD, $10.05 is a somewhat confusing 0.00067 BTC, versus 670 bits, which is a lot clearer.
-Additonally, reverse comparisons are easier as 59 bits being $1 is easier to comprehend for most people than 0.000059 BTC being $1. Similar comparisons can be made to other currencies: 1 yen being 0.8 bits, 1 won being 0.07 bits and so on.
+Additionally, reverse comparisons are easier as 59 bits being $1 is easier to comprehend for most people than 0.000059 BTC being $1. Similar comparisons can be made to other currencies: 1 yen being 0.8 bits, 1 won being 0.07 bits and so on.
Potential benefits of utilizing "bits" include:
diff --git a/bip-0300.mediawiki b/bip-0300.mediawiki
index 20d1936..e5048e7 100644
--- a/bip-0300.mediawiki
+++ b/bip-0300.mediawiki
@@ -75,54 +75,44 @@ D1 is a list of active sidechains. D1 is updated via M1 and M2.
| Version number.
|-
| 3
-| String KeyID
-| string
-| Used to derive all sidechain deposit addresses.
-|-
-| 4<br />
-| Sidechain Private Key
-| string
-| The private key of the sidechain deposit script.
-|- style="vertical-align:middle;"
-| 5<br />
-| ScriptPubKey
-| CScript
-| Where the sidechain coins go. This always stays the same, even though the CTIP (UTXO) containing the coins is always changing.
-|- style="vertical-align:middle;"
-| 6
| Sidechain Name
| string
| A human-readable name of the sidechain.
|- style="vertical-align:middle;"
-| 7
+| 4
| Sidechain Description
| string
| A human-readable name description of the sidechain.
|- style="vertical-align:middle;"
-| 8
+| 5
| Hash1 - tarball hash
| uint256
| Intended as the sha256 hash of the tar.gz of the canonical sidechain software. (This is not enforced anywhere by Bip300, and is for human purposes only.)
|- style="vertical-align:middle;"
-| 9
+| 6
| Hash2 - git commit hash
| uint160
| Intended as the git commit hash of the canonical sidechain node software. (This is not enforced anywhere by Bip300, and is for human purposes only.)
|-
-| 10
+| 7
| Active
| bool
| Does this sidechain slot contain an active sidechain?<br />
|- style="vertical-align:middle;"
-| 11
-| "CTIP" -- Part 1 "TxID"
+| 8
+| Activation Status
+| int , int
+| The age of the proposal (in blocks); and the number of "fails" (a block that does NOT ack the sidechain). This is discarded after the sidechain activates.
+|- style="vertical-align:middle;"
+| 9
+| "CTIP" -- "TxID"
| uint256
-| The CTIP, or "Critical (TxID, Index) Pair" is a variable for keeping track of where the sidechain's money is (ie, which member of the UTXO set).
+| A UTXO that holds the sidechain's money. (Part 1 of 2).
|- style="vertical-align:middle;"
-| 12
-| "CTIP" -- Part 2 "Index"
+| 10
+| "CTIP" -- "vout"
| int32_t
-| Of the CTIP, the second element of the pair: the Index. See #11 above.
+| A UTXO that holds the sidechain's money. (Part 2 of 2).
|}
@@ -138,7 +128,7 @@ D2 is driven by M3, M4, M5, and M6. Those messages enforce the following princip
# From one block to the next, the value in "ACKs" may either increase or decrease, by a maximum of 1 (see M4).
# If a Bundle's "ACKs" reach 13150 or greater, it "succeeds" and its corresponding M6 message can be included in a block.
# If the M6 of a Bundle is paid out, it is also removed.
-# If a Bundle cannot possibly succeed ( 13500 - "ACKs" > "Blocks Remaining" ), it is removed immediately.
+# If a Bundle cannot possibly succeed ( 13150 - "ACKs" > "Blocks Remaining" ), it is removed immediately.
{| class="wikitable"
@@ -158,19 +148,21 @@ D2 is driven by M3, M4, M5, and M6. Those messages enforce the following princip
| A withdrawal attempt. Specifically, it is a "blinded transaction id" (ie, the double-Sha256 of a txn that has had two fields zeroed out, see M6) of a txn which could withdraw funds from a sidechain.
|-
| 3
-| ACKs (Work Score)
+| Work Score (ACKs)
| uint16_t
-| The current ACK-counter, which is the total number of ACKs (the PoW that has been used to validate the Bundle).
+| How many miner upvotes a withdrawal has. Starts at 0. Fastest possible rate of increase is 1 per block.
|-
| 4
-| Blocks Remaining (Age)
+| Blocks Remaining
| uint16_t
-| The number of blocks which this Bundle has remaining to accumulate ACKs
+| How long this bundle has left to live (measured in blocks). Starts at 26,300 and counts down.
|}
+D1, with all 256 slots active, reaches a maximum size of: 256 * ( 1 (map index) + 36 (outpoint) + 8 (amount) ) = 11,520 bytes.
+D2, under normal conditions, would reach a size of: (38 bytes per withdrawal * 256 sidechains) = 9,728 bytes.
-
+It is possible to spam D2. A miner can add the max M3s (256) every block, forever. This costs 9,728 on-chain bytes per block, an opportunity cost of about 43 txns. It results in no benefit to the miner whatsoever. D2 will eventually hit a ceiling at 124.5568 MB. (By comparison, the Bitcoin UTXO set is about 7,000 MB.) When the attacker stops, D2 will eventually shrink back down to 9,728 bytes.
=== The Six New Bip300 Messages ===
@@ -188,19 +180,22 @@ M1 is a coinbase OP Return output containing the following:
N-byte - The serialization of the sidechain.
1-byte nSidechain
4-byte nVersion
- x-byte strKeyID
- x-byte strPrivKey
- x-byte scriptPubKey
x-byte title
x-byte description
32-byte hashID1
20-byte hashID2
-===== Examples =====
-<img src="bip-0300/m1-gui.jpg?raw=true" align="middle"></img>
+M1 is invalid if:
+
+* It would add a duplicate entry to D1.
+* There is already an M1 in this block.
+* The sidechain serialization does not parse.
+
+Otherwise:
+
+* A new entry is added to D1, whose initial Activation Status is (age=0, fails=0).
-<img src="bip-0300/m1-cli.png?raw=true" align="middle"></img>
==== M2 -- ACK Sidechain Proposal ====
@@ -208,14 +203,30 @@ M2 is a coinbase OP Return output containing the following:
1-byte - OP_RETURN (0x6a)
4-byte - Message header (0xD6E1C5BF)
- 32-byte - sha256D hash of sidechain's serialization
+ 32-byte - the sha256D hash of sidechain's serialization
+
-===== Notes =====
+M2 is ignored if it doesn't parse, or if it is for a sidechain that doesn't exist.
-The new M1/M2 validation rules are:
+M2 is invalid if:
-# Any miner can propose a new sidechain (M1) at any time. This procedure resembles BIP 9 soft fork activation: the network must see a properly-formatted M1, followed by "acknowledgment" of the sidechain (M2) in 90% of the following 2016 blocks.
-# Bip300 comes with only 256 sidechain-slots. If all are used, it is possible to "overwrite" a sidechain. This requires vastly more M2 ACKs -- 50% of the following 26300 blocks must contain an M2. The possibility of overwrite, does not change the Bip300 security assumptions (because we already assume that the sidechain is vulnerable to miners, at a rate of 1 catastrophe per 13150 blocks).
+* An M2 is already in this block.
+* It tries to ACK two different M1s for the same slot.
+
+Otherwise:
+
+* The sidechain is "ACK"ed and does NOT get a "fail" for this block. (As it otherwise would.)
+
+A sidechain fails to activate if:
+
+* If the slot is unused: during the next 2016 blocks, it accumulates 201 fails. (Ie, 90% threshold).
+* If the slot is in use: during the next 26,300 blocks, it accumulates 13,150 fails. (Ie, 50% threshold).
+
+( Thus we can overwrite a used sidechain slot. Bip300 sidechains are already vulnerable to one catastrophe per 13150 blocks (the invalid withdrawal) so this slot-overwrite option does not change the security assumptions. )
+
+Otherwise, the sidechain activates (Active is set to TRUE).
+
+In the block in which the sidechain activates, the coinbase MUST include at least one 0-valued OP_DRIVECHAIN output. This output becomes the initial CTIP for the sidechain.
@@ -227,7 +238,7 @@ For an M6 to be valid, it must be first "prepped" by one M3 and then 13,150+ M4s
===== What are Bundles? =====
-Sidechain withdrawals take the form of “Bundles” -- named because they "bundle up" many individual withdrawal-requests into a single rare layer1 transaction.
+Sidechain withdrawals take the form of "Bundles" -- named because they "bundle up" many individual withdrawal-requests into a single rare layer1 transaction.
Sidechain full nodes aggregate the withdrawal-requests into a big set. The sidechain calculates what M6 would have to look like, to pay all of these withdrawal-requests out. Finally, the sidechain calculates what the hash of this M6 would be. This 32-byte hash identifies the Bundle.
@@ -248,11 +259,18 @@ M3 is a coinbase OP Return output containing the following:
1-byte - OP_RETURN (0x6a)
4-byte - Commitment header (0xD45AA943)
32-byte - The Bundle hash, to populate a new D2 entry
+ 1-byte - nSidechain (the slot number)
+
+M3 is ignored if it does not parse, or if it is for a sidechain that doesn't exist.
+
+M3 is invalid if:
-The new validation rules pertaining to M3 are:
+* This block already has an M3 for that nSidechain.
+* A bundle with this hash is already in D2.
+* A bundle with this hash already paid out.
+* A bundle with this hash was rejected in the past.
-# If the network detects a properly-formatted M3, it must add an entry to D2 in the very next block. The starting "Blocks Remaining" value is 26,299. The starting ACKs count is 1.
-# Each block can only contain one M3 per sidechain.
+Otherwise: M3 adds an entry to D2, with initial ACK score = 1 and initial Blocks Remaining = 26,299. (Merely being added to D2, does count as your first upvote.)
Once a Bundle is in D2, how can we give it enough ACKs to make it valid?
@@ -263,44 +281,199 @@ M4 is a coinbase OP Return output containing the following:
1-byte - OP_RETURN (0x6a)
4-byte - Commitment header (0xD77D1776)
1-byte - Version
- n-byte - The vector describing the "upvoted" bundle-choice, for each sidechain.
+ n-byte - The "upvote vector" -- describes which bundle-choice is "upvoted", for each sidechain.
-Version 0x01 uses one byte per sidechain, and applies in most cases. Version 0x02 uses two bytes per sidechain and applies in unusual situations where at least one sidechain has more than 256 distinct withdrawal-bundles in progress at one time. Other interesting versions are possible: 0x03 might say "do exactly what was done in the previous block" (which could consume a fixed 6 bytes total, regardless of how many sidechains). 0x04 might say "upvote everyone who is clearly in the lead" (which also would require a mere 6 bytes), and so forth.
+The upvote vector will code "abstain" as 0xFF (or 0xFFFF); it will code "alarm" as 0xFE (or 0xFFFE). Otherwise it simply indicates which withdrawal-bundle in the list, is the one to be "upvoted".
+
+For example: if there are two sidechains, and we wish to upvote the 7th bundle on sidechain #1 plus the 4th bundle on sidechain #2, then the upvote vector would be { 07, 04 }. And M4 would be [0x6A,D77D1776,00,0006,0003].
+
+The version number allows us to shrink the upvote vector in many cases.
+Version 0x00 omits the upvote vector entirely (ie, 6 bytes for the whole M4) and sets this block's M4 equal to the previous block's M4.
+Version 0x01 uses one byte per sidechain, and can be used while all ACKed withdrawals have an index under 256 (ie, 99.99%+ of the time).
+Version 0x02 uses a full two bytes per sidechain (each encoded in little endian), but it always works no matter how many withdrawal proposals exist.
+Version 0x03 omits the upvote vector, and instead upvotes only those withdrawals that are leading their rivals by at least 50 votes.
If a sidechain has no pending bundles, then it is skipped over when M4 is created and parsed.
-The upvote vector will code "abstain" as 0xFF (or 0xFFFF); it will code "alarm" as 0xFE (or 0xFFFE). Otherwise it simply indicates which withdrawal-bundle in the list, is the one to be "upvoted". For example, if there are two sidechains, and we wish to upvote the 7th bundle on sidechain #1 plus the 4th bundle on sidechain #2, then the vector would be 0x0704.
+For example, an upvote vector of { 2 , N/A, 1 } would be represented as [0x6A,D77D1776,01,01,00]. It means: "upvote the second bundle in sidechain #1; and the first bundle in sidechain #3" (iff sidechains #2 has no bundles proposed).
-The M4 message will be invalid (and invalidate the block), if it tries to upvote a Bundle that doesn't exist (for example, trying to upvote the 7th bundle on sidechain #2, when sidechain #2 has only three bundles). If there are no Bundles at all (no one is trying to withdraw from any sidechain), then *any* M4 message present in the coinbase will be invalid. If M4 is NOT present in a block, then it is treated as "abstain".
+An upvote vector of { N/A, N/A, 4 } would be [0x6A,D77D1776,01,03].
-The ACKed withdrawal will gain one point for its ACK field. Therefore, the ACK-counter of any Bundle can only change by (-1,0,+1).
-Within a sidechain-group, upvoting one Bundle ("+1") requires you to downvote all other Bundles in that group. However, the minimum ACK-counter is zero. While only one Bundle can be upvoted at once; the whole group can all be unchanged at once ("abstain"), and they can all be downvoted at once ("alarm").
+The M4 message will be invalid (and invalidate the block), if:
+
+* It tries to upvote a Bundle that doesn't exist. (For example, trying to upvote the 7th bundle on sidechain #2, when sidechain #2 has only three bundles.)
+* There are no Bundles at all, from any sidechain.
+
+If M4 is NOT present in a block, then it is treated as "abstain".
+
+If M4 is present and valid: each withdrawal-bundle that is ACKed, will gain one upvote.
+
+Important: Within a sidechain-group, upvoting one Bundle ("+1") automatically downvotes ("-1") all other Bundles in that group. However, the minimum ACK-counter is zero. While only one Bundle can be upvoted at once; the whole group can all be unchanged at once ("abstain"), and they can all be downvoted at once ("alarm").
+
+For example:
+
+{| class="wikitable"
+|-
+! SC#
+! Bundle Hash
+! ACKs
+! Blocks Remaining
+|-
+| 1
+| h1
+| 45
+| 22,109
+|-
+| 1
+| h2
+| 12
+| 22,008
+|-
+| 2
+| h3
+| 13
+| 22,999
+|-
+| 2
+| h4
+| 8
+| 23,550<br />
+|-
+| 2
+| h5
+| 2
+| 22,560
+|}
+
+
+...in block 900,000 could become...
+
+
+{| class="wikitable"
+|-
+! SC#
+! Bundle Hash
+! ACKs
+! Blocks Remaining
+|-
+| 1
+| h1
+| 46
+| 22,108
+|-
+| 1
+| h2
+| 11
+| 22,007
+|-
+| 2
+| h3
+| 12
+| 22,998
+|-
+| 2
+| h4
+| 9
+| 23,549<br />
+|-
+| 2
+| h5
+| 1
+| 22,559
+|}
+
+...if M4 were [0x6A,D77D1776,00,0000,0001].
-Finally, we describe Deposits and Withdrawals.
+Finally, we describe Deposits and Withdrawals.
==== M5 -- Deposit BTC to Sidechain ====
-Both M5 and M6 are regular Bitcoin txns. They are distinguished from regular txns (non-M5 non-M6 txns), when they select one of the special Bip300 CTIP UTXOs as one of their inputs (see D1).
+Each sidechain stores all its BTC in one UTXO, called the "CTIP".
+
+By definition, an M5 is a transaction which spends the CTIP and '''increases''' the quantity of coins. An M6 is a transaction which spends the CTIP and '''decreases''' the quantity of coins in the CTIP. See [https://github.com/LayerTwo-Labs/mainchain/blob/391ab390adaa19f92871d769f8e120ca62c1cf14/src/validation.cpp#L688-L801 here].
+
+Every time a deposit/withdrawal is made, the old CTIP is spent and a new one is created. (Deposits/Withdrawals never cause UTXO bloat.) At all times, the CTIP of each sidechain is cached in D1 (above).
-All of a sidechain’s coins, are stored in one UTXO, called the "CTIP". Every time a deposit or withdrawal is made, the CTIP changes. Each deposit/withdrawal will select the sidechains CTIP, and generate a new CTIP. (Deposits/Withdrawals never cause UTXO bloat.) The current CTIP is cached in D1 (above).
+Every M5 is valid, as long as:
-If the '''quantity of coins''', in the from-CTIP-to-CTIP transaction, goes '''up''', (ie, if the user is adding coins), then the txn is treated as a Deposit (M5). Else it is treated as a Withdrawal (M6). See [https://github.com/drivechain-project/mainchain/blob/e37b008fafe0701b8313993c8b02ba41dc0f8a29/src/validation.cpp#L667-L780 here].
+* It has exactly one OP_DRIVECHAIN output -- this becomes the new CTIP.
+* The new CTIP has '''more''' coins in it, than before.
-As far as mainchain consensus is concerned, all deposits to a sidechain are always valid.
==== M6 -- Withdraw BTC from a Sidechain ====
We come, finally, to the critical matter: where users can take their money *out* of the sidechain.
-First, M6 must obey the same CTIP rules of M5 (see immediately above).
+M6 is invalid if:
+
+* The blinded hash of M6 does NOT match one of the approved Bundle-hashes. (In other words: M6 must first be approved by 13,150 upvotes.)
+* The first output of M6 is NOT an OP_DRIVECHAIN. (This OP_DRIVECHAIN becomes the new CTIP. In other words: all non-withdrawn coins are paid back to the sidechain.)
+* The second output is NOT a zero-value OP_RETURN script of exactly 10 bytes, of which 8 bytes are a serialized Bitcoin amount.
+* The txn fee of M6 is NOT exactly equal to the amount of the previous bullet point.
+* There are additional OP_DRIVECHAIN outputs after the first one.
+
+Else, M6 is valid.
+
+(The point of the latter two bullet points, is to allow the bundle hash to cover the L1 transaction fee.)
-Second, an M6 is only valid for inclusion in a block, if its blinded TxID matches an "approved" Bundle hash (ie, one with an ACK score of 13150+). In other words, an M6 can only be included in a block, after the 3+ month (13150 block) ceremony.
+===OP_DRIVECHAIN===
+
+This proposal adds a single new opcode, OP_DRIVECHAIN, which has strict semantics for usage.
+OP_NOP5 (0xb4) is redefined as OP_DRIVECHAIN if and only if the entire script is OP_DRIVECHAIN followed by a single-byte push and OP_TRUE (exactly 4 bytes).
+The single-byte push contains the sidechain number.
+Note that this is not a "script number", and cannot be OP_1..OP_16 or any other kind of push; it is also unsigned, and must not be padded even if over sidechain number 127.
+The final OP_TRUE is to ensure this change remains a softfork:
+without it, sidechain numbers 0 and 128 would cause the legacy script interpreter to fail.
+
+If an OP_DRIVECHAIN input is spent, the additional rules for M5 or M6 (see above) must be enforced.
+
+====Weight adjustments====
+
+To account for the additional drivechain checks, each message adds to the block's weight:
+
+{|class="wikitable"
+! Message !! Additional weight
+|-
+| M1 || 840
+|-
+| M2 || 336
+|-
+| M3 || 848
+|-
+| M4 || ?
+|-
+| M5 || 340
+|-
+| M6 || 352
+|}
-Third, M6 must meet two accounting criteria, lest it be invalid:
-# "Give change back to Escrow" -- The first output, TxOut0, must be paid back to the sidechain's Bip300 script. In other words, all non-withdrawn coins must be paid back into the sidechain.
-# "No traditional txn fee" -- For this txn, the sum of all inputs must equal the sum of all outputs. No traditional tx fee is possible. (Of course, there is still a txn fee for miners: it is paid via an OP TRUE output in the Bundle.) We want the withdraw-ers to set the fee "inside" the Bundle, and ACK it over 3 months like everything else.
+<!--
+get: 168 WU for 1 byte
+delete: free?
+create: 168 WU for 33 bytes
+hash: 4 WU??
+search outputs: ?
+permanent "proposal rejected" lookup: infinite??
+read prev block: a lot?? maybe store...
+comparison: 4 WU?
+encode script: ?
+
+M1: 3 get, 2 create
+M2: 1 get, 1 delete, 1 create
+M3: 3 get, 1 delete, 2 create, 2 hash
+ for each coinbase output: search for prior M3 for this sidechain
+ lookup if M3 was ever rejected or paid in the past
+ for each prior proposed withdrawal: (included in 1 get+delete+create)
+M4: 1 get
+ + for every proposed withdraw, 1 get, 1 delete, 1 create, 1 add
+ v0 needs to read and parse previous block
+M5/M6 OP_DRIVECHAIN spends require 2 additional input lookups
+ for each output: check for duplicate OP_DRIVECHAINs
+ amount comparison
+ M6: encode & compare fee amount, 2 hash, counter compare
+-->
==Backward compatibility==
@@ -331,7 +504,7 @@ See http://www.drivechain.info/literature/index.html
==Credits==
-Thanks to everyone who contributed to the discussion, especially: ZmnSCPxj, Adam Back, Peter Todd, Dan Anderson, Sergio Demian Lerner, Chris Stewart, Matt Corallo, Sjors Provoost, Tier Nolan, Erik Aronesty, Jason Dreyzehner, Joe Miyamoto, Ben Goldhaber.
+Thanks to everyone who contributed to the discussion, especially: Luke Dashjr, ZmnSCPxj, Adam Back, Peter Todd, Dan Anderson, Sergio Demian Lerner, Chris Stewart, Matt Corallo, Sjors Provoost, Tier Nolan, Erik Aronesty, Jason Dreyzehner, Joe Miyamoto, Ben Goldhaber.
==Copyright==
diff --git a/bip-0324.mediawiki b/bip-0324.mediawiki
index c0572a3..8050b15 100644
--- a/bip-0324.mediawiki
+++ b/bip-0324.mediawiki
@@ -112,17 +112,19 @@ Given a newly established connection (typically TCP/IP) between two v2 P2P nodes
#** Generates a random ephemeral secp256k1 private key and sends a corresponding 64-byte ElligatorSwift<ref name="ellswift_paper">'''What is ElligatorSwift and why use it?''' The [https://eprint.iacr.org/2022/759.pdf SwiftEC paper] describes a method called ElligatorSwift which allows encoding elliptic curve points in a way that is indistinguishable from a uniformly distributed bitstream. While a random 256-bit string has about 50% chance of being a valid X coordinate on the secp256k1 curve, every 512-bit string is a valid ElligatorSwift encoding of a curve point, making the encoded point indistinguishable from random when using an encoder that can sample uniformly.</ref><ref name="ellswift_perf">'''How fast is ElligatorSwift?''' Our benchmarks show that ElligatorSwift encoded ECDH is about 50% more expensive than unencoded ECDH. Given the fast performance of ECDH and the low frequency of new connections, we found the performance trade-off acceptable for the pseudorandom bytestream and future censorship resistance it can enable.</ref>-encoded public key to the responder.
#** May send up to 4095<ref name="why_4095_garbage">'''How was the limit of 4095 bytes garbage chosen?''' It is a balance between having sufficient freedom to hide information, and allowing it to be large enough so that the necessary 64 bytes of public key is small compared to it on the one hand, and bandwidth waste on the other hand.</ref> bytes of arbitrary data after their public key, called '''garbage''', providing a form of shapability and avoiding a recognizable pattern of exactly 64 bytes.<ref name="why_garbage">'''Why does the affordance for garbage exist in the protocol?''' The garbage strings after the public keys are needed for shapability of the handshake. Neither peer can send decoy packets before having received at least the other peer's public key, i.e., neither peer can send more than 64 bytes before having received 64 bytes.</ref>
#* The responder:
-#** Waits until one byte is received which does not match the 12 bytes consisting of the network magic followed by "version\x00". If the first 12 bytes do match, the connection is treated as using the v1 protocol instead.<ref name="why_no_prefix_check">'''What if a v2 initiator's public key starts accidentally with these 12 bytes?''' This is so unlikely (probability of ''2<sup>-96</sup>'') to happen randomly in the v2 protocol that the initiator does not need to specifically avoid it.</ref><ref>Bitcoin Core versions <=0.4.0 and >=22.0 ignore valid P2P messages that are received prior to a VERSION message. Bitcoin Core versions between 0.4.0 and 22.0 assign a misbehavior score to the peer upon receiving such messages. v2 clients implementing this proposal will interpret any message other than VERSION received as the first message to be the initiation of a v2 connection, and will result in disconnection for v1 initiators that send any message type other than VERSION as the first message. We are not aware of any implementations where this could pose a problem.</ref>
+#** Waits until one byte is received which does not match the 16 bytes consisting of the network magic followed by "version\x00\x00\x00\x00\x00". If the first 16 bytes do match, the connection is treated as using the v1 protocol instead.<ref name="why_no_prefix_check">'''What if a v2 initiator's public key starts accidentally with these 16 bytes?''' This is so unlikely (probability of ''2<sup>-128</sup>'') to happen randomly in the v2 protocol that the initiator does not need to specifically avoid it. The optional detection of wrong-network v1 peers has a probability of ''2<sup>-96</sup>'', which is still negligible compared to random network failures.</ref><ref>Bitcoin Core versions <=0.4.0 and >=22.0 ignore valid P2P messages that are received prior to a VERSION message. Bitcoin Core versions between 0.4.0 and 22.0 assign a misbehavior score to the peer upon receiving such messages. v2 clients implementing this proposal will interpret any message other than VERSION received as the first message to be the initiation of a v2 connection, and will result in disconnection for v1 initiators that send any message type other than VERSION as the first message. We are not aware of any implementations where this could pose a problem.</ref>
+#** If the first 4 received bytes do not match the network magic, but the 12 bytes after that do match the version message encoding above, implementations may interpret this as a v1 peer of a different network, and disconnect them.
#** Similarly generates a random ephemeral private key and sends a corresponding 64-byte ElligatorSwift-encoded public key to the initiator.
#** Similarly may send up to 4095 bytes of garbage data after their public key.
#* Both parties:
#** Receive (the remainder of) the full 64-byte public key from the other side.
#** Use X-only<ref name="xonly_ecdh">'''Why use X-only ECDH?''' Using only the X coordinate provides the same security as using a full encoding of the secret curve point but allows for more efficient implementation by avoiding the need for square roots to compute Y coordinates.</ref> ECDH to compute a shared secret from their private key and the exchanged public keys<ref name="why_ecdh_pubkeys">'''Why is the shared secret computation a function of the exact 64-byte public encodings sent?''' This makes sure that an attacker cannot modify the public key encoding used without modifying the rest of the stream. If a third party wants the ability to modify stream bytes, they need to perform a full MitM attack on the connection.</ref>, and deterministically derive from the secret 4 '''encryption keys''' (two in each direction: one for packet lengths, one for content encryption), a '''session id''', and two 16-byte '''garbage terminators'''<ref>'''What length is sufficient for garbage terminators?''' The length of the garbage terminators determines the probability of accidental termination of a legitimate v2 connection due to garbage bytes (sent prior to ECDH) inadvertently including the terminator. 16 byte terminators with 4095 bytes of garbage yield a negligible probability of such collision which is likely orders of magnitude lower than random connection failure on the Internet.</ref><ref>'''What does a garbage terminator in the wild look like?''' <div>[[File:bip-0324/garbage_terminator.png|none|256px|A garbage terminator model TX-v2 in the wild... sent by the responder]]</div>
</ref> (one in each direction) using HKDF-SHA256.
-#** Send their 16-byte garbage terminator<ref name="why_garbage_term">'''Why does the protocol need a garbage terminator?''' While it is in principle possible to use the garbage authentication packet directly as a terminator (scan until a valid authentication packet follows), this would be significantly slower than just scanning for a fixed byte sequence, as it would require recomputing a Poly1305 tag after every received byte.</ref> followed by a '''garbage authentication packet'''<ref name="why_garbage_auth">'''Why does the protocol require a garbage authentication packet?''' Without the garbage authentication packet, the garbage would be modifiable by a third party without consequences. We want to force any active attacker to have to maintain a full protocol state. In addition, such malleability without the consequence of connection termination could enable protocol fingerprinting.</ref>, an '''encrypted packet''' (see further) with arbitrary '''contents''', and '''associated data''' equal to the garbage.
+#** Send their 16-byte garbage terminator.<ref name="why_garbage_term">'''Why does the protocol need a garbage terminator?''' While it is in principle possible to use the first packet after the garbage directly as a terminator (scan until a valid packet follows), this would be significantly slower than just scanning for a fixed byte sequence, as it would require recomputing a Poly1305 tag after every received byte.</ref>
#** Receive up to 4111 bytes, stopping when encountering the garbage terminator.
-#** Receive an encrypted packet, verify that it decrypts correctly with associated data set to the garbage received, and then ignore its contents.
-#* At this point, both parties have the same keys, and all further communication proceeds in the form of encrypted packets. Packets have an '''ignore bit''', which makes them '''decoy packets''' if set. Decoy packets are to be ignored by the receiver apart from verifying they decrypt correctly. Either peer may send such decoy packets at any point after this. These form the primary shapability mechanism in the protocol. How and when to use them is out of scope for this document.
+#* At this point, both parties have the same keys, and all further communication proceeds in the form of '''encrypted packets'''.
+#** Encrypted packets have an '''ignore bit''', which makes them '''decoy packets''' if set. Decoy packets are to be ignored by the receiver apart from verifying they decrypt correctly. Either peer may send such decoy packets at any point from here on. These form the primary shapability mechanism in the protocol. How and when to use them is out of scope for this document.
+#** For each of the two directions, the first encrypted packet that will be sent in that direction (regardless of it being a decoy packet or not) will make use of the associated authenticated data (AAD) feature of the AEAD to authenticate the garbage that has been sent in that direction.<ref name="why_garbage_auth">'''Why does the protocol authenticate the garbage?''' Without garbage authentication, the garbage would be modifiable by a third party without consequences. We want to force any active attacker to have to maintain a full protocol state. In addition, such malleability without the consequence of connection termination could enable protocol fingerprinting.</ref>
# The '''Version negotiation phase''', where parties negotiate what transport version they will use, as well as data defined by that version.<ref name="example_versions">'''What features could be added in future protocol versions?''' Examples of features that could be added in future versions include post-quantum cryptography upgrades to the handshake, and optional authentication.</ref>
#* The responder:
#** Sends a '''version packet''' with empty content, to indicate support for the v2 P2P protocol proposed by this document. Any other value for content is reserved for future versions.
@@ -136,19 +138,19 @@ Given a newly established connection (typically TCP/IP) between two v2 P2P nodes
To avoid the recognizable pattern of first messages being at least 64 bytes, a future backwards-compatible upgrade to this protocol may allow both peers to send their public key + garbage + garbage terminator in multiple rounds, slicing those bytes up into messages arbitrarily, as long as progress is guaranteed.<ref name="handshake_progress">'''How can progress be guaranteed in a backwards-compatible way?''' In order to guarantee progress, it must be ensured that no deadlock occurs, i.e., no state is reached in which each party waits for the other party indefinitely. For example, any upgrade that adheres to the following conditions will guarantee progress:
-* The initiator must start by sending at least as many bytes as necessary to mismatch the magic/version 12 bytes prefix.
-* The responder must start sending after having received at least one byte that mismatches that 12-byte prefix.
+* The initiator must start by sending at least as many bytes as necessary to mismatch the magic/version 16 bytes prefix.
+* The responder must start sending after having received at least one byte that mismatches that 16-byte prefix.
* As soon as either party has received the other peer's garbage terminator, or has received 4095 bytes of garbage, they must send their own garbage terminator. (When either of these conditions is met, the other party has nothing to respond with anymore that would be needed to guarantee progress otherwise.)
* Whenever either party receives any nonzero number of bytes, while not having sent their garbage terminator completely yet, they must send at least one byte in response without waiting for more bytes.
-* After either party has sent their garbage terminator, they must also send the garbage authentication packet without waiting for more bytes, and transition to the version negotiation phase.
+* After either party has sent their garbage terminator, they must transition to the version negotiation phase without waiting for more bytes.
Since the protocol as specified here adheres to these conditions, any upgrade which also adheres to these conditions will be backwards-compatible.</ref>
-Note that the version negotiation phase does not need to wait for the key exchange phase to complete; version packets can be sent immediately after sending the garbage authentication packet. So the first two phases together, jointly called '''the handshake''', comprise just 1.5 roundtrips:
+Note that the version negotiation phase does not need to wait for the key exchange phase to complete; version packets can be sent immediately after sending the garbage terminator. So the first two phases together, jointly called '''the handshake''', comprise just 1.5 roundtrips:
* the initiator sends public key + garbage
-* the responder sends public key + garbage + garbage terminator + garbage authentication packet + version packet
-* the initiator sends garbage terminator + garbage authentication packet + version packet
+* the responder sends public key + garbage + garbage terminator + decoy packets (optional) + version packet
+* the initiator sends garbage terminator + decoy packets (optional) + version packet
'''Packet encryption overview'''
@@ -181,26 +183,24 @@ As explained before, these messages are sent to set up the connection:
----------------------------------------------------------------------------------------------------
| Initiator Responder |
| |
- | x, ellswift_X = ellswift_create(initiating=True) |
+ | x, ellswift_X = ellswift_create() |
| |
- | --- ellswift_X + initiator_garbage (initiator_garbage_len bytes; max 4095) ---> |
+ | ---- ellswift_X + initiator_garbage (initiator_garbage_len bytes; max 4095) ---> |
| |
- | y, ellswift_Y = ellswift_create(initiating=False) |
+ | y, ellswift_Y = ellswift_create() |
| ecdh_secret = v2_ecdh( |
| y, ellswift_X, ellswift_Y, initiating=False) |
| v2_initialize(initiator, ecdh_secret, initiating=False) |
| |
- | <-- ellswift_Y + responder_garbage (responder_garbage_len bytes; max 4095) + |
- | responder_garbage_terminator (16 bytes) + |
- | v2_enc_packet(initiator, b'', aad=responder_garbage) + |
- | v2_enc_packet(initiator, RESPONDER_TRANSPORT_VERSION) --- |
+ | <--- ellswift_Y + responder_garbage (responder_garbage_len bytes; max 4095) + |
+ | responder_garbage_terminator (16 bytes) + |
+ | v2_enc_packet(initiator, RESPONDER_TRANSPORT_VERSION, aad=responder_garbage) ---- |
| |
| ecdh_secret = v2_ecdh(x, ellswift_Y, ellswift_X, initiating=True) |
| v2_initialize(responder, ecdh_secret, initiating=True) |
| |
- | --- initiator_garbage_terminator (16 bytes) + |
- | v2_enc_packet(responder, b'', aad=initiator_garbage) + |
- | v2_enc_packet(responder, INITIATOR_TRANSPORT_VERSION) ---> |
+ | ---- initiator_garbage_terminator (16 bytes) + |
+ | v2_enc_packet(responder, INITIATOR_TRANSPORT_VERSION, aad=initiator_garbage) ---> |
| |
----------------------------------------------------------------------------------------------------
</pre>
@@ -333,51 +333,58 @@ To establish a v2 encrypted connection, the initiator generates an ephemeral sec
<pre>
def initiate_v2_handshake(peer, garbage_len):
- peer.privkey_ours, peer.ellswift_ours = ellswift_create(initiating=True)
+ peer.privkey_ours, peer.ellswift_ours = ellswift_create()
peer.sent_garbage = rand_bytes(garbage_len)
send(peer, peer.ellswift_ours + peer.sent_garbage)
</pre>
-The responder generates an ephemeral keypair for itself and derives the shared ECDH secret (using the first 64 received bytes) which enables it to instantiate the encrypted transport. It then sends 64 bytes of the unencrypted ElligatorSwift encoding of its own public key and its own <code>responder_garbage</code> also of length <code>garbage_len < 4096</code>. If the first 12 bytes received match the v1 prefix, the v1 protocol is used instead.
+The responder generates an ephemeral keypair for itself and derives the shared ECDH secret (using the first 64 received bytes) which enables it to instantiate the encrypted transport. It then sends 64 bytes of the unencrypted ElligatorSwift encoding of its own public key and its own <code>responder_garbage</code> also of length <code>garbage_len < 4096</code>. If the first 16 bytes received match the v1 prefix, the v1 protocol is used instead.
<pre>
TRANSPORT_VERSION = b''
NETWORK_MAGIC = b'\xf9\xbe\xb4\xd9' # Mainnet network magic; differs on other networks.
-V1_PREFIX = NETWORK_MAGIC + b'version\x00'
+V1_PREFIX = NETWORK_MAGIC + b'version\x00\x00\x00\x00\x00'
def respond_v2_handshake(peer, garbage_len):
peer.received_prefix = b""
- while len(peer.received_prefix) < 12:
+ while len(peer.received_prefix) < len(V1_PREFIX):
peer.received_prefix += receive(peer, 1)
if peer.received_prefix[-1] != V1_PREFIX[len(peer.received_prefix) - 1]:
- peer.privkey_ours, peer.ellswift_ours = ellswift_create(initiating=False)
+ peer.privkey_ours, peer.ellswift_ours = ellswift_create()
peer.sent_garbage = rand_bytes(garbage_len)
send(peer, ellswift_Y + peer.sent_garbage)
return
use_v1_protocol()
</pre>
-Upon receiving the encoded responder public key, the initiator derives the shared ECDH secret and instantiates the encrypted transport. It then sends the derived 16-byte <code>initiator_garbage_terminator</code> followed by an authenticated, encrypted packet with empty contents<ref name="send_empty_garbauth">'''Does the content of the garbage authentication packet need to be empty?''' The receiver ignores the content of the garbage authentication packet, so its content can be anything, and it can in principle be used as a shaping mechanism too. There is however no need for that, as immediately afterward the initiator can start using decoy packets as (a much more flexible) shaping mechanism instead.</ref> to authenticate the garbage, and its own version packet. It then receives the responder's garbage and garbage authentication packet (delimited by the garbage terminator), and checks if the garbage is authenticated correctly. The responder performs very similar steps but includes the earlier received prefix bytes in the public key. As mentioned before, the encrypted packets for the '''version negotiation phase''' can be piggybacked with the garbage authentication packet to minimize roundtrips.
+Upon receiving the encoded responder public key, the initiator derives the shared ECDH secret and instantiates the encrypted transport. It then sends the derived 16-byte <code>initiator_garbage_terminator</code>, optionally followed by an arbitrary number of decoy packets. Afterwards, it receives the responder's garbage (delimited by the garbage terminator). The responder performs very similar steps but includes the earlier received prefix bytes in the public key. Both the initiator and the responder set the AAD of the first encrypted packet they send after the garbage terminator (i.e., either an optional decoy packet or the version packet) to the garbage they have just sent, not including the garbage terminator.
<pre>
-def complete_handshake(peer, initiating):
+def complete_handshake(peer, initiating, decoy_content_lengths=[]):
received_prefix = b'' if initiating else peer.received_prefix
ellswift_theirs = receive(peer, 64 - len(received_prefix))
+ if not initiating and ellswift_theirs[4:16] == V1_PREFIX[4:16]:
+ # Looks like a v1 peer from the wrong network.
+ disconnect(peer)
ecdh_secret = v2_ecdh(peer.privkey_ours, ellswift_theirs, peer.ellswift_ours,
initiating=initiating)
initialize_v2_transport(peer, ecdh_secret, initiating=True)
- # Send garbage terminator + garbage authentication packet + version packet.
- send(peer, peer.send_garbage_terminator +
- v2_enc_packet(peer, b'', aad=peer.sent_garbage) +
- v2_enc_packet(peer, TRANSPORT_VERSION))
+ # Send garbage terminator
+ send(peer, peer.send_garbage_terminator)
+ # Optionally send decoy packets after garbage terminator.
+ aad = peer.sent_garbage
+ for decoy_content_len in decoy_content_lengths:
+ send(v2_enc_packet(peer, decoy_content_len * b'\x00', aad=aad))
+ aad = b''
+ # Send version packet.
+ send(v2_enc_packet(peer, TRANSPORT_VERSION, aad=aad))
# Skip garbage, until encountering garbage terminator.
received_garbage = recv(peer, 16)
for i in range(4096):
if received_garbage[-16:] == peer.recv_garbage_terminator:
- # Receive, decode, and ignore garbage authentication packet (decoy or not)
- v2_receive_packet(peer, aad=received_garbage, skip_decoy=False)
- # Receive, decode, and ignore version packet, skipping decoys
- v2_receive_packet(peer)
+ # Receive, decode, and ignore version packet.
+ # This includes skipping decoys and authenticating the received garbage.
+ v2_receive_packet(peer, aad=received_garbage)
return
else:
received_garbage += recv(peer, 1)
@@ -396,7 +403,7 @@ Packet encryption is built on two existing primitives:
* '''ChaCha20Poly1305''' is specified as <code>AEAD_CHACHA20_POLY1305</code> in [https://datatracker.ietf.org/doc/html/rfc8439#section-2.8 RFC 8439 section 2.8]. It is an authenticated encryption protocol with associated data (AEAD), taking a 256-bit key, 96-bit nonce, and an arbitrary-length byte array of associated authenticated data (AAD). Due to the built-in authentication tag, ciphertexts are 16 bytes longer than the corresponding plaintext. In what follows:
** <code>aead_chacha20_poly1305_encrypt(key, nonce, aad, plaintext)</code> refers to a function that takes as input a 32-byte array ''key'', a 12-byte array ''nonce'', an arbitrary-length byte array ''aad'', and an arbitrary-length byte array ''plaintext'', and returns a byte array ''ciphertext'', 16 bytes longer than the plaintext.
** <code>aead_chacha20_poly1305_decrypt(key, nonce, aad, ciphertext)</code> refers to a function that takes as input a 32-byte array ''key'', a 12-byte array ''nonce'', an arbitrary-length byte array ''aad'', and an arbitrary-length byte array ''ciphertext'', and returns either a byte array ''plaintext'' (16 bytes shorter than the ciphertext), or ''None'' in case the ciphertext was not a valid ChaCha20Poly1305 encryption of any plaintext with the specified ''key'', ''nonce'', and ''aad''.
-* The '''ChaCha20 Block Function''' is specified in [https://datatracker.ietf.org/doc/html/rfc8439#section-2.8 RFC 8439 section 2.3]. It is a pseudorandom function (PRF) taking a 256-bit key, 96-bit nonce, and 32-bit counter, and outputs 64 pseudorandom bytes. It is the underlying building block on which ChaCha20 (and ultimately, ChaCha20Poly1305) is built. In what follows:
+* The '''ChaCha20 Block Function''' is specified in [https://datatracker.ietf.org/doc/html/rfc8439#section-2.3 RFC 8439 section 2.3]. It is a pseudorandom function (PRF) taking a 256-bit key, 96-bit nonce, and 32-bit counter, and outputs 64 pseudorandom bytes. It is the underlying building block on which ChaCha20 (and ultimately, ChaCha20Poly1305) is built. In what follows:
** <code>chacha20_block(key, nonce, count)</code> refers to a function that takes as input a 32-byte array ''key'', a 12-byte array ''nonce'', and an integer ''count'' in range ''0..2<sup>32</sup>-1'', and returns a byte array of length 64.
These will be used for plaintext encryption and length encryption, respectively.
@@ -490,17 +497,19 @@ def v2_enc_packet(peer, contents, aad=b'', ignore=False):
<pre>
CHACHA20POLY1305_EXPANSION = 16
-def v2_receive_packet(peer, aad=b'', skip_decoy=True):
+def v2_receive_packet(peer, aad=b''):
while True:
enc_contents_len = receive(peer, LENGTH_FIELD_LEN)
contents_len = int.from_bytes(peer.recv_L.crypt(enc_contents_len), 'little')
aead_ciphertext = receive(peer, HEADER_LEN + contents_len + CHACHA20POLY1305_EXPANSION)
- plaintext = peer.recv_P.decrypt(aead_ciphertext)
+ plaintext = peer.recv_P.decrypt(aad, aead_ciphertext)
if plaintext is None:
disconnect(peer)
break
+ # Only the first packet is expected to have non-empty AAD.
+ aad = b''
header = plaintext[:HEADER_LEN]
- if not (skip_decoy and header[0] & (1 << IGNORE_BIT_POS)):
+ if not (header[0] & (1 << IGNORE_BIT_POS)):
return plaintext[HEADER_LEN:]
</pre>
@@ -554,12 +563,9 @@ The following table lists currently defined message type IDs:
|<code>GETCFHEADERS</code>||<code>CFHEADERS</code>||<code>GETCFCHECKPT</code>||<code>CFCHECKPT</code>
|-
!+28
-|<code>ADDRV2</code>||<code>REQRECON</code>||<code>SKETCH</code>||<code>REQSKETCHEXT</code>
+|<code>ADDRV2</code>
|-
-!+32
-|<code>RECONCILDIFF</code>
-|-
-!&geq;33
+!&geq;29
|| colspan="4" | (undefined)
|}
diff --git a/bip-0327.mediawiki b/bip-0327.mediawiki
index 07b40f5..b5600ab 100644
--- a/bip-0327.mediawiki
+++ b/bip-0327.mediawiki
@@ -123,7 +123,7 @@ This is by design: All algorithms in this proposal handle multiple signers who (
and applications are not required to check for duplicate individual public keys.
In fact, applications are recommended to omit checks for duplicate individual public keys in order to simplify error handling.
Moreover, it is often impossible to tell at key aggregation which signer is to blame for the duplicate, i.e., which signer came up with an individual public key honestly and which disruptive signer copied it.
-In contrast, MuSig2 is designed to identify disruptive signers at signing time (see [[#identifiying-disruptive-signers|Identifiying Disruptive Signers]]).
+In contrast, MuSig2 is designed to identify disruptive signers at signing time (see [[#identifying-disruptive-signers|Identifying Disruptive Signers]]).
While the algorithms in this proposal are able to handle duplicate individual public keys, there are scenarios where applications may choose to abort when encountering duplicates.
For example, we can imagine a scenario where a single entity creates a MuSig2 setup with multiple signing devices.
@@ -211,7 +211,7 @@ The bit can be obtained with ''GetPlainPubkey(keyagg_ctx)[0] & 1''.
The following specification of the algorithms has been written with a focus on clarity.
As a result, the specified algorithms are not always optimal in terms of computation and space.
-In particular, some values are recomputed but can be cached in actual implementations (see [[#signing-flow|Signing Flow]]).
+In particular, some values are recomputed but can be cached in actual implementations (see [[#general-signing-flow|General Signing Flow]]).
=== Notation ===
@@ -367,7 +367,7 @@ Algorithm ''ApplyTweak(keyagg_ctx, tweak, is_xonly_t)'':
Algorithm ''NonceGen(sk, pk, aggpk, m, extra_in)'':
* Inputs:
** The secret signing key ''sk'': a 32-byte array (optional argument)
-** The individual public key ''pk'': a 33-byte array (see [[#modifications-to-nonce-generation|Modifications to Nonce Generation]] for the reason that this argument is mandatory)
+** The individual public key ''pk'': a 33-byte array (see [[#signing-with-tweaked-individual-keys|Signing with Tweaked Individual Keys]] for the reason that this argument is mandatory)
** The x-only aggregate public key ''aggpk'': a 32-byte array (optional argument)
** The message ''m'': a byte array (optional argument)<ref name="mlen">In theory, the allowed message size is restricted because SHA256 accepts byte strings only up to size of 2^61-1 bytes (and because of the 8-byte length encoding).</ref>
** The auxiliary input ''extra_in'': a byte array with ''0 &le; len(extra_in) &le; 2<sup>32</sup>-1'' (optional argument)
@@ -465,7 +465,7 @@ Algorithm ''Sign(secnonce, sk, session_ctx)'':
* Fail if ''pk &ne; secnonce[64:97]''
* Let ''a = GetSessionKeyAggCoeff(session_ctx, P)''; fail if that fails<ref>Failing ''Sign'' when ''GetSessionKeyAggCoeff(session_ctx, P)'' fails is not necessary for unforgeability. It merely indicates to the caller that the scheme is not being used correctly.</ref>
* Let ''g = 1'' if ''has_even_y(Q)'', otherwise let ''g = -1 mod n''
-* <div id="Sign negation"></div>Let ''d = g⋅gacc⋅d' mod n'' (See [[negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]])
+* <div id="Sign negation"></div>Let ''d = g⋅gacc⋅d' mod n'' (See [[#negation-of-the-secret-key-when-signing|Negation Of The Secret Key When Signing]])
* Let ''s = (k<sub>1</sub> + b⋅k<sub>2</sub> + e⋅a⋅d) mod n''
* Let ''psig = bytes(32, s)''
* Let ''pubnonce = cbytes(k<sub>1</sub>'⋅G) || cbytes(k<sub>2</sub>'⋅G)''
diff --git a/bip-0329.mediawiki b/bip-0329.mediawiki
index 9a8b270..fc5da42 100644
--- a/bip-0329.mediawiki
+++ b/bip-0329.mediawiki
@@ -26,8 +26,10 @@ These standards are well supported and allow users to move easily between differ
There is, however, no defined standard to transfer any labels the user may have applied to the transactions, addresses, public keys, inputs, outputs or xpubs in their wallet.
The UTXO model that Bitcoin uses makes these labels particularly valuable as they may indicate the source of funds, whether received externally or as a result of change from a prior transaction.
In both cases, care must be taken when spending to avoid undesirable leaks of private information.
+
Labels provide valuable guidance in this regard, and have even become mandatory when spending in several Bitcoin wallets.
Allowing users to import and export their labels in a standardized way ensures that they do not experience lock-in to a particular wallet application.
+In addition, many wallets allow unspent outputs to be frozen or made unspendable within the wallet. Since this wallet-related metadata is similar to labels and not captured elsewhere, it is also included in this format.
==Rationale==
@@ -44,7 +46,7 @@ It is also a convenient format for command-line processing, which is often line-
Further to the JSON Lines specification, an export of labels from a wallet must be a UTF-8 encoded text file, containing one record per line consisting of a valid JSON object.
Lines are separated by <tt>\n</tt>. Multiline values are not permitted.
-Each JSON object must contain 3 key/value pairs, defined as follows:
+Each JSON object must contain 3 or 4 key/value pairs, defined as follows:
{| class="wikitable"
|-
@@ -59,6 +61,12 @@ Each JSON object must contain 3 key/value pairs, defined as follows:
|-
| <tt>label</tt>
| The label applied to the reference
+|-
+| <tt>origin</tt>
+| Optional key origin information referencing the wallet associated with the label
+|-
+| <tt>spendable</tt>
+| One of <tt>true</tt> or <tt>false</tt>, denoting if an output should be spendable by the wallet
|}
The reference is defined for each <tt>type</tt> as follows:
@@ -94,6 +102,11 @@ The reference is defined for each <tt>type</tt> as follows:
| <tt>xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8Nq...</tt>
|}
+Each JSON object must contain both <tt>type</tt> and <tt>ref</tt> properties. The <tt>label</tt>, <tt>origin</tt> and <tt>spendable</tt> properties are optional. If the <tt>label</tt> or <tt>spendable</tt> properties are omitted, the importing wallet should not alter these values. The <tt>origin</tt> property should only appear where type is <tt>tx</tt>, and the <tt>spendable</tt> property only where type is <tt>output</tt>.
+
+If present, the optional <tt>origin</tt> property must contain an abbreviated output descriptor (as defined by BIP380<ref>[https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki BIP-0380]</ref>) describing a BIP32 compatible originating wallet, including all key origin information but excluding any actual keys, any child path elements, or a checksum.
+This property should be used to disambiguate transaction labels from different wallets contained in the same export, particularly when exporting multiple accounts derived from the same seed.
+
Care should be taken when exporting due to the privacy sensitive nature of the data.
Encryption in transit over untrusted networks is highly recommended, and encryption at rest should also be considered.
Unencrypted exports should be deleted as soon as possible.
@@ -101,7 +114,7 @@ For security reasons no private key types are defined.
==Importing==
-* An importing wallet may ignore records it does not store, and truncate labels if necessary.
+* An importing wallet may ignore records it does not store, and truncate labels if necessary. A suggested default for maximum label length is 255 characters, and an importing wallet should consider warning the user if truncation is applied.
* Wallets importing public key records may derive addresses from them to match against known wallet addresses.
* Wallets importing extended public keys may match them against signers, for example in a multisig setup.
@@ -114,12 +127,13 @@ However, importing wallets complying to this specification may ignore types not
The following fragment represents a wallet label export:
<pre>
-{ "type": "tx", "ref": "f91d0a8a78462bc59398f2c5d7a84fcff491c26ba54c4833478b202796c8aafd", "label": "Transaction" }
+{ "type": "tx", "ref": "f91d0a8a78462bc59398f2c5d7a84fcff491c26ba54c4833478b202796c8aafd", "label": "Transaction", "origin": "wpkh([d34db33f/84'/0'/0'])" }
{ "type": "addr", "ref": "bc1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c", "label": "Address" }
{ "type": "pubkey", "ref": "0283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f448", "label": "Public Key" }
{ "type": "input", "ref": "f91d0a8a78462bc59398f2c5d7a84fcff491c26ba54c4833478b202796c8aafd:0", "label": "Input" }
-{ "type": "output", "ref": "f91d0a8a78462bc59398f2c5d7a84fcff491c26ba54c4833478b202796c8aafd:1", "label": "Output" }
+{ "type": "output", "ref": "f91d0a8a78462bc59398f2c5d7a84fcff491c26ba54c4833478b202796c8aafd:1", "label": "Output" , "spendable" : "false" }
{ "type": "xpub", "ref": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", "label": "Extended Public Key" }
+{ "type": "tx", "ref": "f546156d9044844e02b181026a1a407abfca62e7ea1159f87bbeaa77b4286c74", "label": "Account #1 Transaction", "origin": "wpkh([d34db33f/84'/0'/1'])" }
</pre>
==Reference Implementation==
diff --git a/bip-0330.mediawiki b/bip-0330.mediawiki
index c24ea42..996f74e 100644
--- a/bip-0330.mediawiki
+++ b/bip-0330.mediawiki
@@ -210,7 +210,7 @@ The reconcildiff message is used by reconciliation initiator to announce transac
| uint32[] || ask_shortids || The short IDs that the sender did not have.
|}
-Upon receipt a "reconcildiff" message with ''success=1'' (reconciliation success), a node sends an "inv" message for the transactions requested by 32-bit IDs (first vector) containing their wtxids (with parent transactions occuring before their dependencies).
+Upon receipt a "reconcildiff" message with ''success=1'' (reconciliation success), a node sends an "inv" message for the transactions requested by 32-bit IDs (first vector) containing their wtxids (with parent transactions occurring before their dependencies).
If ''success=0'' (reconciliation failure), receiver should announce all transactions from the reconciliation set via an "inv" message.
In both cases, transactions the sender of the message thinks the receiver is missing are announced via an "inv" message.
The regular "inv" deduplication should apply.
diff --git a/bip-0340.mediawiki b/bip-0340.mediawiki
index c99b77a..c941916 100644
--- a/bip-0340.mediawiki
+++ b/bip-0340.mediawiki
@@ -86,7 +86,7 @@ Despite halving the size of the set of valid public keys, implicit Y coordinates
For example, without tagged hashing a BIP340 signature could also be valid for a signature scheme where the only difference is that the arguments to the hash function are reordered. Worse, if the BIP340 nonce derivation function was copied or independently created, then the nonce could be accidentally reused in the other scheme leaking the secret key.
-This proposal suggests to include the tag by prefixing the hashed data with ''SHA256(tag) || SHA256(tag)''. Because this is a 64-byte long context-specific constant and the ''SHA256'' block size is also 64 bytes, optimized implementations are possible (identical to SHA256 itself, but with a modified initial state). Using SHA256 of the tag name itself is reasonably simple and efficient for implementations that don't choose to use the optimization.
+This proposal suggests to include the tag by prefixing the hashed data with ''SHA256(tag) || SHA256(tag)''. Because this is a 64-byte long context-specific constant and the ''SHA256'' block size is also 64 bytes, optimized implementations are possible (identical to SHA256 itself, but with a modified initial state). Using SHA256 of the tag name itself is reasonably simple and efficient for implementations that don't choose to use the optimization. In general, tags can be arbitrary byte arrays, but are suggested to be textual descriptions in UTF-8 encoding.
'''Final scheme''' As a result, our final scheme ends up using public key ''pk'' which is the X coordinate of a point ''P'' on the curve whose Y coordinate is even and signatures ''(r,s)'' where ''r'' is the X coordinate of a point ''R'' whose Y coordinate is even. The signature satisfies ''s⋅G = R + tagged_hash(r || pk || m)⋅P''.
@@ -116,7 +116,7 @@ The following conventions are used, with constants as defined for [https://www.s
*** Let ''y = c<sup>(p+1)/4</sup> mod p''.
*** Fail if ''c &ne; y<sup>2</sup> mod p''.
*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y'' if ''y mod 2 = 0'' or ''y(P) = p-y'' otherwise.
-** The function ''hash<sub>tag</sub>(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''.
+** The function ''hash<sub>name</sub>(x)'' where ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)'', where ''tag'' is the UTF-8 encoding of ''name''.
==== Public Key Generation ====
@@ -138,7 +138,7 @@ As an alternative to generating keys randomly, it is also possible and safe to r
Input:
* The secret key ''sk'': a 32-byte array
-* The message ''m'': a 32-byte array
+* The message ''m'': a byte array
* Auxiliary random data ''a'': a 32-byte array
The algorithm ''Sign(sk, m)'' is defined as:
@@ -174,7 +174,7 @@ It should be noted that various alternative signing algorithms can be used to pr
Input:
* The public key ''pk'': a 32-byte array
-* The message ''m'': a 32-byte array
+* The message ''m'': a byte array
* A signature ''sig'': a 64-byte array
The algorithm ''Verify(pk, m, sig)'' is defined as:
@@ -197,7 +197,7 @@ Note that the correctness of verification relies on the fact that ''lift_x'' alw
Input:
* The number ''u'' of signatures
* The public keys ''pk<sub>1..u</sub>'': ''u'' 32-byte arrays
-* The messages ''m<sub>1..u</sub>'': ''u'' 32-byte arrays
+* The messages ''m<sub>1..u</sub>'': ''u'' byte arrays
* The signatures ''sig<sub>1..u</sub>'': ''u'' 64-byte arrays
The algorithm ''BatchVerify(pk<sub>1..u</sub>, m<sub>1..u</sub>, sig<sub>1..u</sub>)'' is defined as:
@@ -213,6 +213,50 @@ The algorithm ''BatchVerify(pk<sub>1..u</sub>, m<sub>1..u</sub>, sig<sub>1..u</s
If all individual signatures are valid (i.e., ''Verify'' would return success for them), ''BatchVerify'' will always return success. If at least one signature is invalid, ''BatchVerify'' will return success with at most a negligible probability.
+=== Usage Considerations ===
+
+==== Messages of Arbitrary Size ====
+
+The signature scheme specified in this BIP accepts byte strings of arbitrary size as input messages.<ref>In theory, the message size is restricted due to the fact that SHA256 accepts byte strings only up to size of 2^61-1 bytes.</ref>
+It is understood that implementations may reject messages which are too large in their environment or application context,
+e.g., messages which exceed predefined buffers or would otherwise cause resource exhaustion.
+
+Earlier revisions of this BIP required messages to be exactly 32 bytes.
+This restriction puts a burden on callers
+who typically need to perform pre-hashing of the actual input message by feeding it through SHA256 (or another collision-resistant cryptographic hash function)
+to create a 32-byte digest which can be passed to signing or verification
+(as for example done in [[bip-0341.mediawiki|BIP341]].)
+
+Since pre-hashing may not always be desirable,
+e.g., when actual messages are shorter than 32 bytes,<ref>Another reason to omit pre-hashing is to protect against certain types of cryptanalytic advances against the hash function used for pre-hashing: If pre-hashing is used, an attacker that can find collisions in the pre-hashing function can necessarily forge signatures under chosen-message attacks. If pre-hashing is not used, an attacker that can find collisions in SHA256 (as used inside the signature scheme) may not be able to forge signatures. However, this seeming advantage is mostly irrelevant in the context of Bitcoin, which already relies on collision resistance of SHA256 in other places, e.g., for transaction hashes.</ref>
+the restriction to 32-byte messages has been lifted.
+We note that pre-hashing is recommended for performance reasons in applications that deal with large messages.
+If large messages are not pre-hashed,
+the algorithms of the signature scheme will perform more hashing internally.
+In particular, the signing algorithm needs two sequential hashing passes over the message,
+which means that the full message must necessarily be kept in memory during signing,
+and large messages entail a runtime penalty.<ref>Typically, messages of 56 bytes or longer enjoy a performance benefit from pre-hashing, assuming the speed of SHA256 inside the signing algorithm matches that of the pre-hashing done by the calling application.</ref>
+
+==== Domain Separation ====
+
+It is good cryptographic practice to use a key pair only for a single purpose.
+Nevertheless, there may be situations in which it may be desirable to use the same key pair in multiple contexts,
+i.e., to sign different types of messages within the same application
+or even messages in entirely different applications
+(e.g., a secret key may be used to sign Bitcoin transactions as well plain text messages).
+
+As a consequence, applications should ensure that a signed application message intended for one context is never deemed valid in a different context
+(e.g., a signed plain text message should never be misinterpreted as a signed Bitcoin transaction, because this could cause unintended loss of funds).
+This is called "domain separation" and it is typically realized by partitioning the message space.
+Even if key pairs are intended to be used only within a single context,
+domain separation is a good idea because it makes it easy to add more contexts later.
+
+As a best practice, we recommend applications to use exactly one of the following methods to pre-process application messages before passing it to the signature scheme:
+* Either, pre-hash the application message using ''hash<sub>name</sub>'', where ''name'' identifies the context uniquely (e.g., "foo-app/signed-bar"),
+* or prefix the actual message with a 33-byte string that identifies the context uniquely (e.g., the UTF-8 encoding of "foo-app/signed-bar", padded with null bytes to 33 bytes).
+
+As the two pre-processing methods yield different message sizes (32 bytes vs. at least 33 bytes), there is no risk of collision between them.
+
== Applications ==
There are several interesting applications beyond simple signatures.
@@ -248,6 +292,7 @@ The reference implementation is for demonstration purposes only and not to be us
To help implementors understand updates to this BIP, we keep a list of substantial changes.
* 2022-08: Fix function signature of lift_x in reference code
+* 2023-04: Allow messages of arbitrary size
== Footnotes ==
diff --git a/bip-0340/reference.py b/bip-0340/reference.py
index 162bb88..b327e0a 100644
--- a/bip-0340/reference.py
+++ b/bip-0340/reference.py
@@ -96,8 +96,6 @@ def pubkey_gen(seckey: bytes) -> bytes:
return bytes_from_point(P)
def schnorr_sign(msg: bytes, seckey: bytes, aux_rand: bytes) -> bytes:
- if len(msg) != 32:
- raise ValueError('The message must be a 32-byte array.')
d0 = int_from_bytes(seckey)
if not (1 <= d0 <= n - 1):
raise ValueError('The secret key must be an integer in the range 1..n-1.')
@@ -121,8 +119,6 @@ def schnorr_sign(msg: bytes, seckey: bytes, aux_rand: bytes) -> bytes:
return sig
def schnorr_verify(msg: bytes, pubkey: bytes, sig: bytes) -> bool:
- if len(msg) != 32:
- raise ValueError('The message must be a 32-byte array.')
if len(pubkey) != 32:
raise ValueError('The public key must be a 32-byte array.')
if len(sig) != 64:
diff --git a/bip-0340/test-vectors.csv b/bip-0340/test-vectors.csv
index a1a63e1..6723391 100644
--- a/bip-0340/test-vectors.csv
+++ b/bip-0340/test-vectors.csv
@@ -14,3 +14,7 @@ index,secret key,public key,aux_rand,message,signature,verification result,comme
12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is equal to field size
13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order
14,,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key is not a valid X coordinate because it exceeds the field size
+15,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,,71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A55895464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF0574E427AB63,TRUE,message of size 0 (added 2022-12)
+16,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,11,08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14564CEC2BACBF,TRUE,message of size 1 (added 2022-12)
+17,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,0102030405060708090A0B0C0D0E0F1011,5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B50AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C5566A97EA5A5,TRUE,message of size 17 (added 2022-12)
+18,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163ECA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894CFD249F22367,TRUE,message of size 100 (added 2022-12)
diff --git a/bip-0340/test-vectors.py b/bip-0340/test-vectors.py
index d1bf6b2..317f2ec 100644
--- a/bip-0340/test-vectors.py
+++ b/bip-0340/test-vectors.py
@@ -249,6 +249,20 @@ def vector14():
return (None, pubkey, None, msg, sig, "FALSE", "public key is not a valid X coordinate because it exceeds the field size")
+def varlen_vector(msg_int):
+ seckey = bytes_from_int(int(16 * "0340", 16))
+ pubkey = pubkey_gen(seckey)
+ aux_rand = bytes_from_int(0)
+ msg = msg_int.to_bytes((msg_int.bit_length() + 7) // 8, "big")
+ sig = schnorr_sign(msg, seckey, aux_rand)
+ comment = "message of size %d (added 2022-12)"
+ return (seckey, pubkey, aux_rand, msg, sig, "TRUE", comment % len(msg))
+
+vector15 = lambda : varlen_vector(0)
+vector16 = lambda : varlen_vector(0x11)
+vector17 = lambda : varlen_vector(0x0102030405060708090A0B0C0D0E0F1011)
+vector18 = lambda : varlen_vector(int(100 * "99", 16))
+
vectors = [
vector0(),
vector1(),
@@ -264,7 +278,11 @@ vectors = [
vector11(),
vector12(),
vector13(),
- vector14()
+ vector14(),
+ vector15(),
+ vector16(),
+ vector17(),
+ vector18(),
]
# Converts the byte strings of a test vector into hex strings
diff --git a/bip-0341.mediawiki b/bip-0341.mediawiki
index 392ad67..639cec6 100644
--- a/bip-0341.mediawiki
+++ b/bip-0341.mediawiki
@@ -152,7 +152,7 @@ Satisfying any of these conditions is sufficient to spend the output.
'''Initial steps''' The first step is determining what the internal key and the organization of the rest of the scripts should be. The specifics are likely application dependent, but here are some general guidelines:
* When deciding between scripts with conditionals (<code>OP_IF</code> etc.) and splitting them up into multiple scripts (each corresponding to one execution path through the original script), it is generally preferable to pick the latter.
* When a single condition requires signatures with multiple keys, key aggregation techniques like MuSig can be used to combine them into a single key. The details are out of scope for this document, but note that this may complicate the signing procedure.
-* If one or more of the spending conditions consist of just a single key (after aggregation), the most likely one should be made the internal key. If no such condition exists, it may be worthwhile adding one that consists of an aggregation of all keys participating in all scripts combined; effectively adding an "everyone agrees" branch. If that is inacceptable, pick as internal key a point with unknown discrete logarithm. One example of such a point is ''H = lift_x(0x0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)'' which is [https://github.com/ElementsProject/secp256k1-zkp/blob/11af7015de624b010424273be3d91f117f172c82/src/modules/rangeproof/main_impl.h#L16 constructed] by taking the hash of the standard uncompressed encoding of the [https://www.secg.org/sec2-v2.pdf secp256k1] base point ''G'' as X coordinate. In order to avoid leaking the information that key path spending is not possible it is recommended to pick a fresh integer ''r'' in the range ''0...n-1'' uniformly at random and use ''H + rG'' as internal key. It is possible to prove that this internal key does not have a known discrete logarithm with respect to ''G'' by revealing ''r'' to a verifier who can then reconstruct how the internal key was created.
+* If one or more of the spending conditions consist of just a single key (after aggregation), the most likely one should be made the internal key. If no such condition exists, it may be worthwhile adding one that consists of an aggregation of all keys participating in all scripts combined; effectively adding an "everyone agrees" branch. If that is inacceptable, pick as internal key a "Nothing Up My Sleeve" (NUMS) point, i.e., a point with unknown discrete logarithm. One example of such a point is ''H = lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)'' which is [https://github.com/ElementsProject/secp256k1-zkp/blob/11af7015de624b010424273be3d91f117f172c82/src/modules/rangeproof/main_impl.h#L16 constructed] by taking the hash of the standard uncompressed encoding of the [https://www.secg.org/sec2-v2.pdf secp256k1] base point ''G'' as X coordinate. In order to avoid leaking the information that key path spending is not possible it is recommended to pick a fresh integer ''r'' in the range ''0...n-1'' uniformly at random and use ''H + rG'' as internal key. It is possible to prove that this internal key does not have a known discrete logarithm with respect to ''G'' by revealing ''r'' to a verifier who can then reconstruct how the internal key was created.
* If the spending conditions do not require a script path, the output key should commit to an unspendable script path instead of having no script path. This can be achieved by computing the output key point as ''Q = P + int(hash<sub>TapTweak</sub>(bytes(P)))G''. <ref>'''Why should the output key always have a taproot commitment, even if there is no script path?'''
If the taproot output key is an aggregate of keys, there is the possibility for a malicious party to add a script path without being noticed by the other parties.
This allows to bypass the multiparty policy and to steal the coin.
diff --git a/bip-0345.mediawiki b/bip-0345.mediawiki
new file mode 100644
index 0000000..a6ead31
--- /dev/null
+++ b/bip-0345.mediawiki
@@ -0,0 +1,688 @@
+<pre>
+ BIP: 345
+ Layer: Consensus (soft fork)
+ Title: OP_VAULT
+ Author: James O'Beirne <vaults@au92.org>
+ Greg Sanders <gsanders87@gmail.com>
+ Anthony Towns <aj@erisian.com.au>
+ Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0345
+ Status: Draft
+ Type: Standards Track
+ Created: 2023-02-03
+ License: BSD-3-Clause
+ Post-History: 2023-01-09: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-January/021318.html [bitcoin-dev] OP_VAULT announcment
+ 2023-03-01: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-March/021510.html [bitcoin-dev] BIP for OP_VAULT
+</pre>
+
+
+== Introduction ==
+
+This BIP proposes two new tapscript opcodes that add consensus support for a specialized
+covenant: <code>OP_VAULT</code> and <code>OP_VAULT_RECOVER</code>. These opcodes, in conjunction with
+<code>OP_CHECKTEMPLATEVERIFY</code>
+([https://github.com/bitcoin/bips/blob/master/bip-0119.mediawiki BIP-0119]),
+allow users to enforce a delay period before designated coins may be spent to
+an arbitrary destination, with the exception of a prespecified "recovery" path.
+At any time prior to final withdrawal, the coins can be spent to the
+recovery path.
+
+=== Copyright ===
+
+This document is licensed under the 3-clause BSD license.
+
+
+=== Motivation ===
+
+The hazard of custodying Bitcoin is well-known. Users of Bitcoin must go to
+significant effort to secure their private keys, and hope that once provisioned
+their custody system does not yield to any number of evolving and
+persistent threats. Users have little means to intervene once a compromise is
+detected. This proposal introduces a mechanism that significantly
+mitigates the worst-case outcome of key compromise: coin loss.
+
+Introducing a way to intervene during unexpected spends allows users to
+incorporate highly secure key storage methods or unusual fallback strategies
+that are only exercised in the worst case, and which may otherwise be
+operationally prohibitive. The goal of this proposal is to make this strategy
+usable for custodians of any size with minimal complication.
+
+==== Example uses ====
+
+A common configuration for an individual custodying Bitcoin is "single
+signature and passphrase" using a hardware wallet. A user with such a
+configuration might be concerned about the risk associated with relying on a
+single manufacturer for key management, as well as physical access to the
+hardware.
+
+This individual can use <code>OP_VAULT</code> to make use of a highly secure
+key as the unlikely recovery path, while using their existing signing procedure
+as the withdrawal trigger key with a configured spend delay of e.g. 1 day.
+
+The recovery path key can be of a highly secure nature that might otherwise
+make it impractical for daily use. For example, the key could be generated in
+some analog fashion, or on an old computer that is then destroyed, with the
+private key replicated only in paper form. Or the key could be a 2-of-3
+multisig using devices from different manufacturers. Perhaps the key is
+geographically or socially distributed.
+
+Since it can be any Bitcoin script policy, the recovery key can include a
+number of spending conditions, e.g. a time-delayed fallback to an "easier"
+recovery method, in case the highly secure key winds up being ''too'' highly
+secure.
+
+The user can run software on their mobile device that monitors the blockchain
+for spends of the vault outpoints. If the vaulted coins move in an unexpected
+way, the user can immediately sweep them to the recovery path, but spending the
+coins on a daily basis works in the same way it did prior to vaulting (aside
+from the spend delay).
+
+Institutional custodians of Bitcoin may use vaults in similar fashion.
+
+===== Provable timelocks =====
+
+This proposal provides a mitigation to the
+[https://web.archive.org/web/20230210123933/https://xkcd.com/538/ "$5 wrench attack."] By
+setting the spend delay to, say, a week, and using as the recovery path a
+script that enforces a longer relative timelock, the owner of the vault can
+prove that he is unable to access its value immediately. To the author's
+knowledge, this is the only way to configure this defense without rolling
+timelocked coins for perpetuity or relying on a trusted third party.
+
+== Goals ==
+
+[[File:bip-0345/vaults-Basic.png|frame|center]]
+
+Vaults in Bitcoin have been discussed formally since 2016
+([http://fc16.ifca.ai/bitcoin/papers/MES16.pdf MES16]) and informally since [https://web.archive.org/web/20160220215151/https://bitcointalk.org/index.php?topic=511881.0 2014]. The value of
+having a configurable delay period with recovery capability in light of an
+unexpected spend has been widely recognized.
+
+The only way to implement vaults given the existing consensus rules, aside from
+[https://github.com/revault emulating vaults with large multisig
+configurations], is to use presigned transactions created with a one-time-use
+key. This approach was first demonstrated
+[https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-April/017755.html in 2020].
+
+Unfortunately, this approach has a number of practical shortcomings:
+* generating and securely deleting ephemeral keys, which are used to emulate the vault covenant, is required,
+* amounts and withdrawal patterns must be precommitted to,
+* there is a necessity to precommit to an address that the funds must pass through on their way to the final withdrawal target, which is likely only known at unvault time,
+* the particular fee management technique or wallet must be decided upon vault creation,
+* coin loss follows if a vault address is reused,
+* the transaction data that represents the "bearer asset" of the vault must be stored for perpetuity, otherwise value is lost, and
+* the vault creation ceremony must be performed each time a new balance is to be deposited.
+
+The deployment of a "precomputed" covenant mechanism like
+[https://github.com/bitcoin/bips/blob/master/bip-0119.mediawiki OP_CHECKTEMPLATEVERIFY] or
+[https://github.com/bitcoin/bips/blob/master/bip-0118.mediawiki SIGHASH_ANYPREVOUT]
+would both remove the necessity to use an ephemeral key, since the
+covenant is enforced on-chain, and lessen the burden of sensitive data storage,
+since the necessary transactions can be generated from a set of compact
+parameters. This approach was demonstrated [https://github.com/jamesob/simple-ctv-vault in
+2022].
+
+However, the limitations of precomputation still apply: amounts,
+destinations, and fee management are all fixed. Funds must flow through a fixed
+intermediary to their final destination. Batch operations, which may be vital
+for successful recovery during fee spikes or short spend delay, are not possible.
+
+[[File:bip-0345/withdrawal-comparison.drawio.png|frame|center]]
+
+Having a "general" covenant mechanism that can encode arbitrary transactional
+state machines would allow us to solve these issues, but at the cost of complex
+and large scripts that would probably be duplicated many times over in the
+blockchain. The particular design and deployment timeline of such a general
+framework is also uncertain. This approach was demonstrated
+[https://blog.blockstream.com/en-covenants-in-elements-alpha/ in 2016].
+
+This proposal intends to address the problems outlined above by
+providing a delay period/recovery path use with minimal transactional and
+operational overhead using a specialized covenant.
+
+The design goals of the proposal are:
+
+* '''efficient reuse of an existing vault configuration.'''<ref>'''Why does this support address reuse?''' The proposal doesn't rely on or encourage address reuse, but certain uses are unsafe if address reuse cannot be handled - for example, if a custodian gives its users a vault address to deposit to, it cannot enforce that those users make a single deposit for each address.</ref> A single vault configuration, whether the same literal <code>scriptPubKey</code> or not, should be able to “receive” multiple deposits.
+
+* '''batched operations''' for recovery and withdrawal to allow managing multiple vault coins efficiently.
+
+* '''unbounded partial withdrawals''', which allows users to withdraw partial vault balances without having to perform the setup ceremony for a new vault.
+
+* '''dynamic unvault targets''', which allow the proposed withdrawal target for a vault to be specified at withdrawal time rather than when the vault is first created. This would remove the need for a prespecified, intermediate wallet that only exists to route unvaulted funds to their desired destination.
+
+* '''dynamic fee management''' that, like dynamic targets, defers the specification of fee rates and source to unvault time rather than vault creation time.
+
+These goals are accompanied by basic safety considerations (e.g. not being
+vulnerable to mempool pinning) and a desire for concision, both in terms of the number
+of outputs created as well as script sizes.
+
+This proposal is designed to be compatible with any future sighash modes (e.g. <code>SIGHASH_GROUP</code>) or fee management strategies (e.g. [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-September/018168.html transaction sponsors]) that may be introduced. Use of these opcodes will benefit from, but do not strictly rely on, [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-September/020937.html v3 transaction relay] and [https://github.com/instagibbs/bips/blob/ephemeral_anchor/bip-ephemeralanchors.mediawiki ephemeral anchors].
+
+== Design ==
+
+In typical usage, a vault is created by encumbering coins under a
+taptree [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki (BIP-341)]
+containing at least two leaves: one with an <code>OP_VAULT</code>-containing script that
+facilitates the expected withdrawal process, and another leaf with
+<code>OP_VAULT_RECOVER</code> which ensures the coins can be recovered
+at any time prior to withdrawal finalization.
+
+The rules of <code>OP_VAULT</code> ensure the timelocked, interruptible
+withdrawal by allowing a spending transaction to replace the
+<code>OP_VAULT</code> tapleaf with a prespecified script template, allowing for
+some parameters to be set at spend (trigger) time. All other leaves in the
+taptree must be unchanged in the destination output, which preserves the recovery path as well as any
+other spending conditions originally included in the vault. This is similar to
+the <code>TAPLEAF_UPDATE_VERIFY</code> design that was proposed
+[https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-September/019419.html in 2021].
+
+These tapleaf replacement rules, described more precisely below, ensure a
+timelocked withdrawal, where the timelock is fixed by the original
+<code>OP_VAULT</code> parameters, to a fixed set of outputs (via
+<code>OP_CHECKTEMPLATEVERIFY</code><ref>'''Why is <code>OP_CHECKTEMPLATEVERIFY</code> (BIP-119) relied upon for this proposal?''' During the withdrawal process, the proposed final destination for value being withdrawn must be committed to. <code>OP_CTV</code> is the simplest, safest way to commit the spend of some coins to a particular set of outputs. An earlier version of this proposal attempted to use a simpler, but similar method, of locking the spend of coins to a set of outputs, but this method introduced txid malleability.<br />Note that if some other method of locking spends to a particular set of outputs should be deployed, that method can be used in the <code>OP_VAULT</code> <code><leaf-update-script-body></code> with no changes.</ref>) which is chosen when the withdrawal
+process is triggered.
+
+While <code>OP_CHECKTEMPLATEVERIFY</code> is used in this proposal as the
+preferred method to bind the proposed withdrawal to a particular set of final
+outputs, <code>OP_VAULT</code> is composable with other (and future) opcodes to
+facilitate other kinds of withdrawal processes.
+
+[[File:bip-0345/opvault.drawio.png|frame|center]]
+
+
+=== Transaction types ===
+
+The vault has a number of stages, some of them optional:
+
+* '''vault transaction''': encumbers some coins into a Taproot structure that includes at least one <code>OP_VAULT</code> leaf and one <code>OP_VAULT_RECOVER</code> leaf.
+
+* '''trigger transaction''': spends one or more <code>OP_VAULT</code>-tapleaf inputs into an output which is encumbered by a timelocked withdrawal to a fixed set of outputs, chosen at trigger time. This publicly broadcasts the intent to withdraw to some specific set of outputs.<br /><br />The trigger transaction may have an additional output which allocates some of the vault balance into a partial "revault," which simply encumbers the revaulted portion of the value into the same <code>scriptPubKey</code> as the <code>OP_VAULT</code>-containing input(s) being spent.
+
+* '''withdrawal transaction''': spends the timelocked, destination-locked trigger inputs into a compatible set of final withdrawal outputs (per, e.g., a <code>CHECKTEMPLATEVERIFY</code> hash), after the trigger inputs have matured per the spend delay. Timelocked CTV transactions are the motivating usage of OP_VAULT, but any script template can be specified during the creation of the vault.
+
+* '''recovery transaction''': spends one or more vault inputs via <code>OP_VAULT_RECOVER</code> tapleaf to the prespecified recovery path, which can be done at any point before the withdrawal transaction confirms. Each input can optionally require a witness satisfying a specified ''recovery authorization'' script, an optional script prefixing the <code>OP_VAULT_RECOVER</code> fragment. The use of recovery authorization has certain trade-offs discussed later.
+
+
+=== Fee management ===
+
+A primary consideration of this proposal is how fee management is handled.
+Providing dynamic fee management is critical to the operation of a vault, since
+
+* precalculated fees are prone to making transactions unconfirmable in high fee environments, and
+* a fee wallet that is prespecified might be compromised or lost before use.
+
+But dynamic fee management can introduce
+[https://bitcoinops.org/en/topics/transaction-pinning/ pinning vectors]. Care
+has been taken to avoid unnecessarily introducing these vectors when using the new
+destination-based spending policies that this proposal introduces.
+
+Originally, this proposal had a hard dependency on reformed transaction
+nVersion=3 policies, including ephemeral anchors, but it has since been revised
+to simply benefit from these changes in policy as well as other potential fee
+management mechanisms.
+
+
+== Specification ==
+
+The tapscript opcodes <code>OP_SUCCESS187</code> (<code>0xbb</code>) and
+<code>OP_SUCCESS188</code> (<code>0xbc</code>) are constrained with new rules
+to implement <code>OP_VAULT</code> and <code>OP_VAULT_RECOVER</code>,
+respectively.
+
+=== <code>OP_VAULT</code> evaluation ===
+
+When evaluating <code>OP_VAULT</code> (<code>OP_SUCCESS187</code>,
+<code>0xbb</code>), the expected format of the stack, shown top to bottom, is:
+
+<source>
+<leaf-update-script-body>
+<push-count>
+[ <push-count> leaf-update script data items ... ]
+<trigger-vout-idx>
+<revault-vout-idx>
+<revault-amount>
+</source>
+
+where
+
+* <code><leaf-update-script-body></code> is a minimally-encoded data push of a serialized script. <ref>In conjunction with the leaf-update data items, it dictates the tapleaf script in the output taptree that will replace the one currently executing.</ref>
+** Otherwise, script execution MUST fail and terminate immediately.
+
+* <code><push-count></code> is an up to 4-byte minimally encoded <code>CScriptNum</code> indicating how many leaf-update script items should be popped off the stack. <ref>'''Why only prefix with data pushes?''' Prefixing the <code>leaf-update-script-body</code> with opcodes opens up the door to prefix OP_SUCCESSX opcodes, to name a single issue only, side-stepping the validation that was meant to be run by the committed script.</ref>
+** If this value does not decode to a valid CScriptNum, script execution MUST fail and terminate immediately.
+** If this value is less than 0, script execution MUST fail and terminate immediately.
+** If there are fewer than 3 items following the <code><push-count></code> items on the stack, script execution MUST fail and terminate immediately. In other words, after popping <code><leaf-update-script-body></code>, there must be at least <code>3 + <push-count></code> items remaining on the stack.
+
+* The following <code><push-count></code> stack items are popped off the stack and prefixed as minimally-encoded push-data arguments to the <code><leaf-update-script-body></code> to construct the expected tapleaf replacement script.
+
+* <code><trigger-vout-idx></code> is an up to 4-byte minimally encoded <code>CScriptNum</code> indicating the index of the output which, in conjunction with an optional revault output, carries forward the value of this input, and has an identical taptree aside from the currently executing leaf.
+** If this value does not decode to a valid CScriptNum, script execution MUST fail and terminate immediately.
+** If this value is less than 0 or is greater than or equal to the number of outputs, script execution MUST fail and terminate immediately.
+
+* <code><revault-vout-idx></code> is an up to 4-byte minimally encoded <code>CScriptNum</code> optionally indicating the index of an output which, in conjunction with the trigger output, carries forward the value of this input, and has an identical scriptPubKey to the current input.
+** If this value does not decode to a valid CScriptNum, script execution MUST fail and terminate immediately.
+** If this value is greater than or equal to the number of outputs, script execution MUST fail and terminate immediately.
+** If this value is negative and not equal to -1, script execution MUST fail and terminate immediately.<ref>'''Why is -1 the only allowable negative value for revault-vout-idx?''' A negative revault index indicates that no revault output exists; if this value were allowed to be any negative number, the witness could be malleated (and bloated) while a transaction is waiting for confirmation.</ref>
+
+* <code><revault-amount></code> is an up to 7-byte minimally encoded CScriptNum indicating the number of satoshis being revaulted.
+** If this value does not decode to a valid CScriptNum, script execution MUST fail and terminate immediately.
+** If this value is not greater than or equal to 0, script execution MUST fail and terminate immediately.
+** If this value is non-zero but <code><revault-vout-idx></code> is negative, script execution MUST fail and terminate immediately.
+** If this value is zero but <code><revault-vout-idx></code> is not -1, script execution MUST fail and terminate immediately.
+
+After the stack is parsed, the following validation checks are performed:
+
+* Decrement the per-script sigops budget (see [https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#user-content-Resource_limits BIP-0342]) by 60<ref>'''Why is the sigops cost for OP_VAULT set to 60?''' To determine the validity of a trigger output, OP_VAULT must perform an EC multiplication and hashing proportional to the length of the control block in order to generate the output's expected TapTweak. This has been measured to have a cost in the worst case (max length control block) of roughly twice a Schnorr verification. Because the hashing cost could be mitigated by caching midstate, the cost is 60 and not 100.</ref>; if the budget is brought below zero, script execution MUST fail and terminate immediately.
+* Let the output designated by <code><trigger-vout-idx></code> be called ''triggerOut''.
+* If the scriptPubKey of ''triggerOut'' is not a version 1 witness program, script execution MUST fail and terminate immediately.
+* Let the script constructed by taking the <code><leaf-update-script-body></code> and prefixing it with minimally-encoded data pushes of the <code><push-count></code> leaf-update script data items be called the ''leaf-update-script''.
+* If the scriptPubKey of ''triggerOut'' does not match that of a taptree that is identical to that of the currently evaluated input, but with the leaf script substituted for ''leaf-update-script'', script execution MUST fail and terminate immediately.
+** Note: the parity bit of the resulting taproot output is allowed to vary, so both values for the new output must be checked.
+* Let the output designated by <code><revault-vout-idx></code> (if the index value is non-negative) be called ''revaultOut''.
+* If the scriptPubKey of ''revaultOut'' is not equal to the scriptPubKey of the input being spent, script execution MUST fail and terminate immediately.
+* Implementation recommendation: if the sum of the amounts of ''triggerOut'' and ''revaultOut'' (if any) are not greater than or equal to the value of this input, script execution SHOULD fail and terminate immediately. This ensures that (at a minimum) the vaulted value for this input is carried through.
+** Amount checks are ultimately done with deferred checks, but this check can help short-circuit obviously invalid spends.
+* Queue a deferred check<ref>'''What is a deferred check and why does this proposal require them for correct script evaluation?''' A deferred check is a validation check that is executed only after all input scripts have been validated, and is based on aggregate information collected during each input's EvalScript run.<br /><br />Currently, the validity of each input is (usually) checked concurrently across all inputs in a transaction. Because this proposal allows batching the spend of multiple vault inputs into a single recovery or withdrawal output, we need a mechanism to ensure that all expected values per output can be summed and then checked. This necessitates the introduction of an "aggregating" set of checks which can only be executed after each input's script is evaluated. Note that similar functionality would be required for batch input validation or cross-input signature aggregation.</ref> that ensures the satoshis for this input's <code>nValue</code> minus <code><revault-amount></code> are included within the output <code>nValue</code> found at <code><trigger-vout-idx></code>.
+* Queue a deferred check that ensures <code><revault-amount></code> satoshis, if non-zero, are included within the output's <code>nValue</code> found at <code><revault-vout-idx></code>.
+** These deferred checks could be characterized in terms of the pseudocode below (in ''Deferred checks'') as<br /><code>TriggerCheck(input_amount, <revault-amount>, <trigger-vout-idx>, <revault-vout-idx>)</code>.
+
+If none of the conditions fail, a single true value (<code>0x01</code>) is left on the stack.
+
+=== <code>OP_VAULT_RECOVER</code> evaluation ===
+
+When evaluating <code>OP_VAULT_RECOVER</code> (<code>OP_SUCCESS188</code>,
+<code>0xbb</code>), the expected format of the stack, shown top to bottom, is:
+
+<source>
+<recovery-sPK-hash>
+<recovery-vout-idx>
+</source>
+
+where
+
+* <code><recovery-sPK-hash></code> is a 32-byte data push.
+** If this is not 32 bytes in length, script execution MUST fail and terminate immediately.
+* <code><recovery-vout-idx></code> is an up to 4-byte minimally encoded <code>CScriptNum</code> indicating the index of the recovery output.
+** If this value does not decode to a valid CScriptNum, script execution MUST fail and terminate immediately.
+** If this value is less than 0 or is greater than or equal to the number of outputs, script execution MUST fail and terminate immediately.
+
+After the stack is parsed, the following validation checks are performed:
+
+* Let the output at index <code><recovery-vout-idx></code> be called ''recoveryOut''.
+* If the scriptPubKey of ''recoveryOut'' does not have a tagged hash equal to <code><recovery-sPK-hash></code> (<code>tagged_hash("VaultRecoverySPK", recoveryOut.scriptPubKey) == recovery-sPK-hash</code>, where <code>tagged_hash()</code> is from the [https://github.com/bitcoin/bips/blob/master/bip-0340/reference.py BIP-0340 reference code]), script execution MUST fail and terminate immediately.
+** Implementation recommendation: if ''recoveryOut'' does not have an <code>nValue</code> greater than or equal to this input's amount, the script SHOULD fail and terminate immediately.
+* Queue a deferred check that ensures the <code>nValue</code> of ''recoveryOut'' contains the entire <code>nValue</code> of this input.<ref>'''How do recovery transactions pay for fees?''' If the recovery is unauthorized, fees are attached either via CPFP with an ephemeral anchor or as inputs which are solely spent to fees (i.e. no change output). If the recovery is authorized, fees can be attached in any manner, e.g. unrelated inputs and outputs or CPFP via anchor.</ref>
+** This deferred check could be characterized in terms of the pseudocode below as <code>RecoveryCheck(<recovery-vout-idx>, input_amount)</code>.
+
+If none of the conditions fail, a single true value (<code>0x01</code>) is left on the stack.
+
+=== Deferred check evaluation ===
+
+Once all inputs for a transaction are validated per the rules above, any
+deferred checks queued MUST be evaluated.
+
+The Python pseudocode for this is as follows:
+
+<source lang="python">
+class TriggerCheck:
+ """Queued by evaluation of OP_VAULT (withdrawal trigger)."""
+ input_amount: int
+ revault_amount: int
+ trigger_vout_idx: int
+ revault_vout_idx: int
+
+
+class RecoveryCheck:
+ """Queued by evaluation of OP_VAULT_RECOVER."""
+ input_amount: int
+ vout_idx: int
+
+
+def validate_deferred_checks(checks: [DeferredCheck], tx: Transaction) -> bool:
+ """
+ Ensure that all value from vault inputs being triggered or recovered is preserved
+ in suitable output nValues.
+ """
+ # Map to hold expected output values.
+ out_map: Dict[int, int] = defaultdict(lambda: 0)
+
+ for c in checks:
+ if isinstance(c, TriggerCheck):
+ out_map[c.trigger_vout_idx] += (c.input_amount - c.revault_amount)
+
+ if c.revault_amount > 0:
+ out_map[c.revault_vout_idx] += c.revault_amount
+
+ elif isinstance(c, RecoveryCheck):
+ out_map[c.vout_idx] += c.input_amount
+
+ for (vout_idx, amount_sats) in out_map.items():
+ # Trigger/recovery value can be greater than the constituent vault input
+ # amounts.
+ if tx.vout[vout_idx].nValue < amount_sats:
+ return False
+
+ return True
+</source>
+
+If the above procedure, or an equivalent, returns false, script execution MUST fail and terminate
+immediately.
+
+This ensures that all compatible vault inputs can be batched into shared
+corresponding trigger or recovery outputs while preserving their entire input value.
+
+
+== Policy changes ==
+
+In order to prevent possible pinning attacks, recovery transactions must be replaceable.
+
+* When validating an <code>OP_VAULT_RECOVER</code> input being spent, the script MUST fail (by policy, not consensus) and terminate immediately if both<ref>'''Why are recovery transactions required to be replaceable?''' In the case of unauthorized recoveries, an attacker may attempt to pin recovery transactions by broadcasting a "rebundled" version with a low fee rate. Vault owners must be able to overcome this with replacement. In the case of authorized recovery, if an attacker steals the recovery authorization key, the attacker may try to pin the recovery transaction during theft. Requiring replaceability ensures that the owner can always raise the fee rate of the recovery transaction, even if they are RBF rule #3 griefed in the process.</ref>
+*# the input is not marked as opt-in replaceable by having an nSequence number less than <code>0xffffffff - 1</code>, per [https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki BIP-0125], and
+*# the version of the recovery transaction has an nVersion other than 3.
+
+If the script containing <code>OP_VAULT_RECOVER</code> is 34 bytes or less<ref>34 bytes is the length of a recovery script that consists solely of <code><recovery-sPK-hash> OP_VAULT_RECOVER</code>.</ref>, let
+it be called "unauthorized," because there is no script guarding the recovery
+process. In order to prevent pinning attacks in the case of unauthorized
+recovery - since the spend of the input (and the structure of the
+transaction) is not authorized by a signed signature message - the output structure of
+unauthorized recovery transaction is limited.
+
+* If the recovery is unauthorized, the recovery transaction MUST (by policy) abide by the following constraints:
+** If the spending transaction has more than two outputs, the script MUST fail and terminate immediately.
+** If the spending transaction has two outputs, and the output which is not ''recoveryOut'' is not an [https://github.com/instagibbs/bips/blob/ephemeral_anchor/bip-ephemeralanchors.mediawiki ephemeral anchor], the script MUST fail and terminate immediately.<ref>'''Why can unauthorized recoveries only process a single recovery path?''' Because there is no signature required for unauthorized recoveries, if additional outputs were allowed, someone observing a recovery in the mempool would be able to rebundle and broadcast the recovery with a lower fee rate.</ref>
+
+== Implementation ==
+
+A sample implementation is available on bitcoin-inquisition [https://github.com/jamesob/bitcoin/tree/2023-01-opvault-inq here], with an associated [https://github.com/bitcoin-inquisition/bitcoin/pull/21 pull request].
+
+
+== Applications ==
+
+The specification above, perhaps surprisingly, does not specifically cover how a relative timelocked withdrawal process with a fixed target is implemented. The tapleaf update semantics specified in <code>OP_VAULT</code> as well as the output-based authorization enabled by <code>OP_VAULT_RECOVER</code> can be used to implement a vault, but they are incomplete without two other pieces:
+
+* a way to enforce relative timelocks, like <code>OP_CHECKSEQUENCEVERIFY</code>, and
+* a way to enforce that proposed withdrawals are ultimately being spent to a precise set of outputs, like <code>OP_CHECKTEMPLATEVERIFY</code>.
+
+These two pieces are combined with the tapleaf update capabilities of
+<code>OP_VAULT</code> to create a vault, described below.
+
+=== Creating a vault ===
+
+In order to vault coins, they can be spent into a witness v1 <code>scriptPubKey</code>
+that contains a taptree of the form
+
+<source>
+tr(<internal-pubkey>,
+ leaves = {
+ recover:
+ <recovery-sPK-hash> OP_VAULT_RECOVER,
+
+ trigger:
+ <trigger-auth-pubkey> OP_CHECKSIGVERIFY (i)
+ <spend-delay> 2 $leaf-update-script-body OP_VAULT, (ii)
+
+ ... [ possibly other leaves ]
+ }
+)
+</source>
+where
+* <code>$leaf-update-script-body</code> is, for example, <code>OP_CHECKSEQUENCEVERIFY OP_DROP OP_CHECKTEMPLATEVERIFY</code>.
+** This is one example of a trigger script, but ''any'' script fragment can be used, allowing the creation of different types of vaults. For example, you could use <code>OP_CHECKSEQUENCEVERIFY OP_DROP OP_CHECKSIG</code> to do a time-delayed transfer of the coins to another key. This also future-proofs <code>OP_VAULT</code> for future scripting capabilities.
+* The script fragment in <code>(i)</code> is called the "trigger authorization," because it gates triggering the withdrawal. This can be done in whatever manner the wallet designer would like.
+* The script fragment in <code>(ii)</code> is the incomplete <code>OP_VAULT</code> invocation - it will be completed once the rest of the parameters (the CTV target hash, trigger vout index, and revault vout index) are provided by the trigger transaction witness.
+
+Typically, the internal key for the vault taproot output will be specified so
+that it is controlled by the same descriptor as the recovery path, which
+facilitates another (though probably unused) means of recovering the vault
+output to the recovery path. This has the potential advantage of recovering the
+coin without ever revealing it was a vault.
+
+Otherwise, the internal key can be chosen to be an unspendable NUMS point to
+force execution of the taptree contents.
+
+=== Triggering a withdrawal ===
+
+To make use of the vault, and spend it towards some output, we construct a spend
+of the above <code>tr()</code> output that simply replaces the "trigger" leaf with the
+full leaf-update script (in this case, a timelocked CTV script):
+
+<source>
+Witness stack:
+
+- <revault-amount>
+- <revault-vout-idx> (-1 if none)
+- <trigger-vout-idx>
+- <target-CTV-hash>
+- <trigger-auth-pubkey-signature>
+- [ "trigger" leaf script contents ]
+- [ taproot control block prompting a script-path spend to "trigger" leaf ]
+
+Output scripts:
+
+[
+ tr(<internal-pubkey>,
+ leaves = {
+ recover:
+ <recovery-sPK-hash> OP_VAULT_RECOVER, <-- unchanged
+
+ trigger:
+ <target-CTV-hash> <spend-delay>
+ OP_CHECKSEQUENCEVERIFY OP_DROP OP_CHECKTEMPLATEVERIFY <-- changed per the
+ leaf-update
+ rules of OP_VAULT
+ ... [ possibly other leaves ]
+ }
+ ),
+
+ [ optional revault output with the
+ same sPK as the original vault output ],
+]
+</source>
+
+<code>OP_VAULT</code> has allowed the taptree to be transformed so that the trigger leaf
+becomes a timelocked CTV script, which is what actually facilitates the announced
+withdrawal. The withdrawal is interruptible by the recovery path because the
+"recover" leaf is preserved exactly from the original taptree.
+
+Note that the CTV hash is specified at spend time using the witness stack, and
+"locked in" via the <code>OP_VAULT</code> spend rules which assert its existence in the output.
+
+The vault funds can be recovered at any time prior to the spend of the
+timelocked CTV script by way of a script-path spend using the "recover" leaf.
+
+
+=== Recovery authorization ===
+
+When configuring a vault, the user must decide if they want to have the
+recovery process gated by a script fragment prefixing the
+<code>OP_VAULT_RECOVER</code> instruction in the "recover" leaf. Its use
+entails trade-offs.
+
+==== Unauthorized recovery ====
+
+Unauthorized recovery simplifies vault use in that recovery never requires additional information aside from the location of the vault outpoints and the recovery path - the "authorization" is simply the reveal of the recovery path, i.e. the preimage of <code><recovery-sPK-hash></code>.
+
+But because this reveal is the only authorization necessary to spend the vault coins to recovery, the user must expect to recover all such vaults at once, since an observer can replay this recovery (provided they know the outpoints).
+
+Additionally, unauthorized recovery across multiple distinct recovery paths
+cannot be done in the same transaction, and fee control is more constrained:
+because the output structure is limited for unauthorized recovery, fee
+management relies either on inputs which are completely spent to fees or the
+use of the optional ephemeral anchor and package relay.
+
+These limitations are to avoid pinning attacks.
+
+==== Authorized recovery ====
+
+With authorized recovery, the user must keep track of an additional piece of information: how to solve the recovery authorization script fragment when recovery is required.
+
+If this key is lost, the user will be unable to initiate the recovery process for their coins. If an attacker obtains the recovery key, they may grief the user during the recovery process by constructing a low fee rate recovery transaction and broadcasting it (though they will not be able to pin because of the replaceability requirement on recovery transactions).
+
+However, authorized recovery configurations have significant benefits. Batched recoveries are possible for vaults with otherwise incompatible recovery parameters. Fee management is much more flexible, since authorized recovery transactions are "free form" and unrelated inputs and outputs can be added, potentially to handle fees.
+
+==== Recommendation: use a simple, offline recovery authorization key seed ====
+
+The benefits of batching and fee management that authorized recovery provides are significant. If the recovery authorization key falls into the hands of an attacker, the outcome is not catastrophic, whereas if the user loses their recovery authorization key as well as their trigger key, the result is likely coin loss. Consequently, the author's recommendation is to use a simple seed for the recovery authorization key that can be written down offline and replicated.
+
+Note that the recovery authorization key '''is not''' the recovery path key, and
+this is '''much different''' than any recommendation on how to generate the
+recovery path key itself.
+
+=== Address reuse and recovery ===
+
+When creating a vault, four factors affect the resulting P2TR address:
+# The internal pubkey (likely belonging to the recovery wallet)
+# The recovery leaf
+# The trigger leaf
+# Any other leaves that exist in the taptree
+
+The end user has the option of varying certain contents along descriptors in
+order to avoid reusing vault addresses without affecting key management, e.g.
+the trigger authorization pubkeys.
+
+Note that when using unauthorized recovery, the reveal of the
+recovery scriptPubKey will allow any observer to initiate the recovery process
+for any vault with matching recovery params, provided they are able to locate
+the vault outpoints. As a result, it is recommended to expect that
+'''all outputs sharing an identical unauthorized <code><recovery-sPK-hash></code> should be recovered together'''.
+
+This situation can be avoided with a comparable key management model by varying
+the generation of each vault's recovery scriptPubKey along a single descriptor,
+but note that this will prevent recovering multiple separate vaults into a single
+recovery output.
+
+Varying the internal pubkey will prevent batching the trigger of multiple vault
+inputs into a single trigger output; consequently it is recommended that users
+instead vary some component of the trigger leaf script if address reuse is
+undesirable. Users could vary the trigger pubkey along a descriptor, keeping
+the recovery path and internal-pubkey the same, which both avoids reusing
+addresses and allows batched trigger and recovery operations.
+
+==== Recommendation: generate new recovery addresses for new trigger keys ====
+
+If using unauthorized recovery, it is recommended that you do not share recovery scriptPubKeys
+across separate trigger keys. If one trigger key is compromised, that will necessitate the (unauthorized)
+recovery of all vaults with that trigger key, which will reveal the recovery path preimage. This
+means that an observer might be able to initiate recovery for vaults controlled by an uncompromised
+trigger key.
+
+==== Fee management ====
+
+Fees can be managed in a variety of ways, but it's worth noting that both
+trigger and recovery transactions must preserve the total value of vault
+inputs, so vaulted values cannot be repurposed to pay for fees. This does not
+apply to the withdrawal transaction, which can allocate value arbitrarily.
+
+In the case of vaults that use recovery authorization, all transactions can
+"bring their own fees" in the form of unrelated inputs and outputs. These
+transactions are also free to specify ephemeral anchors, once the related relay
+policies are deployed. This means that vaults using recovery authorization have
+no dependence on the deploy of v3 relay policy.
+
+For vaults using unauthorized recovery, the recovery
+transaction relies on the use of either fully-spent fee inputs or an ephemeral
+anchor output. This means that vaults which do not use recovery authorization
+are essentially dependent on v3 transaction relay policy being deployed.
+
+=== Batching ===
+
+==== During trigger ====
+
+<code>OP_VAULT</code> outputs with the same taptree, aside from slightly
+different trigger leaves, can be batched together in the same withdrawal
+process. Two "trigger" leaves are compatible if they have the same
+<code>OP_VAULT</code> arguments.
+
+Note that this allows the trigger authorization -- the script prefixing the
+<code>OP_VAULT</code> invocation -- to differ while still allowing batching.
+
+Trigger transactions can act on multiple incompatible <code>OP_VAULT</code>
+input sets, provided each set has a suitable associated ''triggerOut''
+output.
+
+Since <code>SIGHASH_DEFAULT</code> can be used to sign the trigger
+authorization, unrelated inputs and outputs can be included, possibly to
+facilitate fee management or the batch withdrawal of incompatible vaults.
+
+==== During withdrawal ====
+
+During final withdrawal, multiple trigger outputs can be used towards the same
+withdrawal transaction provided that they share identical
+<code><target-CTV-hash></code> parameters. This facilitates batched
+withdrawals.
+
+==== During recovery ====
+
+<code>OP_VAULT_RECOVER</code> outputs with the same <code><recovery-sPK-hash></code>
+can be recovered into the same output.
+
+Recovery-incompatible vaults which have authorized recovery can be recovered in
+the same transaction, so long as each set (grouped by
+<code><recovery-sPK-hash></code>) has an associated ''recoveryOut''. This allows
+unrelated recoveries to share common fee management.
+
+=== Watchtowers ===
+
+The value of vaults is contingent upon having monitoring in place that will
+alert the owner when unexpected spends are taking place. This can be done in a
+variety of ways, with varying degrees of automation and trust in the
+watchtower.
+
+In the maximum-trust case, the watchtower can be fully aware of all vaulted
+coins and has the means to initiate the recovery process if spends are not
+pre-reported to the watchtower.
+
+In the minimum-trust case, the user can supply a probabilistic filter of which
+coins they wish to monitor; the watchtower would then alert the user if any
+coins matching the filter move, and the user would be responsible for ignoring
+false positives and handling recovery initiation.
+
+=== Output descriptors ===
+
+Output descriptors for vault-related outputs will be covered in a subsequent BIP.
+
+== Deployment ==
+
+Activation mechanism is to be determined.
+
+This BIP should be deployed concurrently with BIP-0119 to enable full use of vaults.
+
+== Backwards compatibility ==
+
+<code>OP_VAULT</code> and <code>OP_VAULT_RECOVER</code> replace, respectively,
+the witness v1-only opcodes OP_SUCCESS187 and OP_SUCCESS188 with stricter
+verification semantics. Consequently, scripts using those opcodes which
+previously were valid will cease to be valid with this change.
+
+Stricter verification semantics for an OP_SUCCESSx opcode are a soft fork, so
+existing software will be fully functional without upgrade except for mining
+and block validation.
+
+Backwards compatibility considerations are very comparable to previous
+deployments for OP_CHECKSEQUENCEVERIFY and OP_CHECKLOCKTIMEVERIFY (see
+[https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki BIP-0065] and
+[https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki BIP-0112]).
+
+
+== Rationale ==
+
+<references />
+
+== References ==
+
+* [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2016-February/012470.html [bitcoin-dev] Bitcoin Vaults (2016)]
+* [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-February/015793.html [bitcoin-dev] Simple lock/unlock mechanism (2018)]
+* [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-April/017755.html [bitcoin-dev] On-chain vaults prototype (2020)]
+* [https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-September/019419.html [bitcoin-dev] TAPLEAF_UPDATE_VERIFY covenant opcode (2021)]
+* [https://arxiv.org/abs/2005.11776 Custody Protocols Using Bitcoin Vaults (2020)]
+* [https://jameso.be/vaults.pdf Vaults and Covenants (2023)]
+
+== Acknowledgements ==
+
+The author would like to thank
+
+* AJ Towns and Greg Sanders for discussion, numerous suggestions that improved the proposal, and advice.
+* Jeremy Rubin for inspiration, advice, and mentorship.
+* BL for discussion and insight.
+* John Moffett for early feedback and a test case demonstrating a recursive script evaluation attack.
+* Johan Halseth for providing conceptual review and pointing out a pinning attack.
+* Pieter Wuille for implementation advice.
diff --git a/bip-0345/opvault.drawio.png b/bip-0345/opvault.drawio.png
new file mode 100644
index 0000000..702189d
--- /dev/null
+++ b/bip-0345/opvault.drawio.png
Binary files differ
diff --git a/bip-0345/vaults-Basic.png b/bip-0345/vaults-Basic.png
new file mode 100644
index 0000000..591b633
--- /dev/null
+++ b/bip-0345/vaults-Basic.png
Binary files differ
diff --git a/bip-0345/vaults.drawio b/bip-0345/vaults.drawio
new file mode 100644
index 0000000..6f7fd4e
--- /dev/null
+++ b/bip-0345/vaults.drawio
@@ -0,0 +1,1113 @@
+<mxfile host="app.diagrams.net" modified="2023-03-23T20:50:16.927Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36" etag="MVPrlQq-FqlMbts0SwvB" version="21.1.0" type="device" pages="8">
+ <diagram id="qHG0FeF2aWp-aiau7VVg" name="Basic flow">
+ <mxGraphModel dx="2162" dy="1316" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="b8DSxFJpJzC5LI19bmsF-1" target="b8DSxFJpJzC5LI19bmsF-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-8" value="&lt;div&gt;Sign with trigger key&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="b8DSxFJpJzC5LI19bmsF-5" vertex="1" connectable="0">
+ <mxGeometry x="-0.3102" y="-1" relative="1" as="geometry">
+ <mxPoint x="1" y="6" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-6" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeColor=default;dashed=1;" parent="1" source="b8DSxFJpJzC5LI19bmsF-1" target="b8DSxFJpJzC5LI19bmsF-2" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-7" value="Reveal recovery &lt;br&gt;scriptPubKey&lt;br&gt;and (if applicable)&lt;br&gt;satisfy recovery &lt;br&gt;auth. script" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="b8DSxFJpJzC5LI19bmsF-6" vertex="1" connectable="0">
+ <mxGeometry x="-0.17" y="2" relative="1" as="geometry">
+ <mxPoint x="52" y="57" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-1" value="&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;&lt;b&gt;Vault transaction&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;i style=&quot;background-color: initial;&quot;&gt;&amp;lt;recovery-params&amp;gt;&lt;/i&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;spend-delay&lt;/i&gt;&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;trigger-sPK-hash&lt;/i&gt;&amp;gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b style=&quot;border-color: var(--border-color);&quot;&gt;OP_VAULT&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=middle;align=center;verticalAlign=middle;" parent="1" vertex="1">
+ <mxGeometry x="190" y="250" width="140" height="100" as="geometry" />
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-2" value="&lt;b&gt;Recovery transaction&lt;br&gt;&lt;/b&gt;&lt;br&gt;[outputs controlled &lt;br&gt;by recovery keys]" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="443" y="420" width="130" height="70" as="geometry" />
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-9" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;dashed=1;strokeColor=default;endArrow=none;endFill=0;rounded=1;" parent="1" source="b8DSxFJpJzC5LI19bmsF-3" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="400" y="300" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="iAfIXZV-x1gRYHwice2W-8" value="Recover from trigger,&lt;br style=&quot;font-size: 11px;&quot;&gt;before withdrawal&lt;br style=&quot;font-size: 11px;&quot;&gt;confirms" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=11;" parent="b8DSxFJpJzC5LI19bmsF-9" vertex="1" connectable="0">
+ <mxGeometry x="0.4001" y="-1" relative="1" as="geometry">
+ <mxPoint x="-13" y="19" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=default;" parent="1" source="b8DSxFJpJzC5LI19bmsF-3" target="b8DSxFJpJzC5LI19bmsF-10" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-12" value="&lt;div&gt;Wait &lt;i&gt;spend-delay&lt;/i&gt; blocks &lt;b&gt;&amp;amp;&amp;amp;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;outputs match target hash&lt;br&gt;&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="b8DSxFJpJzC5LI19bmsF-11" vertex="1" connectable="0">
+ <mxGeometry x="-0.302" y="2" relative="1" as="geometry">
+ <mxPoint x="1" y="4" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="oT6HpDHtKCBb9ui_6_kA-1" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.001;exitY=0.406;exitDx=0;exitDy=0;entryX=0.001;entryY=0.595;entryDx=0;entryDy=0;dashed=1;endArrow=classic;endFill=1;entryPerimeter=0;exitPerimeter=0;" parent="1" source="b8DSxFJpJzC5LI19bmsF-3" target="b8DSxFJpJzC5LI19bmsF-1" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <Array as="points">
+ <mxPoint x="90" y="441" />
+ <mxPoint x="90" y="310" />
+ </Array>
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="oT6HpDHtKCBb9ui_6_kA-2" value="Optional &lt;br&gt;partial-balance&lt;br&gt;revault" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="oT6HpDHtKCBb9ui_6_kA-1" vertex="1" connectable="0">
+ <mxGeometry x="0.1091" y="1" relative="1" as="geometry">
+ <mxPoint y="13" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-3" value="&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;Trigger transaction&lt;/span&gt;&lt;br&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;i style=&quot;background-color: initial;&quot;&gt;&lt;br&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i style=&quot;background-color: initial;&quot;&gt;&amp;lt;recovery-params&amp;gt;&lt;/i&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;spend-delay&lt;/i&gt;&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;target-outputs-hash&lt;/i&gt;&amp;gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b style=&quot;border-color: var(--border-color);&quot;&gt;OP_UNVAULT&lt;/b&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="190" y="400" width="140" height="100" as="geometry" />
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-10" value="&lt;b&gt;Withdrawal transaction&lt;br&gt;&lt;/b&gt;&lt;br&gt;[dynamically chosen target outputs]" style="rounded=1;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
+ <mxGeometry x="190" y="567" width="140" height="73" as="geometry" />
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;strokeColor=default;" parent="1" source="b8DSxFJpJzC5LI19bmsF-13" target="b8DSxFJpJzC5LI19bmsF-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="b8DSxFJpJzC5LI19bmsF-13" value="" style="points=[[0.145,0.145,0],[0.5,0,0],[0.855,0.145,0],[1,0.5,0],[0.855,0.855,0],[0.5,1,0],[0.145,0.855,0],[0,0.5,0]];shape=mxgraph.bpmn.event;html=1;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;align=center;perimeter=ellipsePerimeter;outlineConnect=0;aspect=fixed;outline=standard;symbol=general;rounded=1;" parent="1" vertex="1">
+ <mxGeometry x="245" y="200" width="30" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="BqwL7Yf8YW1r5e_O7xE0-1" value="" style="shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#4495D1;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;shape=mxgraph.veeam.time;" parent="1" vertex="1">
+ <mxGeometry x="158" y="512" width="30" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="BqwL7Yf8YW1r5e_O7xE0-2" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;" parent="1" vertex="1">
+ <mxGeometry x="183" y="358" width="15" height="33" as="geometry" />
+ </mxCell>
+ <mxCell id="oT6HpDHtKCBb9ui_6_kA-7" value="" style="endArrow=none;html=1;rounded=1;entryX=1;entryY=0.25;entryDx=0;entryDy=0;exitX=0;exitY=0.25;exitDx=0;exitDy=0;" parent="1" source="b8DSxFJpJzC5LI19bmsF-1" target="b8DSxFJpJzC5LI19bmsF-1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="190" y="280" as="sourcePoint" />
+ <mxPoint x="240" y="230" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="oT6HpDHtKCBb9ui_6_kA-8" value="" style="endArrow=none;html=1;rounded=1;entryX=1;entryY=0.25;entryDx=0;entryDy=0;exitX=0;exitY=0.25;exitDx=0;exitDy=0;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="190" y="427" as="sourcePoint" />
+ <mxPoint x="330" y="427" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="oT6HpDHtKCBb9ui_6_kA-9" value="" style="endArrow=none;html=1;rounded=1;entryX=1;entryY=0.25;entryDx=0;entryDy=0;exitX=0;exitY=0.25;exitDx=0;exitDy=0;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="190" y="596" as="sourcePoint" />
+ <mxPoint x="330" y="596" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="oT6HpDHtKCBb9ui_6_kA-10" value="" style="endArrow=none;html=1;rounded=1;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="443" y="446" as="sourcePoint" />
+ <mxPoint x="573" y="446" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="LweIh1WkpCqs_c0vHIex-1" value="" style="endArrow=none;html=1;rounded=1;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="393" y="236" as="sourcePoint" />
+ <mxPoint x="413" y="236" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="LweIh1WkpCqs_c0vHIex-2" value="Withdrawal path" style="text;strokeColor=none;align=left;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="415" y="221" width="90" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="LweIh1WkpCqs_c0vHIex-3" value="" style="endArrow=none;html=1;rounded=1;dashed=1;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="393" y="252" as="sourcePoint" />
+ <mxPoint x="413" y="252" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="LweIh1WkpCqs_c0vHIex-4" value="Optional path" style="text;strokeColor=none;align=left;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="416" y="237" width="90" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="iAfIXZV-x1gRYHwice2W-2" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=default;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="359" y="449.76" as="sourcePoint" />
+ <mxPoint x="369" y="449.76" as="targetPoint" />
+ <Array as="points">
+ <mxPoint x="359" y="449.76" />
+ </Array>
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="iAfIXZV-x1gRYHwice2W-4" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=default;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="399" y="299.76" as="sourcePoint" />
+ <mxPoint x="409" y="299.76" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="iAfIXZV-x1gRYHwice2W-5" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=default;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="150" y="440" as="sourcePoint" />
+ <mxPoint x="140" y="440" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="hQFg2SRqlWPJF2oUK6n1" name="Batch sweep">
+ <mxGraphModel dx="1430" dy="1768" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="DGUraX8pYsX29eg1CZX8-1" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="636" y="415" width="138" height="160" as="geometry" />
+ </mxCell>
+ <mxCell id="DGUraX8pYsX29eg1CZX8-2" value="" style="endArrow=none;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="DGUraX8pYsX29eg1CZX8-1" target="DGUraX8pYsX29eg1CZX8-1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="660" y="519.5" as="sourcePoint" />
+ <mxPoint x="710" y="469.5" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="DGUraX8pYsX29eg1CZX8-3" value="&lt;b style=&quot;background-color: initial;&quot;&gt;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
+ <mxGeometry x="722" y="440" width="100" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-11" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="380" y="300" width="138" height="122" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-12" value="" style="endArrow=none;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="IMLKYxiTQTyD-2dyPs5i-11" target="IMLKYxiTQTyD-2dyPs5i-11" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="398" y="352" as="sourcePoint" />
+ <mxPoint x="448" y="302" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-13" value="&lt;b style=&quot;background-color: initial;&quot;&gt;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
+ <mxGeometry x="460" y="313" width="100" height="83" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-16" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="400" y="499" width="115" height="138" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-17" value="" style="endArrow=none;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="435" y="637" as="sourcePoint" />
+ <mxPoint x="435" y="499" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-18" value="&lt;b style=&quot;background-color: initial;&quot;&gt;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
+ <mxGeometry x="457" y="516" width="100" height="101" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-27" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="165" y="420" width="138" height="140" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-28" value="" style="endArrow=none;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="201" y="560" as="sourcePoint" />
+ <mxPoint x="201" y="420" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-29" value="&lt;b style=&quot;background-color: initial;&quot;&gt;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
+ <mxGeometry x="230" y="437" width="100" height="101" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-30" value="&lt;p style=&quot;line-height: 40%;&quot;&gt;&lt;b&gt;scriptPubKey&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); line-height: 4.8px;&quot;&gt;&lt;i style=&quot;border-color: var(--border-color);&quot;&gt;&lt;b style=&quot;border-color: var(--border-color);&quot;&gt;&lt;font style=&quot;border-color: var(--border-color);&quot; color=&quot;#0066cc&quot;&gt;recov-hash&amp;nbsp;&lt;/font&gt;&lt;font style=&quot;border-color: var(--border-color);&quot; color=&quot;#00060d&quot;&gt;...&lt;/font&gt;&lt;/b&gt;&amp;nbsp;&lt;/i&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); line-height: 4.8px;&quot;&gt;&amp;nbsp; OP_VAULT&lt;/p&gt;&lt;p style=&quot;line-height: 40%;&quot;&gt;&lt;b style=&quot;background-color: initial;&quot;&gt;amount&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 40%;&quot;&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;a3&lt;/span&gt;&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="461" y="513.5" width="100" height="110" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-31" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="134" y="270" width="88" height="122" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-32" value="" style="endArrow=none;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="157" y="392" as="sourcePoint" />
+ <mxPoint x="157" y="270" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-33" value="&lt;b style=&quot;background-color: initial;&quot;&gt;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;" parent="1" vertex="1">
+ <mxGeometry x="180" y="282" width="100" height="101" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-34" value="&lt;p style=&quot;line-height: 40%;&quot;&gt;&lt;b&gt;scriptPubKey&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 40%;&quot;&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;&lt;i&gt;&lt;b style=&quot;&quot;&gt;&lt;font color=&quot;#0066cc&quot;&gt;recov-hash &lt;/font&gt;&lt;font color=&quot;#00060d&quot;&gt;...&lt;/font&gt;&lt;/b&gt;&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 40%;&quot;&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;&amp;nbsp; OP_VAULT&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 120%;&quot;&gt;&lt;b&gt;amount&lt;/b&gt;&lt;br&gt;a1&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="183" y="271" width="100" height="120" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-52" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.25;exitDx=0;exitDy=0;" parent="1" source="IMLKYxiTQTyD-2dyPs5i-38" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="280" y="333" as="targetPoint" />
+ <Array as="points">
+ <mxPoint x="340" y="333" />
+ </Array>
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-38" value="" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="340" y="319" width="90" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-39" value="&lt;p style=&quot;line-height: 40%;&quot;&gt;&lt;b&gt;witness&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 40%;&quot;&gt;[&lt;i&gt;trigger-key&amp;nbsp;&lt;/i&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 40%;&quot;&gt;&amp;nbsp; signature]&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="353" y="310" width="90" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="IMagvj_H5wSyhYbexlPS-2" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" target="IMLKYxiTQTyD-2dyPs5i-18" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="600" y="538" as="sourcePoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-54" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.25;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" target="IMLKYxiTQTyD-2dyPs5i-13" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="600" y="448.5" as="sourcePoint" />
+ <Array as="points">
+ <mxPoint x="580" y="449" />
+ <mxPoint x="580" y="355" />
+ </Array>
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IMagvj_H5wSyhYbexlPS-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.367;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" source="IMLKYxiTQTyD-2dyPs5i-43" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="600" y="491.5" as="sourcePoint" />
+ <mxPoint x="330" y="486.5" as="targetPoint" />
+ <Array as="points">
+ <mxPoint x="600" y="487" />
+ <mxPoint x="465" y="487" />
+ </Array>
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-43" value="Script-path reveal" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="600" y="473" width="90" height="35" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-20" value="&lt;p style=&quot;line-height: 40%;&quot;&gt;&lt;b&gt;scriptPubKey&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); line-height: 4.8px;&quot;&gt;&lt;i style=&quot;border-color: var(--border-color);&quot;&gt;&lt;b style=&quot;border-color: var(--border-color);&quot;&gt;&lt;font style=&quot;border-color: var(--border-color);&quot; color=&quot;#0066cc&quot;&gt;recov-hash&amp;nbsp;&lt;/font&gt;&lt;font style=&quot;border-color: var(--border-color);&quot; color=&quot;#00060d&quot;&gt;...&lt;/font&gt;&lt;/b&gt;&amp;nbsp;&lt;/i&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); line-height: 4.8px;&quot;&gt;&lt;span style=&quot;border-color: var(--border-color); background-color: initial;&quot;&gt;&amp;nbsp; OP_VAULT&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 120%;&quot;&gt;&lt;b&gt;amount&lt;/b&gt;&lt;br&gt;a2&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="233" y="430" width="100" height="120" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-53" value="" style="group" parent="1" vertex="1" connectable="0">
+ <mxGeometry x="465" y="307.5" width="120" height="95" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-15" value="&lt;p style=&quot;line-height: 10%;&quot;&gt;&lt;b&gt;scriptPubKey&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); line-height: 4.8px;&quot;&gt;&lt;i style=&quot;border-color: var(--border-color);&quot;&gt;&lt;b style=&quot;border-color: var(--border-color);&quot;&gt;&lt;font style=&quot;border-color: var(--border-color);&quot; color=&quot;#0066cc&quot;&gt;recov-hash&amp;nbsp;&lt;/font&gt;&lt;font style=&quot;border-color: var(--border-color);&quot; color=&quot;#00060d&quot;&gt;...&lt;/font&gt;&lt;/b&gt;&amp;nbsp;&lt;/i&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); line-height: 4.8px;&quot;&gt;&lt;span style=&quot;border-color: var(--border-color); background-color: initial;&quot;&gt;&amp;nbsp; OP_UNVAULT&lt;/span&gt;&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="IMLKYxiTQTyD-2dyPs5i-53" vertex="1">
+ <mxGeometry width="110" height="70" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-26" value="&lt;p style=&quot;line-height: 10%;&quot;&gt;&lt;b&gt;amount&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 10%;&quot;&gt;a1&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="IMLKYxiTQTyD-2dyPs5i-53" vertex="1">
+ <mxGeometry y="45" width="70" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-60" value="" style="group" parent="1" vertex="1" connectable="0">
+ <mxGeometry x="730" y="438" width="100" height="82" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-58" value="&lt;p style=&quot;line-height: 20%;&quot;&gt;&lt;b&gt;scriptPubKey&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 20%;&quot;&gt;&lt;i&gt;[recovery-spk]&lt;/i&gt;&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="IMLKYxiTQTyD-2dyPs5i-60" vertex="1">
+ <mxGeometry width="100" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-59" value="&lt;p style=&quot;line-height: 10%;&quot;&gt;&lt;b&gt;amount&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 10%;&quot;&gt;a1 + a2 + a3&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="IMLKYxiTQTyD-2dyPs5i-60" vertex="1">
+ <mxGeometry y="32" width="90" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-61" value="&lt;i&gt;Ephemeral anchor&lt;/i&gt;" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="723.5" y="527" width="97" height="35" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-62" value="Recovered to interrupt unvault" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontSize=17;" parent="1" vertex="1">
+ <mxGeometry x="277" y="268.5" width="250" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-63" value="Recovered while still vaulted" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontSize=17;" parent="1" vertex="1">
+ <mxGeometry x="139" y="571" width="240" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IMLKYxiTQTyD-2dyPs5i-64" value="Batch recovery" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontSize=17;" parent="1" vertex="1">
+ <mxGeometry x="635" y="380.5" width="140" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IMagvj_H5wSyhYbexlPS-9" value="output" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="723" y="273" width="90" height="21" as="geometry" />
+ </mxCell>
+ <mxCell id="IMagvj_H5wSyhYbexlPS-10" value="optional output" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="723" y="302" width="90" height="21" as="geometry" />
+ </mxCell>
+ <mxCell id="IMagvj_H5wSyhYbexlPS-11" value="input" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="723" y="332" width="90" height="22" as="geometry" />
+ </mxCell>
+ <mxCell id="prdVbKwsFvf7KEGo0tpI-2" value="Script-path reveal" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="600" y="515" width="90" height="35" as="geometry" />
+ </mxCell>
+ <mxCell id="T7j29g-1OFRtJtuNoE9x-1" value="Script-path reveal" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="600" y="434" width="90" height="35" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="x3-0X1WiPTrt-eOLsWqB" name="Recovery comparison">
+ <mxGraphModel dx="1236" dy="1768" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-1" target="QagaKE3Mm4n1A5BtnNWS-2" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-1" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="120" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-2" target="QagaKE3Mm4n1A5BtnNWS-5" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-2" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="200" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-4" value="&lt;div&gt;Presigned&lt;/div&gt;&lt;div&gt;vault&lt;br&gt;&lt;/div&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="180" y="200" width="80" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-5" target="QagaKE3Mm4n1A5BtnNWS-8" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-5" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="280" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-7" value="Unvault" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="265" y="205" width="70" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-8" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="360" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-10" value="To recovery" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="335" y="205" width="90" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-12" target="QagaKE3Mm4n1A5BtnNWS-14" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-12" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="120" y="299" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-14" target="QagaKE3Mm4n1A5BtnNWS-16" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-14" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="200" y="299" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-16" target="QagaKE3Mm4n1A5BtnNWS-17" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-16" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="280" y="299" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-17" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="360" y="299" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-21" value="" style="group" parent="1" vertex="1" connectable="0">
+ <mxGeometry x="240" y="260" width="40" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-18" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-21" vertex="1">
+ <mxGeometry width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-19" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-21" vertex="1">
+ <mxGeometry y="10" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-20" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-21" vertex="1">
+ <mxGeometry y="20" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-22" value="&lt;b&gt;Precomputed vaults&lt;/b&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="210" y="168" width="140" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-24" target="QagaKE3Mm4n1A5BtnNWS-26" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-24" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="480" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-26" target="QagaKE3Mm4n1A5BtnNWS-29" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-26" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="560" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-27" value="&lt;div&gt;OP_VAULT&lt;/div&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Courier New;" parent="1" vertex="1">
+ <mxGeometry x="540" y="205" width="80" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-29" target="QagaKE3Mm4n1A5BtnNWS-31" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-29" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="640" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-30" value="OP_UNVAULT" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Courier New;" parent="1" vertex="1">
+ <mxGeometry x="615" y="205" width="90" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-31" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="720" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-32" value="To recovery" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="695" y="205" width="90" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-34" target="QagaKE3Mm4n1A5BtnNWS-36" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-34" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="480" y="299" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-36" target="QagaKE3Mm4n1A5BtnNWS-38" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-36" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="560" y="299" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-37" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-38" target="QagaKE3Mm4n1A5BtnNWS-31" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="720" y="342" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-38" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="640" y="299" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-40" value="" style="group" parent="1" vertex="1" connectable="0">
+ <mxGeometry x="600" y="260" width="40" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-41" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-40" vertex="1">
+ <mxGeometry width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-42" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-40" vertex="1">
+ <mxGeometry y="10" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-43" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-40" vertex="1">
+ <mxGeometry y="20" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-44" value="&lt;b&gt;OP_VAULT&lt;/b&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Courier New;" parent="1" vertex="1">
+ <mxGeometry x="600" y="168" width="80" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-45" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-46" target="QagaKE3Mm4n1A5BtnNWS-48" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-46" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="120" y="449" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-47" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-48" target="QagaKE3Mm4n1A5BtnNWS-51" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-48" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="200" y="449" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-49" value="&lt;div&gt;Presigned&lt;/div&gt;&lt;div&gt;vault&lt;br&gt;&lt;/div&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="180" y="409" width="80" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-51" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="280" y="449" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-52" value="To recovery" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="255" y="414" width="90" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-55" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-56" target="QagaKE3Mm4n1A5BtnNWS-58" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-56" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="120" y="503" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-57" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-58" target="QagaKE3Mm4n1A5BtnNWS-60" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-58" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="200" y="503" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-60" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="280" y="503" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-62" value="" style="group" parent="1" vertex="1" connectable="0">
+ <mxGeometry x="238" y="467" width="40" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-63" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-62" vertex="1">
+ <mxGeometry width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-64" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-62" vertex="1">
+ <mxGeometry y="10" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-65" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-62" vertex="1">
+ <mxGeometry y="20" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-66" value="&lt;b&gt;Precomputed vaults&lt;/b&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="210" y="377" width="140" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-67" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-68" target="QagaKE3Mm4n1A5BtnNWS-70" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-68" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="480" y="449" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-69" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-70" target="QagaKE3Mm4n1A5BtnNWS-73" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-70" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="560" y="449" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-71" value="&lt;div&gt;OP_VAULT&lt;/div&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Courier New;" parent="1" vertex="1">
+ <mxGeometry x="540" y="412" width="80" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-73" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="640" y="449" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-76" value="To recovery" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="615" y="412" width="90" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-77" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-78" target="QagaKE3Mm4n1A5BtnNWS-80" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-78" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="480" y="503" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-79" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="QagaKE3Mm4n1A5BtnNWS-80" target="QagaKE3Mm4n1A5BtnNWS-73" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="640" y="551" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-80" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="560" y="503" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-83" value="" style="group" parent="1" vertex="1" connectable="0">
+ <mxGeometry x="517" y="468" width="40" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-84" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-83" vertex="1">
+ <mxGeometry width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-85" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-83" vertex="1">
+ <mxGeometry y="10" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-86" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="QagaKE3Mm4n1A5BtnNWS-83" vertex="1">
+ <mxGeometry y="20" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QagaKE3Mm4n1A5BtnNWS-87" value="&lt;b&gt;OP_VAULT&lt;/b&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Courier New;" parent="1" vertex="1">
+ <mxGeometry x="600" y="377" width="80" height="30" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="51t3zBxVp8Nxi1LOdNUq" name="Withdrawal comparison">
+ <mxGraphModel dx="1430" dy="1768" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-2" target="ezkKjIhg79-38QoQ9XiY-4" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-2" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="120" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-4" target="ezkKjIhg79-38QoQ9XiY-7" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-4" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="190" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-5" value="&lt;div&gt;Presigned&lt;/div&gt;&lt;div&gt;vault&lt;br&gt;&lt;/div&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="167" y="198" width="80" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-7" target="ezkKjIhg79-38QoQ9XiY-9" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-7" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="260" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-8" value="Unvault" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="243" y="202" width="70" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-78" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-9" target="ezkKjIhg79-38QoQ9XiY-77" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-9" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="335" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-10" value="&lt;div&gt;&quot;Warm&quot; &lt;br&gt;&lt;/div&gt;&lt;div&gt;wallet&lt;br&gt;&lt;/div&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="322" y="199" width="60" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-12" target="ezkKjIhg79-38QoQ9XiY-14" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-12" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="120" y="322" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-14" target="ezkKjIhg79-38QoQ9XiY-16" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-14" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="190" y="322" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-16" target="ezkKjIhg79-38QoQ9XiY-17" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-16" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="260" y="322" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-90" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontFamily=Helvetica;" parent="1" source="ezkKjIhg79-38QoQ9XiY-17" target="ezkKjIhg79-38QoQ9XiY-80" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-17" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="335" y="322" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-18" value="" style="group" parent="1" vertex="1" connectable="0">
+ <mxGeometry x="220" y="270" width="40" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-19" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="ezkKjIhg79-38QoQ9XiY-18" vertex="1">
+ <mxGeometry width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-20" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="ezkKjIhg79-38QoQ9XiY-18" vertex="1">
+ <mxGeometry y="10" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-21" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="ezkKjIhg79-38QoQ9XiY-18" vertex="1">
+ <mxGeometry y="20" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-22" value="&lt;b&gt;Precomputed vaults&lt;/b&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
+ <mxGeometry x="210" y="158" width="140" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-24" target="ezkKjIhg79-38QoQ9XiY-26" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-24" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="519" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-26" target="ezkKjIhg79-38QoQ9XiY-29" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-26" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="599" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-27" value="&lt;div&gt;OP_VAULT&lt;/div&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Courier New;" parent="1" vertex="1">
+ <mxGeometry x="579" y="203" width="80" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-29" target="ezkKjIhg79-38QoQ9XiY-31" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-89" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0;entryDx=0;entryDy=0;fontFamily=Helvetica;" parent="1" target="ezkKjIhg79-38QoQ9XiY-83" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="740" y="260" as="sourcePoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-29" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="690" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-31" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="769" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ezkKjIhg79-38QoQ9XiY-34" target="ezkKjIhg79-38QoQ9XiY-36" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-34" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="519" y="322" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="ezkKjIhg79-38QoQ9XiY-36" target="ezkKjIhg79-38QoQ9XiY-29" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="660" y="260" as="targetPoint" />
+ <Array as="points">
+ <mxPoint x="660" y="342" />
+ <mxPoint x="660" y="260" />
+ </Array>
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-36" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="599" y="322" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-39" value="" style="group" parent="1" vertex="1" connectable="0">
+ <mxGeometry x="559" y="270" width="40" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-40" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="ezkKjIhg79-38QoQ9XiY-39" vertex="1">
+ <mxGeometry width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-41" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="ezkKjIhg79-38QoQ9XiY-39" vertex="1">
+ <mxGeometry y="10" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-42" value="" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" parent="ezkKjIhg79-38QoQ9XiY-39" vertex="1">
+ <mxGeometry y="20" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-43" value="&lt;b&gt;OP_VAULT&lt;/b&gt;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Courier New;" parent="1" vertex="1">
+ <mxGeometry x="639" y="158" width="80" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-77" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="414" y="240" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-80" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="414" y="322" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-83" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="769" y="290" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-84" value="Targets" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Helvetica;" parent="1" vertex="1">
+ <mxGeometry x="401" y="202" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="ezkKjIhg79-38QoQ9XiY-85" value="Targets" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Helvetica;" parent="1" vertex="1">
+ <mxGeometry x="758" y="203" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="hU3LCPZSUflRbzsWwJJh-1" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="1" vertex="1">
+ <mxGeometry x="462" y="170" width="20" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="hU3LCPZSUflRbzsWwJJh-2" value="=&amp;nbsp; UTXO" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;" parent="1" vertex="1">
+ <mxGeometry x="482" y="166" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="zbme-GfWksCJWwraIK2J-1" value="&quot;Trigger&quot;" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;fontFamily=Helvetica;" vertex="1" parent="1">
+ <mxGeometry x="673" y="203" width="70" height="30" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="M-0T8bRLORY_nlIivCLq" name="Alt-vaults">
+ <mxGraphModel dx="1236" dy="1160" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-1" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="-zpKa_FQ8lR9X4kw_iqW-5" target="-zpKa_FQ8lR9X4kw_iqW-9" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-2" value="&lt;div&gt;Sign with unvault key&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="-zpKa_FQ8lR9X4kw_iqW-1" vertex="1" connectable="0">
+ <mxGeometry x="-0.3102" y="-1" relative="1" as="geometry">
+ <mxPoint x="1" y="6" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeColor=default;dashed=1;" parent="1" source="-zpKa_FQ8lR9X4kw_iqW-5" target="-zpKa_FQ8lR9X4kw_iqW-6" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-4" value="Reveal cold address" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="-zpKa_FQ8lR9X4kw_iqW-3" vertex="1" connectable="0">
+ <mxGeometry x="-0.17" y="2" relative="1" as="geometry">
+ <mxPoint x="6" y="-8" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-5" value="&lt;div&gt;&lt;b&gt;OP_VAULT&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;cold-addr-hash&lt;/i&gt;&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;spend-delay&lt;/i&gt;&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;unvault-pk&lt;/i&gt;&amp;gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=middle;align=center;verticalAlign=middle;" parent="1" vertex="1">
+ <mxGeometry x="190" y="270" width="140" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-6" value="[recovery path]" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="460" y="285" width="120" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=default;" parent="1" source="-zpKa_FQ8lR9X4kw_iqW-9" target="-zpKa_FQ8lR9X4kw_iqW-10" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-8" value="&lt;div&gt;Wait &lt;i&gt;spend-delay&lt;/i&gt; blocks &lt;b&gt;&amp;amp;&amp;amp;&lt;/b&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;outputs match target hash&lt;br&gt;&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="-zpKa_FQ8lR9X4kw_iqW-7" vertex="1" connectable="0">
+ <mxGeometry x="-0.302" y="2" relative="1" as="geometry">
+ <mxPoint y="5" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-9" value="&lt;div&gt;&lt;b&gt;OP_UNVAULT&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;cold-addr-hash&lt;/i&gt;&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;spend-delay&lt;/i&gt;&amp;gt;&lt;/div&gt;&lt;div&gt;&amp;lt;&lt;i&gt;target-outputs-hash&lt;/i&gt;&amp;gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="190" y="400" width="140" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-10" value="[arbitrary unvault target]" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="520" y="360" width="140" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;strokeColor=default;" parent="1" source="-zpKa_FQ8lR9X4kw_iqW-12" target="-zpKa_FQ8lR9X4kw_iqW-5" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-12" value="" style="points=[[0.145,0.145,0],[0.5,0,0],[0.855,0.145,0],[1,0.5,0],[0.855,0.855,0],[0.5,1,0],[0.145,0.855,0],[0,0.5,0]];shape=mxgraph.bpmn.event;html=1;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;align=center;perimeter=ellipsePerimeter;outlineConnect=0;aspect=fixed;outline=standard;symbol=general;rounded=1;" parent="1" vertex="1">
+ <mxGeometry x="245" y="220" width="30" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-13" value="" style="shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#4495D1;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;shape=mxgraph.veeam.time;" parent="1" vertex="1">
+ <mxGeometry x="505" y="425" width="30" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-14" value="" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.security.key_permissions;" parent="1" vertex="1">
+ <mxGeometry x="170" y="360" width="15" height="33" as="geometry" />
+ </mxCell>
+ <mxCell id="-zpKa_FQ8lR9X4kw_iqW-15" value="" style="endArrow=none;dashed=1;html=1;rounded=0;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="330" y="420" as="sourcePoint" />
+ <mxPoint x="390" y="310" as="targetPoint" />
+ <Array as="points">
+ <mxPoint x="390" y="420" />
+ </Array>
+ </mxGeometry>
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="9IyR_zxcH8IqVGAvo76N" name="Basic">
+ <mxGraphModel dx="1236" dy="1768" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="uh7-YCnJg2CyufrYdqnU-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" source="uh7-YCnJg2CyufrYdqnU-1" target="uh7-YCnJg2CyufrYdqnU-2" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="uh7-YCnJg2CyufrYdqnU-7" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" source="uh7-YCnJg2CyufrYdqnU-1" target="uh7-YCnJg2CyufrYdqnU-4" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="uh7-YCnJg2CyufrYdqnU-1" value="&lt;div&gt;User spends UTXO(s)&lt;br&gt;&lt;/div&gt;&lt;div&gt;into vault&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=middle;align=center;verticalAlign=middle;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" vertex="1">
+ <mxGeometry x="90" y="310" width="140" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="uh7-YCnJg2CyufrYdqnU-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" source="uh7-YCnJg2CyufrYdqnU-2" target="uh7-YCnJg2CyufrYdqnU-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="KdfuowOYJgZ3zCW8n2jm-2" value="&lt;div&gt;After some&lt;/div&gt;&lt;div&gt;delay&lt;br&gt;&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=1;points=[];movable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="uh7-YCnJg2CyufrYdqnU-6" vertex="1" connectable="0">
+ <mxGeometry x="-0.2" y="-1" relative="1" as="geometry">
+ <mxPoint x="15" y="-1" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="uh7-YCnJg2CyufrYdqnU-10" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" source="uh7-YCnJg2CyufrYdqnU-2" target="uh7-YCnJg2CyufrYdqnU-4" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="uh7-YCnJg2CyufrYdqnU-2" value="&lt;div&gt;Unvault attempt&lt;br&gt;&lt;/div&gt;&lt;div&gt;is triggered&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=middle;align=center;verticalAlign=middle;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" vertex="1">
+ <mxGeometry x="260" y="310" width="140" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="uh7-YCnJg2CyufrYdqnU-3" value="&lt;div&gt;Withdrawal is finalized&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=middle;align=center;verticalAlign=middle;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" vertex="1">
+ <mxGeometry x="500" y="310" width="140" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="uh7-YCnJg2CyufrYdqnU-4" value="&lt;div&gt;Vaulted coins &lt;br&gt;&lt;/div&gt;&lt;div&gt;swept to prespecified&lt;/div&gt;&lt;div&gt;recovery path&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=middle;align=center;verticalAlign=middle;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" vertex="1">
+ <mxGeometry x="340" y="230" width="140" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="KdfuowOYJgZ3zCW8n2jm-1" value="" style="shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#4495D1;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;shape=mxgraph.veeam.time;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" vertex="1">
+ <mxGeometry x="446" y="360" width="20" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="4beDPwlwmHm2dIG_X1VO-2" value="" style="endArrow=none;html=1;rounded=1;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="520" y="259" as="sourcePoint" />
+ <mxPoint x="540" y="259" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="4beDPwlwmHm2dIG_X1VO-3" value="Expected path" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" vertex="1">
+ <mxGeometry x="542" y="244" width="90" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="4beDPwlwmHm2dIG_X1VO-4" value="" style="endArrow=none;html=1;rounded=1;dashed=1;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="520" y="275" as="sourcePoint" />
+ <mxPoint x="540" y="275" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="4beDPwlwmHm2dIG_X1VO-5" value="Recovery path" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" parent="1" vertex="1">
+ <mxGeometry x="543" y="260" width="90" height="30" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="jRtaY6zHFwBRHzIYjIhC" name="Page-7">
+ <mxGraphModel dx="2162" dy="1316" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-56" value="" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;strokeColor=#4D4D4D;fontFamily=Courier New;fontSize=20;" parent="1" vertex="1">
+ <mxGeometry x="308" y="660" width="232" height="100" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-50" value="" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;strokeColor=#4D4D4D;fontFamily=Courier New;fontSize=20;" parent="1" vertex="1">
+ <mxGeometry x="57" y="380" width="413" height="230" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-48" value="" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;strokeColor=#4D4D4D;fontFamily=Courier New;fontSize=20;" parent="1" vertex="1">
+ <mxGeometry x="590" y="380" width="230" height="221" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-46" value="" style="rounded=1;whiteSpace=wrap;html=1;strokeColor=#4D4D4D;fontFamily=Courier New;fontSize=8;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="182" y="102.5" width="509" height="183.5" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-19" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="445" y="169.5" width="120" height="100" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-2" value="&amp;nbsp;[trigger auth]&amp;nbsp;&lt;i&gt;&amp;lt;spend-delay&amp;gt;&lt;/i&gt; 2 &quot;OP_CSV OP_DROP OP_CTV&quot; &lt;b&gt;OP_VAULT&amp;nbsp;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=right;fontFamily=Courier New;verticalAlign=bottom;strokeColor=#336600;" parent="1" vertex="1">
+ <mxGeometry x="199" y="187.5" width="471" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-20" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="288" y="488" width="120" height="100" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-21" value="&amp;nbsp;&lt;i&gt; &lt;font color=&quot;#994c00&quot;&gt;&amp;lt;CTV-hash&amp;gt;&lt;/font&gt;&amp;nbsp;&amp;lt;spend-delay&amp;gt;&lt;/i&gt;&amp;nbsp;OP_CSV OP_DROP OP_CTV&amp;nbsp;" style="rounded=1;whiteSpace=wrap;html=1;align=right;fontFamily=Courier New;verticalAlign=bottom;strokeColor=#B01E1E;" parent="1" vertex="1">
+ <mxGeometry x="80" y="508" width="380" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-22" value="[recovery auth]&lt;i&gt; &amp;lt;recovery-sPK-hash&amp;gt;&lt;/i&gt; &lt;b&gt;OP_VAULT_RECOVER&amp;nbsp;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=right;fontFamily=Courier New;verticalAlign=bottom;strokeColor=#007FFF;" parent="1" vertex="1">
+ <mxGeometry x="80" y="545" width="380" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-18" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.394;entryY=0.97;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Courier New;fontSize=22;" parent="1" source="Y65zMmu6unbP29DxbX5J-24" target="Y65zMmu6unbP29DxbX5J-25" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-24" value="&lt;i&gt;&amp;lt;internal-pubkey&amp;gt;&lt;/i&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
+ <mxGeometry x="139" y="483" width="120" height="16.5" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-25" value="TR" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="239" y="444.5" width="100" height="28.5" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-17" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;fontFamily=Courier New;fontSize=22;" parent="1" source="Y65zMmu6unbP29DxbX5J-26" target="Y65zMmu6unbP29DxbX5J-25" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-26" value="Tapleaves" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" parent="1" vertex="1">
+ <mxGeometry x="290" y="481" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-27" value="&lt;i&gt;withdrawal&lt;/i&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" parent="1" vertex="1">
+ <mxGeometry x="80" y="499" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-28" value="&lt;i&gt;recover&lt;br&gt;&lt;/i&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" parent="1" vertex="1">
+ <mxGeometry x="75" y="536" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-29" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=1;fontFamily=Courier New;fontSize=9;endWidth=7.5;endSize=4.325;strokeColor=#336600;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="310" y="300" as="sourcePoint" />
+ <mxPoint x="263" y="366" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-30" value="script-path spend of &lt;i style=&quot;font-size: 11px;&quot;&gt;&lt;font color=&quot;#336600&quot; style=&quot;font-size: 11px;&quot;&gt;trigger&lt;/font&gt;&lt;/i&gt;&amp;nbsp;leaf,&lt;br style=&quot;font-size: 11px;&quot;&gt;supplying &lt;font color=&quot;#cc6600&quot; style=&quot;font-size: 11px;&quot;&gt;CTV hash &lt;/font&gt;in witness,&lt;br style=&quot;font-size: 11px;&quot;&gt;satisfying trigger auth" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;" parent="1" vertex="1">
+ <mxGeometry x="97" y="305" width="167" height="43" as="geometry" />
+ </mxCell>
+ <mxCell id="Y65zMmu6unbP29DxbX5J-33" value="script-path spend of &lt;i style=&quot;font-size: 11px;&quot;&gt;&lt;font color=&quot;#007fff&quot; style=&quot;font-size: 11px;&quot;&gt;recover&lt;/font&gt;&lt;/i&gt;&amp;nbsp;leaf, satisfying recovery authorization &lt;br style=&quot;font-size: 11px;&quot;&gt;script, if specified" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;" parent="1" vertex="1">
+ <mxGeometry x="640" y="295.5" width="140" height="64" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-12" value="OP_VAULT allows templated replacement of its leaf during spend (green to red) - otherwise taptree unchanged" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontStyle=0" parent="1" vertex="1">
+ <mxGeometry x="251" y="398" width="202" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-13" value="" style="endArrow=classic;html=1;rounded=1;fontFamily=Courier New;fontSize=22;strokeColor=#007FFF;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="570" y="300" as="sourcePoint" />
+ <mxPoint x="642" y="360" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-21" value="&amp;nbsp;[recovery auth] &lt;i&gt;&amp;lt;recovery-sPK-hash&amp;gt;&lt;/i&gt; &lt;b&gt;OP_VAULT_RECOVER&amp;nbsp;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=right;fontFamily=Courier New;verticalAlign=bottom;strokeColor=#007FFF;" parent="1" vertex="1">
+ <mxGeometry x="199" y="226.5" width="471" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-22" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.394;entryY=0.97;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Courier New;fontSize=22;" parent="1" source="nh3bwgDFfLlpKYWmzqJt-23" target="nh3bwgDFfLlpKYWmzqJt-24" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-23" value="&lt;i&gt;&amp;lt;internal-pubkey&amp;gt;&lt;/i&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
+ <mxGeometry x="289" y="164.5" width="120" height="16.5" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-24" value="TR" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="389" y="126" width="100" height="28.5" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-26" value="Tapleaves" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" parent="1" vertex="1">
+ <mxGeometry x="444" y="162.5" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-27" value="&lt;i&gt;trigger&lt;/i&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" parent="1" vertex="1">
+ <mxGeometry x="188" y="180.5" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-28" value="&lt;i&gt;recover&lt;br&gt;&lt;/i&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" parent="1" vertex="1">
+ <mxGeometry x="191" y="217.5" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-29" value="1" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=8;fontFamily=Courier New;" parent="1" vertex="1">
+ <mxGeometry x="444" y="139.5" width="12" height="10" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-30" value="2" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=8;fontFamily=Courier New;" parent="1" vertex="1">
+ <mxGeometry x="270" y="449" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-37" value="&lt;p style=&quot;border-color: var(--border-color); line-height: 2.4px;&quot;&gt;&lt;i style=&quot;border-color: var(--border-color);&quot;&gt;&lt;font style=&quot;border-color: var(--border-color);&quot; color=&quot;#cc6600&quot;&gt;[transaction&lt;/font&gt;&lt;/i&gt;&lt;i style=&quot;background-color: initial; border-color: var(--border-color);&quot;&gt;&lt;font style=&quot;border-color: var(--border-color);&quot; color=&quot;#cc6600&quot;&gt;&amp;nbsp;satisfying&amp;nbsp;&lt;/font&gt;&lt;/i&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); line-height: 2.4px;&quot;&gt;&lt;i style=&quot;border-color: var(--border-color);&quot;&gt;&lt;font style=&quot;border-color: var(--border-color);&quot; color=&quot;#cc6600&quot;&gt;&amp;nbsp; CTV hash]&lt;/font&gt;&lt;/i&gt;&lt;/p&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
+ <mxGeometry x="339" y="695" width="171" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-43" value="" style="endArrow=classic;html=1;rounded=1;fontFamily=Courier New;fontSize=22;strokeColor=#CC0000;fontColor=#CC0000;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="266" y="620" as="sourcePoint" />
+ <mxPoint x="300" y="660" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-44" value="timelocked CTV spend to predefined destination" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;" parent="1" vertex="1">
+ <mxGeometry x="150" y="654" width="120" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-47" value="1. initial vault" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontStyle=2;fontColor=#808080;" parent="1" vertex="1">
+ <mxGeometry x="199.5" y="104.5" width="131" height="28" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-49" value="recovery" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontStyle=2;fontColor=#808080;" parent="1" vertex="1">
+ <mxGeometry x="688" y="378" width="131" height="28" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-51" value="2. trigger" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontStyle=2;fontColor=#808080;" parent="1" vertex="1">
+ <mxGeometry x="50" y="380.5" width="131" height="28" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-52" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fontFamily=Courier New;fontSize=20;" parent="1" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="565.0000000000002" y="340" as="sourcePoint" />
+ <mxPoint x="565.0000000000002" y="340" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-53" value="" style="group" parent="1" vertex="1" connectable="0">
+ <mxGeometry x="610" y="418.5" width="210" height="160" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-1" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="nh3bwgDFfLlpKYWmzqJt-53" vertex="1">
+ <mxGeometry x="22.105263157894736" width="127.10526315789473" height="160" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-3" value="&lt;b style=&quot;background-color: initial;&quot;&gt;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;" parent="nh3bwgDFfLlpKYWmzqJt-53" vertex="1">
+ <mxGeometry x="101.31578947368422" y="14" width="92.10526315789473" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-5" value="" style="group;fontSize=9;" parent="nh3bwgDFfLlpKYWmzqJt-53" vertex="1" connectable="0">
+ <mxGeometry x="105.6842105263158" y="14" width="101.31578947368422" height="82" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-6" value="&lt;p style=&quot;line-height: 20%; font-size: 11px;&quot;&gt;&lt;b style=&quot;&quot;&gt;&lt;font style=&quot;font-size: 11px;&quot;&gt;scriptPubKey&lt;/font&gt;&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 20%; font-size: 11px;&quot;&gt;&lt;i style=&quot;&quot;&gt;&lt;font style=&quot;font-size: 11px;&quot;&gt;[recovery-spk]&lt;/font&gt;&lt;/i&gt;&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="nh3bwgDFfLlpKYWmzqJt-5" vertex="1">
+ <mxGeometry x="-2" width="100" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-7" value="&lt;p style=&quot;line-height: 10%; font-size: 11px;&quot;&gt;&lt;b style=&quot;&quot;&gt;&lt;font style=&quot;font-size: 11px;&quot;&gt;amount&lt;/font&gt;&lt;/b&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 10%; font-size: 11px;&quot;&gt;&lt;font style=&quot;font-size: 11px;&quot;&gt;&lt;i&gt;[full vault amount]&lt;/i&gt;&lt;/font&gt;&lt;/p&gt;" style="text;html=1;resizable=0;autosize=1;align=left;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;dashed=1;" parent="nh3bwgDFfLlpKYWmzqJt-5" vertex="1">
+ <mxGeometry x="-2" y="32" width="110" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-8" value="&lt;i&gt;&lt;font style=&quot;font-size: 11px;&quot;&gt;Ephemeral anchor&lt;/font&gt;&lt;/i&gt;" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;" parent="nh3bwgDFfLlpKYWmzqJt-53" vertex="1">
+ <mxGeometry x="102.69736842105263" y="107" width="89.34210526315789" height="35" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-11" value="Script-path reveal" style="rounded=0;whiteSpace=wrap;html=1;fontSize=10;" parent="nh3bwgDFfLlpKYWmzqJt-53" vertex="1">
+ <mxGeometry y="30" width="64.47368421052632" height="35" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-2" value="" style="endArrow=none;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="nh3bwgDFfLlpKYWmzqJt-53" source="nh3bwgDFfLlpKYWmzqJt-1" target="nh3bwgDFfLlpKYWmzqJt-1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="540.6578947368422" y="505" as="sourcePoint" />
+ <mxPoint x="586.7105263157895" y="455" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-57" value="3. withdrawal" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontStyle=2;fontColor=#808080;" parent="1" vertex="1">
+ <mxGeometry x="395" y="660" width="131" height="28" as="geometry" />
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-61" value="" style="endArrow=classic;html=1;rounded=1;strokeColor=#000000;fontFamily=Courier New;fontSize=11;fontColor=#808080;exitX=0.098;exitY=-0.004;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.663;entryY=0.984;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="nh3bwgDFfLlpKYWmzqJt-19" target="nh3bwgDFfLlpKYWmzqJt-24" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="446.5" y="176" as="sourcePoint" />
+ <mxPoint x="496.5" y="126" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="nh3bwgDFfLlpKYWmzqJt-62" value="" style="endArrow=classic;html=1;rounded=1;fontFamily=Courier New;fontSize=22;strokeColor=#007FFF;" parent="1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="490" y="440" as="sourcePoint" />
+ <mxPoint x="570" y="440" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="gvPKSLqK7s9hnQgXeOr3" name="Page-8">
+ <mxGraphModel dx="547" dy="897" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="IdHh1SD0108lOCAqlYyb-2" value="" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;strokeColor=#4D4D4D;fontFamily=Courier New;fontSize=20;" vertex="1" parent="1">
+ <mxGeometry x="223" y="387" width="427" height="230" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-4" value="" style="rounded=1;whiteSpace=wrap;html=1;strokeColor=#4D4D4D;fontFamily=Courier New;fontSize=8;dashed=1;" vertex="1" parent="1">
+ <mxGeometry x="182" y="102.5" width="509" height="183.5" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-48" value="" style="endArrow=classic;html=1;rounded=1;strokeColor=#000000;fontFamily=Courier New;fontSize=11;fontColor=#808080;entryX=0.544;entryY=1.002;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.1;exitY=0.583;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="IdHh1SD0108lOCAqlYyb-26" target="IdHh1SD0108lOCAqlYyb-25">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="450" y="178" as="sourcePoint" />
+ <mxPoint x="496.5" y="126" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-5" value="" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+ <mxGeometry x="445" y="169.5" width="120" height="100" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-6" value="&amp;nbsp;[trigger auth]&amp;nbsp;&lt;i&gt;&amp;lt;spend-delay&amp;gt;&lt;/i&gt; 2 &quot;OP_CSV OP_DROP OP_CTV&quot; &lt;b&gt;OP_VAULT&amp;nbsp;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=right;fontFamily=Courier New;verticalAlign=bottom;strokeColor=#336600;" vertex="1" parent="1">
+ <mxGeometry x="199" y="187.5" width="471" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-7" value="" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+ <mxGeometry x="454" y="495" width="120" height="100" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-8" value="&amp;nbsp;&lt;i&gt; &lt;font color=&quot;#994c00&quot;&gt;&amp;lt;CTV-hash&amp;gt;&lt;/font&gt;&amp;nbsp;&amp;lt;spend-delay&amp;gt;&lt;/i&gt;&amp;nbsp;OP_CSV OP_DROP OP_CTV&amp;nbsp;" style="rounded=1;whiteSpace=wrap;html=1;align=right;fontFamily=Courier New;verticalAlign=bottom;strokeColor=#B01E1E;" vertex="1" parent="1">
+ <mxGeometry x="240" y="515" width="386" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-9" value="[recovery auth]&lt;i&gt; &amp;lt;recovery-sPK-hash&amp;gt;&lt;/i&gt; &lt;b&gt;OP_VAULT_RECOVER&amp;nbsp;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=right;fontFamily=Courier New;verticalAlign=bottom;strokeColor=#007FFF;" vertex="1" parent="1">
+ <mxGeometry x="240" y="552" width="386" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-10" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.394;entryY=0.97;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Courier New;fontSize=22;" edge="1" parent="1" source="IdHh1SD0108lOCAqlYyb-11" target="IdHh1SD0108lOCAqlYyb-12">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-11" value="&lt;i&gt;&amp;lt;internal-pubkey&amp;gt;&lt;/i&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
+ <mxGeometry x="305" y="490" width="120" height="16.5" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-12" value="TR" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+ <mxGeometry x="405" y="451.5" width="100" height="28.5" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-13" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;fontFamily=Courier New;fontSize=22;" edge="1" parent="1" source="IdHh1SD0108lOCAqlYyb-14" target="IdHh1SD0108lOCAqlYyb-12">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-14" value="Tapleaves" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" vertex="1" parent="1">
+ <mxGeometry x="456" y="488" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-15" value="&lt;i&gt;withdrawal&lt;/i&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" vertex="1" parent="1">
+ <mxGeometry x="239" y="506" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-16" value="&lt;i&gt;recover&lt;br&gt;&lt;/i&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" vertex="1" parent="1">
+ <mxGeometry x="235" y="543" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-17" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=1;fontFamily=Courier New;fontSize=9;endWidth=7.5;endSize=4.325;strokeColor=#336600;" edge="1" parent="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="438" y="301" as="sourcePoint" />
+ <mxPoint x="438" y="371" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-18" value="script-path spend of &lt;i style=&quot;font-size: 11px;&quot;&gt;&lt;font color=&quot;#336600&quot; style=&quot;font-size: 11px;&quot;&gt;trigger&lt;/font&gt;&lt;/i&gt;&amp;nbsp;leaf,&lt;br style=&quot;font-size: 11px;&quot;&gt;supplying &lt;font color=&quot;#cc6600&quot; style=&quot;font-size: 11px;&quot;&gt;CTV hash &lt;/font&gt;in witness,&lt;br style=&quot;font-size: 11px;&quot;&gt;satisfying trigger auth" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;" vertex="1" parent="1">
+ <mxGeometry x="248" y="310" width="167" height="43" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-20" value="OP_VAULT allows templated replacement of its leaf during spend (green to red) - otherwise taptree unchanged" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontStyle=0" vertex="1" parent="1">
+ <mxGeometry x="417" y="405" width="202" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-22" value="&amp;nbsp;[recovery auth] &lt;i&gt;&amp;lt;recovery-sPK-hash&amp;gt;&lt;/i&gt; &lt;b&gt;OP_VAULT_RECOVER&amp;nbsp;&lt;br&gt;&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=right;fontFamily=Courier New;verticalAlign=bottom;strokeColor=#007FFF;" vertex="1" parent="1">
+ <mxGeometry x="199" y="226.5" width="471" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-23" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.394;entryY=0.97;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=Courier New;fontSize=22;" edge="1" parent="1" source="IdHh1SD0108lOCAqlYyb-24" target="IdHh1SD0108lOCAqlYyb-25">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-24" value="&lt;i&gt;&amp;lt;internal-pubkey&amp;gt;&lt;/i&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
+ <mxGeometry x="289" y="164.5" width="120" height="16.5" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-25" value="TR" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+ <mxGeometry x="389" y="126" width="100" height="28.5" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-26" value="Tapleaves" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" vertex="1" parent="1">
+ <mxGeometry x="444" y="162.5" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-27" value="&lt;i&gt;trigger&lt;/i&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" vertex="1" parent="1">
+ <mxGeometry x="188" y="180.5" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-28" value="&lt;i&gt;recover&lt;br&gt;&lt;/i&gt;" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=9;" vertex="1" parent="1">
+ <mxGeometry x="191" y="217.5" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-29" value="1" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=8;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="444" y="139.5" width="12" height="10" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-30" value="2" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=8;fontFamily=Courier New;" vertex="1" parent="1">
+ <mxGeometry x="436" y="456" width="60" height="30" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-34" value="1. initial vault" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontStyle=2;fontColor=#808080;" vertex="1" parent="1">
+ <mxGeometry x="199.5" y="104.5" width="131" height="28" as="geometry" />
+ </mxCell>
+ <mxCell id="IdHh1SD0108lOCAqlYyb-36" value="2. trigger" style="text;strokeColor=none;align=center;fillColor=none;html=1;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;fontStyle=2;fontColor=#808080;" vertex="1" parent="1">
+ <mxGeometry x="216" y="389.5" width="131" height="28" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+</mxfile>
diff --git a/bip-0345/withdrawal-comparison.drawio.png b/bip-0345/withdrawal-comparison.drawio.png
new file mode 100644
index 0000000..8a76d20
--- /dev/null
+++ b/bip-0345/withdrawal-comparison.drawio.png
Binary files differ
diff --git a/bip-0350.mediawiki b/bip-0350.mediawiki
index 9873d80..439b2a2 100644
--- a/bip-0350.mediawiki
+++ b/bip-0350.mediawiki
@@ -5,7 +5,7 @@
Author: Pieter Wuille <pieter@wuille.net>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0350
- Status: Draft
+ Status: Final
Type: Standards Track
Created: 2020-12-16
License: BSD-2-Clause
diff --git a/bip-0370.mediawiki b/bip-0370.mediawiki
index cb59801..cd448cc 100644
--- a/bip-0370.mediawiki
+++ b/bip-0370.mediawiki
@@ -2,7 +2,7 @@
BIP: 370
Layer: Applications
Title: PSBT Version 2
- Author: Andrew Chow <achow101@gmail.com>
+ Author: Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0370
Status: Draft
diff --git a/bip-0371.mediawiki b/bip-0371.mediawiki
index 599aa54..45b69f8 100644
--- a/bip-0371.mediawiki
+++ b/bip-0371.mediawiki
@@ -2,7 +2,7 @@
BIP: 371
Layer: Applications
Title: Taproot Fields for PSBT
- Author: Andrew Chow <andrew@achow101.com>
+ Author: Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0371
Status: Draft
diff --git a/bip-0380.mediawiki b/bip-0380.mediawiki
index 16a2cf4..27b7908 100644
--- a/bip-0380.mediawiki
+++ b/bip-0380.mediawiki
@@ -3,7 +3,7 @@
Layer: Applications
Title: Output Script Descriptors General Operation
Author: Pieter Wuille <pieter@wuille.net>
- Andrew Chow <andrew@achow101.com>
+ Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0380
Status: Draft
@@ -26,7 +26,7 @@ This BIP is licensed under the BSD 2-clause license.
Bitcoin wallets traditionally have stored a set of keys which are later serialized and mutated to produce the output scripts that the wallet watches and the addresses it provides to users.
Typically backups have consisted of solely the private keys, nowadays primarily in the form of BIP 39 mnemonics.
-However this backup solution is insuffient, especially since the introduction of Segregated Witness which added new output types.
+However this backup solution is insufficient, especially since the introduction of Segregated Witness which added new output types.
Given just the private keys, it is not possible for restored wallets to know which kinds of output scripts and addresses to produce.
This has lead to incompatibilities between wallets when restoring a backup or exporting data for a watch only wallet.
@@ -199,6 +199,63 @@ def descsum_create(s):
</pre>
+==Test Vectors==
+
+The following tests cover the checksum and character set:
+
+* Valid checksum: <tt>raw(deadbeef)#89f8spxm</tt>
+* No checksum: <tt>raw(deadbeef)</tt>
+* Missing checksum: <tt>raw(deadbeef)#</tt>
+* Too long checksum (9 chars): <tt>raw(deadbeef)#89f8spxmx</tt>
+* Too short checksum (7 chars): <tt>raw(deadbeef)#89f8spx</tt>
+* Error in payload: <tt>raw(deedbeef)#89f8spxm</tt>
+* Error in checksum: <tt>raw(deedbeef)##9f8spxm</tt>
+* Invalid characters in payload: <tt>raw(Ü)#00000000</tt>
+
+The following tests cover key expressions:
+
+Valid expressions:
+
+* Compressed public key: <tt>0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Uncompressed public key: <tt>04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235</tt>
+* Public key with key origin: <tt>[deadbeef/0h/0h/0h]0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Public key with key origin (<tt>'</tt> as hardened indicator): <tt>[deadbeef/0'/0'/0']0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Public key with key origin (mixed hardened indicator): <tt>[deadbeef/0'/0h/0']0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* WIF uncompressed private key <tt>5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss</tt>
+* WIF compressed private key <tt>L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1</tt>
+* Extended public key: <tt>xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL</tt>
+* Extended public key with key origin: <tt>[deadbeef/0h/1h/2h]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL</tt>
+* Extended public key with derivation: <tt>[deadbeef/0h/1h/2h]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/3/4/5</tt>
+* Extended public key with derivation and children: <tt>[deadbeef/0h/1h/2h]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/3/4/5/*</tt>
+* Extended public key with hardened derivation and unhardened children: <tt>xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/3h/4h/5h/*</tt>
+* Extended public key with hardened derivation and children: <tt>xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/3h/4h/5h/*h</tt>
+* Extended public key with key origin, hardened derivation and children: <tt>[deadbeef/0h/1h/2]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/3h/4h/5h/*h</tt>
+* Extended private key: <tt>xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc</tt>
+* Extended private key with key origin: <tt>[deadbeef/0h/1h/2h]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc</tt>
+* Extended private key with derivation: <tt>[deadbeef/0h/1h/2h]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/3/4/5</tt>
+* Extended private key with derivation and children: <tt>[deadbeef/0h/1h/2h]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/3/4/5/*</tt>
+* Extended private key with hardened derivation and unhardened children: <tt>xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/3h/4h/5h/*</tt>
+* Extended private key with hardened derivation and children: <tt>xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/3h/4h/5h/*h</tt>
+* Extended private key with key origin, hardened derivation and children: <tt>[deadbeef/0h/1h/2]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/3h/4h/5h/*h</tt>
+
+Invalid expression:
+
+* Children indicator in key origin: <tt>[deadbeef/0h/0h/0h/*]0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Trailing slash in key origin: <tt>[deadbeef/0h/0h/0h/]0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Too short fingerprint: <tt>[deadbef/0h/0h/0h]0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Too long fingerprint: <tt>[deadbeeef/0h/0h/0h]0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Invalid hardened indicators: <tt>[deadbeef/0f/0f/0f]0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Invalid hardened indicators: <tt>[deadbeef/0H/0H/0H]0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Invalid hardened indicators: <tt>[deadbeef/-0/-0/-0]0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600</tt>
+* Private key with derivation: <tt>L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1/0</tt>
+* Private key with derivation children: <tt>L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1/*</tt>
+* Derivation index out of range: <tt>xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648</tt>
+* Invalid derivation index: <tt>xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa</tt>
+* Multiple key origins: <tt>[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0</tt>
+* Missing key origin start: <tt>aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0</tt>
+* Non hex fingerprint: <tt>[gaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0</tt>
+* Key origin with no public key: <tt>[deadbeef]</tt>
+
==Backwards Compatibility==
Output script descriptors are an entirely new language which is not compatible with any existing software.
diff --git a/bip-0381.mediawiki b/bip-0381.mediawiki
index f439597..bfda2c8 100644
--- a/bip-0381.mediawiki
+++ b/bip-0381.mediawiki
@@ -3,7 +3,7 @@
Layer: Applications
Title: Non-Segwit Output Script Descriptors
Author: Pieter Wuille <pieter@wuille.net>
- Andrew Chow <andrew@achow101.com>
+ Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0381
Status: Draft
@@ -70,7 +70,49 @@ OP_HASH160 <SCRIPT_hash160> OP_EQUAL
==Test Vectors==
-TBD
+Valid descriptors followed by the scripts they produce. Descriptors involving derived child keys will have the 0th, 1st, and 2nd scripts listed.
+
+* <tt>pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)</tt>
+** <tt>2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac</tt>
+* <tt>pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)</tt>
+** <tt>2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac</tt>
+* <tt>pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)</tt>
+** <tt>76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac</tt>
+* <tt>pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)</tt>
+** <tt>76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac</tt>
+* <tt>pkh([deadbeef/1/2h/3/4h]03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)</tt>
+** <tt>76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac</tt>
+* <tt>pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)</tt>
+** <tt>4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac</tt>
+* <tt>pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)</tt>
+** <tt>4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac</tt>
+* <tt>pkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)</tt>
+** <tt>76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac</tt>
+* <tt>pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)</tt>
+** <tt>76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac</tt>
+* <tt>sh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))</tt>
+** <tt>a9141857af51a5e516552b3086430fd8ce55f7c1a52487</tt>
+* <tt>sh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+** <tt>a9141857af51a5e516552b3086430fd8ce55f7c1a52487</tt>
+* <tt>sh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))</tt>
+** <tt>a9141a31ad23bf49c247dd531a623c2ef57da3c400c587</tt>
+* <tt>sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+** <tt>a9141a31ad23bf49c247dd531a623c2ef57da3c400c587</tt>
+* <tt>pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)</tt>
+** <tt>76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac</tt>
+* <tt>pkh([bd16bee5/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)</tt>
+** <tt>76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac</tt>
+* <tt>pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)</tt>
+** <tt>210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac</tt>
+* <tt>pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)</tt>
+** <tt>210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac</tt>
+
+Invalid descriptors
+
+* <tt>pk()</tt> only accepts key expressions: <tt>pk(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+* <tt>pkh()</tt> only accepts key expressions: <tt>pkh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+* <tt>sh()</tt> only acceps script expressions: <tt>sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)</tt>
+* <tt>sh()</tt> is top level only: <tt>sh(sh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))</tt>
==Backwards Compatibility==
diff --git a/bip-0382.mediawiki b/bip-0382.mediawiki
index 768079e..bb1951d 100644
--- a/bip-0382.mediawiki
+++ b/bip-0382.mediawiki
@@ -3,7 +3,7 @@
Layer: Applications
Title: Segwit Output Script Descriptors
Author: Pieter Wuille <pieter@wuille.net>
- Andrew Chow <andrew@achow101.com>
+ Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0382
Status: Draft
@@ -57,7 +57,52 @@ OP_0 <SCRIPT_sha256>
==Test Vectors==
-TBD
+Valid descriptors followed by the scripts they produce. Descriptors involving derived child keys will have the 0th, 1st, and 2nd scripts listed.
+
+* <tt>wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)</tt>
+** <tt>00149a1c78a507689f6f54b847ad1cef1e614ee23f1e</tt>
+* <tt>wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)</tt>
+** <tt>00149a1c78a507689f6f54b847ad1cef1e614ee23f1e</tt>
+* <tt>wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/0)</tt>
+** <tt>0014326b2249e3a25d5dc60935f044ee835d090ba859</tt>
+* <tt>wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)</tt>
+** <tt>0014326b2249e3a25d5dc60935f044ee835d090ba859</tt>
+** <tt>0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7</tt>
+** <tt>00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27</tt>
+* <tt>sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))</tt>
+** <tt>a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87</tt>
+** <tt>a914bed59fc0024fae941d6e20a3b44a109ae740129287</tt>
+** <tt>a9148483aa1116eb9c05c482a72bada4b1db24af654387</tt>
+* <tt>sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*h))</tt>
+** <tt>a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87</tt>
+** <tt>a914bed59fc0024fae941d6e20a3b44a109ae740129287</tt>
+** <tt>a9148483aa1116eb9c05c482a72bada4b1db24af654387</tt>
+* <tt>wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))</tt>
+** <tt>0020338e023079b91c58571b20e602d7805fb808c22473cbc391a41b1bd3a192e75b</tt>
+* <tt>wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+** <tt>0020338e023079b91c58571b20e602d7805fb808c22473cbc391a41b1bd3a192e75b</tt>
+* <tt>wsh(pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))</tt>
+** <tt>00202e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa</tt>
+* <tt>wsh(pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+** <tt>00202e271faa2325c199d25d22e1ead982e45b64eeb4f31e73dbdf41bd4b5fec23fa</tt>
+* <tt>sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))</tt>
+** <tt>a914b61b92e2ca21bac1e72a3ab859a742982bea960a87</tt>
+* <tt>sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))</tt>
+** <tt>a914b61b92e2ca21bac1e72a3ab859a742982bea960a87</tt>
+
+Invalid descriptors with descriptions
+
+* Uncompressed public key in <tt>wpkh()</tt>: <tt>wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)</tt>
+* Uncompressed public key in <tt>wpkh()</tt>: <tt>sh(wpkh(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))</tt>
+* Uncompressed public key in <tt>wpkh()</tt>: <tt>wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)</tt>
+* Uncompressed public key in <tt>wpkh()</tt>: <tt>sh(wpkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))</tt>
+* Uncompressed public keys under <tt>wsh()</tt>: <tt>wsh(pk(5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss))</tt>
+* Uncompressed public keys under <tt>wsh()</tt>: <tt>wsh(pk(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235))</tt>
+* <tt>wpkh()</tt> nested in <tt>wsh()</tt>: <tt>wsh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+* <tt>wsh()</tt> nested in <tt>wsh()</tt>: <tt>wsh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))</tt>
+* <tt>wsh()</tt> nested in <tt>wsh()</tt>: <tt>sh(wsh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))))</tt>
+* Script in <tt>wpkh()</tt>: <tt>wpkh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))</tt>
+* Key in <tt>wsh()</tt>: <tt>wsh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)</tt>
==Backwards Compatibility==
diff --git a/bip-0383.mediawiki b/bip-0383.mediawiki
index 92e86e3..66e2f16 100644
--- a/bip-0383.mediawiki
+++ b/bip-0383.mediawiki
@@ -3,7 +3,7 @@
Layer: Applications
Title: Multisig Output Script Descriptors
Author: Pieter Wuille <pieter@wuille.net>
- Andrew Chow <andrew@achow101.com>
+ Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0383
Status: Draft
@@ -65,7 +65,37 @@ This changes the keys in lockstep and allows for output scripts to be indexed in
==Test Vectors==
-TBD
+Valid descriptors followed by the scripts they produce. Descriptors involving derived child keys will have the 0th, 1st, and 2nd scripts listed.
+
+* <tt>multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)</tt>
+** <tt>512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae</tt>
+* <tt>multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)</tt>
+** <tt>512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae</tt>
+* <tt>sortedmulti(1,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)</tt>
+** <tt>512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae</tt>
+* <tt>sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))</tt>
+** <tt>a91445a9a622a8b0a1269944be477640eedc447bbd8487</tt>
+* <tt>sortedmulti(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/0/*)</tt>
+** <tt>5221025d5fc65ebb8d44a5274b53bac21ff8307fec2334a32df05553459f8b1f7fe1b62102fbd47cc8034098f0e6a94c6aeee8528abf0a2153a5d8e46d325b7284c046784652ae</tt>
+** <tt>52210264fd4d1f5dea8ded94c61e9641309349b62f27fbffe807291f664e286bfbe6472103f4ece6dfccfa37b211eb3d0af4d0c61dba9ef698622dc17eecdf764beeb005a652ae</tt>
+** <tt>5221022ccabda84c30bad578b13c89eb3b9544ce149787e5b538175b1d1ba259cbb83321024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c52ae</tt>
+* <tt>wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))</tt>
+** <tt>0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f</tt>
+** <tt>002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203</tt>
+** <tt>0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c</tt>
+* <tt>sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))</tt>
+** <tt>a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87</tt>
+* <tt>wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv))</tt>
+** <tt>0020376bd8344b8b6ebe504ff85ef743eaa1aa9272178223bcb6887e9378efb341ac</tt>
+* <tt>sh(wsh(multi(20,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9,KzRedjSwMggebB3VufhbzpYJnvHfHe9kPJSjCU5QpJdAW3NSZxYS,Kyjtp5858xL7JfeV4PNRCKy2t6XvgqNNepArGY9F9F1SSPqNEMs3,L2D4RLHPiHBidkHS8ftx11jJk1hGFELvxh8LoxNQheaGT58dKenW,KyLPZdwY4td98bKkXqEXTEBX3vwEYTQo1yyLjX2jKXA63GBpmSjv)))</tt>
+** <tt>a914c2c9c510e9d7f92fd6131e94803a8d34a8ef675e87</tt>
+
+Invalid descriptors
+
+* More than 15 keys in P2SH multisig: <tt>sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))</tt>
+* Invalid threshold: <tt>multi(a,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)</tt>
+* Threshold of 0: <tt>multi(0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)</tt>
+* Threshold larger than keys: <tt>multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)</tt>
==Backwards Compatibility==
diff --git a/bip-0384.mediawiki b/bip-0384.mediawiki
index e735d74..ba12b55 100644
--- a/bip-0384.mediawiki
+++ b/bip-0384.mediawiki
@@ -3,7 +3,7 @@
Layer: Applications
Title: combo() Output Script Descriptors
Author: Pieter Wuille <pieter@wuille.net>
- Andrew Chow <andrew@achow101.com>
+ Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0384
Status: Draft
@@ -35,7 +35,38 @@ If the key is/has a compressed public key, then P2WPKH and P2SH-P2WPKH scripts a
==Test Vectors==
-TBD
+Valid descriptors followed by the scripts they produce. Descriptors involving derived child keys will have the 0th, and 1st scripts in additional sub-bullets.
+
+* <tt>combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)</tt>
+** <tt>2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac</tt>
+** <tt>76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac</tt>
+** <tt>00149a1c78a507689f6f54b847ad1cef1e614ee23f1e</tt>
+** <tt>a91484ab21b1b2fd065d4504ff693d832434b6108d7b87</tt>
+* <tt>combo(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)</tt>
+** <tt>4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235ac</tt>
+** <tt>76a914b5bd079c4d57cc7fc28ecf8213a6b791625b818388ac</tt>
+* <tt>combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)</tt>
+** <tt>2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac</tt>
+** <tt>76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac</tt>
+** <tt>001431a507b815593dfc51ffc7245ae7e5aee304246e</tt>
+** <tt>a9142aafb926eb247cb18240a7f4c07983ad1f37922687</tt>
+* <tt>combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)</tt>
+** Child 0
+*** <tt>2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac</tt>
+*** <tt>76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac</tt>
+*** <tt>0014f90e3178ca25f2c808dc76624032d352fdbdfaf2</tt>
+*** <tt>a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087</tt>
+** Child 1
+*** <tt>21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac</tt>
+*** <tt>76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac</tt>
+*** <tt>0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7</tt>
+*** <tt>a91473e39884cb71ae4e5ac9739e9225026c99763e6687</tt>
+
+Invalid descriptors
+
+* <tt>combo()</tt> in <tt>sh</tt> : <tt>sh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+* <tt>combo()</tt> in <tt>wsh</tt> : <tt>wsh(combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+* Script in <tt>combo()</tt>: <tt>combo(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
==Backwards Compatibility==
diff --git a/bip-0385.mediawiki b/bip-0385.mediawiki
index 5c46d75..3e922b3 100644
--- a/bip-0385.mediawiki
+++ b/bip-0385.mediawiki
@@ -3,7 +3,7 @@
Layer: Applications
Title: raw() and addr() Output Script Descriptors
Author: Pieter Wuille <pieter@wuille.net>
- Andrew Chow <andrew@achow101.com>
+ Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0385
Status: Draft
@@ -44,7 +44,25 @@ The output script produced by this descriptor is the output script produced by t
==Test Vectors==
-TBD
+Valid descriptors followed by the scripts they produce.
+
+* <tt>raw(deadbeef)</tt>
+** <tt>deadbeef</tt>
+* <tt>raw(512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae)</tt>
+** <tt>512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae</tt>
+* <tt>raw(a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87)</tt>
+** <tt>a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87</tt>
+* <tt>addr(3PUNyaW7M55oKWJ3kDukwk9bsKvryra15j)</tt>
+** <tt>a914eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee87</tt>
+
+Invalid descriptors
+
+* Non-hex script: <tt>raw(asdf)</tt>
+* Invalid address: <tt>addr(asdf)</tt>
+* <tt>raw</tt> nested in <tt>sh</tt>: <tt>sh(raw(deadbeef))</tt>
+* <tt>raw</tt> nested in <tt>wsh</tt>: <tt>wsh(raw(deadbeef))</tt>
+* <tt>addr</tt> nested in <tt>sh</tt>: <tt>sh(addr(3PUNyaW7M55oKWJ3kDukwk9bsKvryra15j))</tt>
+* <tt>addr</tt> nested in <tt>wsh</tt>: <tt>wsh(addr(3PUNyaW7M55oKWJ3kDukwk9bsKvryra15j))</tt>
==Backwards Compatibility==
diff --git a/bip-0386.mediawiki b/bip-0386.mediawiki
index d90e801..759887d 100644
--- a/bip-0386.mediawiki
+++ b/bip-0386.mediawiki
@@ -3,7 +3,7 @@
Layer: Applications
Title: tr() Output Script Descriptors
Author: Pieter Wuille <pieter@wuille.net>
- Andrew Chow <andrew@achow101.com>
+ Ava Chow <me@achow101.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0386
Status: Draft
@@ -84,7 +84,28 @@ An additional key expression is defined only for use within a <tt>tr()</tt> desc
==Test Vectors==
-TBD
+Valid descriptors followed by the scripts they produce. Descriptors involving derived child keys will have the 0th, 1st, and 2nd scripts listed.
+
+* <tt>tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)</tt>
+** <tt>512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11</tt>
+* <tt>tr(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)</tt>
+** <tt>512077aab6e066f8a7419c5ab714c12c67d25007ed55a43cadcacb4d7a970a093f11</tt>
+* <tt>tr(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/0/*,pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/1/*))</tt>
+** <tt>512078bc707124daa551b65af74de2ec128b7525e10f374dc67b64e00ce0ab8b3e12</tt>
+** <tt>512001f0a02a17808c20134b78faab80ef93ffba82261ccef0a2314f5d62b6438f11</tt>
+** <tt>512021024954fcec88237a9386fce80ef2ced5f1e91b422b26c59ccfc174c8d1ad25</tt>
+* <tt>tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pk(669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0))</tt>
+** <tt>512017cf18db381d836d8923b1bdb246cfcd818da1a9f0e6e7907f187f0b2f937754</tt>
+* <tt>tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,{pk(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/0),{{pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL),pk(02df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076)},pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)}})</tt>
+** <tt>512071fff39599a7b78bc02623cbe814efebf1a404f5d8ad34ea80f213bd8943f574</tt>
+
+Invalid Descriptors
+
+* Uncompressed private key: <tt>tr(5kyzdueo39z3fprtux2qbbwgnnp5ztd7yyr2sc1j299sbcnwjss)</tt>
+* Uncompressed public key: <tt>tr(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)</tt>
+* <tt>tr()</tt> nested in <tt>wsh</tt>: <tt>wsh(tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+* <tt>tr()</tt> nested in <tt>sh</tt>: <tt>sh(tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))</tt>
+* <tt>pkh()</tt> nested in <tt>tr</tt>: <tt>tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd, pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))</tt>
==Backwards Compatibility==
diff --git a/bip-0389.mediawiki b/bip-0389.mediawiki
new file mode 100644
index 0000000..500d7e3
--- /dev/null
+++ b/bip-0389.mediawiki
@@ -0,0 +1,109 @@
+<pre>
+ BIP: 389
+ Layer: Applications
+ Title: Multipath Descriptor Key Expressions
+ Author: Ava Chow <me@achow101.com>
+ Comments-Summary: No comments yet.
+ Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0389
+ Status: Draft
+ Type: Informational
+ Created: 2022-07-26
+ License: BSD-2-Clause
+</pre>
+
+==Abstract==
+
+This document specifies a modification to Key Expressions of Descriptors that are described in BIP 380.
+This modification allows Key Expressions to indicate BIP 32 derivation path steps that can have multiple values.
+
+==Copyright==
+
+This BIP is licensed under the BSD 2-clause license.
+
+==Motivation==
+
+Descriptors can describe the scripts that are used in a wallet, but wallets often require at least two descriptors for all of the scripts that they watch for.
+Wallets typically have one descriptor for producing receiving addresses, and the other for change addresses.
+These descriptors are often extremely similar - they produce the same types of scripts, derive keys from the same master key, and use derivation paths that are almost identical.
+The only differences are in the derivation path where one of the steps will be different between the descriptors.
+Thus it is useful to have a notation to represent both descriptors as a single descriptor where one of the derivation steps is a pair of values.
+
+==Specification==
+
+For extended keys and their derivations paths in a Key Expression, BIP 380 states:
+
+* <tt>xpub</tt> encoded extended public key or <tt>xprv</tt> encoded extended private key (as defined in BIP 32)
+** Followed by zero or more <tt>/NUM</tt> or <tt>/NUMh</tt> path elements indicating BIP 32 derivation steps to be taken after the given extended key.
+** Optionally followed by a single <tt>/*</tt> or <tt>/*h</tt> final step to denote all direct unhardened or hardened children.
+
+This is modifed to state:
+
+* <tt>xpub</tt> encoded extended public key or <tt>xprv</tt> encoded extended private key (as defined in BIP 32)
+** Followed by zero or more <tt>/NUM</tt> (may be followed by <tt>h</tt>, <tt>H</tt>, or <tt>'</tt> to indicate a hardened step) path elements indicating BIP 32 derivation steps to be taken after the given extended key.
+** Followed by zero or one <tt>/<NUM;NUM</tt> (each <tt>NUM</tt> may be followed by <tt>h</tt>, <tt>H</tt>, or <tt>'</tt> to indicate a hardened step) path element indicating a tuple of BIP 32 derivation steps to be taken after the given extended key.
+*** Followed by zero or more <tt>;NUM</tt> (may be followed by <tt>h</tt>, <tt>H</tt>, or <tt>'</tt> to indicate a hardened step) additional tuple values of BIP 32 derivation steps
+*** Followed by a single <tt>>/</tt>
+** Followed by zero or more <tt>/NUM</tt> (may be followed by <tt>h</tt>, <tt>H</tt>, or <tt>'</tt> to indicate a hardened step) path elements indicating BIP 32 derivation steps to be taken after the given extended key.
+** Optionally followed by a single <tt>/*</tt> (may be followed by <tt>h</tt>, <tt>H</tt>, or <tt>'</tt> to indicate a hardened step) final step to denote all direct unhardened or hardened children.
+
+When a <tt>/<NUM;NUM;...;NUM></tt> is encountered, parsers should account for a presence of multiple descriptors where the first descriptor uses the first <tt>NUM</tt>, and a second descriptor uses the second <tt>NUM</tt>, and so on, until each <tt>NUM</tt> is accounted for in the production of public keys, scripts, and addresses, as well as descriptor import and export operations.
+Descriptors that contain multiple Key Expressions that each have a <tt>/<NUM;NUM;...;NUM></tt> must have tuples of exactly the same length so that they are derived in lockstep in the same way that <tt>/*</tt> paths in multiple Key expressions are handled.
+
+The common use case for this is to represent descriptors for producing receiving and change addresses.
+When interpreting for this use case, wallets should use the first descriptor for producing receiving addresses, and the second descriptor for producing change addresses.
+For this use case, the element will commonly be the value <tt>/<0;1></tt>
+
+Note that only one <tt>/<NUM;NUM;...;NUM></tt> specifier is allowed in a Key Expression.
+
+==Test Vectors==
+
+Valid multipath descriptors followed by the descriptors they expand into as sub-bullets
+
+* <tt>pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/<0;1>)</tt>
+** <tt>pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)</tt>
+** <tt>pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/1)</tt>
+* <tt>pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/<2147483647h;0>/0)</tt>
+** <tt>pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647h/0)</tt>
+** <tt>pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/0/0)</tt>
+* <tt>wpkh([ffffffff/13h]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/<1;3>/2/*</tt>
+** <tt>wpkh([ffffffff/13h]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)</tt>
+** <tt>wpkh([ffffffff/13h]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/3/2/*)</tt>
+* <tt>multi(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/<1;2>/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/<3;4>/0/*)</tt>
+** <tt>multi(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/3/0/*)</tt>
+** <tt>multi(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/2/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/4/0/*)</tt>
+* <tt>pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/<0;1;2>)</tt>
+** <tt>pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0)</tt>
+** <tt>pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1)</tt>
+** <tt>pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2)</tt>
+* <tt>sh(multi(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/<1;2;3>/0/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/*,xpub661MyMwAqRbcGDZQUKLqmWodYLcoBQnQH33yYkkF3jjxeLvY8qr2wWGEWkiKFaaQfJCoi3HeEq3Dc5DptfbCyjD38fNhSqtKc1UHaP4ba3t/0/0/<3;4;5>/*))</tt>
+** <tt>sh(multi(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/0/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/*,xpub661MyMwAqRbcGDZQUKLqmWodYLcoBQnQH33yYkkF3jjxeLvY8qr2wWGEWkiKFaaQfJCoi3HeEq3Dc5DptfbCyjD38fNhSqtKc1UHaP4ba3t/0/0/3/*))</tt>
+** <tt>sh(multi(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/2/0/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/*,xpub661MyMwAqRbcGDZQUKLqmWodYLcoBQnQH33yYkkF3jjxeLvY8qr2wWGEWkiKFaaQfJCoi3HeEq3Dc5DptfbCyjD38fNhSqtKc1UHaP4ba3t/0/0/4/*))</tt>
+** <tt>sh(multi(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/3/0/*,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0/*,xpub661MyMwAqRbcGDZQUKLqmWodYLcoBQnQH33yYkkF3jjxeLvY8qr2wWGEWkiKFaaQfJCoi3HeEq3Dc5DptfbCyjD38fNhSqtKc1UHaP4ba3t/0/0/5/*))</tt>
+
+Invalid descriptors
+
+* Multiple multipath specifiers: <tt>pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/<0;1>/<2;3>)</tt>
+* Multipath specifier in origin: <tt>pkh([deadbeef/<0;1>]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0)</tt>
+* Multipath specifiers of mismatched lengths: <tt>tr(xpub661MyMwAqRbcF3yVrV2KyYetLMYA5mCbv4BhrKwUrFE9LZM6JRR1AEt8Jq4V4C8LwtTke6YEEdCZqgXp85YRk2j74EfJKhe3QybQ9kcUjs4/<6;7;8;9>/*,{pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/<1;2;3>/0/*),pk(xpub661MyMwAqRbcGDZQUKLqmWodYLcoBQnQH33yYkkF3jjxeLvY8qr2wWGEWkiKFaaQfJCoi3HeEq3Dc5DptfbCyjD38fNhSqtKc1UHaP4ba3t/0/0/<3;4;5>/*)})</tt>
+* Multipath specifiers of mismatched lengths: <tt>sh(multi(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/<1;2;3>/0/*,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0/*,xprv9s21ZrQH143K3jUwNHoqQNrtzJnJmx4Yup8NkNLdVQCymYbPbJXnPhwkfTfxZfptcs3rLAPUXS39oDLgrNKQGwbGsEmJJ8BU3RzQuvShEG4/0/0/<3;4>/*))</tt>
+* Empty multipath specifier: <tt>wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/<>/*)</tt>
+* Missing multipath start: <tt>wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0>/*)</tt>
+* Missing multipath end: <tt>wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/<0/*)</tt>
+* Missing index in multipath specifier: <tt>wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/<0;>/*)</tt>
+
+==Backwards Compatibility==
+
+This is an addition to the Key Expressions defined in BIP 380.
+Key Expressions using the format described in BIP 380 are compatible with this modification and parsers that implement this will still be able to parse such descriptors.
+However as this is an addition to Key Expressions, older parsers will not be able to understand such descriptors.
+
+This modification to Key Expressions uses two new characters: <tt><</tt> and <tt>;</tt>.
+These are part of the descriptor character set and so are covered by the checksum algorithm.
+As these are previously unused characters, old parsers will not accidentally mistake them for indicating something else.
+
+This proposal is in contrast to similar proposals such as BIP 88 which allow for multiple derivation indexes in a single element.
+This limitation exists in order to reduce the number of descriptors that are expanded, avoid confusion about how to expand the descriptor, and avoid having expanded descriptors that users are not expecting.
+
+==Reference Implementation==
+
+https://github.com/bitcoin/bitcoin/pull/22838