@@ -123,21 +123,94 @@ pub enum TrezorError {
123
123
MultipleSignaturesReturned ,
124
124
#[ error( "A multisig signature was returned for a single address from Device" ) ]
125
125
MultisigSignatureReturned ,
126
+ #[ error( "The file being loaded is a software wallet and does not correspond to the connected hardware wallet" ) ]
127
+ HardwareWalletDifferentFile ,
128
+ #[ error( "PublicKeys missmatch. Wrong device or passphrase:\n file DeviceId \" {file_device_id}\" , connected device \" {connected_device_id}\" ,\n file label \" {file_label}\" and connected device label \" {connected_device_id}\" " ) ]
129
+ HardwareWalletDifferentMnemonicOrPassphrase {
130
+ file_device_id : String ,
131
+ connected_device_id : String ,
132
+ file_label : String ,
133
+ connected_device_label : String ,
134
+ } ,
135
+ #[ error( "The file being loaded correspond to the connected hardware wallet, but public keys are different. Maybe a wrong passphrase was entered?" ) ]
136
+ HardwareWalletDifferentPassphrase ,
126
137
}
127
138
128
139
pub struct TrezorSigner {
129
140
chain_config : Arc < ChainConfig > ,
130
141
client : Arc < Mutex < Trezor > > ,
142
+ session_id : Vec < u8 > ,
131
143
}
132
144
133
145
impl TrezorSigner {
134
- pub fn new ( chain_config : Arc < ChainConfig > , client : Arc < Mutex < Trezor > > ) -> Self {
146
+ pub fn new (
147
+ chain_config : Arc < ChainConfig > ,
148
+ client : Arc < Mutex < Trezor > > ,
149
+ session_id : Vec < u8 > ,
150
+ ) -> Self {
135
151
Self {
136
152
chain_config,
137
153
client,
154
+ session_id,
155
+ }
156
+ }
157
+
158
+ /// Calls initialize on the device with the current session_id.
159
+ ///
160
+ /// If the operation fails due to an I/O error (which may indicate a lost connection to the device),
161
+ /// the function will attempt to reconnect to the Trezor device once before returning an error.
162
+ fn check_session (
163
+ & mut self ,
164
+ db_tx : & impl WalletStorageReadLocked ,
165
+ key_chain : & impl AccountKeyChains ,
166
+ ) -> SignerResult < ( ) > {
167
+ let mut client = self . client . lock ( ) . expect ( "poisoned lock" ) ;
168
+
169
+ match client. init_device ( Some ( self . session_id . clone ( ) ) ) {
170
+ Ok ( _) => Ok ( ( ) ) ,
171
+ // In case of a USB IO error try to reconnect, and try again
172
+ Err ( trezor_client:: Error :: TransportSendMessage (
173
+ trezor_client:: transport:: error:: Error :: Usb ( rusb:: Error :: Io ) ,
174
+ ) ) => {
175
+ let ( mut new_client, data, session_id) = find_trezor_device ( ) ?;
176
+
177
+ check_public_keys_against_key_chain (
178
+ db_tx,
179
+ & mut new_client,
180
+ & data,
181
+ key_chain,
182
+ & self . chain_config ,
183
+ ) ?;
184
+
185
+ * client = new_client;
186
+ self . session_id = session_id;
187
+ Ok ( ( ) )
188
+ }
189
+ Err ( err) => Err ( SignerError :: TrezorError ( TrezorError :: DeviceError (
190
+ err. to_string ( ) ,
191
+ ) ) ) ,
138
192
}
139
193
}
140
194
195
+ /// Attempts to perform an operation on the Trezor client.
196
+ ///
197
+ /// If the operation fails due to an I/O error (which may indicate a lost connection to the device),
198
+ /// the function will attempt to reconnect to the Trezor device once before returning an error.
199
+ fn perform_trezor_operation < F , R > (
200
+ & mut self ,
201
+ operation : F ,
202
+ db_tx : & impl WalletStorageReadLocked ,
203
+ key_chain : & impl AccountKeyChains ,
204
+ ) -> SignerResult < R >
205
+ where
206
+ F : Fn ( & mut Trezor ) -> Result < R , trezor_client:: Error > ,
207
+ {
208
+ self . check_session ( db_tx, key_chain) ?;
209
+
210
+ let mut client = self . client . lock ( ) . expect ( "poisoned lock" ) ;
211
+ operation ( & mut client) . map_err ( |e| TrezorError :: DeviceError ( e. to_string ( ) ) . into ( ) )
212
+ }
213
+
141
214
fn make_signature < F > (
142
215
& self ,
143
216
signatures : & [ MintlayerSignature ] ,
@@ -282,7 +355,7 @@ impl Signer for TrezorSigner {
282
355
& mut self ,
283
356
ptx : PartiallySignedTransaction ,
284
357
key_chain : & impl AccountKeyChains ,
285
- _db_tx : & impl WalletStorageReadUnlocked ,
358
+ db_tx : & impl WalletStorageReadUnlocked ,
286
359
) -> SignerResult < (
287
360
PartiallySignedTransaction ,
288
361
Vec < SignatureStatus > ,
@@ -293,12 +366,13 @@ impl Signer for TrezorSigner {
293
366
let utxos = to_trezor_utxo_msgs ( & ptx, & self . chain_config ) ?;
294
367
let chain_type = to_trezor_chain_type ( & self . chain_config ) ;
295
368
296
- let new_signatures = self
297
- . client
298
- . lock ( )
299
- . expect ( "poisoned lock" )
300
- . mintlayer_sign_tx ( chain_type, inputs, outputs, utxos)
301
- . map_err ( |err| TrezorError :: DeviceError ( err. to_string ( ) ) ) ?;
369
+ let new_signatures = self . perform_trezor_operation (
370
+ move |client| {
371
+ client. mintlayer_sign_tx ( chain_type, inputs. clone ( ) , outputs. clone ( ) , utxos. clone ( ) )
372
+ } ,
373
+ db_tx,
374
+ key_chain,
375
+ ) ?;
302
376
303
377
let inputs_utxo_refs: Vec < _ > = ptx. input_utxos ( ) . iter ( ) . map ( |u| u. as_ref ( ) ) . collect ( ) ;
304
378
@@ -436,11 +510,11 @@ impl Signer for TrezorSigner {
436
510
message : & [ u8 ] ,
437
511
destination : & Destination ,
438
512
key_chain : & impl AccountKeyChains ,
439
- _db_tx : & impl WalletStorageReadUnlocked ,
513
+ db_tx : & impl WalletStorageReadUnlocked ,
440
514
) -> SignerResult < ArbitraryMessageSignature > {
441
515
let data = match key_chain. find_public_key ( destination) {
442
516
Some ( FoundPubKey :: Hierarchy ( xpub) ) => {
443
- let address_n = xpub
517
+ let address_n: Vec < _ > = xpub
444
518
. get_derivation_path ( )
445
519
. as_slice ( )
446
520
. iter ( )
@@ -469,12 +543,19 @@ impl Signer for TrezorSigner {
469
543
470
544
let chain_type = to_trezor_chain_type ( & self . chain_config ) ;
471
545
472
- let sig = self
473
- . client
474
- . lock ( )
475
- . expect ( "poisoned lock" )
476
- . mintlayer_sign_message ( chain_type, address_n, addr_type, message. to_vec ( ) )
477
- . map_err ( |err| TrezorError :: DeviceError ( err. to_string ( ) ) ) ?;
546
+ let sig = self . perform_trezor_operation (
547
+ move |client| {
548
+ client. mintlayer_sign_message (
549
+ chain_type,
550
+ address_n. clone ( ) ,
551
+ addr_type,
552
+ message. to_vec ( ) ,
553
+ )
554
+ } ,
555
+ db_tx,
556
+ key_chain,
557
+ ) ?;
558
+
478
559
let signature = Signature :: from_raw_data ( & sig, SignatureKind :: Secp256k1Schnorr )
479
560
. map_err ( TrezorError :: SignatureError ) ?;
480
561
@@ -1144,6 +1225,7 @@ fn to_trezor_output_lock(lock: &OutputTimeLock) -> trezor_client::protos::Mintla
1144
1225
pub struct TrezorSignerProvider {
1145
1226
client : Arc < Mutex < Trezor > > ,
1146
1227
data : TrezorData ,
1228
+ session_id : Vec < u8 > ,
1147
1229
}
1148
1230
1149
1231
impl std:: fmt:: Debug for TrezorSignerProvider {
@@ -1154,27 +1236,29 @@ impl std::fmt::Debug for TrezorSignerProvider {
1154
1236
1155
1237
impl TrezorSignerProvider {
1156
1238
pub fn new ( ) -> Result < Self , TrezorError > {
1157
- let ( client, data) =
1239
+ let ( client, data, session_id ) =
1158
1240
find_trezor_device ( ) . map_err ( |err| TrezorError :: DeviceError ( err. to_string ( ) ) ) ?;
1159
1241
1160
1242
Ok ( Self {
1161
1243
client : Arc :: new ( Mutex :: new ( client) ) ,
1162
1244
data,
1245
+ session_id,
1163
1246
} )
1164
1247
}
1165
1248
1166
1249
pub fn load_from_database (
1167
1250
chain_config : Arc < ChainConfig > ,
1168
1251
db_tx : & impl WalletStorageReadLocked ,
1169
1252
) -> WalletResult < Self > {
1170
- let ( client, data) = find_trezor_device ( ) . map_err ( SignerError :: TrezorError ) ?;
1253
+ let ( client, data, session_id ) = find_trezor_device ( ) . map_err ( SignerError :: TrezorError ) ?;
1171
1254
1172
1255
let provider = Self {
1173
1256
client : Arc :: new ( Mutex :: new ( client) ) ,
1174
1257
data,
1258
+ session_id,
1175
1259
} ;
1176
1260
1177
- check_public_keys ( db_tx, & provider, chain_config) ?;
1261
+ check_public_keys_against_db ( db_tx, & provider, chain_config) ?;
1178
1262
1179
1263
Ok ( provider)
1180
1264
}
@@ -1184,25 +1268,12 @@ impl TrezorSignerProvider {
1184
1268
chain_config : & Arc < ChainConfig > ,
1185
1269
account_index : U31 ,
1186
1270
) -> SignerResult < ExtendedPublicKey > {
1187
- let derivation_path = make_account_path ( chain_config, account_index) ;
1188
- let account_path =
1189
- derivation_path. as_slice ( ) . iter ( ) . map ( |c| c. into_encoded_index ( ) ) . collect ( ) ;
1190
- let chain_type = to_trezor_chain_type ( chain_config) ;
1191
- let xpub = self
1192
- . client
1193
- . lock ( )
1194
- . expect ( "poisoned lock" )
1195
- . mintlayer_get_public_key ( chain_type, account_path)
1196
- . map_err ( |e| SignerError :: TrezorError ( TrezorError :: DeviceError ( e. to_string ( ) ) ) ) ?;
1197
- let chain_code = ChainCode :: from ( xpub. chain_code . 0 ) ;
1198
- let account_pubkey = Secp256k1ExtendedPublicKey :: new_unchecked (
1199
- derivation_path,
1200
- chain_code,
1201
- Secp256k1PublicKey :: from_bytes ( & xpub. public_key . serialize ( ) )
1202
- . map_err ( |_| SignerError :: TrezorError ( TrezorError :: InvalidKey ) ) ?,
1203
- ) ;
1204
- let account_pubkey = ExtendedPublicKey :: new ( account_pubkey) ;
1205
- Ok ( account_pubkey)
1271
+ fetch_extended_pub_key (
1272
+ & mut self . client . lock ( ) . expect ( "poisoned lock" ) ,
1273
+ chain_config,
1274
+ account_index,
1275
+ )
1276
+ . map_err ( SignerError :: TrezorError )
1206
1277
}
1207
1278
}
1208
1279
@@ -1217,23 +1288,16 @@ fn to_trezor_chain_type(chain_config: &ChainConfig) -> MintlayerChainType {
1217
1288
1218
1289
/// Check that the public keys in the DB are the same as the ones with the connected hardware
1219
1290
/// wallet
1220
- fn check_public_keys (
1291
+ fn check_public_keys_against_key_chain (
1221
1292
db_tx : & impl WalletStorageReadLocked ,
1222
- provider : & TrezorSignerProvider ,
1223
- chain_config : Arc < ChainConfig > ,
1224
- ) -> Result < ( ) , WalletError > {
1225
- let first_acc = db_tx
1226
- . get_accounts_info ( ) ?
1227
- . iter ( )
1228
- . find_map ( |( acc_id, info) | {
1229
- ( info. account_index ( ) == DEFAULT_ACCOUNT_INDEX ) . then_some ( acc_id)
1230
- } )
1231
- . cloned ( )
1232
- . ok_or ( WalletError :: WalletNotInitialized ) ?;
1233
- let expected_pk = provider. fetch_extended_pub_key ( & chain_config, DEFAULT_ACCOUNT_INDEX ) ?;
1234
- let loaded_acc = provider. load_account_from_database ( chain_config, db_tx, & first_acc) ?;
1293
+ client : & mut Trezor ,
1294
+ trezor_data : & TrezorData ,
1295
+ key_chain : & impl AccountKeyChains ,
1296
+ chain_config : & ChainConfig ,
1297
+ ) -> SignerResult < ( ) > {
1298
+ let expected_pk = fetch_extended_pub_key ( client, chain_config, key_chain. account_index ( ) ) ?;
1235
1299
1236
- if loaded_acc . key_chain ( ) . account_public_key ( ) == & expected_pk {
1300
+ if key_chain. account_public_key ( ) == & expected_pk {
1237
1301
return Ok ( ( ) ) ;
1238
1302
}
1239
1303
@@ -1242,24 +1306,75 @@ fn check_public_keys(
1242
1306
HardwareWalletData :: Trezor ( data) => {
1243
1307
// If the device_id is the same but public keys are different, maybe a
1244
1308
// different passphrase was used
1245
- if data. device_id == provider . data . device_id {
1246
- return Err ( WalletError :: HardwareWalletDifferentPassphrase ) ;
1309
+ if data. device_id == trezor_data . device_id {
1310
+ return Err ( TrezorError :: HardwareWalletDifferentPassphrase . into ( ) ) ;
1247
1311
} else {
1248
- return Err ( WalletError :: HardwareWalletDifferentMnemonicOrPassphrase {
1312
+ return Err ( TrezorError :: HardwareWalletDifferentMnemonicOrPassphrase {
1249
1313
file_device_id : data. device_id ,
1250
- connected_device_id : provider . data . device_id . clone ( ) ,
1314
+ connected_device_id : trezor_data . device_id . clone ( ) ,
1251
1315
file_label : data. label ,
1252
- connected_device_label : provider. data . label . clone ( ) ,
1253
- } ) ;
1316
+ connected_device_label : trezor_data. label . clone ( ) ,
1317
+ }
1318
+ . into ( ) ) ;
1254
1319
}
1255
1320
}
1256
1321
}
1257
1322
}
1258
1323
1259
- Err ( WalletError :: HardwareWalletDifferentFile )
1324
+ Err ( TrezorError :: HardwareWalletDifferentFile ) ?
1325
+ }
1326
+
1327
+ fn fetch_extended_pub_key (
1328
+ client : & mut Trezor ,
1329
+ chain_config : & ChainConfig ,
1330
+ account_index : U31 ,
1331
+ ) -> Result < ExtendedPublicKey , TrezorError > {
1332
+ let derivation_path = make_account_path ( chain_config, account_index) ;
1333
+ let account_path = derivation_path. as_slice ( ) . iter ( ) . map ( |c| c. into_encoded_index ( ) ) . collect ( ) ;
1334
+ let chain_type = to_trezor_chain_type ( chain_config) ;
1335
+ let xpub = client
1336
+ . mintlayer_get_public_key ( chain_type, account_path)
1337
+ . map_err ( |e| TrezorError :: DeviceError ( e. to_string ( ) ) ) ?;
1338
+ let chain_code = ChainCode :: from ( xpub. chain_code . 0 ) ;
1339
+ let account_pubkey = Secp256k1ExtendedPublicKey :: new_unchecked (
1340
+ derivation_path,
1341
+ chain_code,
1342
+ Secp256k1PublicKey :: from_bytes ( & xpub. public_key . serialize ( ) )
1343
+ . map_err ( |_| TrezorError :: InvalidKey ) ?,
1344
+ ) ;
1345
+ let account_pubkey = ExtendedPublicKey :: new ( account_pubkey) ;
1346
+ Ok ( account_pubkey)
1347
+ }
1348
+
1349
+ /// Check that the public keys in the DB are the same as the ones with the connected hardware
1350
+ /// wallet
1351
+ fn check_public_keys_against_db (
1352
+ db_tx : & impl WalletStorageReadLocked ,
1353
+ provider : & TrezorSignerProvider ,
1354
+ chain_config : Arc < ChainConfig > ,
1355
+ ) -> Result < ( ) , WalletError > {
1356
+ let first_acc = db_tx
1357
+ . get_accounts_info ( ) ?
1358
+ . iter ( )
1359
+ . find_map ( |( acc_id, info) | {
1360
+ ( info. account_index ( ) == DEFAULT_ACCOUNT_INDEX ) . then_some ( acc_id)
1361
+ } )
1362
+ . cloned ( )
1363
+ . ok_or ( WalletError :: WalletNotInitialized ) ?;
1364
+ let loaded_acc =
1365
+ provider. load_account_from_database ( chain_config. clone ( ) , db_tx, & first_acc) ?;
1366
+
1367
+ check_public_keys_against_key_chain (
1368
+ db_tx,
1369
+ & mut provider. client . lock ( ) . expect ( "poisoned lock" ) ,
1370
+ & provider. data ,
1371
+ loaded_acc. key_chain ( ) ,
1372
+ & chain_config,
1373
+ )
1374
+ . map_err ( WalletError :: SignerError )
1260
1375
}
1261
1376
1262
- fn find_trezor_device ( ) -> Result < ( Trezor , TrezorData ) , TrezorError > {
1377
+ fn find_trezor_device ( ) -> Result < ( Trezor , TrezorData , Vec < u8 > ) , TrezorError > {
1263
1378
let mut devices = find_devices ( false )
1264
1379
. into_iter ( )
1265
1380
. filter ( |device| device. model == Model :: Trezor || device. model == Model :: TrezorEmulator )
@@ -1287,16 +1402,17 @@ fn find_trezor_device() -> Result<(Trezor, TrezorData), TrezorError> {
1287
1402
label : features. label ( ) . to_owned ( ) ,
1288
1403
device_id : features. device_id ( ) . to_owned ( ) ,
1289
1404
} ;
1405
+ let session_id = features. session_id ( ) . to_vec ( ) ;
1290
1406
1291
- Ok ( ( client, data) )
1407
+ Ok ( ( client, data, session_id ) )
1292
1408
}
1293
1409
1294
1410
impl SignerProvider for TrezorSignerProvider {
1295
1411
type S = TrezorSigner ;
1296
1412
type K = AccountKeyChainImplHardware ;
1297
1413
1298
1414
fn provide ( & mut self , chain_config : Arc < ChainConfig > , _account_index : U31 ) -> Self :: S {
1299
- TrezorSigner :: new ( chain_config, self . client . clone ( ) )
1415
+ TrezorSigner :: new ( chain_config, self . client . clone ( ) , self . session_id . clone ( ) )
1300
1416
}
1301
1417
1302
1418
fn make_new_account (
0 commit comments