Skip to content
This repository was archived by the owner on Sep 28, 2023. It is now read-only.
Draft
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
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ members = [
"frame/pallet-xvm",
"frame/xc-asset-config",
"frame/contracts-migration",
"frame/pallet-xcm-transactor",
"frame/pallet-xcm-transactor/primitives",
"frame/pallet-xcm-transactor/xcm-simulator",
"frame/pallet-xcm-transactor/ink-sdk",
# "frame/pallet-xcm-transactor/contract-examples/basic-flip",
"primitives/xcm",
"precompiles/assets-erc20",
"precompiles/dapps-staking",
Expand Down Expand Up @@ -120,12 +125,17 @@ pallet-authorship = { git = "https://github.com/paritytech/substrate", branch =
pallet-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false }
pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false }
pallet-assets = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false }
pallet-insecure-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false }

# Polkadot
# (wasm)
cumulus-pallet-xcm = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.39", default-features = false }
xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
xcm-simulator = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }

# (native)
Expand Down
79 changes: 79 additions & 0 deletions frame/pallet-xcm-transactor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
[package]
name = "pallet-xcm-transactor"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
log = { workspace = true }
num_enum = { version = "0.6", default-features = false }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }

# substrate core
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }

# frame dependencies
frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true }
frame-system = { workspace = true }

# pallets
pallet-contracts = { workspace = true }
pallet-xcm = { workspace = true }

# xcm
xcm = { workspace = true }
xcm-executor = { workspace = true }

# types
xcm-ce-primitives = { path = "./primitives", default-features = false }

[dev-dependencies]
pallet-balances = { workspace = true }
pallet-insecure-randomness-collective-flip = { workspace = true }
pallet-timestamp = { workspace = true }
polkadot-core-primitives = { workspace = true }
polkadot-parachain = { workspace = true }
polkadot-runtime-parachains = { workspace = true }
xcm-builder = { workspace = true, features = ["std"] }

[features]
default = ["std"]
std = [
"num_enum/std",
"parity-scale-codec/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"frame-support/std",
"frame-system/std",
"frame-benchmarking?/std",
"xcm/std",
"xcm-executor/std",
"pallet-xcm/std",
"pallet-contracts/std",
"xcm-ce-primitives/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-xcm/runtime-benchmarks",
"pallet-contracts/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"pallet-xcm/try-runtime",
"pallet-contracts/try-runtime",
]
75 changes: 75 additions & 0 deletions frame/pallet-xcm-transactor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# XCM CE + Companion Pallet
_XCM Chain extension with callback functionality_

> This is WIP draft implementation with a lot of pending TODOs and security considerations still to be addressed.

## Chain Extension

The CE has following commands and [SDK for ink! contracts](frame/pallet-xcm-transactor/ink-sdk).
```rust
pub enum Command {
/// Returns the weight for given XCM and saves it (in CE, per-call scratch buffer) for
/// execution
PrepareExecute = 0,
/// Execute the XCM that was prepared earlier
Execute = 1,
/// Returns the fee required to send XCM and saves it for sending
ValidateSend = 2,
/// Send the validated XCM
Send = 3,
/// Register the new query
NewQuery = 4,
/// Take the response for query if available, in case of no callback query
TakeResponse = 5,
/// Get the pallet account id which will be the caller of contract callback
PalletAccountId = 6,
}

```

## Callback Design
The callback design make use of `pallet_xcm`'s `OnResponse` handler which has capability to notify a dispatch on a XCM response (if notify query is registered).

For us that dispatch is companion pallet's `on_callback_received` which will route the xcm response (`Response` enum) back to contract via a `bare_call` (if wasm contract)

![image](https://user-images.githubusercontent.com/17181457/236989729-acf5ac13-4abe-4340-bcdc-6ca22fb5d411.png)


## Structure
```
├── pallet-xcm-transactor
│ ├── contract-examples # contract examples using XCM CE, some of which are used as fixtures in tests
│ ├── ink-sdk # ink helper methods to build CE calls and export types
│ ├── primitives # common types to share with pallet and CE
│ ├── src # companion pallet, for callback support
│ └── xcm-simulator # xcm simulator, for testing XCM CE
```


## Local testing
All the test scenarios are done inside XCM Simulator - [here](frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs)

### Run tests
- cd into xcm simulator directory
```
cd frame/pallet-xcm-transactor/xcm-simulator
```
- `cargo test` - it will take a while for first time since it needs to build the contracts too

To print the XCM logs, use the below command
```
RUST_LOG="xcm=trace" cargo test -- --nocapture --test-threads=1
```

### To add new contract to fixtures
1. Create the contract inside [`contract-examples`](frame/pallet-xcm-transactor/contract-examples) directory
2. Add the contract in [`build.rs`](frame/pallet-xcm-transactor/xcm-simulator/build.rs) of simulator tests so that contract will be compiled and copied to fixtures dir before test runs.
```
build_contract(
&fixtures_dir,
&contracts_dir.join("YOUR_CONTRACT"),
"YOUR_CONTRACT",
);
```

See the existing tests to know how fixtures are used.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Ignore build artifacts from the local tests sub-crate.
/target/

# Ignore backup files creates by cargo fmt.
**/*.rs.bk

# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "basic_flip"
version = "0.1.0"
authors = ["Ashutosh Varma <ashutoshvarma11@live.com>"]
edition = "2021"

[dependencies]
ink = { version = "~4.2.0", default-features = false }

num_enum = { version = "0.6", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }
xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
xcm-ce-sdk = { path = "../../ink-sdk", default-features = false }

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
"num_enum/std",
"scale/std",
"scale-info/std",
"xcm/std",
"xcm-ce-sdk/std",
]
ink-as-dependency = []

[profile.release]
overflow-checks = false

[workspace]
121 changes: 121 additions & 0 deletions frame/pallet-xcm-transactor/contract-examples/basic-flip/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// This file is part of Astar.

// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later

// Astar 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.

// Astar 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 Astar. If not, see <http://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[ink::contract]
mod contracts {
use ink::{env::DefaultEnvironment, storage::Mapping};
use xcm::{latest::Weight, prelude::*};
pub use xcm_ce_sdk::{
types::{QueryConfig, ValidateSendInput},
Error as XcmCEError, XcmExtension as _XcmExtension,
};

type XcmExtension = _XcmExtension<DefaultEnvironment>;

#[ink(storage)]
#[derive(Default)]
pub struct Contracts {
value: bool,
expecting_query: Mapping<QueryId, bool>,
}

impl Contracts {
#[ink(constructor, selector = 0xFFFFFFFF)]
pub fn default() -> Self {
Self {
..Default::default()
}
}

#[ink(message, selector = 0x11111111)]
pub fn execute(&mut self, xcm: VersionedXcm<()>) -> Result<Weight, XcmCEError> {
let weight = XcmExtension::prepare_execute(xcm)?;
ink::env::debug_println!("[1/2] Prepared XCM");

XcmExtension::execute()?;
ink::env::debug_println!("[2/2] Execute XCM");

Ok(weight)
}

#[ink(message, selector = 0x22222222)]
pub fn send(
&mut self,
input: ValidateSendInput,
) -> Result<VersionedMultiAssets, XcmCEError> {
let fees = XcmExtension::validate_send(input)?;
ink::env::debug_println!("[1/2] Validate Send XCM");

XcmExtension::send()?;
ink::env::debug_println!("[2/2] Send XCM");

Ok(fees)
}

#[ink(message, selector = 0x33333333)]
pub fn query(
&mut self,
config: QueryConfig<AccountId, BlockNumber>,
dest: VersionedMultiLocation,
) -> Result<QueryId, XcmCEError> {
ink::env::debug_println!("[1/3] Registering Query..., {config:?}");
let query_id = XcmExtension::new_query(config, dest)?;
ink::env::debug_println!("[2/3] Registered Query");

self.expecting_query.insert(query_id, &true);
ink::env::debug_println!("[3/3] Save Query");

Ok(query_id)
}

#[ink(message, selector = 0x44444444)]
pub fn poll_response(&mut self, query_id: QueryId) -> Result<Response, XcmCEError> {
ink::env::debug_println!("[1/1] Response Recieved for QueryId - {query_id}");
XcmExtension::take_response(query_id)
}

#[ink(message, selector = 0x55555555)]
pub fn handle_response(
&mut self,
query_id: QueryId,
_responder: MultiLocation,
_response: Response,
) {
ink::env::debug_println!("[1/1] Response Recieved for QueryId - {query_id}");
assert!(XcmExtension::pallet_account_id() == self.env().caller());
match self.expecting_query.get(query_id) {
Some(expecting) if expecting == true => {
// NOTE: do not delete storage, because storage deposit
// refund will fail.
// self.expecting_query.remove(query_id);
self.value = !self.value;
}
_ => {
panic!("Not expecting response");
}
}
}

#[ink(message, selector = 0x66666666)]
pub fn get(&self) -> bool {
self.value
}
}
}
9 changes: 9 additions & 0 deletions frame/pallet-xcm-transactor/ink-sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Ignore build artifacts from the local tests sub-crate.
/target/

# Ignore backup files creates by cargo fmt.
**/*.rs.bk

# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
34 changes: 34 additions & 0 deletions frame/pallet-xcm-transactor/ink-sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "xcm-ce-sdk"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true

[dependencies]
ink = { version = "~4.2.0", default-features = false }

num_enum = { version = "0.6", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }
xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false }
xcm-ce-primitives = { path = "../primitives", default-features = false }

[lib]
path = "lib.rs"

# [profile.release]
# overflow-checks = false # Disable integer overflow checks.

[features]
default = ["std"]
std = [
"ink/std",
"num_enum/std",
"scale/std",
"scale-info/std",
"xcm/std",
"xcm-ce-primitives/std",
]
ink-as-dependency = []
Loading