layer_climb_signer/
key.rs1use super::signer::TxSigner;
2use anyhow::{anyhow, Context, Result};
3use async_trait::async_trait;
4use bip32::DerivationPath;
5use bip39::Mnemonic;
6use signature::Signer;
7use std::{str::FromStr, sync::LazyLock};
8
9pub type PublicKey = tendermint::PublicKey;
10
11pub static COSMOS_HUB_PATH: LazyLock<DerivationPath> =
13 LazyLock::new(|| DerivationPath::from_str("m/44'/118'/0'/0/0").unwrap());
14
15pub fn cosmos_hub_derivation(index: u32) -> Result<DerivationPath> {
16 DerivationPath::from_str(&format!("m/44'/118'/0'/0/{index}")).map_err(|err| anyhow!("{}", err))
17}
18
19pub struct KeySigner {
20 pub key: bip32::XPrv,
21}
22
23impl KeySigner {
24 pub fn new_mnemonic_iter<I, S>(mnemonic: I, derivation: Option<&DerivationPath>) -> Result<Self>
25 where
26 I: IntoIterator<Item = S>,
27 S: AsRef<str>,
28 {
29 let mut joined_str = String::new();
30 for word in mnemonic {
31 joined_str.push_str(word.as_ref());
32 joined_str.push(' ');
33 }
34
35 Self::new_mnemonic_str(&joined_str, derivation)
36 }
37
38 pub fn new_mnemonic_str(mnemonic: &str, derivation: Option<&DerivationPath>) -> Result<Self> {
39 let derivation = derivation.unwrap_or(&COSMOS_HUB_PATH);
40 let mnemonic: Mnemonic = mnemonic.parse()?;
41 let seed = mnemonic.to_seed("");
42 let key =
43 bip32::XPrv::derive_from_path(seed, derivation).map_err(|err| anyhow!("{}", err))?;
44
45 Ok(Self { key })
46 }
47}
48
49cfg_if::cfg_if! {
50 if #[cfg(target_arch = "wasm32")] {
51 #[async_trait(?Send)]
52 impl TxSigner for KeySigner {
53 async fn sign(&self, msg: &layer_climb_proto::tx::SignDoc) -> Result<Vec<u8>> {
54 sign(self, msg).await
55 }
56
57 async fn public_key(&self) -> Result<PublicKey> {
58 public_key(self).await
59 }
60 }
61
62 } else {
63 #[async_trait]
64 impl TxSigner for KeySigner {
65 async fn sign(&self, msg: &layer_climb_proto::tx::SignDoc) -> Result<Vec<u8>> {
66 sign(self, msg).await
67 }
68
69 async fn public_key(&self) -> Result<PublicKey> {
70 public_key(self).await
71 }
72 }
73 }
74}
75
76async fn sign(signer: &KeySigner, msg: &layer_climb_proto::tx::SignDoc) -> Result<Vec<u8>> {
77 let signed: k256::ecdsa::Signature = signer
78 .key
79 .private_key()
80 .try_sign(&layer_climb_proto::proto_into_bytes(msg)?)
81 .map_err(|err| anyhow!("{}", err))?;
82 Ok(signed.to_vec())
83}
84
85async fn public_key(signer: &KeySigner) -> Result<PublicKey> {
86 let public_key = signer.key.public_key();
87 let public_key_bytes = public_key.to_bytes();
88 PublicKey::from_raw_secp256k1(&public_key_bytes).context("Invalid secp256k1 public key")
89}