From bf34fe436288952e24a2c996b710ac0ba26a980c Mon Sep 17 00:00:00 2001 From: Sebastien Guillemot Date: Wed, 17 Jul 2024 02:11:24 +0200 Subject: [PATCH] Add support for parsing Shelley genesis blocks (#331) * Add support for parsing Shelley genesis blocks * Genesis keyHash alias + minor fixes * Add alias for `ShelleyGenesisCredential::keyHash` to account for cardano-node json format * Remove unnecessary clone() * Change `u64` to `Coin` when appropriate in Genesis types * ran cargo fmt * Update Genesis parsing for changes in #340 --------- Co-authored-by: rooooooooob Co-authored-by: rooooooooob --- chain/rust/Cargo.toml | 2 + chain/rust/src/genesis/mod.rs | 1 + chain/rust/src/genesis/shelley/config.rs | 65 +++++ chain/rust/src/genesis/shelley/mod.rs | 3 + chain/rust/src/genesis/shelley/parse.rs | 226 ++++++++++++++++++ chain/rust/src/genesis/shelley/raw.rs | 139 +++++++++++ .../src/genesis/shelley/test_data/test.json | 135 +++++++++++ 7 files changed, 571 insertions(+) create mode 100644 chain/rust/src/genesis/shelley/config.rs create mode 100644 chain/rust/src/genesis/shelley/mod.rs create mode 100644 chain/rust/src/genesis/shelley/parse.rs create mode 100644 chain/rust/src/genesis/shelley/raw.rs create mode 100644 chain/rust/src/genesis/shelley/test_data/test.json diff --git a/chain/rust/Cargo.toml b/chain/rust/Cargo.toml index 5bdb22ad..5b7f5f73 100644 --- a/chain/rust/Cargo.toml +++ b/chain/rust/Cargo.toml @@ -38,6 +38,8 @@ num-integer = "0.1.45" thiserror = "1.0.37" num = "0.4" unicode-segmentation = "1.10.1" +serde-aux = "4.5.0" +chrono = "0.4.38" # non-wasm noop_proc_macro = { version = "0.3.0", optional = false } diff --git a/chain/rust/src/genesis/mod.rs b/chain/rust/src/genesis/mod.rs index f5f3e848..e485153e 100644 --- a/chain/rust/src/genesis/mod.rs +++ b/chain/rust/src/genesis/mod.rs @@ -1,2 +1,3 @@ pub mod byron; pub mod network_info; +pub mod shelley; diff --git a/chain/rust/src/genesis/shelley/config.rs b/chain/rust/src/genesis/shelley/config.rs new file mode 100644 index 00000000..45eade5a --- /dev/null +++ b/chain/rust/src/genesis/shelley/config.rs @@ -0,0 +1,65 @@ +use cml_crypto::{Ed25519KeyHash, VRFKeyHash}; +use fraction::Fraction; +use std::collections::BTreeMap; + +use crate::{address::Address, block::ProtocolVersion, Coin}; + +/// A subset of the Shelley genesis data. The genesis data is a JSON file +/// is something completely different from a epoch genesis block and the Byron genesis block +#[derive(Debug, Clone)] +pub struct ShelleyGenesisData { + pub active_slots_coeff: Fraction, + pub epoch_length: u64, + pub gen_delegs: BTreeMap, + pub initial_funds: BTreeMap, + pub max_kes_evolutions: u64, + pub max_lovelace_supply: Coin, + pub network_id: u64, + pub network_magic: u64, + pub protocol_params: ShelleyGenesisProtocolParameters, + pub security_param: u64, + pub slot_length: u64, + pub slots_per_kes_period: u64, + pub staking: Option, + pub system_start: chrono::DateTime, + pub update_quorum: u64, +} + +#[derive(Debug, Clone)] +pub struct ShelleyGenesisDelegations { + pub delegate: Ed25519KeyHash, + pub vrf: VRFKeyHash, +} + +#[derive(Debug, Clone)] +pub struct ShelleyGenesisStaking { + pub pools: BTreeMap, + // initial delegations of staking key -> pool id + pub stake: BTreeMap, +} + +#[derive(Debug, Clone)] +pub struct ShelleyGenesisProtocolParameters { + pub a0: Fraction, + pub decentralisation_param: Fraction, + pub e_max: u64, + pub extra_entropy: ShelleyGenesisExtraEntropy, + pub key_deposit: Coin, + pub max_block_body_size: u64, + pub max_block_header_size: u64, + pub max_tx_size: u64, + pub min_fee_a: Coin, + pub min_fee_b: Coin, + pub min_pool_cost: Coin, + pub min_utxo_value: Coin, + pub n_opt: u64, + pub pool_deposit: Coin, + pub protocol_version: ProtocolVersion, + pub rho: Fraction, + pub tau: Fraction, +} + +#[derive(Debug, Clone)] +pub struct ShelleyGenesisExtraEntropy { + pub tag: String, +} diff --git a/chain/rust/src/genesis/shelley/mod.rs b/chain/rust/src/genesis/shelley/mod.rs new file mode 100644 index 00000000..9fdecf58 --- /dev/null +++ b/chain/rust/src/genesis/shelley/mod.rs @@ -0,0 +1,3 @@ +pub mod config; +pub mod parse; +pub mod raw; diff --git a/chain/rust/src/genesis/shelley/parse.rs b/chain/rust/src/genesis/shelley/parse.rs new file mode 100644 index 00000000..0dfa4415 --- /dev/null +++ b/chain/rust/src/genesis/shelley/parse.rs @@ -0,0 +1,226 @@ +use cml_core::DeserializeError; +use cml_crypto::{ + chain_crypto::Blake2b256, Ed25519KeyHash, PoolMetadataHash, TransactionHash, VRFKeyHash, +}; +use serde_json; +use std::collections::BTreeMap; +use std::io::Read; +use std::str::FromStr; + +use crate::{ + address::{Address, RewardAccount}, + block::ProtocolVersion, + certs::{Ipv4, Ipv6, PoolMetadata, PoolParams, Relay, StakeCredential, Url}, + UnitInterval, +}; + +use super::{ + config, + raw::{self}, +}; + +#[derive(Debug, thiserror::Error)] +pub enum GenesisJSONError { + #[error("JSON: {0:?}")] + Serde(#[from] serde_json::Error), + #[error("Deserialize: {0:?}")] + Deserialize(#[from] DeserializeError), + #[error("ParseInt: {0:?}")] + ParseInt(#[from] std::num::ParseIntError), + #[error("ParseIP: {0:?}")] + ParseIP(#[from] crate::certs::utils::IPStringParsingError), + #[error("Unexpected network type: {0:?}")] + ParseNetwork(String), +} + +pub fn parse_genesis_data( + json: R, +) -> Result { + let data_value: serde_json::Value = serde_json::from_reader(json)?; + let data: raw::ShelleyGenesisData = serde_json::from_value(data_value)?; + + let mut initial_funds = BTreeMap::new(); + for (addr_hex, balance) in &data.initialFunds { + initial_funds.insert(Address::from_hex(addr_hex)?, *balance); + } + + let network_id = match data.networkId.as_str() { + "Mainnet" => crate::NetworkId::mainnet().network, + "Testnet" => crate::NetworkId::testnet().network, + val => return Err(GenesisJSONError::ParseNetwork(val.to_string())), + }; + + let staking = match data.staking.as_ref() { + Some(raw) => { + // 1) Get stake pools + let mut pools: BTreeMap = BTreeMap::new(); + for (pool_id, params) in &raw.pools { + let ration = fraction::Fraction::from_str(¶ms.margin).unwrap(); + let mut owners = Vec::::new(); + for owner in ¶ms.owners { + owners.push(Ed25519KeyHash::from_hex(owner)?); + } + let mut relays = Vec::::new(); + for relay in ¶ms.relays { + if let Some((key, value)) = relay.iter().next() { + match key.as_str() { + "single host address" => { + let ipv4 = match value.IPv4.as_ref() { + Some(s) => Some(Ipv4::from_str(s)?), + _ => None + }; + let ipv6 = match value.IPv6.as_ref() { + Some(s) => Some(Ipv6::from_str(s)?), + _ => None + }; + relays.push(Relay::new_single_host_addr( + value.port, + ipv4, + ipv6 + )); + }, + _ => panic!("Only single host address relays are supported in cardano-node Relay JSON parsing") + } + } + } + let pool_metadata = match params.metadata.as_ref() { + Some(metadata) => Some(PoolMetadata::new( + Url::new(metadata.url.clone()).unwrap(), + PoolMetadataHash::from_hex(&metadata.hash)?, + )), + _ => None, + }; + let parsed_params = PoolParams::new( + Ed25519KeyHash::from_hex(¶ms.publicKey)?, + VRFKeyHash::from_hex(¶ms.vrf)?, + params.pledge, + params.cost, + UnitInterval::new(*ration.numer().unwrap(), *ration.denom().unwrap()), + RewardAccount::new( + match data.networkId.as_str() { + "Mainnet" => crate::NetworkId::mainnet().network as u8, + "Testnet" => crate::NetworkId::testnet().network as u8, + val => return Err(GenesisJSONError::ParseNetwork(val.to_string())), + }, + StakeCredential::new_pub_key(Ed25519KeyHash::from_hex( + ¶ms.rewardAccount.credential.keyHash, + )?), + ), + owners.into(), + relays, + pool_metadata, + ); + pools.insert(Ed25519KeyHash::from_hex(pool_id)?, parsed_params); + } + // 2) Get initial delegations + let mut stake: BTreeMap = BTreeMap::new(); + for (staking_key, pool_id) in &raw.stake { + stake.insert( + Ed25519KeyHash::from_hex(staking_key)?, + Ed25519KeyHash::from_hex(pool_id)?, + ); + } + Some(config::ShelleyGenesisStaking { stake, pools }) + } + _ => None, + }; + + let mut gen_delegs = BTreeMap::new(); + for (key, val) in data.genDelegs.iter() { + gen_delegs.insert( + Ed25519KeyHash::from_hex(key)?, + config::ShelleyGenesisDelegations { + delegate: Ed25519KeyHash::from_hex(&val.delegate)?, + vrf: VRFKeyHash::from_hex(&val.vrf)?, + }, + ); + } + Ok(config::ShelleyGenesisData { + active_slots_coeff: fraction::Fraction::from_str(&data.activeSlotsCoeff).unwrap(), + epoch_length: data.epochLength, + gen_delegs, + initial_funds, + max_kes_evolutions: data.maxKESEvolutions, + max_lovelace_supply: data.maxLovelaceSupply, + network_id, + network_magic: data.networkMagic, + protocol_params: config::ShelleyGenesisProtocolParameters { + a0: fraction::Fraction::from_str(&data.protocolParams.a0).unwrap(), + decentralisation_param: fraction::Fraction::from_str( + &data.protocolParams.decentralisationParam, + ) + .unwrap(), + e_max: data.protocolParams.eMax, + extra_entropy: config::ShelleyGenesisExtraEntropy { + tag: data.protocolParams.extraEntropy.tag, + }, + key_deposit: data.protocolParams.keyDeposit, + max_block_body_size: data.protocolParams.maxBlockBodySize, + max_block_header_size: data.protocolParams.maxBlockHeaderSize, + max_tx_size: data.protocolParams.maxTxSize, + min_fee_a: data.protocolParams.minFeeA, + min_fee_b: data.protocolParams.minFeeB, + min_pool_cost: data.protocolParams.minPoolCost, + min_utxo_value: data.protocolParams.minUTxOValue, + n_opt: data.protocolParams.nOpt, + pool_deposit: data.protocolParams.poolDeposit, + protocol_version: ProtocolVersion::new( + data.protocolParams.protocolVersion.major, + data.protocolParams.protocolVersion.minor, + ), + rho: fraction::Fraction::from_str(&data.protocolParams.rho).unwrap(), + tau: fraction::Fraction::from_str(&data.protocolParams.tau).unwrap(), + }, + security_param: data.securityParam, + slot_length: data.slotLength, + slots_per_kes_period: data.slotsPerKESPeriod, + staking, + system_start: data.systemStart.parse().expect("Failed to parse date"), + update_quorum: data.updateQuorum, + }) +} + +pub fn redeem_address_to_txid(pubkey: &Address) -> TransactionHash { + let txid = Blake2b256::new(&pubkey.to_raw_bytes()); + TransactionHash::from(*txid.as_hash_bytes()) +} + +#[cfg(test)] +mod test { + use super::*; + + fn get_test_genesis_data() -> &'static str { + include_str!("./test_data/test.json") + } + + #[test] + fn calc_address_txid() { + let hash = redeem_address_to_txid( + &Address::from_bech32("addr_test1qpefp65049pncyz95nyyww2e44sgumqr5kx8mcemm0fuumeftwv8zdtpqct0836wz8y56aakem2uejf604cee7cn2p3qp9p8te").unwrap(), + ); + assert_eq!( + hash.to_hex(), + "66dc6b2e628bf1fb6204797f1a07f8e949d9520a70e859ecbf3ea3076029871e" + ); + } + + #[test] + fn parse_test_genesis_files() { + let genesis_data = super::parse_genesis_data(get_test_genesis_data().as_bytes()).unwrap(); + + assert_eq!(genesis_data.epoch_length, 432000u64); + assert_eq!(genesis_data.network_id, 0); + assert_eq!(genesis_data.network_magic, 764824073u64); + + assert_eq!( + *genesis_data + .initial_funds + .iter() + .find(|(n, _)| n.to_hex() + == "605276322ac7882434173dcc6441905f6737689bd309b68ad8b3614fd8") + .unwrap() + .1, + 3000000000000000u64 + ); + } +} diff --git a/chain/rust/src/genesis/shelley/raw.rs b/chain/rust/src/genesis/shelley/raw.rs new file mode 100644 index 00000000..c89fd6d4 --- /dev/null +++ b/chain/rust/src/genesis/shelley/raw.rs @@ -0,0 +1,139 @@ +use crate::assets::Coin; +use serde::{Deserialize, Serialize}; +use serde_aux::prelude::*; +use std::collections::HashMap; + +/// Parsing of the JSON representation of the Shelley genesis block +/// Note: for a lot of these fields, I didn't check what the max valid size is in the Haskell code +/// so I just used u64 everywhere + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisData { + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub activeSlotsCoeff: String, + pub epochLength: u64, + pub genDelegs: HashMap, + pub initialFunds: HashMap, + pub maxKESEvolutions: u64, + pub maxLovelaceSupply: Coin, + pub networkId: String, + pub networkMagic: u64, + pub protocolParams: ShelleyGenesisProtocolParameters, + pub securityParam: u64, + pub slotLength: u64, + pub slotsPerKESPeriod: u64, + pub staking: Option, + pub systemStart: String, + pub updateQuorum: u64, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisDelegations { + pub delegate: String, + pub vrf: String, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisProtocolParameters { + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub a0: String, + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub decentralisationParam: String, + pub eMax: u64, + pub extraEntropy: ShelleyGenesisExtraEntropy, + pub keyDeposit: Coin, + pub maxBlockBodySize: u64, + pub maxBlockHeaderSize: u64, + pub maxTxSize: u64, + pub minFeeA: Coin, + pub minFeeB: Coin, + pub minPoolCost: Coin, + pub minUTxOValue: Coin, + pub nOpt: u64, + pub poolDeposit: Coin, + pub protocolVersion: ShelleyGenesisProtocolVersion, + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub rho: String, + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub tau: String, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisExtraEntropy { + pub tag: String, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisProtocolVersion { + pub major: u64, + pub minor: u64, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisStaking { + pub pools: HashMap, + pub stake: HashMap, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisPool { + pub cost: Coin, + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub margin: String, + pub metadata: Option, + pub owners: Vec, + pub pledge: Coin, + pub publicKey: String, + pub relays: Vec, + pub rewardAccount: ShelleyGenesisRewardAccount, + pub vrf: String, +} + +// TODO: there are other relay types, but I can't find the JSON type for them +// and I can't find any usage of them in the wild anyway +// The key here defines the relay type +// ex: +// - single host address +type RelayTypeMap = HashMap; + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisPoolSingleHotsRelay { + pub IPv6: Option, + pub port: Option, + pub IPv4: Option, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisPoolMetadata { + pub hash: String, + pub url: String, +} +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisRewardAccount { + pub network: String, + pub credential: ShelleyGenesisCredential, +} +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisCredential { + // for some reason there actually is a space in the JSON key emitted by the Haskell node + // both key hash and keyHash are accepted + #[serde(alias = "key hash")] + pub keyHash: String, +} diff --git a/chain/rust/src/genesis/shelley/test_data/test.json b/chain/rust/src/genesis/shelley/test_data/test.json new file mode 100644 index 00000000..09f234ef --- /dev/null +++ b/chain/rust/src/genesis/shelley/test_data/test.json @@ -0,0 +1,135 @@ +{ + "activeSlotsCoeff": 0.05, + "epochLength": 432000, + "genDelegs": { + "337bc5ef0f1abf205624555c13a37258c42b46b1259a6b1a6d82574e": { + "delegate": "41fd6bb31f34469320aa47cf6ccc3918e58a06d60ea1f2361efe2458", + "vrf": "7053e3ecd2b19db13e5338aa75fb518fc08b6c218f56ad65760d3eb074be95d4" + }, + "b5c3fed76c54bfdcda7993e4171eff2fb853f8ab920ab80047278a91": { + "delegate": "fcb677a90948d1d5b358912f54f6baaf762ecf5cd6579c93bcb49cef", + "vrf": "c7715f726e8e4f7745ccc646f4350758460de71de5694b244a47761fb106ec6e" + }, + "e34a75849b978bb20c366f531f978b3111b91aae0e469108dca8e433": { + "delegate": "81babf3c139646f0f0268abed36d2a54757492a3a68cda2438a37f7e", + "vrf": "ca336185cd781a6543b6c1e62ee1eee53e237d5d1fb065f08412d40d61b6ca06" + } + }, + "initialFunds": { + "007290ea8fa9433c1045a4c8473959ad608e6c03a58c7de33bdbd3ce6f295b987135610616f3c74e11c94d77b6ced5ccc93a7d719cfb135062": 300000000000, + "605276322ac7882434173dcc6441905f6737689bd309b68ad8b3614fd8": 3000000000000000, + "60a0f1aa7dca95017c11e7e373aebcf0c4568cf47ec12b94f8eb5bba8b": 3000000000000000, + "60ba957a0fff6816021b2afa7900beea68fd10f2d78fb5b64de0d2379c": 3000000000000000, + "00c8c47610a36034aac6fc58848bdae5c278d994ff502c05455e3b3ee8f8ed3a0eea0ef835ffa7bbfcde55f7fe9d2cc5d55ea62cecb42bab3c": 10000000000, + "004048ff89ca4f88e66598e620aa0c7128c2145d9a181ae9a4a81ca8e3e849af38840c5562dd382be37c9e76545c8191f9d8f6df1d20cfcee0": 10000000000, + "00ca6e1b1f320d543a24adeabc0aa4627635c7349b639f86f74bdfdd78d31b28c9619a58b3792a7394ab85deb36889c4d7b0632c8167b855d2": 10000000000, + "0007d781fe8e33883e371f9550c2f1087321fc32e06e80b65e349ccb027702d6880e86e77a0520efa37ede45002a1de43b68692e175b742e67": 10000000000, + "00627b2598dd71129167825160c564067d1d245e79cc237094815c5cb2b125e30ec2f4ce4059a069e08c3cd82cdfc9451bfb22487f8a25ceef": 10000000000, + "00c6cf7bd50f37f7e4cc161fc00f07e9b2226ba5552ccaf30d315fa0135bbc8cbd9ab5379f368fc8d3500c37a9d14074cc6ddad89e3686f0e0": 10000000000, + "005164ab186715c86378020956d892cf72f67636b78967d67cfe7360479130dc89cf7a9bc89109f939956b66f93293ade4c3920b72fd40beea": 10000000000, + "003dd38742e9848c6f12c13ddb1f9464fc0ce0bb92102768087975317e5a9f869fcd913562c9b0e0f01f77e5359ea780d37f9355f9702eff8b": 10000000000, + "0088e7e670b45cab2322b518ef7b6f66d30aec0d923dc463e467091a790f67796b9fa71224f2846cebbcf4950c11e040ee124d30f6e164bcd5": 10000000000, + "00c70b8421617802d3f23956cab1957e1d306cd4808589b41760e97927ebfd6053ba12b38288b2b6d5d4c4618d6a8ce59d50580e9c6f704af5": 10000000000, + "00c0933b8238f6f3332e48c34cf1a8e0555943b33cd4abc53aefb7d6124b7ce40dd496bdc02b34602f3a773ff7cccee873991e4c8866f3a70b": 10000000000, + "0069f7d7289de2f01cd1e0265ac5be943b41775abae0ce6b3eac0edee0ce9cadb7cdec2bded3ef8a7bbe3352869bfc1387754c9ee6b1782d9c": 10000000000, + "00709a7070005c652c27df73dbbde3319a90b127bea96aded1c5fb87a59c51dbcf90fa890174497f3f66a0dad06eb7f131e06567995e9c50a5": 10000000000, + "00fc576df3a279885a7a4d0fc500372daa1d96f26c6763542ecd2ad8551753024adea37c134edebb68dc0cfaed5a7009e8305fe1fed8d0ccd1": 10000000000, + "003346a630e6972bf38cce87219db1d63061e7cd324cad88c18e504f2990cac68e973f51256ca938683fa4ea12173d7d047d940fbb883bd0e8": 10000000000, + "0028b862d001e6a64a02b3560cbc532eab4557593477c39cc523e0b9fc527100898c11e731194171b908aad463770d6cbf7ec8871c4cb1e518": 10000000000, + "005e0e57040b06e9d71e0f28f126262838a68db0b52b4fd1b3877dda2203d5d7d4f19c5ee3a1ed51bb670779de19d40aaff2e5e9468cc05c5e": 10000000000, + "00367f65ab69b1e6715c8d5a14964214c9505ed17032266b3209a2c40dcbae9a2a881e603ff39d36e987bacfb87ee98051f222c5fe3efd350c": 10000000000, + "00c5c4ca287f3b53948b5468e5e23b1c87fe61ce52c0d9afd65d070795038751a619d463e91eaed0a774ebdb2f8e12a01a378a153bc3627323": 10000000000, + "00ef198fb7c35e1968308a0b75cfee54a46e13e86dd3354283300831d624165c357b5a0413906a0bfea8ba57587331f0836a186d632ed041b8": 10000000000 + }, + "maxKESEvolutions": 60, + "maxLovelaceSupply": 45000000000000000, + "networkId": "Testnet", + "networkMagic": 764824073, + "protocolParams": { + "a0": 0, + "decentralisationParam": 0, + "eMax": 18, + "extraEntropy": { + "tag": "NeutralNonce" + }, + "keyDeposit": 2000000, + "maxBlockBodySize": 65536, + "maxBlockHeaderSize": 1100, + "maxTxSize": 16384, + "minFeeA": 44, + "minFeeB": 155381, + "minPoolCost": 340000000, + "minUTxOValue": 1000000, + "nOpt": 100, + "poolDeposit": 500000000, + "protocolVersion": { + "major": 8, + "minor": 0 + }, + "rho": 0.003, + "tau": 0.20 + }, + "securityParam": 12, + "slotLength": 1, + "slotsPerKESPeriod": 129600, + "staking": { + "pools": { + "7301761068762f5900bde9eb7c1c15b09840285130f5b0f53606cc57": { + "cost": 340000000, + "margin": 3.0e-2, + "metadata": { + "hash": "b150b12a1301c4b1510ac8b9f53f7571cabb43455f6fd244cd8fd97504b1c869", + "url": "https://adalite.io/ADLT4-metadata.json" + }, + "owners": [ + "463a9695c9222183ee6e1523478722bebcb332fa3769f1d8ef40c7d0", + "5049c1dac0e597ee902f27a74a167cf135ae7c1717b0d3a417cd6c67" + ], + "pledge": 0, + "publicKey": "7301761068762f5900bde9eb7c1c15b09840285130f5b0f53606cc57", + "relays": [ + { + "single host address": { + "IPv6": null, + "port": 3003, + "IPv4": "54.228.75.154" + } + }, + { + "single host address": { + "IPv6": null, + "port": 3001, + "IPv4": "54.228.75.154" + } + }, + { + "single host address": { + "IPv6": null, + "port": 3003, + "IPv4": "34.249.11.89" + } + }, + { + "single host address": { + "IPv6": null, + "port": 3001, + "IPv4": "34.249.11.89" + } + } + ], + "rewardAccount": { + "credential": { + "key hash": "11a14edf73b08a0a27cb98b2c57eb37c780df18fcfcf6785ed5df84a" + }, + "network": "Testnet" + }, + "vrf": "c2b62ffa92ad18ffc117ea3abeb161a68885000a466f9c71db5e4731d6630061" + } + }, + "stake": { + "295b987135610616f3c74e11c94d77b6ced5ccc93a7d719cfb135062": "7301761068762f5900bde9eb7c1c15b09840285130f5b0f53606cc57" + } + }, + "systemStart": "2022-09-15T04:09:11.577484Z", + "updateQuorum": 2 +} \ No newline at end of file