Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move runtime quirk switching to rust
  • Loading branch information
athei committed Jun 18, 2020
commit 2b9e1f304d1351fb230fec81d7eeab876efaace5
110 changes: 78 additions & 32 deletions calc-fee/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod debug;
use core::str::FromStr;
use log::info;
use serde_derive::Deserialize;
use sp_arithmetic::{FixedI128, FixedPointNumber, Perbill};
use sp_arithmetic::{FixedI128, FixedU128, FixedPointNumber, Perbill};
use sp_arithmetic_legacy::Fixed128 as Fixed128Legacy;
use wasm_bindgen::prelude::*;

Expand All @@ -29,30 +29,48 @@ struct Coefficient {

#[derive(Debug)]
enum Multiplier {
Current((FixedI128, bool)),
Legacy(Fixed128Legacy),
V0(Fixed128Legacy),
V1((FixedI128, bool)),
V2(FixedU128),
}

impl Multiplier {
fn new(inner: i128, use_legacy: bool, bug: bool) -> Self {
if use_legacy {
Self::Legacy(Fixed128Legacy::from_parts(inner))
} else {
Self::Current((FixedI128::from_inner(inner), bug))
}
fn new(inner: &str, spec_name: &str, spec_version: u32) -> Option<Self> {
use Multiplier::{V0, V1, V2};
let mult = match (spec_name, spec_version) {
("polkadot", 0) => V1((new_i128(inner), true)),
("polkadot", v) if v < 11 => V1((new_i128(inner), false)),
("polkadot", v) if 11 <= v => V2(new_u128(inner)),

("kusama", 1062) => V0(new_legacy_128(inner)),
("kusama", v) if 1062 < v && v < 2011 => V1((new_i128(inner), false)),
("kusama", v) if 2011 <= v => V2(new_u128(inner)),

("westend", 10) => V0(new_legacy_128(inner)),
("westend", v) if 10 < v && v < 31 => V1((new_i128(inner), false)),
("westend", v) if 31 <= v => V2(new_u128(inner)),

_ => {
info!("Unsupported runtime: {}#{}", spec_name, spec_version);
return None;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This warning is a good idea, and I think it works well for now. It does make me think though that in the future we should maybe think about how to make all of sidecar easily configurable for a wide array of substrate based chains. Polkadot-js already easily supports adding types, and I am wondering if we could do something of similar effect with fee stuff in this crate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of bailing we could just use the current Multiplier as long as the chain supplied a valid Polynomial. That would instantly support all current chains that use the ChargeTransactionPayment (should be most of them).

So maybe we should rather explicitly ban unsupported versions of our runtimes than disallowing anything we do not know.

},
};
Some(mult)
}

fn calc(&self, balance: Balance) -> Balance {
match self {
Self::Current(mult) => {
if mult.1 && mult.0.is_negative() {
Self::V0(mult) => mult.saturated_multiply_accumulate(balance),
Self::V1((mult, negative_bug)) => {
if *negative_bug && mult.is_negative() {
// replicate the fixed128 bug where negative coefficients are not considered
balance
} else {
mult.0.saturating_mul_acc_int(balance)
mult.saturating_mul_acc_int(balance)
}
}
Self::Legacy(mult) => mult.saturated_multiply_accumulate(balance),
},
// V2 changed the range to [0, inf]: we no longer accumulate (only multiply)
Self::V2(mult) => mult.saturating_mul_int(balance),
}
}
}
Expand All @@ -64,6 +82,7 @@ pub struct CalcFee {
multiplier: Multiplier,
per_byte_fee: Balance,
base_fee: Balance,
adjust_len_fee: bool,
}

#[wasm_bindgen]
Expand All @@ -73,51 +92,66 @@ impl CalcFee {
extrinsic_base_weight: Weight,
multiplier: &str,
per_byte_fee: &str,
fixed128_bug: bool,
fixed128_legacy: bool,
) -> Self {
spec_name: &str,
spec_version: u32,
) -> Option<CalcFee> {
debug::setup();

let polynomial: Vec<Coefficient> = {
let poly: Vec<JSCoefficient> = polynomial.into_serde().unwrap();
poly.iter()
.map(|c| Coefficient {
let poly: Option<Vec<JSCoefficient>> = polynomial.into_serde().unwrap();
poly.map(|vec| vec.iter().map(|c| Coefficient {
coeff_integer: Balance::from_str(&c.coeffInteger).unwrap(),
coeff_frac: Perbill::from_parts(c.coeffFrac),
negative: c.negative,
degree: c.degree,
})
.collect()
).unwrap_or_else(|| vec![Coefficient {
coeff_integer: 8,
coeff_frac: Perbill::from_parts(0),
negative: false,
degree: 1
}])
};
let multiplier = Multiplier::new(
i128::from_str(multiplier).unwrap(),
fixed128_legacy,
fixed128_bug,
);
let multiplier = Multiplier::new(multiplier, spec_name, spec_version)?;
let per_byte_fee = Balance::from_str(per_byte_fee).unwrap();
let base_fee = weight_to_fee(&extrinsic_base_weight, &polynomial);
let adjust_len_fee = if let Multiplier::V2(_) = &multiplier {
false
} else {
true
};
let calc = Self {
polynomial,
multiplier,
per_byte_fee,
base_fee,
adjust_len_fee,
};
info!("CalcFee::withParams -> {:#?}", calc);
calc
info!("CalcFee::withParams({}, {}) -> {:#?}", spec_name, spec_version, calc);
Some(calc)
}

pub fn calc_fee(&self, weight: Weight, len: u32) -> String {
let len_fee = self.per_byte_fee.saturating_mul(len.into());
let weight_fee = weight_to_fee(&weight, &self.polynomial);
let unadjusted_len_fee = self.per_byte_fee.saturating_mul(len.into());
let unadjusted_weight_fee = weight_to_fee(&weight, &self.polynomial);

let adjustable_fee = len_fee.saturating_add(weight_fee);
let (len_fee, adjustable_fee) = if self.adjust_len_fee {
(0, unadjusted_len_fee.saturating_add(unadjusted_weight_fee))
} else {
(unadjusted_len_fee, unadjusted_weight_fee)
};
let adjusted_fee = self.multiplier.calc(adjustable_fee);

let result = self.base_fee.saturating_add(adjusted_fee);
let result = self.base_fee
.saturating_add(len_fee)
.saturating_add(adjusted_fee);

info!("calc_fee: ({}, {}) -> len_fee: {} weight_fee: {} adjustable_fee: {} \
adjusted_fee: {} result: {}",
weight, len, len_fee, weight_fee, adjustable_fee, adjusted_fee, result);
weight, len, unadjusted_len_fee, unadjusted_weight_fee, adjustable_fee,
adjusted_fee, result
);

result.to_string()
}
Expand All @@ -141,3 +175,15 @@ fn weight_to_fee(weight: &Weight, polynomial: &[Coefficient]) -> Balance {
acc
})
}

fn new_i128(inner: &str) -> FixedI128 {
FixedI128::from_inner(i128::from_str(inner).unwrap())
}

fn new_u128(inner: &str) -> FixedU128 {
FixedU128::from_inner(u128::from_str(inner).unwrap())
}

fn new_legacy_128(inner: &str) -> Fixed128Legacy {
Fixed128Legacy::from_parts(i128::from_str(inner).unwrap())
}
64 changes: 17 additions & 47 deletions src/ApiHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,59 +191,29 @@ export default class ApiHandler {
const version = await api.rpc.state.getRuntimeVersion(parentHash);
const specName = version.specName.toString();
const specVersion = version.specVersion.toNumber();
const fixed128Bug = specName === 'polkadot' && specVersion === 0;
let fixed128Legacy = false;
const coefficients = (function () {
if (specName === 'kusama' && specVersion === 1062) {
fixed128Legacy = true;
return [
{
coeffInteger: '8',
coeffFrac: 0,
degree: 1,
negative: false,
},
];
} else if (
specName === 'polkadot' ||
(specName === 'kusama' && specVersion > 1062)
) {
return api.consts.transactionPayment.weightToFee.map(function (
c
) {
return {
coeffInteger: c.coeffInteger.toString(),
coeffFrac: c.coeffFrac,
degree: c.degree,
negative: c.negative,
};
});
} else {
// fee calculation not supported for this runtime
return null;
}
})();
const calcFee = (function () {
if (coefficients !== null) {
return CalcFee.from_params(
coefficients,
BigInt(extrinsicBaseWeight.toString()),
multiplier.toString(),
perByte.toString(),
fixed128Bug,
fixed128Legacy
);
} else {
return null;
}
})();
const coefficients = api.consts.transactionPayment.weightToFee.map(function(c) {
return {
coeffInteger: c.coeffInteger.toString(),
coeffFrac: c.coeffFrac,
degree: c.degree,
negative: c.negative,
};
});
const calcFee = CalcFee.from_params(
coefficients,
BigInt(extrinsicBaseWeight.toString()),
multiplier.toString(),
perByte.toString(),
specName,
specVersion,
);

for (let idx = 0; idx < block.extrinsics.length; ++idx) {
if (!extrinsics[idx].paysFee || !block.extrinsics[idx].isSigned) {
continue;
}

if (calcFee === null) {
if (calcFee === null || calcFee === undefined) {
extrinsics[idx].info = {
error: `Fee calculation not supported for ${specName}#${specVersion}`,
};
Expand Down