Skip to content

Commit fa1ed3e

Browse files
committed
Add HWI binary implementation
1 parent c1f20b2 commit fa1ed3e

File tree

4 files changed

+359
-0
lines changed

4 files changed

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

0 commit comments

Comments
 (0)