Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XDM: ensure max outgoing messages per channel is atleast one and ensure to return open channel that can still accept messages #3356

Merged
merged 3 commits into from
Jan 24, 2025
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
4 changes: 4 additions & 0 deletions Cargo.lock

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

7 changes: 6 additions & 1 deletion crates/subspace-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use core::num::NonZeroU64;
use domain_runtime_primitives::opaque::Header as DomainHeader;
use domain_runtime_primitives::{
maximum_domain_block_weight, AccountIdConverter, BlockNumber as DomainNumber,
Hash as DomainHash,
Hash as DomainHash, MAX_OUTGOING_MESSAGES,
};
use frame_support::genesis_builder_helper::{build_state, get_preset};
use frame_support::inherent::ProvideInherent;
Expand Down Expand Up @@ -650,8 +650,12 @@ parameter_types! {
pub const ChannelInitReservePortion: Perbill = Perbill::from_percent(20);
// TODO update the fee model
pub const ChannelFeeModel: FeeModel<Balance> = FeeModel{relay_fee: SSC};
pub const MaxOutgoingMessages: u32 = MAX_OUTGOING_MESSAGES;
}

// ensure the max outgoing messages is not 0.
const_assert!(MaxOutgoingMessages::get() >= 1);

pub struct DomainRegistration;
impl sp_messenger::DomainRegistration for DomainRegistration {
fn is_domain_registered(domain_id: DomainId) -> bool {
Expand Down Expand Up @@ -684,6 +688,7 @@ impl pallet_messenger::Config for Runtime {
type ChannelInitReservePortion = ChannelInitReservePortion;
type DomainRegistration = DomainRegistration;
type ChannelFeeModel = ChannelFeeModel;
type MaxOutgoingMessages = MaxOutgoingMessages;
}

impl<C> frame_system::offchain::SendTransactionTypes<C> for Runtime
Expand Down
18 changes: 0 additions & 18 deletions domains/client/domain-operator/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3784,9 +3784,6 @@ async fn test_domain_sudo_calls() {
.construct_and_send_extrinsic(evm_domain_test_runtime::RuntimeCall::Messenger(
pallet_messenger::Call::initiate_channel {
dst_chain_id: ChainId::Consensus,
params: pallet_messenger::InitiateChannelParams {
max_outgoing_messages: 100,
},
},
))
.await
Expand Down Expand Up @@ -3927,9 +3924,6 @@ async fn test_xdm_between_consensus_and_domain_should_work() {
.construct_and_send_extrinsic(evm_domain_test_runtime::RuntimeCall::Messenger(
pallet_messenger::Call::initiate_channel {
dst_chain_id: ChainId::Consensus,
params: pallet_messenger::InitiateChannelParams {
max_outgoing_messages: 100,
},
},
))
.await
Expand Down Expand Up @@ -4140,9 +4134,6 @@ async fn test_xdm_between_domains_should_work() {
bob.construct_and_send_extrinsic(auto_id_domain_test_runtime::RuntimeCall::Messenger(
pallet_messenger::Call::initiate_channel {
dst_chain_id: ChainId::Domain(EVM_DOMAIN_ID),
params: pallet_messenger::InitiateChannelParams {
max_outgoing_messages: 100,
},
},
))
.await
Expand Down Expand Up @@ -4294,9 +4285,6 @@ async fn test_unordered_cross_domains_message_should_work() {
.construct_and_send_extrinsic(evm_domain_test_runtime::RuntimeCall::Messenger(
pallet_messenger::Call::initiate_channel {
dst_chain_id: ChainId::Consensus,
params: pallet_messenger::InitiateChannelParams {
max_outgoing_messages: 100,
},
},
))
.await
Expand Down Expand Up @@ -5430,9 +5418,6 @@ async fn test_xdm_false_invalid_fraud_proof() {
.construct_and_send_extrinsic(evm_domain_test_runtime::RuntimeCall::Messenger(
pallet_messenger::Call::initiate_channel {
dst_chain_id: ChainId::Consensus,
params: pallet_messenger::InitiateChannelParams {
max_outgoing_messages: 100,
},
},
))
.await
Expand Down Expand Up @@ -5628,9 +5613,6 @@ async fn test_stale_fork_xdm_true_invalid_fraud_proof() {
.construct_and_send_extrinsic(evm_domain_test_runtime::RuntimeCall::Messenger(
pallet_messenger::Call::initiate_channel {
dst_chain_id: ChainId::Consensus,
params: pallet_messenger::InitiateChannelParams {
max_outgoing_messages: 100,
},
},
))
.await
Expand Down
12 changes: 4 additions & 8 deletions domains/pallets/messenger/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ mod benchmarks {
fn initiate_channel() {
let dst_chain_id: ChainId = u32::MAX.into();
assert_ne!(T::SelfChainId::get(), dst_chain_id);
let channel_params = InitiateChannelParams {
max_outgoing_messages: 100,
};
let channel_id = NextChannelId::<T>::get(dst_chain_id);
let account = account("account", 0, 0);
T::Currency::set_balance(
Expand All @@ -47,11 +44,7 @@ mod benchmarks {
ChainAllowlist::<T>::put(list);

#[extrinsic_call]
_(
RawOrigin::Signed(account.clone()),
dst_chain_id,
channel_params,
);
_(RawOrigin::Signed(account.clone()), dst_chain_id);

let channel = Channels::<T>::get(dst_chain_id, channel_id).expect("channel should exist");
assert_eq!(channel.state, ChannelState::Initiated);
Expand Down Expand Up @@ -203,6 +196,9 @@ mod benchmarks {
last_delivered_message_response_nonce: None,
};
Outbox::<T>::insert((dst_chain_id, channel_id, next_outbox_nonce), req_msg);
OutboxMessageCount::<T>::mutate((dst_chain_id, channel_id), |count| {
*count = count.saturating_add(1u32);
});
// Insert a dummy response message which will be handled during the `relay_message_response` call
let resp_msg: Message<BalanceOf<T>> = Message {
src_chain_id: dst_chain_id,
Expand Down
64 changes: 35 additions & 29 deletions domains/pallets/messenger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,6 @@ pub(crate) enum CloseChannelBy<AccountId> {
Sudo,
}

/// Parameters for a new channel between two chains.
#[derive(Default, Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Copy)]
pub struct InitiateChannelParams {
pub max_outgoing_messages: u32,
}

/// Hold identifier trait for messenger specific balance holds
pub trait HoldIdentifier<T: Config> {
fn messenger_channel() -> FungibleHoldId<T>;
Expand All @@ -114,8 +108,8 @@ mod pallet {
use crate::weights::WeightInfo;
use crate::{
BalanceOf, ChainAllowlistUpdate, Channel, ChannelId, ChannelState, CloseChannelBy,
FeeModel, HoldIdentifier, InitiateChannelParams, Nonce, OutboxMessageResult, StateRootOf,
ValidatedRelayMessage, U256,
FeeModel, HoldIdentifier, Nonce, OutboxMessageResult, StateRootOf, ValidatedRelayMessage,
U256,
};
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
Expand Down Expand Up @@ -191,6 +185,8 @@ mod pallet {
type DomainRegistration: DomainRegistration;
/// Channels fee model
type ChannelFeeModel: Get<FeeModel<BalanceOf<Self>>>;
/// Maximum outgoing messages from a given channel
type MaxOutgoingMessages: Get<u32>;
}

/// Pallet messenger used to communicate between chains and other blockchains.
Expand Down Expand Up @@ -243,25 +239,21 @@ mod pallet {
/// Used by the dst_chains to verify the message response.
#[pallet::storage]
#[pallet::getter(fn inbox_responses)]
pub(super) type InboxResponses<T: Config> = CountedStorageMap<
_,
Identity,
(ChainId, ChannelId, Nonce),
Message<BalanceOf<T>>,
OptionQuery,
>;
pub(super) type InboxResponses<T: Config> =
StorageMap<_, Identity, (ChainId, ChannelId, Nonce), Message<BalanceOf<T>>, OptionQuery>;

/// Stores the outgoing messages that are awaiting message responses from the dst_chain.
/// Messages are processed in the outbox nonce order of chain's channel.
#[pallet::storage]
#[pallet::getter(fn outbox)]
pub(super) type Outbox<T: Config> = CountedStorageMap<
_,
Identity,
(ChainId, ChannelId, Nonce),
Message<BalanceOf<T>>,
OptionQuery,
>;
pub(super) type Outbox<T: Config> =
StorageMap<_, Identity, (ChainId, ChannelId, Nonce), Message<BalanceOf<T>>, OptionQuery>;

/// Stores the outgoing messages count that are awaiting message responses from the dst_chain.
#[pallet::storage]
#[pallet::getter(fn outbox_message_count)]
pub(super) type OutboxMessageCount<T: Config> =
StorageMap<_, Identity, (ChainId, ChannelId), u32, ValueQuery>;

/// A temporary storage for storing decoded outbox response message between `pre_dispatch_relay_message_response`
/// and `relay_message_response`.
Expand Down Expand Up @@ -545,6 +537,15 @@ mod pallet {

/// Invalid channel reserve fee
InvalidChannelReserveFee,

/// Invalid max outgoing messages
InvalidMaxOutgoingMessages,

/// Message count overflow
MessageCountOverflow,

/// Message count underflow
MessageCountUnderflow,
}

#[pallet::call]
Expand All @@ -554,11 +555,7 @@ mod pallet {
/// Channel is set to initiated and do not accept or receive any messages.
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::initiate_channel())]
pub fn initiate_channel(
origin: OriginFor<T>,
dst_chain_id: ChainId,
params: InitiateChannelParams,
) -> DispatchResult {
pub fn initiate_channel(origin: OriginFor<T>, dst_chain_id: ChainId) -> DispatchResult {
let owner = ensure_signed(origin)?;

// reserve channel open fees
Expand All @@ -575,7 +572,7 @@ mod pallet {

// initiate the channel config
let channel_open_params = ChannelOpenParams {
max_outgoing_messages: params.max_outgoing_messages,
max_outgoing_messages: T::MaxOutgoingMessages::get(),
fee_model: T::ChannelFeeModel::get(),
};
let channel_id = Self::do_init_channel(
Expand Down Expand Up @@ -907,8 +904,11 @@ mod pallet {
// loop through channels in descending order until open channel is found.
// we always prefer latest opened channel.
while let Some(channel_id) = next_channel_id.checked_sub(ChannelId::one()) {
let message_count = OutboxMessageCount::<T>::get((dst_chain_id, channel_id));
if let Some(channel) = Channels::<T>::get(dst_chain_id, channel_id) {
if channel.state == ChannelState::Open {
if channel.state == ChannelState::Open
&& message_count < channel.max_outgoing_messages
{
return Some((channel_id, channel.fee));
}
}
Expand Down Expand Up @@ -1006,6 +1006,12 @@ mod pallet {
Error::<T>::InvalidChain,
);

// ensure max outgoing messages is at least 1
ensure!(
init_params.max_outgoing_messages >= 1u32,
Error::<T>::InvalidMaxOutgoingMessages
);

// If the channel owner is in this chain then the channel reserve fee
// must not be empty
ensure!(
Expand Down
23 changes: 21 additions & 2 deletions domains/pallets/messenger/src/messages.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(not(feature = "std"))]
extern crate alloc;

use crate::pallet::{ChainAllowlist, UpdatedChannels};
use crate::pallet::{ChainAllowlist, OutboxMessageCount, UpdatedChannels};
use crate::{
BalanceOf, ChannelId, ChannelState, Channels, CloseChannelBy, Config, Error, Event,
InboxResponses, MessageWeightTags as MessageWeightTagStore, Nonce, Outbox, OutboxMessageResult,
Expand Down Expand Up @@ -52,7 +52,7 @@ impl<T: Config> Pallet<T> {
|maybe_channel| -> Result<Nonce, DispatchError> {
let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
// check if the outbox is full
let count = Outbox::<T>::count();
let count = OutboxMessageCount::<T>::get((dst_chain_id, channel_id));
ensure!(
count < channel.max_outgoing_messages,
Error::<T>::OutboxFull
Expand All @@ -72,6 +72,15 @@ impl<T: Config> Pallet<T> {
.latest_response_received_message_nonce,
};
Outbox::<T>::insert((dst_chain_id, channel_id, next_outbox_nonce), msg);
OutboxMessageCount::<T>::try_mutate(
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
(dst_chain_id, channel_id),
|count| -> Result<(), DispatchError> {
*count = count
.checked_add(1u32)
.ok_or(Error::<T>::MessageCountOverflow)?;
Ok(())
},
)?;

// update channel state
channel.next_outbox_nonce = next_outbox_nonce
Expand Down Expand Up @@ -340,6 +349,16 @@ impl<T: Config> Pallet<T> {
let req_msg = Outbox::<T>::take((dst_chain_id, channel_id, nonce))
.ok_or(Error::<T>::MissingMessage)?;

OutboxMessageCount::<T>::try_mutate(
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
(dst_chain_id, channel_id),
|count| -> Result<(), DispatchError> {
*count = count
.checked_sub(1u32)
.ok_or(Error::<T>::MessageCountUnderflow)?;
Ok(())
},
)?;

// clear out box message weight tag
MessageWeightTagStore::<T>::mutate(|maybe_messages| {
let mut messages = maybe_messages.as_mut().cloned().unwrap_or_default();
Expand Down
2 changes: 2 additions & 0 deletions domains/pallets/messenger/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ macro_rules! impl_runtime {
pub const ChannelReserveFee: Balance = 10;
pub const ChannelInitReservePortion: Perbill = Perbill::from_percent(20);
pub const ChannelFeeModel: FeeModel<Balance> = FeeModel{relay_fee: 1};
pub const MaxOutgoingMessages: u32 = 25;
}

#[derive(
Expand Down Expand Up @@ -102,6 +103,7 @@ macro_rules! impl_runtime {
type HoldIdentifier = MockHoldIdentifer;
type DomainRegistration = DomainRegistration;
type ChannelFeeModel = ChannelFeeModel;
type MaxOutgoingMessages = MaxOutgoingMessages;
/// function to fetch endpoint response handler by Endpoint.
fn get_endpoint_handler(
#[allow(unused_variables)] endpoint: &Endpoint,
Expand Down
Loading
Loading