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 to_validator_info(&self, name: String) -> GenesisValidatorInfo {
66 let authority_key: AuthorityPublicKeyBytes = self.authority_key_pair.public().into();
67 let account_key = PublicKey::from(&self.account_key_pair);
68 let network_key: NetworkPublicKey = self.network_key_pair.public().clone();
69 let protocol_key: NetworkPublicKey = self.protocol_key_pair.public().clone();
70 let network_address = self.network_address.clone();
71
72 let info = ValidatorInfo {
73 name,
74 authority_key,
75 protocol_key,
76 network_key,
77 account_address: Address::from(&account_key),
78 gas_price: self.gas_price,
79 commission_rate: self.commission_rate,
80 network_address,
81 p2p_address: self.p2p_address.clone(),
82 primary_address: self.primary_address.clone(),
83 description: String::new(),
84 image_url: String::new(),
85 project_url: String::new(),
86 };
87 let proof_of_possession = generate_proof_of_possession(
88 &self.authority_key_pair,
89 (&PublicKey::from(&self.account_key_pair)).into(),
90 );
91 GenesisValidatorInfo {
92 info,
93 proof_of_possession,
94 }
95 }
96
97 pub fn to_validator_info_with_random_name(&self) -> GenesisValidatorInfo {
99 self.to_validator_info(self.authority_key_pair.public().to_string())
100 }
101}
102
103#[derive(Default)]
104pub struct ValidatorGenesisConfigBuilder {
105 authority_key_pair: Option<AuthorityKeyPair>,
106 account_private_key: Option<AccountPrivateKey>,
107 ip: Option<String>,
108 gas_price: Option<u64>,
109 port_offset: Option<u16>,
112 p2p_listen_ip_address: Option<IpAddr>,
115 metrics_ip_address: Option<IpAddr>,
116}
117
118impl ValidatorGenesisConfigBuilder {
119 pub fn new() -> Self {
120 Self::default()
121 }
122
123 pub fn with_authority_key_pair(mut self, key_pair: AuthorityKeyPair) -> Self {
124 self.authority_key_pair = Some(key_pair);
125 self
126 }
127
128 pub fn with_account_private_key(mut self, private_key: AccountPrivateKey) -> Self {
129 self.account_private_key = Some(private_key);
130 self
131 }
132
133 pub fn with_ip(mut self, ip: String) -> Self {
134 self.ip = Some(ip);
135 self
136 }
137
138 pub fn with_gas_price(mut self, gas_price: u64) -> Self {
139 self.gas_price = Some(gas_price);
140 self
141 }
142
143 pub fn with_deterministic_ports(mut self, port_offset: u16) -> Self {
144 self.port_offset = Some(port_offset);
145 self
146 }
147
148 pub fn with_p2p_listen_ip_address(mut self, p2p_listen_ip_address: IpAddr) -> Self {
149 self.p2p_listen_ip_address = Some(p2p_listen_ip_address);
150 self
151 }
152
153 pub fn with_metrics_ip_address(mut self, metrics_ip_address: IpAddr) -> Self {
157 self.metrics_ip_address = Some(metrics_ip_address);
158 self
159 }
160
161 pub fn build<R: rand::RngCore + rand::CryptoRng>(self, rng: &mut R) -> ValidatorGenesisConfig {
162 let ip = self.ip.unwrap_or_else(local_ip_utils::get_new_ip);
163 let localhost = local_ip_utils::localhost_for_testing();
164
165 let authority_key_pair = self
166 .authority_key_pair
167 .unwrap_or_else(|| get_key_pair_from_rng(rng).1);
168 let account_private_key = self
169 .account_private_key
170 .unwrap_or_else(|| get_key_pair_from_rng(rng).1);
171 let gas_price = self.gas_price.unwrap_or(DEFAULT_VALIDATOR_GAS_PRICE);
172
173 let (protocol_key_pair, network_key_pair): (NetworkKeyPair, NetworkKeyPair) =
174 (get_key_pair_from_rng(rng).1, get_key_pair_from_rng(rng).1);
175
176 let metrics_ip = self.metrics_ip_address.map(|ip| ip.to_string());
177
178 let (
179 network_address,
180 p2p_address,
181 metrics_address,
182 primary_address,
183 admin_interface_address,
184 ) = if let Some(offset) = self.port_offset {
185 (
186 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset),
187 local_ip_utils::new_deterministic_udp_address_for_testing(&ip, offset + 1),
188 match &metrics_ip {
189 Some(metrics_ip) => local_ip_utils::new_deterministic_tcp_address_for_testing(
190 metrics_ip,
191 offset + 2,
192 ),
193 None => {
194 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset + 2)
195 .with_zero_ip()
196 }
197 },
198 local_ip_utils::new_deterministic_udp_address_for_testing(&ip, offset + 3),
199 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset + 4),
200 )
201 } else {
202 (
203 local_ip_utils::new_tcp_address_for_testing(&ip),
204 local_ip_utils::new_udp_address_for_testing(&ip),
205 local_ip_utils::new_tcp_address_for_testing(
206 metrics_ip.as_deref().unwrap_or(&localhost),
207 ),
208 local_ip_utils::new_udp_address_for_testing(&ip),
209 local_ip_utils::new_tcp_address_for_testing(&localhost),
210 )
211 };
212
213 let p2p_listen_address = self
214 .p2p_listen_ip_address
215 .map(|ip| SocketAddr::new(ip, p2p_address.port().unwrap()));
216
217 ValidatorGenesisConfig {
218 authority_key_pair,
219 protocol_key_pair,
220 account_key_pair: account_private_key.into(),
221 network_key_pair,
222 network_address,
223 p2p_address,
224 p2p_listen_address,
225 metrics_address: metrics_address.to_socket_addr().unwrap(),
226 admin_interface_address: admin_interface_address.to_socket_addr().unwrap(),
227 gas_price,
228 commission_rate: DEFAULT_COMMISSION_RATE,
229 primary_address,
230 stake: ProtocolConfig::get_for_version(ProtocolVersion::MAX, Chain::Unknown)
234 .validator_low_stake_threshold_as_option()
235 .unwrap_or(PRE_V32_VALIDATOR_LOW_STAKE_THRESHOLD),
236 name: None,
237 }
238 }
239}
240
241#[derive(Serialize, Deserialize, Default)]
242pub struct GenesisConfig {
243 pub ssfn_config_info: Option<Vec<SsfnGenesisConfig>>,
244 pub validator_config_info: Option<Vec<ValidatorGenesisConfig>>,
245 pub parameters: GenesisCeremonyParameters,
246 pub accounts: Vec<AccountConfig>,
247}
248
249impl Config for GenesisConfig {}
250
251impl GenesisConfig {
252 pub fn protocol_config(&self) -> ProtocolConfig {
254 ProtocolConfig::get_for_version(self.parameters.protocol_version, Chain::Unknown)
255 }
256
257 pub fn generate_accounts<R: rand::RngCore + rand::CryptoRng>(
258 &self,
259 mut rng: R,
260 ) -> Result<(Vec<AccountPrivateKey>, Vec<TokenAllocation>)> {
261 let mut addresses = Vec::new();
262 let mut allocations = Vec::new();
263
264 info!("Creating accounts and token allocations...");
265
266 let mut keys = Vec::new();
267 for account in &self.accounts {
268 let address = if let Some(address) = account.address {
269 address
270 } else {
271 let (address, key) = get_key_pair_from_rng(&mut rng);
272 keys.push(key);
273 address
274 };
275
276 addresses.push(address);
277
278 account.gas_amounts.iter().for_each(|a| {
280 allocations.push(TokenAllocation {
281 recipient_address: address,
282 amount_nanos: *a,
283 staked_with_validator: None,
284 staked_with_timelock_expiration: None,
285 });
286 });
287 }
288
289 Ok((keys, allocations))
290 }
291}
292
293fn default_socket_address() -> SocketAddr {
294 local_ip_utils::new_local_tcp_socket_for_testing()
295}
296
297fn default_stake() -> u64 {
298 ProtocolConfig::get_for_version(ProtocolVersion::MAX, Chain::Unknown)
302 .validator_low_stake_threshold_as_option()
303 .unwrap_or(PRE_V32_VALIDATOR_LOW_STAKE_THRESHOLD)
304}
305
306fn default_bls12381_key_pair() -> AuthorityKeyPair {
307 get_key_pair_from_rng(&mut rand::rngs::OsRng).1
308}
309
310fn default_ed25519_key_pair() -> NetworkKeyPair {
311 get_key_pair_from_rng(&mut rand::rngs::OsRng).1
312}
313
314fn default_iota_key_pair() -> SimpleKeypair {
315 SimpleKeypair::from(AccountPrivateKey::random())
316}
317
318mod base64_formatted_keypair {
321 use fastcrypto::encoding::{Base64, Encoding};
322 use iota_sdk_crypto::simple::SimpleKeypair;
323 use serde::{Deserialize, Deserializer, Serializer};
324
325 pub fn serialize<S: Serializer>(kp: &SimpleKeypair, serializer: S) -> Result<S::Ok, S::Error> {
326 serializer.serialize_str(&Base64::encode(kp.to_bytes()))
327 }
328
329 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<SimpleKeypair, D::Error> {
330 use serde::de::Error;
331
332 let s = String::deserialize(d)?;
333 let bytes = Base64::decode(&s).map_err(Error::custom)?;
334 SimpleKeypair::from_bytes(&bytes).map_err(Error::custom)
335 }
336}
337
338#[derive(Serialize, Deserialize, Debug, Clone)]
339pub struct AccountConfig {
340 #[serde(skip_serializing_if = "Option::is_none")]
341 pub address: Option<Address>,
342 pub gas_amounts: Vec<u64>,
343}
344
345pub const DEFAULT_GAS_AMOUNT: u64 = 30_000_000_000_000_000;
346pub const DEFAULT_NUMBER_OF_AUTHORITIES: usize = 4;
347const DEFAULT_NUMBER_OF_ACCOUNT: usize = 5;
348pub const DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT: usize = 5;
349
350impl GenesisConfig {
351 pub const BENCHMARKS_RNG_SEED: u64 = 0;
354 pub const BENCHMARKS_PORT_OFFSET: u16 = 2000;
356 const BENCHMARK_EPOCH_DURATION_MS: u64 = 60 * 60 * 1000;
358
359 pub fn for_local_testing() -> Self {
360 Self::custom_genesis(
361 DEFAULT_NUMBER_OF_ACCOUNT,
362 DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT,
363 )
364 }
365
366 pub fn for_local_testing_with_addresses(addresses: Vec<Address>) -> Self {
367 Self::custom_genesis_with_addresses(addresses, DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT)
368 }
369
370 pub fn custom_genesis(num_accounts: usize, num_objects_per_account: usize) -> Self {
371 let mut accounts = Vec::new();
372 for _ in 0..num_accounts {
373 accounts.push(AccountConfig {
374 address: None,
375 gas_amounts: vec![DEFAULT_GAS_AMOUNT; num_objects_per_account],
376 })
377 }
378
379 Self {
380 accounts,
381 ..Default::default()
382 }
383 }
384
385 pub fn custom_genesis_with_addresses(
386 addresses: Vec<Address>,
387 num_objects_per_account: usize,
388 ) -> Self {
389 let mut accounts = Vec::new();
390 for address in addresses {
391 accounts.push(AccountConfig {
392 address: Some(address),
393 gas_amounts: vec![DEFAULT_GAS_AMOUNT; num_objects_per_account],
394 })
395 }
396
397 Self {
398 accounts,
399 ..Default::default()
400 }
401 }
402
403 pub fn new_for_benchmarks(
417 ips: &[String],
418 epoch_duration_ms: Option<u64>,
419 chain_start_timestamp_ms: Option<u64>,
420 num_additional_gas_accounts: Option<usize>,
421 total_available_amount: u64,
422 ) -> Self {
423 assert!(
425 total_available_amount < u64::MAX,
426 "Total available amount must be less than 18446744073709551615u64"
427 );
428 let mut rng = StdRng::seed_from_u64(Self::BENCHMARKS_RNG_SEED);
431 let validator_config_info: Vec<_> = ips
432 .iter()
433 .enumerate()
434 .map(|(i, ip)| {
435 ValidatorGenesisConfigBuilder::new()
436 .with_ip(ip.to_string())
437 .with_deterministic_ports(Self::BENCHMARKS_PORT_OFFSET + 10 * i as u16)
438 .with_p2p_listen_ip_address("0.0.0.0".parse().unwrap())
439 .build(&mut rng)
440 })
441 .collect();
442
443 let num_validators = validator_config_info.len();
445 let num_accounts = num_additional_gas_accounts.unwrap_or(0) + num_validators;
446
447 let total_gas_objects = num_accounts * DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT;
449 let gas_amount_per_object = if total_gas_objects > 0 {
450 total_available_amount / total_gas_objects as u64
451 } else {
452 0
453 };
454
455 let account_configs = Self::benchmark_gas_keys(num_accounts)
456 .iter()
457 .map(|gas_key| {
458 let gas_address = Address::from(&PublicKey::from(gas_key));
459
460 AccountConfig {
461 address: Some(gas_address),
462 gas_amounts: vec![gas_amount_per_object; DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT],
463 }
464 })
465 .collect();
466
467 let parameters = GenesisCeremonyParameters {
470 chain_start_timestamp_ms: chain_start_timestamp_ms.unwrap_or(0),
471 epoch_duration_ms: if let Some(duration_ms) = epoch_duration_ms {
472 duration_ms
473 } else {
474 Self::BENCHMARK_EPOCH_DURATION_MS
475 },
476 ..GenesisCeremonyParameters::new()
477 };
478
479 GenesisConfig {
481 ssfn_config_info: None,
482 validator_config_info: Some(validator_config_info),
483 parameters,
484 accounts: account_configs,
485 }
486 }
487
488 pub fn benchmark_gas_keys(n: usize) -> Vec<SimpleKeypair> {
493 let mut rng = StdRng::seed_from_u64(Self::BENCHMARKS_RNG_SEED);
494 (0..n)
495 .map(|_| SimpleKeypair::from(AccountPrivateKey::random_with(&mut rng)))
496 .collect()
497 }
498
499 pub fn add_faucet_account(mut self) -> Self {
500 self.accounts.push(AccountConfig {
501 address: None,
502 gas_amounts: vec![DEFAULT_GAS_AMOUNT; DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT],
503 });
504 self
505 }
506}
507
508#[cfg(test)]
509mod tests {
510 use std::net::Ipv4Addr;
511
512 use rand::rngs::OsRng;
513
514 use super::ValidatorGenesisConfigBuilder;
515
516 #[test]
517 fn deterministic_ports_fill_the_five_slots_of_a_validator() {
518 let config = ValidatorGenesisConfigBuilder::new()
519 .with_ip("127.0.0.1".to_owned())
520 .with_deterministic_ports(9200)
521 .with_metrics_ip_address(Ipv4Addr::LOCALHOST.into())
522 .build(&mut OsRng);
523
524 assert_eq!(
525 config.network_address.to_string(),
526 "/ip4/127.0.0.1/tcp/9200/http"
527 );
528 assert_eq!(
529 config.p2p_address.to_string(),
530 "/ip4/127.0.0.1/udp/9201/http"
531 );
532 assert_eq!(config.metrics_address.to_string(), "127.0.0.1:9202");
533 assert_eq!(
534 config.primary_address.to_string(),
535 "/ip4/127.0.0.1/udp/9203/http"
536 );
537 assert_eq!(config.admin_interface_address.to_string(), "127.0.0.1:9204");
538 }
539
540 #[test]
541 fn the_metrics_endpoint_binds_all_interfaces_unless_an_ip_is_given() {
542 let config = ValidatorGenesisConfigBuilder::new()
543 .with_ip("127.0.0.1".to_owned())
544 .with_deterministic_ports(9200)
545 .build(&mut OsRng);
546
547 assert_eq!(config.metrics_address.to_string(), "0.0.0.0:9202");
548 }
549}