Skip to content

Commit fd62a71

Browse files
committed
Add ledger support in CLI and GUI wallet
1 parent d4a8f4d commit fd62a71

File tree

33 files changed

+962
-150
lines changed

33 files changed

+962
-150
lines changed

node-gui/backend/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,4 @@ ledger = [
6565
"wallet-rpc-client/ledger",
6666
"wallet-cli-commands/ledger",
6767
]
68+
default = ["trezor", "ledger"]

node-gui/backend/src/backend_impl.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,10 +354,44 @@ impl Backend {
354354

355355
(wallet_data, accounts_info, best_block)
356356
}
357+
#[cfg(feature = "ledger")]
358+
(WalletType::Ledger, ColdHotNodeController::Hot(controller)) => {
359+
let handles_client = WalletHandlesClient::new(
360+
controller.chainstate.clone(),
361+
controller.mempool.clone(),
362+
controller.block_prod.clone(),
363+
controller.p2p.clone(),
364+
)
365+
.await
366+
.map_err(|e| BackendError::WalletError(e.to_string()))?;
367+
368+
let (wallet_rpc, command_handler, best_block, accounts_info, accounts_data) = self
369+
.create_wallet(
370+
handles_client,
371+
file_path.clone(),
372+
wallet_args,
373+
import,
374+
wallet_events,
375+
)
376+
.await?;
377+
378+
let wallet_data = WalletData {
379+
controller: GuiHotColdController::Hot(wallet_rpc, command_handler),
380+
accounts: accounts_data,
381+
best_block,
382+
updated: false,
383+
};
384+
385+
(wallet_data, accounts_info, best_block)
386+
}
357387
#[cfg(feature = "trezor")]
358388
(WalletType::Trezor, ColdHotNodeController::Cold) => {
359389
return Err(BackendError::ColdTrezorNotSupported)
360390
}
391+
#[cfg(feature = "ledger")]
392+
(WalletType::Ledger, ColdHotNodeController::Cold) => {
393+
return Err(BackendError::ColdTrezorNotSupported)
394+
}
361395
(WalletType::Hot, ColdHotNodeController::Cold) => {
362396
return Err(BackendError::HotNotSupported)
363397
}
@@ -593,10 +627,50 @@ impl Backend {
593627

594628
(wallet_data, accounts_info, best_block, encryption_state)
595629
}
630+
#[cfg(feature = "ledger")]
631+
(WalletType::Ledger, ColdHotNodeController::Hot(controller)) => {
632+
let handles_client = WalletHandlesClient::new(
633+
controller.chainstate.clone(),
634+
controller.mempool.clone(),
635+
controller.block_prod.clone(),
636+
controller.p2p.clone(),
637+
)
638+
.await
639+
.map_err(|e| BackendError::WalletError(e.to_string()))?;
640+
641+
let (
642+
wallet_rpc,
643+
command_handler,
644+
encryption_state,
645+
best_block,
646+
accounts_info,
647+
accounts_data,
648+
) = self
649+
.open_wallet(
650+
handles_client,
651+
file_path.clone(),
652+
wallet_events,
653+
Some(HardwareWalletType::Ledger),
654+
)
655+
.await?;
656+
657+
let wallet_data = WalletData {
658+
controller: GuiHotColdController::Hot(wallet_rpc, command_handler),
659+
accounts: accounts_data,
660+
best_block,
661+
updated: false,
662+
};
663+
664+
(wallet_data, accounts_info, best_block, encryption_state)
665+
}
596666
#[cfg(feature = "trezor")]
597667
(WalletType::Trezor, ColdHotNodeController::Cold) => {
598668
return Err(BackendError::ColdTrezorNotSupported)
599669
}
670+
#[cfg(feature = "ledger")]
671+
(WalletType::Ledger, ColdHotNodeController::Cold) => {
672+
return Err(BackendError::ColdTrezorNotSupported)
673+
}
600674
(WalletType::Hot, ColdHotNodeController::Cold) => {
601675
return Err(BackendError::HotNotSupported)
602676
}

node-gui/src/main_window/main_menu.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,32 @@ fn make_menu_file<'a>(wallet_mode: WalletMode) -> Item<'a, MenuMessage, Theme, i
146146
},
147147
));
148148
}
149+
#[cfg(feature = "ledger")]
150+
{
151+
menu.push(menu_item(
152+
"(Beta) Create new Ledger wallet",
153+
MenuMessage::CreateNewWallet {
154+
wallet_type: WalletType::Ledger,
155+
},
156+
));
157+
menu.push(menu_item(
158+
"(Beta) Recover from Ledger wallet",
159+
MenuMessage::RecoverWallet {
160+
wallet_type: WalletType::Ledger,
161+
},
162+
));
163+
menu.push(menu_item(
164+
"(Beta) Open Ledger wallet",
165+
MenuMessage::OpenWallet {
166+
wallet_type: WalletType::Ledger,
167+
},
168+
));
169+
}
170+
149171
// TODO: enable setting when needed
150172
// menu.push(menu_item("Settings", MenuMessage::NoOp));
151173
menu.push(menu_item("Exit", MenuMessage::Exit));
174+
152175
menu
153176
}
154177
WalletMode::Cold => {

node-gui/src/main_window/main_widget/tabs/wallet/left_panel.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ pub fn view_left_panel(
126126
WalletType::Trezor => {
127127
wallet_info.best_block.1.next_height() < node_state.chain_info.best_block_height
128128
}
129+
#[cfg(feature = "ledger")]
130+
WalletType::Ledger => {
131+
wallet_info.best_block.1.next_height() < node_state.chain_info.best_block_height
132+
}
129133
WalletType::Hot => {
130134
wallet_info.best_block.1.next_height() < node_state.chain_info.best_block_height
131135
}
@@ -221,6 +225,41 @@ pub fn view_left_panel(
221225
)
222226
]
223227
}
228+
#[cfg(feature = "ledger")]
229+
WalletType::Ledger => {
230+
column![
231+
panel_button(
232+
"Transactions",
233+
SelectedPanel::Transactions,
234+
selected_panel,
235+
TRANSACTIONS_TOOLTIP_TEXT
236+
),
237+
panel_button(
238+
"Addresses",
239+
SelectedPanel::Addresses,
240+
selected_panel,
241+
ADDRESSES_TOOLTIP_TEXT
242+
),
243+
panel_button(
244+
"Send",
245+
SelectedPanel::Send,
246+
selected_panel,
247+
SEND_TOOLTIP_TEXT
248+
),
249+
panel_button(
250+
"Delegation",
251+
SelectedPanel::Delegation,
252+
selected_panel,
253+
DELEGATION_TOOLTIP_TEXT
254+
),
255+
panel_button(
256+
"Console",
257+
SelectedPanel::Console,
258+
selected_panel,
259+
CONSOLE_TOOLTIP_TEXT,
260+
)
261+
]
262+
}
224263
WalletType::Cold => {
225264
column![
226265
panel_button(

node-gui/src/main_window/main_widget/tabs/wallet/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ impl WalletTab {
183183
WalletType::Cold => SelectedPanel::Addresses,
184184
#[cfg(feature = "trezor")]
185185
WalletType::Trezor => SelectedPanel::Transactions,
186+
#[cfg(feature = "ledger")]
187+
WalletType::Ledger => SelectedPanel::Transactions,
186188
};
187189

188190
WalletTab {
@@ -496,6 +498,10 @@ impl Tab for WalletTab {
496498
WalletType::Trezor => {
497499
wallet_info.best_block.1.next_height() < node_state.chain_info.best_block_height
498500
}
501+
#[cfg(feature = "ledger")]
502+
WalletType::Ledger => {
503+
wallet_info.best_block.1.next_height() < node_state.chain_info.best_block_height
504+
}
499505
WalletType::Hot => {
500506
wallet_info.best_block.1.next_height() < node_state.chain_info.best_block_height
501507
}

node-gui/src/main_window/mod.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use wallet_cli_commands::ConsoleCommand;
3838
use wallet_controller::types::WalletTypeArgs;
3939
use wallet_types::{seed_phrase::StoreSeedPhrase, wallet_type::WalletType, ImportOrCreate};
4040

41-
#[cfg(feature = "trezor")]
41+
#[cfg(any(feature = "trezor", feature = "ledger"))]
4242
use crate::widgets::create_hw_wallet::hw_wallet_create_dialog;
4343
use crate::{
4444
main_window::{main_menu::MenuMessage, main_widget::MainWidgetMessage},
@@ -148,6 +148,8 @@ pub enum WalletArgs {
148148
},
149149
#[cfg(feature = "trezor")]
150150
Trezor,
151+
#[cfg(feature = "ledger")]
152+
Ledger,
151153
}
152154

153155
impl From<&WalletArgs> for WalletType {
@@ -165,6 +167,8 @@ impl From<&WalletArgs> for WalletType {
165167
}
166168
#[cfg(feature = "trezor")]
167169
WalletArgs::Trezor => WalletType::Trezor,
170+
#[cfg(feature = "ledger")]
171+
WalletArgs::Ledger => WalletType::Ledger,
168172
}
169173
}
170174
}
@@ -291,6 +295,8 @@ impl MainWindow {
291295
},
292296
#[cfg(feature = "trezor")]
293297
WalletType::Trezor => WalletArgs::Trezor,
298+
#[cfg(feature = "ledger")]
299+
WalletType::Ledger => WalletArgs::Ledger,
294300
};
295301
self.active_dialog = ActiveDialog::WalletCreate { wallet_args };
296302
Task::none()
@@ -733,6 +739,8 @@ impl MainWindow {
733739
}
734740
#[cfg(feature = "trezor")]
735741
WalletArgs::Trezor => WalletTypeArgs::Trezor { device_id: None },
742+
#[cfg(feature = "ledger")]
743+
WalletArgs::Ledger => WalletTypeArgs::Ledger,
736744
};
737745

738746
self.file_dialog_active = true;
@@ -864,6 +872,16 @@ impl MainWindow {
864872
ImportOrCreate::Create,
865873
)
866874
.into(),
875+
#[cfg(feature = "ledger")]
876+
WalletArgs::Ledger => hw_wallet_create_dialog(
877+
Box::new(move || MainWindowMessage::ImportWalletMnemonic {
878+
args: WalletArgs::Ledger,
879+
import: ImportOrCreate::Create,
880+
}),
881+
Box::new(|| MainWindowMessage::CloseDialog),
882+
ImportOrCreate::Create,
883+
)
884+
.into(),
867885
},
868886
ActiveDialog::WalletRecover { wallet_type } => {
869887
let is_cold = *wallet_type == WalletType::Cold;
@@ -888,6 +906,16 @@ impl MainWindow {
888906
ImportOrCreate::Import,
889907
)
890908
.into(),
909+
#[cfg(feature = "ledger")]
910+
WalletType::Ledger => hw_wallet_create_dialog(
911+
Box::new(move || MainWindowMessage::ImportWalletMnemonic {
912+
args: WalletArgs::Ledger,
913+
import: ImportOrCreate::Create,
914+
}),
915+
Box::new(|| MainWindowMessage::CloseDialog),
916+
ImportOrCreate::Import,
917+
)
918+
.into(),
891919
}
892920
}
893921

wallet/src/key_chain/master_key_chain/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use crypto::key::hdkd::u31::U31;
2121
use crypto::vrf::ExtendedVRFPrivateKey;
2222
use std::sync::Arc;
2323
use wallet_storage::{
24-
StoreTxRwUnlocked, WalletStorageReadLocked, WalletStorageReadUnlocked,
25-
WalletStorageWriteLocked, WalletStorageWriteUnlocked,
24+
WalletStorageReadLocked, WalletStorageReadUnlocked, WalletStorageWriteLocked,
25+
WalletStorageWriteUnlocked,
2626
};
2727
use wallet_types::seed_phrase::{SerializableSeedPhrase, StoreSeedPhrase};
2828

@@ -59,9 +59,9 @@ impl MasterKeyChain {
5959
))
6060
}
6161

62-
pub fn new_from_mnemonic<B: storage::AsyncBackend>(
62+
pub fn new_from_mnemonic(
6363
chain_config: Arc<ChainConfig>,
64-
db_tx: &mut StoreTxRwUnlocked<B>,
64+
db_tx: &mut impl WalletStorageWriteUnlocked,
6565
mnemonic_str: &str,
6666
passphrase: Option<&str>,
6767
save_seed_phrase: StoreSeedPhrase,
@@ -80,9 +80,9 @@ impl MasterKeyChain {
8080
)
8181
}
8282

83-
fn new_from_root_key<B: storage::AsyncBackend>(
83+
fn new_from_root_key(
8484
chain_config: Arc<ChainConfig>,
85-
db_tx: &mut StoreTxRwUnlocked<B>,
85+
db_tx: &mut impl WalletStorageWriteUnlocked,
8686
root_key: ExtendedPrivateKey,
8787
root_vrf_key: ExtendedVRFPrivateKey,
8888
seed_phrase: Option<SerializableSeedPhrase>,

wallet/src/signer/ledger_signer/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,9 +1243,8 @@ impl LedgerSignerProvider {
12431243
pub async fn load_from_database<T: WalletStorageReadLocked + Send>(
12441244
chain_config: Arc<ChainConfig>,
12451245
db_tx: T,
1246-
_device_id: Option<String>,
1246+
// _device_id: Option<String>,
12471247
) -> (T, WalletResult<Self>) {
1248-
//let (client, data) = find_trezor_device_from_db(db_tx, device_id)?;
12491248
let (mut client, data) = match find_ledger_device().await {
12501249
Ok(x) => x,
12511250
Err(e) => return (db_tx, Err(e.into())),

wallet/src/signer/software_signer/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ use crypto::key::{
5252
};
5353
use randomness::make_true_rng;
5454
use wallet_storage::{
55-
StoreTxRwUnlocked, WalletStorageReadLocked, WalletStorageReadUnlocked,
56-
WalletStorageWriteUnlocked,
55+
WalletStorageReadLocked, WalletStorageReadUnlocked, WalletStorageWriteUnlocked,
5756
};
5857
use wallet_types::{
5958
hw_data::HardwareWalletFullInfo, partially_signed_transaction::PartiallySignedTransaction,
@@ -489,9 +488,9 @@ pub struct SoftwareSignerProvider {
489488
}
490489

491490
impl SoftwareSignerProvider {
492-
pub fn new_from_mnemonic<B: storage::AsyncBackend>(
491+
pub fn new_from_mnemonic(
493492
chain_config: Arc<ChainConfig>,
494-
db_tx: &mut StoreTxRwUnlocked<B>,
493+
db_tx: &mut impl WalletStorageWriteUnlocked,
495494
mnemonic_str: &str,
496495
passphrase: Option<&str>,
497496
save_seed_phrase: StoreSeedPhrase,

0 commit comments

Comments
 (0)