1use cosmrs::bip32;
2use cosmrs::bip32::secp256k1::elliptic_curve::rand_core::OsRng;
3use cosmrs::crypto::{secp256k1, PublicKey};
4use cosmrs::tendermint::block::Height;
5use cosmrs::tx::{Body, SignDoc, SignerInfo};
6
7#[cfg(feature = "keyring")]
8use keyring::Entry;
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11
12use crate::chain::error::ChainError;
13use crate::chain::fee::Fee;
14use crate::chain::msg::Msg;
15use crate::chain::tx::RawTx;
16use crate::modules::auth::model::{Account, Address};
17
18#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
19pub struct SigningKey {
20 pub name: String,
22 pub key: Key,
24}
25
26impl SigningKey {
27 pub async fn public_key(&self, derivation_path: &str) -> Result<PublicKey, ChainError> {
28 match &self.key {
29 Key::Raw(bytes) => {
30 let key = raw_bytes_to_signing_key(bytes)?;
31 Ok(key.public_key())
32 }
33
34 Key::Mnemonic(phrase) => {
35 let key = mnemonic_to_signing_key(phrase, derivation_path)?;
36 Ok(key.public_key())
37 }
38
39 #[cfg(feature = "keyring")]
40 Key::Keyring(params) => {
41 let entry = Entry::new(¶ms.service, ¶ms.key_name)?;
42 let key = mnemonic_to_signing_key(&entry.get_password()?, derivation_path)?;
43 Ok(key.public_key())
44 }
45 }
46 }
47
48 #[allow(clippy::too_many_arguments)]
49 pub async fn sign(
50 &self,
51 msgs: Vec<impl Msg + Serialize>,
52 timeout_height: u64,
53 memo: &str,
54 account: Account,
55 fee: Fee,
56 chain_id: &str,
57 derivation_path: &str,
58 ) -> Result<RawTx, ChainError> {
59 let public_key = if account.pubkey.is_none() {
60 Some(self.public_key(derivation_path).await?)
61 } else {
62 account.pubkey
63 };
64
65 match &self.key {
66 Key::Raw(bytes) => {
67 let sign_doc = build_sign_doc(
68 msgs,
69 timeout_height,
70 memo,
71 &account,
72 fee,
73 public_key,
74 chain_id,
75 )?;
76
77 let key = raw_bytes_to_signing_key(bytes)?;
78
79 let raw = sign_doc.sign(&key).map_err(ChainError::crypto)?;
80 Ok(raw.into())
81 }
82
83 Key::Mnemonic(phrase) => {
84 let sign_doc = build_sign_doc(
85 msgs,
86 timeout_height,
87 memo,
88 &account,
89 fee,
90 public_key,
91 chain_id,
92 )?;
93
94 let key = mnemonic_to_signing_key(phrase, derivation_path)?;
95
96 let raw = sign_doc.sign(&key).map_err(ChainError::crypto)?;
97 Ok(raw.into())
98 }
99
100 #[cfg(feature = "keyring")]
101 Key::Keyring(params) => {
102 let sign_doc = build_sign_doc(
103 msgs,
104 timeout_height,
105 memo,
106 &account,
107 fee,
108 public_key,
109 chain_id,
110 )?;
111
112 let entry = Entry::new(¶ms.service, ¶ms.key_name)?;
113 let key = mnemonic_to_signing_key(&entry.get_password()?, derivation_path)?;
114
115 let raw = sign_doc.sign(&key).map_err(ChainError::crypto)?;
116 Ok(raw.into())
117 }
118 }
119 }
120
121 pub async fn to_addr(
122 &self,
123 prefix: &str,
124 derivation_path: &str,
125 ) -> Result<Address, ChainError> {
126 let account = self
127 .public_key(derivation_path)
128 .await?
129 .account_id(prefix)
130 .map_err(ChainError::crypto)?;
131 Ok(account.into())
132 }
133
134 pub fn random_mnemonic(key_name: String) -> SigningKey {
135 let mnemonic = bip32::Mnemonic::random(OsRng, Default::default());
136
137 SigningKey {
138 name: key_name,
139 key: Key::Mnemonic(mnemonic.phrase().to_string()),
140 }
141 }
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
145#[non_exhaustive]
146pub enum Key {
147 Raw(Vec<u8>),
149
150 Mnemonic(String),
154
155 #[cfg(feature = "keyring")]
159 Keyring(KeyringParams),
160 }
162
163#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
164pub struct KeyringParams {
165 pub service: String,
166 pub key_name: String,
167}
168
169fn mnemonic_to_signing_key(
170 mnemonic: &str,
171 derivation_path: &str,
172) -> Result<secp256k1::SigningKey, ChainError> {
173 let seed = bip32::Mnemonic::new(mnemonic, bip32::Language::English)
174 .map_err(|_| ChainError::Mnemonic)?
175 .to_seed("");
176
177 secp256k1::SigningKey::derive_from_path(
178 seed,
179 &derivation_path
180 .parse()
181 .map_err(|_| ChainError::DerviationPath)?,
182 )
183 .map_err(|_| ChainError::DerviationPath)
184}
185
186fn raw_bytes_to_signing_key(bytes: &[u8]) -> Result<secp256k1::SigningKey, ChainError> {
187 secp256k1::SigningKey::from_slice(bytes).map_err(ChainError::crypto)
188}
189
190fn build_sign_doc(
191 msgs: Vec<impl Msg>,
192 timeout_height: u64,
193 memo: &str,
194 account: &Account,
195 fee: Fee,
196 public_key: Option<PublicKey>,
197 chain_id: &str,
198) -> Result<SignDoc, ChainError> {
199 let timeout: Height = timeout_height.try_into()?;
200
201 let tx = Body::new(
202 msgs.into_iter()
203 .map(|m| m.into_any())
204 .collect::<Result<Vec<_>, _>>()
205 .map_err(|e| ChainError::ProtoEncoding {
206 message: e.to_string(),
207 })?,
208 memo,
209 timeout,
210 );
211
212 let auth_info =
214 SignerInfo::single_direct(public_key, account.sequence).auth_info(fee.try_into()?);
215
216 SignDoc::new(
217 &tx,
218 &auth_info,
219 &chain_id.parse().map_err(|_| ChainError::ChainId {
220 chain_id: chain_id.to_string(),
221 })?,
222 account.account_number,
223 )
224 .map_err(ChainError::proto_encoding)
225}