1use std::net::{IpAddr, SocketAddr};
6
7use anyhow::Result;
8use fastcrypto::traits::KeyPair;
9use iota_config::{
10 Config,
11 genesis::{GenesisCeremonyParameters, 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_types::{
20 base_types::IotaAddress,
21 crypto::{
22 AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes, IotaKeyPair, NetworkKeyPair,
23 NetworkPublicKey, PublicKey, generate_proof_of_possession, get_key_pair_from_rng,
24 },
25 multiaddr::Multiaddr,
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")]
46 pub account_key_pair: IotaKeyPair,
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 = self.account_key_pair.public();
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: IotaAddress::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 (&self.account_key_pair.public()).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_key_pair: Option<AccountKeyPair>,
107 ip: Option<String>,
108 gas_price: Option<u64>,
109 port_offset: Option<u16>,
112 p2p_listen_ip_address: Option<IpAddr>,
115}
116
117impl ValidatorGenesisConfigBuilder {
118 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn with_authority_key_pair(mut self, key_pair: AuthorityKeyPair) -> Self {
123 self.authority_key_pair = Some(key_pair);
124 self
125 }
126
127 pub fn with_account_key_pair(mut self, key_pair: AccountKeyPair) -> Self {
128 self.account_key_pair = Some(key_pair);
129 self
130 }
131
132 pub fn with_ip(mut self, ip: String) -> Self {
133 self.ip = Some(ip);
134 self
135 }
136
137 pub fn with_gas_price(mut self, gas_price: u64) -> Self {
138 self.gas_price = Some(gas_price);
139 self
140 }
141
142 pub fn with_deterministic_ports(mut self, port_offset: u16) -> Self {
143 self.port_offset = Some(port_offset);
144 self
145 }
146
147 pub fn with_p2p_listen_ip_address(mut self, p2p_listen_ip_address: IpAddr) -> Self {
148 self.p2p_listen_ip_address = Some(p2p_listen_ip_address);
149 self
150 }
151
152 pub fn build<R: rand::RngCore + rand::CryptoRng>(self, rng: &mut R) -> ValidatorGenesisConfig {
153 let ip = self.ip.unwrap_or_else(local_ip_utils::get_new_ip);
154 let localhost = local_ip_utils::localhost_for_testing();
155
156 let authority_key_pair = self
157 .authority_key_pair
158 .unwrap_or_else(|| get_key_pair_from_rng(rng).1);
159 let account_key_pair = self
160 .account_key_pair
161 .unwrap_or_else(|| get_key_pair_from_rng(rng).1);
162 let gas_price = self.gas_price.unwrap_or(DEFAULT_VALIDATOR_GAS_PRICE);
163
164 let (protocol_key_pair, network_key_pair): (NetworkKeyPair, NetworkKeyPair) =
165 (get_key_pair_from_rng(rng).1, get_key_pair_from_rng(rng).1);
166
167 let (
168 network_address,
169 p2p_address,
170 metrics_address,
171 primary_address,
172 admin_interface_address,
173 ) = if let Some(offset) = self.port_offset {
174 (
175 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset),
176 local_ip_utils::new_deterministic_udp_address_for_testing(&ip, offset + 1),
177 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset + 2)
178 .with_zero_ip(),
179 local_ip_utils::new_deterministic_udp_address_for_testing(&ip, offset + 3),
180 local_ip_utils::new_deterministic_tcp_address_for_testing(&ip, offset + 4),
181 )
182 } else {
183 (
184 local_ip_utils::new_tcp_address_for_testing(&ip),
185 local_ip_utils::new_udp_address_for_testing(&ip),
186 local_ip_utils::new_tcp_address_for_testing(&localhost),
187 local_ip_utils::new_udp_address_for_testing(&ip),
188 local_ip_utils::new_tcp_address_for_testing(&localhost),
189 )
190 };
191
192 let p2p_listen_address = self
193 .p2p_listen_ip_address
194 .map(|ip| SocketAddr::new(ip, p2p_address.port().unwrap()));
195
196 ValidatorGenesisConfig {
197 authority_key_pair,
198 protocol_key_pair,
199 account_key_pair: account_key_pair.into(),
200 network_key_pair,
201 network_address,
202 p2p_address,
203 p2p_listen_address,
204 metrics_address: metrics_address.to_socket_addr().unwrap(),
205 admin_interface_address: admin_interface_address.to_socket_addr().unwrap(),
206 gas_price,
207 commission_rate: DEFAULT_COMMISSION_RATE,
208 primary_address,
209 stake: iota_types::governance::VALIDATOR_LOW_STAKE_THRESHOLD_NANOS,
210 name: None,
211 }
212 }
213}
214
215#[derive(Serialize, Deserialize, Default)]
216pub struct GenesisConfig {
217 pub ssfn_config_info: Option<Vec<SsfnGenesisConfig>>,
218 pub validator_config_info: Option<Vec<ValidatorGenesisConfig>>,
219 pub parameters: GenesisCeremonyParameters,
220 pub accounts: Vec<AccountConfig>,
221 pub migration_sources: Vec<SnapshotSource>,
222 pub delegator: Option<IotaAddress>,
223}
224
225impl Config for GenesisConfig {}
226
227impl GenesisConfig {
228 pub fn generate_accounts<R: rand::RngCore + rand::CryptoRng>(
229 &self,
230 mut rng: R,
231 ) -> Result<(Vec<AccountKeyPair>, Vec<TokenAllocation>)> {
232 let mut addresses = Vec::new();
233 let mut allocations = Vec::new();
234
235 info!("Creating accounts and token allocations...");
236
237 let mut keys = Vec::new();
238 for account in &self.accounts {
239 let address = if let Some(address) = account.address {
240 address
241 } else {
242 let (address, keypair) = get_key_pair_from_rng(&mut rng);
243 keys.push(keypair);
244 address
245 };
246
247 addresses.push(address);
248
249 account.gas_amounts.iter().for_each(|a| {
251 allocations.push(TokenAllocation {
252 recipient_address: address,
253 amount_nanos: *a,
254 staked_with_validator: None,
255 staked_with_timelock_expiration: None,
256 });
257 });
258 }
259
260 Ok((keys, allocations))
261 }
262}
263
264fn default_socket_address() -> SocketAddr {
265 local_ip_utils::new_local_tcp_socket_for_testing()
266}
267
268fn default_stake() -> u64 {
269 iota_types::governance::VALIDATOR_LOW_STAKE_THRESHOLD_NANOS
270}
271
272fn default_bls12381_key_pair() -> AuthorityKeyPair {
273 get_key_pair_from_rng(&mut rand::rngs::OsRng).1
274}
275
276fn default_ed25519_key_pair() -> NetworkKeyPair {
277 get_key_pair_from_rng(&mut rand::rngs::OsRng).1
278}
279
280fn default_iota_key_pair() -> IotaKeyPair {
281 IotaKeyPair::Ed25519(get_key_pair_from_rng(&mut rand::rngs::OsRng).1)
282}
283
284#[derive(Serialize, Deserialize, Debug, Clone)]
285pub struct AccountConfig {
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub address: Option<IotaAddress>,
288 pub gas_amounts: Vec<u64>,
289}
290
291pub const DEFAULT_GAS_AMOUNT: u64 = 30_000_000_000_000_000;
292pub const DEFAULT_NUMBER_OF_AUTHORITIES: usize = 4;
293const DEFAULT_NUMBER_OF_ACCOUNT: usize = 5;
294pub const DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT: usize = 5;
295
296impl GenesisConfig {
297 pub const BENCHMARKS_RNG_SEED: u64 = 0;
300 pub const BENCHMARKS_PORT_OFFSET: u16 = 2000;
302 const BENCHMARK_GAS_AMOUNT: u64 = 1_000_000_000_000_000_000;
304 const BENCHMARK_EPOCH_DURATION_MS: u64 = 60 * 60 * 1000;
306
307 pub fn for_local_testing() -> Self {
308 Self::custom_genesis(
309 DEFAULT_NUMBER_OF_ACCOUNT,
310 DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT,
311 )
312 }
313
314 pub fn for_local_testing_with_addresses(addresses: Vec<IotaAddress>) -> Self {
315 Self::custom_genesis_with_addresses(addresses, DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT)
316 }
317
318 pub fn custom_genesis(num_accounts: usize, num_objects_per_account: usize) -> Self {
319 let mut accounts = Vec::new();
320 for _ in 0..num_accounts {
321 accounts.push(AccountConfig {
322 address: None,
323 gas_amounts: vec![DEFAULT_GAS_AMOUNT; num_objects_per_account],
324 })
325 }
326
327 Self {
328 accounts,
329 ..Default::default()
330 }
331 }
332
333 pub fn custom_genesis_with_addresses(
334 addresses: Vec<IotaAddress>,
335 num_objects_per_account: usize,
336 ) -> Self {
337 let mut accounts = Vec::new();
338 for address in addresses {
339 accounts.push(AccountConfig {
340 address: Some(address),
341 gas_amounts: vec![DEFAULT_GAS_AMOUNT; num_objects_per_account],
342 })
343 }
344
345 Self {
346 accounts,
347 ..Default::default()
348 }
349 }
350
351 pub fn new_for_benchmarks(
361 ips: &[String],
362 epoch_duration_ms: Option<u64>,
363 chain_start_timestamp_ms: Option<u64>,
364 num_additional_gas_accounts: Option<usize>,
365 ) -> Self {
366 let mut rng = StdRng::seed_from_u64(Self::BENCHMARKS_RNG_SEED);
369 let validator_config_info: Vec<_> = ips
370 .iter()
371 .enumerate()
372 .map(|(i, ip)| {
373 ValidatorGenesisConfigBuilder::new()
374 .with_ip(ip.to_string())
375 .with_deterministic_ports(Self::BENCHMARKS_PORT_OFFSET + 10 * i as u16)
376 .with_p2p_listen_ip_address("0.0.0.0".parse().unwrap())
377 .build(&mut rng)
378 })
379 .collect();
380
381 let num_accounts = num_additional_gas_accounts.unwrap_or(0) + validator_config_info.len();
383 let account_configs = Self::benchmark_gas_keys(num_accounts)
384 .iter()
385 .map(|gas_key| {
386 let gas_address = IotaAddress::from(&gas_key.public());
387
388 AccountConfig {
389 address: Some(gas_address),
390 gas_amounts: vec![
394 u64::min(
395 Self::BENCHMARK_GAS_AMOUNT,
396 u64::MAX / (6u64 * num_accounts as u64),
397 );
398 5
399 ],
400 }
401 })
402 .collect();
403
404 let parameters = GenesisCeremonyParameters {
407 chain_start_timestamp_ms: chain_start_timestamp_ms.unwrap_or(0),
408 epoch_duration_ms: if let Some(duration_ms) = epoch_duration_ms {
409 duration_ms
410 } else {
411 Self::BENCHMARK_EPOCH_DURATION_MS
412 },
413 ..GenesisCeremonyParameters::new()
414 };
415
416 GenesisConfig {
418 ssfn_config_info: None,
419 validator_config_info: Some(validator_config_info),
420 parameters,
421 accounts: account_configs,
422 migration_sources: Default::default(),
423 delegator: Default::default(),
424 }
425 }
426
427 pub fn benchmark_gas_keys(n: usize) -> Vec<IotaKeyPair> {
432 let mut rng = StdRng::seed_from_u64(Self::BENCHMARKS_RNG_SEED);
433 (0..n)
434 .map(|_| IotaKeyPair::Ed25519(NetworkKeyPair::generate(&mut rng)))
435 .collect()
436 }
437
438 pub fn add_faucet_account(mut self) -> Self {
439 self.accounts.push(AccountConfig {
440 address: None,
441 gas_amounts: vec![DEFAULT_GAS_AMOUNT; DEFAULT_NUMBER_OF_OBJECT_PER_ACCOUNT],
442 });
443 self
444 }
445
446 pub fn add_delegator(mut self, address: IotaAddress) -> Self {
447 self.delegator = Some(address);
448 self
449 }
450}