Skip to content

Commit

Permalink
Merge pull request #73 from wizardsardine/jade
Browse files Browse the repository at this point in the history
Add jade
  • Loading branch information
edouardparis authored Apr 4, 2024
2 parents 6dbf410 + 205703c commit ccb1001
Show file tree
Hide file tree
Showing 12 changed files with 935 additions and 95 deletions.
25 changes: 16 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ authors.workspace = true
repository.workspace = true

[features]
default = ["ledger", "specter", "coldcard", "bitbox"]
default = ["ledger", "specter", "coldcard", "bitbox", "jade"]
bitbox = ["tokio", "hidapi", "bitbox-api", "regex"]
coldcard = ["dep:coldcard", "regex"]
specter = ["tokio", "tokio-serial", "serialport"]
jade = ["tokio", "tokio-serial", "serde", "serde_bytes", "serde_cbor", "serialport", "reqwest"]
ledger = ["regex", "tokio", "ledger_bitcoin_client", "ledger-transport-hidapi", "ledger-apdu", "hidapi"]
regex = ["dep:regex"]

Expand All @@ -31,27 +32,33 @@ async-trait = "0.1.52"
futures = "0.3"
bitcoin = { version = "0.31", default-features = false, features = ["base64", "serde", "std"] }

# specter
# specter & jade
tokio-serial = { version = "5.4.1", optional = true }
serialport = { version = "4.2", optional = true }
serialport = { version = "4.3", optional = true }

#bitbox
# jade
serde = { version = "1.0", features = ["derive"], optional = true }
serde_bytes = { version = "0.11.14", optional = true }
serde_cbor = { version = "0.11", optional = true }
reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] , optional = true}

# bitbox
bitbox-api = { version = "0.2.3", default-features = false, features = ["usb", "tokio", "multithreaded"], optional = true }

#coldcard
# coldcard
coldcard = { version = "0.12.1", optional = true }

# ledger
ledger_bitcoin_client = { version = "0.4.1", optional = true }
ledger-apdu = { version = "0.10", optional = true }
ledger-transport-hidapi = { version = "0.10.0", optional = true }

#bitbox & ledger
hidapi = { version = "2.4.1", features = ["linux-static-hidraw"], default-features = false, optional = true }
# bitbox & ledger
hidapi = { version = "2.5.1", features = ["linux-static-hidraw"], default-features = false, optional = true }
regex = { version = "1.6.0", optional = true }

# specter & ledger & bitbox
tokio = { version = "1.21.0", features = ["net", "time", "io-util", "sync"], optional = true }
# jade & specter & ledger & bitbox
tokio = { version = "1.21.0", features = ["net", "time", "io-util", "sync", "macros"], optional = true }

[dev-dependencies]
tokio = { version = "1.21", features = ["macros", "net", "rt", "rt-multi-thread", "io-util", "sync"] }
39 changes: 19 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ Current **Minimum Supported Rust Version**: v1.65.0
/// HWI is the common Hardware Wallet Interface.
#[async_trait]
pub trait HWI: Debug {
/// Return the device kind
/// 0. Return the device kind
fn device_kind(&self) -> DeviceKind;
/// Application version or OS version.
/// 1. Application version or OS version.
async fn get_version(&self) -> Result<Version, Error>;
/// Get master fingerprint.
/// 2. Get master fingerprint.
async fn get_master_fingerprint(&self) -> Result<Fingerprint, Error>;
/// Get the xpub with the given derivation path.
/// 3. Get the xpub with the given derivation path.
async fn get_extended_pubkey(&self, path: &DerivationPath) -> Result<Xpub, Error>;
/// Register a new wallet policy
/// 4. Register a new wallet policy
async fn register_wallet(&self, name: &str, policy: &str) -> Result<Option<[u8; 32]>, Error>;
/// Returns true if the wallet is registered
async fn is_wallet_registered(&self, name: &str, policy: &str) -> Result<bool, HWIError>;
/// Display an address on the device screen
/// 5. Returns true if the wallet is registered
async fn is_wallet_registered(&self, name: &str, policy: &str) -> Result<bool, HWIError>;
/// 6. Display an address on the device screen
async fn display_address(&self, script: &AddressScript) -> Result<(), Error>;
/// Sign a partially signed bitcoin transaction (PSBT).
/// 7. Sign a partially signed bitcoin transaction (PSBT).
async fn sign_tx(&self, tx: &mut Psbt) -> Result<(), Error>;
}

Expand All @@ -37,17 +37,16 @@ pub enum AddressScript {

A Empty case means the method is unimplemented on the client or device side.

| | BitBox02[^1] | Coldcard[^2] | Ledger Nano S/S+[^3] | Specter[^4] |
|----------------------- |--------------|------------- |----------------------|-------------|
| get_version | | >= 6.2.1X | >= v2.1.2 | |
| get_master_fingerprint | >= v9.15.0 | >= 6.2.1X | >= v2.1.2 | >= v1.8.0 |
| get_extended_pubkey | >= v9.15.0 | >= 6.2.1X | >= v2.1.2 | >= v1.8.0 |
| register_wallet | >= v9.15.0 | >= 6.2.1X | >= v2.1.2 | >= v1.8.0 |
| is_wallet_registered | >= v9.15.0 | >= 6.2.1X | *check hmac presence | |
| display_address | >= v9.15.0 | >= 6.2.1X | >= v2.1.2 | |
| sign_tx | >= v9.15.0 | >= 6.2.1X | >= v2.1.2 | >= v1.8.0 |
| device | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| -------------------- | ---------- | ---------- | ---------- | ---------- | -------------------- | ---------- | ---------- |
| BitBox02[^1] | | >= v9.15.0 | >= v9.15.0 | >= v9.15.0 | >= v9.15.0 | >= v9.15.0 | >= v9.15.0 |
| Coldcard[^2] | >= v6.2.1X | >= v6.2.1X | >= v6.2.1X | >= v6.2.1X | >= v6.2.1X | >= v6.2.1X | >= v6.2.1X |
| Jade[^3] | >= v1.0.30 | >= v1.0.30 | >= v1.0.30 | >= v1.0.30 | | >= v1.0.30 | >= v1.0.30 |
| Ledger Nano S/S+[^4] | >= v2.1.2 | >= v2.1.2 | >= v2.1.2 | >= v2.1.2 | *check hmac presence | >= v2.1.2 | >= v2.1.2 |
| Specter[^5] | | >= v1.8.0 | >= v1.8.0 | >= v1.8.0 | | | >= v1.8.0 |

[^1]: https://github.com/digitalbitbox/bitbox02-firmware
[^2]: https://github.com/alfred-hodler/rust-coldcard
[^3]: https://github.com/LedgerHQ/app-bitcoin-new
[^4]: https://github.com/cryptoadvance/specter-diy
[^3]: https://github.com/Blockstream/Jade
[^4]: https://github.com/LedgerHQ/app-bitcoin-new
[^5]: https://github.com/cryptoadvance/specter-diy
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ path = "src/bin/hwi.rs"
clap = { version = "4.4.7", features = ["derive"] }
bitcoin = "0.31"
hex = "0.4"
async-hwi = "0.0.14"
async-hwi = { path = ".." }
tokio = { version = "1", features = ["macros", "net", "rt", "rt-multi-thread", "io-util", "sync"] }
15 changes: 8 additions & 7 deletions cli/src/bin/hwi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ enum PsbtCommands {
#[arg(long)]
wallet_name: Option<String>,
#[arg(long)]
wallet_policy: String,
wallet_policy: Option<String>,
#[arg(long)]
hmac: Option<String>,
},
Expand Down Expand Up @@ -113,7 +113,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
args.network,
Some(command::Wallet {
name: wallet_name.as_ref(),
policy: &policy,
policy: Some(&policy),
hmac: hmac.as_ref(),
}),
)
Expand Down Expand Up @@ -148,7 +148,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
}
Commands::Device(DeviceCommands::List) => {
for device in command::list(args.network, None).await? {
eprint!("{}", device.get_master_fingerprint().await?,);
eprint!("{}", device.get_master_fingerprint().await?);
eprint!(" {}", device.device_kind());
if let Ok(version) = device.get_version().await.map(|v| v.to_string()) {
eprint!(" {}", version);
Expand Down Expand Up @@ -187,9 +187,10 @@ async fn main() -> Result<(), Box<dyn Error>> {
}
}
let (name, policy) = match device.device_kind() {
DeviceKind::Ledger | DeviceKind::LedgerSimulator | DeviceKind::Coldcard => {
(name.clone().expect("name is required"), policy.clone())
}
DeviceKind::Ledger
| DeviceKind::LedgerSimulator
| DeviceKind::Coldcard
| DeviceKind::Jade => (name.clone().expect("name is required"), policy.clone()),
_ => ("".into(), policy.clone()),
};
let res = device.is_wallet_registered(&name, &policy).await?;
Expand All @@ -206,7 +207,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
args.network,
Some(command::Wallet {
name: wallet_name.as_ref(),
policy: &wallet_policy,
policy: wallet_policy.as_ref(),
hmac: hmac.as_ref(),
}),
)
Expand Down
30 changes: 26 additions & 4 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod command {
use async_hwi::{
bitbox::{api::runtime, BitBox02, PairingBitbox02WithLocalCache},
coldcard,
jade::{self, Jade},
ledger::{HidApi, Ledger, LedgerSimulator, TransportHID},
specter::{Specter, SpecterSimulator},
HWI,
Expand All @@ -11,7 +12,7 @@ pub mod command {

pub struct Wallet<'a> {
pub name: Option<&'a String>,
pub policy: &'a String,
pub policy: Option<&'a String>,
pub hmac: Option<&'a String>,
}

Expand All @@ -31,6 +32,25 @@ pub mod command {
}
}

match Jade::enumerate().await {
Err(e) => println!("{:?}", e),
Ok(devices) => {
for device in devices {
let device = device.with_network(network);
if let Ok(info) = device.get_info().await {
if info.jade_state == jade::api::JadeState::Locked {
if let Err(e) = device.auth().await {
eprintln!("auth {:?}", e);
continue;
}
}

hws.push(device.into());
}
}
}
}

if let Ok(device) = LedgerSimulator::try_connect().await {
hws.push(device.into());
}
Expand All @@ -48,8 +68,8 @@ pub mod command {
{
if let Ok((device, _)) = device.wait_confirm().await {
let mut bb02 = BitBox02::from(device).with_network(network);
if let Some(ref wallet) = wallet {
bb02 = bb02.with_policy(wallet.policy)?;
if let Some(ref policy) = wallet.as_ref().map(|w| w.policy).flatten() {
bb02 = bb02.with_policy(policy)?;
}
hws.push(bb02.into());
}
Expand Down Expand Up @@ -92,7 +112,9 @@ pub mod command {
wallet
.name
.ok_or::<Box<dyn Error>>("ledger requires a wallet name".into())?,
wallet.policy,
wallet
.policy
.ok_or::<Box<dyn Error>>("ledger requires a wallet policy".into())?,
hmac,
)?;
}
Expand Down
2 changes: 1 addition & 1 deletion src/bitbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ mod tests {
use super::*;

#[test]
fn test_extract_keys_and_template() {
fn test_extract_script_config_policy() {
let policy = extract_script_config_policy("wsh(or_d(pk([f5acc2fd/49'/1'/0']tpubDCbK3Ysvk8HjcF6mPyrgMu3KgLiaaP19RjKpNezd8GrbAbNg6v5BtWLaCt8FNm6QkLseopKLf5MNYQFtochDTKHdfgG6iqJ8cqnLNAwtXuP/**),and_v(v:pkh(tpubDDtb2WPYwEWw2WWDV7reLV348iJHw2HmhzvPysKKrJw3hYmvrd4jasyoioVPdKGQqjyaBMEvTn1HvHWDSVqQ6amyyxRZ5YjpPBBGjJ8yu8S/**),older(100))))").unwrap();
assert_eq!(2, policy.pubkeys.len());
assert_eq!(
Expand Down
Loading

0 comments on commit ccb1001

Please sign in to comment.