fuel_core_keygen/
lib.rs

1#![deny(clippy::arithmetic_side_effects)]
2#![deny(clippy::cast_possible_truncation)]
3
4use clap::ValueEnum;
5use fuel_core_types::{
6    fuel_crypto::{
7        SecretKey,
8        rand::{
9            SeedableRng,
10            prelude::StdRng,
11        },
12    },
13    fuel_tx::Input,
14    fuel_types::Address,
15};
16use libp2p_identity::{
17    Keypair,
18    PeerId,
19    secp256k1,
20};
21use serde::Serialize;
22use std::{
23    ops::Deref,
24    str::FromStr,
25};
26
27#[derive(Clone, Copy, Debug, Default, Serialize, ValueEnum)]
28#[serde(rename_all = "kebab-case")]
29pub enum KeyType {
30    #[default]
31    BlockProduction,
32    Peering,
33}
34
35impl From<KeyType> for &'static str {
36    fn from(key_type: KeyType) -> Self {
37        match key_type {
38            KeyType::BlockProduction => "block-production",
39            KeyType::Peering => "p2p",
40        }
41    }
42}
43
44#[derive(Clone, Debug, Serialize)]
45pub struct ParseSecretResponse {
46    #[serde(skip_serializing_if = "Option::is_none")]
47    address: Option<Address>,
48    #[serde(
49        serialize_with = "serialize_option_to_string",
50        skip_serializing_if = "Option::is_none"
51    )]
52    peer_id: Option<PeerId>,
53    #[serde(rename = "type")]
54    typ: KeyType,
55}
56
57#[derive(Clone, Debug, Serialize)]
58pub struct NewKeyResponse {
59    secret: SecretKey,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    address: Option<Address>,
62    #[serde(
63        serialize_with = "serialize_option_to_string",
64        skip_serializing_if = "Option::is_none"
65    )]
66    peer_id: Option<PeerId>,
67    #[serde(rename = "type")]
68    typ: KeyType,
69}
70
71fn serialize_option_to_string<S, T>(
72    opt: &Option<T>,
73    serializer: S,
74) -> Result<S::Ok, S::Error>
75where
76    S: serde::Serializer,
77    T: ToString,
78{
79    if let Some(value) = opt.as_ref() {
80        value.to_string().serialize(serializer)
81    } else {
82        serializer.serialize_none()
83    }
84}
85
86pub fn new_key(key_type: KeyType) -> anyhow::Result<NewKeyResponse> {
87    let mut rng = StdRng::from_entropy();
88    let secret = SecretKey::random(&mut rng);
89    let public_key = secret.public_key();
90
91    Ok(match key_type {
92        KeyType::BlockProduction => {
93            let address = Input::owner(&public_key);
94            NewKeyResponse {
95                secret,
96                address: Some(address),
97                peer_id: None,
98                typ: key_type,
99            }
100        }
101        KeyType::Peering => {
102            let mut bytes = *secret.deref();
103            let p2p_secret = secp256k1::SecretKey::try_from_bytes(&mut bytes)
104                .expect("Should be a valid private key");
105            let p2p_keypair = secp256k1::Keypair::from(p2p_secret);
106            let libp2p_keypair = Keypair::from(p2p_keypair);
107            let peer_id = PeerId::from_public_key(&libp2p_keypair.public());
108            NewKeyResponse {
109                secret,
110                address: None,
111                peer_id: Some(peer_id),
112                typ: key_type,
113            }
114        }
115    })
116}
117
118pub fn parse_secret(
119    key_type: KeyType,
120    secret: &str,
121) -> anyhow::Result<ParseSecretResponse> {
122    let secret =
123        SecretKey::from_str(secret).map_err(|_| anyhow::anyhow!("invalid secret key"))?;
124    Ok(match key_type {
125        KeyType::BlockProduction => {
126            let address = Input::owner(&secret.public_key());
127            ParseSecretResponse {
128                address: Some(address),
129                peer_id: None,
130                typ: key_type,
131            }
132        }
133        KeyType::Peering => {
134            let mut bytes = *secret.deref();
135            let p2p_secret = secp256k1::SecretKey::try_from_bytes(&mut bytes)
136                .expect("Should be a valid private key");
137            let p2p_keypair = secp256k1::Keypair::from(p2p_secret);
138            let libp2p_keypair = Keypair::from(p2p_keypair);
139            let peer_id = PeerId::from_public_key(&libp2p_keypair.public());
140            ParseSecretResponse {
141                address: None,
142                peer_id: Some(peer_id),
143                typ: key_type,
144            }
145        }
146    })
147}