reth_cli_util/
load_secret_key.rs1use reth_fs_util::{self as fs, FsPathError};
2use secp256k1::{Error as SecretKeyBaseError, SecretKey};
3use std::{
4 io,
5 path::{Path, PathBuf},
6};
7use thiserror::Error;
8
9pub fn rng_secret_key() -> SecretKey {
11 SecretKey::new(&mut rand::rng())
12}
13
14#[derive(Error, Debug)]
16pub enum SecretKeyError {
17 #[error(transparent)]
19 SecretKeyDecodeError(#[from] SecretKeyBaseError),
20
21 #[error(transparent)]
23 SecretKeyFsPathError(#[from] FsPathError),
24
25 #[error("failed to access key file {secret_file:?}: {error}")]
27 FailedToAccessKeyFile {
28 error: io::Error,
30 secret_file: PathBuf,
32 },
33
34 #[error("invalid hex string: {0}")]
36 InvalidHexString(String),
37}
38
39pub fn get_secret_key(secret_key_path: &Path) -> Result<SecretKey, SecretKeyError> {
43 let exists = secret_key_path.try_exists();
44
45 match exists {
46 Ok(true) => {
47 let contents = fs::read_to_string(secret_key_path)?;
48 Ok(contents.as_str().parse().map_err(SecretKeyError::SecretKeyDecodeError)?)
49 }
50 Ok(false) => {
51 if let Some(dir) = secret_key_path.parent() {
52 fs::create_dir_all(dir)?;
54 }
55
56 let secret = rng_secret_key();
57 let hex = alloy_primitives::hex::encode(secret.as_secret_bytes());
58 fs::write(secret_key_path, hex)?;
59 Ok(secret)
60 }
61 Err(error) => Err(SecretKeyError::FailedToAccessKeyFile {
62 error,
63 secret_file: secret_key_path.to_path_buf(),
64 }),
65 }
66}
67
68pub fn parse_secret_key_from_hex(hex_str: &str) -> Result<SecretKey, SecretKeyError> {
72 let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
74
75 let bytes = alloy_primitives::hex::decode(hex_str)
77 .map_err(|e| SecretKeyError::InvalidHexString(e.to_string()))?;
78
79 SecretKey::from_secret_bytes(
81 bytes.try_into().map_err(|_| SecretKeyBaseError::InvalidSecretKey)?,
82 )
83 .map_err(SecretKeyError::SecretKeyDecodeError)
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_parse_secret_key_from_hex_without_prefix() {
92 let hex = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
94 let result = parse_secret_key_from_hex(hex);
95 assert!(result.is_ok());
96
97 let secret_key = result.unwrap();
98 assert_eq!(alloy_primitives::hex::encode(secret_key.to_secret_bytes()), hex);
99 }
100
101 #[test]
102 fn test_parse_secret_key_from_hex_with_0x_prefix() {
103 let hex = "0x4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
105 let result = parse_secret_key_from_hex(hex);
106 assert!(result.is_ok());
107
108 let secret_key = result.unwrap();
109 let expected = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
110 assert_eq!(alloy_primitives::hex::encode(secret_key.to_secret_bytes()), expected);
111 }
112
113 #[test]
114 fn test_parse_secret_key_from_hex_invalid_length() {
115 let hex = "4c0883a69102937d";
117 let result = parse_secret_key_from_hex(hex);
118 assert!(matches!(
119 result,
120 Err(SecretKeyError::SecretKeyDecodeError(SecretKeyBaseError::InvalidSecretKey))
121 ));
122 }
123
124 #[test]
125 fn test_parse_secret_key_from_hex_invalid_chars() {
126 let hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
128 let result = parse_secret_key_from_hex(hex);
129 assert!(result.is_err());
130
131 if let Err(SecretKeyError::InvalidHexString(_)) = result {
132 } else {
134 panic!("Expected InvalidHexString error");
135 }
136 }
137
138 #[test]
139 fn test_parse_secret_key_from_hex_empty() {
140 let hex = "";
141 let result = parse_secret_key_from_hex(hex);
142 assert!(matches!(
143 result,
144 Err(SecretKeyError::SecretKeyDecodeError(SecretKeyBaseError::InvalidSecretKey))
145 ));
146 }
147}