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 = "os_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::config::cfg::ChainConfig;
16use crate::modules::auth::model::{Account, Address};
17use crate::modules::tx::model::RawTx;
18
19#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
20pub struct SigningKey {
21 pub name: String,
23 pub key: Key,
25 pub derivation_path: String,
28}
29
30impl SigningKey {
31 pub async fn public_key(&self) -> Result<PublicKey, ChainError> {
32 match &self.key {
33 Key::Raw(bytes) => {
34 let key = raw_bytes_to_signing_key(bytes)?;
35 Ok(key.public_key())
36 }
37
38 Key::Mnemonic(phrase) => {
39 let key = mnemonic_to_signing_key(phrase, &self.derivation_path)?;
40 Ok(key.public_key())
41 }
42
43 #[cfg(feature = "os_keyring")]
44 Key::Keyring(params) => {
45 let entry = Entry::new(¶ms.service, ¶ms.key_name);
46 let key = mnemonic_to_signing_key(&entry.get_password()?, &self.derivation_path)?;
47 Ok(key.public_key())
48 }
49 }
50 }
51
52 pub async fn sign(
53 &self,
54 msgs: Vec<impl Msg + Serialize>,
55 timeout_height: u64,
56 memo: &str,
57 account: Account,
58 fee: Fee,
59 cfg: &ChainConfig,
60 ) -> Result<RawTx, ChainError> {
61 let public_key = if account.pubkey.is_none() {
62 Some(self.public_key().await?)
63 } else {
64 account.pubkey
65 };
66
67 match &self.key {
68 Key::Raw(bytes) => {
69 let sign_doc =
70 build_sign_doc(msgs, timeout_height, memo, &account, fee, public_key, cfg)?;
71
72 let key = raw_bytes_to_signing_key(bytes)?;
73
74 let raw = sign_doc.sign(&key).map_err(ChainError::crypto)?;
75 Ok(raw.into())
76 }
77
78 Key::Mnemonic(phrase) => {
79 let sign_doc =
80 build_sign_doc(msgs, timeout_height, memo, &account, fee, public_key, cfg)?;
81
82 let key = mnemonic_to_signing_key(phrase, &self.derivation_path)?;
83
84 let raw = sign_doc.sign(&key).map_err(ChainError::crypto)?;
85 Ok(raw.into())
86 }
87
88 #[cfg(feature = "os_keyring")]
89 Key::Keyring(params) => {
90 let sign_doc =
91 build_sign_doc(msgs, timeout_height, memo, &account, fee, public_key, cfg)?;
92
93 let entry = Entry::new(¶ms.service, ¶ms.key_name);
94 let key = mnemonic_to_signing_key(&entry.get_password()?, &self.derivation_path)?;
95
96 let raw = sign_doc.sign(&key).map_err(ChainError::crypto)?;
97 Ok(raw.into())
98 }
99 }
100 }
101
102 pub async fn to_addr(&self, prefix: &str) -> Result<Address, ChainError> {
103 let account = self
104 .public_key()
105 .await?
106 .account_id(prefix)
107 .map_err(ChainError::crypto)?;
108 Ok(account.into())
109 }
110
111 pub fn random_mnemonic(key_name: String, derivation_path: String) -> SigningKey {
112 let mnemonic = bip32::Mnemonic::random(OsRng, Default::default());
113
114 SigningKey {
115 name: key_name,
116 key: Key::Mnemonic(mnemonic.phrase().to_string()),
117 derivation_path,
118 }
119 }
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
123#[non_exhaustive]
124pub enum Key {
125 Raw(Vec<u8>),
127
128 Mnemonic(String),
132
133 #[cfg(feature = "os_keyring")]
137 Keyring(KeyringParams),
138 }
140
141#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
142pub struct KeyringParams {
143 pub service: String,
144 pub key_name: String,
145}
146
147fn mnemonic_to_signing_key(
148 mnemonic: &str,
149 derivation_path: &str,
150) -> Result<secp256k1::SigningKey, ChainError> {
151 let seed = bip32::Mnemonic::new(mnemonic, bip32::Language::English)
152 .map_err(|_| ChainError::Mnemonic)?
153 .to_seed("");
154
155 secp256k1::SigningKey::derive_from_path(
156 seed,
157 &derivation_path
158 .parse()
159 .map_err(|_| ChainError::DerviationPath)?,
160 )
161 .map_err(|_| ChainError::DerviationPath)
162}
163
164fn raw_bytes_to_signing_key(bytes: &[u8]) -> Result<secp256k1::SigningKey, ChainError> {
165 secp256k1::SigningKey::from_bytes(bytes).map_err(ChainError::crypto)
166}
167
168fn build_sign_doc(
169 msgs: Vec<impl Msg>,
170 timeout_height: u64,
171 memo: &str,
172 account: &Account,
173 fee: Fee,
174 public_key: Option<PublicKey>,
175 cfg: &ChainConfig,
176) -> Result<SignDoc, ChainError> {
177 let timeout: Height = timeout_height.try_into()?;
178
179 let tx = Body::new(
180 msgs.into_iter()
181 .map(|m| m.into_any())
182 .collect::<Result<Vec<_>, _>>()
183 .map_err(|e| ChainError::ProtoEncoding {
184 message: e.to_string(),
185 })?,
186 memo,
187 timeout,
188 );
189
190 let auth_info =
192 SignerInfo::single_direct(public_key, account.sequence).auth_info(fee.try_into()?);
193
194 SignDoc::new(
195 &tx,
196 &auth_info,
197 &cfg.chain_id.parse().map_err(|_| ChainError::ChainId {
198 chain_id: cfg.chain_id.to_string(),
199 })?,
200 account.account_number,
201 )
202 .map_err(ChainError::proto_encoding)
203}