From 48f4c3ac92928e2de829d6033ff4a8c1645a926b Mon Sep 17 00:00:00 2001 From: Enzo Cioppettini <48031343+ecioppettini@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:19:48 -0300 Subject: [PATCH 1/2] remove serde_aux and use arbitrary_precision feature for decimals --- chain/rust/Cargo.toml | 3 +-- chain/rust/src/genesis/shelley/parse.rs | 18 +++++++------- chain/rust/src/genesis/shelley/raw.rs | 32 ++++++++++--------------- 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/chain/rust/Cargo.toml b/chain/rust/Cargo.toml index 739a62fd..704f315c 100644 --- a/chain/rust/Cargo.toml +++ b/chain/rust/Cargo.toml @@ -23,7 +23,7 @@ cbor_event = "2.2.0" linked-hash-map = "0.5.3" derivative = "2.2.0" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.57" +serde_json = { version = "1.0.57", features = ["arbitrary_precision"] } schemars = "0.8.8" bech32 = "0.7.2" @@ -38,7 +38,6 @@ 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 diff --git a/chain/rust/src/genesis/shelley/parse.rs b/chain/rust/src/genesis/shelley/parse.rs index 1ec4077e..8a09d18a 100644 --- a/chain/rust/src/genesis/shelley/parse.rs +++ b/chain/rust/src/genesis/shelley/parse.rs @@ -36,8 +36,7 @@ pub enum GenesisJSONError { 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 data: raw::ShelleyGenesisData = serde_json::from_reader(json)?; let mut initial_funds = BTreeMap::new(); for (addr_hex, balance) in &data.initialFunds { @@ -55,7 +54,7 @@ pub fn parse_genesis_data( // 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 ration = fraction::Fraction::from_str(¶ms.margin.to_string()).unwrap(); let mut owners = Vec::::new(); for owner in ¶ms.owners { owners.push(Ed25519KeyHash::from_hex(owner)?); @@ -136,7 +135,8 @@ pub fn parse_genesis_data( ); } Ok(config::ShelleyGenesisData { - active_slots_coeff: fraction::Fraction::from_str(&data.activeSlotsCoeff).unwrap(), + active_slots_coeff: fraction::Fraction::from_str(&data.activeSlotsCoeff.to_string()) + .unwrap(), epoch_length: data.epochLength, gen_delegs, initial_funds, @@ -145,9 +145,9 @@ pub fn parse_genesis_data( network_id, network_magic: data.networkMagic, protocol_params: config::ShelleyGenesisProtocolParameters { - a0: fraction::Fraction::from_str(&data.protocolParams.a0).unwrap(), + a0: fraction::Fraction::from_str(&data.protocolParams.a0.to_string()).unwrap(), decentralisation_param: fraction::Fraction::from_str( - &data.protocolParams.decentralisationParam, + &data.protocolParams.decentralisationParam.to_string(), ) .unwrap(), e_max: data.protocolParams.eMax, @@ -168,11 +168,11 @@ pub fn parse_genesis_data( 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(), + rho: fraction::Fraction::from_str(&data.protocolParams.rho.to_string()).unwrap(), + tau: fraction::Fraction::from_str(&data.protocolParams.tau.to_string()).unwrap(), }, security_param: data.securityParam, - slot_length: fraction::Fraction::from_str(&data.slotLength).unwrap(), + slot_length: fraction::Fraction::from_str(&data.slotLength.to_string()).unwrap(), slots_per_kes_period: data.slotsPerKESPeriod, staking, system_start: data.systemStart.parse().expect("Failed to parse date"), diff --git a/chain/rust/src/genesis/shelley/raw.rs b/chain/rust/src/genesis/shelley/raw.rs index 58a0db72..9e623e00 100644 --- a/chain/rust/src/genesis/shelley/raw.rs +++ b/chain/rust/src/genesis/shelley/raw.rs @@ -1,6 +1,5 @@ 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 @@ -11,8 +10,7 @@ use std::collections::HashMap; #[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 activeSlotsCoeff: serde_json::Number, pub epochLength: u64, pub genDelegs: HashMap, pub initialFunds: HashMap, @@ -22,8 +20,7 @@ pub struct ShelleyGenesisData { pub networkMagic: u64, pub protocolParams: ShelleyGenesisProtocolParameters, pub securityParam: u64, - #[serde(deserialize_with = "deserialize_string_from_number")] - pub slotLength: String, + pub slotLength: serde_json::Number, pub slotsPerKESPeriod: u64, pub staking: Option, pub systemStart: String, @@ -40,12 +37,10 @@ pub struct ShelleyGenesisDelegations { #[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, + // this uses the serde_json arbitrary_precision feature to avoid lossy Rust f64 + pub a0: serde_json::Number, + // this uses the serde_json arbitrary_precision feature to avoid lossy Rust f64 + pub decentralisationParam: serde_json::Number, pub eMax: u64, pub extraEntropy: ShelleyGenesisExtraEntropy, pub keyDeposit: Coin, @@ -59,12 +54,10 @@ pub struct ShelleyGenesisProtocolParameters { 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, + // this uses the serde_json arbitrary_precision feature to avoid lossy Rust f64 + pub rho: serde_json::Number, + // this uses the serde_json arbitrary_precision feature to avoid lossy Rust f64 + pub tau: serde_json::Number, } #[allow(non_snake_case)] @@ -91,9 +84,8 @@ pub struct ShelleyGenesisStaking { #[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, + // this uses the serde_json arbitrary_precision feature to avoid lossy Rust f64 + pub margin: serde_json::Number, pub metadata: Option, pub owners: Vec, pub pledge: Coin, From 8ea3d50ab9a01d82ca72ab6d8258b4b36aad2ce7 Mon Sep 17 00:00:00 2001 From: Enzo Cioppettini <48031343+ecioppettini@users.noreply.github.com> Date: Thu, 26 Sep 2024 20:40:35 -0300 Subject: [PATCH 2/2] handle exponential notation --- chain/rust/src/genesis/shelley/parse.rs | 36 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/chain/rust/src/genesis/shelley/parse.rs b/chain/rust/src/genesis/shelley/parse.rs index 8a09d18a..7d222804 100644 --- a/chain/rust/src/genesis/shelley/parse.rs +++ b/chain/rust/src/genesis/shelley/parse.rs @@ -2,6 +2,7 @@ use cml_core::DeserializeError; use cml_crypto::{ chain_crypto::Blake2b256, Ed25519KeyHash, PoolMetadataHash, TransactionHash, VRFKeyHash, }; +use num::traits::Pow as _; use serde_json; use std::collections::BTreeMap; use std::io::Read; @@ -54,7 +55,7 @@ pub fn parse_genesis_data( // 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.to_string()).unwrap(); + let ration = json_number_to_fraction(¶ms.margin); let mut owners = Vec::::new(); for owner in ¶ms.owners { owners.push(Ed25519KeyHash::from_hex(owner)?); @@ -135,8 +136,7 @@ pub fn parse_genesis_data( ); } Ok(config::ShelleyGenesisData { - active_slots_coeff: fraction::Fraction::from_str(&data.activeSlotsCoeff.to_string()) - .unwrap(), + active_slots_coeff: json_number_to_fraction(&data.activeSlotsCoeff), epoch_length: data.epochLength, gen_delegs, initial_funds, @@ -145,11 +145,10 @@ pub fn parse_genesis_data( network_id, network_magic: data.networkMagic, protocol_params: config::ShelleyGenesisProtocolParameters { - a0: fraction::Fraction::from_str(&data.protocolParams.a0.to_string()).unwrap(), - decentralisation_param: fraction::Fraction::from_str( - &data.protocolParams.decentralisationParam.to_string(), - ) - .unwrap(), + a0: json_number_to_fraction(&data.protocolParams.a0), + decentralisation_param: json_number_to_fraction( + &data.protocolParams.decentralisationParam, + ), e_max: data.protocolParams.eMax, extra_entropy: config::ShelleyGenesisExtraEntropy { tag: data.protocolParams.extraEntropy.tag, @@ -168,11 +167,11 @@ pub fn parse_genesis_data( data.protocolParams.protocolVersion.major, data.protocolParams.protocolVersion.minor, ), - rho: fraction::Fraction::from_str(&data.protocolParams.rho.to_string()).unwrap(), - tau: fraction::Fraction::from_str(&data.protocolParams.tau.to_string()).unwrap(), + rho: json_number_to_fraction(&data.protocolParams.rho), + tau: json_number_to_fraction(&data.protocolParams.tau), }, security_param: data.securityParam, - slot_length: fraction::Fraction::from_str(&data.slotLength.to_string()).unwrap(), + slot_length: json_number_to_fraction(&data.slotLength), slots_per_kes_period: data.slotsPerKESPeriod, staking, system_start: data.systemStart.parse().expect("Failed to parse date"), @@ -180,6 +179,21 @@ pub fn parse_genesis_data( }) } +fn json_number_to_fraction(param: &serde_json::Number) -> fraction::GenericFraction { + let s = param.to_string(); + + if let Some(exp_position) = s.find('e').or_else(|| s.find('E')) { + let (a, b) = s.split_at_checked(exp_position).unwrap(); + + let exp = fraction::Ratio::from(10u64).pow(i32::from_str(&b[1..]).unwrap()); + + fraction::Fraction::from_str(a).unwrap() + * fraction::Fraction::new(*exp.numer(), *exp.denom()) + } else { + fraction::Fraction::from_str(¶m.to_string()).unwrap() + } +} + pub fn redeem_address_to_txid(pubkey: &Address) -> TransactionHash { let txid = Blake2b256::new(&pubkey.to_raw_bytes()); TransactionHash::from(*txid.as_hash_bytes())