Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Taproot descriptor #267

Closed

Conversation

SarcasticNastik
Copy link
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
Contributor Author

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

Copy link
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.

@@ -45,6 +45,7 @@ mod bare;
mod segwitv0;
mod sh;
mod sortedmulti;
mod tr;
Copy link
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
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
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.

@@ -0,0 +1,201 @@
// Tapscript

// use super::{
Copy link
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

use {miniscript::Miniscript, Error, MiniscriptKey};

// TODO: Update this to infer version from descriptor.
const VER: u8 = 0xc0;
Copy link
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.

#[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
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


#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Tr<Pk: MiniscriptKey> {
key_path: Pk,
Copy link
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.

Pk::Hash: FromStr,
<Pk as FromStr>::Err: ToString,
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
{
Copy link
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.

Pk::Hash: FromStr,
<Pk as FromStr>::Err: ToString,
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
{
Copy link
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
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

<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
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.

@@ -38,11 +38,29 @@ pub trait FromTree: Sized {
}

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
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.

#![deny(missing_docs)]
// #![deny(dead_code)]
// #![deny(unused_imports)]
// #![deny(missing_docs)]
Copy link
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
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 :)

match tree {
TapTree::Tree(ref left_tree, ref right_tree) => {
1 + max(
Self::taptree_height(&**left_tree),
Copy link
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
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

};

if nodes > TAPROOT_MAX_NODE_COUNT {
Ok(Self { internal_key, tree })
Copy link
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

}
}

pub fn get_internal_key(&self) -> Result<Pk, Error> {
Copy link
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
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.

Ok(self.internal_key.clone())
}

pub fn get_taptree(&self) -> Result<TapTree<Pk>, Error> {
Copy link
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

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
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

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
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)))

}
}

return if s.len() > 3 && &s[..3] == "tr(" && s.as_bytes()[s.len() - 1] == b')' {
Copy link
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

if found == inp.len() - 1 {
Some((&inp[..], &""))
} else {
Some((&inp[..found], &inp[found + 1..]))
Copy link
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
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
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.

@@ -45,6 +45,7 @@ mod bare;
mod segwitv0;
mod sh;
mod sortedmulti;
mod tr;
Copy link
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.

@dr-orlovsky dr-orlovsky added this to In process in Taproot Sep 25, 2021
#[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
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
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

@dr-orlovsky dr-orlovsky moved this from In process to In review in Taproot Jan 10, 2022
@sanket1729 sanket1729 mentioned this pull request Jan 21, 2022
@sanket1729
Copy link
Member

This commit was merged as a part of #278.

@sanket1729 sanket1729 closed this Mar 9, 2022
Taproot automation moved this from In review to Done Mar 9, 2022
@dr-orlovsky dr-orlovsky moved this from Done to Rejected in Taproot Mar 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Taproot
Rejected
Development

Successfully merging this pull request may close these issues.

None yet

3 participants