bitbox_api/
cardano.rs

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