openethereum/json/src/spec/builtin.rs
Niklas Adolfsson 13729a0f7f [builtin]: support multiple prices and activations in chain spec (#11039)
* [builtin]: impl new builtin type

Have an enum to deserialize either a builtin of a single price or several prices

* [builtin]: style cleanup

* [builtin]: fix tests

* [builtin]: replace boxing with wrapper enum

* cleanup

* fix: make it backward compatible with old builtin

* fix: update chain specs

* fix: revert use of `type alias` on enum

The CI doesn't use the latest rust.

This commit reverts that change

* fix: builtin tests

* fix: revert use of `type alias` on enum

* [basic-authority]: update test-chainspec

* fix failing tests

* [builtin]: multi-prices add `info field`

It might be hard to read chain specs with several activations points.
This commit introduces a `info` field which may be used to write some
information about the current activation such as:

`Istanbul hardfork EIP-1108` or something similar.

* fix: bad rebase

Co-Authored-By: David <dvdplm@gmail.com>

* fix(grumbles): make it backward compatible

* grumbles: resolve `NOTE`

* revert chain specs changes

* rename test

Co-Authored-By: David <dvdplm@gmail.com>

* [builtin docs]: price -> Fixed price

Co-Authored-By: Andronik Ordian <write@reusable.software>

* [json]: address naming grumbles

InnerPricing -> PricingInner
PriceWithActivationAt -> PricingAt

* docs: revert changes for `AltBn128ConstOperations`

* [json]: usize -> u64

Use explicit types to cope with platform dependent issues for `usize`

* grumble: simplify `spec_backward_compability.json`

* docs: add issue link to `TODO`

* [builtin]: replace `match` with `map`

* [builtin]: add deprecation message `eip1108` params

* nits

* [json spec tests]: fix json indentation

* [json docs]: fix typos

* [json]: `compability layer` + deser to BTreeMap

Previously we had to match `Pricing::Single` and `PricingMulti` which this fixes.
It does by introducing a compability layer and into() implemenentation.

In addition, I switched the deserialization to `BTreeMap` instead of `Vec`.
That changes the format of the chain spec again

* [json]: rename `BuiltinCombat` -> `BuiltinCompat`

* Update ethcore/builtin/src/lib.rs

Co-Authored-By: David <dvdplm@gmail.com>

* [json builtin]: improve docs

Co-Authored-By: David <dvdplm@gmail.com>

* [json builtin]: improve docs

Co-Authored-By: David <dvdplm@gmail.com>

* chore(builtin): sort dependencies

* [json builtin]: deprecate `eip1108` params

* [machine]: add bench for calling builtin contract

* [machine]: reduce calls to `Builtin::is_active`

* [builtin]: fix nits

* [json]: revert breakage of chain specs

* [json builtin]: remove `eip1108` params

* [chain specs]: update to new format

* [machine]: revert changes

* [devp2p]: revert change

* [builtin]: doc nits
2019-10-31 17:09:08 +01:00

224 lines
5.9 KiB
Rust

// Copyright 2015-2019 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 <http://www.gnu.org/licenses/>.
//! 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<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::{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::<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 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 })
}
]);
}
}