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::{
16 SnapshotSource,
17 validator_info::{GenesisValidatorInfo, ValidatorInfo},
18};
19use iota_protocol_config::{Chain, ProtocolConfig};
20use iota_sdk_types::Address;
21use iota_types::{
22 committee::ProtocolVersion,
23 crypto::{
24 AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes, IotaKeyPair, NetworkKeyPair,
25 NetworkPublicKey, PublicKey, generate_proof_of_possession, get_key_pair_from_rng,
26 },
27 multiaddr::Multiaddr,
28};
29use rand::{SeedableRng, rngs::StdRng};
30use serde::{Deserialize, Serialize};
31use tracing::info;
32
33#[derive(Serialize, Deserialize, Debug)]
35pub struct SsfnGenesisConfig {
36 pub p2p_address: Multiaddr,
37 pub network_key_pair: Option<NetworkKeyPair>,
38}
39
40#[derive(Serialize, Deserialize)]
42pub struct ValidatorGenesisConfig {
43 #[serde(default = "default_bls12381_key_pair")]
44 pub authority_key_pair: AuthorityKeyPair,
45 #[serde(default = "default_ed25519_key_pair")]
46 pub protocol_key_pair: NetworkKeyPair,
47 #[serde(default = "default_iota_key_pair")]
48 pub account_key_pair: IotaKeyPair,
49 #[serde(default = "default_ed25519_key_pair")]
50 pub network_key_pair: NetworkKeyPair,
51 pub network_address: Multiaddr,
52 pub p2p_address: Multiaddr,
53 pub p2p_listen_address: Option<SocketAddr>,
54 #[serde(default = "default_socket_address")]
55 pub metrics_address: SocketAddr,
56 #[serde(default = "default_socket_address")]
57 pub admin_interface_address: SocketAddr,
58 pub gas_price: u64,
59 pub commission_rate: u64,
60 pub primary_address: Multiaddr,
61 #[serde(default = "default_stake")]
62 pub stake: u64,
63 pub name: Option<String>,
64}
65
66impl ValidatorGenesisConfig {
67 pub fn to_validator_info(&self, name: String) -> GenesisValidatorInfo {
68 let authority_key: AuthorityPublicKeyBytes = self.authority_key_pair.public().into();
69 let account_key: PublicKey = self.account_key_pair.public();
70 let network_key: NetworkPublicKey = self.network_key_pair.public().clone();
71 let protocol_key: NetworkPublicKey = self.protocol_key_pair.public().clone();
72 let network_address = self.network_address.clone();
73
74 let info = ValidatorInfo {
75 name,
76 authority_key,
77 protocol_key,
78 network_key,
79 account_address: Address::from(&account_key),
80 gas_price: self.gas_price,
81 commission_rate: self.commission_rate,
82 network_address,
83 p2p_address: self.p2p_address.clone(),
84 primary_address: self.primary_address.clone(),
85 description: String::new(),
86 image_url: String::new(),
87 project_url: String::new(),
88 };
89 let proof_of_possession = generate_proof_of_possession(
90 &self.authority_key_pair,
91 (&self.account_key_pair.public()).into(),
92 );
93 GenesisValidatorInfo {
94 info,
95 proof_of_possession,
96 }
97 }
98
99 pub fn to_validator_info_with_random_name(&self) -> GenesisValidatorInfo {
101 self.to_validator_info(self.authority_key_pair.public().to_string())
102 }
103}
104
105#[derive(Default)]
106pub struct ValidatorGenesisConfigBuilder {
107 authority_key_pair: Option<AuthorityKeyPair>,
108 account_key_pair: Option<AccountKeyPair>,
109 ip: Option<String>,
110 gas_price: Option<u64>,
111 port_offset: Option<u16>,
114 p2p_listen_ip_address: Option<IpAddr>,
117}
118
119impl ValidatorGenesisConfigBuilder {
120 pub fn new() -> Self {
121 Self::default()
122 }
123
124 pub fn with_authority_key_pair(mut self, key_pair: AuthorityKeyPair) -> Self {
125 self.authority_key_pair = Some(key_pair);
126 self
127 }
128
129 pub fn with_account_key_pair(mut self, key_pair: AccountKeyPair) -> Self {
130 self.account_key_pair = Some(key_pair);
131 self
132 }
133
134 pub fn with_ip(mut self, ip: String) -> Self {
135 self.ip = Some(ip);
136 self
137 }
138
139 pub fn with_gas_price(mut self, gas_price: u64) -> Self {
140 self.gas_price = Some(gas_price);
141 self
142 }
143
144 pub fn with_deterministic_ports(mut self, port_offset: u16) -> Self {
145 self.port_offset = Some(port_offset);
146 self
147 }
148
149 pub fn with_p2p_listen_ip_address(mut self, p2p_listen_ip_address: IpAddr) -> Self {
150 self.p2p_listen_ip_address = Some(p2p_listen_ip_address);
151 self
152 }
153
154 pub fn build<R: rand::RngCore + rand::CryptoRng>(self, rng: &mut R) -> ValidatorGenesisConfig {
155 let ip = self.ip.unwrap_or_else(local_ip_utils::get_new_ip);
156 let localhost = local_ip_utils::localhost_for_testing();
157
158 let authority_key_pair = self
159 .authority_key_pair
160 .unwrap_or_else(|| get_key_pair_from_rng(rng).1);
161 let account_key_pair = self
162 .account_key_pair
163 .unwrap_or_else(|| get_key_pair_from_rng(rng).1);
164 let gas_price = self.gas_price.unwrap_or(DEFAULT_VALIDATOR_GAS_PRICE);
165
166 let (protocol_key_pair, network_key_pair): (NetworkKeyPair, NetworkKeyPair) =
167 (get_key_pair_from_rng(rng).1, get_key_pair_from_rng(rng).1);
168
169 let (
170 network_address,
171 p2p_address,
172 metrics_address,
173 primary_address,
174 admin_interface_address,
175 ) = if let Some(offset) = self.port_offset {
176 (
177 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset),
178 local_ip_utils::new_deterministic_udp_address_for_testing(&ip, offset + 1),
179 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset + 2)
180 .with_zero_ip(),
181 local_ip_utils::new_deterministic_udp_address_for_testing(&ip, offset + 3),
182 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset + 4),
183 )
184 } else {
185 (
186 local_ip_utils::new_tcp_address_for_testing(&ip),
187 local_ip_utils::new_udp_address_for_testing(&ip),
188 local_ip_utils::new_tcp_address_for_testing(&localhost),
189 local_ip_utils::new_udp_address_for_testing(&ip),
190 local_ip_utils::new_tcp_address_for_testing(&localhost),
191 )
192 };
193
194 let p2p_listen_address = self
195 .p2p_listen_ip_address
196 .map(|ip| SocketAddr::new(ip, p2p_address.port().unwrap()));
197
198 ValidatorGenesisConfig {
199 authority_key_pair,
200 protocol_key_pair,
201 account_key_pair: account_key_pair.into(),
202 network_key_pair,
203 network_address,
204 p2p_address,
205 p2p_listen_address,
206 metrics_address: metrics_address.to_socket_addr().unwrap(),
207 admin_interface_address: admin_interface_address.to_socket_addr().unwrap(),
208 gas_price,
209 commission_rate: DEFAULT_COMMISSION_RATE,
210 primary_address,
211 stake: ProtocolConfig::get_for_version(ProtocolVersion::MAX, Chain::Unknown)
215 .validator_low_stake_threshold_as_option()
216 .unwrap_or(PRE_V32_VALIDATOR_LOW_STAKE_THRESHOLD),
217 name: None,
218 }
219 }
220}
221
222#[derive(Serialize, Deserialize, Default)]
223pub struct GenesisConfig {
224 pub ssfn_config_info: Option<Vec<SsfnGenesisConfig>>,
225 pub validator_config_info: Option<Vec<ValidatorGenesisConfig>>,
226 pub parameters: GenesisCeremonyParameters,
227 pub accounts: Vec<AccountConfig>,
228 pub migration_sources: Vec<SnapshotSource>,
229 pub delegator: Option<Address>,
230}
231
232impl Config for GenesisConfig {}
233
234impl GenesisConfig {
235 pub fn protocol_config(&self) -> ProtocolConfig {
237 ProtocolConfig::get_for_version(self.parameters.protocol_version, Chain::Unknown)
238 }
239
240 pub fn generate_accounts<R: rand::RngCore + rand::CryptoRng>(
241 &self,
242 mut rng: R,
243 ) -> Result<(Vec<AccountKeyPair>, Vec<TokenAllocation>)> {
244 let mut addresses = Vec::new();
245 let mut allocations = Vec::new();
246
247 info!("Creating accounts and token allocations...");
248
249 let mut keys = Vec::new();
250 for account in &self.accounts {
251 let address = if let Some(address) = account.address {
252 address
253 } else {
254 let (address, keypair) = get_key_pair_from_rng(&mut rng);
255 keys.push(keypair);
256 address
257 };
258
259 addresses.push(address);
260
261 account.gas_amounts.iter().for_each(|a| {
263 allocations.push(TokenAllocation {
264 recipient_address: address,
265 amount_nanos: *a,
266 staked_with_validator: None,
267 staked_with_timelock_expiration: None,
268 });
269 });
270 }
271
272 Ok((keys, allocations))
273 }
274}
275
276fn default_socket_address() -> SocketAddr {
277 local_ip_utils::new_local_tcp_socket_for_testing()
278}
279
280fn default_stake() -> u64 {
281 ProtocolConfig::get_for_version(ProtocolVersion::MAX, Chain::Unknown)
285 .validator_low_stake_threshold_as_option()
286 .unwrap_or(PRE_V32_VALIDATOR_LOW_STAKE_THRESHOLD)
287}
288
289fn default_bls12381_key_pair() -> AuthorityKeyPair {
290 get_key_pair_from_rng(&mut rand::rngs::OsRng).1
291}
292
293fn default_ed25519_key_pair() -> NetworkKeyPair {
294 get_key_pair_from_rng(&mut rand::rngs::OsRng).1
295}
296
297fn default_iota_key_pair() -> IotaKeyPair {
298 IotaKeyPair::Ed25519(get_key_pair_from_rng(&mut rand::rngs::OsRng).1)
299}
300
301#[derive(Serialize, Deserialize, Debug, Clone)]
302pub struct AccountConfig {
303 #[serde(skip_serializing_if = "Option::is_none")]
304 pub address: Option<Address>,
305 pub gas_amounts: Vec<u64>,
306}
307
308pub const DEFAULT_GAS_AMOUNT: u64 = 30_000_000_000_000_000;
309pub const DEFAULT_NUMBER_OF_AUTHORITIES: usize = 4;
310const DEFAULT_NUMBER_OF_ACCOUNT: usize = 5;
311pub const DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT: usize = 5;
312
313impl GenesisConfig {
314 pub const BENCHMARKS_RNG_SEED: u64 = 0;
317 pub const BENCHMARKS_PORT_OFFSET: u16 = 2000;
319 const BENCHMARK_EPOCH_DURATION_MS: u64 = 60 * 60 * 1000;
321
322 pub fn for_local_testing() -> Self {
323 Self::custom_genesis(
324 DEFAULT_NUMBER_OF_ACCOUNT,
325 DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT,
326 )
327 }
328
329 pub fn for_local_testing_with_addresses(addresses: Vec<Address>) -> Self {
330 Self::custom_genesis_with_addresses(addresses, DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT)
331 }
332
333 pub fn custom_genesis(num_accounts: usize, num_objects_per_account: usize) -> Self {
334 let mut accounts = Vec::new();
335 for _ in 0..num_accounts {
336 accounts.push(AccountConfig {
337 address: None,
338 gas_amounts: vec![DEFAULT_GAS_AMOUNT; num_objects_per_account],
339 })
340 }
341
342 Self {
343 accounts,
344 ..Default::default()
345 }
346 }
347
348 pub fn custom_genesis_with_addresses(
349 addresses: Vec<Address>,
350 num_objects_per_account: usize,
351 ) -> Self {
352 let mut accounts = Vec::new();
353 for address in addresses {
354 accounts.push(AccountConfig {
355 address: Some(address),
356 gas_amounts: vec![DEFAULT_GAS_AMOUNT; num_objects_per_account],
357 })
358 }
359
360 Self {
361 accounts,
362 ..Default::default()
363 }
364 }
365
366 pub fn new_for_benchmarks(
380 ips: &[String],
381 epoch_duration_ms: Option<u64>,
382 chain_start_timestamp_ms: Option<u64>,
383 num_additional_gas_accounts: Option<usize>,
384 total_available_amount: u64,
385 ) -> Self {
386 assert!(
388 total_available_amount < u64::MAX,
389 "Total available amount must be less than 18446744073709551615u64"
390 );
391 let mut rng = StdRng::seed_from_u64(Self::BENCHMARKS_RNG_SEED);
394 let validator_config_info: Vec<_> = ips
395 .iter()
396 .enumerate()
397 .map(|(i, ip)| {
398 ValidatorGenesisConfigBuilder::new()
399 .with_ip(ip.to_string())
400 .with_deterministic_ports(Self::BENCHMARKS_PORT_OFFSET + 10 * i as u16)
401 .with_p2p_listen_ip_address("0.0.0.0".parse().unwrap())
402 .build(&mut rng)
403 })
404 .collect();
405
406 let num_validators = validator_config_info.len();
408 let num_accounts = num_additional_gas_accounts.unwrap_or(0) + num_validators;
409
410 let total_gas_objects = num_accounts * DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT;
412 let gas_amount_per_object = if total_gas_objects > 0 {
413 total_available_amount / total_gas_objects as u64
414 } else {
415 0
416 };
417
418 let account_configs = Self::benchmark_gas_keys(num_accounts)
419 .iter()
420 .map(|gas_key| {
421 let gas_address = Address::from(&gas_key.public());
422
423 AccountConfig {
424 address: Some(gas_address),
425 gas_amounts: vec![gas_amount_per_object; DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT],
426 }
427 })
428 .collect();
429
430 let parameters = GenesisCeremonyParameters {
433 chain_start_timestamp_ms: chain_start_timestamp_ms.unwrap_or(0),
434 epoch_duration_ms: if let Some(duration_ms) = epoch_duration_ms {
435 duration_ms
436 } else {
437 Self::BENCHMARK_EPOCH_DURATION_MS
438 },
439 ..GenesisCeremonyParameters::new()
440 };
441
442 GenesisConfig {
444 ssfn_config_info: None,
445 validator_config_info: Some(validator_config_info),
446 parameters,
447 accounts: account_configs,
448 migration_sources: Default::default(),
449 delegator: Default::default(),
450 }
451 }
452
453 pub fn benchmark_gas_keys(n: usize) -> Vec<IotaKeyPair> {
458 let mut rng = StdRng::seed_from_u64(Self::BENCHMARKS_RNG_SEED);
459 (0..n)
460 .map(|_| IotaKeyPair::Ed25519(AccountKeyPair::generate(&mut rng)))
461 .collect()
462 }
463
464 pub fn add_faucet_account(mut self) -> Self {
465 self.accounts.push(AccountConfig {
466 address: None,
467 gas_amounts: vec![DEFAULT_GAS_AMOUNT; DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT],
468 });
469 self
470 }
471
472 pub fn add_delegator(mut self, address: Address) -> Self {
473 self.delegator = Some(address);
474 self
475 }
476}