From 4cd8bbf5a944b72dad262b7c21e3142aa37b5296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Wed, 17 Jun 2020 14:10:48 +0200 Subject: [PATCH 1/3] Add debug build of calc-fee that outputs more information --- README.md | 10 ++++++++++ calc-fee/Cargo.lock | 31 +++++++++++++++++++++++++++++ calc-fee/Cargo.toml | 4 +++- calc-fee/build.sh | 10 ++++++++++ calc-fee/src/debug.rs | 8 ++++++++ calc-fee/src/lib.rs | 46 ++++++++++++++++++++++++++----------------- calc-fee/src/panic.rs | 4 ---- package.json | 2 +- src/ApiHandler.ts | 3 +++ 9 files changed, 94 insertions(+), 24 deletions(-) create mode 100755 calc-fee/build.sh create mode 100644 calc-fee/src/debug.rs delete mode 100644 calc-fee/src/panic.rs diff --git a/README.md b/README.md index 346d0cb30..73e253e5f 100644 --- a/README.md +++ b/README.md @@ -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** diff --git a/calc-fee/Cargo.lock b/calc-fee/Cargo.lock index f2e28ade6..97ed6e6e5 100644 --- a/calc-fee/Cargo.lock +++ b/calc-fee/Cargo.lock @@ -35,6 +35,8 @@ name = "calc-fee" version = "0.1.0" dependencies = [ "console_error_panic_hook", + "console_log", + "log", "serde", "serde_derive", "sp-arithmetic 2.0.0-dev", @@ -58,6 +60,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "console_log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494" +dependencies = [ + "log", + "web-sys", +] + [[package]] name = "crunchy" version = "0.2.2" @@ -85,6 +97,15 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +[[package]] +name = "js-sys" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce10c23ad2ea25ceca0093bd3192229da4c5b3c0f2de499c1ecac0d98d452177" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -366,3 +387,13 @@ name = "wasm-bindgen-shared" version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd" + +[[package]] +name = "web-sys" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b72fe77fd39e4bd3eaa4412fd299a0be6b3dfe9d2597e2f1c20beb968f41d17" +dependencies = [ + "js-sys", + "wasm-bindgen", +] diff --git a/calc-fee/Cargo.toml b/calc-fee/Cargo.toml index 0b0e4126d..3c68ae38f 100644 --- a/calc-fee/Cargo.toml +++ b/calc-fee/Cargo.toml @@ -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" diff --git a/calc-fee/build.sh b/calc-fee/build.sh new file mode 100755 index 000000000..4e795ac64 --- /dev/null +++ b/calc-fee/build.sh @@ -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 diff --git a/calc-fee/src/debug.rs b/calc-fee/src/debug.rs new file mode 100644 index 000000000..076951140 --- /dev/null +++ b/calc-fee/src/debug.rs @@ -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() {} diff --git a/calc-fee/src/lib.rs b/calc-fee/src/lib.rs index 93a74555c..4fc4fd0e6 100644 --- a/calc-fee/src/lib.rs +++ b/calc-fee/src/lib.rs @@ -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_legacy::Fixed128 as Fixed128Legacy; use wasm_bindgen::prelude::*; -use core::str::FromStr; type Balance = u128; type Weight = u64; @@ -18,6 +19,7 @@ pub struct JSCoefficient { degree: u8, } +#[derive(Debug)] struct Coefficient { coeff_integer: Balance, coeff_frac: Perbill, @@ -25,6 +27,7 @@ struct Coefficient { degree: u8, } +#[derive(Debug)] enum Multiplier { Current((FixedI128, bool)), Legacy(Fixed128Legacy), @@ -48,13 +51,14 @@ impl Multiplier { } else { mult.0.saturating_mul_acc_int(balance) } - }, + } Self::Legacy(mult) => mult.saturated_multiply_accumulate(balance), } } } #[wasm_bindgen] +#[derive(Debug)] pub struct CalcFee { polynomial: Vec, multiplier: Multiplier, @@ -72,44 +76,50 @@ impl CalcFee { fixed128_bug: bool, fixed128_legacy: bool, ) -> Self { - panic::set_hook(); + debug::setup(); + let polynomial: Vec = { let poly: Vec = polynomial.into_serde().unwrap(); - poly.iter().map(|c| - Coefficient { + poly.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() }; let multiplier = Multiplier::new( i128::from_str(multiplier).unwrap(), fixed128_legacy, - fixed128_bug + fixed128_bug, ); let per_byte_fee = Balance::from_str(per_byte_fee).unwrap(); let base_fee = weight_to_fee(&extrinsic_base_weight, &polynomial); - Self { + let calc = Self { polynomial, multiplier, per_byte_fee, base_fee, - } + }; + info!("CalcFee::withParams -> {:#?}", calc); + 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_weight_fee = weight_to_fee(&weight, &self.polynomial); + let weight_fee = weight_to_fee(&weight, &self.polynomial); - let adjustable_fee = len_fee.saturating_add(unadjusted_weight_fee); + let adjustable_fee = len_fee.saturating_add(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(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); + + result.to_string() } } diff --git a/calc-fee/src/panic.rs b/calc-fee/src/panic.rs deleted file mode 100644 index 068a7a24d..000000000 --- a/calc-fee/src/panic.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub fn set_hook() { - #[cfg(feature = "debug")] - console_error_panic_hook::set_once(); -} diff --git a/package.json b/package.json index 3407da47a..bf9921923 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/ApiHandler.ts b/src/ApiHandler.ts index 8b1c9bfd4..27d733fcc 100644 --- a/src/ApiHandler.ts +++ b/src/ApiHandler.ts @@ -68,6 +68,9 @@ export default class ApiHandler { ]); const { parentHash, number, stateRoot, extrinsicsRoot } = block.header; + + api.registry.setMetadata(await api.rpc.state.getMetadata(parentHash)); + const onInitialize = { events: [] as SanitizedEvent[] }; const onFinalize = { events: [] as SanitizedEvent[] }; From 2b9e1f304d1351fb230fec81d7eeab876efaace5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 18 Jun 2020 11:59:22 +0200 Subject: [PATCH 2/3] Move runtime quirk switching to rust --- calc-fee/src/lib.rs | 110 +++++++++++++++++++++++++++++++------------- src/ApiHandler.ts | 64 +++++++------------------- 2 files changed, 95 insertions(+), 79 deletions(-) diff --git a/calc-fee/src/lib.rs b/calc-fee/src/lib.rs index 4fc4fd0e6..9be3afcdd 100644 --- a/calc-fee/src/lib.rs +++ b/calc-fee/src/lib.rs @@ -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::*; @@ -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 { + 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; + }, + }; + 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), } } } @@ -64,6 +82,7 @@ pub struct CalcFee { multiplier: Multiplier, per_byte_fee: Balance, base_fee: Balance, + adjust_len_fee: bool, } #[wasm_bindgen] @@ -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 { debug::setup(); let polynomial: Vec = { - let poly: Vec = polynomial.into_serde().unwrap(); - poly.iter() - .map(|c| Coefficient { + let poly: Option> = 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() } @@ -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()) +} diff --git a/src/ApiHandler.ts b/src/ApiHandler.ts index 27d733fcc..03c1f16f3 100644 --- a/src/ApiHandler.ts +++ b/src/ApiHandler.ts @@ -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}`, }; From 9223ef65ed1777cd39deca67f12940ebc5066866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Thu, 18 Jun 2020 12:56:22 +0200 Subject: [PATCH 3/3] Remove debugging artifact --- src/ApiHandler.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ApiHandler.ts b/src/ApiHandler.ts index 03c1f16f3..83e146576 100644 --- a/src/ApiHandler.ts +++ b/src/ApiHandler.ts @@ -69,8 +69,6 @@ export default class ApiHandler { const { parentHash, number, stateRoot, extrinsicsRoot } = block.header; - api.registry.setMetadata(await api.rpc.state.getMetadata(parentHash)); - const onInitialize = { events: [] as SanitizedEvent[] }; const onFinalize = { events: [] as SanitizedEvent[] };