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
PSBT responsibilities API according to BIP-174 #455
Comments
I think these responsibilities should be modeled as traits that say something has the capability to do some step. E.g. trait PsbtSigner {
type Error: Debug;
fn sign(&self, psbt: PartiallySignedTransaction) -> Result<PartiallySignedTransaction, Self::Error>;
}
fn validate_psbt(&psbt: PartiallySignedTransaction) -> Result<(), PsbtValidationError>; That would give the flexibility to implement HSM signers and software signers using the same API. Maybe we also want to provide a software signer that holds some keys in a pub->priv key map (as should be outputted from miniscript once rust-bitcoin/rust-miniscript#116) as a default and for testing. The validator could be just a public function that is called by the default signer and can be reused for other people's signers. enum PsbtSignError { … }
struct SoftwareSigner {
keys: HashMap<PubKey, PrivKey>,
}
impl PsbtSigner for SoftwareSigner {
type Error = PsbtSignError;
fn sign(&self, psbt: PartiallySignedTransaction) -> Result<PartiallySignedTransaction, Self::Error> {
validate_psbt(&psbt)?;
// do actual signing using keys in key map
Ok(psbt)
}
} |
@sgeisler I see the idea, but wouldn't it be nice (1) to have all non-wallet specific internal PSBT validation API in the rust-bitcoin (this renders responsibilities as structs, not traits), and (2) why the callback approach from my sample above for the signing procedure would not work in case of HSM signers? (of course it may be extended with |
Imo having a function to do the validation is good enough. It could even be a member of
Your signature is |
I think that the code will be used on hardware device, not from outside. So |
I see your point of using the callback function now. Still, I find it quite odd that the signer owns the PSBT but nothing actually concerning signing (where to get the sig from). So you have to re-create it for every PSBT and externally supply the signing code. What do you think about such an API: https://gist.github.com/rust-play/48ecc1455bf2d6e9f15278b37db15c3a ? It would combine both approaches in a way. It supplies a default signer that can easily be used on a hardware device, while also allowing arbitrary other implementations using the same trait (e.g. a trezor/ledger client). |
Looks good to me! I will take this approach, if no other suggestions will follow |
One thing you might want to consider is if you can allocate on the embedded platform you were thinking about, i.e. if you can use |
There is no allocation planned internally of that type; and for those who use the library I assume they will need to use generics if they have to store different concrete implementations of some responsibility trait. |
Why not just use a Also, I'd suggest following Rust naming convention and name the trait |
@Kixunil That sounds great! I don't think we'd need |
For what it is worth, I actually recently played around with signing a psbt using a Nano Ledger S in Rust: https://github.com/thomaseizinger/rust-ledger-poc/blob/master/examples/sign_psbt.rs For some weird reason, it doesn't fully work as in: the Ledger is not happy with the way it is communicated with but that is a concern of lib implementing the communication (https://github.com/summa-tx/bitcoins-rs/tree/master/ledger-btc) and may very well be fixed in the future as the lib matures. The main structure is there though :) My point is, at least for now, what is exposed by this libraries From my experience working with the Nano Ledger S, it is unfortunately not as simple as "sign this message". Instead, the Ledger exposes a stateful protocol in which one has to send over bits of the transaction one by one. Here is the spec: https://blog.ledger.com/btchip-doc/bitcoin-technical-beta.html#_untrusted_hash_transaction_input_start That means for anyone wanting to integrate with Ledger, the most important thing is that all the data of the transaction is nicely accessible and can easily be converted into whatever byte representation the messages expect. Another thing to consider is, signing with a HW wallet involves confirmation by the user and hence reading from the USB interface would be blocking, hence the |
I guess Ledger implementation would need to have additional code, but maybe all impls will need additional code.
Oh, good point. Trait functions can't be |
Yes certainly. I just wanted to mention that for said implementation, being able to pick out specific data is basically a must for now. Maybe we will get a native psbt signing interface at some point though :) |
I drafted #456, can you pls have a look and give your suggestions? It seems like it meets all of the discussed points. With hardware devices, I think they just have to implement their own version of |
When it comes to dependency, the |
What do you think about defining a sync and an async version of this trait? We could define the contract of the sync version in a way that it isn't allowed to block, that would enable us to automatically impl the async version too in these cases (just calling the sync version in an async fn). |
@Kixunil it's there since 1.36, which does not fit this project MSRV 1.22 |
What do you think about defining a sync and an async version of this trait? We could define the contract of the sync version in a way that it isn't allowed to block, that would enable us to automatically impl the async version too in these cases (just calling the sync version in an async fn). That would make for a beautiful hierarchy imo, the looser your requirements the more signing backends you can use, but you don't have to. We could add the async version at a later time once the MSRV discussion is concluded. I think that futures will be a significant enough language feature to consider 1.36. |
@sgeisler I'm up for your idea, but it seems we are blocked right now by MSRV. I have already wrote to the discussion on that topic. |
How so? We can do the first, sync part now and later add the async trait imo. Or are there any other features we need for the sync version that aren't available right now? |
Oh, in this way (only sync version). So you mean just to add |
AH, forgot about MSRV, good point. Could be behind a feature flag, I guess? |
Maybe not even that. Let's just build a sync API first that can be easily integrated into an async one later (which will add exactly one trait with async in the name per "complex responsibility"). If our sync traits are specified to not block we can impl the async version for every struct implementing the sync version of the traits later, either feature flagged or in a completely different crate should it be too contentious. |
I did not read the whole thread, BUT |
@elichai It wouldn't work on Rust 1.22 |
I totally forgot that |
I think having set of common traits etc for interoperability be part of |
@dpc I thought about putting PSBTs into a separate crate. Would the community support that? |
Removing functionality from rust-bitcoin would certainly suck. And the PSBT crate would depend on it, so we couldn't even just reexport the types to avoid that. I also wrote a partial API proposal in #457. I still have to do some research on finalization, but it will either end up like combine or like sign (depending on if it needs external data). So I'd be happy to hear your thoughts on the proposal. |
BIP-174 defines not just a structure and serialization for PSBTs, but also responsibilities — a well-defined roles of how PSBT can be modified by different actors, which is required for correct workflow organization. Right now PSBT implementation in the current rust-bitcoin code does not address this part of the spec; neither there any other rust implementation of it. Even PSBT signers (Firma and MagicalBitcoin) misses validation parts that are required by BIP-174.
While this library is not a wallet library, it seems that implementation of well-defined API for the responsibilities will be beneficial; plus we can implement non-signing part of PSBT validation described in this section of BIP-174. I am planning to work on that and do it as a number of decorators+facades, one per responsibility, at the same time limiting possible PSBT operations (facade) and implementing common responsibility business logic (like validation of the internal structure) as methods (decorator).
Related PR in miniscript: rust-bitcoin/rust-miniscript#119
The text was updated successfully, but these errors were encountered: