trezor_client/
utils.rs

1use crate::error::{Error, Result};
2use bitcoin::{
3    address,
4    address::Payload,
5    bip32,
6    blockdata::script::Script,
7    hashes::{sha256d, Hash},
8    psbt,
9    secp256k1::ecdsa::{RecoverableSignature, RecoveryId},
10    Address, Network,
11};
12
13/// Retrieve an address from the given script.
14pub fn address_from_script(script: &Script, network: Network) -> Option<address::Address> {
15    let payload = Payload::from_script(script).ok()?;
16    Some(Address::new(network, payload))
17}
18
19/// Find the (first if multiple) PSBT input that refers to the given txid.
20pub fn psbt_find_input(psbt: &psbt::Psbt, txid: sha256d::Hash) -> Result<&psbt::Input> {
21    let inputs = &psbt.unsigned_tx.input;
22    let idx = inputs
23        .iter()
24        .position(|tx| *tx.previous_output.txid.as_raw_hash() == txid)
25        .ok_or(Error::TxRequestUnknownTxid(txid))?;
26    psbt.inputs.get(idx).ok_or(Error::TxRequestInvalidIndex(idx))
27}
28
29/// Get a hash from a reverse byte representation.
30pub fn from_rev_bytes(rev_bytes: &[u8]) -> Option<sha256d::Hash> {
31    let mut bytes = rev_bytes.to_vec();
32    bytes.reverse();
33    sha256d::Hash::from_slice(&bytes).ok()
34}
35
36/// Get the reverse byte representation of a hash.
37pub fn to_rev_bytes(hash: &sha256d::Hash) -> [u8; 32] {
38    let mut bytes = hash.to_byte_array();
39    bytes.reverse();
40    bytes
41}
42
43/// Parse a Bitcoin Core-style 65-byte recoverable signature.
44pub fn parse_recoverable_signature(
45    sig: &[u8],
46) -> Result<RecoverableSignature, bitcoin::secp256k1::Error> {
47    if sig.len() != 65 {
48        return Err(bitcoin::secp256k1::Error::InvalidSignature)
49    }
50
51    // Bitcoin Core sets the first byte to `27 + rec + (fCompressed ? 4 : 0)`.
52    let rec_id = RecoveryId::from_i32(if sig[0] >= 31 {
53        (sig[0] - 31) as i32
54    } else {
55        (sig[0] - 27) as i32
56    })?;
57
58    RecoverableSignature::from_compact(&sig[1..], rec_id)
59}
60
61/// Convert a bitcoin network constant to the Trezor-compatible coin_name string.
62pub fn coin_name(network: Network) -> Result<String> {
63    match network {
64        Network::Bitcoin => Ok("Bitcoin".to_owned()),
65        Network::Testnet => Ok("Testnet".to_owned()),
66        _ => Err(Error::UnsupportedNetwork),
67    }
68}
69
70/// Convert a BIP-32 derivation path into a `Vec<u32>`.
71pub fn convert_path(path: &bip32::DerivationPath) -> Vec<u32> {
72    path.into_iter().map(|i| u32::from(*i)).collect()
73}