Skip to content

Commit 64b9b14

Browse files
committed
Add Ledger signer provider
1 parent abe8a88 commit 64b9b14

File tree

17 files changed

+791
-487
lines changed

17 files changed

+791
-487
lines changed

wallet/src/key_chain/master_key_chain/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crypto::vrf::ExtendedVRFPrivateKey;
2222
use std::sync::Arc;
2323
use wallet_storage::{
2424
StoreTxRwUnlocked, WalletStorageReadLocked, WalletStorageReadUnlocked,
25-
WalletStorageWriteUnlocked,
25+
WalletStorageWriteLocked, WalletStorageWriteUnlocked,
2626
};
2727
use wallet_types::seed_phrase::{SerializableSeedPhrase, StoreSeedPhrase};
2828

@@ -131,7 +131,7 @@ impl MasterKeyChain {
131131

132132
pub fn create_account_key_chain(
133133
&self,
134-
db_tx: &mut impl WalletStorageWriteUnlocked,
134+
db_tx: &mut (impl WalletStorageWriteLocked + WalletStorageReadUnlocked),
135135
account_index: U31,
136136
lookahead_size: u32,
137137
) -> KeyChainResult<AccountKeyChainImplSoftware> {

wallet/src/signer/ledger_signer/mod.rs

Lines changed: 157 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,23 +59,29 @@ use randomness::make_true_rng;
5959
use serialization::Encode;
6060
use tokio::sync::Mutex;
6161
use utils::ensure;
62-
use wallet_storage::{WalletStorageReadLocked, WalletStorageReadUnlocked};
62+
use wallet_storage::{
63+
StoreLocalReadWriteUnlocked, WalletStorageReadLocked, WalletStorageReadUnlocked,
64+
};
6365
use wallet_types::{
64-
hw_data::LedgerData, partially_signed_transaction::PartiallySignedTransaction,
66+
account_info::DEFAULT_ACCOUNT_INDEX,
67+
hw_data::{HardwareWalletData, LedgerData},
68+
partially_signed_transaction::PartiallySignedTransaction,
6569
signature_status::SignatureStatus,
70+
AccountId,
6671
};
6772

6873
use crate::{
69-
key_chain::{make_account_path, AccountKeyChains, FoundPubKey},
74+
key_chain::{make_account_path, AccountKeyChainImplHardware, AccountKeyChains, FoundPubKey},
7075
signer::{
7176
ledger_signer::ledger_messages::{
7277
check_current_app, get_app_name, get_extended_public_key, sign_challenge, sign_tx,
7378
LedgerBip32Path, LedgerInputAddressPath, LedgerSignature, LedgerTxInput,
7479
LedgerTxInputCommitment, LedgerTxOutput,
7580
},
7681
signer_utils::{is_htlc_utxo, sign_input_with_standalone_key},
77-
Signer, SignerError, SignerResult,
82+
Signer, SignerError, SignerProvider, SignerResult,
7883
},
84+
Account, WalletResult,
7985
};
8086

8187
/// Signer errors
@@ -122,7 +128,8 @@ pub trait LProvider {
122128

123129
async fn find_ledger_device_from_db(
124130
&self,
125-
db_tx: &impl WalletStorageReadLocked,
131+
db_tx: &(impl WalletStorageReadLocked + Sync),
132+
chain_config: Arc<ChainConfig>,
126133
) -> SignerResult<(Self::L, LedgerData)>;
127134
}
128135

@@ -167,7 +174,7 @@ where
167174
/// the function will attempt to reconnect to the Ledger device once before returning an error.
168175
async fn check_session(
169176
&mut self,
170-
db_tx: &impl WalletStorageReadLocked,
177+
db_tx: &(impl WalletStorageReadLocked + Sync),
171178
key_chain: &impl AccountKeyChains,
172179
) -> SignerResult<()> {
173180
let mut client = self.client.lock().await;
@@ -184,13 +191,14 @@ where
184191
| ledger_lib::Error::Tcp(_)
185192
| ledger_lib::Error::Ble(_),
186193
) => {
187-
let (mut new_client, data) =
188-
self.provider.find_ledger_device_from_db(db_tx).await?;
194+
let (mut new_client, _data) = self
195+
.provider
196+
.find_ledger_device_from_db(db_tx, self.chain_config.clone())
197+
.await?;
189198

190199
check_public_keys_against_key_chain(
191200
db_tx,
192201
&mut new_client,
193-
&data,
194202
key_chain,
195203
&self.chain_config,
196204
)
@@ -215,7 +223,7 @@ where
215223
async fn perform_ledger_operation<F, R>(
216224
&mut self,
217225
operation: F,
218-
db_tx: &impl WalletStorageReadLocked,
226+
db_tx: &(impl WalletStorageReadLocked + Sync),
219227
key_chain: &impl AccountKeyChains,
220228
) -> SignerResult<R>
221229
where
@@ -1007,7 +1015,6 @@ fn to_ledger_chain_type(chain_config: &ChainConfig) -> u8 {
10071015
}
10081016
}
10091017

1010-
#[allow(dead_code)]
10111018
async fn find_ledger_device() -> SignerResult<(LedgerHandle, LedgerData)> {
10121019
let mut provider = LedgerProvider::init().await;
10131020
let mut devices = provider
@@ -1030,9 +1037,8 @@ async fn find_ledger_device() -> SignerResult<(LedgerHandle, LedgerData)> {
10301037
/// Check that the public keys in the provided key chain are the same as the ones from the
10311038
/// connected hardware wallet
10321039
async fn check_public_keys_against_key_chain<L: Exchange>(
1033-
db_tx: &impl WalletStorageReadLocked,
1040+
db_tx: &(impl WalletStorageReadLocked + Sync),
10341041
client: &mut L,
1035-
_ledger_data: &LedgerData,
10361042
key_chain: &impl AccountKeyChains,
10371043
chain_config: &ChainConfig,
10381044
) -> SignerResult<()> {
@@ -1050,6 +1056,31 @@ async fn check_public_keys_against_key_chain<L: Exchange>(
10501056
Err(LedgerError::HardwareWalletDifferentFile)?
10511057
}
10521058

1059+
/// Check that the public keys in the DB are the same as the ones from the connected hardware
1060+
/// wallet
1061+
async fn check_public_keys_against_db(
1062+
db_tx: &(impl WalletStorageReadLocked + Sync),
1063+
client: &mut LedgerHandle,
1064+
chain_config: Arc<ChainConfig>,
1065+
) -> SignerResult<()> {
1066+
let (id, first_acc) = db_tx
1067+
.get_accounts_info()?
1068+
.iter()
1069+
.find_map(|(id, info)| {
1070+
(info.account_index() == DEFAULT_ACCOUNT_INDEX).then_some((id.clone(), info.clone()))
1071+
})
1072+
.ok_or(SignerError::WalletNotInitialized)?;
1073+
1074+
let loaded_acc = AccountKeyChainImplHardware::load_from_database(
1075+
chain_config.clone(),
1076+
db_tx,
1077+
&id,
1078+
&first_acc,
1079+
)?;
1080+
1081+
check_public_keys_against_key_chain(db_tx, client, &loaded_acc, &chain_config).await
1082+
}
1083+
10531084
async fn fetch_extended_pub_key<L: Exchange>(
10541085
client: &mut L,
10551086
chain_config: &ChainConfig,
@@ -1079,6 +1110,119 @@ fn single_signature(
10791110
}
10801111
}
10811112

1113+
#[derive(Clone)]
1114+
pub struct LedgerSignerProvider {
1115+
client: Arc<Mutex<LedgerHandle>>,
1116+
data: LedgerData,
1117+
}
1118+
1119+
impl std::fmt::Debug for LedgerSignerProvider {
1120+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1121+
f.write_str("LedgerSignerProvider")
1122+
}
1123+
}
1124+
1125+
#[async_trait]
1126+
impl LProvider for LedgerSignerProvider {
1127+
type L = LedgerHandle;
1128+
1129+
async fn find_ledger_device_from_db(
1130+
&self,
1131+
db_tx: &(impl WalletStorageReadLocked + Sync),
1132+
chain_config: Arc<ChainConfig>,
1133+
) -> SignerResult<(Self::L, LedgerData)> {
1134+
let (mut client, data) = find_ledger_device().await?;
1135+
1136+
check_public_keys_against_db(db_tx, &mut client, chain_config).await?;
1137+
1138+
Ok((client, data))
1139+
}
1140+
}
1141+
1142+
impl LedgerSignerProvider {
1143+
pub async fn new() -> SignerResult<Self> {
1144+
let (client, data) = find_ledger_device().await?;
1145+
1146+
Ok(Self {
1147+
client: Arc::new(Mutex::new(client)),
1148+
data,
1149+
})
1150+
}
1151+
1152+
pub async fn load_from_database(
1153+
chain_config: Arc<ChainConfig>,
1154+
db_tx: &(impl WalletStorageReadLocked + Sync),
1155+
_device_id: Option<String>,
1156+
) -> WalletResult<Self> {
1157+
//let (client, data) = find_trezor_device_from_db(db_tx, device_id)?;
1158+
let (mut client, data) = find_ledger_device().await?;
1159+
1160+
check_public_keys_against_db(db_tx, &mut client, chain_config).await?;
1161+
1162+
let provider = Self {
1163+
client: Arc::new(Mutex::new(client)),
1164+
data,
1165+
};
1166+
1167+
Ok(provider)
1168+
}
1169+
1170+
async fn fetch_extended_pub_key(
1171+
&self,
1172+
chain_config: &Arc<ChainConfig>,
1173+
account_index: U31,
1174+
) -> SignerResult<ExtendedPublicKey> {
1175+
fetch_extended_pub_key(&mut *self.client.lock().await, chain_config, account_index)
1176+
.await
1177+
.map_err(SignerError::LedgerError)
1178+
}
1179+
}
1180+
1181+
#[async_trait]
1182+
impl SignerProvider for LedgerSignerProvider {
1183+
type S = LedgerSigner<LedgerHandle, LedgerSignerProvider>;
1184+
type K = AccountKeyChainImplHardware;
1185+
1186+
fn provide(&mut self, chain_config: Arc<ChainConfig>, _account_index: U31) -> Self::S {
1187+
LedgerSigner::new(chain_config, self.client.clone(), self.clone())
1188+
}
1189+
1190+
async fn make_new_account<B: storage::Backend>(
1191+
&mut self,
1192+
chain_config: Arc<ChainConfig>,
1193+
account_index: U31,
1194+
name: Option<String>,
1195+
db_tx: &mut StoreLocalReadWriteUnlocked<B>,
1196+
) -> WalletResult<Account<Self::K>> {
1197+
let account_pubkey = self.fetch_extended_pub_key(&chain_config, account_index).await?;
1198+
1199+
let lookahead_size = db_tx.get_lookahead_size()?;
1200+
1201+
let key_chain = AccountKeyChainImplHardware::new_from_hardware_key(
1202+
chain_config.clone(),
1203+
db_tx,
1204+
account_pubkey,
1205+
account_index,
1206+
lookahead_size,
1207+
)?;
1208+
1209+
Account::new(chain_config, db_tx, key_chain, name)
1210+
}
1211+
1212+
fn load_account_from_database(
1213+
&self,
1214+
chain_config: Arc<ChainConfig>,
1215+
db_tx: &impl WalletStorageReadLocked,
1216+
id: &AccountId,
1217+
) -> WalletResult<Account<Self::K>> {
1218+
Account::load_from_database(chain_config, db_tx, id)
1219+
}
1220+
1221+
fn get_hardware_wallet_data(&self) -> Option<HardwareWalletData> {
1222+
Some(HardwareWalletData::Ledger(self.data.clone()))
1223+
}
1224+
}
1225+
10821226
#[cfg(feature = "enable-ledger-device-tests")]
10831227
#[cfg(test)]
10841228
mod tests;

wallet/src/signer/ledger_signer/tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ impl LProvider for DummyProvider {
104104

105105
async fn find_ledger_device_from_db(
106106
&self,
107-
_db_tx: &(impl WalletStorageReadLocked),
107+
_db_tx: &(impl WalletStorageReadLocked + Sync),
108+
_chain_config: Arc<ChainConfig>,
108109
) -> SignerResult<(Self::L, LedgerData)> {
109110
Err(SignerError::LedgerError(LedgerError::NoDeviceFound))
110111
}

wallet/src/signer/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use common::{
3636
};
3737
use crypto::key::hdkd::{derivable::DerivationError, u31::U31};
3838
use wallet_storage::{
39-
WalletStorageReadLocked, WalletStorageReadUnlocked, WalletStorageWriteUnlocked,
39+
StoreLocalReadWriteUnlocked, WalletStorageReadLocked, WalletStorageReadUnlocked,
4040
};
4141
use wallet_types::{
4242
hw_data::HardwareWalletData,
@@ -110,6 +110,8 @@ pub enum SignerError {
110110
HtlcMultisigDestinationExpected,
111111
#[error("Partially signed transaction error: {0}")]
112112
PartiallySignedTransactionError(#[from] PartiallySignedTransactionError),
113+
#[error("Wallet not initialized")]
114+
WalletNotInitialized,
113115
}
114116

115117
type SignerResult<T> = Result<T, SignerError>;
@@ -153,18 +155,19 @@ pub trait Signer {
153155
) -> SignerResult<SignedTransactionIntent>;
154156
}
155157

156-
pub trait SignerProvider {
158+
#[async_trait]
159+
pub trait SignerProvider: Send {
157160
type S: Signer + Send;
158161
type K: AccountKeyChains + Sync + Send;
159162

160163
fn provide(&mut self, chain_config: Arc<ChainConfig>, account_index: U31) -> Self::S;
161164

162-
fn make_new_account(
165+
async fn make_new_account<B: storage::Backend>(
163166
&mut self,
164167
chain_config: Arc<ChainConfig>,
165168
account_index: U31,
166169
name: Option<String>,
167-
db_tx: &mut impl WalletStorageWriteUnlocked,
170+
db_tx: &mut StoreLocalReadWriteUnlocked<B>,
168171
) -> WalletResult<Account<Self::K>>;
169172

170173
fn load_account_from_database(

wallet/src/signer/software_signer/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ use crypto::key::{
5151
};
5252
use randomness::make_true_rng;
5353
use wallet_storage::{
54-
StoreTxRwUnlocked, WalletStorageReadLocked, WalletStorageReadUnlocked,
55-
WalletStorageWriteUnlocked,
54+
StoreLocalReadWriteUnlocked, StoreTxRwUnlocked, WalletStorageReadLocked,
55+
WalletStorageReadUnlocked,
5656
};
5757
use wallet_types::{
5858
hw_data::HardwareWalletData, partially_signed_transaction::PartiallySignedTransaction,
@@ -473,6 +473,7 @@ impl SoftwareSignerProvider {
473473
}
474474
}
475475

476+
#[async_trait]
476477
impl SignerProvider for SoftwareSignerProvider {
477478
type S = SoftwareSigner;
478479
type K = AccountKeyChainImplSoftware;
@@ -481,12 +482,12 @@ impl SignerProvider for SoftwareSignerProvider {
481482
SoftwareSigner::new(chain_config, account_index)
482483
}
483484

484-
fn make_new_account(
485+
async fn make_new_account<B: storage::Backend>(
485486
&mut self,
486487
chain_config: Arc<ChainConfig>,
487488
next_account_index: U31,
488489
name: Option<String>,
489-
db_tx: &mut impl WalletStorageWriteUnlocked,
490+
db_tx: &mut StoreLocalReadWriteUnlocked<B>,
490491
) -> WalletResult<Account<AccountKeyChainImplSoftware>> {
491492
let lookahead_size = db_tx.get_lookahead_size()?;
492493
let account_key_chain = self.master_key_chain.create_account_key_chain(

wallet/src/signer/trezor_signer/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ use trezor_client::{
8888
};
8989
use utils::ensure;
9090
use wallet_storage::{
91-
WalletStorageReadLocked, WalletStorageReadUnlocked, WalletStorageWriteUnlocked,
91+
StoreLocalReadWriteUnlocked, WalletStorageReadLocked, WalletStorageReadUnlocked,
9292
};
9393
use wallet_types::{
9494
account_info::DEFAULT_ACCOUNT_INDEX,
@@ -1757,6 +1757,7 @@ fn find_trezor_device(
17571757
Ok((client, data, session_id))
17581758
}
17591759

1760+
#[async_trait]
17601761
impl SignerProvider for TrezorSignerProvider {
17611762
type S = TrezorSigner;
17621763
type K = AccountKeyChainImplHardware;
@@ -1765,12 +1766,12 @@ impl SignerProvider for TrezorSignerProvider {
17651766
TrezorSigner::new(chain_config, self.client.clone(), self.session_id.clone())
17661767
}
17671768

1768-
fn make_new_account(
1769+
async fn make_new_account<B: storage::Backend>(
17691770
&mut self,
17701771
chain_config: Arc<ChainConfig>,
17711772
account_index: U31,
17721773
name: Option<String>,
1773-
db_tx: &mut impl WalletStorageWriteUnlocked,
1774+
db_tx: &mut StoreLocalReadWriteUnlocked<B>,
17741775
) -> WalletResult<Account<Self::K>> {
17751776
let account_pubkey = self.fetch_extended_pub_key(&chain_config, account_index)?;
17761777

0 commit comments

Comments
 (0)