Skip to content

idn-consumer-pallet: add trusted deliverers and pulse consumption #377

@juangirini

Description

@juangirini

The aim of this is to make sure that:

  1. Authorized origins can manage subscriptions: only authorized origins can call those dispatchables that interact with the idn manager directly to create/update/delete a subscription (create_subscription, kill_subscription, etc)
  2. Authorized origins can deliver pulses: the consumer only receives pulses sent by authorized origins
    3. [ ] Filter known subs: the consumer only receives pulses from known subscriptions

This is analog to how the contract lib/example already solves these, respectively:

  1. Authorized origins can manage subscriptions: ensure_authorized
  2. Authorized origins can deliver pulses: ensure_authorized_deliverer
    3. Filter known subs: ensure_active_sub

Proposed solution

  1. Authorized origins can manage subscriptions: Management dispatchables (create_subscription, kill_subscription, etc) to implement a custom EnsureOrigin instead of a fixed ensure_signed. Something similar to the current IdnOriginFilter should be implemented
type OriginFilter: EnsureOrigin<(Self::RuntimeOrigin, AuthorizedDeliverers), Success = <Self as frame_system::Config>::AccountId>;

Where AuthorizedDeliverers is a storage type.
Then instead of

ensure_signed(origin)?;

Do

T::OriginFilter::ensure_origin(origin)?;

On the Consumer runtime the implementation of this should do

let authorized = ensure_signed(origin)?;
AuthorizedDeliverers::<T>::insert(authorized);
  1. Authorized origins can deliver pulses: when a consume dispatchable is called (consume_pulse, consume_quote, consume_sub_info) we need to verify that the account associated with the origin is in the authorized list, then we need to update the IdnOriginFilter to take the AuthorizedDeliverers into account:
type IdnOriginFilter: EnsureOrigin<(Self::RuntimeOrigin, AuthorizedDeliverers), Success = Location>;

On the consumer runtime, the implementation should first extract the account id from the xcm location and verify it is in the authorized list. This could be done by updating the AllowSiblingOnly to something like AllowAuthorizedOnly.

pub struct AllowAuthorizedOnly<SiblingParaId, AuthorizedDeliverers>(PhantomData<SiblingParaId>);
impl<...> for AllowAuthorizedOnly<SiblingParaId, AuthorizedDeliverers> {
   fn contains(location: &Location) -> bool {
      match location.unpack() {
         (1, [Junction::Parachain(para_id), Junction::AccountId32 { network: _, id: account_id }]) =>
             para_id == SiblingParaId::get() && AuthorizedDeliverers::has(account_id),  
         _ => false,
		}
	}
}

3. Filter known subs: When calling create_subscription, do_create_subscription returns a subscription id, this id should be passed to a new config type AfterCreateHook which implements a new trait with a function handle_after_create_sub(sub_id). Also we need a new KnownSubscriptions storage item.
On the consumer runtime, the implementation of this type should store the id in the KnownSubscription.
Then we should update the PulseConsumerImpl to check that the pulse comes from known subscriptions.

pub struct PulseConsumerImpl<KnownSubs>;
impl<KnownSubs: xxx> PulseConsumer<Pulse, SubscriptionId, (), ()> for PulseConsumerImpl<KnownSubs> {
  fn consume_pulse(pulse: Pulse, sub_id: SubscriptionId) -> Result<(), ()> {
    ensure KnownSubs::has(sub_id);
....

Important suggested solutions are a mix of code and pseudocode, and might have errors. They should be verified by the implementor

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

Backlog

Relationships

None yet

Development

No branches or pull requests

Issue actions