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

Descriptor support #37

Closed
wants to merge 4 commits into from
Closed

Conversation

justinmoon
Copy link

@justinmoon justinmoon commented Jun 10, 2020

Leverage descriptors to communicate your addresses to bwt.

src/hd.rs Show resolved Hide resolved
src/config.rs Outdated
@@ -95,6 +95,16 @@ pub struct Config {
)]
pub bitcoind_cookie: Option<path::PathBuf>,

#[structopt(
short = "D",
Copy link
Author

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?

Copy link
Collaborator

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/hd.rs Outdated Show resolved Hide resolved
@@ -181,111 +181,59 @@ impl HDWallet {
}
}

pub fn from_bare_xpub(
Copy link
Author

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?

Copy link
Collaborator

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/hd.rs Outdated Show resolved Hide resolved
src/hd.rs Outdated Show resolved Hide resolved
src/types.rs Outdated
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
Copy link
Author

@justinmoon justinmoon Jun 10, 2020

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);
Copy link
Author

@justinmoon justinmoon Jun 10, 2020

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?

Copy link
Collaborator

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

Copy link
Author

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

Copy link
Collaborator

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

Copy link
Collaborator

@shesek shesek Jun 26, 2020

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.

@bwt-dev bwt-dev deleted a comment Jun 14, 2020
Copy link
Collaborator

@shesek shesek left a 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",
Copy link
Collaborator

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()?)?;
Copy link
Collaborator

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

src/config.rs Show resolved Hide resolved
@@ -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, ':');
Copy link
Collaborator

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.

Copy link
Author

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

Copy link
Collaborator

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.

src/hd.rs Show resolved Hide resolved
src/query.rs Outdated Show resolved Hide resolved
src/query.rs Outdated Show resolved Hide resolved
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())
Copy link
Collaborator

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.

Copy link
Author

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());
Copy link
Collaborator

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.

src/types.rs Outdated Show resolved Hide resolved
src/hd.rs Outdated
Standalone,
// bwt never does hardended derivation itself, but can receive an hardend --bare-xpub
DerivedHard(Fingerprint, u32),
DerivedHard(DescriptorChecksum, u32),
Copy link
Collaborator

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.

Copy link
Author

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())
Copy link
Collaborator

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.

Copy link
Author

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(),
Copy link
Collaborator

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?

Copy link
Author

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()),
Copy link
Collaborator

Choose a reason for hiding this comment

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

/* is missing here

Copy link
Author

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);
Copy link
Collaborator

@shesek shesek Jun 26, 2020

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)

Copy link
Author

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)?;
Copy link
Collaborator

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 {
Copy link
Collaborator

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

Copy link
Author

Choose a reason for hiding this comment

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

I agree. Fixed.

@justinmoon justinmoon force-pushed the descriptor branch 2 times, most recently from f82547e to d9a689b Compare June 26, 2020 01:45
for change in 0..2 {
let descriptor = match xyz.script_type {
ScriptType::P2pkh => {
format!("pkh({}/{}/*)", xyz.extended_pubkey.to_string(), change)
Copy link
Collaborator

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

src/hd.rs Outdated Show resolved Hide resolved
- Descriptor has "body" and "checksum" attributes
- Forgot a /* turning bare xpubs into descriptors
- Removed unnecessary clones
- Rename "hd" -> "wallet" in a few more places
@justinmoon justinmoon changed the title WIP output script descriptor support Descriptor support Jun 26, 2020
@shesek shesek force-pushed the master branch 13 times, most recently from f9fe0ff to 9b4c7fc Compare September 23, 2020 09:46
@justinmoon
Copy link
Author

This PR is no longer relevant after this commit d305ec9#diff-30965b7e36f841dad79ecf3d968902df6d07bc7214a33ec928e77c82385fc2ca

@justinmoon justinmoon closed this Oct 31, 2020
Bitcoin Wallet Tracker automation moved this from Next up to Done Oct 31, 2020
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

2 participants