Skip to content

Commit e1a5bcc

Browse files
committed
Add HWI binary implementation
1 parent 9ffb85e commit e1a5bcc

File tree

4 files changed

+362
-0
lines changed

4 files changed

+362
-0
lines changed

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub enum Error {
7878
Io(std::io::Error),
7979
Hwi(String, Option<ErrorCode>),
8080
Python(pyo3::PyErr),
81+
NotImplemented,
8182
}
8283

8384
impl fmt::Display for Error {
@@ -90,6 +91,7 @@ impl fmt::Display for Error {
9091
Io(_) => f.write_str("I/O error"),
9192
Hwi(ref s, ref code) => write!(f, "HWI error: {}, ({:?})", s, code),
9293
Python(_) => f.write_str("python error"),
94+
NotImplemented => f.write_str("not implemented"),
9395
}
9496
}
9597
}
@@ -104,6 +106,7 @@ impl std::error::Error for Error {
104106
Io(ref e) => Some(e),
105107
Hwi(_, _) => None,
106108
Python(ref e) => Some(e),
109+
NotImplemented => None,
107110
}
108111
}
109112
}

src/implementations.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod binary_implementation;
12
pub mod python_implementation;
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
use serde_json::value::Value;
2+
use std::str;
3+
4+
use crate::error::Error;
5+
use crate::types::{
6+
HWIAddressType, HWIBinaryExecutor, HWIChain, HWIDevice, HWIDeviceType, HWIImplementation,
7+
LogLevel,
8+
};
9+
use bitcoin::Psbt;
10+
11+
macro_rules! deserialize_obj {
12+
( $e: expr ) => {{
13+
let value: Value = serde_json::from_str($e)?;
14+
let obj = value.clone();
15+
serde_json::from_value(value)
16+
.map_err(|e| Error::Hwi(format!("error {} while deserializing {}", e, obj), None))
17+
}};
18+
}
19+
20+
#[derive(Debug)]
21+
pub struct BinaryHWIImplementation<T: HWIBinaryExecutor> {
22+
device: Option<HWIDevice>,
23+
expert: bool,
24+
chain: HWIChain,
25+
_phantom: std::marker::PhantomData<T>,
26+
}
27+
28+
impl<T: HWIBinaryExecutor> HWIImplementation for BinaryHWIImplementation<T> {
29+
fn enumerate() -> Result<String, Error> {
30+
let output =
31+
BinaryHWIImplementation::<T>::run_hwi_command(None, false, None, vec!["enumerate"])?;
32+
Ok(output.to_string())
33+
}
34+
35+
fn get_client(device: &HWIDevice, expert: bool, chain: HWIChain) -> Result<Self, Error> {
36+
Ok(Self {
37+
device: Some(device.clone()),
38+
expert,
39+
chain,
40+
_phantom: std::marker::PhantomData,
41+
})
42+
}
43+
44+
fn find_device(
45+
password: Option<&str>,
46+
device_type: Option<HWIDeviceType>,
47+
fingerprint: Option<&str>,
48+
expert: bool,
49+
chain: HWIChain,
50+
) -> Result<Self, Error> {
51+
let mut client = BinaryHWIImplementation {
52+
device: None,
53+
expert,
54+
chain,
55+
_phantom: std::marker::PhantomData,
56+
};
57+
58+
let mut args = vec!["enumerate"];
59+
60+
if let Some(pw) = password {
61+
args.extend_from_slice(&["--password", pw]);
62+
}
63+
64+
let device_type_str = device_type.map(|dt| dt.to_string());
65+
66+
if let Some(dt) = device_type_str.as_deref() {
67+
args.extend_from_slice(&["--device-type", dt]);
68+
}
69+
if let Some(fp) = fingerprint {
70+
args.extend_from_slice(&["--fingerprint", fp]);
71+
}
72+
73+
let output =
74+
BinaryHWIImplementation::<T>::run_hwi_command(None, expert, Some(&client.chain), args)?;
75+
let devices: Vec<HWIDevice> = deserialize_obj!(&output)?;
76+
77+
if devices.is_empty() {
78+
return Err(Error::Hwi("No devices found".to_string(), None));
79+
}
80+
81+
client.device = Some(devices[0].clone());
82+
Ok(client)
83+
}
84+
85+
fn get_master_xpub(&self, addrtype: HWIAddressType, account: u32) -> Result<String, Error> {
86+
let mut args = vec!["getmasterxpub"];
87+
let addrtype_str = addrtype.to_string();
88+
let account_str = account.to_string();
89+
args.extend_from_slice(&["--addr-type", &addrtype_str]);
90+
args.extend_from_slice(&["--account", &account_str]);
91+
92+
BinaryHWIImplementation::<T>::run_hwi_command(
93+
self.device.as_ref(),
94+
self.expert,
95+
Some(&self.chain),
96+
args,
97+
)
98+
}
99+
100+
fn sign_tx(&self, psbt: &Psbt) -> Result<String, Error> {
101+
let psbt_str = psbt.to_string();
102+
let args = vec!["signtx", &psbt_str];
103+
104+
let output = BinaryHWIImplementation::<T>::run_hwi_command(
105+
self.device.as_ref(),
106+
self.expert,
107+
Some(&self.chain),
108+
args,
109+
)?;
110+
Ok(output)
111+
}
112+
113+
fn get_xpub(&self, path: &str, expert: bool) -> Result<String, Error> {
114+
let args = vec!["getxpub", &path];
115+
116+
BinaryHWIImplementation::<T>::run_hwi_command(
117+
self.device.as_ref(),
118+
expert,
119+
Some(&self.chain),
120+
args,
121+
)
122+
}
123+
124+
fn sign_message(&self, message: &str, path: &str) -> Result<String, Error> {
125+
let args = vec!["signmessage", message, path];
126+
127+
BinaryHWIImplementation::<T>::run_hwi_command(
128+
self.device.as_ref(),
129+
self.expert,
130+
Some(&self.chain),
131+
args,
132+
)
133+
}
134+
135+
fn get_keypool(
136+
&self,
137+
keypool: bool,
138+
internal: bool,
139+
addr_type: HWIAddressType,
140+
addr_all: bool,
141+
account: u32,
142+
path: Option<String>,
143+
start: u32,
144+
end: u32,
145+
) -> Result<String, Error> {
146+
let mut args = vec!["getkeypool"];
147+
148+
if keypool {
149+
args.push("--keypool");
150+
}
151+
if internal {
152+
args.push("--internal");
153+
}
154+
let addrtype_str = addr_type.to_string();
155+
args.extend_from_slice(&["--addr-type", &addrtype_str]);
156+
if addr_all {
157+
args.push("--addr-all");
158+
}
159+
let account_str = account.to_string();
160+
args.extend_from_slice(&["--account", &account_str]);
161+
if let Some(p) = path.as_deref() {
162+
args.extend_from_slice(&["--path", p]);
163+
}
164+
let start_str = start.to_string();
165+
args.push(&start_str);
166+
let end_str = end.to_string();
167+
args.push(&end_str);
168+
169+
BinaryHWIImplementation::<T>::run_hwi_command(
170+
self.device.as_ref(),
171+
self.expert,
172+
Some(&self.chain),
173+
args,
174+
)
175+
}
176+
177+
fn get_descriptors(&self, account: u32) -> Result<String, Error> {
178+
let mut args = vec!["getdescriptors"];
179+
let account_str = account.to_string();
180+
args.extend_from_slice(&["--account", &account_str]);
181+
182+
BinaryHWIImplementation::<T>::run_hwi_command(
183+
self.device.as_ref(),
184+
self.expert,
185+
Some(&self.chain),
186+
args,
187+
)
188+
}
189+
190+
fn display_address_with_desc(&self, descriptor: &str) -> Result<String, Error> {
191+
let mut args = vec!["displayaddress"];
192+
args.push("--desc");
193+
args.push(descriptor);
194+
195+
BinaryHWIImplementation::<T>::run_hwi_command(
196+
self.device.as_ref(),
197+
self.expert,
198+
Some(&self.chain),
199+
args,
200+
)
201+
}
202+
203+
fn display_address_with_path(
204+
&self,
205+
path: &str,
206+
address_type: HWIAddressType,
207+
) -> Result<String, Error> {
208+
let mut args = vec!["displayaddress"];
209+
args.extend_from_slice(&["--path", path]);
210+
let addr_type_str = address_type.to_string();
211+
args.extend_from_slice(&["--addr-type", &addr_type_str]);
212+
213+
BinaryHWIImplementation::<T>::run_hwi_command(
214+
self.device.as_ref(),
215+
self.expert,
216+
Some(&self.chain),
217+
args,
218+
)
219+
}
220+
221+
fn install_udev_rules(_: &str, location: &str) -> Result<String, Error> {
222+
let mut args = vec!["installudevrules"];
223+
args.extend_from_slice(&["--location", location]);
224+
225+
BinaryHWIImplementation::<T>::run_hwi_command(None, false, None, args)
226+
}
227+
228+
fn set_log_level(_: LogLevel) -> Result<(), Error> {
229+
Err(Error::NotImplemented)
230+
}
231+
232+
fn toggle_passphrase(&self) -> Result<String, Error> {
233+
let args = vec!["togglepassphrase"];
234+
BinaryHWIImplementation::<T>::run_hwi_command(
235+
self.device.as_ref(),
236+
self.expert,
237+
Some(&self.chain),
238+
args,
239+
)
240+
}
241+
242+
fn setup_device(&self, label: &str, passphrase: &str) -> Result<String, Error> {
243+
let mut args = vec!["setup"];
244+
args.extend_from_slice(&["--label", label]);
245+
args.extend_from_slice(&["--backup_passphrase", passphrase]);
246+
247+
BinaryHWIImplementation::<T>::run_hwi_command(
248+
self.device.as_ref(),
249+
self.expert,
250+
Some(&self.chain),
251+
args,
252+
)
253+
}
254+
255+
fn restore_device(&self, label: &str, word_count: u8) -> Result<String, Error> {
256+
let mut args = vec!["restore"];
257+
let word_count_str = word_count.to_string();
258+
args.extend_from_slice(&["--word_count", &word_count_str]);
259+
args.extend_from_slice(&["--label", label]);
260+
261+
BinaryHWIImplementation::<T>::run_hwi_command(
262+
self.device.as_ref(),
263+
self.expert,
264+
Some(&self.chain),
265+
args,
266+
)
267+
}
268+
fn backup_device(&self, label: &str, backup_passphrase: &str) -> Result<String, Error> {
269+
let mut args = vec!["backup"];
270+
args.extend_from_slice(&["--label", label]);
271+
args.extend_from_slice(&["--backup_passphrase", backup_passphrase]);
272+
273+
BinaryHWIImplementation::<T>::run_hwi_command(
274+
self.device.as_ref(),
275+
self.expert,
276+
Some(&self.chain),
277+
args,
278+
)
279+
}
280+
281+
fn wipe_device(&self) -> Result<String, Error> {
282+
let args = vec!["wipe"];
283+
BinaryHWIImplementation::<T>::run_hwi_command(
284+
self.device.as_ref(),
285+
self.expert,
286+
Some(&self.chain),
287+
args,
288+
)
289+
}
290+
291+
fn get_version() -> Result<String, Error> {
292+
let args = vec!["--version"];
293+
BinaryHWIImplementation::<T>::run_hwi_command(None, false, None, args)
294+
}
295+
296+
fn install_hwilib(_: String) -> Result<(), Error> {
297+
Err(Error::NotImplemented)
298+
}
299+
}
300+
301+
impl<T: HWIBinaryExecutor> BinaryHWIImplementation<T> {
302+
fn run_hwi_command(
303+
device: Option<&HWIDevice>,
304+
expert: bool,
305+
chain: Option<&HWIChain>,
306+
args: Vec<&str>,
307+
) -> Result<String, Error> {
308+
let mut command_args = Vec::new();
309+
310+
if args[0] != "enumerate" && args[0] != "--version" {
311+
let fingerprint = device
312+
.ok_or(Error::Hwi("Device fingerprint not set".to_string(), None))?
313+
.fingerprint;
314+
command_args.push("--fingerprint".to_string());
315+
command_args.push(fingerprint.to_string());
316+
}
317+
318+
if expert {
319+
command_args.push("--expert".to_string());
320+
}
321+
322+
if let Some(c) = chain {
323+
command_args.push("--chain".to_string());
324+
command_args.push(c.to_string());
325+
}
326+
327+
command_args.extend(args.iter().map(|s| s.to_string()));
328+
329+
T::execute_command(command_args)
330+
}
331+
}

0 commit comments

Comments
 (0)