bitbox_api/
cardano.rs

1use crate::runtime::Runtime;
2
3use crate::error::Error;
4use crate::pb::{self, request::Request, response::Response};
5use crate::Keypath;
6use crate::PairedBitBox;
7
8#[cfg(feature = "wasm")]
9pub(crate) fn serde_deserialize_network<'de, D>(deserializer: D) -> Result<i32, D::Error>
10where
11    D: serde::Deserializer<'de>,
12{
13    use serde::Deserialize;
14    let network = pb::CardanoNetwork::deserialize(deserializer)?;
15    Ok(network as i32)
16}
17
18#[cfg(feature = "wasm")]
19pub(crate) fn serde_deserialize_drep_type<'de, D>(deserializer: D) -> Result<i32, D::Error>
20where
21    D: serde::Deserializer<'de>,
22{
23    use serde::Deserialize;
24    let drep_type = pb::cardano_sign_transaction_request::certificate::vote_delegation::CardanoDRepType::deserialize(deserializer)?;
25    Ok(drep_type as i32)
26}
27
28#[cfg(feature = "wasm")]
29#[derive(serde::Deserialize)]
30pub(crate) struct SerdeScriptConfig(pb::cardano_script_config::Config);
31
32#[cfg(feature = "wasm")]
33impl From<SerdeScriptConfig> for pb::CardanoScriptConfig {
34    fn from(value: SerdeScriptConfig) -> Self {
35        pb::CardanoScriptConfig {
36            config: Some(value.0),
37        }
38    }
39}
40
41#[cfg(feature = "wasm")]
42#[derive(serde::Deserialize)]
43pub(crate) struct SerdeCert(pb::cardano_sign_transaction_request::certificate::Cert);
44
45#[cfg(feature = "wasm")]
46impl From<SerdeCert> for pb::cardano_sign_transaction_request::Certificate {
47    fn from(value: SerdeCert) -> Self {
48        pb::cardano_sign_transaction_request::Certificate {
49            cert: Some(value.0),
50        }
51    }
52}
53
54/// Create a Shelley PaymentKeyHash/StakeKeyHash config.
55/// <https://github.com/cardano-foundation/CIPs/blob/6c249ef48f8f5b32efc0ec768fadf4321f3173f2/CIP-0019/CIP-0019.md#shelley-addresses>
56pub fn make_script_config_pkh_skh(
57    keypath_payment: &Keypath,
58    keypath_stake: &Keypath,
59) -> pb::CardanoScriptConfig {
60    pb::CardanoScriptConfig {
61        config: Some(pb::cardano_script_config::Config::PkhSkh(
62            pb::cardano_script_config::PkhSkh {
63                keypath_payment: keypath_payment.to_vec(),
64                keypath_stake: keypath_stake.to_vec(),
65            },
66        )),
67    }
68}
69
70impl<R: Runtime> PairedBitBox<R> {
71    async fn query_proto_cardano(
72        &self,
73        request: pb::cardano_request::Request,
74    ) -> Result<pb::cardano_response::Response, Error> {
75        self.validate_version(">=9.8.0")?; // Cardano since 9.8.0
76
77        match self
78            .query_proto(Request::Cardano(pb::CardanoRequest {
79                request: Some(request),
80            }))
81            .await?
82        {
83            Response::Cardano(pb::CardanoResponse {
84                response: Some(response),
85            }) => Ok(response),
86            _ => Err(Error::UnexpectedResponse),
87        }
88    }
89
90    /// Does this device support Cardano functionality? Currently this means BitBox02 Multi.
91    pub fn cardano_supported(&self) -> bool {
92        self.is_multi_edition()
93    }
94
95    /// Query the device for xpubs. The result contains one xpub per requested keypath. Each xpub is
96    /// 64 bytes: 32 byte chain code + 32 byte pubkey.
97    pub async fn cardano_xpubs(&self, keypaths: &[Keypath]) -> Result<Vec<Vec<u8>>, Error> {
98        match self
99            .query_proto_cardano(pb::cardano_request::Request::Xpubs(
100                pb::CardanoXpubsRequest {
101                    keypaths: keypaths.iter().map(|kp| kp.into()).collect(),
102                },
103            ))
104            .await?
105        {
106            pb::cardano_response::Response::Xpubs(pb::CardanoXpubsResponse { xpubs }) => Ok(xpubs),
107            _ => Err(Error::UnexpectedResponse),
108        }
109    }
110
111    /// Query the device for a Cardano address.
112    pub async fn cardano_address(
113        &self,
114        network: pb::CardanoNetwork,
115        script_config: &pb::CardanoScriptConfig,
116        display: bool,
117    ) -> Result<String, Error> {
118        match self
119            .query_proto_cardano(pb::cardano_request::Request::Address(
120                pb::CardanoAddressRequest {
121                    network: network.into(),
122                    display,
123                    script_config: Some(script_config.clone()),
124                },
125            ))
126            .await?
127        {
128            pb::cardano_response::Response::Pub(pb::PubResponse { r#pub: address }) => Ok(address),
129            _ => Err(Error::UnexpectedResponse),
130        }
131    }
132
133    /// Sign a Cardano transaction.
134    pub async fn cardano_sign_transaction(
135        &self,
136        transaction: pb::CardanoSignTransactionRequest,
137    ) -> Result<pb::CardanoSignTransactionResponse, Error> {
138        if transaction.tag_cbor_sets {
139            self.validate_version(">=9.22.0")?;
140        }
141        match self
142            .query_proto_cardano(pb::cardano_request::Request::SignTransaction(transaction))
143            .await?
144        {
145            pb::cardano_response::Response::SignTransaction(response) => Ok(response),
146            _ => Err(Error::UnexpectedResponse),
147        }
148    }
149}