Newer
Older
Svyatoslav Nikolsky
committed
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common 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 Bridges Common 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 Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Substrate client as Substrate messages target. The chain we connect to should have
//! runtime that implements `<BridgedChainName>HeaderApi` to allow bridging with
//! <BridgedName> chain.
use crate::messages_lane::SubstrateMessageLane;
use crate::messages_source::{read_client_state, SubstrateMessagesProof};
use crate::on_demand_headers::OnDemandHeadersRelay;
use async_trait::async_trait;
use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState};
use bp_runtime::ChainId;
use bridge_runtime_common::messages::{
source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof,
};
use codec::{Decode, Encode};
use frame_support::{traits::Instance, weights::Weight};
message_lane::{SourceHeaderIdOf, TargetHeaderIdOf},
message_lane_loop::{TargetClient, TargetClientState},
};
use num_traits::{Bounded, One, Zero};
use relay_substrate_client::{Chain, Client, Error as SubstrateError, HashOf};
use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId};
use sp_runtime::{traits::Header as HeaderT, DeserializeOwned, FixedPointNumber, FixedU128};
use std::{convert::TryFrom, marker::PhantomData, ops::RangeInclusive};
/// Message receiving proof returned by the target Substrate node.
pub type SubstrateMessagesReceivingProof<C> = (
UnrewardedRelayersState,
FromBridgedChainMessagesDeliveryProof<HashOf<C>>,
);
/// Substrate client as Substrate messages target.
pub struct SubstrateMessagesTarget<SC: Chain, TC: Chain, P: SubstrateMessageLane, I> {
client: Client<TC>,
lane: P,
lane_id: LaneId,
instance: ChainId,
source_to_target_headers_relay: Option<OnDemandHeadersRelay<SC>>,
impl<SC: Chain, TC: Chain, P: SubstrateMessageLane, I> SubstrateMessagesTarget<SC, TC, P, I> {
/// Create new Substrate headers target.
lane: P,
lane_id: LaneId,
instance: ChainId,
source_to_target_headers_relay: Option<OnDemandHeadersRelay<SC>>,
SubstrateMessagesTarget {
client,
lane,
source_to_target_headers_relay,
Svyatoslav Nikolsky
committed
_phantom: Default::default(),
impl<SC: Chain, TC: Chain, P: SubstrateMessageLane, I> Clone for SubstrateMessagesTarget<SC, TC, P, I> {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
lane: self.lane.clone(),
lane_id: self.lane_id,
source_to_target_headers_relay: self.source_to_target_headers_relay.clone(),
Svyatoslav Nikolsky
committed
_phantom: Default::default(),
impl<SC, TC, P, I> RelayClient for SubstrateMessagesTarget<SC, TC, P, I>
Svyatoslav Nikolsky
committed
where
Svyatoslav Nikolsky
committed
P: SubstrateMessageLane,
I: Send + Sync + Instance,
{
type Error = SubstrateError;
async fn reconnect(&mut self) -> Result<(), SubstrateError> {
self.client.reconnect().await
}
}
impl<SC, TC, P, I> TargetClient<P> for SubstrateMessagesTarget<SC, TC, P, I>
SC: Chain<Hash = P::SourceHeaderHash, BlockNumber = P::SourceHeaderNumber, Balance = P::SourceChainBalance>,
SC::Balance: TryFrom<TC::Balance> + Bounded,
TC: Chain<Hash = P::TargetHeaderHash, BlockNumber = P::TargetHeaderNumber>,
TC::Hash: Copy,
TC::BlockNumber: Copy,
TC::Header: DeserializeOwned,
TC::Index: DeserializeOwned,
<TC::Header as HeaderT>::Number: BlockNumberBase,
MessagesProof = SubstrateMessagesProof<SC>,
MessagesReceivingProof = SubstrateMessagesReceivingProof<TC>,
SourceChain = SC,
TargetChain = TC,
>,
P::SourceHeaderNumber: Decode,
P::SourceHeaderHash: Decode,
Svyatoslav Nikolsky
committed
I: Send + Sync + Instance,
async fn state(&self) -> Result<TargetClientState<P>, SubstrateError> {
// we can't continue to deliver messages if target node is out of sync, because
// it may have already received (some of) messages that we're going to deliver
self.client.ensure_synced().await?;
read_client_state::<_, P::SourceHeaderHash, P::SourceHeaderNumber>(
&self.client,
P::BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET,
)
.await
}
async fn latest_received_nonce(
&self,
id: TargetHeaderIdOf<P>,
) -> Result<(TargetHeaderIdOf<P>, MessageNonce), SubstrateError> {
let encoded_response = self
.client
.state_call(
P::INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD.into(),
Bytes(self.lane_id.encode()),
Some(id.1),
)
.await?;
let latest_received_nonce: MessageNonce =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, latest_received_nonce))
}
async fn latest_confirmed_received_nonce(
&self,
id: TargetHeaderIdOf<P>,
) -> Result<(TargetHeaderIdOf<P>, MessageNonce), SubstrateError> {
let encoded_response = self
.client
.state_call(
P::INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD.into(),
Bytes(self.lane_id.encode()),
Some(id.1),
)
.await?;
let latest_received_nonce: MessageNonce =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, latest_received_nonce))
}
async fn unrewarded_relayers_state(
&self,
id: TargetHeaderIdOf<P>,
) -> Result<(TargetHeaderIdOf<P>, UnrewardedRelayersState), SubstrateError> {
let encoded_response = self
.client
.state_call(
P::INBOUND_LANE_UNREWARDED_RELAYERS_STATE.into(),
Bytes(self.lane_id.encode()),
Some(id.1),
)
.await?;
let unrewarded_relayers_state: UnrewardedRelayersState =
Decode::decode(&mut &encoded_response.0[..]).map_err(SubstrateError::ResponseParseFailed)?;
Ok((id, unrewarded_relayers_state))
}
async fn prove_messages_receiving(
&self,
id: TargetHeaderIdOf<P>,
) -> Result<(TargetHeaderIdOf<P>, P::MessagesReceivingProof), SubstrateError> {
Svyatoslav Nikolsky
committed
let (id, relayers_state) = self.unrewarded_relayers_state(id).await?;
let inbound_data_key = pallet_bridge_messages::storage_keys::inbound_lane_data_key::<I>(&self.lane_id);
let proof = self
.client
Svyatoslav Nikolsky
committed
.prove_storage(vec![inbound_data_key], id.1)
.await?
.iter_nodes()
.collect();
let proof = FromBridgedChainMessagesDeliveryProof {
bridged_header_hash: id.1,
storage_proof: proof,
lane: self.lane_id,
};
Svyatoslav Nikolsky
committed
Ok((id, (relayers_state, proof)))
}
async fn submit_messages_proof(
&self,
generated_at_header: SourceHeaderIdOf<P>,
nonces: RangeInclusive<MessageNonce>,
) -> Result<RangeInclusive<MessageNonce>, SubstrateError> {
self.client
.submit_signed_extrinsic(self.lane.target_transactions_author(), |transaction_nonce| {
self.lane.make_messages_delivery_transaction(
transaction_nonce,
generated_at_header,
nonces.clone(),
proof,
)
})
.await?;
Ok(nonces)
}
async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<P>) {
if let Some(ref source_to_target_headers_relay) = self.source_to_target_headers_relay {
source_to_target_headers_relay.require_finalized_header(id).await;
async fn estimate_delivery_transaction_in_source_tokens(
&self,
nonces: RangeInclusive<MessageNonce>,
total_dispatch_weight: Weight,
total_size: u32,
) -> P::SourceChainBalance {
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
// TODO: use actual rate (https://github.com/paritytech/parity-bridges-common/issues/997)
convert_target_tokens_to_source_tokens::<SC, TC>(
FixedU128::one(),
self.client
.estimate_extrinsic_fee(self.lane.make_messages_delivery_transaction(
Zero::zero(),
HeaderId(Default::default(), Default::default()),
nonces.clone(),
prepare_dummy_messages_proof::<SC>(nonces, total_dispatch_weight, total_size),
))
.await
.unwrap_or_else(|_| TC::Balance::max_value()),
)
}
}
/// Prepare 'dummy' messages proof that will compose the delivery transaction.
///
/// We don't care about proof actually being the valid proof, because its validity doesn't
/// affect the call weight - we only care about its size.
fn prepare_dummy_messages_proof<SC: Chain>(
nonces: RangeInclusive<MessageNonce>,
total_dispatch_weight: Weight,
total_size: u32,
) -> SubstrateMessagesProof<SC> {
(
total_dispatch_weight,
FromBridgedChainMessagesProof {
bridged_header_hash: Default::default(),
storage_proof: vec![vec![0; SC::STORAGE_PROOF_OVERHEAD.saturating_add(total_size) as usize]],
lane: Default::default(),
nonces_start: *nonces.start(),
nonces_end: *nonces.end(),
},
)
}
/// Given delivery transaction fee in target chain tokens and conversion rate to the source
/// chain tokens, compute transaction cost in source chain tokens.
fn convert_target_tokens_to_source_tokens<SC: Chain, TC: Chain>(
target_to_source_conversion_rate: FixedU128,
target_transaction_fee: TC::Balance,
) -> SC::Balance
where
SC::Balance: TryFrom<TC::Balance>,
{
SC::Balance::try_from(target_to_source_conversion_rate.saturating_mul_int(target_transaction_fee))
.unwrap_or_else(|_| SC::Balance::max_value())
}
#[cfg(test)]
mod tests {
use super::*;
use relay_millau_client::Millau;
use relay_rialto_client::Rialto;
#[test]
fn prepare_dummy_messages_proof_works() {
const DISPATCH_WEIGHT: Weight = 1_000_000;
const SIZE: u32 = 1_000;
let dummy_proof = prepare_dummy_messages_proof::<Rialto>(1..=10, DISPATCH_WEIGHT, SIZE);
assert_eq!(dummy_proof.0, DISPATCH_WEIGHT);
assert!(
dummy_proof.1.encode().len() as u32 > SIZE,
"Expected proof size at least {}. Got: {}",
SIZE,
dummy_proof.1.encode().len(),
);
}
#[test]
fn convert_target_tokens_to_source_tokens_works() {
assert_eq!(
convert_target_tokens_to_source_tokens::<Rialto, Millau>((150, 100).into(), 1_000),
1_500
);
assert_eq!(
convert_target_tokens_to_source_tokens::<Rialto, Millau>((50, 100).into(), 1_000),
500
);
assert_eq!(
convert_target_tokens_to_source_tokens::<Rialto, Millau>((100, 100).into(), 1_000),
1_000
);