1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//! [AccountHandle] for account-based operations

// Copyright (c) 2022-2023 The MobileCoin Foundation

use std::{sync::Arc, time::Duration};

use futures::executor::block_on;
use ledger_lib::Device;
use log::debug;
use tokio::sync::Mutex;

use ledger_mob_apdu::{
    key_image::{KeyImageReq, KeyImageResp},
    wallet_keys::{WalletKeyReq, WalletKeyResp},
};

use mc_core::account::ViewAccount;
use mc_crypto_ring_signature::KeyImage;
use mc_transaction_signer::traits::{KeyImageComputer, ViewAccountProvider};

use crate::Error;

/// Handle to a hardware wallet configured with an account index
///
/// See [DeviceHandle::account][super::DeviceHandle::account] to
/// create a [AccountHandle]
#[derive(Clone)]
pub struct AccountHandle<T: Device> {
    pub(crate) account_index: u32,
    pub(crate) user_timeout: Duration,
    pub(crate) t: Arc<Mutex<T>>,
}

impl<T: Device> AccountHandle<T> {}

impl<T: Device> KeyImageComputer for AccountHandle<T> {
    type Error = Error;

    fn compute_key_image(
        &self,
        subaddress_index: u64,
        tx_out_public_key: &mc_core::keys::TxOutPublic,
    ) -> Result<KeyImage, Self::Error> {
        let mut buff = [0u8; 256];

        let account_index = self.account_index;
        let timeout = self.user_timeout;
        let t = self.t.clone();

        tokio::task::block_in_place(|| {
            block_on(async {
                debug!(
                    "Resolving key image for account: {}, subaddress: {}, tx_public_key: {}",
                    account_index, subaddress_index, tx_out_public_key
                );

                let req =
                    KeyImageReq::new(account_index, subaddress_index, tx_out_public_key.clone());
                let resp = t
                    .lock()
                    .await
                    .request::<KeyImageResp>(req, &mut buff, timeout)
                    .await?;

                Ok(resp.key_image)
            })
        })
    }
}

impl<T: Device> ViewAccountProvider for AccountHandle<T> {
    type Error = Error;

    fn account(&self) -> Result<ViewAccount, Self::Error> {
        let mut buff = [0u8; 256];

        let account_index = self.account_index;
        let timeout = self.user_timeout;
        let t = self.t.clone();

        tokio::task::block_in_place(|| {
            block_on(async {
                debug!("Requesting root keys for account: {}", account_index);

                let req = WalletKeyReq::new(account_index);
                let resp = t
                    .lock()
                    .await
                    .request::<WalletKeyResp>(req, &mut buff, timeout)
                    .await?;

                Ok(ViewAccount::new(resp.view_private, resp.spend_public))
            })
        })
    }
}