Skip to content

Taproot descriptor#267

Closed
SarcasticNastik wants to merge 2 commits intorust-bitcoin:masterfrom
SarcasticNastik:taproot-descriptor
Closed

Taproot descriptor#267
SarcasticNastik wants to merge 2 commits intorust-bitcoin:masterfrom
SarcasticNastik:taproot-descriptor

Conversation

@SarcasticNastik
Copy link
Copy Markdown
Contributor

@SarcasticNastik SarcasticNastik commented Aug 30, 2021

This defines tree structure for TapTree and implements parsing of Taproot Descriptors as a part of #255 . The purpose is to get basic functionalities for the same and integrate it with the Miniscript compiler.

Feedback about changes to tr.rs and expression.rs would be really appreciated.

  • Currently helper functions specific to Tr are required to parse taproot descriptor into Tree (as defined in expression.rs).

TODO

  • Docs for tr.rs
  • Pass fuzz tests
  • Include hex checksum for taproot descriptor.

Future Work

  • Implement functionalities required to integrate it with Descriptor (in mod.rs)

@SarcasticNastik SarcasticNastik marked this pull request as ready for review August 31, 2021 17:50
@SarcasticNastik
Copy link
Copy Markdown
Contributor Author

@sanket1729 Kindly review this PR while I add the required docs for tr.rs.

Copy link
Copy Markdown
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. I just did a partial review and left some specific comments in the first commit.
Here are some general remarks:

In general, commits should be atomic (and they should just do one thing and the one thing completely in that commit) and they should do pass all tests/build.

A good way to test this is via -x option in git rebase.

git rebase -i HEAD~4 -x "cargo test --features=compiler". This will stop the code at the commit where the build/test fails, you can fix the changes there and git rebase --continue.

It might take a while to get familiar with advanced git techniques, but it is an important skill in the long run. Every project uses this method so that it is easier for tools like git bisect(essentially a binary search on commits) to figure out errors when something goes wrong and figure out which commit things went bad.

I think for starters, you can separate this into two commits. Adding the code and later another commit for adding the test.

Comment thread src/descriptor/mod.rs
mod segwitv0;
mod sh;
mod sortedmulti;
mod tr;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 73dd23e,
You should be exposing public utilities from private module tr to the external user. In particular, I think you need to expose TapTree/Tr.

A good exercise might be to write an example (see /examples folder) to see how an external user might use the library.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still not addressed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need a statement with pub use self::tr::{TapTree} and all other utilities that need to be exposed.

Comment thread src/descriptor/tr.rs Outdated
@@ -0,0 +1,201 @@
// Tapscript

// use super::{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary commented code here in 73dd23e

Comment thread src/descriptor/tr.rs Outdated
use {miniscript::Miniscript, Error, MiniscriptKey};

// TODO: Update this to infer version from descriptor.
const VER: u8 = 0xc0;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 73dd23e,
The above two line should be removed.

Comment thread src/descriptor/tr.rs Outdated
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum TapTree<Pk: MiniscriptKey> {
Tree(Arc<TapTree<Pk>>, Arc<TapTree<Pk>>),
Miniscript_(u8, Arc<Miniscript<Pk, Segwitv0>>),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 73dd23e,
The leaf version should be removed from Miniscript, and I think we can name the enum variants as Miniscript_ as Leaf

Comment thread src/descriptor/tr.rs Outdated

#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Tr<Pk: MiniscriptKey> {
key_path: Pk,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename as internal_key and tree? key_path and script_path represent the ways in which we spend scripts, not the structures.

Comment thread src/descriptor/tr.rs Outdated
Pk::Hash: FromStr,
<Pk as FromStr>::Err: ToString,
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about the bounds here.

Comment thread src/descriptor/tr.rs
Pk::Hash: FromStr,
<Pk as FromStr>::Err: ToString,
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about the bounds here. Try to separate out the functions that really need the bound into its own impl. For example, the new function Tr::new() should not need this bound.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also make accessor function for fields in Tr as those are private

Comment thread src/descriptor/tr.rs Outdated
<Pk as FromStr>::Err: ToString,
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
{
pub fn new(key_path: Pk, script_path: Option<TapTree<Pk>>) -> Result<Self, Error> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this check and return an error when this does not hold.
https://github.com/bitcoin/bitcoin/blob/81f4a3e84d6f30e7b12a9605dabc3359f614da93/src/script/interpreter.h#L229.

Introduce this constant in limits.rs

Right now, you can implement a simple depth/height function on Taptree.

We can cache the height/depth in the future.

Comment thread src/expression.rs
impl<'a> Tree<'a> {
fn from_slice(sl: &'a str) -> Result<(Tree<'a>, &'a str), Error> {
Self::from_slice_helper(sl, 0u32)
pub fn from_slice(sl: &'a str) -> Result<(Tree<'a>, &'a str), Error> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 73dd23e, the function signature should be changed in this commit itself.

Comment thread src/lib.rs
#![deny(missing_docs)]
// #![deny(dead_code)]
// #![deny(unused_imports)]
// #![deny(missing_docs)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in 73dd23e, this is fine.

Adding this change to check this is re-enabled again.

Copy link
Copy Markdown
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some more remarks, we are getting closer :)

Comment thread src/descriptor/tr.rs Outdated
match tree {
TapTree::Tree(ref left_tree, ref right_tree) => {
1 + max(
Self::taptree_height(&**left_tree),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more idiomatic to call left_tree.taptree_height()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and same for right tree below

Comment thread src/descriptor/tr.rs
};

if nodes > TAPROOT_MAX_NODE_COUNT {
Ok(Self { internal_key, tree })
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If condition is reversed. It should be error if nodes > TAPROOT_MAX_NODE_COUNT

Comment thread src/descriptor/tr.rs Outdated
}
}

pub fn get_internal_key(&self) -> Result<Pk, Error> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for this function to return a result, it should return &Pk.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In rust, typically the name get is not added for function names. See https://rust-lang.github.io/api-guidelines/naming.html#getter-names-follow-rust-convention-c-getter if you are interested for more guidelines.

Comment thread src/descriptor/tr.rs Outdated
Ok(self.internal_key.clone())
}

pub fn get_taptree(&self) -> Result<TapTree<Pk>, Error> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should option and reference to TapTree. In general, try to avoid cloning(extra allocation) as possible

Comment thread src/descriptor/tr.rs Outdated
Tree { name, args } if name.len() > 0 && args.len() == 0 => {
let script = name;
let script = Self::parse_miniscript(script)?;
let script = Arc::new(script);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: slight code preference for inlining some operations. You don't really need the function parse_miniscript. You can call

Miniscript::<Pk, Ctx>::from_str()

to get the miniscript directly. The function already does all the checks that you implement here. Also, the method parse_method does make sense itself inside an impl for TapTree

Comment thread src/descriptor/tr.rs Outdated
let left_tree = Self::tr_script_path(&left_branch)?;
let right_tree = Self::tr_script_path(&right_branch)?;
let left_ref = Arc::new(left_tree);
let right_ref = Arc::new(right_tree);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Prefer inlining some code instead of 6 lines.

let left = Self::tr_script_path(&args[0])?;
..
Ok(TapTree::Tree(Arc::new(left_ref), Arc::new(right_ref)))

Comment thread src/descriptor/tr.rs Outdated
}
}

return if s.len() > 3 && &s[..3] == "tr(" && s.as_bytes()[s.len() - 1] == b')' {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assign let ret = expr; and do a return statement in the end

Comment thread src/descriptor/tr.rs
if found == inp.len() - 1 {
Some((&inp[..], &""))
} else {
Some((&inp[..found], &inp[found + 1..]))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment that this deals with the case when the , is the last character.

@SarcasticNastik SarcasticNastik force-pushed the taproot-descriptor branch 2 times, most recently from c75ffcc to ff365fd Compare September 16, 2021 05:22
@SarcasticNastik
Copy link
Copy Markdown
Contributor Author

SarcasticNastik commented Sep 16, 2021

@sanket1729 I have made the suggested changes. Apart from that,

  • In the tr_roundtrip_tree test, using DummyKey caused AnalysisError::RepeatedPubKeys error (I think we have to turn that off while using DummyKey) so I changed the test to use PublicKey accordingly.

Copy link
Copy Markdown
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is good overall. I can take it over from here and update the PR once #255 is merged.

Comment thread src/descriptor/mod.rs
mod segwitv0;
mod sh;
mod sortedmulti;
mod tr;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need a statement with pub use self::tr::{TapTree} and all other utilities that need to be exposed.

Comment thread src/descriptor/tr.rs
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum TapTree<Pk: MiniscriptKey> {
Tree(Arc<TapTree<Pk>>, Arc<TapTree<Pk>>),
Leaf(Arc<Miniscript<Pk, Segwitv0>>),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sanket1729: I assume we can't use Segwitv0 here. Neither Tap, since tapscript is only valid for leafs with hash version 0xC0. No idea how to solve this problem without refactoring the whole context system

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dr-orlovsky. This is being done in #278 now

@sanket1729 sanket1729 mentioned this pull request Jan 21, 2022
@sanket1729
Copy link
Copy Markdown
Member

This commit was merged as a part of #278.

@sanket1729 sanket1729 closed this Mar 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants