tx_bakery/utils/
key_wallet.rs1use std::io::Cursor;
4use std::path::Path;
5
6use anyhow::anyhow;
7use data_encoding::HEXLOWER;
8use futures::future::OptionFuture;
9use plutus_ledger_api::csl::{csl_to_pla::ToPLA, lib as csl};
10use plutus_ledger_api::v3::{
11 address::{Address, Credential, StakingCredential},
12 crypto::Ed25519PubKeyHash,
13};
14use thiserror::Error;
15use tokio;
16use tokio::fs;
17
18use crate::wallet::{Wallet, WalletError};
19
20#[derive(Error, Debug)]
21pub enum KeyWalletError {
22 #[error("Failed to read private key: {0}")]
23 PrivateKeyReadError(std::io::Error),
24
25 #[error("Failed to parse private key: {0}")]
26 PrivateKeyParseError(anyhow::Error),
27}
28
29impl From<KeyWalletError> for WalletError {
30 fn from(err: KeyWalletError) -> WalletError {
31 WalletError(anyhow!(err))
32 }
33}
34
35#[derive(Debug, serde::Deserialize)]
36struct TextEnvelope {
37 #[serde(rename(deserialize = "cborHex"))]
42 cbor_hex: String,
43}
44
45pub struct KeyWallet {
47 pay_priv_key: csl::PrivateKey,
48 pay_pkh: Ed25519PubKeyHash,
49 address: Address,
53}
54
55impl KeyWallet {
56 pub async fn new(
58 payment_skey: impl AsRef<Path>,
59 staking_skey: Option<impl AsRef<Path>>,
60 ) -> Result<KeyWallet, KeyWalletError> {
61 let pay_priv_key = Self::read_priv_key(payment_skey).await?;
62 let pay_pkh: Ed25519PubKeyHash = pay_priv_key.to_public().hash().to_pla();
63
64 let stk_priv_key = OptionFuture::from(staking_skey.map(Self::read_priv_key))
65 .await
66 .transpose()?;
67 let stk_pkh = stk_priv_key
68 .as_ref()
69 .map(|priv_key| priv_key.to_public().hash().to_pla());
70
71 let address = Address {
72 credential: Credential::PubKey(pay_pkh.clone()),
73 staking_credential: stk_pkh
74 .clone()
75 .map(|pkh| StakingCredential::Hash(Credential::PubKey(pkh))),
76 };
77
78 Ok(KeyWallet {
79 pay_priv_key,
80 pay_pkh,
81 address,
84 })
85 }
86
87 pub async fn new_enterprise(
89 payment_skey: impl AsRef<Path>,
90 ) -> Result<KeyWallet, KeyWalletError> {
91 Self::new(payment_skey, None::<&str>).await
92 }
93
94 async fn read_priv_key(filepath: impl AsRef<Path>) -> Result<csl::PrivateKey, KeyWalletError> {
96 let skey_str = fs::read_to_string(&filepath)
97 .await
98 .map_err(KeyWalletError::PrivateKeyReadError)?;
99
100 let text_envelope: TextEnvelope = serde_json::from_str(&skey_str)
101 .map_err(|err| KeyWalletError::PrivateKeyParseError(anyhow!(err)))?;
102
103 let mut raw = cbor_event::de::Deserializer::from(Cursor::new(
104 HEXLOWER
105 .decode(&text_envelope.cbor_hex.clone().into_bytes())
106 .unwrap(),
107 ));
108 let bytes: Vec<u8> = raw.bytes().unwrap();
109
110 csl::PrivateKey::from_normal_bytes(&bytes)
111 .map_err(|err| KeyWalletError::PrivateKeyParseError(anyhow!(err)))
112 }
113}
114
115impl Wallet for KeyWallet {
116 fn sign_transaction(&self, tx: &csl::FixedTransaction) -> csl::FixedTransaction {
117 let tx_hash = tx.transaction_hash();
118
119 let witness = &csl::make_vkey_witness(&tx_hash, &self.pay_priv_key);
120
121 let mut tx = tx.clone();
122 tx.add_vkey_witness(witness);
123
124 tx
125 }
126
127 fn get_change_pkh(&self) -> Ed25519PubKeyHash {
128 self.pay_pkh.clone()
129 }
130
131 fn get_change_addr(&self) -> Address {
132 self.address.clone()
133 }
134}