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 Compiler #291

Merged
merged 5 commits into from
May 19, 2022
Merged

Conversation

SarcasticNastik
Copy link
Contributor

@SarcasticNastik SarcasticNastik commented Jan 16, 2022

This PR builds on top of #278.

This is one in a series of PRs aimed to implement a feature-complete Pay-to-Taproot P2Tr compiler.
Specifically, this introduces a basic compilation for a given policy by collecting the leaf nodes of the tree generated by considering root-level disjunctive ORand using this to generate a TapTree.

Assuming that duplicate keys are NOT possible even in different branches of the TapTree.

Follow Up PRs

@dr-orlovsky dr-orlovsky added this to In process in Taproot Jan 16, 2022
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.

Did an initial review. I think we would need a few more iterations before we get this.

@@ -994,7 +995,9 @@ where
})
.collect();

if key_vec.len() == subs.len() && subs.len() <= MAX_PUBKEYS_PER_MULTISIG {
if Ctx::sig_type() == SigType::Schnorr {
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 explicit matching over all script arms instead of if else

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored in b36c116

use Miniscript;
#[cfg(feature = "compiler")]
use Tap;
Copy link
Member

Choose a reason for hiding this comment

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

nit: You can import all of these in one line.

#[cfg(feature = "compiler")]
use {}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored in b36c116

@@ -128,6 +137,33 @@ impl fmt::Display for PolicyError {
}

impl<Pk: MiniscriptKey> Policy<Pk> {
/// Single-Node compilation
#[cfg(feature = "compiler")]
fn compile_huffman_taptree(policy: &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.

It is a convention to use &self instead of &Self. &self stands for self: &Self

Copy link
Member

Choose a reason for hiding this comment

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

This function is called compile_huffman. I expect in the next commit it would do something different

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. The naming convention for the function was kept keeping future in mind. I'll rename it to compile_leaf_taptree for proper naming convention for this commit.

}
/// Extract the Taproot internal_key from policy tree.
#[cfg(feature = "compiler")]
fn extract_key(policy: &Self, unspendable_key: Option<Pk>) -> Result<(Pk, &Policy<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.

Some comment about &Self. This function should consume self. Functionally this should change the policy and extra the internal key out of it. I think this maybe done in the next commit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would require us to make the compile_tr function consume self which doesn't align with the compile<Pk, Ctx>(&self) function. I will update this function as suggested and use a cloned copy for the same until we discuss further.

src/policy/concrete.rs Outdated Show resolved Hide resolved
1,
vec![Policy::KeyHash("".to_owned()), Policy::Older(1000),]
)
Policy::Threshold(1, vec![Policy::KeyHash("".to_owned()), Policy::Older(1000)])
Copy link
Member

Choose a reason for hiding this comment

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

These changes in semantic.rs apart from pub(crate) one seem unnecessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. This code snippet was automatically formatted by cargo fmt (version: 1.58.0).

image

It was necessary to keep this change to pass the linter.

Copy link
Member

Choose a reason for hiding this comment

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

I see this is because you removed the "," at the end of all lines. This change is okay, but it is not a part of this commit. But I can live with this

Copy link
Contributor

Choose a reason for hiding this comment

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

i would recommend to bump 1.58.0 and then cargo fmt as one commit maybe?

src/policy/concrete.rs Outdated Show resolved Hide resolved

for key in concrete_keys.iter() {
if semantic_policy
.clone()
Copy link
Member

Choose a reason for hiding this comment

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

This algorithm is poor in memory performance because it calls clone for every single iteration. We can do a new tree traversal for every key we see. I think we will eventually need a better algorithm that avoids all these allocations.

We can first focus on the correctness/code hygine of the PR, then address these efficiency issues.

src/policy/concrete.rs Outdated Show resolved Hide resolved
src/policy/concrete.rs Outdated Show resolved Hide resolved
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.

Very close. Last few comments.

src/policy/compiler.rs Outdated Show resolved Hide resolved
src/policy/compiler.rs Outdated Show resolved Hide resolved
src/policy/concrete.rs Outdated Show resolved Hide resolved
.pop()
.expect("huffman tree algorithm is broken")
.1;
Ok((*node).clone())
Copy link
Member

Choose a reason for hiding this comment

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

suggested how to fix it above.

1,
vec![Policy::KeyHash("".to_owned()), Policy::Older(1000),]
)
Policy::Threshold(1, vec![Policy::KeyHash("".to_owned()), Policy::Older(1000)])
Copy link
Member

Choose a reason for hiding this comment

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

I see this is because you removed the "," at the end of all lines. This change is okay, but it is not a part of this commit. But I can live with this

@SarcasticNastik
Copy link
Contributor Author

SarcasticNastik commented Feb 25, 2022

The Lint+Fuzz test fails here because of this issue. Locally running the scripts work just fine.
Edit: #300 fixes the issue. Cherry-picked the commit into this branch.

@SarcasticNastik SarcasticNastik marked this pull request as ready for review February 25, 2022 05:56
@SarcasticNastik SarcasticNastik force-pushed the tr-compiler branch 3 times, most recently from 255fbed to 4b8c5fe Compare March 4, 2022 08:28
src/policy/compiler.rs Outdated Show resolved Hide resolved
@JeremyRubin
Copy link
Contributor

JeremyRubin commented Mar 9, 2022

shouldn't this try to compile the common: And(Or(a,b), Or(c,d)) to: ac ad bc bd?

how do we judge if the increased number of nodes vs script length cutoff makes sense?

It seems like there is a script length beyond which it always makes sense, maybe like 32 byte script?

the issue is that many scripts it'd be exp to actually do this unrolling, but it's what i'd expect.

@SarcasticNastik
Copy link
Contributor Author

SarcasticNastik commented Mar 16, 2022

@JeremyRubin You're right in the intuitive analysis of the method taproot compiler should ideally adopt. I plan on introducing this type to reduction/ enumeration of policy nodes in a follow-up PR. The reason to keep that on hold was to understand if doing so would turn out to be intractable. For example:- Consider a generic thresh(k, ...) k-of-n keys scenario. Enumerating this as separate nodes in disjunction results in nCk (exponential in k). This can very easily blow up. Any suggestions on this are welcome!
In addition, this PR just serves to provide one of the many compilation methods we plan to try/test upon. Another version of this compiler is underway which uses a heuristic to re-arrange the policy nodes and only compile the nodes (A and B) separately if resulting expected total cost (script_size + average satisfaction cost) of compilation into a taptree is lesser than compilation of a combination of the two (Or(A, B)).

@dr-orlovsky dr-orlovsky moved this from In process to In review in Taproot Mar 20, 2022
@dr-orlovsky
Copy link
Contributor

@sanket1729 is there plans of getting this into the release?

@sanket1729
Copy link
Member

@dr-orlovsky, no this won't go into the release. There is still some work to be done here, and the compiler implemented here is feature complete. We still have a few things we want before we support this.

Maybe we will have another release with support for partial descriptors, unspendable key support, musig descriptors etc... Possibly 1 month from now

let mut prob = 0.;
let semantic_policy = self.lift()?;
let concrete_keys = self.keys();
let key_prob_map: HashMap<_, _> = self
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The logic of internal-key extraction has been changed from selecting first-encountered key satisfying the policy to most-probable key satisfying the policy.

Copy link
Member

Choose a reason for hiding this comment

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

I see the ertract key is updated in next commit. No need to fix it here.

@sanket1729
Copy link
Member

Can you rebase on top of master.? And remove the last CI commit.

src/policy/concrete.rs Outdated Show resolved Hide resolved
f: T,
) -> Result<TapTree<Pk>, Error>
where
T: Fn(OrdF64) -> OrdF64,
Copy link
Member

Choose a reason for hiding this comment

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

Let's not have this confusing argument for now. And hard-code whatever dummy metric we have

src/policy/concrete.rs Outdated Show resolved Hide resolved
@SarcasticNastik
Copy link
Contributor Author

SarcasticNastik commented May 2, 2022

Changes in the force update:

  1. Updated documentation .
  2. Rebased on top of master and removed CI commit.
  3. Moved with_huffman_tree out of the impl block.
  4. Seperate internal_key_extraction and taptree compilation into different commits.

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.

Looks mostly good

src/policy/concrete.rs Outdated Show resolved Hide resolved
src/policy/concrete.rs Outdated Show resolved Hide resolved
@sanket1729
Copy link
Member

@SarcasticNastik can you also add in the description how all the PRs relate and what they do.

@SarcasticNastik SarcasticNastik force-pushed the tr-compiler branch 2 times, most recently from 3e9214c to 08c6530 Compare May 9, 2022 22:21
src/policy/concrete.rs Outdated Show resolved Hide resolved
@sanket1729
Copy link
Member

Sorry, @SarcasticNastik this would require a rebase again :( .

@apoelstra requesting your review. This is a major contribution from a new contributor and I don't want to go ahead without your review. The commit messages and PR description are helpful in understanding how this is structured.

@SarcasticNastik
Copy link
Contributor Author

SarcasticNastik commented May 10, 2022

@sanket1729 I've rebased the PR onto current master. @apoelstra A kind request for your review.

EDIT
Changes in the latest force-push 2b13694:

  1. Minor changes in doc-comments .
  2. Rebase on top of current master.

A new method `compile_tr` is introduced in policy for creating a Tr descriptor with TapTree containing single script child-node. An internal-key extraction method from the policy has also been implemented for the same.
The internal-key extraction method now selects the most-likely key (in the policy) as suggested by BIP341.
Introduce a `compiler_tr` API for compiling a policy to a Tr Descriptor.
Copy link
Member

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK 2b13694

Taproot automation moved this from In review to Ready for merge May 18, 2022
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.

ACK 2b13694 . Let's push forward on this. In the interest of ACKs and multiple rebases already done here, I am merging this. We recently shifted to rust 2018. And this code has a bunch of warnings because of it.

@sanket1729 sanket1729 merged commit 7efe5e0 into rust-bitcoin:master May 19, 2022
Taproot automation moved this from Ready for merge to Done May 19, 2022
SarcasticNastik added a commit to SarcasticNastik/rust-miniscript that referenced this pull request May 19, 2022
sanket1729 added a commit that referenced this pull request May 19, 2022
62a42bc Fix compiler warnings in PR #291 (Aman Rojjha)

Pull request description:

ACKs for top commit:
  sanket1729:
    utACK 62a42bc

Tree-SHA512: c64f51a5d9637bd8f511a200c0ef60cd66263e28b19951077ba43b4232a3fc7bc45381def7cea8386fc65adea150c99ea5d768599aa31b699c31e42c3f121fe2
@SarcasticNastik SarcasticNastik deleted the tr-compiler branch June 3, 2022 11:02
sanket1729 added a commit that referenced this pull request Jun 17, 2022
ef4249d Add taproot compiler example usage (Aman Rojjha)
cef2a5b Add Tr-compiler write-up and doc-comment (Aman Rojjha)
5233c66 Add Taproot compiler API (Aman Rojjha)

Pull request description:

  This PR builds on top of #291. This aims to introduce an efficient version of the tapscript compiler by using a few heuristics to optimize over the expected average total cost for the TapTree.

  ## Strategy implemented

  - While merging TapTrees `A` and `B`, check whether the compilation of `Policy::Or(policy(A), policy(B))` is more efficient than a TapTree with the respective children `A` and `B`.

  **Note**: This doesn't include the `thresh(k, ...n..)` enumeration strategy. Planning on working on it separately.

ACKs for top commit:
  sanket1729:
    ACK ef4249d

Tree-SHA512: ae5949b5170ff4cc8202434655af41b4938e96801209896d117bf4e4bd85c0becc6fd0c7affb100c31655f0085c1ff5e7c19184db3433b2831f73db22d94348d
sanket1729 added a commit that referenced this pull request Jun 20, 2022
169d849 Add policy to descriptor target compilation method (Aman Rojjha)

Pull request description:

  This PR works on top of  #291 and #342. It introduces a new function `compile_to_descriptor` which requires a `DescriptorCtx` (suggestions for a better enum name are welcome!) allowing directly compilation of a policy into the specified descriptor type.

ACKs for top commit:
  apoelstra:
    ACK 169d849
  sanket1729:
    ACK 169d849

Tree-SHA512: 0f895f4774ea4f56db76280ac756df616a70e5d7d60cc128f9a050ef86658f786b2d45885d748dba51751146a6e93b3731f4ae193b7d80284ffaea20be1e8f92
joemphilips pushed a commit to joemphilips/rust-miniscript that referenced this pull request Dec 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Development

Successfully merging this pull request may close these issues.

None yet

5 participants