Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
feat(xcm): Adds pallet to allow target parachains to receive fulfillm…
Browse files Browse the repository at this point in the history
…ents from acurast (#11)

* feat(acurast-receiver): adds pallet that allows target parachains to receive fulfillments from acurast

* feat(acurast-receiver-pallet-generic-routing): modified interface to accept optional bytes

* chore: rename receiver crate to acurast-xcm-receiver

Co-authored-by: Mike Godenzi <m.godenzi@papers.ch>
  • Loading branch information
RomarQ and godenzim authored Oct 20, 2022
1 parent d0ba541 commit 647dc7c
Show file tree
Hide file tree
Showing 7 changed files with 423 additions and 0 deletions.
47 changes: 47 additions & 0 deletions pallets/acurast-xcm-receiver/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[package]
name = "pallet-acurast-xcm-receiver"
authors = ["Papers AG"]
description = "Pallet for defining the receiving logic from acurast parachain."
version = "0.0.1"
license = "Unlicense"
homepage = "https://docs.acurast.com/"
edition = "2021"
publish = false
repository = "https://github.com/acurast/"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }

# Benchmarks
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.29", optional = true, default-features = false }

# Substrate
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.29", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.29", default-features = false }
scale-info = { version = "2.0", default-features = false, features = [ "derive" ] }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.29", default-features = false }

[dev-dependencies]
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.29", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.29", default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.29", default-features = false }
# XCM
xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.29" }
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.29" }
pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.29" }

[features]
default = ["std"]
std = [
"codec/std",
"scale-info/std",
"frame-support/std",
"frame-system/std",
"frame-benchmarking/std",
]

runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"]
try-runtime = ["frame-support/try-runtime"]
86 changes: 86 additions & 0 deletions pallets/acurast-xcm-receiver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Acurast XCM Receiver Pallet

## 🚧🚧🚧 The project is still a work in progress 🚧🚧🚧

## Introduction

The `pallet-acurast-xcm-receiver` adds support for parachains to receive [XCM](https://wiki.polkadot.network/docs/learn-xcm) messages from [Acurast parachain](https://docs.acurast.com/).

The Pallet exposes the following extrinsics.

### fulfill

Allows to post the fulfillment of a registered job. The `fulfill` call will fail if the job was not previously assigned to the origin. The fulfillment structure consists of:

## Setup

1. Add the following dependency to your Cargo manifest:

```toml
[dependencies]
pallet-acurast-xcm-receiver = { git = "https://github.com/Acurast/acurast-core.git" }
```

2. Implement `pallet_acurast_xcm_receiver::Config` for your `Runtime` and add the Pallet:

```rust
/// Runtime example

pub struct ParachainBarrier;
impl pallet_acurast_xcm_receiver::traits::ParachainBarrier<Runtime> for ParachainBarrier {
fn ensure_xcm_origin(
origin: frame_system::pallet_prelude::OriginFor<Runtime>,
) -> Result<(), sp_runtime::DispatchError> {
// List of allowd parachains
let allowed_parachains = [
// The Acurast parachain identifier
xcm::opaque::latest::Junction::Parachain(2001),
];

// Ensure that the call comes from an xcm message
let location = pallet_xcm::ensure_xcm(origin)?;

let is_valid_origin = location
.interior()
.iter()
.any(|junction| allowed_parachains.contains(junction));

if !is_valid_origin {
return Err(sp_runtime::DispatchError::Other(
"MultiLocation not allowed.",
));
}

Ok(())
}
}

pub struct OnAcurastFulfillment;
impl pallet_acurast_xcm_receiver::traits::OnFulfillment<Runtime> for OnAcurastFulfillment {
fn fulfill(
payload: &[u8],
) -> sp_runtime::DispatchResultWithInfo<frame_support::weights::PostDispatchInfo> {
// handle payload (e.i. Call a contract)
}
}

impl pallet_acurast_xcm_receiver::Config for Runtime {
type Event = Event;
type Payload = sp_runtime::bounded::bounded_vec::BoundedVec<u8, ConstU32<128>>;
type OnFulfillment = OnAcurastFulfillment;
type Barrier = ParachainBarrier;
}

// Add pallet to the runtime
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
// All your other pallets
...
AcurastReceiver: pallet_acurast_xcm_receiver::{Pallet, Storage, Call, Event<T>};
}
);
```
12 changes: 12 additions & 0 deletions pallets/acurast-xcm-receiver/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Benchmarking setup for pallet-template
// use super::*;

// #[allow(unused)]
// use frame_benchmarking::{benchmarks, whitelisted_caller};
// use frame_system::RawOrigin;

// benchmarks! {
// // TODO: add benchmarks
// impl_benchmark_test_suite!(crate::Pallet, crate::mock::new_test_ext(), crate::mock::Test);
// }
84 changes: 84 additions & 0 deletions pallets/acurast-xcm-receiver/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

pub mod traits;

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;

#[frame_support::pallet]
pub mod pallet {
use crate::traits::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::OriginFor;
use sp_std::prelude::*;

/// Configure the pallet by specifying the parameters and types on which it depends.
#[pallet::config]
pub trait Config: frame_system::Config {
/// Because this pallet emits events, it depends on the runtime's definition of an event.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// The fulfillment payload.
type Payload: Parameter + Member + Clone + Into<Vec<u8>>;
/// Generic parameters
type Parameters: Parameter + Member + Clone + Into<Vec<u8>>;
/// Handler to notify the runtime when a new fulfillment is received.
type OnFulfillment: OnFulfillment<Self>;
/// Handle origin validation
type Barrier: ParachainBarrier<Self>;
}

#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);

/// TODO: We may want to add a storage for job identifiers, allowing acurast parachain to send
/// the (job identifer + payload). The requester address would be indexed by job identifier.
// Pallets use events to inform users when important changes are made.
// https://docs.substrate.io/v3/runtime/events-and-errors
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
FulfillReceived(T::Payload, Option<T::Parameters>),
}

// Errors inform users that something went wrong.
#[pallet::error]
pub enum Error<T> {}

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Dispatchable function that notifies the runtime about a fulfilment coming from acurast parachain.
#[pallet::weight(Weight::from_ref_time(10_000).saturating_add(T::DbWeight::get().writes(1)))]
pub fn fulfill(
origin: OriginFor<T>,
payload: T::Payload,
parameters: Option<T::Parameters>,
) -> DispatchResult {
// Check that the extrinsic comes from a trusted xcm channel.
T::Barrier::ensure_xcm_origin(origin)?;

// Notify the runtime about the fulfillment.
match T::OnFulfillment::fulfill(
payload.clone().into(),
parameters.clone().map(|parameters| parameters.into()),
) {
Err(err) => Err(err.error),
Ok(_) => {
// Emit events
Self::deposit_event(Event::FulfillReceived(payload, parameters));

Ok(())
}
}
}
}
}
141 changes: 141 additions & 0 deletions pallets/acurast-xcm-receiver/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use crate as pallet_acurast_xcm_receiver;
use frame_support::{
traits::{ConstU16, ConstU64, Everything, Nothing},
weights::{Pays, PostDispatchInfo},
};
use frame_system as system;
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
};
use sp_std::prelude::*;
use xcm::v2::Junction::Parachain;

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;

// Configure a mock runtime to test the pallet.
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
AcurastReceiver: pallet_acurast_xcm_receiver::{Pallet, Call, Storage, Event<T>},
PolkadotXcm: pallet_xcm::{Pallet, Storage, Call, Event<T>, Origin, Config},
}
);

frame_support::parameter_types! {
/// The amount of weight an XCM operation takes. This is safe overestimate.
pub UnitWeightCost: xcm::v2::Weight = 200_000_000;
/// Maximum number of instructions in a single XCM fragment. A sanity check against
/// weight caculations getting too crazy.
pub MaxInstructions: u32 = 100;
// The ancestry, defines the multilocation describing this consensus system
pub Ancestry: xcm::v2::MultiLocation = Parachain(2001).into();
}

impl pallet_xcm::Config for Test {
type Event = Event;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, ()>;
type XcmRouter = ();
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, ()>;
type XcmExecuteFilter = Nothing;
type XcmExecutor = ();
type XcmTeleportFilter = Nothing;
type XcmReserveTransferFilter = Everything;
type Weigher = xcm_builder::FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;
type LocationInverter = xcm_builder::LocationInverter<Ancestry>;
type Origin = Origin;
type Call = Call;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
}

impl system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type Origin = Origin;
type Call = Call;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = Event;
type BlockHashCount = ConstU64<250>;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ConstU16<42>;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}

pub struct OnAcurastFulfillment;
impl crate::traits::OnFulfillment<Test> for OnAcurastFulfillment {
fn fulfill(
_payload: Vec<u8>,
_parameters: Option<Vec<u8>>,
) -> frame_support::sp_runtime::DispatchResultWithInfo<PostDispatchInfo> {
Ok(PostDispatchInfo {
actual_weight: None,
pays_fee: Pays::No,
})
}
}

pub struct ParachainBarrier;
impl crate::traits::ParachainBarrier<Test> for ParachainBarrier {
fn ensure_xcm_origin(
origin: frame_system::pallet_prelude::OriginFor<Test>,
) -> Result<(), sp_runtime::DispatchError> {
// List of allowd parachains
let allowed_parachains = [
// Acurast
xcm::opaque::latest::Junction::Parachain(2001),
];

// Ensure that the call comes from an xcm message
let location = pallet_xcm::ensure_xcm(origin)?;

let is_valid_origin = location
.interior()
.iter()
.any(|junction| allowed_parachains.contains(junction));

if !is_valid_origin {
return Err(sp_runtime::DispatchError::Other(
"MultiLocation not allowed.",
));
}

Ok(())
}
}

impl pallet_acurast_xcm_receiver::Config for Test {
type Event = Event;
type Payload = Vec<u8>;
type Parameters = Vec<u8>;
type OnFulfillment = OnAcurastFulfillment;
type Barrier = ParachainBarrier;
}

// Build genesis storage according to the mock runtime.
pub fn new_test_ext() -> sp_io::TestExternalities {
system::GenesisConfig::default()
.build_storage::<Test>()
.unwrap()
.into()
}
Loading

0 comments on commit 647dc7c

Please sign in to comment.