-
Notifications
You must be signed in to change notification settings - Fork 88
feat(compile): compile taproot descriptor with randomized unspendable internal key #225
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
base: master
Are you sure you want to change the base?
feat(compile): compile taproot descriptor with randomized unspendable internal key #225
Conversation
Pull Request Test Coverage Report for Build 21115332680Details
💛 - Coveralls |
tvpeter
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this @va-an
I have left a few comments.
110CodingP
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactoring the tests to usestd::process::Command instead of calling the internal function handle_compile_subcommand make the tests vulnerable to the state of environment variables and makes debugging harder in my opinion. But I have no strong disagreement to the approach in the PR.
My main idea is to have integration tests, not just unit tests, because that's the closest to how users actually interact with it. As for the environment variable state concern — we could remove related env vars to isolate integration tests, for example for compile tests: let output = Command::new("cargo")
.args(args)
.env_remove("NETWORK")
.env_remove("DATADIR")
.env_remove("POLICY")
.env_remove("TYPE")
.output()
.unwrap();This way we can isolate tests from the environment. Maintaining such tests might be a bit less convenient, but I promise to keep an eye on it :) |
That definitely makes sense.
Thanks! I did not know that we could remove them. I agree this could be helpful. Do you think it should be done in a separate PR though? maybe one that also refactors |
I would prefer to keep this PR as is, since @tvpeter has already looked at these changes. |
|
@110CodingP I decided to add the code for removing env vars in tests, as we discussed, so we don't forget to do this in future PRs. I verified locally that this works as expected — the command process will not receive these env vars from the parent process. use std::{env, process::Command};
fn main() {
unsafe {
env::set_var("FOO", "1");
env::set_var("BAR", "2");
env::set_var("BAZ", "3");
}
let cmd = Command::new("printenv")
.env_remove("FOO")
.env_remove("BAR")
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&cmd.stdout);
assert!(!stdout.contains("FOO"), "FOO should be removed");
assert!(!stdout.contains("BAR"), "BAR should be removed");
assert!(stdout.contains("BAZ"), "BAZ should still be present");
println!("it's all good man");
}Please take a look — afc76aa. |
tvpeter
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for updating the PR @va-an
Once you address the little comments, I will do a final test.
| .table() | ||
| .display() | ||
| .map_err(|e| Error::Generic(e.to_string()))?; | ||
| let mut rows = vec![vec!["Descriptor".cell().bold(true), descriptor.cell()]]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the descriptor value is quite long, can you apply the [shorten](https://github.com/bitcoindevkit/bdk-cli/blob/fb7f6c60ca4ac1ad80750391435bbafd7346c714/src/utils.rs#L381) fn so it displays better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! I also used shorten() for r.
I extracted shorten description into a variable to shorten only the internal key and leave the rest of the descriptor as is.
without pretty:
-> % cargo run --all-features -- compile "pk(ABC)" -t tr
{
"descriptor": "tr(1985c2a86e01bbcdb1b5806422531a73c77e568ecfdd6d2863c158a76628ea50,pk(ABC))#6qsjh5q3",
"r": "e38caa40d0db7cae7037336c982278a7fc6b770e7a80f8bbce0d79976a14d5e1"
}with pretty:
-> % cargo run --all-features -- compile "pk(ABC)" -t tr --pretty
+------------+----------------------------------+
| Descriptor | tr(ccb4...b1d0,pk(ABC))#qrk923py |
+------------+----------------------------------+
| r | a129...7a14 |
+------------+----------------------------------+| let output = Command::new("cargo") | ||
| .args(args) | ||
| .env_remove("NETWORK") | ||
| .env_remove("DATADIR") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: ig removing DATADIR env variable is not required? Since the compile command does not use it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, DATADIR isn't used in compile command.
I'm planning to reuse this command execution code in other tests, so I preemptively cleared all env vars.
| .map_err(|e| Error::Generic(e.to_string()))?; | ||
| let descriptor = shorten_descriptor.unwrap_or(descriptor); | ||
|
|
||
| let mut rows = vec![vec!["Descriptor".cell().bold(true), descriptor.cell()]]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let mut rows = vec![vec!["Descriptor".cell().bold(true), descriptor.cell()]]; | |
| let mut rows = vec![vec!["Descriptor".cell().bold(true), shorten(descriptor, 32, 29).cell()]]; |
you can remove: 908, 931-934, 946
you can also revert 951; it should size most screens comfortably.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I’ll add these changes when I do the squash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made the changes, and only then remembered why I did it this way — if we apply shorten globally (not just for tr), we'll get an error for other descriptors because we're trying to shorten an already too short string.
Here's an example of how to reproduce this (if you apply your suggested changes locally):
-> % cargo run --all-features -- compile "pk(ABC)" -t tr --pretty
+------------+------------------------------------------------------------------+
| Descriptor | tr(cf07afc46391a3ce22c1024a682ca...fc9486809e6,pk(ABC))#3qlg4fwz |
+------------+------------------------------------------------------------------+
| r | ee1c3591351b7c879fd415c0fbe3871bf61c752d7d35ed2ed94f0a278de9073d |
+------------+------------------------------------------------------------------+
-> % cargo run --all-features -- compile "pk(ABC)" -t wsh --pretty
thread 'main' (1258654) panicked at src/utils.rs:383:39:
byte index 32 is out of bounds of `wsh(pk(ABC))#8gmtcnaw`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtraceAlso, I'm not sure we should successfully compile a script like "pk(ABC)" at all, but we can discuss this in a separate issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I'm not sure we should successfully compile a script like "pk(ABC)" at all, but we can discuss this in a separate issue.
Well, policy compilation is actually from the upstream source but I have tested policies that failed. I will try to see if I can know what made this one to pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I'm not sure we should successfully compile a script like "pk(ABC)" at all, but we can discuss this in a separate issue.
Well, policy compilation is actually from the upstream source but I have tested policies that failed. I will try to see if I can know what made this one to pass.
I just noticed that bdk-cli can create a descriptor that bitcoin-cli doesn't accept as valid:
-> % bdk-cli compile "pk(ABC)"
{
"descriptor": "wsh(pk(ABC))#8gmtcnaw"
}
-> % bitcoin-cli deriveaddresses "wsh(pk(ABC))#8gmtcnaw"
error code: -5
error message:
pk(): key 'ABC' is not validThis might confuse some users.
As a simple fix, we could explicitly state in the compile command documentation that we don't validate keys at compile time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tvpeter
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @va-an,
Could you please squash your commits into two or three commits and fix the shortening approach?
Aside from that, it looks good to go. Thank you.
787c701 to
b5dac76
Compare
Squashed into 2 commits, but without the shortening approach changes - I've replied about that in the discussion above. |
Description
Resolves #218 (part 1/2).
Depends on #203 (
shorten()fix).Implements randomization of the unspendable internal key for taproot descriptors. This is the first part of #218, which consists of two parts:
Split into separate PRs for easier review and iteration, and to allow independent discussion of the verification command implementation, as one of the possible approaches could introduce breaking changes.
Notes to the reviewers
The
compilecommand now returns an additionalrfield for taproot descriptors (-t tr), containing the randomly generated internal key. Each compilation will produce a different internal key instead of using a fixed NUMS key.Example output for taproot (first execution):
Same descriptor compiled again produces different
rand internal key:Other descriptor types remain unchanged:
Tests for
compilecommand have been moved fromhandlers.rsto thetestsdirectory. Since taproot descriptors now generate a random internal key on each invocation, the test for thecompilecommand has been simplified. I plan to enhance this test in a follow-up PR once the verification command is implemented.Changelog notice
Checklists
All Submissions:
cargo fmtandcargo clippybefore committingNew Features:
CHANGELOG.md