cml_chain/genesis/shelley/
parse.rs

1use 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            // 1) Get stake pools
56            let mut pools: BTreeMap<Ed25519KeyHash, PoolParams> = BTreeMap::new();
57            for (pool_id, params) in &raw.pools {
58                let ration = json_number_to_fraction(&params.margin);
59                let mut owners = Vec::<Ed25519KeyHash>::new();
60                for owner in &params.owners {
61                    owners.push(Ed25519KeyHash::from_hex(owner)?);
62                }
63                let mut relays = Vec::<Relay>::new();
64                for relay in &params.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(&params.publicKey)?,
95                    VRFKeyHash::from_hex(&params.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                            &params.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            // 2) Get initial delegations
116            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(&param.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}