1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
34// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
89// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
1314// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
1617//! Tools for supporting message lanes between two Substrate-based chains.
1819use crate::TaggedAccount;
2021use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
22use codec::{Decode, EncodeLike};
23use frame_system::AccountInfo;
24use messages_relay::Labeled;
25use pallet_balances::AccountData;
26use relay_substrate_client::{
27 metrics::{FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric},
28 AccountIdOf, BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithRewards, Client,
29 Error as SubstrateError, NonceOf,
30};
31use relay_utils::metrics::{MetricsParams, StandaloneMetric};
32use sp_core::storage::StorageData;
33use sp_runtime::{FixedPointNumber, FixedU128};
34use std::{fmt::Debug, marker::PhantomData};
3536/// Add relay accounts balance metrics.
37pub async fn add_relay_balances_metrics<C: ChainWithBalances>(
38 client: impl Client<C>,
39 metrics: &MetricsParams,
40 relay_accounts: &Vec<TaggedAccount<AccountIdOf<C>>>,
41) -> anyhow::Result<()>
42where
43BalanceOf<C>: Into<u128> + std::fmt::Debug,
44{
45if relay_accounts.is_empty() {
46return Ok(())
47 }
4849// if `tokenDecimals` is missing from system properties, we'll be using
50let token_decimals = client
51 .token_decimals()
52 .await?
53.inspect(|token_decimals| {
54log::info!(target: "bridge", "Read `tokenDecimals` for {}: {}", C::NAME, token_decimals);
55 })
56 .unwrap_or_else(|| {
57// turns out it is normal not to have this property - e.g. when polkadot binary is
58 // started using `polkadot-local` chain. Let's use minimal nominal here
59log::info!(target: "bridge", "Using default (zero) `tokenDecimals` value for {}", C::NAME);
600
61});
62let token_decimals = u32::try_from(token_decimals).map_err(|e| {
63anyhow::format_err!(
64"Token decimals value ({}) of {} doesn't fit into u32: {:?}",
65 token_decimals,
66 C::NAME,
67 e,
68 )
69 })?;
7071for account in relay_accounts {
72let relay_account_balance_metric = FloatStorageValueMetric::new(
73 AccountBalanceFromAccountInfo::<C> { token_decimals, _phantom: Default::default() },
74 client.clone(),
75 C::account_info_storage_key(account.id()),
76format!("at_{}_relay_{}_balance", C::NAME, account.tag()),
77format!("Balance of the {} relay account at the {}", account.tag(), C::NAME),
78 )?;
79 relay_account_balance_metric.register_and_spawn(&metrics.registry)?;
80 }
8182Ok(())
83}
8485/// Add relay accounts rewards metrics.
86pub async fn add_relay_rewards_metrics<C: ChainWithRewards, BC: ChainWithMessages, LaneId>(
87 client: impl Client<C>,
88 metrics: &MetricsParams,
89 relay_accounts: &Vec<TaggedAccount<AccountIdOf<C>>>,
90 lanes: &[LaneId],
91) -> anyhow::Result<()>
92where
93C::RewardBalance: Into<u128> + std::fmt::Debug,
94 C::Reward: From<RewardsAccountParams<LaneId>>,
95 LaneId: Clone + Copy + Decode + EncodeLike + Send + Sync + Labeled,
96{
97if relay_accounts.is_empty() {
98return Ok(())
99 }
100101for account in relay_accounts {
102if let Some(_) = C::WITH_CHAIN_RELAYERS_PALLET_NAME {
103for lane in lanes {
104 FloatStorageValueMetric::new(
105 FixedU128OrOne,
106 client.clone(),
107 C::account_reward_storage_key(account.id(), RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::ThisChain)),
108format!("at_{}_relay_{}_reward_for_msgs_from_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, lane.label()),
109format!("Reward of the {} relay account at {} for delivering messages from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane.label()),
110 )?.register_and_spawn(&metrics.registry)?;
111112 FloatStorageValueMetric::new(
113 FixedU128OrOne,
114 client.clone(),
115 C::account_reward_storage_key(account.id(), RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::BridgedChain)),
116format!("at_{}_relay_{}_reward_for_msgs_to_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, lane.label()),
117format!("Reward of the {} relay account at {} for delivering messages confirmations from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane.label()),
118 )?.register_and_spawn(&metrics.registry)?;
119 }
120 }
121 }
122123Ok(())
124}
125126/// Adapter for `FloatStorageValueMetric` to decode account free balance.
127#[derive(Clone, Debug)]
128struct AccountBalanceFromAccountInfo<C> {
129 token_decimals: u32,
130 _phantom: PhantomData<C>,
131}
132133impl<C> FloatStorageValue for AccountBalanceFromAccountInfo<C>
134where
135C: Chain,
136 BalanceOf<C>: Into<u128>,
137{
138type Value = FixedU128;
139140fn decode(
141&self,
142 maybe_raw_value: Option<StorageData>,
143 ) -> Result<Option<Self::Value>, SubstrateError> {
144 maybe_raw_value
145 .map(|raw_value| {
146 AccountInfo::<NonceOf<C>, AccountData<BalanceOf<C>>>::decode(&mut &raw_value.0[..])
147 .map_err(SubstrateError::ResponseParseFailed)
148 .map(|account_data| {
149 convert_to_token_balance(account_data.data.free.into(), self.token_decimals)
150 })
151 })
152 .transpose()
153 }
154}
155156/// Convert from raw `u128` balance (nominated in smallest chain token units) to the float regular
157/// tokens value.
158fn convert_to_token_balance(balance: u128, token_decimals: u32) -> FixedU128 {
159 FixedU128::from_inner(balance.saturating_mul(FixedU128::DIV / 10u128.pow(token_decimals)))
160}
161162#[cfg(test)]
163mod tests {
164use super::*;
165#[test]
166fn token_decimals_used_properly() {
167let plancks = 425_000_000_000;
168let token_decimals = 10;
169let dots = convert_to_token_balance(plancks, token_decimals);
170assert_eq!(dots, FixedU128::saturating_from_rational(425, 10));
171 }
172}