|
1 |
| -use libp2p::identity::Keypair; |
2 |
| -use libp2p::multiaddr::Protocol; |
3 |
| -use libp2p::{Multiaddr, PeerId}; |
4 |
| -use rand::{rngs::OsRng, Rng}; |
5 |
| -use serde::{Deserialize, Serialize}; |
6 |
| -use std::fs; |
7 |
| -use std::path::Path; |
8 |
| -use thiserror::Error; |
| 1 | +//! Static configuration (the bootstrap node(s)). |
9 | 2 |
|
10 | 3 | pub const BOOTSTRAP_NODES: &[&str] =
|
11 | 4 | &["/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"];
|
12 |
| - |
13 |
| -/// See test cases for examples how to write such file. |
14 |
| -#[derive(Clone, Debug, Serialize, Deserialize)] |
15 |
| -pub struct ConfigFile { |
16 |
| - #[serde(flatten)] |
17 |
| - key: KeyMaterial, |
18 |
| - bootstrap: Vec<Multiaddr>, |
19 |
| -} |
20 |
| - |
21 |
| -/// KeyMaterial is an additional abstraction as the identity::Keypair does not support Clone or |
22 |
| -/// Debug nor is there a clearcut way to serialize and deserialize such yet at least. |
23 |
| -#[derive(Clone, Serialize, Deserialize)] |
24 |
| -#[serde(untagged)] |
25 |
| -enum KeyMaterial { |
26 |
| - Ed25519 { |
27 |
| - private_key: [u8; 32], |
28 |
| - #[serde(skip)] |
29 |
| - keypair: Option<Box<libp2p::identity::ed25519::Keypair>>, |
30 |
| - }, |
31 |
| - RsaPkcs8File { |
32 |
| - #[serde(rename = "rsa_pkcs8_filename")] |
33 |
| - filename: String, |
34 |
| - #[serde(skip)] |
35 |
| - keypair: Option<Box<libp2p::identity::rsa::Keypair>>, |
36 |
| - }, |
37 |
| -} |
38 |
| - |
39 |
| -use std::fmt; |
40 |
| - |
41 |
| -impl fmt::Debug for KeyMaterial { |
42 |
| - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
43 |
| - match *self { |
44 |
| - KeyMaterial::Ed25519 { ref keypair, .. } => { |
45 |
| - if let Some(kp) = keypair.as_ref() { |
46 |
| - write!(fmt, "{:?}", kp) |
47 |
| - } else { |
48 |
| - write!(fmt, "Ed25519(not loaded)") |
49 |
| - } |
50 |
| - } |
51 |
| - KeyMaterial::RsaPkcs8File { |
52 |
| - ref keypair, |
53 |
| - ref filename, |
54 |
| - } => { |
55 |
| - if let Some(kp) = keypair.as_ref() { |
56 |
| - write!(fmt, "{:?}", kp.public()) |
57 |
| - } else { |
58 |
| - write!(fmt, "Rsa(not loaded: {:?})", filename) |
59 |
| - } |
60 |
| - } |
61 |
| - } |
62 |
| - } |
63 |
| -} |
64 |
| - |
65 |
| -#[derive(Debug, Error)] |
66 |
| -pub enum KeyMaterialLoadingFailure { |
67 |
| - #[error("{0}")] |
68 |
| - Io(#[from] std::io::Error), |
69 |
| - #[error("{0}")] |
70 |
| - RsaDecoding(#[from] libp2p::identity::error::DecodingError), |
71 |
| - #[error("{0}")] |
72 |
| - Config(#[from] serde_json::error::Error), |
73 |
| -} |
74 |
| - |
75 |
| -impl KeyMaterial { |
76 |
| - fn clone_keypair(&self) -> Keypair { |
77 |
| - match *self { |
78 |
| - KeyMaterial::Ed25519 { ref keypair, .. } => keypair |
79 |
| - .as_ref() |
80 |
| - .map(|kp| Keypair::Ed25519(kp.as_ref().clone())), |
81 |
| - KeyMaterial::RsaPkcs8File { ref keypair, .. } => { |
82 |
| - keypair.as_ref().map(|kp| Keypair::Rsa(kp.as_ref().clone())) |
83 |
| - } |
84 |
| - } |
85 |
| - .expect("KeyMaterial needs to be loaded before accessing the keypair") |
86 |
| - } |
87 |
| - |
88 |
| - fn load(&mut self) -> Result<(), KeyMaterialLoadingFailure> { |
89 |
| - match *self { |
90 |
| - KeyMaterial::Ed25519 { |
91 |
| - ref private_key, |
92 |
| - ref mut keypair, |
93 |
| - } if keypair.is_none() => { |
94 |
| - let mut cloned = *private_key; |
95 |
| - let sk = libp2p::identity::ed25519::SecretKey::from_bytes(&mut cloned) |
96 |
| - .expect("Failed to extract ed25519::SecretKey"); |
97 |
| - |
98 |
| - let kp = libp2p::identity::ed25519::Keypair::from(sk); |
99 |
| - |
100 |
| - *keypair = Some(Box::new(kp)); |
101 |
| - } |
102 |
| - KeyMaterial::RsaPkcs8File { |
103 |
| - ref filename, |
104 |
| - ref mut keypair, |
105 |
| - } if keypair.is_none() => { |
106 |
| - let mut bytes = std::fs::read(filename).map_err(KeyMaterialLoadingFailure::Io)?; |
107 |
| - let kp = libp2p::identity::rsa::Keypair::from_pkcs8(&mut bytes) |
108 |
| - .map_err(KeyMaterialLoadingFailure::RsaDecoding)?; |
109 |
| - *keypair = Some(Box::new(kp)); |
110 |
| - } |
111 |
| - _ => { /* all set */ } |
112 |
| - } |
113 |
| - |
114 |
| - Ok(()) |
115 |
| - } |
116 |
| - |
117 |
| - fn into_loaded(mut self) -> Result<Self, KeyMaterialLoadingFailure> { |
118 |
| - self.load()?; |
119 |
| - Ok(self) |
120 |
| - } |
121 |
| -} |
122 |
| - |
123 |
| -impl ConfigFile { |
124 |
| - pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, KeyMaterialLoadingFailure> { |
125 |
| - if path.as_ref().exists() { |
126 |
| - let content = fs::read_to_string(&path)?; |
127 |
| - let config = Self::parse(&content)?; |
128 |
| - config.into_loaded() |
129 |
| - } else { |
130 |
| - let config = ConfigFile::default(); |
131 |
| - config.store_at(path)?; |
132 |
| - config.into_loaded() |
133 |
| - } |
134 |
| - } |
135 |
| - |
136 |
| - fn load(&mut self) -> Result<(), KeyMaterialLoadingFailure> { |
137 |
| - self.key.load() |
138 |
| - } |
139 |
| - |
140 |
| - fn into_loaded(mut self) -> Result<Self, KeyMaterialLoadingFailure> { |
141 |
| - self.load()?; |
142 |
| - Ok(self) |
143 |
| - } |
144 |
| - |
145 |
| - fn parse(s: &str) -> Result<Self, KeyMaterialLoadingFailure> { |
146 |
| - Ok(serde_json::from_str(s)?) |
147 |
| - } |
148 |
| - |
149 |
| - pub fn store_at<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> { |
150 |
| - fs::create_dir_all(path.as_ref().parent().unwrap())?; |
151 |
| - let string = serde_json::to_string_pretty(self).unwrap(); |
152 |
| - fs::write(path, string) |
153 |
| - } |
154 |
| - |
155 |
| - pub fn identity_key_pair(&self) -> Keypair { |
156 |
| - self.key.clone_keypair() |
157 |
| - } |
158 |
| - |
159 |
| - pub fn bootstrap(&self) -> Vec<(Multiaddr, PeerId)> { |
160 |
| - let mut bootstrap = Vec::new(); |
161 |
| - for addr in &self.bootstrap { |
162 |
| - let mut addr = addr.to_owned(); |
163 |
| - let peer_id = match addr.pop() { |
164 |
| - Some(Protocol::P2p(hash)) => PeerId::from_multihash(hash).unwrap(), |
165 |
| - _ => panic!("No peer id for addr"), |
166 |
| - }; |
167 |
| - bootstrap.push((addr, peer_id)); |
168 |
| - } |
169 |
| - bootstrap |
170 |
| - } |
171 |
| -} |
172 |
| - |
173 |
| -impl Default for ConfigFile { |
174 |
| - fn default() -> Self { |
175 |
| - // the ed25519 has no chance of working with go-ipfs as of now because of |
176 |
| - // https://github.com/libp2p/specs/issues/138 and |
177 |
| - // https://github.com/libp2p/go-libp2p-core/blob/dc718fa4dab1866476fd9f379718fdd619455a4f/peer/peer.go#L23-L34 |
178 |
| - // |
179 |
| - // and on the other hand: |
180 |
| - // https://github.com/libp2p/rust-libp2p/blob/eb7b7bd919b93e6acf00847c19d1a76c09016120/core/src/peer_id.rs#L62-L74 |
181 |
| - let private_key: [u8; 32] = OsRng.gen(); |
182 |
| - |
183 |
| - let bootstrap = BOOTSTRAP_NODES |
184 |
| - .iter() |
185 |
| - .map(|node| node.parse().unwrap()) |
186 |
| - .collect(); |
187 |
| - ConfigFile { |
188 |
| - key: KeyMaterial::Ed25519 { |
189 |
| - private_key, |
190 |
| - keypair: None, |
191 |
| - } |
192 |
| - .into_loaded() |
193 |
| - .unwrap(), |
194 |
| - bootstrap, |
195 |
| - } |
196 |
| - } |
197 |
| -} |
198 |
| - |
199 |
| -#[cfg(test)] |
200 |
| -mod tests { |
201 |
| - |
202 |
| - use super::ConfigFile; |
203 |
| - |
204 |
| - #[test] |
205 |
| - fn supports_older_v1_and_ed25519_v2() { |
206 |
| - let input = r#"{"private_key":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"bootstrap":[]}"#; |
207 |
| - |
208 |
| - let actual = ConfigFile::parse(input).unwrap(); |
209 |
| - |
210 |
| - let roundtrip = serde_json::to_string(&actual).unwrap(); |
211 |
| - |
212 |
| - assert_eq!(input, roundtrip); |
213 |
| - } |
214 |
| - |
215 |
| - #[test] |
216 |
| - fn supports_v2() { |
217 |
| - let input = r#"{"rsa_pkcs8_filename":"foobar.pk8","bootstrap":[]}"#; |
218 |
| - |
219 |
| - let actual = ConfigFile::parse(input).unwrap(); |
220 |
| - |
221 |
| - let roundtrip = serde_json::to_string(&actual).unwrap(); |
222 |
| - |
223 |
| - assert_eq!(input, roundtrip); |
224 |
| - } |
225 |
| -} |
0 commit comments