// 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 . use crate::{ BridgedChainOf, Config, InboundLane, InboundLaneStorage, InboundLanes, OutboundLane, OutboundLaneStorage, OutboundLanes, OutboundMessages, StoredInboundLaneData, StoredMessagePayload, }; use bp_messages::{ target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneState, MessageKey, MessageNonce, OutboundLaneData, }; use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; use pezbp_runtime::AccountIdOf; use pezframe_support::{ensure, pezsp_runtime::RuntimeDebug, PalletError}; use pezsp_std::marker::PhantomData; use scale_info::TypeInfo; /// Lanes manager errors. #[derive( Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo, )] pub enum LanesManagerError { /// Inbound lane already exists. InboundLaneAlreadyExists, /// Outbound lane already exists. OutboundLaneAlreadyExists, /// No inbound lane with given id. UnknownInboundLane, /// No outbound lane with given id. UnknownOutboundLane, /// Inbound lane with given id is closed. ClosedInboundLane, /// Outbound lane with given id is closed. ClosedOutboundLane, /// Message dispatcher is inactive at given inbound lane. This is logical equivalent /// of the [`Self::ClosedInboundLane`] variant. LaneDispatcherInactive, } /// Message lanes manager. pub struct LanesManager(PhantomData<(T, I)>); impl, I: 'static> Default for LanesManager { fn default() -> Self { Self::new() } } impl, I: 'static> LanesManager { /// Create new lanes manager. pub fn new() -> Self { Self(PhantomData) } /// Create new inbound lane in `Opened` state. pub fn create_inbound_lane( &self, lane_id: T::LaneId, ) -> Result>, LanesManagerError> { InboundLanes::::try_mutate(lane_id, |lane| match lane { Some(_) => Err(LanesManagerError::InboundLaneAlreadyExists), None => { *lane = Some(StoredInboundLaneData(InboundLaneData { state: LaneState::Opened, ..Default::default() })); Ok(()) }, })?; self.active_inbound_lane(lane_id) } /// Create new outbound lane in `Opened` state. pub fn create_outbound_lane( &self, lane_id: T::LaneId, ) -> Result>, LanesManagerError> { OutboundLanes::::try_mutate(lane_id, |lane| match lane { Some(_) => Err(LanesManagerError::OutboundLaneAlreadyExists), None => { *lane = Some(OutboundLaneData { state: LaneState::Opened, ..Default::default() }); Ok(()) }, })?; self.active_outbound_lane(lane_id) } /// Get existing inbound lane, checking that it is in usable state. pub fn active_inbound_lane( &self, lane_id: T::LaneId, ) -> Result>, LanesManagerError> { Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, true)?)) } /// Get existing outbound lane, checking that it is in usable state. pub fn active_outbound_lane( &self, lane_id: T::LaneId, ) -> Result>, LanesManagerError> { Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, true)?)) } /// Get existing inbound lane without any additional state checks. pub fn any_state_inbound_lane( &self, lane_id: T::LaneId, ) -> Result>, LanesManagerError> { Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, false)?)) } /// Get existing outbound lane without any additional state checks. pub fn any_state_outbound_lane( &self, lane_id: T::LaneId, ) -> Result>, LanesManagerError> { Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, false)?)) } } /// Runtime inbound lane storage. pub struct RuntimeInboundLaneStorage, I: 'static = ()> { pub(crate) lane_id: T::LaneId, pub(crate) cached_data: InboundLaneData>>, } impl, I: 'static> RuntimeInboundLaneStorage { /// Creates new runtime inbound lane storage for given **existing** lane. fn from_lane_id( lane_id: T::LaneId, check_active: bool, ) -> Result, LanesManagerError> { let cached_data = InboundLanes::::get(lane_id).ok_or(LanesManagerError::UnknownInboundLane)?; if check_active { // check that the lane is not explicitly closed ensure!(cached_data.state.is_active(), LanesManagerError::ClosedInboundLane); // apart from the explicit closure, the lane may be unable to receive any messages. // Right now we do an additional check here, but it may be done later (e.g. by // explicitly closing the lane and reopening it from // `pezpallet-xcm-bridge-hub::on-initialize`) // // The fact that we only check it here, means that the `MessageDispatch` may switch // to inactive state during some message dispatch in the middle of message delivery // transaction. But we treat result of `MessageDispatch::is_active()` as a hint, so // we know that it won't drop messages - just it experiences problems with processing. // This would allow us to check that in our signed extensions, and invalidate // transaction early, thus avoiding losing honest relayers funds. This problem should // gone with relayers coordination protocol. // // There's a limit on number of messages in the message delivery transaction, so even // if we dispatch (enqueue) some additional messages, we'll know the maximal queue // length; ensure!( T::MessageDispatch::is_active(lane_id), LanesManagerError::LaneDispatcherInactive ); } Ok(RuntimeInboundLaneStorage { lane_id, cached_data: cached_data.into() }) } /// Returns number of bytes that may be subtracted from the PoV component of /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the /// maximal configured. /// /// Maximal inbound lane state set size is configured by the /// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` constant from the pezpallet configuration. The /// PoV of the call includes the maximal size of inbound lane state. If the actual size is /// smaller, we may subtract extra bytes from this component. pub fn extra_proof_size_bytes(&self) -> u64 { let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); let relayers_count = self.data().relayers.len(); let actual_encoded_len = InboundLaneData::>>::encoded_size_hint(relayers_count) .unwrap_or(usize::MAX); max_encoded_len.saturating_sub(actual_encoded_len) as _ } } impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { type Relayer = AccountIdOf>; type LaneId = T::LaneId; fn id(&self) -> Self::LaneId { self.lane_id } fn max_unrewarded_relayer_entries(&self) -> MessageNonce { BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX } fn max_unconfirmed_messages(&self) -> MessageNonce { BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX } fn data(&self) -> InboundLaneData>> { self.cached_data.clone() } fn set_data(&mut self, data: InboundLaneData>>) { self.cached_data = data.clone(); InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) } fn purge(self) { InboundLanes::::remove(self.lane_id) } } /// Runtime outbound lane storage. #[derive(Debug, PartialEq, Eq)] pub struct RuntimeOutboundLaneStorage, I: 'static> { pub(crate) lane_id: T::LaneId, pub(crate) cached_data: OutboundLaneData, pub(crate) _phantom: PhantomData<(T, I)>, } impl, I: 'static> RuntimeOutboundLaneStorage { /// Creates new runtime outbound lane storage for given **existing** lane. fn from_lane_id(lane_id: T::LaneId, check_active: bool) -> Result { let cached_data = OutboundLanes::::get(lane_id).ok_or(LanesManagerError::UnknownOutboundLane)?; ensure!( !check_active || cached_data.state.is_active(), LanesManagerError::ClosedOutboundLane ); Ok(Self { lane_id, cached_data, _phantom: PhantomData }) } } impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { type StoredMessagePayload = StoredMessagePayload; type LaneId = T::LaneId; fn id(&self) -> Self::LaneId { self.lane_id } fn data(&self) -> OutboundLaneData { self.cached_data.clone() } fn set_data(&mut self, data: OutboundLaneData) { self.cached_data = data.clone(); OutboundLanes::::insert(self.lane_id, data) } #[cfg(test)] fn message(&self, nonce: &MessageNonce) -> Option { OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) .map(Into::into) } fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload) { OutboundMessages::::insert( MessageKey { lane_id: self.lane_id, nonce }, message_payload, ); } fn remove_message(&mut self, nonce: &MessageNonce) { OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); } fn purge(self) { OutboundLanes::::remove(self.lane_id) } }