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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! Simple wallet reading the signing key(s) from disk

use super::csl_to_pla::ToPLA;
use crate::wallet::{Wallet, WalletError};
use anyhow::anyhow;
use cardano_serialization_lib as csl;
use data_encoding::HEXLOWER;
use futures::future::OptionFuture;
use plutus_ledger_api::v2::address::{Address, Credential, StakingCredential};
use plutus_ledger_api::v2::crypto::Ed25519PubKeyHash;
use std::io::Cursor;
use std::path::Path;
use thiserror::Error;
use tokio;
use tokio::fs;

#[derive(Error, Debug)]
pub enum KeyWalletError {
    #[error("Failed to read private key: {0}")]
    PrivateKeyReadError(std::io::Error),

    #[error("Failed to parse private key: {0}")]
    PrivateKeyParseError(anyhow::Error),
}

impl From<KeyWalletError> for WalletError {
    fn from(err: KeyWalletError) -> WalletError {
        WalletError(anyhow!(err))
    }
}

#[derive(Debug, serde::Deserialize)]
struct TextEnvelope {
    // TODO: Verify that the TextEnvelope is correct (PaymentSigningKeyShelley_ed25519 or StakeSigningKeyShelley_ed25519)
    // #[serde(rename(deserialize = "type"))]
    // data_type: String,
    // description: String,
    #[serde(rename(deserialize = "cborHex"))]
    cbor_hex: String,
}

/// Simple wallet reading the signing key(s) from disk
pub struct KeyWallet {
    pay_priv_key: csl::PrivateKey,
    pay_pkh: Ed25519PubKeyHash,
    // TODO: Use these to implement staking features
    // stk_priv_key: Option<PrivateKey>,
    // stk_pkh: Option<Ed25519PubKeyHash>,
    address: Address,
}

impl KeyWallet {
    /// Initialise a base wallet by reading the signinig keys into memory
    pub async fn new(
        payment_skey: impl AsRef<Path>,
        staking_skey: Option<impl AsRef<Path>>,
    ) -> Result<KeyWallet, KeyWalletError> {
        let pay_priv_key = Self::read_priv_key(payment_skey).await?;
        let pay_pkh: Ed25519PubKeyHash = pay_priv_key.to_public().hash().to_pla();

        let stk_priv_key = OptionFuture::from(staking_skey.map(Self::read_priv_key))
            .await
            .transpose()?;
        let stk_pkh = stk_priv_key
            .as_ref()
            .map(|priv_key| priv_key.to_public().hash().to_pla());

        let address = Address {
            credential: Credential::PubKey(pay_pkh.clone()),
            staking_credential: stk_pkh
                .clone()
                .map(|pkh| StakingCredential::Hash(Credential::PubKey(pkh))),
        };

        Ok(KeyWallet {
            pay_priv_key,
            pay_pkh,
            // stk_priv_key,
            // stk_pkh,
            address,
        })
    }

    /// Initialise an enterprise wallet by reading the signinig key into memory
    pub async fn new_enterprise(
        payment_skey: impl AsRef<Path>,
    ) -> Result<KeyWallet, KeyWalletError> {
        Self::new(payment_skey, None::<&str>).await
    }

    /// Get the private key
    async fn read_priv_key(filepath: impl AsRef<Path>) -> Result<csl::PrivateKey, KeyWalletError> {
        let skey_str = fs::read_to_string(&filepath)
            .await
            .map_err(KeyWalletError::PrivateKeyReadError)?;

        let text_envelope: TextEnvelope = serde_json::from_str(&skey_str)
            .map_err(|err| KeyWalletError::PrivateKeyParseError(anyhow!(err)))?;

        let mut raw = cbor_event::de::Deserializer::from(Cursor::new(
            HEXLOWER
                .decode(&text_envelope.cbor_hex.clone().into_bytes())
                .unwrap(),
        ));
        let bytes: Vec<u8> = raw.bytes().unwrap();

        csl::PrivateKey::from_normal_bytes(&bytes)
            .map_err(|err| KeyWalletError::PrivateKeyParseError(anyhow!(err)))
    }
}

impl Wallet for KeyWallet {
    fn sign_transaction(&self, tx: &csl::Transaction) -> csl::Transaction {
        let tx_body = tx.body();
        let mut witness_set = tx.witness_set();
        let aux_data = tx.auxiliary_data();

        let mut vkey_witnesses = witness_set.vkeys().unwrap_or(csl::Vkeywitnesses::new());
        vkey_witnesses.add(&csl::make_vkey_witness(
            &csl::hash_transaction(&tx_body),
            &self.pay_priv_key,
        ));

        witness_set.set_vkeys(&vkey_witnesses);

        csl::Transaction::new(&tx_body, &witness_set, aux_data)
    }

    fn get_change_pkh(&self) -> Ed25519PubKeyHash {
        self.pay_pkh.clone()
    }

    fn get_change_addr(&self) -> Address {
        self.address.clone()
    }
}