1+ use std:: collections:: HashMap ;
2+
13use super :: { Trezor , TrezorResponse } ;
24use crate :: { error:: Result , flows:: sign_tx:: SignTxProgress , protos, utils} ;
35use bitcoin:: {
@@ -7,20 +9,51 @@ use bitcoin::{
79
810pub use crate :: protos:: InputScriptType ;
911
12+ #[ derive( Default ) ]
13+ pub struct SignedTx {
14+ pub signatures : Vec < ( usize , Vec < u8 > ) > ,
15+ pub serialized : Vec < u8 > ,
16+ }
17+
1018impl Trezor {
1119 pub fn get_public_key (
1220 & mut self ,
1321 path : & bip32:: DerivationPath ,
1422 script_type : InputScriptType ,
1523 network : Network ,
1624 show_display : bool ,
17- ) -> Result < TrezorResponse < ' _ , bip32:: Xpub , protos :: PublicKey > > {
25+ ) -> Result < bip32:: Xpub > {
1826 let mut req = protos:: GetPublicKey :: new ( ) ;
1927 req. address_n = utils:: convert_path ( path) ;
2028 req. set_show_display ( show_display) ;
2129 req. set_coin_name ( utils:: coin_name ( network) ?) ;
2230 req. set_script_type ( script_type) ;
23- self . call ( req, Box :: new ( |_, m| Ok ( m. xpub ( ) . parse ( ) ?) ) )
31+ self . call ( req, Box :: new ( |_, m : protos:: PublicKey | Ok ( m. xpub ( ) . parse ( ) ?) ) ) ?. interact ( )
32+ }
33+
34+ pub fn get_root_fingerprint ( & mut self ) -> Result < bip32:: Fingerprint > {
35+ let xpub = self . get_public_key (
36+ & bip32:: DerivationPath :: default ( ) ,
37+ InputScriptType :: SPENDADDRESS ,
38+ bitcoin:: Network :: Bitcoin ,
39+ false ,
40+ ) ?;
41+ Ok ( xpub. fingerprint ( ) )
42+ }
43+
44+ pub fn register_policy (
45+ & mut self ,
46+ name : String ,
47+ template : String ,
48+ xpubs : Vec < String > ,
49+ network : Network ,
50+ ) -> Result < protos:: RegisteredPolicy > {
51+ let mut req = protos:: Policy :: new ( ) ;
52+ req. set_coin_name ( utils:: coin_name ( network) ?) ;
53+ req. set_name ( name) ;
54+ req. set_template ( template) ;
55+ req. xpubs = xpubs;
56+ self . call ( req, Box :: new ( |_, m : protos:: RegisteredPolicy | Ok ( m) ) ) ?. interact ( )
2457 }
2558
2659 //TODO(stevenroose) multisig
@@ -30,28 +63,77 @@ impl Trezor {
3063 script_type : InputScriptType ,
3164 network : Network ,
3265 show_display : bool ,
33- ) -> Result < TrezorResponse < ' _ , Address , protos:: Address > > {
66+ registered : Option < protos:: RegisteredPolicy > ,
67+ ) -> Result < Address > {
3468 let mut req = protos:: GetAddress :: new ( ) ;
3569 req. address_n = utils:: convert_path ( path) ;
3670 req. set_coin_name ( utils:: coin_name ( network) ?) ;
3771 req. set_show_display ( show_display) ;
3872 req. set_script_type ( script_type) ;
39- self . call ( req, Box :: new ( |_, m| parse_address ( m. address ( ) ) ) )
73+ req. registered = registered. into ( ) ;
74+ self . call ( req, Box :: new ( |_, m : protos:: Address | parse_address ( m. address ( ) ) ) ) ?. interact ( )
4075 }
4176
4277 pub fn sign_tx (
4378 & mut self ,
4479 psbt : & psbt:: Psbt ,
4580 network : Network ,
46- ) -> Result < TrezorResponse < ' _ , SignTxProgress < ' _ > , protos:: TxRequest > > {
81+ registered : Option < protos:: RegisteredPolicy > ,
82+ ) -> Result < SignedTx > {
83+ let root_fingerprint = self . get_root_fingerprint ( ) ?;
84+
85+ // Filter public keys that belong to the current wallet.
86+ // Public key derivation must be done before transaction signature process.
87+ let mut derivations = HashMap :: new ( ) ;
88+ for input in & psbt. inputs {
89+ for ( pubkey, ( fpr, path) ) in & input. bip32_derivation {
90+ if * fpr != root_fingerprint {
91+ continue ;
92+ }
93+ let derived_pubkey = self
94+ . get_public_key ( & path, InputScriptType :: SPENDADDRESS , network, false ) ?
95+ . public_key ;
96+ if * pubkey == derived_pubkey {
97+ if derivations. insert ( derived_pubkey, path. clone ( ) ) . is_some ( ) {
98+ return Err ( crate :: Error :: InvalidPsbt ( format ! (
99+ "Duplicate public key: {}" ,
100+ derived_pubkey
101+ ) ) ) ;
102+ }
103+ }
104+ }
105+ }
106+
47107 let tx = & psbt. unsigned_tx ;
48108 let mut req = protos:: SignTx :: new ( ) ;
49109 req. set_inputs_count ( tx. input . len ( ) as u32 ) ;
50110 req. set_outputs_count ( tx. output . len ( ) as u32 ) ;
51111 req. set_coin_name ( utils:: coin_name ( network) ?) ;
52112 req. set_version ( tx. version . 0 as u32 ) ;
53113 req. set_lock_time ( tx. lock_time . to_consensus_u32 ( ) ) ;
54- self . call ( req, Box :: new ( |c, m| Ok ( SignTxProgress :: new ( c, m) ) ) )
114+
115+ if registered. is_some ( ) {
116+ // TODO: tx serialization is not fully supported with Miniscript
117+ req. serialize = Some ( false ) ;
118+ }
119+
120+ let req = self . call ( req, Box :: new ( move |_, m : protos:: TxRequest | Ok ( m) ) ) ?. interact ( ) ?;
121+
122+ let mut progress =
123+ SignTxProgress :: new ( self , req, registered, root_fingerprint, derivations) ;
124+ let mut signed = SignedTx :: default ( ) ;
125+ loop {
126+ if let Some ( part) = progress. get_serialized_tx_part ( ) {
127+ signed. serialized . extend ( part) ;
128+ }
129+ if let Some ( ( i, sig) ) = progress. get_signature ( ) {
130+ signed. signatures . push ( ( i, sig. to_vec ( ) ) ) ;
131+ }
132+ if progress. finished ( ) {
133+ return Ok ( signed) ;
134+ }
135+ progress. ack_psbt ( psbt, network) ?;
136+ }
55137 }
56138
57139 pub fn sign_message (
0 commit comments