cml_chain/genesis/shelley/
parse.rs1use cml_core::DeserializeError;
2use cml_crypto::{
3 chain_crypto::Blake2b256, Ed25519KeyHash, PoolMetadataHash, TransactionHash, VRFKeyHash,
4};
5use num::traits::Pow as _;
6use serde_json;
7use std::collections::BTreeMap;
8use std::io::Read;
9use std::str::FromStr;
10
11use crate::{
12 address::{Address, RewardAccount},
13 block::ProtocolVersion,
14 certs::{Ipv4, Ipv6, PoolMetadata, PoolParams, Relay, StakeCredential, Url},
15 UnitInterval,
16};
17
18use super::{
19 config,
20 raw::{self},
21};
22
23#[derive(Debug, thiserror::Error)]
24pub enum GenesisJSONError {
25 #[error("JSON: {0:?}")]
26 Serde(#[from] serde_json::Error),
27 #[error("Deserialize: {0:?}")]
28 Deserialize(#[from] DeserializeError),
29 #[error("ParseInt: {0:?}")]
30 ParseInt(#[from] std::num::ParseIntError),
31 #[error("ParseIP: {0:?}")]
32 ParseIP(#[from] crate::certs::utils::IPStringParsingError),
33 #[error("Unexpected network type: {0:?}")]
34 ParseNetwork(String),
35}
36
37pub fn parse_genesis_data<R: Read>(
38 json: R,
39) -> Result<config::ShelleyGenesisData, GenesisJSONError> {
40 let data: raw::ShelleyGenesisData = serde_json::from_reader(json)?;
41
42 let mut initial_funds = BTreeMap::new();
43 for (addr_hex, balance) in &data.initialFunds {
44 initial_funds.insert(Address::from_hex(addr_hex)?, *balance);
45 }
46
47 let network_id = match data.networkId.as_str() {
48 "Mainnet" => crate::NetworkId::mainnet().network,
49 "Testnet" => crate::NetworkId::testnet().network,
50 val => return Err(GenesisJSONError::ParseNetwork(val.to_string())),
51 };
52
53 let staking = match data.staking.as_ref() {
54 Some(raw) => {
55 let mut pools: BTreeMap<Ed25519KeyHash, PoolParams> = BTreeMap::new();
57 for (pool_id, params) in &raw.pools {
58 let ration = json_number_to_fraction(¶ms.margin);
59 let mut owners = Vec::<Ed25519KeyHash>::new();
60 for owner in ¶ms.owners {
61 owners.push(Ed25519KeyHash::from_hex(owner)?);
62 }
63 let mut relays = Vec::<Relay>::new();
64 for relay in ¶ms.relays {
65 if let Some((key, value)) = relay.iter().next() {
66 match key.as_str() {
67 "single host address" => {
68 let ipv4 = match value.IPv4.as_ref() {
69 Some(s) => Some(Ipv4::from_str(s)?),
70 _ => None
71 };
72 let ipv6 = match value.IPv6.as_ref() {
73 Some(s) => Some(Ipv6::from_str(s)?),
74 _ => None
75 };
76 relays.push(Relay::new_single_host_addr(
77 value.port,
78 ipv4,
79 ipv6
80 ));
81 },
82 _ => panic!("Only single host address relays are supported in cardano-node Relay JSON parsing")
83 }
84 }
85 }
86 let pool_metadata = match params.metadata.as_ref() {
87 Some(metadata) => Some(PoolMetadata::new(
88 Url::new(metadata.url.clone()).unwrap(),
89 PoolMetadataHash::from_hex(&metadata.hash)?,
90 )),
91 _ => None,
92 };
93 let parsed_params = PoolParams::new(
94 Ed25519KeyHash::from_hex(¶ms.publicKey)?,
95 VRFKeyHash::from_hex(¶ms.vrf)?,
96 params.pledge,
97 params.cost,
98 UnitInterval::new(*ration.numer().unwrap(), *ration.denom().unwrap()),
99 RewardAccount::new(
100 match data.networkId.as_str() {
101 "Mainnet" => crate::NetworkId::mainnet().network as u8,
102 "Testnet" => crate::NetworkId::testnet().network as u8,
103 val => return Err(GenesisJSONError::ParseNetwork(val.to_string())),
104 },
105 StakeCredential::new_pub_key(Ed25519KeyHash::from_hex(
106 ¶ms.rewardAccount.credential.keyHash,
107 )?),
108 ),
109 owners.into(),
110 relays,
111 pool_metadata,
112 );
113 pools.insert(Ed25519KeyHash::from_hex(pool_id)?, parsed_params);
114 }
115 let mut stake: BTreeMap<Ed25519KeyHash, Ed25519KeyHash> = BTreeMap::new();
117 for (staking_key, pool_id) in &raw.stake {
118 stake.insert(
119 Ed25519KeyHash::from_hex(staking_key)?,
120 Ed25519KeyHash::from_hex(pool_id)?,
121 );
122 }
123 Some(config::ShelleyGenesisStaking { stake, pools })
124 }
125 _ => None,
126 };
127
128 let mut gen_delegs = BTreeMap::new();
129 for (key, val) in data.genDelegs.iter() {
130 gen_delegs.insert(
131 Ed25519KeyHash::from_hex(key)?,
132 config::ShelleyGenesisDelegations {
133 delegate: Ed25519KeyHash::from_hex(&val.delegate)?,
134 vrf: VRFKeyHash::from_hex(&val.vrf)?,
135 },
136 );
137 }
138 Ok(config::ShelleyGenesisData {
139 active_slots_coeff: json_number_to_fraction(&data.activeSlotsCoeff),
140 epoch_length: data.epochLength,
141 gen_delegs,
142 initial_funds,
143 max_kes_evolutions: data.maxKESEvolutions,
144 max_lovelace_supply: data.maxLovelaceSupply,
145 network_id,
146 network_magic: data.networkMagic,
147 protocol_params: config::ShelleyGenesisProtocolParameters {
148 a0: json_number_to_fraction(&data.protocolParams.a0),
149 decentralisation_param: json_number_to_fraction(
150 &data.protocolParams.decentralisationParam,
151 ),
152 e_max: data.protocolParams.eMax,
153 extra_entropy: config::ShelleyGenesisExtraEntropy {
154 tag: data.protocolParams.extraEntropy.tag,
155 },
156 key_deposit: data.protocolParams.keyDeposit,
157 max_block_body_size: data.protocolParams.maxBlockBodySize,
158 max_block_header_size: data.protocolParams.maxBlockHeaderSize,
159 max_tx_size: data.protocolParams.maxTxSize,
160 min_fee_a: data.protocolParams.minFeeA,
161 min_fee_b: data.protocolParams.minFeeB,
162 min_pool_cost: data.protocolParams.minPoolCost,
163 min_utxo_value: data.protocolParams.minUTxOValue,
164 n_opt: data.protocolParams.nOpt,
165 pool_deposit: data.protocolParams.poolDeposit,
166 protocol_version: ProtocolVersion::new(
167 data.protocolParams.protocolVersion.major,
168 data.protocolParams.protocolVersion.minor,
169 ),
170 rho: json_number_to_fraction(&data.protocolParams.rho),
171 tau: json_number_to_fraction(&data.protocolParams.tau),
172 },
173 security_param: data.securityParam,
174 slot_length: json_number_to_fraction(&data.slotLength),
175 slots_per_kes_period: data.slotsPerKESPeriod,
176 staking,
177 system_start: data.systemStart.parse().expect("Failed to parse date"),
178 update_quorum: data.updateQuorum,
179 })
180}
181
182fn json_number_to_fraction(param: &serde_json::Number) -> fraction::GenericFraction<u64> {
183 let s = param.to_string();
184
185 if let Some(exp_position) = s.find('e').or_else(|| s.find('E')) {
186 let (a, b) = s.split_at_checked(exp_position).unwrap();
187
188 let exp = fraction::Ratio::from(10u64).pow(i32::from_str(&b[1..]).unwrap());
189
190 fraction::Fraction::from_str(a).unwrap()
191 * fraction::Fraction::new(*exp.numer(), *exp.denom())
192 } else {
193 fraction::Fraction::from_str(¶m.to_string()).unwrap()
194 }
195}
196
197pub fn redeem_address_to_txid(pubkey: &Address) -> TransactionHash {
198 let txid = Blake2b256::new(&pubkey.to_raw_bytes());
199 TransactionHash::from(*txid.as_hash_bytes())
200}
201
202#[cfg(test)]
203mod test {
204 use super::*;
205
206 fn get_test_genesis_data() -> &'static str {
207 include_str!("./test_data/test.json")
208 }
209
210 fn get_test_genesis_data_yaci() -> &'static str {
211 include_str!("./test_data/test-yaci.json")
212 }
213
214 #[test]
215 fn calc_address_txid() {
216 let hash = redeem_address_to_txid(
217 &Address::from_bech32("addr_test1qpefp65049pncyz95nyyww2e44sgumqr5kx8mcemm0fuumeftwv8zdtpqct0836wz8y56aakem2uejf604cee7cn2p3qp9p8te").unwrap(),
218 );
219 assert_eq!(
220 hash.to_hex(),
221 "66dc6b2e628bf1fb6204797f1a07f8e949d9520a70e859ecbf3ea3076029871e"
222 );
223 }
224
225 #[test]
226 fn parse_test_genesis_files() {
227 let genesis_data = super::parse_genesis_data(get_test_genesis_data().as_bytes()).unwrap();
228
229 assert_eq!(genesis_data.epoch_length, 432000u64);
230 assert_eq!(genesis_data.network_id, 0);
231 assert_eq!(genesis_data.network_magic, 764824073u64);
232
233 assert_eq!(
234 *genesis_data
235 .initial_funds
236 .iter()
237 .find(|(n, _)| n.to_hex()
238 == "605276322ac7882434173dcc6441905f6737689bd309b68ad8b3614fd8")
239 .unwrap()
240 .1,
241 3000000000000000u64
242 );
243 }
244
245 #[test]
246 fn parse_test_genesis_yaci_files() {
247 super::parse_genesis_data(get_test_genesis_data_yaci().as_bytes()).unwrap();
248 }
249}