Derivation path improvements, closes #473#498
Derivation path improvements, closes #473#498apoelstra merged 2 commits intorust-bitcoin:masterfrom BP-WG:pending/derivation
Conversation
src/util/bip32.rs
Outdated
There was a problem hiding this comment.
So, this is basically just TryInto<DerivationPath, Error=Error>? Wish we could just use it directly (1.34.0), but if we can't maybe it would be better to just name it TryIntoDerivationPath to make the future transition easier? There might be some little kinks wrt such a transition, but since rust-bitcoin can still do breaking changes on ongoing basis, there's no point in worrying too much about it.
There was a problem hiding this comment.
Yes, if we were >=1.34, it would be TryInto. Regarding current naming, many Rust Into/FromSomething have the name without Try even if they fail (FromStr for instance) and I do not know any counter-examples of traits named with Try... other than TryInto/From themselves. Also this will change method naming, which will hardly derive from the naming used across this crate (where from_... usually failiable)
stevenroose
left a comment
There was a problem hiding this comment.
I don't really see the merit of this trait over just having a to_derivation_path method on whatever type you'd like to implement this on or having the trait in your own project. If it was a std TryInto or so, it would allow plugging into existing places potentially, but like this it seems quite pointless.
Plus there is the extra generic impls that I suspect might cause problems for implementors of the trait.
src/util/bip32.rs
Outdated
There was a problem hiding this comment.
Isn't this one of those constructions where whenever you implement IntoDerivationPath for a type, cargo will complain "can't allow this impl because rust-bitcoin can implement From<T> for DerivationPath and then this implementation is conflicting"? I bumped into that before.
@dr-orlovsky did you try this?
There was a problem hiding this comment.
There is a good solution for that which I use widely: trait implementation strategies and holder. Proposed by @Kixunil.
A sample can be found here:
https://github.com/LNP-BP/rust-lnpbp/blob/master/src/paradigms/strict_encoding.rs#L298-L445
There was a problem hiding this comment.
And, actually, with this downstreams would not need to impl IntoDerivationPath at all, just do a Into<DerivationPath> impl and everything else will work. This is another reason of having this as a trait
There was a problem hiding this comment.
do you have a link to this trait thing?
There was a problem hiding this comment.
But did you try to implement IntoDerivationPath on a foreign type already?
There was a problem hiding this comment.
do you have a link to this trait thing?
Sorry, which one? Strategy? I gave it above
There was a problem hiding this comment.
I don't understand how the linked code addresses Steven's concern about Rust's coherence rules
There was a problem hiding this comment.
There are three problems we are mixing here.
-
Implementing foreign traits for foreign types. There is nothing specific for this problem with this PR, it's a common rust issue and the only way of addressing it is to do either wrapped data type or do a new marker trait with generic dependency on the foreign one (like
pub trait NewOne where Self: ForeignOne { /* repeat all methods from ForeignOne */ }) and implement it on foreign data types. I do not know why and how we started discussion this problem, b/c again there is nothing in this commit which makes this problem anyhow harder to solve. -
Implementing trait for a generic type ("blanket implementation") more than once (applies both for local and foreign traits) - or implement foreign trait for a concrete type where there is some blanket implementation in the upstream. The solution is to use special pattern by @Kixunil. I use it widely and have a special helper type here: https://github.com/LNP-BP/rust-amplify/blob/master/src/strategy.rs#L26-L45
With that helper type you can write the following code, which will provide you with efficiently multiple blanket implementations of some trait
SampleTrait:// Define strategies, one per specific implementation that you need, // either blanket or concrete pub struct StrategyA; pub struct StrategyB; pub struct StrategyC; // Define a single marker type pub trait Strategy { type Strategy; } // Do a single blanket implementation using Holder and Strategy marker trait impl<T> SampleTrait for T where T: Strategy + Clone, amplify::Holder<T, <T as Strategy>::Strategy>: SampleTrait, { // Do this for each of sample trait methods: fn sample_trait_method(&self) { amplify::Holder::new(self.clone()).sample_trait_method() } } // Do this type of implementation for each of the strategies impl<T> SampleTrait for amplify::Holder<T, StrategyA> where T: Strategy { fn sample_trait_method(&self) { /* ... write your implementation-specific code here */ } } // Finally, apply specific implementation strategy to a concrete type // (or do it in a blanket generic way) as a marker: impl Strategy for ConcreteTypeA { tyoe Strategy = StrategyA; }
-
Implementing type
IntoDerivationPathfrom this PR on some concrete type in downstream dependency. This should not be done, instead one should implementInto<DerivationPath>and will getinto_derivation_path()method for free, thanks to a blanket implementation from this PR, and, if for any wired (and unrecommended) reason he'd like to do a direct implementation ofIntoDerivationPathfor some concrete type or another blanket, he should use solution from pt.2.
There was a problem hiding this comment.
Heh, sounds like I should move my ass and finally write that article about the pattern which I planned for some time. :)
There was a problem hiding this comment.
I think the main problem with downstream only being able to implement Into<DerivationPath> is that it doesn't allow failure. Idk if that's a blocker though. Imo this trait is a fixme anyway and should be deprecated as soon as MSRV >= 1.34.0, at which point this wouldn't be a problem anymore.
|
@stevenroose without that trait you can't do stuff like |
apoelstra
left a comment
There was a problem hiding this comment.
ack c36e471277692173ad03bfb65fcdd4106e433bf6
There was a problem hiding this comment.
This seems to be a useful and good enough stop-gap solution until we can bump our MSRV. It needs a rebase though, I'll re-ack then.
EDIT: ping @dr-orlovsky
Adding IntoDerivationPath trait DerivationPath is_master function DerivationPath constructor for empty path + Default impl
|
Rebased and ready |
stevenroose
left a comment
There was a problem hiding this comment.
LGTM, though probably IntoDerivationPath should probably have been named TryIntoDerivationPath since it returns a Result<DerivationPath, Error>.
| DerivationPath(vec![]) | ||
| } | ||
|
|
||
| /// Returns whether derivation path represents master key (i.e. it's length |
Part of epic #473