// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . //! Spec builtin deserialization. use std::collections::BTreeMap; use crate::uint::Uint; use serde::Deserialize; /// Linear pricing. #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct Linear { /// Base price. pub base: u64, /// Price for word. pub word: u64, } /// Pricing for modular exponentiation. #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct Modexp { /// Price divisor. pub divisor: u64, } /// Pricing for constant alt_bn128 operations (ECADD and ECMUL) #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct AltBn128ConstOperations { /// price pub price: u64, } /// Pricing for alt_bn128_pairing. #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct AltBn128Pairing { /// Base price. pub base: u64, /// Price per point pair. pub pair: u64, } /// Pricing variants. #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] #[serde(rename_all = "snake_case")] pub enum Pricing { /// Pricing for Blake2 compression function: each call costs the same amount per round. Blake2F { /// Price per round of Blake2 compression function. gas_per_round: u64, }, /// Linear pricing. Linear(Linear), /// Pricing for modular exponentiation. Modexp(Modexp), /// Pricing for alt_bn128_pairing exponentiation. AltBn128Pairing(AltBn128Pairing), /// Pricing for constant alt_bn128 operations AltBn128ConstOperations(AltBn128ConstOperations), } /// Builtin compability layer #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct BuiltinCompat { /// Builtin name. name: String, /// Builtin pricing. pricing: PricingCompat, /// Activation block. activate_at: Option, } /// Spec builtin. #[derive(Debug, PartialEq, Clone)] pub struct Builtin { /// Builtin name. pub name: String, /// Builtin pricing. pub pricing: BTreeMap, } impl From for Builtin { fn from(legacy: BuiltinCompat) -> Self { let pricing = match legacy.pricing { PricingCompat::Single(pricing) => { let mut map = BTreeMap::new(); let activate_at: u64 = legacy.activate_at.map_or(0, Into::into); map.insert(activate_at, PricingAt { info: None, price: pricing }); map } PricingCompat::Multi(pricings) => { pricings.into_iter().map(|(a, p)| (a.into(), p)).collect() } }; Self { name: legacy.name, pricing } } } /// Compability layer for different pricings #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(untagged)] enum PricingCompat { /// Single builtin Single(Pricing), /// Multiple builtins Multi(BTreeMap), } /// Price for a builtin, with the block number to activate it on #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct PricingAt { /// Description of the activation, e.g. "PunyPony HF, March 12, 2025". pub info: Option, /// Builtin pricing. pub price: Pricing, } #[cfg(test)] mod tests { use super::{Builtin, BuiltinCompat, BTreeMap, Pricing, PricingAt, Linear, Modexp, AltBn128ConstOperations}; use macros::map; #[test] fn builtin_deserialization() { let s = r#"{ "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } }"#; let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "ecrecover"); assert_eq!(builtin.pricing, map![ 0 => PricingAt { info: None, price: Pricing::Linear(Linear { base: 3000, word: 0 }) } ]); } #[test] fn deserialize_multiple_pricings() { let s = r#"{ "name": "ecrecover", "pricing": { "0": { "price": {"linear": { "base": 3000, "word": 0 }} }, "500": { "info": "enable fake EIP at block 500", "price": {"linear": { "base": 10, "word": 0 }} } } }"#; let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "ecrecover"); assert_eq!(builtin.pricing, map![ 0 => PricingAt { info: None, price: Pricing::Linear(Linear { base: 3000, word: 0 }) }, 500 => PricingAt { info: Some(String::from("enable fake EIP at block 500")), price: Pricing::Linear(Linear { base: 10, word: 0 }) } ]); } #[test] fn deserialization_blake2_f_builtin() { let s = r#"{ "name": "blake2_f", "activate_at": "0xffffff", "pricing": { "blake2_f": { "gas_per_round": 123 } } }"#; let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "blake2_f"); assert_eq!(builtin.pricing, map![ 0xffffff => PricingAt { info: None, price: Pricing::Blake2F { gas_per_round: 123 } } ]); } #[test] fn deserialization_alt_bn128_const_operations() { let s = r#"{ "name": "alt_bn128_mul", "pricing": { "100500": { "price": { "alt_bn128_const_operations": { "price": 123 }} } } }"#; let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "alt_bn128_mul"); assert_eq!(builtin.pricing, map![ 100500 => PricingAt { info: None, price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { price: 123, }), } ]); } #[test] fn activate_at() { let s = r#"{ "name": "late_start", "activate_at": 100000, "pricing": { "modexp": { "divisor": 5 } } }"#; let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "late_start"); assert_eq!(builtin.pricing, map![ 100_000 => PricingAt { info: None, price: Pricing::Modexp(Modexp { divisor: 5 }) } ]); } }