Skip to content

Commit 78ddcf9

Browse files
committed
Add HWI binary implementation
1 parent c1f20b2 commit 78ddcf9

File tree

4 files changed

+358
-0
lines changed

4 files changed

+358
-0
lines changed

Diff for: src/error.rs

+3
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
}

Diff for: src/implementations.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod binary_implementation;
12
pub mod python_implementation;

Diff for: src/implementations/binary_implementation.rs

+326
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
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 output =
65+
BinaryHWIImplementation::<T>::run_hwi_command(None, expert, Some(&client.chain), args)?;
66+
let devices: Vec<HWIDevice> = deserialize_obj!(&output)?;
67+
68+
let device = devices
69+
.into_iter()
70+
.find(|d| {
71+
device_type.as_ref().map_or(true, |t| &d.device_type == t)
72+
&& fingerprint.map_or(true, |f| d.fingerprint.to_string() == f)
73+
})
74+
.ok_or_else(|| Error::Hwi("No matching device found".to_string(), None))?;
75+
76+
client.device = Some(device);
77+
Ok(client)
78+
}
79+
80+
fn get_master_xpub(&self, addrtype: HWIAddressType, account: u32) -> Result<String, Error> {
81+
let mut args = vec!["getmasterxpub"];
82+
let addrtype_str = addrtype.to_string();
83+
let account_str = account.to_string();
84+
args.extend_from_slice(&["--addr-type", &addrtype_str]);
85+
args.extend_from_slice(&["--account", &account_str]);
86+
87+
BinaryHWIImplementation::<T>::run_hwi_command(
88+
self.device.as_ref(),
89+
self.expert,
90+
Some(&self.chain),
91+
args,
92+
)
93+
}
94+
95+
fn sign_tx(&self, psbt: &Psbt) -> Result<String, Error> {
96+
let psbt_str = psbt.to_string();
97+
let args = vec!["signtx", &psbt_str];
98+
99+
let output = BinaryHWIImplementation::<T>::run_hwi_command(
100+
self.device.as_ref(),
101+
self.expert,
102+
Some(&self.chain),
103+
args,
104+
)?;
105+
Ok(output)
106+
}
107+
108+
fn get_xpub(&self, path: &str, expert: bool) -> Result<String, Error> {
109+
let args = vec!["getxpub", &path];
110+
111+
BinaryHWIImplementation::<T>::run_hwi_command(
112+
self.device.as_ref(),
113+
expert,
114+
Some(&self.chain),
115+
args,
116+
)
117+
}
118+
119+
fn sign_message(&self, message: &str, path: &str) -> Result<String, Error> {
120+
let args = vec!["signmessage", message, path];
121+
122+
BinaryHWIImplementation::<T>::run_hwi_command(
123+
self.device.as_ref(),
124+
self.expert,
125+
Some(&self.chain),
126+
args,
127+
)
128+
}
129+
130+
fn get_keypool(
131+
&self,
132+
keypool: bool,
133+
internal: bool,
134+
addr_type: HWIAddressType,
135+
addr_all: bool,
136+
account: u32,
137+
path: Option<String>,
138+
start: u32,
139+
end: u32,
140+
) -> Result<String, Error> {
141+
let mut args = vec!["getkeypool"];
142+
143+
if keypool {
144+
args.push("--keypool");
145+
}
146+
if internal {
147+
args.push("--internal");
148+
}
149+
let addrtype_str = addr_type.to_string();
150+
args.extend_from_slice(&["--addr-type", &addrtype_str]);
151+
if addr_all {
152+
args.push("--addr-all");
153+
}
154+
let account_str = account.to_string();
155+
args.extend_from_slice(&["--account", &account_str]);
156+
if let Some(p) = path.as_deref() {
157+
args.extend_from_slice(&["--path", p]);
158+
}
159+
let start_str = start.to_string();
160+
args.push(&start_str);
161+
let end_str = end.to_string();
162+
args.push(&end_str);
163+
164+
BinaryHWIImplementation::<T>::run_hwi_command(
165+
self.device.as_ref(),
166+
self.expert,
167+
Some(&self.chain),
168+
args,
169+
)
170+
}
171+
172+
fn get_descriptors(&self, account: u32) -> Result<String, Error> {
173+
let mut args = vec!["getdescriptors"];
174+
let account_str = account.to_string();
175+
args.extend_from_slice(&["--account", &account_str]);
176+
177+
BinaryHWIImplementation::<T>::run_hwi_command(
178+
self.device.as_ref(),
179+
self.expert,
180+
Some(&self.chain),
181+
args,
182+
)
183+
}
184+
185+
fn display_address_with_desc(&self, descriptor: &str) -> Result<String, Error> {
186+
let mut args = vec!["displayaddress"];
187+
args.push("--desc");
188+
args.push(descriptor);
189+
190+
BinaryHWIImplementation::<T>::run_hwi_command(
191+
self.device.as_ref(),
192+
self.expert,
193+
Some(&self.chain),
194+
args,
195+
)
196+
}
197+
198+
fn display_address_with_path(
199+
&self,
200+
path: &str,
201+
address_type: HWIAddressType,
202+
) -> Result<String, Error> {
203+
let mut args = vec!["displayaddress"];
204+
args.extend_from_slice(&["--path", path]);
205+
let addr_type_str = address_type.to_string();
206+
args.extend_from_slice(&["--addr-type", &addr_type_str]);
207+
208+
BinaryHWIImplementation::<T>::run_hwi_command(
209+
self.device.as_ref(),
210+
self.expert,
211+
Some(&self.chain),
212+
args,
213+
)
214+
}
215+
216+
fn install_udev_rules(_: &str, location: &str) -> Result<String, Error> {
217+
let mut args = vec!["installudevrules"];
218+
args.extend_from_slice(&["--location", location]);
219+
220+
BinaryHWIImplementation::<T>::run_hwi_command(None, false, None, args)
221+
}
222+
223+
fn set_log_level(_: LogLevel) -> Result<(), Error> {
224+
Err(Error::NotImplemented)
225+
}
226+
227+
fn toggle_passphrase(&self) -> Result<String, Error> {
228+
let args = vec!["togglepassphrase"];
229+
BinaryHWIImplementation::<T>::run_hwi_command(
230+
self.device.as_ref(),
231+
self.expert,
232+
Some(&self.chain),
233+
args,
234+
)
235+
}
236+
237+
fn setup_device(&self, label: &str, passphrase: &str) -> Result<String, Error> {
238+
let mut args = vec!["setup"];
239+
args.extend_from_slice(&["--label", label]);
240+
args.extend_from_slice(&["--backup_passphrase", passphrase]);
241+
242+
BinaryHWIImplementation::<T>::run_hwi_command(
243+
self.device.as_ref(),
244+
self.expert,
245+
Some(&self.chain),
246+
args,
247+
)
248+
}
249+
250+
fn restore_device(&self, label: &str, word_count: u8) -> Result<String, Error> {
251+
let mut args = vec!["restore"];
252+
let word_count_str = word_count.to_string();
253+
args.extend_from_slice(&["--word_count", &word_count_str]);
254+
args.extend_from_slice(&["--label", label]);
255+
256+
BinaryHWIImplementation::<T>::run_hwi_command(
257+
self.device.as_ref(),
258+
self.expert,
259+
Some(&self.chain),
260+
args,
261+
)
262+
}
263+
fn backup_device(&self, label: &str, backup_passphrase: &str) -> Result<String, Error> {
264+
let mut args = vec!["backup"];
265+
args.extend_from_slice(&["--label", label]);
266+
args.extend_from_slice(&["--backup_passphrase", backup_passphrase]);
267+
268+
BinaryHWIImplementation::<T>::run_hwi_command(
269+
self.device.as_ref(),
270+
self.expert,
271+
Some(&self.chain),
272+
args,
273+
)
274+
}
275+
276+
fn wipe_device(&self) -> Result<String, Error> {
277+
let args = vec!["wipe"];
278+
BinaryHWIImplementation::<T>::run_hwi_command(
279+
self.device.as_ref(),
280+
self.expert,
281+
Some(&self.chain),
282+
args,
283+
)
284+
}
285+
286+
fn get_version() -> Result<String, Error> {
287+
let args = vec!["--version"];
288+
BinaryHWIImplementation::<T>::run_hwi_command(None, false, None, args)
289+
}
290+
291+
fn install_hwilib(_: String) -> Result<(), Error> {
292+
Err(Error::NotImplemented)
293+
}
294+
}
295+
296+
impl<T: HWIBinaryExecutor> BinaryHWIImplementation<T> {
297+
fn run_hwi_command(
298+
device: Option<&HWIDevice>,
299+
expert: bool,
300+
chain: Option<&HWIChain>,
301+
args: Vec<&str>,
302+
) -> Result<String, Error> {
303+
let mut command_args = Vec::new();
304+
305+
if !args.contains(&"enumerate") && !args.contains(&"--version") {
306+
let fingerprint = device
307+
.ok_or(Error::Hwi("Device fingerprint not set".to_string(), None))?
308+
.fingerprint;
309+
command_args.push("--fingerprint".to_string());
310+
command_args.push(fingerprint.to_string());
311+
}
312+
313+
if expert {
314+
command_args.push("--expert".to_string());
315+
}
316+
317+
if let Some(c) = chain {
318+
command_args.push("--chain".to_string());
319+
command_args.push(c.to_string());
320+
}
321+
322+
command_args.extend(args.iter().map(|s| s.to_string()));
323+
324+
T::execute_command(command_args)
325+
}
326+
}

0 commit comments

Comments
 (0)