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