cargo_tangle/command/
keys.rs

1use bip39::{Language, Mnemonic};
2use blueprint_crypto::bn254::{ArkBlsBn254Public, ArkBlsBn254Secret};
3use blueprint_crypto::k256::{K256Ecdsa, K256SigningKey};
4use blueprint_crypto::sp_core::{
5    SpBls377, SpBls377Pair, SpBls377Public, SpBls381, SpBls381Pair, SpBls381Public, SpEcdsa,
6    SpEcdsaPair, SpEcdsaPublic, SpEd25519, SpEd25519Pair, SpEd25519Public, SpSr25519,
7    SpSr25519Pair, SpSr25519Public,
8};
9use blueprint_crypto::{KeyTypeId, bn254::ArkBlsBn254};
10use blueprint_crypto_core::{BytesEncoding, KeyType};
11use blueprint_keystore::{Keystore, KeystoreConfig, backends::Backend};
12use blueprint_runner::config::Protocol;
13use blueprint_std::path::Path;
14use color_eyre::eyre::Result;
15use dialoguer::{Input, Select};
16
17#[derive(thiserror::Error, Debug)]
18pub enum Error {
19    #[error("Unknown key type: {0}")]
20    UnknownKeyType(String),
21    #[error("Keystore error: {0}")]
22    KeystoreError(#[from] blueprint_keystore::error::Error),
23    #[error("Invalid key format: {0}")]
24    InvalidKeyFormat(String),
25    #[error("Invalid mnemonic word count: {0}. Must be 12, 15, 18, 21, or 24")]
26    InvalidWordCount(u32),
27}
28
29#[allow(clippy::missing_errors_doc)]
30pub fn prompt_for_keys(key_types: Vec<KeyTypeId>) -> color_eyre::Result<Vec<(KeyTypeId, String)>> {
31    let mut collected_keys = Vec::new();
32    let all_key_types = [
33        KeyTypeId::Bn254,
34        KeyTypeId::Ecdsa,
35        KeyTypeId::Sr25519,
36        KeyTypeId::Ed25519,
37        KeyTypeId::Bls381,
38        KeyTypeId::Bls377,
39    ];
40
41    if key_types.is_empty() {
42        loop {
43            let mut options = all_key_types
44                .iter()
45                .map(|kt| format!("Enter key for {:?}", kt))
46                .collect::<Vec<_>>();
47            options.push("Continue".to_string());
48
49            let selection = Select::new()
50                .with_prompt("Select key type to enter (or Continue when done)")
51                .items(&options)
52                .default(0)
53                .interact()?;
54
55            if selection == options.len() - 1 {
56                // User selected "Continue"
57                break;
58            }
59
60            let key_type = all_key_types[selection];
61            let key: String = Input::new()
62                .with_prompt(format!("Enter private key for {:?}", key_type))
63                .interact_text()?;
64
65            collected_keys.push((key_type, key));
66        }
67    } else {
68        // When specific key types are provided, just prompt for each one
69        for key_type in key_types {
70            let key: String = Input::new()
71                .with_prompt(format!("Enter private key for {:?}", key_type))
72                .interact_text()?;
73
74            collected_keys.push((key_type, key));
75        }
76    }
77
78    Ok(collected_keys)
79}
80
81/// Import a key into the keystore
82///
83/// # Errors
84///
85/// If `output` is provided:
86/// * The keystore may fail to be created. See [`KeystoreConfig::fs_root()`].
87/// * The keystore may fail to write the new key. See [`Backend::insert()`].
88pub fn generate_key(
89    key_type: KeyTypeId,
90    output: Option<&impl AsRef<Path>>,
91    seed: Option<&[u8]>,
92    show_secret: bool,
93) -> Result<(String, Option<String>)> {
94    // Create keystore configuration
95    let mut config = KeystoreConfig::new();
96    if let Some(path) = output {
97        if !path.as_ref().exists() {
98            std::fs::create_dir_all(path.as_ref())?;
99        }
100        config = config.fs_root(path);
101    }
102
103    let keystore = Keystore::new(config)?;
104
105    // Generate key based on type
106    let (public_bytes, secret_bytes) = match key_type {
107        KeyTypeId::Sr25519 => {
108            let public = keystore.generate::<SpSr25519>(seed)?;
109            let secret = keystore.get_secret::<SpSr25519>(&public)?;
110            keystore.insert::<SpSr25519>(&secret)?;
111            (public.to_bytes(), secret.to_bytes())
112        }
113        KeyTypeId::Ed25519 => {
114            let public = keystore.generate::<SpEd25519>(seed)?;
115            let secret = keystore.get_secret::<SpEd25519>(&public)?;
116            keystore.insert::<SpEd25519>(&secret)?;
117            (public.to_bytes(), secret.to_bytes())
118        }
119        KeyTypeId::Ecdsa => {
120            let public = keystore.generate::<SpEcdsa>(seed)?;
121            let secret = keystore.get_secret::<SpEcdsa>(&public)?;
122            keystore.insert::<SpEcdsa>(&secret)?;
123            (public.to_bytes(), secret.to_bytes())
124        }
125        KeyTypeId::Bls381 => {
126            let public = keystore.generate::<SpBls381>(seed)?;
127            let secret = keystore.get_secret::<SpBls381>(&public)?;
128            keystore.insert::<SpBls381>(&secret)?;
129            (public.to_bytes(), secret.to_bytes())
130        }
131        KeyTypeId::Bls377 => {
132            let public = keystore.generate::<SpBls377>(seed)?;
133            let secret = keystore.get_secret::<SpBls377>(&public)?;
134            keystore.insert::<SpBls377>(&secret)?;
135            (public.to_bytes(), secret.to_bytes())
136        }
137        KeyTypeId::Bn254 => {
138            let public = keystore.generate::<ArkBlsBn254>(seed)?;
139            let secret = keystore.get_secret::<ArkBlsBn254>(&public)?;
140            keystore.insert::<ArkBlsBn254>(&secret)?;
141            (public.to_bytes(), secret.to_bytes())
142        }
143    };
144
145    let (public, secret) = (hex::encode(public_bytes), hex::encode(secret_bytes));
146
147    let mut secret = Some(secret);
148    if !show_secret {
149        secret = None;
150    }
151
152    Ok((public, secret))
153}
154
155/// Generate a new mnemonic
156///
157/// If no `word_count` is provided, it defaults to 12.
158///
159/// # Errors
160///
161/// The word count is not in the range of `12..=24` or is not divisible by 3
162pub fn generate_mnemonic(word_count: Option<u32>) -> Result<String> {
163    let count = match word_count {
164        Some(count) if !(12..=24).contains(&count) || count % 3 != 0 => {
165            return Err(Error::InvalidWordCount(count).into());
166        }
167        Some(count) => count,
168        None => 12,
169    };
170    let mut rng = bip39::rand::thread_rng();
171    let mnemonic = Mnemonic::generate_in_with(&mut rng, Language::English, count as usize)?;
172    Ok(mnemonic.to_string())
173}
174
175/// Import a key into the keystore
176///
177/// # Errors
178///
179/// * The key is not a valid hex string
180/// * The key is not a valid representation of the given `key_type`
181/// * The keystore, depending on the backend, may fail to open and/or write this new key
182pub fn import_key(
183    protocol: Protocol,
184    key_type: KeyTypeId,
185    secret: &str,
186    keystore_path: &Path,
187) -> Result<String> {
188    let mut config = KeystoreConfig::new();
189    config = config.fs_root(keystore_path);
190    let keystore = Keystore::new(config)?;
191
192    let secret_bytes = hex::decode(secret).map_err(|e| Error::InvalidKeyFormat(e.to_string()))?;
193
194    let public_key = match key_type {
195        KeyTypeId::Sr25519 => {
196            let key = SpSr25519Pair::from_bytes(&secret_bytes)?;
197            keystore.insert::<SpSr25519>(&key)?;
198            hex::encode(key.public().to_bytes())
199        }
200        KeyTypeId::Ed25519 => {
201            let key = SpEd25519Pair::from_bytes(&secret_bytes)?;
202            keystore.insert::<SpEd25519>(&key)?;
203            hex::encode(key.public().to_bytes())
204        }
205        KeyTypeId::Ecdsa => match protocol {
206            Protocol::Tangle => {
207                let key = SpEcdsaPair::from_bytes(&secret_bytes)?;
208                keystore.insert::<SpEcdsa>(&key)?;
209                hex::encode(key.public().to_bytes())
210            }
211            _ => {
212                let key = K256SigningKey::from_bytes(&secret_bytes)?;
213                keystore.insert::<K256Ecdsa>(&key)?;
214                hex::encode(key.public().to_bytes())
215            }
216        },
217        KeyTypeId::Bls381 => {
218            let key = SpBls381Pair::from_bytes(&secret_bytes)?;
219            keystore.insert::<SpBls381>(&key)?;
220            hex::encode(key.public().to_bytes())
221        }
222        KeyTypeId::Bls377 => {
223            let key = SpBls377Pair::from_bytes(&secret_bytes)?;
224            keystore.insert::<SpBls377>(&key)?;
225            hex::encode(key.public().to_bytes())
226        }
227        KeyTypeId::Bn254 => {
228            let key = ArkBlsBn254Secret::from_bytes(&secret_bytes)?;
229            keystore.insert::<ArkBlsBn254>(&key)?;
230            let public = ArkBlsBn254::public_from_secret(&key);
231            hex::encode(public.to_bytes())
232        }
233    };
234
235    Ok(public_key)
236}
237
238/// Get the hex encoded secret for a given public key
239///
240/// # Errors
241///
242/// * `public` is not a valid hex string
243/// * See [`Keystore::get_secret()`]
244pub fn export_key(key_type: KeyTypeId, public: &str, keystore_path: &Path) -> Result<String> {
245    let mut config = KeystoreConfig::new();
246    config = config.fs_root(keystore_path);
247    let keystore = Keystore::new(config)?;
248
249    let public_bytes = hex::decode(public).map_err(|e| Error::InvalidKeyFormat(e.to_string()))?;
250
251    let secret = match key_type {
252        KeyTypeId::Sr25519 => {
253            let public = SpSr25519Public::from_bytes(&public_bytes)?;
254            let secret = keystore.get_secret::<SpSr25519>(&public)?;
255            hex::encode(secret.to_bytes())
256        }
257        KeyTypeId::Ed25519 => {
258            let public = SpEd25519Public::from_bytes(&public_bytes)?;
259            let secret = keystore.get_secret::<SpEd25519>(&public)?;
260            hex::encode(secret.to_bytes())
261        }
262        KeyTypeId::Ecdsa => {
263            let public = SpEcdsaPublic::from_bytes(&public_bytes)?;
264            let secret = keystore.get_secret::<SpEcdsa>(&public)?;
265            hex::encode(secret.to_bytes())
266        }
267        KeyTypeId::Bls381 => {
268            let public = SpBls381Public::from_bytes(&public_bytes)?;
269            let secret = keystore.get_secret::<SpBls381>(&public)?;
270            hex::encode(secret.to_bytes())
271        }
272        KeyTypeId::Bls377 => {
273            let public = SpBls377Public::from_bytes(&public_bytes)?;
274            let secret = keystore.get_secret::<SpBls377>(&public)?;
275            hex::encode(secret.to_bytes())
276        }
277        KeyTypeId::Bn254 => {
278            let public = ArkBlsBn254Public::from_bytes(&public_bytes)?;
279            let secret = keystore.get_secret::<ArkBlsBn254>(&public)?;
280            hex::encode(secret.to_bytes())
281        }
282    };
283
284    Ok(secret)
285}
286
287/// Collect all keys from the keystore at `keystore_path`
288///
289/// # Errors
290///
291/// * See [`KeystoreConfig::fs_root()`] for potential IO errors
292/// * As this uses the filesystem, the keystore may fail to read the keys, for any reason.
293pub fn list_keys(keystore_path: &Path) -> Result<Vec<(KeyTypeId, String)>> {
294    let mut config = KeystoreConfig::new();
295    config = config.fs_root(keystore_path);
296    let keystore = Keystore::new(config)?;
297
298    let mut keys = Vec::new();
299
300    // List keys for each type
301    for key in keystore.list_local::<SpSr25519>()? {
302        keys.push((KeyTypeId::Sr25519, hex::encode(key.to_bytes())));
303    }
304    for key in keystore.list_local::<SpEd25519>()? {
305        keys.push((KeyTypeId::Ed25519, hex::encode(key.to_bytes())));
306    }
307    for key in keystore.list_local::<SpEcdsa>()? {
308        keys.push((KeyTypeId::Ecdsa, hex::encode(key.to_bytes())));
309    }
310    for key in keystore.list_local::<SpBls381>()? {
311        keys.push((KeyTypeId::Bls381, hex::encode(key.to_bytes())));
312    }
313    for key in keystore.list_local::<SpBls377>()? {
314        keys.push((KeyTypeId::Bls377, hex::encode(key.to_bytes())));
315    }
316    for key in keystore.list_local::<ArkBlsBn254>()? {
317        keys.push((KeyTypeId::Bn254, hex::encode(key.to_bytes())));
318    }
319
320    Ok(keys)
321}