-
-
Notifications
You must be signed in to change notification settings - Fork 26
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
Descriptor support #37
Conversation
src/config.rs
Outdated
@@ -95,6 +95,16 @@ pub struct Config { | |||
)] | |||
pub bitcoind_cookie: Option<path::PathBuf>, | |||
|
|||
#[structopt( | |||
short = "D", |
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.
How's -D
for this?
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 think this important enough to deserve getting the lowercase -d
. We can change --bitcoind-dir
to use -D
.
Also, this should be --descriptor
(singular form, not plural).
@@ -181,111 +181,59 @@ impl HDWallet { | |||
} | |||
} | |||
|
|||
pub fn from_bare_xpub( |
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.
What should I do about these two? Just guess the descriptor based on extended pubkey version (xpub/ypub/zpub etc) and call from_descriptor
?
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.
Basically yes, figure out the appropriate descriptor(s) (two in the case of --xpub
, one for --bare-xpub
) and delegate to from_descriptor
. Note that ypubs/zpubs will have to be provided in their xpub form (this is already case if encode the ExtendedPubKey
to a string).
There should also be an ext_key: Option<XyzPubKey>
field on the Wallet
struct set for wallets that originated from an xpub, for backward compatibility. But lets leave that to later for now.
src/types.rs
Outdated
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "{}", self.0) | ||
} | ||
} |
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 expect I'm doing lots of noobish stuff in here ...
src/types.rs
Outdated
} | ||
|
||
#[derive(Serialize, Clone, Eq, PartialEq, Debug, Hash)] | ||
pub struct Descriptor(pub String); |
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.
Perhaps this should be an enum of "ranged" and "unranged" descriptors?
We could distinguish by matching *
inside the string?
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 think this could be a boolean field. getdescriptorinfo
returns an isrange
field that we could use instead of matching *
.
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 agree that could work, but I'm going to leave this work to a future PR
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.
This could be From<&str>
or From<String>
since the conversion is not fallible. (they return T
directly and not a Result<T>
like FromStr
.)
Though, if its just wrapping a String
, this could also simply be constructed using DescriptorChecksum(cs_string)
instead of DescriptorChecksum::from(cs_string)
.
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.
Actually, it seems like you are in fact constructing them with DescriptorChecksum(cs_string)
, the conversion method could simply be removed.
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.
Very nice! Thanks for sending this. :)
I added some comments and suggestions, let me know what you think.
src/config.rs
Outdated
@@ -95,6 +95,16 @@ pub struct Config { | |||
)] | |||
pub bitcoind_cookie: Option<path::PathBuf>, | |||
|
|||
#[structopt( | |||
short = "D", |
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 think this important enough to deserve getting the lowercase -d
. We can change --bitcoind-dir
to use -D
.
Also, this should be --descriptor
(singular form, not plural).
src/app.rs
Outdated
let wallets = HDWallet::from_xpubs( | ||
&config.xpubs[..], | ||
&config.bare_xpubs[..], | ||
let rpc = RpcClient::new(config.bitcoind_url(), config.bitcoind_auth()?)?; |
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 can wrap this in a Arc::new()
here instead of doing this later. It'll automatically get decast to an &RpcClient
when passed to from_descriptors()
.
@@ -345,6 +355,16 @@ fn parse_xpub(s: &str) -> Result<(XyzPubKey, RescanSince)> { | |||
Ok((xpub, rescan)) | |||
} | |||
|
|||
fn parse_descriptor(s: &str) -> Result<(String, RescanSince)> { | |||
let mut parts = s.splitn(2, ':'); |
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.
Could descriptors contains :
? I can't seem to find any descriptors that do, but :
appears to be included in the INPUT_CHARSET
used for checksumming.
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.
:
separates the descriptor and the "rescan since" timestamp. I just copied parse_xpub
(link).
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.
Yes, I understood, I asked whether descriptors can contain :
because if they do, the :
separator would be ambiguous with the syntax of the descriptor itself.
But AFAICT, descriptors cannot contain :
, so this shouldn't be ambiguous.
let max_funded_index = wallet.max_funded_index?; // return None if this wallet has no history at all | ||
|
||
let gap = (0..=max_funded_index) | ||
.map(|derivation_index| ScriptHash::from(&wallet.derive_address(derivation_index))) | ||
.map(|derivation_index| { | ||
ScriptHash::from(&wallet.derive_address(derivation_index, &self.rpc).unwrap()) |
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.
Similarly to get_hd_script_info()
, this now also need to be a fallible method that returns Result
due to the reliance on RPC (and unwrap()
also needs to be changed accordingly).
Which somewhat makes me reconsider whether we should do descriptor derivation in Rust without relying on the RPC... relying on the RPC will result in a cascade of functions that now need to be fallible, which is kind of annoying. Also, for find_hd_gap()
, this is going to require many RPC calls, which is not ideal either.
But let's wait with this for now and give this some more thought.
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 agree that doing the derivation in rust is preferable. But I think we need to wait until apoelstra/rust-miniscript#93 lands.
@@ -345,6 +355,16 @@ fn parse_xpub(s: &str) -> Result<(XyzPubKey, RescanSince)> { | |||
Ok((xpub, rescan)) | |||
} | |||
|
|||
fn parse_descriptor(s: &str) -> Result<(String, RescanSince)> { | |||
let mut parts = s.splitn(2, ':'); | |||
let descriptor = String::from(parts.next().unwrap()); |
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 two unwraps here should instead return an Err
, similarly to how its done in parse_xpub
.
1e9c0ed
to
a957961
Compare
src/hd.rs
Outdated
Standalone, | ||
// bwt never does hardended derivation itself, but can receive an hardend --bare-xpub | ||
DerivedHard(Fingerprint, u32), | ||
DerivedHard(DescriptorChecksum, u32), |
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 DerivedHard
variant is no longer necessary, the descriptor itself already includes this information.
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.
Fixed
src/types.rs
Outdated
|
||
pub fn checksum(&self) -> DescriptorChecksum { | ||
// This assumes that the descriptor is legit ... | ||
DescriptorChecksum(self.0.split("#").collect::<Vec<_>>()[1].to_string()) |
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.
This would cause a panic if the descriptor is somehow invalid. How about adding a separate checksum field to the Descriptor
struct and populating it in the constructor? We're already getting the checksum as a separate field from getdescriptorinfo
.
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.
Fixed
- desc_check -> descr_cs - 'hd'-> 'wallet' in HTTP API - Return `Result` where appropriate - Replaced a few `.unwrap` with `?` - Fix origin mapping and remove KeyOrigin::DerivedHard - HDWallet.from_config accepts descriptors, xpubs, and bare xpubs - Swap CLI -d and -D shorthands
src/hd.rs
Outdated
rpc.clone(), | ||
) | ||
.with_context(|| format!("Invalid xpub-derived descriptor {}", descriptor))? | ||
.clone(), |
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.
Why is clone
needed here?
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.
Because I'm a noob. Fixed.
src/hd.rs
Outdated
// Bare xpubs | ||
for (xyz, rescan) in bare_xpubs { | ||
let descriptor = match xyz.script_type { | ||
ScriptType::P2pkh => format!("pkh({})", xyz.extended_pubkey.to_string()), |
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.
/*
is missing here
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.
fixed
src/http.rs
Outdated
.or_err(StatusCode::NOT_FOUND)?; | ||
let next_index = wallet.get_next_index(); | ||
let uri = format!("/hd/{}/{}", fingerprint, next_index); | ||
let uri = format!("/hd/{}/{}", descr_cs, next_index); |
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.
/hd/
should be changed here too (and possibly some other places that construct a uri)
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.
Fixed here and in some comments.
rgb.serialize_field("network", &self.network)?; | ||
rgb.serialize_field("script_type", &self.script_type)?; |
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.
We technically don't need the custom serializer for HDWallet
now that it doesn't have the virtual origin
field, this could simply #[derive(Serialize)]
.
But since this will eventually be added back for compatibility with xpub-based usage, maybe better to keep this here for now anyway.
src/hd.rs
Outdated
// Xpubs | ||
for (xyz, rescan) in xpubs { | ||
// Change and receiving output descriptors | ||
for change in 0..2 { |
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 kinda prefer the readability of inclusive ranges for such cases (0..=1
), but don't feel strongly about this
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 agree. Fixed.
for change in 0..2 { | ||
let descriptor = match xyz.script_type { | ||
ScriptType::P2pkh => { | ||
format!("pkh({}/{}/*)", xyz.extended_pubkey.to_string(), change) |
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.
Perhaps this logic could be extracted from here, into something like Descriptor::from(XyzPubKey)
for the bare case and Descriptor::from((XyzPubKey, usize))
for the external/internal chains case?
Then you could use descriptor: xyz.into()
and descriptor: (xyz, change).into()
.
- Descriptor has "body" and "checksum" attributes - Forgot a /* turning bare xpubs into descriptors - Removed unnecessary clones - Rename "hd" -> "wallet" in a few more places
f9fe0ff
to
9b4c7fc
Compare
This PR is no longer relevant after this commit d305ec9#diff-30965b7e36f841dad79ecf3d968902df6d07bc7214a33ec928e77c82385fc2ca |
Leverage descriptors to communicate your addresses to bwt.