1use std::net::{IpAddr, SocketAddr};
6
7use anyhow::Result;
8use fastcrypto::traits::KeyPair;
9use iota_config::{
10 Config,
11 genesis::{GenesisCeremonyParameters, PRE_V32_VALIDATOR_LOW_STAKE_THRESHOLD, TokenAllocation},
12 local_ip_utils,
13 node::{DEFAULT_COMMISSION_RATE, DEFAULT_VALIDATOR_GAS_PRICE},
14};
15use iota_genesis_builder::validator_info::{GenesisValidatorInfo, ValidatorInfo};
16use iota_multiaddr::Multiaddr;
17use iota_protocol_config::{Chain, ProtocolConfig};
18use iota_sdk_crypto::simple::SimpleKeypair;
19use iota_sdk_types::Address;
20use iota_types::{
21 committee::ProtocolVersion,
22 crypto::{
23 AccountPrivateKey, AuthorityKeyPair, AuthorityPublicKeyBytes, NetworkKeyPair,
24 NetworkPublicKey, PublicKey, generate_proof_of_possession, get_key_pair_from_rng,
25 },
26};
27use rand::{SeedableRng, rngs::StdRng};
28use serde::{Deserialize, Serialize};
29use tracing::info;
30
31#[derive(Serialize, Deserialize, Debug)]
33pub struct SsfnGenesisConfig {
34 pub p2p_address: Multiaddr,
35 pub network_key_pair: Option<NetworkKeyPair>,
36}
37
38#[derive(Serialize, Deserialize)]
40pub struct ValidatorGenesisConfig {
41 #[serde(default = "default_bls12381_key_pair")]
42 pub authority_key_pair: AuthorityKeyPair,
43 #[serde(default = "default_ed25519_key_pair")]
44 pub protocol_key_pair: NetworkKeyPair,
45 #[serde(default = "default_iota_key_pair", with = "base64_formatted_keypair")]
46 pub account_key_pair: SimpleKeypair,
47 #[serde(default = "default_ed25519_key_pair")]
48 pub network_key_pair: NetworkKeyPair,
49 pub network_address: Multiaddr,
50 pub p2p_address: Multiaddr,
51 pub p2p_listen_address: Option<SocketAddr>,
52 #[serde(default = "default_socket_address")]
53 pub metrics_address: SocketAddr,
54 #[serde(default = "default_socket_address")]
55 pub admin_interface_address: SocketAddr,
56 pub gas_price: u64,
57 pub commission_rate: u64,
58 pub primary_address: Multiaddr,
59 #[serde(default = "default_stake")]
60 pub stake: u64,
61 pub name: Option<String>,
62}
63
64impl ValidatorGenesisConfig {
65 pub fn copy_with_private_keys(&self) -> Self {
69 Self {
70 authority_key_pair: self.authority_key_pair.copy(),
71 protocol_key_pair: self.protocol_key_pair.copy(),
72 account_key_pair: self.account_key_pair.clone(),
73 network_key_pair: self.network_key_pair.copy(),
74 network_address: self.network_address.clone(),
75 p2p_address: self.p2p_address.clone(),
76 p2p_listen_address: self.p2p_listen_address,
77 metrics_address: self.metrics_address,
78 admin_interface_address: self.admin_interface_address,
79 gas_price: self.gas_price,
80 commission_rate: self.commission_rate,
81 primary_address: self.primary_address.clone(),
82 stake: self.stake,
83 name: self.name.clone(),
84 }
85 }
86
87 pub fn to_validator_info(&self, name: String) -> GenesisValidatorInfo {
88 let authority_key: AuthorityPublicKeyBytes = self.authority_key_pair.public().into();
89 let account_key = PublicKey::from(&self.account_key_pair);
90 let network_key: NetworkPublicKey = self.network_key_pair.public().clone();
91 let protocol_key: NetworkPublicKey = self.protocol_key_pair.public().clone();
92 let network_address = self.network_address.clone();
93
94 let info = ValidatorInfo {
95 name,
96 authority_key,
97 protocol_key,
98 network_key,
99 account_address: Address::from(&account_key),
100 gas_price: self.gas_price,
101 commission_rate: self.commission_rate,
102 network_address,
103 p2p_address: self.p2p_address.clone(),
104 primary_address: self.primary_address.clone(),
105 description: String::new(),
106 image_url: String::new(),
107 project_url: String::new(),
108 };
109 let proof_of_possession = generate_proof_of_possession(
110 &self.authority_key_pair,
111 (&PublicKey::from(&self.account_key_pair)).into(),
112 );
113 GenesisValidatorInfo {
114 info,
115 proof_of_possession,
116 }
117 }
118
119 pub fn to_validator_info_with_random_name(&self) -> GenesisValidatorInfo {
121 self.to_validator_info(self.authority_key_pair.public().to_string())
122 }
123}
124
125#[derive(Default)]
126pub struct ValidatorGenesisConfigBuilder {
127 authority_key_pair: Option<AuthorityKeyPair>,
128 account_private_key: Option<AccountPrivateKey>,
129 ip: Option<String>,
130 gas_price: Option<u64>,
131 port_offset: Option<u16>,
134 p2p_listen_ip_address: Option<IpAddr>,
137 metrics_ip_address: Option<IpAddr>,
138}
139
140impl ValidatorGenesisConfigBuilder {
141 pub fn new() -> Self {
142 Self::default()
143 }
144
145 pub fn with_authority_key_pair(mut self, key_pair: AuthorityKeyPair) -> Self {
146 self.authority_key_pair = Some(key_pair);
147 self
148 }
149
150 pub fn with_account_private_key(mut self, private_key: AccountPrivateKey) -> Self {
151 self.account_private_key = Some(private_key);
152 self
153 }
154
155 pub fn with_ip(mut self, ip: String) -> Self {
156 self.ip = Some(ip);
157 self
158 }
159
160 pub fn with_gas_price(mut self, gas_price: u64) -> Self {
161 self.gas_price = Some(gas_price);
162 self
163 }
164
165 pub fn with_deterministic_ports(mut self, port_offset: u16) -> Self {
166 self.port_offset = Some(port_offset);
167 self
168 }
169
170 pub fn with_p2p_listen_ip_address(mut self, p2p_listen_ip_address: IpAddr) -> Self {
171 self.p2p_listen_ip_address = Some(p2p_listen_ip_address);
172 self
173 }
174
175 pub fn with_metrics_ip_address(mut self, metrics_ip_address: IpAddr) -> Self {
179 self.metrics_ip_address = Some(metrics_ip_address);
180 self
181 }
182
183 pub fn build<R: rand::RngCore + rand::CryptoRng>(self, rng: &mut R) -> ValidatorGenesisConfig {
184 let ip = self.ip.unwrap_or_else(local_ip_utils::get_new_ip);
185 let localhost = local_ip_utils::localhost_for_testing();
186
187 let authority_key_pair = self
188 .authority_key_pair
189 .unwrap_or_else(|| get_key_pair_from_rng(rng).1);
190 let account_private_key = self
191 .account_private_key
192 .unwrap_or_else(|| get_key_pair_from_rng(rng).1);
193 let gas_price = self.gas_price.unwrap_or(DEFAULT_VALIDATOR_GAS_PRICE);
194
195 let (protocol_key_pair, network_key_pair): (NetworkKeyPair, NetworkKeyPair) =
196 (get_key_pair_from_rng(rng).1, get_key_pair_from_rng(rng).1);
197
198 let metrics_ip = self.metrics_ip_address.map(|ip| ip.to_string());
199
200 let (
201 network_address,
202 p2p_address,
203 metrics_address,
204 primary_address,
205 admin_interface_address,
206 ) = if let Some(offset) = self.port_offset {
207 (
208 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset),
209 local_ip_utils::new_deterministic_udp_address_for_testing(&ip, offset + 1),
210 match &metrics_ip {
211 Some(metrics_ip) => local_ip_utils::new_deterministic_tcp_address_for_testing(
212 metrics_ip,
213 offset + 2,
214 ),
215 None => {
216 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset + 2)
217 .with_zero_ip()
218 }
219 },
220 local_ip_utils::new_deterministic_udp_address_for_testing(&ip, offset + 3),
221 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset + 4),
222 )
223 } else {
224 (
225 local_ip_utils::new_tcp_address_for_testing(&ip),
226 local_ip_utils::new_udp_address_for_testing(&ip),
227 local_ip_utils::new_tcp_address_for_testing(
228 metrics_ip.as_deref().unwrap_or(&localhost),
229 ),
230 local_ip_utils::new_udp_address_for_testing(&ip),
231 local_ip_utils::new_tcp_address_for_testing(&localhost),
232 )
233 };
234
235 let p2p_listen_address = self
236 .p2p_listen_ip_address
237 .map(|ip| SocketAddr::new(ip, p2p_address.port().unwrap()));
238
239 ValidatorGenesisConfig {
240 authority_key_pair,
241 protocol_key_pair,
242 account_key_pair: account_private_key.into(),
243 network_key_pair,
244 network_address,
245 p2p_address,
246 p2p_listen_address,
247 metrics_address: metrics_address.to_socket_addr().unwrap(),
248 admin_interface_address: admin_interface_address.to_socket_addr().unwrap(),
249 gas_price,
250 commission_rate: DEFAULT_COMMISSION_RATE,
251 primary_address,
252 stake: ProtocolConfig::get_for_version(ProtocolVersion::MAX, Chain::Unknown)
256 .validator_low_stake_threshold_as_option()
257 .unwrap_or(PRE_V32_VALIDATOR_LOW_STAKE_THRESHOLD),
258 name: None,
259 }
260 }
261}
262
263#[derive(Serialize, Deserialize, Default)]
264pub struct GenesisConfig {
265 pub ssfn_config_info: Option<Vec<SsfnGenesisConfig>>,
266 pub validator_config_info: Option<Vec<ValidatorGenesisConfig>>,
267 #[serde(default)]
273 pub fullnode_config_info: Option<ValidatorGenesisConfig>,
274 pub parameters: GenesisCeremonyParameters,
275 pub accounts: Vec<AccountConfig>,
276}
277
278impl Config for GenesisConfig {}
279
280impl GenesisConfig {
281 pub fn protocol_config(&self) -> ProtocolConfig {
283 ProtocolConfig::get_for_version(self.parameters.protocol_version, Chain::Unknown)
284 }
285
286 pub fn generate_accounts<R: rand::RngCore + rand::CryptoRng>(
287 &self,
288 mut rng: R,
289 ) -> Result<(Vec<AccountPrivateKey>, Vec<TokenAllocation>)> {
290 let mut addresses = Vec::new();
291 let mut allocations = Vec::new();
292
293 info!("Creating accounts and token allocations...");
294
295 let mut keys = Vec::new();
296 for account in &self.accounts {
297 let address = if let Some(address) = account.address {
298 address
299 } else {
300 let (address, key) = get_key_pair_from_rng(&mut rng);
301 keys.push(key);
302 address
303 };
304
305 addresses.push(address);
306
307 account.gas_amounts.iter().for_each(|a| {
309 allocations.push(TokenAllocation {
310 recipient_address: address,
311 amount_nanos: *a,
312 staked_with_validator: None,
313 staked_with_timelock_expiration: None,
314 });
315 });
316 }
317
318 Ok((keys, allocations))
319 }
320}
321
322fn default_socket_address() -> SocketAddr {
323 local_ip_utils::new_local_tcp_socket_for_testing()
324}
325
326fn default_stake() -> u64 {
327 ProtocolConfig::get_for_version(ProtocolVersion::MAX, Chain::Unknown)
331 .validator_low_stake_threshold_as_option()
332 .unwrap_or(PRE_V32_VALIDATOR_LOW_STAKE_THRESHOLD)
333}
334
335fn default_bls12381_key_pair() -> AuthorityKeyPair {
336 get_key_pair_from_rng(&mut rand::rngs::OsRng).1
337}
338
339fn default_ed25519_key_pair() -> NetworkKeyPair {
340 get_key_pair_from_rng(&mut rand::rngs::OsRng).1
341}
342
343fn default_iota_key_pair() -> SimpleKeypair {
344 SimpleKeypair::from(AccountPrivateKey::random())
345}
346
347mod base64_formatted_keypair {
350 use fastcrypto::encoding::{Base64, Encoding};
351 use iota_sdk_crypto::simple::SimpleKeypair;
352 use serde::{Deserialize, Deserializer, Serializer};
353
354 pub fn serialize<S: Serializer>(kp: &SimpleKeypair, serializer: S) -> Result<S::Ok, S::Error> {
355 serializer.serialize_str(&Base64::encode(kp.to_bytes()))
356 }
357
358 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<SimpleKeypair, D::Error> {
359 use serde::de::Error;
360
361 let s = String::deserialize(d)?;
362 let bytes = Base64::decode(&s).map_err(Error::custom)?;
363 SimpleKeypair::from_bytes(&bytes).map_err(Error::custom)
364 }
365}
366
367#[derive(Serialize, Deserialize, Debug, Clone)]
368pub struct AccountConfig {
369 #[serde(skip_serializing_if = "Option::is_none")]
370 pub address: Option<Address>,
371 pub gas_amounts: Vec<u64>,
372}
373
374pub const DEFAULT_GAS_AMOUNT: u64 = 30_000_000_000_000_000;
375pub const DEFAULT_NUMBER_OF_AUTHORITIES: usize = 4;
376const DEFAULT_NUMBER_OF_ACCOUNT: usize = 5;
377pub const DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT: usize = 5;
378
379impl GenesisConfig {
380 pub const BENCHMARKS_RNG_SEED: u64 = 0;
383 pub const BENCHMARKS_PORT_OFFSET: u16 = 2000;
385 const BENCHMARK_EPOCH_DURATION_MS: u64 = 60 * 60 * 1000;
387
388 pub fn for_local_testing() -> Self {
389 Self::custom_genesis(
390 DEFAULT_NUMBER_OF_ACCOUNT,
391 DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT,
392 )
393 }
394
395 pub fn for_local_testing_with_addresses(addresses: Vec<Address>) -> Self {
396 Self::custom_genesis_with_addresses(addresses, DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT)
397 }
398
399 pub fn custom_genesis(num_accounts: usize, num_objects_per_account: usize) -> Self {
400 let mut accounts = Vec::new();
401 for _ in 0..num_accounts {
402 accounts.push(AccountConfig {
403 address: None,
404 gas_amounts: vec![DEFAULT_GAS_AMOUNT; num_objects_per_account],
405 })
406 }
407
408 Self {
409 accounts,
410 ..Default::default()
411 }
412 }
413
414 pub fn custom_genesis_with_addresses(
415 addresses: Vec<Address>,
416 num_objects_per_account: usize,
417 ) -> Self {
418 let mut accounts = Vec::new();
419 for address in addresses {
420 accounts.push(AccountConfig {
421 address: Some(address),
422 gas_amounts: vec![DEFAULT_GAS_AMOUNT; num_objects_per_account],
423 })
424 }
425
426 Self {
427 accounts,
428 ..Default::default()
429 }
430 }
431
432 pub fn new_for_benchmarks(
446 ips: &[String],
447 epoch_duration_ms: Option<u64>,
448 chain_start_timestamp_ms: Option<u64>,
449 num_additional_gas_accounts: Option<usize>,
450 total_available_amount: u64,
451 ) -> Self {
452 assert!(
454 total_available_amount < u64::MAX,
455 "Total available amount must be less than 18446744073709551615u64"
456 );
457 let mut rng = StdRng::seed_from_u64(Self::BENCHMARKS_RNG_SEED);
460 let validator_config_info: Vec<_> = ips
461 .iter()
462 .enumerate()
463 .map(|(i, ip)| {
464 ValidatorGenesisConfigBuilder::new()
465 .with_ip(ip.to_string())
466 .with_deterministic_ports(Self::BENCHMARKS_PORT_OFFSET + 10 * i as u16)
467 .with_p2p_listen_ip_address("0.0.0.0".parse().unwrap())
468 .build(&mut rng)
469 })
470 .collect();
471
472 let num_validators = validator_config_info.len();
474 let num_accounts = num_additional_gas_accounts.unwrap_or(0) + num_validators;
475
476 let total_gas_objects = num_accounts * DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT;
478 let gas_amount_per_object = if total_gas_objects > 0 {
479 total_available_amount / total_gas_objects as u64
480 } else {
481 0
482 };
483
484 let account_configs = Self::benchmark_gas_keys(num_accounts)
485 .iter()
486 .map(|gas_key| {
487 let gas_address = Address::from(&PublicKey::from(gas_key));
488
489 AccountConfig {
490 address: Some(gas_address),
491 gas_amounts: vec![gas_amount_per_object; DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT],
492 }
493 })
494 .collect();
495
496 let parameters = GenesisCeremonyParameters {
499 chain_start_timestamp_ms: chain_start_timestamp_ms.unwrap_or(0),
500 epoch_duration_ms: if let Some(duration_ms) = epoch_duration_ms {
501 duration_ms
502 } else {
503 Self::BENCHMARK_EPOCH_DURATION_MS
504 },
505 ..GenesisCeremonyParameters::new()
506 };
507
508 GenesisConfig {
510 ssfn_config_info: None,
511 validator_config_info: Some(validator_config_info),
512 fullnode_config_info: None,
513 parameters,
514 accounts: account_configs,
515 }
516 }
517
518 pub fn benchmark_gas_keys(n: usize) -> Vec<SimpleKeypair> {
523 let mut rng = StdRng::seed_from_u64(Self::BENCHMARKS_RNG_SEED);
524 (0..n)
525 .map(|_| SimpleKeypair::from(AccountPrivateKey::random_with(&mut rng)))
526 .collect()
527 }
528
529 pub fn add_faucet_account(mut self) -> Self {
530 self.accounts.push(AccountConfig {
531 address: None,
532 gas_amounts: vec![DEFAULT_GAS_AMOUNT; DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT],
533 });
534 self
535 }
536}
537
538#[cfg(test)]
539mod tests {
540 use std::net::Ipv4Addr;
541
542 use rand::rngs::OsRng;
543
544 use super::ValidatorGenesisConfigBuilder;
545
546 #[test]
547 fn deterministic_ports_fill_the_five_slots_of_a_validator() {
548 let config = ValidatorGenesisConfigBuilder::new()
549 .with_ip("127.0.0.1".to_owned())
550 .with_deterministic_ports(9200)
551 .with_metrics_ip_address(Ipv4Addr::LOCALHOST.into())
552 .build(&mut OsRng);
553
554 assert_eq!(
555 config.network_address.to_string(),
556 "/ip4/127.0.0.1/tcp/9200/http"
557 );
558 assert_eq!(
559 config.p2p_address.to_string(),
560 "/ip4/127.0.0.1/udp/9201/http"
561 );
562 assert_eq!(config.metrics_address.to_string(), "127.0.0.1:9202");
563 assert_eq!(
564 config.primary_address.to_string(),
565 "/ip4/127.0.0.1/udp/9203/http"
566 );
567 assert_eq!(config.admin_interface_address.to_string(), "127.0.0.1:9204");
568 }
569
570 #[test]
571 fn the_metrics_endpoint_binds_all_interfaces_unless_an_ip_is_given() {
572 let config = ValidatorGenesisConfigBuilder::new()
573 .with_ip("127.0.0.1".to_owned())
574 .with_deterministic_ports(9200)
575 .build(&mut OsRng);
576
577 assert_eq!(config.metrics_address.to_string(), "0.0.0.0:9202");
578 }
579}