Merge rust-bitcoin/rust-bitcoin#912: Improve docs in taproot module

c25eddd187 Remove unnecessary documentation (Tobin Harding)
8631474f08 Improve docs in taproot module (Tobin Harding)

Pull request description:

  I should have done this PR a month ago, my bad. This one is kind of important IMO because we are going to have so many people looking at this part of the code soon as we release.

  As has been done in other places in the codebase; improve the docs in the `taproot` module by doing:

  - Use full sentences (capital letters + full stops)
  - Use back ticks and links for types where appropriate
  - Fix grammar
  - Fix stale docs
  - Use third person for describing functions
  - Use 100 character line width
  - Use markdown sections (`# Examples`, `# Returns`) where appropriate
  - Separate brief heading from extended description when appropriate
  - Use `///` for all functions/types (both private and public)

  I also did:

  - Build the docs and check all the links
  - Read all the built docs, check for sanity and pretty-ness

  Its all in one patch, I couldn't really tease it apart. I can try a bit harder if it proves too annoying to review.

ACKs for top commit:
  sanket1729:
    ACK c25eddd187
  dr-orlovsky:
    ACK c25eddd187
  apoelstra:
    ACK c25eddd187

Tree-SHA512: 72f35bf8779392060388db985df5abc42a89796eaad1eafd08ea50b635d469fbd07a53ff253cdf27ad4d4baed7d37cec6ea1da1aece3672b9447f87181e218f8
This commit is contained in:
Dr. Maxim Orlovsky 2022-03-30 19:29:36 +03:00
commit 58a958e3f7
No known key found for this signature in database
GPG Key ID: AF91255DEA466640
1 changed files with 195 additions and 180 deletions

View File

@ -53,7 +53,7 @@ const MIDSTATE_TAPTWEAK: [u8; 32] = [
]; ];
// d129a2f3701c655d6583b6c3b941972795f4e23294fd54f4a2ae8d8547ca590b // d129a2f3701c655d6583b6c3b941972795f4e23294fd54f4a2ae8d8547ca590b
/// The SHA-256 midstate value for the TapSigHash hash. /// The SHA-256 midstate value for the [`TapSighashHash`].
const MIDSTATE_TAPSIGHASH: [u8; 32] = [ const MIDSTATE_TAPSIGHASH: [u8; 32] = [
245, 4, 164, 37, 215, 248, 120, 59, 19, 99, 134, 138, 227, 229, 86, 88, 110, 238, 148, 93, 188, 245, 4, 164, 37, 215, 248, 120, 59, 19, 99, 134, 138, 227, 229, 86, 88, 110, 238, 148, 93, 188,
120, 136, 221, 2, 166, 226, 195, 24, 115, 254, 159, 120, 136, 221, 2, 166, 226, 195, 24, 115, 254, 159,
@ -99,8 +99,8 @@ sha256t_hash_newtype!(TapSighashHash, TapSighashTag, MIDSTATE_TAPSIGHASH, 64,
); );
impl TapTweakHash { impl TapTweakHash {
/// Create a new BIP341 [`TapTweakHash`] from key and tweak /// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where
/// Produces H_taptweak(P||R) where P is internal key and R is the merkle root /// `P` is the internal key and `R` is the merkle root.
pub fn from_key_and_tweak( pub fn from_key_and_tweak(
internal_key: UntweakedPublicKey, internal_key: UntweakedPublicKey,
merkle_root: Option<TapBranchHash>, merkle_root: Option<TapBranchHash>,
@ -118,7 +118,7 @@ impl TapTweakHash {
} }
impl TapLeafHash { impl TapLeafHash {
/// function to compute leaf hash from components /// Computes the leaf hash from components.
pub fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash { pub fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash {
let mut eng = TapLeafHash::engine(); let mut eng = TapLeafHash::engine();
ver.to_consensus() ver.to_consensus()
@ -131,24 +131,24 @@ impl TapLeafHash {
} }
} }
/// Maximum depth of a Taproot Tree Script spend path /// Maximum depth of a taproot tree script spend path.
// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L229 // https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L229
pub const TAPROOT_CONTROL_MAX_NODE_COUNT: usize = 128; pub const TAPROOT_CONTROL_MAX_NODE_COUNT: usize = 128;
/// Size of a taproot control node /// Size of a taproot control node.
// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L228 // https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L228
pub const TAPROOT_CONTROL_NODE_SIZE: usize = 32; pub const TAPROOT_CONTROL_NODE_SIZE: usize = 32;
/// Tapleaf mask for getting the leaf version from first byte of control block /// Tapleaf mask for getting the leaf version from first byte of control block.
// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L225 // https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L225
pub const TAPROOT_LEAF_MASK: u8 = 0xfe; pub const TAPROOT_LEAF_MASK: u8 = 0xfe;
/// Tapscript leaf version /// Tapscript leaf version.
// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L226 // https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L226
pub const TAPROOT_LEAF_TAPSCRIPT: u8 = 0xc0; pub const TAPROOT_LEAF_TAPSCRIPT: u8 = 0xc0;
/// Taproot annex prefix /// Taproot annex prefix.
pub const TAPROOT_ANNEX_PREFIX: u8 = 0x50; pub const TAPROOT_ANNEX_PREFIX: u8 = 0x50;
/// Tapscript control base size /// Tapscript control base size.
// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L227 // https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L227
pub const TAPROOT_CONTROL_BASE_SIZE: usize = 33; pub const TAPROOT_CONTROL_BASE_SIZE: usize = 33;
/// Tapscript control max size /// Tapscript control max size.
// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L230 // https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L230
pub const TAPROOT_CONTROL_MAX_SIZE: usize = pub const TAPROOT_CONTROL_MAX_SIZE: usize =
TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT; TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;
@ -156,45 +156,48 @@ pub const TAPROOT_CONTROL_MAX_SIZE: usize =
// type alias for versioned tap script corresponding merkle proof // type alias for versioned tap script corresponding merkle proof
type ScriptMerkleProofMap = BTreeMap<(Script, LeafVersion), BTreeSet<TaprootMerkleBranch>>; type ScriptMerkleProofMap = BTreeMap<(Script, LeafVersion), BTreeSet<TaprootMerkleBranch>>;
/// Data structure for representing Taproot spending information. /// Represents taproot spending information.
/// Taproot output corresponds to a combination of a
/// single public key condition (known the internal key), and zero or more
/// general conditions encoded in scripts organized in the form of a binary tree.
/// ///
/// Taproot can be spent be either: /// Taproot output corresponds to a combination of a single public key condition (known as the
/// - Spending using the key path i.e., with secret key corresponding to the output_key /// internal key), and zero or more general conditions encoded in scripts organized in the form of a
/// - By satisfying any of the scripts in the script spent path. Each script can be satisfied by providing /// binary tree.
/// a witness stack consisting of the script's inputs, plus the script itself and the control block.
/// ///
/// If one or more of the spending conditions consist of just a single key (after aggregation), /// Taproot can be spent by either:
/// the most likely one should be made the internal key. /// - Spending using the key path i.e., with secret key corresponding to the tweaked `output_key`.
/// See [BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) for more details /// - By satisfying any of the scripts in the script spend path. Each script can be satisfied by
/// on choosing internal keys for a taproot application /// providing a witness stack consisting of the script's inputs, plus the script itself and the
/// control block.
/// ///
/// Note: This library currently does not support [annex](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-5) /// If one or more of the spending conditions consist of just a single key (after aggregation), the
/// most likely key should be made the internal key.
/// See [BIP341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) for more details on
/// choosing internal keys for a taproot application.
///
/// Note: This library currently does not support
/// [annex](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-5).
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TaprootSpendInfo { pub struct TaprootSpendInfo {
/// The BIP341 internal key. /// The BIP341 internal key.
internal_key: UntweakedPublicKey, internal_key: UntweakedPublicKey,
/// The Merkle root of the script tree (None if there are no scripts) /// The merkle root of the script tree (None if there are no scripts).
merkle_root: Option<TapBranchHash>, merkle_root: Option<TapBranchHash>,
/// The sign final output pubkey as per BIP 341 /// The sign final output pubkey as per BIP 341.
output_key_parity: secp256k1::Parity, output_key_parity: secp256k1::Parity,
/// The tweaked output key /// The tweaked output key.
output_key: TweakedPublicKey, output_key: TweakedPublicKey,
/// Map from (script, leaf_version) to (sets of) [`TaprootMerkleBranch`]. /// Map from (script, leaf_version) to (sets of) [`TaprootMerkleBranch`]. More than one control
/// More than one control block for a given script is only possible if it /// block for a given script is only possible if it appears in multiple branches of the tree. In
/// appears in multiple branches of the tree. In all cases, keeping one should /// all cases, keeping one should be enough for spending funds, but we keep all of the paths so
/// be enough for spending funds, but we keep all of the paths so that /// that a full tree can be constructed again from spending data if required.
/// a full tree can be constructed again from spending data if required.
script_map: ScriptMerkleProofMap, script_map: ScriptMerkleProofMap,
} }
impl TaprootSpendInfo { impl TaprootSpendInfo {
/// Create a new [`TaprootSpendInfo`] from a list of script(with default script version). /// Creates a new [`TaprootSpendInfo`] from a list of scripts (with default script version) and
/// weights of satisfaction for that script.
/// ///
/// See [`TaprootBuilder::with_huffman_tree`] for more detailed documentation /// See [`TaprootBuilder::with_huffman_tree`] for more detailed documentation.
pub fn with_huffman_tree<C, I>( pub fn with_huffman_tree<C, I>(
secp: &Secp256k1<C>, secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey, internal_key: UntweakedPublicKey,
@ -207,18 +210,17 @@ impl TaprootSpendInfo {
TaprootBuilder::with_huffman_tree(script_weights)?.finalize(secp, internal_key) TaprootBuilder::with_huffman_tree(script_weights)?.finalize(secp, internal_key)
} }
/// Create a new key spend with internal key and proided merkle root. /// Creates a new key spend with `internal_key` and `merkle_root`. Provide [`None`] for
/// Provide [`None`] for merkle_root if there is no script path. /// `merkle_root` if there is no script path.
/// ///
/// *Note*: As per BIP341 /// *Note*: As per BIP341
/// ///
/// When the merkle root is [`None`], the output key commits to an unspendable /// When the merkle root is [`None`], the output key commits to an unspendable script path
/// script path instead of having no script path. This is achieved by computing /// instead of having no script path. This is achieved by computing the output key point as
/// the output key point as Q = P + int(hashTapTweak(bytes(P)))G. /// `Q = P + int(hashTapTweak(bytes(P)))G`. See also [`TaprootSpendInfo::tap_tweak`].
/// See also [`TaprootSpendInfo::tap_tweak`].
/// Refer to BIP 341 footnote (Why should the output key always have
/// a taproot commitment, even if there is no script path?) for more details
/// ///
/// Refer to BIP 341 footnote ('Why should the output key always have a taproot commitment, even
/// if there is no script path?') for more details.
pub fn new_key_spend<C: secp256k1::Verification>( pub fn new_key_spend<C: secp256k1::Verification>(
secp: &Secp256k1<C>, secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey, internal_key: UntweakedPublicKey,
@ -234,35 +236,36 @@ impl TaprootSpendInfo {
} }
} }
/// Obtain the tweak and parity used to compute the output_key /// Returns the `TapTweakHash` for this [`TaprootSpendInfo`] i.e., the tweak using `internal_key`
/// and `merkle_root`.
pub fn tap_tweak(&self) -> TapTweakHash { pub fn tap_tweak(&self) -> TapTweakHash {
TapTweakHash::from_key_and_tweak(self.internal_key, self.merkle_root) TapTweakHash::from_key_and_tweak(self.internal_key, self.merkle_root)
} }
/// Obtain the internal key /// Returns the internal key for this [`TaprootSpendInfo`].
pub fn internal_key(&self) -> UntweakedPublicKey { pub fn internal_key(&self) -> UntweakedPublicKey {
self.internal_key self.internal_key
} }
/// Obtain the merkle root /// Returns the merkle root for this [`TaprootSpendInfo`].
pub fn merkle_root(&self) -> Option<TapBranchHash> { pub fn merkle_root(&self) -> Option<TapBranchHash> {
self.merkle_root self.merkle_root
} }
/// Output key(the key used in script pubkey) from Spend data. See also /// Returns the output key (the key used in script pubkey) for this [`TaprootSpendInfo`].
/// [`TaprootSpendInfo::output_key_parity`]
pub fn output_key(&self) -> TweakedPublicKey { pub fn output_key(&self) -> TweakedPublicKey {
self.output_key self.output_key
} }
/// Parity of the output key. See also [`TaprootSpendInfo::output_key`] /// Returns the parity of the output key. See also [`TaprootSpendInfo::output_key`].
pub fn output_key_parity(&self) -> secp256k1::Parity { pub fn output_key_parity(&self) -> secp256k1::Parity {
self.output_key_parity self.output_key_parity
} }
/// Compute [`TaprootSpendInfo`] from [`NodeInfo`], and internal key. /// Computes the [`TaprootSpendInfo`] from `internal_key` and `node`.
/// This is useful when you want to manually build a taproot tree wihtout ///
/// using [`TaprootBuilder`]. /// This is useful when you want to manually build a taproot tree without using
/// [`TaprootBuilder`].
pub fn from_node_info<C: secp256k1::Verification>( pub fn from_node_info<C: secp256k1::Verification>(
secp: &Secp256k1<C>, secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey, internal_key: UntweakedPublicKey,
@ -288,14 +291,17 @@ impl TaprootSpendInfo {
info info
} }
/// Access the internal script map /// Returns the internal script map.
pub fn as_script_map(&self) -> &ScriptMerkleProofMap { pub fn as_script_map(&self) -> &ScriptMerkleProofMap {
&self.script_map &self.script_map
} }
/// Obtain a [`ControlBlock`] for particular script with the given version. /// Constructs a [`ControlBlock`] for particular script with the given version.
/// Returns [`None`] if the script is not contained in the [`TaprootSpendInfo`] ///
/// If there are multiple ControlBlocks possible, this returns the shortest one. /// # Returns
///
/// - If there are multiple control blocks possible, returns the shortest one.
/// - If the script is not contained in the [`TaprootSpendInfo`], returns `None`.
pub fn control_block(&self, script_ver: &(Script, LeafVersion)) -> Option<ControlBlock> { pub fn control_block(&self, script_ver: &(Script, LeafVersion)) -> Option<ControlBlock> {
let merkle_branch_set = self.script_map.get(script_ver)?; let merkle_branch_set = self.script_map.get(script_ver)?;
// Choose the smallest one amongst the multiple script maps // Choose the smallest one amongst the multiple script maps
@ -312,22 +318,23 @@ impl TaprootSpendInfo {
} }
} }
/// Builder for building taproot iteratively. Users can specify tap leaf or omitted/hidden /// Builder for building taproot iteratively. Users can specify tap leaf or omitted/hidden branches
/// branches in a DFS(Depth first search) walk to construct this tree. /// in a depth-first search (DFS) walk order to construct this tree.
// Similar to Taproot Builder in bitcoin core ///
/// See Wikipedia for more details on [DFS](https://en.wikipedia.org/wiki/Depth-first_search).
// Similar to Taproot Builder in bitcoin core.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TaprootBuilder { pub struct TaprootBuilder {
// The following doc-comment is from bitcoin core, but modified for rust // The following doc-comment is from bitcoin core, but modified for Rust. It describes the
// The comment below describes the current state of the builder for a given tree. // current state of the builder for a given tree.
// //
// For each level in the tree, one NodeInfo object may be present. branch at index 0 // For each level in the tree, one NodeInfo object may be present. Branch at index 0 is
// is information about the root; further values are for deeper subtrees being // information about the root; further values are for deeper subtrees being explored.
// explored.
// //
// During the construction of Taptree, for every right branch taken to // During the construction of Taptree, for every right branch taken to reach the position we're
// reach the position we're currently working in, there will be a (Some(_)) // currently working on, there will be a `(Some(_))` entry in branch corresponding to the left
// entry in branch corresponding to the left branch at that level. // branch at that level.
// //
// For example, imagine this tree: - N0 - // For example, imagine this tree: - N0 -
// / \ // / \
@ -337,50 +344,50 @@ pub struct TaprootBuilder {
// / \ // / \
// D E // D E
// //
// Initially, branch is empty. After processing leaf A, it would become // Initially, branch is empty. After processing leaf A, it would become {None, None, A}. When
// {None, None, A}. When processing leaf B, an entry at level 2 already // processing leaf B, an entry at level 2 already exists, and it would thus be combined with it
// exists, and it would thus be combined with it to produce a level 1 one, // to produce a level 1 entry, resulting in {None, N1}. Adding C and D takes us to {None, N1, C}
// resulting in {None, N1}. Adding C and D takes us to {None, N1, C} // and {None, N1, C, D} respectively. When E is processed, it is combined with D, and then C,
// and {None, N1, C, D} respectively. When E is processed, it is combined // and then N1, to produce the root, resulting in {N0}.
// with D, and then C, and then N1, to produce the root, resulting in {N0}.
// //
// This structure allows processing with just O(log n) overhead if the leaves // This structure allows processing with just O(log n) overhead if the leaves are computed on
// are computed on the fly. // the fly.
//
// As an invariant, there can never be None entries at the end. There can
// also not be more than 128 entries (as that would mean more than 128 levels
// in the tree). The depth of newly added entries will always be at least
// equal to the current size of branch (otherwise it does not correspond
// to a depth-first traversal of a tree). branch is only empty if no entries
// have ever be processed. branch having length 1 corresponds to being done.
// //
// As an invariant, there can never be None entries at the end. There can also not be more than
// 128 entries (as that would mean more than 128 levels in the tree). The depth of newly added
// entries will always be at least equal to the current size of branch (otherwise it does not
// correspond to a depth-first traversal of a tree). A branch is only empty if no entries have
// ever be processed. A branch having length 1 corresponds to being done.
branch: Vec<Option<NodeInfo>>, branch: Vec<Option<NodeInfo>>,
} }
impl TaprootBuilder { impl TaprootBuilder {
/// Create a new instance of [`TaprootBuilder`] /// Creates a new instance of [`TaprootBuilder`].
pub fn new() -> Self { pub fn new() -> Self {
TaprootBuilder { branch: vec![] } TaprootBuilder { branch: vec![] }
} }
/// Create a new [`TaprootBuilder`] from a list of script(with default script version) and /// Creates a new [`TaprootSpendInfo`] from a list of scripts (with default script version) and
/// weights of satisfaction for that script. The weights represent the probability of /// weights of satisfaction for that script.
/// each branch being taken. If probabilities/weights for each condition are known, ///
/// constructing the tree as a Huffman tree is the optimal way to minimize average /// The weights represent the probability of each branch being taken. If probabilities/weights
/// case satisfaction cost. This function takes an iterator of (`u32`, &[`Script`]) tuples /// for each condition are known, constructing the tree as a Huffman Tree is the optimal way to
/// as an input, where `u32` represents the satisfaction weights of the script branch. /// minimize average case satisfaction cost. This function takes as input an iterator of
/// For example, [(3, S1), (2, S2), (5, S3)] would construct a TapTree that has optimal /// `tuple(u32, &Script)` where `u32` represents the satisfaction weights of the branch. For
/// example, [(3, S1), (2, S2), (5, S3)] would construct a [`TapTree`] that has optimal
/// satisfaction weight when probability for S1 is 30%, S2 is 20% and S3 is 50%. /// satisfaction weight when probability for S1 is 30%, S2 is 20% and S3 is 50%.
/// ///
/// # Errors: /// # Errors:
/// ///
/// - When the optimal huffman tree has a depth more than 128 /// - When the optimal Huffman Tree has a depth more than 128.
/// - If the provided list of script weights is empty /// - If the provided list of script weights is empty.
/// ///
/// # Edge Cases: /// # Edge Cases:
/// - If the script weight calculations overflow, a sub-optimal tree may be generated. This ///
/// should not happen unless you are dealing with billions of branches with weights close to /// If the script weight calculations overflow, a sub-optimal tree may be generated. This should
/// 2^32. /// not happen unless you are dealing with billions of branches with weights close to 2^32.
///
/// [`TapTree`]: ::util::psbt::TapTree
pub fn with_huffman_tree<I>( pub fn with_huffman_tree<I>(
script_weights: I, script_weights: I,
) -> Result<Self, TaprootBuilderError> ) -> Result<Self, TaprootBuilderError>
@ -412,7 +419,8 @@ impl TaprootBuilder {
Ok(TaprootBuilder{branch: vec![Some(node)]}) Ok(TaprootBuilder{branch: vec![Some(node)]})
} }
/// Just like [`TaprootBuilder::add_leaf`] but allows to specify script version /// Adds a leaf script at `depth` to the builder with script version `ver`. Errors if the leaves
/// are not provided in DFS walk order. The depth of the root node is 0.
pub fn add_leaf_with_ver( pub fn add_leaf_with_ver(
self, self,
depth: usize, depth: usize,
@ -423,29 +431,27 @@ impl TaprootBuilder {
self.insert(leaf, depth) self.insert(leaf, depth)
} }
/// Add a leaf script at a depth `depth` to the builder with default script version. /// Adds a leaf script at `depth` to the builder with default script version. Errors if the
/// This will error if the leave are not provided in a DFS walk order. The depth of the /// leaves are not provided in DFS walk order. The depth of the root node is 0.
/// root node is 0 and it's immediate child would be at depth 1. ///
/// See [`TaprootBuilder::add_leaf_with_ver`] for adding a leaf with specific version /// See [`TaprootBuilder::add_leaf_with_ver`] for adding a leaf with specific version.
/// See [Wikipedia](https://en.wikipedia.org/wiki/Depth-first_search) for more details
pub fn add_leaf(self, depth: usize, script: Script) -> Result<Self, TaprootBuilderError> { pub fn add_leaf(self, depth: usize, script: Script) -> Result<Self, TaprootBuilderError> {
self.add_leaf_with_ver(depth, script, LeafVersion::TapScript) self.add_leaf_with_ver(depth, script, LeafVersion::TapScript)
} }
/// Add a hidden/omitted node at a depth `depth` to the builder. /// Adds a hidden/omitted node at `depth` to the builder. Errors if the leaves are not provided
/// This will error if the node are not provided in a DFS walk order. The depth of the /// in DFS walk order. The depth of the root node is 0.
/// root node is 0 and it's immediate child would be at depth 1.
pub fn add_hidden(self, depth: usize, hash: sha256::Hash) -> Result<Self, TaprootBuilderError> { pub fn add_hidden(self, depth: usize, hash: sha256::Hash) -> Result<Self, TaprootBuilderError> {
let node = NodeInfo::new_hidden(hash); let node = NodeInfo::new_hidden(hash);
self.insert(node, depth) self.insert(node, depth)
} }
/// Check if the builder is a complete tree /// Checks if the builder is a complete tree.
pub fn is_complete(&self) -> bool { pub fn is_complete(&self) -> bool {
self.branch.len() == 1 && self.branch[0].is_some() self.branch.len() == 1 && self.branch[0].is_some()
} }
/// Create [`TaprootSpendInfo`] with the given internal key /// Creates a [`TaprootSpendInfo`] with the given internal key.
pub fn finalize<C: secp256k1::Verification>( pub fn finalize<C: secp256k1::Verification>(
mut self, mut self,
secp: &Secp256k1<C>, secp: &Secp256k1<C>,
@ -466,7 +472,7 @@ impl TaprootBuilder {
&self.branch &self.branch
} }
// Helper function to insert a leaf at a depth /// Inserts a leaf at `depth`.
fn insert(mut self, mut node: NodeInfo, mut depth: usize) -> Result<Self, TaprootBuilderError> { fn insert(mut self, mut node: NodeInfo, mut depth: usize) -> Result<Self, TaprootBuilderError> {
// early error on invalid depth. Though this will be checked later // early error on invalid depth. Though this will be checked later
// while constructing TaprootMerkelBranch // while constructing TaprootMerkelBranch
@ -513,15 +519,16 @@ impl TaprootBuilder {
} }
} }
/// Data structure used to represent node information in taproot tree. /// Represents the node information in taproot tree.
///
/// You can use [`TaprootSpendInfo::from_node_info`] to a get [`TaprootSpendInfo`] /// You can use [`TaprootSpendInfo::from_node_info`] to a get [`TaprootSpendInfo`]
/// from the merkle root [`NodeInfo`]. /// from the merkle root [`NodeInfo`].
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct NodeInfo { pub struct NodeInfo {
/// Merkle Hash for this node /// Merkle hash for this node.
pub(crate) hash: sha256::Hash, pub(crate) hash: sha256::Hash,
/// information about leaves inside this node /// Information about leaves inside this node.
pub(crate) leaves: Vec<LeafInfo>, pub(crate) leaves: Vec<LeafInfo>,
} }
@ -569,20 +576,20 @@ impl NodeInfo {
} }
} }
// Internally used structure to store information about taproot leaf node /// Store information about taproot leaf node.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub(crate) struct LeafInfo { pub(crate) struct LeafInfo {
// The underlying script /// The underlying script.
pub(crate) script: Script, pub(crate) script: Script,
// The leaf version /// The leaf version.
pub(crate) ver: LeafVersion, pub(crate) ver: LeafVersion,
// The merkle proof(hashing partners) to get this node /// The merkle proof (hashing partners) to get this node.
pub(crate) merkle_branch: TaprootMerkleBranch, pub(crate) merkle_branch: TaprootMerkleBranch,
} }
impl LeafInfo { impl LeafInfo {
// Create an instance of Self from Script with default version and no merkle branch /// Creates an new [`LeafInfo`] from `script` and `ver` and no merkle branch.
fn new(script: Script, ver: LeafVersion) -> Self { fn new(script: Script, ver: LeafVersion) -> Self {
Self { Self {
script: script, script: script,
@ -591,27 +598,27 @@ impl LeafInfo {
} }
} }
// Compute a leaf hash for the given leaf /// Computes a leaf hash for this [`LeafInfo`].
fn hash(&self) -> sha256::Hash { fn hash(&self) -> sha256::Hash {
let leaf_hash = TapLeafHash::from_script(&self.script, self.ver); let leaf_hash = TapLeafHash::from_script(&self.script, self.ver);
sha256::Hash::from_inner(leaf_hash.into_inner()) sha256::Hash::from_inner(leaf_hash.into_inner())
} }
} }
/// The Merkle proof for inclusion of a tree in a taptree hash /// The merkle proof for inclusion of a tree in a taptree hash.
// The type of hash is sha256::Hash because the vector might contain // The type of hash is `sha256::Hash` because the vector might contain both `TapBranchHash` and
// both TapBranchHash and TapLeafHash // `TapLeafHash`.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TaprootMerkleBranch(Vec<sha256::Hash>); pub struct TaprootMerkleBranch(Vec<sha256::Hash>);
impl TaprootMerkleBranch { impl TaprootMerkleBranch {
/// Obtain a reference to inner /// Returns a reference to the inner vector of hashes.
pub fn as_inner(&self) -> &[sha256::Hash] { pub fn as_inner(&self) -> &[sha256::Hash] {
&self.0 &self.0
} }
/// Create a merkle proof from slice /// Creates a merkle proof from raw data representing a list of hashes.
pub fn from_slice(sl: &[u8]) -> Result<Self, TaprootError> { pub fn from_slice(sl: &[u8]) -> Result<Self, TaprootError> {
if sl.len() % TAPROOT_CONTROL_NODE_SIZE != 0 { if sl.len() % TAPROOT_CONTROL_NODE_SIZE != 0 {
Err(TaprootError::InvalidMerkleBranchSize(sl.len())) Err(TaprootError::InvalidMerkleBranchSize(sl.len()))
@ -630,7 +637,11 @@ impl TaprootMerkleBranch {
} }
} }
/// Serialize to a writer. Returns the number of bytes written /// Serializes to a writer.
///
/// # Returns
///
/// The number of bytes written to the writer.
pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> { pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> {
for hash in self.0.iter() { for hash in self.0.iter() {
writer.write_all(hash)?; writer.write_all(hash)?;
@ -638,12 +649,12 @@ impl TaprootMerkleBranch {
Ok(self.0.len() * sha256::Hash::LEN) Ok(self.0.len() * sha256::Hash::LEN)
} }
/// Serialize self as bytes /// Serializes `self` as bytes.
pub fn serialize(&self) -> Vec<u8> { pub fn serialize(&self) -> Vec<u8> {
self.0.iter().map(|e| e.as_inner()).flatten().map(|x| *x).collect::<Vec<u8>>() self.0.iter().map(|e| e.as_inner()).flatten().map(|x| *x).collect::<Vec<u8>>()
} }
// Internal function to append elements to proof /// Appends elements to proof.
fn push(&mut self, h: sha256::Hash) -> Result<(), TaprootBuilderError> { fn push(&mut self, h: sha256::Hash) -> Result<(), TaprootBuilderError> {
if self.0.len() >= TAPROOT_CONTROL_MAX_NODE_COUNT { if self.0.len() >= TAPROOT_CONTROL_MAX_NODE_COUNT {
Err(TaprootBuilderError::InvalidMerkleTreeDepth(self.0.len())) Err(TaprootBuilderError::InvalidMerkleTreeDepth(self.0.len()))
@ -653,8 +664,11 @@ impl TaprootMerkleBranch {
} }
} }
/// Create a MerkleProof from Vec<[`sha256::Hash`]>. Returns an error when /// Creates a merkle proof from list of hashes.
/// inner proof len is more than TAPROOT_CONTROL_MAX_NODE_COUNT (128) ///
/// # Errors
///
/// If inner proof length is more than [`TAPROOT_CONTROL_MAX_NODE_COUNT`] (128).
pub fn from_inner(inner: Vec<sha256::Hash>) -> Result<Self, TaprootError> { pub fn from_inner(inner: Vec<sha256::Hash>) -> Result<Self, TaprootError> {
if inner.len() > TAPROOT_CONTROL_MAX_NODE_COUNT { if inner.len() > TAPROOT_CONTROL_MAX_NODE_COUNT {
Err(TaprootError::InvalidMerkleTreeDepth(inner.len())) Err(TaprootError::InvalidMerkleTreeDepth(inner.len()))
@ -663,35 +677,38 @@ impl TaprootMerkleBranch {
} }
} }
/// Consume Self to get Vec<[`sha256::Hash`]> /// Returns the inner list of hashes.
pub fn into_inner(self) -> Vec<sha256::Hash> { pub fn into_inner(self) -> Vec<sha256::Hash> {
self.0 self.0
} }
} }
/// Control Block data structure used in Tapscript satisfaction /// Control block data structure used in Tapscript satisfaction.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ControlBlock { pub struct ControlBlock {
/// The tapleaf version, /// The tapleaf version.
pub leaf_version: LeafVersion, pub leaf_version: LeafVersion,
/// The parity of the output key (NOT THE INTERNAL KEY WHICH IS ALWAYS XONLY) /// The parity of the output key (NOT THE INTERNAL KEY WHICH IS ALWAYS XONLY).
pub output_key_parity: secp256k1::Parity, pub output_key_parity: secp256k1::Parity,
/// The internal key /// The internal key.
pub internal_key: UntweakedPublicKey, pub internal_key: UntweakedPublicKey,
/// The merkle proof of a script associated with this leaf /// The merkle proof of a script associated with this leaf.
pub merkle_branch: TaprootMerkleBranch, pub merkle_branch: TaprootMerkleBranch,
} }
impl ControlBlock { impl ControlBlock {
/// Obtain a ControlBlock from slice. This is an extra witness element /// Constructs a `ControlBlock` from slice. This is an extra witness element that provides the
/// that provides the proof that taproot script pubkey is correctly computed /// proof that taproot script pubkey is correctly computed with some specified leaf hash. This
/// with some specified leaf hash. This is the last element in /// is the last element in taproot witness when spending a output via script path.
/// taproot witness when spending a output via script path.
/// ///
/// # Errors: /// # Errors
/// - If the control block size is not of the form 33 + 32m where ///
/// 0 <= m <= 128, InvalidControlBlock is returned /// - `TaprootError::InvalidControlBlockSize` if `sl` is not of size 1 + 32 + 32N for any N >= 0.
/// - `TaprootError::InvalidParity` if first byte of `sl` is not a valid output key parity.
/// - `TaprootError::InvalidTaprootLeafVersion` if first byte of `sl` is not a valid leaf version.
/// - `TaprootError::InvalidInternalKey` if internal key is invalid (first 32 bytes after the parity byte).
/// - `TaprootError::InvalidMerkleTreeDepth` if merkle tree is too deep (more than 128 levels).
pub fn from_slice(sl: &[u8]) -> Result<ControlBlock, TaprootError> { pub fn from_slice(sl: &[u8]) -> Result<ControlBlock, TaprootError> {
if sl.len() < TAPROOT_CONTROL_BASE_SIZE if sl.len() < TAPROOT_CONTROL_BASE_SIZE
|| (sl.len() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE != 0 || (sl.len() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE != 0
@ -712,13 +729,17 @@ impl ControlBlock {
}) })
} }
/// Obtain the size of control block. Faster and more efficient than calling /// Returns the size of control block. Faster and more efficient than calling
/// serialize() followed by len(). Can be handy for fee estimation /// `Self::serialize().len()`. Can be handy for fee estimation.
pub fn size(&self) -> usize { pub fn size(&self) -> usize {
TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * self.merkle_branch.as_inner().len() TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * self.merkle_branch.as_inner().len()
} }
/// Serialize to a writer. Returns the number of bytes written /// Serializes to a writer.
///
/// # Returns
///
/// The number of bytes written to the writer.
pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> { pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> {
let first_byte: u8 = i32::from(self.output_key_parity) as u8 | self.leaf_version.to_consensus(); let first_byte: u8 = i32::from(self.output_key_parity) as u8 | self.leaf_version.to_consensus();
writer.write_all(&[first_byte])?; writer.write_all(&[first_byte])?;
@ -727,19 +748,21 @@ impl ControlBlock {
Ok(self.size()) Ok(self.size())
} }
/// Serialize the control block. This would be required when /// Serializes the control block.
/// using ControlBlock as a witness element while spending an output via ///
/// script path. This serialization does not include the VarInt prefix that would be /// This would be required when using [`ControlBlock`] as a witness element while spending an
/// applied when encoding this element as a witness. /// output via script path. This serialization does not include the [`::VarInt`] prefix that would
/// be applied when encoding this element as a witness.
pub fn serialize(&self) -> Vec<u8> { pub fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.size()); let mut buf = Vec::with_capacity(self.size());
self.encode(&mut buf).expect("writers don't error"); self.encode(&mut buf).expect("writers don't error");
buf buf
} }
/// Verify that a control block is correct proof for a given output key and script /// Verifies that a control block is correct proof for a given output key and script.
/// This only checks that script is contained inside the taptree described by ///
/// output key, full verification must also execute the script with witness data /// Only checks that script is contained inside the taptree described by output key. Full
/// verification must also execute the script with witness data.
pub fn verify_taproot_commitment<C: secp256k1::Verification>( pub fn verify_taproot_commitment<C: secp256k1::Verification>(
&self, &self,
secp: &Secp256k1<C>, secp: &Secp256k1<C>,
@ -791,7 +814,7 @@ impl FutureLeafVersion {
} }
} }
/// Get consensus representation of the future leaf version. /// Returns the consensus representation of this [`FutureLeafVersion`].
#[inline] #[inline]
pub fn to_consensus(self) -> u8 { pub fn to_consensus(self) -> u8 {
self.0 self.0
@ -819,31 +842,23 @@ impl fmt::UpperHex for FutureLeafVersion {
} }
} }
/// The leaf version for tapleafs /// The leaf version for tapleafs.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LeafVersion { pub enum LeafVersion {
/// BIP-342 tapscript /// BIP-342 tapscript.
TapScript, TapScript,
/// Future leaf version /// Future leaf version.
Future(FutureLeafVersion) Future(FutureLeafVersion)
} }
impl LeafVersion { impl LeafVersion {
/// Obtain LeafVersion from consensus byte representation. /// Creates a [`LeafVersion`] from consensus byte representation.
/// ///
/// # Errors /// # Errors
///
/// - If the last bit of the `version` is odd. /// - If the last bit of the `version` is odd.
/// - If the `version` is 0x50 ([`TAPROOT_ANNEX_PREFIX`]). /// - If the `version` is 0x50 ([`TAPROOT_ANNEX_PREFIX`]).
// Text from BIP341:
// In order to support some forms of static analysis that rely on
// being able to identify script spends without access to the output being
// spent, it is recommended to avoid using any leaf versions that would conflict
// with a valid first byte of either a valid P2WPKH pubkey or a valid P2WSH script
// (that is, both v and v | 1 should be an undefined, invalid or disabled opcode
// or an opcode that is not valid as the first opcode).
// The values that comply to this rule are the 32 even values between
// 0xc0 and 0xfe and also 0x66, 0x7e, 0x80, 0x84, 0x96, 0x98, 0xba, 0xbc, 0xbe
pub fn from_consensus(version: u8) -> Result<Self, TaprootError> { pub fn from_consensus(version: u8) -> Result<Self, TaprootError> {
match version { match version {
TAPROOT_LEAF_TAPSCRIPT => Ok(LeafVersion::TapScript), TAPROOT_LEAF_TAPSCRIPT => Ok(LeafVersion::TapScript),
@ -852,7 +867,7 @@ impl LeafVersion {
} }
} }
/// Get consensus representation of the [`LeafVersion`]. /// Returns the consensus representation of this [`LeafVersion`].
pub fn to_consensus(self) -> u8 { pub fn to_consensus(self) -> u8 {
match self { match self {
LeafVersion::TapScript => TAPROOT_LEAF_TAPSCRIPT, LeafVersion::TapScript => TAPROOT_LEAF_TAPSCRIPT,
@ -884,7 +899,7 @@ impl fmt::UpperHex for LeafVersion {
} }
} }
/// Serializes LeafVersion as u8 using consensus encoding /// Serializes [`LeafVersion`] as a `u8` using consensus encoding.
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl ::serde::Serialize for LeafVersion { impl ::serde::Serialize for LeafVersion {
@ -896,7 +911,7 @@ impl ::serde::Serialize for LeafVersion {
} }
} }
/// Deserializes LeafVersion as u8 using consensus encoding /// Deserializes [`LeafVersion`] as a `u8` using consensus encoding.
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> ::serde::Deserialize<'de> for LeafVersion { impl<'de> ::serde::Deserialize<'de> for LeafVersion {
@ -926,20 +941,20 @@ impl<'de> ::serde::Deserialize<'de> for LeafVersion {
} }
} }
/// Detailed error type for taproot builder /// Detailed error type for taproot builder.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TaprootBuilderError { pub enum TaprootBuilderError {
/// Merkle Tree depth must not be more than 128 /// Merkle tree depth must not be more than 128.
InvalidMerkleTreeDepth(usize), InvalidMerkleTreeDepth(usize),
/// Nodes must be added specified in DFS order /// Nodes must be added specified in DFS walk order.
NodeNotInDfsOrder, NodeNotInDfsOrder,
/// Two nodes at depth 0 are not allowed /// Two nodes at depth 0 are not allowed.
OverCompleteTree, OverCompleteTree,
/// Invalid taproot internal key /// Invalid taproot internal key.
InvalidInternalKey(secp256k1::Error), InvalidInternalKey(secp256k1::Error),
/// Called finalize on an incomplete tree /// Called finalize on an incomplete tree.
IncompleteTree, IncompleteTree,
/// Called finalize on a empty tree /// Called finalize on a empty tree.
EmptyTree, EmptyTree,
} }
@ -974,22 +989,22 @@ impl fmt::Display for TaprootBuilderError {
#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl error::Error for TaprootBuilderError {} impl error::Error for TaprootBuilderError {}
/// Detailed error type for taproot utilities /// Detailed error type for taproot utilities.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TaprootError { pub enum TaprootError {
/// Proof size must be a multiple of 32 /// Proof size must be a multiple of 32.
InvalidMerkleBranchSize(usize), InvalidMerkleBranchSize(usize),
/// Merkle Tree depth must not be more than 128 /// Merkle tree depth must not be more than 128.
InvalidMerkleTreeDepth(usize), InvalidMerkleTreeDepth(usize),
/// The last bit of tapleaf version must be zero /// The last bit of tapleaf version must be zero.
InvalidTaprootLeafVersion(u8), InvalidTaprootLeafVersion(u8),
/// Invalid Control Block Size /// Invalid control block size.
InvalidControlBlockSize(usize), InvalidControlBlockSize(usize),
/// Invalid taproot internal key /// Invalid taproot internal key.
InvalidInternalKey(secp256k1::Error), InvalidInternalKey(secp256k1::Error),
/// Invalid parity for internal key /// Invalid parity for internal key.
InvalidParity(secp256k1::InvalidParityValue), InvalidParity(secp256k1::InvalidParityValue),
/// Empty TapTree /// Empty tap tree.
EmptyTree, EmptyTree,
} }