cml_chain/genesis/byron/
parse.rs

1use base64::{
2    engine::general_purpose::{STANDARD, URL_SAFE},
3    Engine,
4};
5use cbor_event::cbor;
6use cml_core::DeserializeError;
7use cml_crypto::{CryptoError, RawBytesEncoding};
8use serde_json;
9use std::collections::BTreeMap;
10use std::io::Read;
11use std::str::FromStr;
12use std::time::{Duration, SystemTime};
13
14use crate::byron::{
15    AddressContent, ByronAddress, ParseExtendedAddrError, ProtocolMagic, StakeholderId,
16};
17use crate::crypto::{BlockHeaderHash, TransactionHash};
18use crate::fees::LinearFee;
19use cml_crypto::chain_crypto::byron_proxy_key::ByronProxySecretKey;
20use cml_crypto::chain_crypto::{
21    self, Blake2b256, Ed25519, Ed25519Bip32, Signature, SignatureFromStrError,
22};
23use cml_crypto::{blake2b256, Bip32PublicKey};
24
25use super::{config, raw};
26
27#[derive(Debug, thiserror::Error)]
28pub enum GenesisJSONError {
29    #[error("JSON: {0:?}")]
30    Serde(#[from] serde_json::Error),
31    #[error("Crypto: {0:?}")]
32    CryptoError(#[from] CryptoError),
33    #[error("Deserialize: {0:?}")]
34    DeserializeError(#[from] DeserializeError),
35    #[error("Base64: {0:?}")]
36    Base64(#[from] base64::DecodeError),
37    #[error("ParseInt: {0:?}")]
38    ParseInt(#[from] std::num::ParseIntError),
39    #[error("ByronAddress: {0:?}")]
40    ByronAddress(#[from] ParseExtendedAddrError),
41    #[error("SignatureParse: {0:?}")]
42    SignatureParse(#[from] SignatureFromStrError),
43    #[error("Stakeholder not found: {0}")]
44    StakeholderMissing(String),
45}
46
47pub fn parse_genesis_data<R: Read>(json: R) -> Result<config::GenesisData, GenesisJSONError> {
48    let data_value: serde_json::Value = serde_json::from_reader(json)?;
49    let genesis_prev =
50        BlockHeaderHash::from_raw_bytes(&blake2b256(data_value.to_string().as_bytes()))?;
51    let data: raw::GenesisData = serde_json::from_value(data_value.clone())?;
52
53    let protocol_magic = ProtocolMagic::from(data.protocolConsts.protocolMagic);
54
55    let parse_fee_constant = |s: &str| s.parse::<u64>();
56
57    let mut avvm_distr = BTreeMap::new();
58    for (avvm, balance) in &data.avvmDistr {
59        avvm_distr.insert(
60            chain_crypto::PublicKey::<Ed25519>::from_binary(&URL_SAFE.decode(avvm)?)
61                .map_err(CryptoError::from)?,
62            balance.parse::<u64>()?,
63        );
64    }
65
66    let slot_duration = {
67        let v = data.blockVersionData.slotDuration.parse::<u64>()?;
68        Duration::from_millis(v)
69    };
70
71    let start_time = {
72        let unix_displacement = Duration::from_secs(data.startTime);
73        SystemTime::UNIX_EPOCH + unix_displacement
74    };
75
76    let mut non_avvm_balances = BTreeMap::new();
77    for (address, balance) in &data.nonAvvmBalances {
78        non_avvm_balances.insert(ByronAddress::from_str(address)?, balance.parse::<u64>()?);
79    }
80
81    let mut boot_stakeholders = BTreeMap::new();
82
83    for (stakeholder_id, weight) in &data.bootStakeholders {
84        let heavy = data
85            .heavyDelegation
86            .get(stakeholder_id)
87            .ok_or_else(|| GenesisJSONError::StakeholderMissing(stakeholder_id.clone()))?;
88
89        let stakeholder_id = StakeholderId::from_hex(stakeholder_id)?;
90
91        let psk = ByronProxySecretKey {
92            omega: 0,
93            issuer_pk: chain_crypto::PublicKey::<Ed25519Bip32>::from_binary(
94                &STANDARD.decode(&heavy.issuerPk)?,
95            )
96            .map_err(CryptoError::from)?,
97            delegate_pk: chain_crypto::PublicKey::<Ed25519Bip32>::from_binary(
98                &STANDARD.decode(&heavy.delegatePk)?,
99            )
100            .map_err(CryptoError::from)?,
101            cert: Signature::<(), Ed25519Bip32>::from_str(&heavy.cert)?,
102        };
103
104        // Check that the stakeholder ID corresponds to the issuer public key.
105        assert_eq!(
106            stakeholder_id,
107            StakeholderId::new(&Bip32PublicKey(psk.issuer_pk.clone()))
108        );
109
110        // Check that the certificate is correct.
111        assert!(psk.verify(protocol_magic));
112
113        boot_stakeholders.insert(
114            stakeholder_id,
115            config::BootStakeholder {
116                weight: *weight,
117                issuer_pk: psk.issuer_pk,
118                delegate_pk: psk.delegate_pk,
119                cert: psk.cert,
120            },
121        );
122    }
123
124    Ok(config::GenesisData {
125        genesis_prev,
126        epoch_stability_depth: data.protocolConsts.k,
127        protocol_magic,
128        fee_policy: LinearFee::new(
129            parse_fee_constant(&data.blockVersionData.txFeePolicy.multiplier)?,
130            parse_fee_constant(&data.blockVersionData.txFeePolicy.summand)?,
131            0,
132        ),
133        avvm_distr,
134        non_avvm_balances,
135        start_time,
136        slot_duration,
137        boot_stakeholders,
138    })
139}
140
141pub fn canonicalize_json<R: Read>(json: R) -> Result<String, GenesisJSONError> {
142    let data: serde_json::Value = serde_json::from_reader(json)?;
143    Ok(data.to_string())
144}
145
146pub fn redeem_pubkey_to_txid(
147    pubkey: &chain_crypto::PublicKey<Ed25519>,
148    protocol_magic: Option<ProtocolMagic>,
149) -> (TransactionHash, ByronAddress) {
150    let address_content =
151        AddressContent::new_redeem(cml_crypto::PublicKey(pubkey.clone()), protocol_magic);
152    let byron_address = address_content.to_address();
153    let txid = Blake2b256::new(&cbor!(&byron_address).unwrap());
154    (TransactionHash::from(*txid.as_hash_bytes()), byron_address)
155}
156
157#[cfg(test)]
158mod test {
159    use super::*;
160
161    use crate::crypto::BlockHeaderHash;
162
163    fn get_test_genesis_data(genesis_prev: &BlockHeaderHash) -> Result<&str, BlockHeaderHash> {
164        if genesis_prev
165            == &BlockHeaderHash::from_hex(
166                "5f20df933584822601f9e3f8c024eb5eb252fe8cefb24d1317dc3d432e940ebb",
167            )
168            .unwrap()
169        {
170            Ok(include_str!(
171                "./test_data/5f20df933584822601f9e3f8c024eb5eb252fe8cefb24d1317dc3d432e940ebb.json"
172            ))
173        } else if genesis_prev
174            == &BlockHeaderHash::from_hex(
175                "b7f76950bc4866423538ab7764fc1c7020b24a5f717a5bee3109ff2796567214",
176            )
177            .unwrap()
178        {
179            Ok(include_str!(
180                "./test_data/b7f76950bc4866423538ab7764fc1c7020b24a5f717a5bee3109ff2796567214.json"
181            ))
182        } else if genesis_prev
183            == &BlockHeaderHash::from_hex(
184                "c6a004d3d178f600cd8caa10abbebe1549bef878f0665aea2903472d5abf7323",
185            )
186            .unwrap()
187        {
188            Ok(include_str!(
189                "./test_data/c6a004d3d178f600cd8caa10abbebe1549bef878f0665aea2903472d5abf7323.json"
190            ))
191        } else if genesis_prev
192            == &BlockHeaderHash::from_hex(
193                "96fceff972c2c06bd3bb5243c39215333be6d56aaf4823073dca31afe5038471",
194            )
195            .unwrap()
196        {
197            Ok(include_str!(
198                "./test_data/96fceff972c2c06bd3bb5243c39215333be6d56aaf4823073dca31afe5038471.json"
199            ))
200        } else {
201            Err(*genesis_prev)
202        }
203    }
204
205    #[test]
206    pub fn calc_redeem_txid() {
207        let (hash, address) = redeem_pubkey_to_txid(
208            &chain_crypto::PublicKey::<Ed25519>::from_binary(
209                &URL_SAFE
210                    .decode("AAG3vJwTzCcL0zp2-1yfI-mn_7haYvSYJln2xR_aBS8=")
211                    .unwrap(),
212            )
213            .unwrap(),
214            None,
215        );
216        assert_eq!(
217            hash.to_hex(),
218            "927edb96f3386ab91b5f5d85d84cb4253c65b1c2f65fa7df25f81fab1d62987a"
219        );
220        assert_eq!(
221            address.to_base58(),
222            "Ae2tdPwUPEZ9vtyppa1FdJzvqJZkEcXgdHxVYAzTWcPaoNycVq5rc36LC1S"
223        );
224    }
225
226    #[test]
227    pub fn parse_test_genesis_files() {
228        let genesis_hash = BlockHeaderHash::from_hex(
229            "c6a004d3d178f600cd8caa10abbebe1549bef878f0665aea2903472d5abf7323",
230        )
231        .unwrap();
232
233        let genesis_data =
234            super::parse_genesis_data(get_test_genesis_data(&genesis_hash).unwrap().as_bytes())
235                .unwrap();
236
237        assert_eq!(genesis_data.epoch_stability_depth, 2160);
238        assert_eq!(
239            genesis_data
240                .start_time
241                .duration_since(SystemTime::UNIX_EPOCH)
242                .unwrap()
243                .as_secs(),
244            1506450213
245        );
246        assert_eq!(genesis_data.slot_duration.as_secs(), 20);
247        assert_eq!(genesis_data.slot_duration.subsec_millis(), 0);
248        assert_eq!(genesis_data.protocol_magic, 633343913.into());
249        assert_eq!(genesis_data.fee_policy.coefficient, 43946 * 1_000_000u64);
250        assert_eq!(genesis_data.fee_policy.constant, 155381 * 1_000_000_000u64);
251
252        assert_eq!(
253            URL_SAFE.encode(
254                genesis_data
255                    .avvm_distr
256                    .iter()
257                    .find(|(_, v)| **v == 9999300000000u64)
258                    .unwrap()
259                    .0
260            ),
261            "-0BJDi-gauylk4LptQTgjMeo7kY9lTCbZv12vwOSTZk="
262        );
263
264        let genesis_hash = BlockHeaderHash::from_hex(
265            "b7f76950bc4866423538ab7764fc1c7020b24a5f717a5bee3109ff2796567214",
266        )
267        .unwrap();
268
269        let genesis_data =
270            super::parse_genesis_data(get_test_genesis_data(&genesis_hash).unwrap().as_bytes())
271                .unwrap();
272
273        assert_eq!(
274            *genesis_data
275                .non_avvm_balances
276                .iter()
277                .find(|(n, _)| n.to_string()
278                    == "2cWKMJemoBaheSTiK9XEtQDf47Z3My8jwN25o5jjm7s7jaXin2nothhWQrTDd8m433M8K")
279                .unwrap()
280                .1,
281            5428571428571429u64
282        );
283    }
284}