Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions bhwi-async/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub mod transport;
use std::{error::Error as StdError, fmt::Debug};

use async_trait::async_trait;
pub use bhwi::common::Version;
pub use bhwi::common::Info;
use bhwi::{
Interpreter,
bitcoin::{
Expand Down Expand Up @@ -35,7 +35,7 @@ pub trait HttpClient {
pub trait HWI {
type Error: Debug;
async fn unlock(&mut self, network: Network) -> Result<(), Self::Error>;
async fn get_version(&mut self) -> Result<Version, Self::Error>;
async fn get_info(&mut self) -> Result<Info, Self::Error>;
async fn get_master_fingerprint(&mut self) -> Result<Fingerprint, Self::Error>;
async fn get_extended_pubkey(
&mut self,
Expand All @@ -55,7 +55,7 @@ pub trait HWI {
#[async_trait(?Send)]
pub trait HWIDevice {
async fn unlock(&mut self, network: Network) -> Result<(), HWIDeviceError>;
async fn get_version(&mut self) -> Result<Version, HWIDeviceError>;
async fn get_info(&mut self) -> Result<Info, HWIDeviceError>;
async fn get_master_fingerprint(&mut self) -> Result<Fingerprint, HWIDeviceError>;
async fn get_extended_pubkey(
&mut self,
Expand Down Expand Up @@ -112,8 +112,8 @@ where
Ok(())
}

async fn get_version(&mut self) -> Result<Version, Self::Error> {
if let common::Response::Version(version) =
async fn get_info(&mut self) -> Result<Info, Self::Error> {
if let common::Response::Info(version) =
run_command(self, common::Command::GetVersion).await?
{
Ok(version)
Expand Down Expand Up @@ -179,8 +179,8 @@ where
.map_err(HWIDeviceError::new)
}

async fn get_version(&mut self) -> Result<Version, HWIDeviceError> {
HWI::get_version(self).await.map_err(HWIDeviceError::new)
async fn get_info(&mut self) -> Result<Info, HWIDeviceError> {
HWI::get_info(self).await.map_err(HWIDeviceError::new)
}

async fn get_master_fingerprint(&mut self) -> Result<Fingerprint, HWIDeviceError> {
Expand Down
2 changes: 1 addition & 1 deletion bhwi-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ path = "src/bin/bhwi.rs"
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
bitcoin.workspace = true
bitcoin = { workspace = true, features = ["serde"] }
bhwi-async = { workspace = true, features = ["emulators"] }
futures.workspace = true
hex = { workspace = true, features = ["serde"] }
Expand Down
6 changes: 3 additions & 3 deletions bhwi-cli/src/bin/bhwi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ async fn main() -> Result<()> {
let fingerprint = device.fingerprint().await?;
let name = device.name().to_string();
let is_emulated = device.is_emulated();
let version = device.version().await?;
let info = device.info().await?;
match format {
Some(OutputFormat::Pretty) => {
if i == 0 {
Expand All @@ -104,10 +104,10 @@ async fn main() -> Result<()> {
);
}
println!("{}", "-".repeat(80));
let network = version.network.unwrap_or_default();
let network = info.networks_string();
println!(
"{name:<18} | {is_emulated:<8} | {fingerprint:<15} | {network:<12} | {:<8}",
version.version
info.version
);
println!("{}", "-".repeat(80));
}
Expand Down
67 changes: 54 additions & 13 deletions bhwi-cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use anyhow::Result;
use async_trait::async_trait;
use bhwi_async::HWIDevice;
use bhwi_async::Version;
use bitcoin::bip32::Fingerprint;
use bitcoin::{Network, bip32::Fingerprint};
use clap::ValueEnum;
use futures::future::join_all;
use serde::{Serialize, Serializer};
Expand All @@ -26,7 +25,35 @@ pub struct Device {
#[serde(default, serialize_with = "option_fingerprint")]
fingerprint: Option<Fingerprint>,
#[serde(flatten, skip_serializing_if = "Option::is_none")]
version: Option<Version>,
info: Option<Info>,
}

/// Serializable Device Information
#[derive(Debug, Clone, Default, Serialize)]
pub struct Info {
pub version: String,
pub networks: Vec<Network>,
pub firmware: Option<String>,
}

impl Info {
pub fn networks_string(&self) -> String {
self.networks
.iter()
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(", ")
}
}

impl From<bhwi_async::Info> for Info {
fn from(info: bhwi_async::Info) -> Self {
Self {
version: info.version,
networks: info.networks,
firmware: info.firmware,
}
}
}

impl Device {
Expand All @@ -36,7 +63,7 @@ impl Device {
device,
is_emulated,
fingerprint: None,
version: None,
info: None,
})
}

Expand All @@ -62,13 +89,13 @@ impl Device {
}
}

pub async fn version(&mut self) -> Result<Version> {
if let Some(ref version) = self.version {
Ok(version.clone())
pub async fn info(&mut self) -> Result<Info> {
if let Some(ref info) = self.info {
Ok(info.clone())
} else {
let version = self.device.get_version().await?;
self.version = Some(version.clone());
Ok(version)
let info: Info = self.device.get_info().await?.into();
self.info = Some(info.clone());
Ok(info)
}
}
}
Expand Down Expand Up @@ -100,17 +127,31 @@ impl DeviceManager {
}

pub async fn get_device_with_fingerprint(&self) -> Result<Option<Device>> {
let mut target_dev = None;
for mut d in self.enumerate().await? {
d.device.unlock(self.config.network).await?;
if let Some(fingerprint) = self.config.fingerprint {
if fingerprint == d.fingerprint().await? {
return Ok(Some(d));
target_dev = Some(d);
}
} else {
return Ok(Some(d));
target_dev = Some(d);
}
}
Ok(None)
let Some(mut dev) = target_dev else {
return Ok(None);
};
let info = dev.info().await?;
let networks = &info.networks;
let net = self.config.network;
if !networks.is_empty() && !networks.contains(&net) {
eprintln!(
"Warning: device {} is on {}, expected {net}",
dev.name,
info.networks_string()
);
}
Ok(Some(dev))
}

pub async fn enumerate(&self) -> Result<Vec<Device>> {
Expand Down
6 changes: 3 additions & 3 deletions bhwi/src/coldcard/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use bitcoin::secp256k1::ecdsa::Signature;

use crate::Interpreter;
use crate::coldcard::api::response::ResponseMessage;
use crate::common::{Command, Error, Recipient, Response, Transmit, Version};
use crate::common::{Command, Error, Info, Recipient, Response, Transmit};
use crate::device::DeviceId;

pub const DEFAULT_CKCC_SOCKET: &str = "/tmp/ckcc-simulator.sock";
Expand Down Expand Up @@ -219,9 +219,9 @@ impl From<ColdcardResponse> for Response {
ColdcardResponse::Version {
version,
device_model,
} => Response::Version(Version {
} => Response::Info(Info {
version: version.as_str().into(),
network: None,
networks: vec![],
firmware: Some(device_model),
}),
ColdcardResponse::MyPub { encryption_key, .. } => {
Expand Down
12 changes: 5 additions & 7 deletions bhwi/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,18 @@ pub enum Command {
pub enum Response {
TaskDone,
TaskBusy,
Version(Version),
Info(Info),
MasterFingerprint(Fingerprint),
Xpub(Xpub),
EncryptionKey([u8; 64]),
Signature(u8, Signature),
}

/// Version information returned from a device.
#[derive(Debug, Clone, Default, serde::Serialize)]
pub struct Version {
/// Device Information
#[derive(Debug, Clone, Default)]
pub struct Info {
pub version: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub network: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub networks: Vec<Network>,
pub firmware: Option<String>,
}

Expand Down
18 changes: 18 additions & 0 deletions bhwi/src/jade/api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// See https://github.com/Blockstream/Jade/blob/master/docs/index.rst
use bitcoin::Network;
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, fmt::Display};

Expand Down Expand Up @@ -133,6 +134,23 @@ pub enum JadeNetworks {
All,
}

impl From<JadeNetworks> for Vec<Network> {
fn from(networks: JadeNetworks) -> Self {
let main = [Network::Bitcoin];
let testnets = [Network::Testnet, Network::Testnet4];
match networks {
JadeNetworks::Main => main.to_vec(),
JadeNetworks::Test => testnets.to_vec(),
JadeNetworks::All => main
.iter()
.chain(testnets.iter())
.chain([Network::Signet, Network::Regtest].iter())
.copied()
.collect(),
}
}
}

impl Display for JadeNetworks {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
Expand Down
6 changes: 3 additions & 3 deletions bhwi/src/jade/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use serde::Serialize;
use serde::de::DeserializeOwned;

use crate::Interpreter;
use crate::common::{Command, Error, Recipient, Response, Transmit, Version};
use crate::common::{Command, Error, Info, Recipient, Response, Transmit};
use crate::device::DeviceId;
use crate::jade::api::GetInfoResponse;

Expand Down Expand Up @@ -287,9 +287,9 @@ impl From<JadeResponse> for Response {
JadeResponse::MasterFingerprint(fg) => Response::MasterFingerprint(fg),
JadeResponse::Xpub(xpub) => Response::Xpub(xpub),
JadeResponse::Signature(header, signature) => Response::Signature(header, signature),
JadeResponse::GetInfo(info) => Response::Version(Version {
JadeResponse::GetInfo(info) => Response::Info(Info {
version: info.jade_version.as_str().into(),
network: Some(info.jade_networks.to_string()),
networks: info.jade_networks.into(),
firmware: None,
}),
}
Expand Down
6 changes: 3 additions & 3 deletions bhwi/src/ledger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use store::{DelegatedStore, StoreError};
pub use wallet::{WalletPolicy, WalletPubKey};

use crate::Interpreter;
use crate::common::{Command, Error, Response, Version};
use crate::common::{Command, Error, Info, Response};
use crate::device::DeviceId;

pub const LEDGER_DEVICE_ID: DeviceId = DeviceId::new(0x2c97)
Expand Down Expand Up @@ -310,9 +310,9 @@ impl TryFrom<Command> for LedgerCommand {
impl From<LedgerResponse> for Response {
fn from(res: LedgerResponse) -> Response {
match res {
LedgerResponse::AppInfo(res) => Response::Version(Version {
LedgerResponse::AppInfo(res) => Response::Info(Info {
version: res.version.to_string(),
network: Some(res.network().to_string()),
networks: vec![res.network()],
firmware: None,
}),
LedgerResponse::Signature(header, signature) => Response::Signature(header, signature),
Expand Down
8 changes: 4 additions & 4 deletions e2e/coldcard/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ mod tests {
}

#[tokio::test]
async fn can_get_version() {
async fn can_get_info() {
let (mut dev, _) = device().await;
let version = dev.get_version().await.unwrap();
assert_eq!(version.firmware, Some("mk4".to_string()));
assert_eq!(version.version.to_string(), "5.x.x");
let info = dev.get_info().await.unwrap();
assert_eq!(info.firmware, Some("mk4".to_string()));
assert_eq!(info.version.to_string(), "5.x.x");
}
}
18 changes: 14 additions & 4 deletions e2e/jade/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,21 @@ mod tests {
}

#[tokio::test]
async fn can_get_version() {
async fn can_get_info() {
let mut dev = device().await;
let version = dev.get_version().await.unwrap();
let info = dev.get_info().await.unwrap();
// 1.0.39-beta2-11-g1ca0a0a4-dirty
assert!(version.version.to_string().contains("1.0.39"));
assert_eq!(version.network.unwrap(), "all");
assert!(info.version.to_string().contains("1.0.39"));
// jade has "all" networks
assert_eq!(
info.networks,
vec![
Network::Bitcoin,
Network::Testnet,
Network::Testnet4,
Network::Regtest,
Network::Signet
]
);
}
}
8 changes: 4 additions & 4 deletions e2e/ledger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ mod tests {
#[tokio::test]
async fn can_get_version() {
let (mut dev, _) = init().await;
let version = dev.get_version().await.unwrap();
assert_eq!(version.version.to_string(), "2.4.5");
assert_eq!(version.firmware, Some("Bitcoin Test".to_string()));
assert_eq!(version.network.unwrap().to_string(), "testnet");
let info = dev.get_info().await.unwrap();
assert_eq!(info.version.to_string(), "2.4.5");
assert_eq!(info.firmware, Some("Bitcoin Test".to_string()));
assert_eq!(info.networks.first().unwrap().to_string(), "testnet");
}
}
Loading