hw-wallet 0.1.2

Shared wallet orchestration utilities for CLI and FFI
Documentation
use serde::Deserialize;
use trezor_connect::thp::{BtcOrigTx, BtcPaymentRequest, BtcRefTx, BtcSignTx};

use super::inputs::TxInputInput;
use super::orig_tx::TxInputOrigTx;
use super::outputs::TxInputOutput;
use super::owner::TxOwner;
use super::payment_request::TxInputPaymentRequest;
use super::ref_tx::TxInputRefTx;
use super::tx_links::TxLinks;
use crate::error::{WalletError, WalletResult};

#[derive(Debug, Deserialize)]
pub struct TxInput {
    #[serde(default = "default_version")]
    pub version: u32,
    #[serde(default)]
    pub lock_time: u32,
    #[serde(default)]
    pub chunkify: bool,
    pub inputs: Vec<TxInputInput>,
    pub outputs: Vec<TxInputOutput>,
    #[serde(default)]
    pub ref_txs: Vec<TxInputRefTx>,
    #[serde(default)]
    pub orig_txs: Vec<TxInputOrigTx>,
    #[serde(default)]
    pub payment_reqs: Vec<TxInputPaymentRequest>,
}

fn default_version() -> u32 {
    2
}

impl TxInput {
    pub fn from_json(json: &str) -> WalletResult<Self> {
        serde_json::from_str(json)
            .map_err(|err| WalletError::Signing(format!("invalid bitcoin tx JSON: {err}")))
    }
}

impl TryFrom<TxInput> for BtcSignTx {
    type Error = WalletError;

    fn try_from(tx: TxInput) -> WalletResult<Self> {
        if tx.inputs.is_empty() {
            return Err(WalletError::Signing(
                "bitcoin tx must contain at least one input".into(),
            ));
        }
        if tx.outputs.is_empty() {
            return Err(WalletError::Signing(
                "bitcoin tx must contain at least one output".into(),
            ));
        }

        let sign_tx = Self {
            version: tx.version,
            lock_time: tx.lock_time,
            inputs: tx
                .inputs
                .into_iter()
                .map(|input| input.into_sign_input(TxOwner::Signing))
                .collect::<WalletResult<_>>()?,
            outputs: tx
                .outputs
                .into_iter()
                .map(|output| output.into_sign_output(TxOwner::Signing))
                .collect::<WalletResult<_>>()?,
            ref_txs: tx
                .ref_txs
                .into_iter()
                .map(BtcRefTx::try_from)
                .collect::<WalletResult<_>>()?,
            orig_txs: tx
                .orig_txs
                .into_iter()
                .map(BtcOrigTx::try_from)
                .collect::<WalletResult<_>>()?,
            payment_reqs: tx
                .payment_reqs
                .into_iter()
                .map(BtcPaymentRequest::try_from)
                .collect::<WalletResult<_>>()?,
            chunkify: tx.chunkify,
        };
        sign_tx.validate_ref_txs()?;
        sign_tx.validate_orig_tx_links()?;
        Ok(sign_tx)
    }
}