openethereum/crates/ethjson/src/spec/builtin.rs

393 lines
11 KiB
Rust

// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum 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.
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Spec builtin deserialization.
use crate::uint::Uint;
use std::collections::BTreeMap;
/// 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 EIP2565 modular exponentiation.
#[derive(Debug, PartialEq, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Modexp2565 {}
/// 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,
}
/// Bls12 pairing price
#[derive(Debug, PartialEq, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Bls12Pairing {
/// Price per final exp
pub base: u64,
/// Price per pair (Miller loop)
pub pair: u64,
}
/// Pricing for constant Bls12 operations (ADD and MUL in G1 and G2, as well as mappings)
#[derive(Debug, PartialEq, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Bls12ConstOperations {
/// Fixed price.
pub price: u64,
}
/// Pricing for constant Bls12 operations (ADD and MUL in G1, as well as mappings)
#[derive(Debug, PartialEq, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Bls12G1Multiexp {
/// Base const of the operation (G1 or G2 multiplication)
pub base: u64,
}
/// Pricing for constant Bls12 operations (ADD and MUL in G2, as well as mappings)
#[derive(Debug, PartialEq, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Bls12G2Multiexp {
/// Base const of the operation (G1 or G2 multiplication)
pub base: 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 EIP198 modular exponentiation.
Modexp(Modexp),
/// Pricing for EIP2565 modular exponentiation.
Modexp2565(Modexp2565),
/// Pricing for alt_bn128_pairing exponentiation.
AltBn128Pairing(AltBn128Pairing),
/// Pricing for constant alt_bn128 operations
AltBn128ConstOperations(AltBn128ConstOperations),
/// Pricing of constant price bls12_381 operations
Bls12ConstOperations(Bls12ConstOperations),
/// Pricing of pairing bls12_381 operation
Bls12Pairing(Bls12Pairing),
/// Pricing of bls12_381 multiexp operations in G1
Bls12G1Multiexp(Bls12G1Multiexp),
/// Pricing of bls12_381 multiexp operations in G2
Bls12G2Multiexp(Bls12G2Multiexp),
}
/// 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<Uint>,
}
/// Spec builtin.
#[derive(Debug, PartialEq, Clone)]
pub struct Builtin {
/// Builtin name.
pub name: String,
/// Builtin pricing.
pub pricing: BTreeMap<u64, PricingAt>,
}
impl From<BuiltinCompat> 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<Uint, PricingAt>),
}
/// 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<String>,
/// Builtin pricing.
pub price: Pricing,
}
#[cfg(test)]
mod tests {
use super::{
AltBn128ConstOperations, BTreeMap, Bls12G1Multiexp, Bls12G2Multiexp, Builtin,
BuiltinCompat, Linear, Modexp, Modexp2565, Pricing, PricingAt,
};
use macros::map;
use serde_json;
#[test]
fn builtin_deserialization() {
let s = r#"{
"name": "ecrecover",
"pricing": { "linear": { "base": 3000, "word": 0 } }
}"#;
let builtin: Builtin = serde_json::from_str::<BuiltinCompat>(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::<BuiltinCompat>(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::<BuiltinCompat>(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::<BuiltinCompat>(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::<BuiltinCompat>(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 })
}
]
);
}
#[test]
fn deserialization_bls12_381_multiexp_operation() {
let s = r#"{
"name": "bls12_381_g1_multiexp",
"pricing": {
"10000000": {
"price": { "bls12_g1_multiexp": { "base": 12000}}
}
}
}"#;
let builtin: Builtin = serde_json::from_str::<BuiltinCompat>(s).unwrap().into();
assert_eq!(builtin.name, "bls12_381_g1_multiexp");
assert_eq!(
builtin.pricing,
btreemap![
10000000 => PricingAt {
info: None,
price: Pricing::Bls12G1Multiexp(Bls12G1Multiexp{
base: 12000
}),
}
]
);
}
#[test]
fn deserialization_bls12_381_multiexp_operation_in_g2() {
let s = r#"{
"name": "bls12_381_g2_multiexp",
"pricing": {
"10000000": {
"price": { "bls12_g2_multiexp": { "base": 55000}}
}
}
}"#;
let builtin: Builtin = serde_json::from_str::<BuiltinCompat>(s).unwrap().into();
assert_eq!(builtin.name, "bls12_381_g2_multiexp");
assert_eq!(
builtin.pricing,
btreemap![
10000000 => PricingAt {
info: None,
price: Pricing::Bls12G2Multiexp(Bls12G2Multiexp{
base: 55000
}),
}
]
);
}
#[test]
fn deserialization_modexp2565() {
let s = r#"{
"name": "modexp",
"pricing": {
"10000000": {
"price": { "modexp2565": { } }
}
}
}"#;
let builtin: Builtin = serde_json::from_str::<BuiltinCompat>(s).unwrap().into();
assert_eq!(builtin.name, "modexp");
assert_eq!(
builtin.pricing,
btreemap![
10000000 => PricingAt {
info: None,
price: Pricing::Modexp2565(Modexp2565{}),
}
]
);
}
}