fuel_core_keygen/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#![deny(clippy::arithmetic_side_effects)]
#![deny(clippy::cast_possible_truncation)]

use clap::ValueEnum;
use fuel_core_types::{
    fuel_crypto::{
        rand::{
            prelude::StdRng,
            SeedableRng,
        },
        SecretKey,
    },
    fuel_tx::Input,
    fuel_types::Address,
};
use libp2p_identity::{
    secp256k1,
    Keypair,
    PeerId,
};
use serde::Serialize;
use std::{
    ops::Deref,
    str::FromStr,
};

#[derive(Clone, Copy, Debug, Default, Serialize, ValueEnum)]
#[serde(rename_all = "kebab-case")]
pub enum KeyType {
    #[default]
    BlockProduction,
    Peering,
}

impl From<KeyType> for &'static str {
    fn from(key_type: KeyType) -> Self {
        match key_type {
            KeyType::BlockProduction => "block-production",
            KeyType::Peering => "p2p",
        }
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct ParseSecretResponse {
    #[serde(skip_serializing_if = "Option::is_none")]
    address: Option<Address>,
    #[serde(
        serialize_with = "serialize_option_to_string",
        skip_serializing_if = "Option::is_none"
    )]
    peer_id: Option<PeerId>,
    #[serde(rename = "type")]
    typ: KeyType,
}

#[derive(Clone, Debug, Serialize)]
pub struct NewKeyResponse {
    secret: SecretKey,
    #[serde(skip_serializing_if = "Option::is_none")]
    address: Option<Address>,
    #[serde(
        serialize_with = "serialize_option_to_string",
        skip_serializing_if = "Option::is_none"
    )]
    peer_id: Option<PeerId>,
    #[serde(rename = "type")]
    typ: KeyType,
}

fn serialize_option_to_string<S, T>(
    opt: &Option<T>,
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
    T: ToString,
{
    if let Some(value) = opt.as_ref() {
        value.to_string().serialize(serializer)
    } else {
        serializer.serialize_none()
    }
}

pub fn new_key(key_type: KeyType) -> anyhow::Result<NewKeyResponse> {
    let mut rng = StdRng::from_entropy();
    let secret = SecretKey::random(&mut rng);
    let public_key = secret.public_key();

    Ok(match key_type {
        KeyType::BlockProduction => {
            let address = Input::owner(&public_key);
            NewKeyResponse {
                secret,
                address: Some(address),
                peer_id: None,
                typ: key_type,
            }
        }
        KeyType::Peering => {
            let mut bytes = *secret.deref();
            let p2p_secret = secp256k1::SecretKey::try_from_bytes(&mut bytes)
                .expect("Should be a valid private key");
            let p2p_keypair = secp256k1::Keypair::from(p2p_secret);
            let libp2p_keypair = Keypair::from(p2p_keypair);
            let peer_id = PeerId::from_public_key(&libp2p_keypair.public());
            NewKeyResponse {
                secret,
                address: None,
                peer_id: Some(peer_id),
                typ: key_type,
            }
        }
    })
}

pub fn parse_secret(
    key_type: KeyType,
    secret: &str,
) -> anyhow::Result<ParseSecretResponse> {
    let secret =
        SecretKey::from_str(secret).map_err(|_| anyhow::anyhow!("invalid secret key"))?;
    Ok(match key_type {
        KeyType::BlockProduction => {
            let address = Input::owner(&secret.public_key());
            ParseSecretResponse {
                address: Some(address),
                peer_id: None,
                typ: key_type,
            }
        }
        KeyType::Peering => {
            let mut bytes = *secret.deref();
            let p2p_secret = secp256k1::SecretKey::try_from_bytes(&mut bytes)
                .expect("Should be a valid private key");
            let p2p_keypair = secp256k1::Keypair::from(p2p_secret);
            let libp2p_keypair = Keypair::from(p2p_keypair);
            let peer_id = PeerId::from_public_key(&libp2p_keypair.public());
            ParseSecretResponse {
                address: None,
                peer_id: Some(peer_id),
                typ: key_type,
            }
        }
    })
}