Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ yarn
yarn start
```

### Fee Calculation Debugging

It is possible to get more information about the fee calculation process logged to
the console. Because this fee calculation happens in the statically compiled web assembly part
a re-compile with the proper environment variable set is necessary:

```
FEE_DEBUG=1 yarn
```

### Available paths

Block IDs may take two forms: a non-negative decimal integer that denotes the block _height_ **or**
Expand Down
31 changes: 31 additions & 0 deletions calc-fee/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion calc-fee/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ description = "Calculate extrinsic fees off-chain"
crate-type = ["cdylib"]

[features]
debug = ["console_error_panic_hook"]
debug = ["console_error_panic_hook", "console_log"]

[dependencies]
wasm-bindgen = { version = "0.2", default_features = false, features = ["serde-serialize"] }
serde_derive = { version = "1", default_features = false }
serde = { version = "1", default_features = false }
console_error_panic_hook = { version = "0.1", optional = true }
console_log = { version = "0.2.0", optional = true }
log = "0.4.8"

[dependencies.sp-arithmetic]
version = "2.0.0-rc3"
Expand Down
10 changes: 10 additions & 0 deletions calc-fee/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

if [ -z ${FEE_DEBUG} ]; then
wasm-pack build --target nodejs --scope polkadot "$SCRIPT_DIR"
else
echo "Fee debugging enabled"
wasm-pack build --target nodejs --scope polkadot "$SCRIPT_DIR" -- --features debug
fi
8 changes: 8 additions & 0 deletions calc-fee/src/debug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#[cfg(feature = "debug")]
pub fn setup() {
console_error_panic_hook::set_once();
console_log::init_with_level(log::Level::Debug).ok();
}

#[cfg(not(feature = "debug"))]
pub fn setup() {}
132 changes: 94 additions & 38 deletions calc-fee/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
mod panic;
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::*;
use core::str::FromStr;

type Balance = u128;
type Weight = u64;
Expand All @@ -18,48 +19,70 @@ pub struct JSCoefficient {
degree: u8,
}

#[derive(Debug)]
struct Coefficient {
coeff_integer: Balance,
coeff_frac: Perbill,
negative: bool,
degree: u8,
}

#[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),
}
}
}

#[wasm_bindgen]
#[derive(Debug)]
pub struct CalcFee {
polynomial: Vec<Coefficient>,
multiplier: Multiplier,
per_byte_fee: Balance,
base_fee: Balance,
adjust_len_fee: bool,
}

#[wasm_bindgen]
Expand All @@ -69,47 +92,68 @@ impl CalcFee {
extrinsic_base_weight: Weight,
multiplier: &str,
per_byte_fee: &str,
fixed128_bug: bool,
fixed128_legacy: bool,
) -> Self {
panic::set_hook();
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()
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);
Self {
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({}, {}) -> {:#?}", spec_name, spec_version, calc);
Some(calc)
}

pub fn calc_fee(&self, weight: Weight, len: u32) -> String {
panic::set_hook();

let len_fee = self.per_byte_fee.saturating_mul(len.into());
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(unadjusted_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);

self.base_fee.saturating_add(adjusted_fee).to_string()
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, unadjusted_len_fee, unadjusted_weight_fee, adjustable_fee,
adjusted_fee, result
);

result.to_string()
}
}

Expand All @@ -131,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())
}
4 changes: 0 additions & 4 deletions calc-fee/src/panic.rs

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"preinstall": "wasm-pack build --target nodejs --scope polkadot calc-fee",
"preinstall": "./calc-fee/build.sh",
"postinstall": "yarn upgrade @polkadot/calc-fee",
"build": "tsc",
"lint": "tsc && eslint . --ext ts",
Expand Down
Loading