mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 21:01:05 +00:00
Init of pallet with transfer_asset_via_bridge call
This commit is contained in:
Generated
+17
-17
@@ -713,22 +713,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bridge-assets-transfer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"sp-version",
|
||||
"xcm",
|
||||
"xcm-builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bs58"
|
||||
version = "0.4.0"
|
||||
@@ -5501,6 +5485,22 @@ dependencies = [
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-bridge-assets-transfer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"sp-version",
|
||||
"xcm",
|
||||
"xcm-builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-child-bounties"
|
||||
version = "4.0.0-dev"
|
||||
@@ -11533,7 +11533,6 @@ name = "statemine-runtime"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"asset-test-utils",
|
||||
"bridge-assets-transfer",
|
||||
"cumulus-pallet-aura-ext",
|
||||
"cumulus-pallet-dmp-queue",
|
||||
"cumulus-pallet-parachain-system",
|
||||
@@ -11558,6 +11557,7 @@ dependencies = [
|
||||
"pallet-aura",
|
||||
"pallet-authorship",
|
||||
"pallet-balances",
|
||||
"pallet-bridge-assets-transfer",
|
||||
"pallet-collator-selection",
|
||||
"pallet-multisig",
|
||||
"pallet-proxy",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "bridge-assets-transfer"
|
||||
name = "pallet-bridge-assets-transfer"
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2021"
|
||||
@@ -22,10 +22,10 @@ frame-system = { git = "https://github.com/paritytech/substrate", default-featur
|
||||
|
||||
# Polkadot
|
||||
xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
|
||||
xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
|
||||
|
||||
[dev-dependencies]
|
||||
sp-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
|
||||
xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
@@ -37,4 +37,10 @@ std = [
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"xcm/std",
|
||||
"xcm-builder/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = ["frame-support/try-runtime"]
|
||||
|
||||
@@ -0,0 +1,889 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Alliance pallet benchmarking.
|
||||
|
||||
use sp_runtime::traits::{Bounded, Hash, StaticLookup};
|
||||
use sp_std::{
|
||||
cmp,
|
||||
convert::{TryFrom, TryInto},
|
||||
mem::size_of,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use frame_benchmarking::{account, benchmarks_instance_pallet};
|
||||
use frame_support::traits::{EnsureOrigin, Get, UnfilteredDispatchable};
|
||||
use frame_system::{Pallet as System, RawOrigin as SystemOrigin};
|
||||
|
||||
use super::{Call as AllianceCall, Pallet as Alliance, *};
|
||||
|
||||
const SEED: u32 = 0;
|
||||
|
||||
const MAX_BYTES: u32 = 1_024;
|
||||
|
||||
fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
|
||||
frame_system::Pallet::<T>::assert_last_event(generic_event.into());
|
||||
}
|
||||
|
||||
fn cid(input: impl AsRef<[u8]>) -> Cid {
|
||||
use sha2::{Digest, Sha256};
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(input);
|
||||
let result = hasher.finalize();
|
||||
Cid::new_v0(&*result)
|
||||
}
|
||||
|
||||
fn rule(input: impl AsRef<[u8]>) -> Cid {
|
||||
cid(input)
|
||||
}
|
||||
|
||||
fn announcement(input: impl AsRef<[u8]>) -> Cid {
|
||||
cid(input)
|
||||
}
|
||||
|
||||
fn funded_account<T: Config<I>, I: 'static>(name: &'static str, index: u32) -> T::AccountId {
|
||||
let account: T::AccountId = account(name, index, SEED);
|
||||
T::Currency::make_free_balance_be(&account, BalanceOf::<T, I>::max_value() / 100u8.into());
|
||||
account
|
||||
}
|
||||
|
||||
fn founder<T: Config<I>, I: 'static>(index: u32) -> T::AccountId {
|
||||
funded_account::<T, I>("founder", index)
|
||||
}
|
||||
|
||||
fn fellow<T: Config<I>, I: 'static>(index: u32) -> T::AccountId {
|
||||
funded_account::<T, I>("fellow", index)
|
||||
}
|
||||
|
||||
fn ally<T: Config<I>, I: 'static>(index: u32) -> T::AccountId {
|
||||
funded_account::<T, I>("ally", index)
|
||||
}
|
||||
|
||||
fn outsider<T: Config<I>, I: 'static>(index: u32) -> T::AccountId {
|
||||
funded_account::<T, I>("outsider", index)
|
||||
}
|
||||
|
||||
fn generate_unscrupulous_account<T: Config<I>, I: 'static>(index: u32) -> T::AccountId {
|
||||
funded_account::<T, I>("unscrupulous", index)
|
||||
}
|
||||
|
||||
fn set_members<T: Config<I>, I: 'static>() {
|
||||
let founders: BoundedVec<_, T::MaxMembersCount> =
|
||||
BoundedVec::try_from(vec![founder::<T, I>(1), founder::<T, I>(2)]).unwrap();
|
||||
Members::<T, I>::insert(MemberRole::Founder, founders.clone());
|
||||
|
||||
let fellows: BoundedVec<_, T::MaxMembersCount> =
|
||||
BoundedVec::try_from(vec![fellow::<T, I>(1), fellow::<T, I>(2)]).unwrap();
|
||||
fellows.iter().for_each(|who| {
|
||||
T::Currency::reserve(&who, T::AllyDeposit::get()).unwrap();
|
||||
<DepositOf<T, I>>::insert(&who, T::AllyDeposit::get());
|
||||
});
|
||||
Members::<T, I>::insert(MemberRole::Fellow, fellows.clone());
|
||||
|
||||
let allies: BoundedVec<_, T::MaxMembersCount> =
|
||||
BoundedVec::try_from(vec![ally::<T, I>(1)]).unwrap();
|
||||
allies.iter().for_each(|who| {
|
||||
T::Currency::reserve(&who, T::AllyDeposit::get()).unwrap();
|
||||
<DepositOf<T, I>>::insert(&who, T::AllyDeposit::get());
|
||||
});
|
||||
Members::<T, I>::insert(MemberRole::Ally, allies);
|
||||
|
||||
T::InitializeMembers::initialize_members(&[founders.as_slice(), fellows.as_slice()].concat());
|
||||
}
|
||||
|
||||
benchmarks_instance_pallet! {
|
||||
// This tests when proposal is created and queued as "proposed"
|
||||
propose_proposed {
|
||||
let b in 1 .. MAX_BYTES;
|
||||
let x in 2 .. T::MaxFounders::get();
|
||||
let y in 0 .. T::MaxFellows::get();
|
||||
let p in 1 .. T::MaxProposals::get();
|
||||
|
||||
let m = x + y;
|
||||
|
||||
let bytes_in_storage = b + size_of::<Cid>() as u32 + 32;
|
||||
|
||||
// Construct `members`.
|
||||
let founders = (0 .. x).map(founder::<T, I>).collect::<Vec<_>>();
|
||||
let proposer = founders[0].clone();
|
||||
let fellows = (0 .. y).map(fellow::<T, I>).collect::<Vec<_>>();
|
||||
|
||||
Alliance::<T, I>::init_members(
|
||||
SystemOrigin::Root.into(),
|
||||
founders,
|
||||
fellows,
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
let threshold = m;
|
||||
// Add previous proposals.
|
||||
for i in 0 .. p - 1 {
|
||||
// Proposals should be different so that different proposal hashes are generated
|
||||
let proposal: T::Proposal = AllianceCall::<T, I>::set_rule {
|
||||
rule: rule(vec![i as u8; b as usize])
|
||||
}.into();
|
||||
Alliance::<T, I>::propose(
|
||||
SystemOrigin::Signed(proposer.clone()).into(),
|
||||
threshold,
|
||||
Box::new(proposal),
|
||||
bytes_in_storage,
|
||||
)?;
|
||||
}
|
||||
|
||||
let proposal: T::Proposal = AllianceCall::<T, I>::set_rule { rule: rule(vec![p as u8; b as usize]) }.into();
|
||||
|
||||
}: propose(SystemOrigin::Signed(proposer.clone()), threshold, Box::new(proposal.clone()), bytes_in_storage)
|
||||
verify {
|
||||
// New proposal is recorded
|
||||
let proposal_hash = T::Hashing::hash_of(&proposal);
|
||||
assert_eq!(T::ProposalProvider::proposal_of(proposal_hash), Some(proposal));
|
||||
}
|
||||
|
||||
vote {
|
||||
// We choose 5 (3 founders + 2 fellows) as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
|
||||
let x in 3 .. T::MaxFounders::get();
|
||||
let y in 2 .. T::MaxFellows::get();
|
||||
|
||||
let m = x + y;
|
||||
|
||||
let p = T::MaxProposals::get();
|
||||
let b = MAX_BYTES;
|
||||
let bytes_in_storage = b + size_of::<Cid>() as u32 + 32;
|
||||
|
||||
// Construct `members`.
|
||||
let founders = (0 .. x).map(founder::<T, I>).collect::<Vec<_>>();
|
||||
let proposer = founders[0].clone();
|
||||
let fellows = (0 .. y).map(fellow::<T, I>).collect::<Vec<_>>();
|
||||
|
||||
let mut members = Vec::with_capacity(founders.len() + fellows.len());
|
||||
members.extend(founders.clone());
|
||||
members.extend(fellows.clone());
|
||||
|
||||
Alliance::<T, I>::init_members(
|
||||
SystemOrigin::Root.into(),
|
||||
founders,
|
||||
fellows,
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
// Threshold is 1 less than the number of members so that one person can vote nay
|
||||
let threshold = m - 1;
|
||||
|
||||
// Add previous proposals
|
||||
let mut last_hash = T::Hash::default();
|
||||
for i in 0 .. p {
|
||||
// Proposals should be different so that different proposal hashes are generated
|
||||
let proposal: T::Proposal = AllianceCall::<T, I>::set_rule {
|
||||
rule: rule(vec![i as u8; b as usize])
|
||||
}.into();
|
||||
Alliance::<T, I>::propose(
|
||||
SystemOrigin::Signed(proposer.clone()).into(),
|
||||
threshold,
|
||||
Box::new(proposal.clone()),
|
||||
b,
|
||||
)?;
|
||||
last_hash = T::Hashing::hash_of(&proposal);
|
||||
}
|
||||
|
||||
let index = p - 1;
|
||||
// Have almost everyone vote aye on last proposal, while keeping it from passing.
|
||||
for j in 0 .. m - 3 {
|
||||
let voter = &members[j as usize];
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
|
||||
let voter = members[m as usize - 3].clone();
|
||||
// Voter votes aye without resolving the vote.
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
true,
|
||||
)?;
|
||||
|
||||
// Voter switches vote to nay, but does not kill the vote, just updates + inserts
|
||||
let approve = false;
|
||||
|
||||
// Whitelist voter account from further DB operations.
|
||||
let voter_key = frame_system::Account::<T>::hashed_key_for(&voter);
|
||||
frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into());
|
||||
}: _(SystemOrigin::Signed(voter), last_hash.clone(), index, approve)
|
||||
verify {
|
||||
}
|
||||
|
||||
veto {
|
||||
let p in 1 .. T::MaxProposals::get();
|
||||
|
||||
let m = 3;
|
||||
let b = MAX_BYTES;
|
||||
let bytes_in_storage = b + size_of::<Cid>() as u32 + 32;
|
||||
|
||||
// Construct `members`.
|
||||
let founders = (0 .. m).map(founder::<T, I>).collect::<Vec<_>>();
|
||||
let vetor = founders[0].clone();
|
||||
|
||||
Alliance::<T, I>::init_members(
|
||||
SystemOrigin::Root.into(),
|
||||
founders,
|
||||
vec![],
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
// Threshold is one less than total members so that two nays will disapprove the vote
|
||||
let threshold = m - 1;
|
||||
|
||||
// Add proposals
|
||||
let mut last_hash = T::Hash::default();
|
||||
for i in 0 .. p {
|
||||
// Proposals should be different so that different proposal hashes are generated
|
||||
let proposal: T::Proposal = AllianceCall::<T, I>::set_rule {
|
||||
rule: rule(vec![i as u8; b as usize])
|
||||
}.into();
|
||||
Alliance::<T, I>::propose(
|
||||
SystemOrigin::Signed(vetor.clone()).into(),
|
||||
threshold,
|
||||
Box::new(proposal.clone()),
|
||||
bytes_in_storage,
|
||||
)?;
|
||||
last_hash = T::Hashing::hash_of(&proposal);
|
||||
}
|
||||
|
||||
}: _(SystemOrigin::Signed(vetor), last_hash.clone())
|
||||
verify {
|
||||
// The proposal is removed
|
||||
assert_eq!(T::ProposalProvider::proposal_of(last_hash), None);
|
||||
}
|
||||
|
||||
close_early_disapproved {
|
||||
// We choose 4 (2 founders + 2 fellows) as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
|
||||
let x in 2 .. T::MaxFounders::get();
|
||||
let y in 2 .. T::MaxFellows::get();
|
||||
let p in 1 .. T::MaxProposals::get();
|
||||
|
||||
let m = x + y;
|
||||
|
||||
let bytes = 100;
|
||||
let bytes_in_storage = bytes + size_of::<Cid>() as u32 + 32;
|
||||
|
||||
// Construct `members`.
|
||||
let founders = (0 .. x).map(founder::<T, I>).collect::<Vec<_>>();
|
||||
let fellows = (0 .. y).map(fellow::<T, I>).collect::<Vec<_>>();
|
||||
|
||||
let mut members = Vec::with_capacity(founders.len() + fellows.len());
|
||||
members.extend(founders.clone());
|
||||
members.extend(fellows.clone());
|
||||
|
||||
Alliance::<T, I>::init_members(
|
||||
SystemOrigin::Root.into(),
|
||||
founders,
|
||||
fellows,
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
let proposer = members[0].clone();
|
||||
let voter = members[1].clone();
|
||||
|
||||
// Threshold is total members so that one nay will disapprove the vote
|
||||
let threshold = m;
|
||||
|
||||
// Add previous proposals
|
||||
let mut last_hash = T::Hash::default();
|
||||
for i in 0 .. p {
|
||||
// Proposals should be different so that different proposal hashes are generated
|
||||
let proposal: T::Proposal = AllianceCall::<T, I>::set_rule {
|
||||
rule: rule(vec![i as u8; bytes as usize])
|
||||
}.into();
|
||||
Alliance::<T, I>::propose(
|
||||
SystemOrigin::Signed(proposer.clone()).into(),
|
||||
threshold,
|
||||
Box::new(proposal.clone()),
|
||||
bytes_in_storage,
|
||||
)?;
|
||||
last_hash = T::Hashing::hash_of(&proposal);
|
||||
assert_eq!(T::ProposalProvider::proposal_of(last_hash), Some(proposal));
|
||||
}
|
||||
|
||||
let index = p - 1;
|
||||
// Have most everyone vote aye on last proposal, while keeping it from passing.
|
||||
for j in 2 .. m - 1 {
|
||||
let voter = &members[j as usize];
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Voter votes aye without resolving the vote.
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
true,
|
||||
)?;
|
||||
|
||||
// Voter switches vote to nay, which kills the vote
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
false,
|
||||
)?;
|
||||
|
||||
// Whitelist voter account from further DB operations.
|
||||
let voter_key = frame_system::Account::<T>::hashed_key_for(&voter);
|
||||
frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into());
|
||||
}: close(SystemOrigin::Signed(voter), last_hash.clone(), index, Weight::MAX, bytes_in_storage)
|
||||
verify {
|
||||
// The last proposal is removed.
|
||||
assert_eq!(T::ProposalProvider::proposal_of(last_hash), None);
|
||||
}
|
||||
|
||||
close_early_approved {
|
||||
let b in 1 .. MAX_BYTES;
|
||||
// We choose 4 (2 founders + 2 fellows) as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
|
||||
let x in 2 .. T::MaxFounders::get();
|
||||
let y in 2 .. T::MaxFellows::get();
|
||||
let p in 1 .. T::MaxProposals::get();
|
||||
|
||||
let m = x + y;
|
||||
let bytes_in_storage = b + size_of::<Cid>() as u32 + 32;
|
||||
|
||||
// Construct `members`.
|
||||
let founders = (0 .. x).map(founder::<T, I>).collect::<Vec<_>>();
|
||||
let fellows = (0 .. y).map(fellow::<T, I>).collect::<Vec<_>>();
|
||||
|
||||
let mut members = Vec::with_capacity(founders.len() + fellows.len());
|
||||
members.extend(founders.clone());
|
||||
members.extend(fellows.clone());
|
||||
|
||||
Alliance::<T, I>::init_members(
|
||||
SystemOrigin::Root.into(),
|
||||
founders,
|
||||
fellows,
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
let proposer = members[0].clone();
|
||||
let voter = members[1].clone();
|
||||
|
||||
// Threshold is 2 so any two ayes will approve the vote
|
||||
let threshold = 2;
|
||||
|
||||
// Add previous proposals
|
||||
let mut last_hash = T::Hash::default();
|
||||
for i in 0 .. p {
|
||||
// Proposals should be different so that different proposal hashes are generated
|
||||
let proposal: T::Proposal = AllianceCall::<T, I>::set_rule {
|
||||
rule: rule(vec![i as u8; b as usize])
|
||||
}.into();
|
||||
Alliance::<T, I>::propose(
|
||||
SystemOrigin::Signed(proposer.clone()).into(),
|
||||
threshold,
|
||||
Box::new(proposal.clone()),
|
||||
bytes_in_storage,
|
||||
)?;
|
||||
last_hash = T::Hashing::hash_of(&proposal);
|
||||
assert_eq!(T::ProposalProvider::proposal_of(last_hash), Some(proposal));
|
||||
}
|
||||
|
||||
let index = p - 1;
|
||||
// Caller switches vote to nay on their own proposal, allowing them to be the deciding approval vote
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(proposer.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
false,
|
||||
)?;
|
||||
|
||||
// Have almost everyone vote nay on last proposal, while keeping it from failing.
|
||||
for j in 2 .. m - 1 {
|
||||
let voter = &members[j as usize];
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Member zero is the first aye
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(members[0].clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
true,
|
||||
)?;
|
||||
|
||||
let voter = members[1].clone();
|
||||
// Caller switches vote to aye, which passes the vote
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
true,
|
||||
)?;
|
||||
}: close(SystemOrigin::Signed(voter), last_hash.clone(), index, Weight::MAX, bytes_in_storage)
|
||||
verify {
|
||||
// The last proposal is removed.
|
||||
assert_eq!(T::ProposalProvider::proposal_of(last_hash), None);
|
||||
}
|
||||
|
||||
close_disapproved {
|
||||
// We choose 2 (2 founders / 2 fellows) as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
|
||||
let x in 2 .. T::MaxFounders::get();
|
||||
let y in 2 .. T::MaxFellows::get();
|
||||
let p in 1 .. T::MaxProposals::get();
|
||||
|
||||
let m = x + y;
|
||||
|
||||
let bytes = 100;
|
||||
let bytes_in_storage = bytes + size_of::<Cid>() as u32 + 32;
|
||||
|
||||
// Construct `members`.
|
||||
let founders = (0 .. x).map(founder::<T, I>).collect::<Vec<_>>();
|
||||
let fellows = (0 .. y).map(fellow::<T, I>).collect::<Vec<_>>();
|
||||
|
||||
let mut members = Vec::with_capacity(founders.len() + fellows.len());
|
||||
members.extend(founders.clone());
|
||||
members.extend(fellows.clone());
|
||||
|
||||
Alliance::<T, I>::init_members(
|
||||
SystemOrigin::Root.into(),
|
||||
founders,
|
||||
fellows,
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
let proposer = members[0].clone();
|
||||
let voter = members[1].clone();
|
||||
|
||||
// Threshold is one less than total members so that two nays will disapprove the vote
|
||||
let threshold = m - 1;
|
||||
|
||||
// Add proposals
|
||||
let mut last_hash = T::Hash::default();
|
||||
for i in 0 .. p {
|
||||
// Proposals should be different so that different proposal hashes are generated
|
||||
let proposal: T::Proposal = AllianceCall::<T, I>::set_rule {
|
||||
rule: rule(vec![i as u8; bytes as usize])
|
||||
}.into();
|
||||
Alliance::<T, I>::propose(
|
||||
SystemOrigin::Signed(proposer.clone()).into(),
|
||||
threshold,
|
||||
Box::new(proposal.clone()),
|
||||
bytes_in_storage,
|
||||
)?;
|
||||
last_hash = T::Hashing::hash_of(&proposal);
|
||||
assert_eq!(T::ProposalProvider::proposal_of(last_hash), Some(proposal));
|
||||
}
|
||||
|
||||
let index = p - 1;
|
||||
// Have almost everyone vote aye on last proposal, while keeping it from passing.
|
||||
// A few abstainers will be the nay votes needed to fail the vote.
|
||||
for j in 2 .. m - 1 {
|
||||
let voter = &members[j as usize];
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
false,
|
||||
)?;
|
||||
|
||||
System::<T>::set_block_number(T::BlockNumber::max_value());
|
||||
|
||||
}: close(SystemOrigin::Signed(voter), last_hash.clone(), index, Weight::MAX, bytes_in_storage)
|
||||
verify {
|
||||
// The last proposal is removed.
|
||||
assert_eq!(T::ProposalProvider::proposal_of(last_hash), None);
|
||||
}
|
||||
|
||||
close_approved {
|
||||
let b in 1 .. MAX_BYTES;
|
||||
// We choose 4 (2 founders + 2 fellows) as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
|
||||
let x in 2 .. T::MaxFounders::get();
|
||||
let y in 2 .. T::MaxFellows::get();
|
||||
let p in 1 .. T::MaxProposals::get();
|
||||
|
||||
let m = x + y;
|
||||
let bytes_in_storage = b + size_of::<Cid>() as u32 + 32;
|
||||
|
||||
// Construct `members`.
|
||||
let founders = (0 .. x).map(founder::<T, I>).collect::<Vec<_>>();
|
||||
let fellows = (0 .. y).map(fellow::<T, I>).collect::<Vec<_>>();
|
||||
|
||||
let mut members = Vec::with_capacity(founders.len() + fellows.len());
|
||||
members.extend(founders.clone());
|
||||
members.extend(fellows.clone());
|
||||
|
||||
Alliance::<T, I>::init_members(
|
||||
SystemOrigin::Root.into(),
|
||||
founders,
|
||||
fellows,
|
||||
vec![],
|
||||
)?;
|
||||
|
||||
let proposer = members[0].clone();
|
||||
let voter = members[1].clone();
|
||||
|
||||
// Threshold is two, so any two ayes will pass the vote
|
||||
let threshold = 2;
|
||||
|
||||
// Add proposals
|
||||
let mut last_hash = T::Hash::default();
|
||||
for i in 0 .. p {
|
||||
// Proposals should be different so that different proposal hashes are generated
|
||||
let proposal: T::Proposal = AllianceCall::<T, I>::set_rule {
|
||||
rule: rule(vec![i as u8; b as usize])
|
||||
}.into();
|
||||
Alliance::<T, I>::propose(
|
||||
SystemOrigin::Signed(proposer.clone()).into(),
|
||||
threshold,
|
||||
Box::new(proposal.clone()),
|
||||
bytes_in_storage,
|
||||
)?;
|
||||
last_hash = T::Hashing::hash_of(&proposal);
|
||||
assert_eq!(T::ProposalProvider::proposal_of(last_hash), Some(proposal));
|
||||
}
|
||||
|
||||
// The prime member votes aye, so abstentions default to aye.
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(proposer.clone()).into(),
|
||||
last_hash.clone(),
|
||||
p - 1,
|
||||
true // Vote aye.
|
||||
)?;
|
||||
|
||||
let index = p - 1;
|
||||
// Have almost everyone vote nay on last proposal, while keeping it from failing.
|
||||
// A few abstainers will be the aye votes needed to pass the vote.
|
||||
for j in 2 .. m - 1 {
|
||||
let voter = &members[j as usize];
|
||||
Alliance::<T, I>::vote(
|
||||
SystemOrigin::Signed(voter.clone()).into(),
|
||||
last_hash.clone(),
|
||||
index,
|
||||
false
|
||||
)?;
|
||||
}
|
||||
|
||||
// caller is prime, prime already votes aye by creating the proposal
|
||||
System::<T>::set_block_number(T::BlockNumber::max_value());
|
||||
|
||||
}: close(SystemOrigin::Signed(voter), last_hash.clone(), index, Weight::MAX, bytes_in_storage)
|
||||
verify {
|
||||
// The last proposal is removed.
|
||||
assert_eq!(T::ProposalProvider::proposal_of(last_hash), None);
|
||||
}
|
||||
|
||||
init_members {
|
||||
// at least 1 founders
|
||||
let x in 1 .. T::MaxFounders::get();
|
||||
let y in 0 .. T::MaxFellows::get();
|
||||
let z in 0 .. T::MaxAllies::get();
|
||||
|
||||
let mut founders = (0 .. x).map(founder::<T, I>).collect::<Vec<_>>();
|
||||
let mut fellows = (0 .. y).map(fellow::<T, I>).collect::<Vec<_>>();
|
||||
let mut allies = (0 .. z).map(ally::<T, I>).collect::<Vec<_>>();
|
||||
|
||||
}: _(SystemOrigin::Root, founders.clone(), fellows.clone(), allies.clone())
|
||||
verify {
|
||||
founders.sort();
|
||||
fellows.sort();
|
||||
allies.sort();
|
||||
assert_last_event::<T, I>(Event::MembersInitialized {
|
||||
founders: founders.clone(),
|
||||
fellows: fellows.clone(),
|
||||
allies: allies.clone(),
|
||||
}.into());
|
||||
assert_eq!(Alliance::<T, I>::members(MemberRole::Founder), founders);
|
||||
assert_eq!(Alliance::<T, I>::members(MemberRole::Fellow), fellows);
|
||||
assert_eq!(Alliance::<T, I>::members(MemberRole::Ally), allies);
|
||||
}
|
||||
|
||||
disband {
|
||||
// at least 1 founders
|
||||
let x in 1 .. T::MaxFounders::get() + T::MaxFellows::get();
|
||||
let y in 0 .. T::MaxAllies::get();
|
||||
let z in 0 .. T::MaxMembersCount::get() / 2;
|
||||
|
||||
let voting_members = (0 .. x).map(founder::<T, I>).collect::<Vec<_>>();
|
||||
let allies = (0 .. y).map(ally::<T, I>).collect::<Vec<_>>();
|
||||
let witness = DisbandWitness{
|
||||
voting_members: x,
|
||||
ally_members: y,
|
||||
};
|
||||
|
||||
// setting the Alliance to disband on the benchmark call
|
||||
Alliance::<T, I>::init_members(
|
||||
SystemOrigin::Root.into(),
|
||||
voting_members.clone(),
|
||||
vec![],
|
||||
allies.clone(),
|
||||
)?;
|
||||
|
||||
// reserve deposits
|
||||
let deposit = T::AllyDeposit::get();
|
||||
for member in voting_members.iter().chain(allies.iter()).take(z as usize) {
|
||||
T::Currency::reserve(&member, deposit)?;
|
||||
<DepositOf<T, I>>::insert(&member, deposit);
|
||||
}
|
||||
|
||||
assert_eq!(Alliance::<T, I>::voting_members_count(), x);
|
||||
assert_eq!(Alliance::<T, I>::ally_members_count(), y);
|
||||
}: _(SystemOrigin::Root, witness)
|
||||
verify {
|
||||
assert_last_event::<T, I>(Event::AllianceDisbanded {
|
||||
voting_members: x,
|
||||
ally_members: y,
|
||||
unreserved: cmp::min(z, x + y),
|
||||
}.into());
|
||||
|
||||
assert!(!Alliance::<T, I>::is_initialized());
|
||||
}
|
||||
|
||||
set_rule {
|
||||
set_members::<T, I>();
|
||||
|
||||
let rule = rule(b"hello world");
|
||||
|
||||
let call = Call::<T, I>::set_rule { rule: rule.clone() };
|
||||
let origin = T::AdminOrigin::successful_origin();
|
||||
}: { call.dispatch_bypass_filter(origin)? }
|
||||
verify {
|
||||
assert_eq!(Alliance::<T, I>::rule(), Some(rule.clone()));
|
||||
assert_last_event::<T, I>(Event::NewRuleSet { rule }.into());
|
||||
}
|
||||
|
||||
announce {
|
||||
set_members::<T, I>();
|
||||
|
||||
let announcement = announcement(b"hello world");
|
||||
|
||||
let call = Call::<T, I>::announce { announcement: announcement.clone() };
|
||||
let origin = T::AnnouncementOrigin::successful_origin();
|
||||
}: { call.dispatch_bypass_filter(origin)? }
|
||||
verify {
|
||||
assert!(Alliance::<T, I>::announcements().contains(&announcement));
|
||||
assert_last_event::<T, I>(Event::Announced { announcement }.into());
|
||||
}
|
||||
|
||||
remove_announcement {
|
||||
set_members::<T, I>();
|
||||
|
||||
let announcement = announcement(b"hello world");
|
||||
let announcements: BoundedVec<_, T::MaxAnnouncementsCount> = BoundedVec::try_from(vec![announcement.clone()]).unwrap();
|
||||
Announcements::<T, I>::put(announcements);
|
||||
|
||||
let call = Call::<T, I>::remove_announcement { announcement: announcement.clone() };
|
||||
let origin = T::AnnouncementOrigin::successful_origin();
|
||||
}: { call.dispatch_bypass_filter(origin)? }
|
||||
verify {
|
||||
assert!(Alliance::<T, I>::announcements().is_empty());
|
||||
assert_last_event::<T, I>(Event::AnnouncementRemoved { announcement }.into());
|
||||
}
|
||||
|
||||
join_alliance {
|
||||
set_members::<T, I>();
|
||||
|
||||
let outsider = outsider::<T, I>(1);
|
||||
assert!(!Alliance::<T, I>::is_member(&outsider));
|
||||
assert_eq!(DepositOf::<T, I>::get(&outsider), None);
|
||||
}: _(SystemOrigin::Signed(outsider.clone()))
|
||||
verify {
|
||||
assert!(Alliance::<T, I>::is_member_of(&outsider, MemberRole::Ally)); // outsider is now an ally
|
||||
assert_eq!(DepositOf::<T, I>::get(&outsider), Some(T::AllyDeposit::get())); // with a deposit
|
||||
assert!(!Alliance::<T, I>::has_voting_rights(&outsider)); // allies don't have voting rights
|
||||
assert_last_event::<T, I>(Event::NewAllyJoined {
|
||||
ally: outsider,
|
||||
nominator: None,
|
||||
reserved: Some(T::AllyDeposit::get())
|
||||
}.into());
|
||||
}
|
||||
|
||||
nominate_ally {
|
||||
set_members::<T, I>();
|
||||
|
||||
let founder1 = founder::<T, I>(1);
|
||||
assert!(Alliance::<T, I>::is_member_of(&founder1, MemberRole::Founder));
|
||||
|
||||
let outsider = outsider::<T, I>(1);
|
||||
assert!(!Alliance::<T, I>::is_member(&outsider));
|
||||
assert_eq!(DepositOf::<T, I>::get(&outsider), None);
|
||||
|
||||
let outsider_lookup = T::Lookup::unlookup(outsider.clone());
|
||||
}: _(SystemOrigin::Signed(founder1.clone()), outsider_lookup)
|
||||
verify {
|
||||
assert!(Alliance::<T, I>::is_member_of(&outsider, MemberRole::Ally)); // outsider is now an ally
|
||||
assert_eq!(DepositOf::<T, I>::get(&outsider), None); // without a deposit
|
||||
assert!(!Alliance::<T, I>::has_voting_rights(&outsider)); // allies don't have voting rights
|
||||
assert_last_event::<T, I>(Event::NewAllyJoined {
|
||||
ally: outsider,
|
||||
nominator: Some(founder1),
|
||||
reserved: None
|
||||
}.into());
|
||||
}
|
||||
|
||||
elevate_ally {
|
||||
set_members::<T, I>();
|
||||
|
||||
let ally1 = ally::<T, I>(1);
|
||||
assert!(Alliance::<T, I>::is_ally(&ally1));
|
||||
|
||||
let ally1_lookup = T::Lookup::unlookup(ally1.clone());
|
||||
let call = Call::<T, I>::elevate_ally { ally: ally1_lookup };
|
||||
let origin = T::MembershipManager::successful_origin();
|
||||
}: { call.dispatch_bypass_filter(origin)? }
|
||||
verify {
|
||||
assert!(!Alliance::<T, I>::is_ally(&ally1));
|
||||
assert!(Alliance::<T, I>::is_fellow(&ally1));
|
||||
assert_last_event::<T, I>(Event::AllyElevated { ally: ally1 }.into());
|
||||
}
|
||||
|
||||
give_retirement_notice {
|
||||
set_members::<T, I>();
|
||||
let fellow2 = fellow::<T, I>(2);
|
||||
|
||||
assert!(Alliance::<T, I>::is_fellow(&fellow2));
|
||||
}: _(SystemOrigin::Signed(fellow2.clone()))
|
||||
verify {
|
||||
assert!(Alliance::<T, I>::is_member_of(&fellow2, MemberRole::Retiring));
|
||||
|
||||
assert_eq!(
|
||||
RetiringMembers::<T, I>::get(&fellow2),
|
||||
Some(System::<T>::block_number() + T::RetirementPeriod::get())
|
||||
);
|
||||
assert_last_event::<T, I>(
|
||||
Event::MemberRetirementPeriodStarted {member: fellow2}.into()
|
||||
);
|
||||
}
|
||||
|
||||
retire {
|
||||
set_members::<T, I>();
|
||||
|
||||
let fellow2 = fellow::<T, I>(2);
|
||||
assert!(Alliance::<T, I>::is_fellow(&fellow2));
|
||||
|
||||
assert_eq!(
|
||||
Alliance::<T, I>::give_retirement_notice(
|
||||
SystemOrigin::Signed(fellow2.clone()).into()
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
System::<T>::set_block_number(System::<T>::block_number() + T::RetirementPeriod::get());
|
||||
|
||||
assert_eq!(DepositOf::<T, I>::get(&fellow2), Some(T::AllyDeposit::get()));
|
||||
}: _(SystemOrigin::Signed(fellow2.clone()))
|
||||
verify {
|
||||
assert!(!Alliance::<T, I>::is_member(&fellow2));
|
||||
assert_eq!(DepositOf::<T, I>::get(&fellow2), None);
|
||||
assert_last_event::<T, I>(Event::MemberRetired {
|
||||
member: fellow2,
|
||||
unreserved: Some(T::AllyDeposit::get())
|
||||
}.into());
|
||||
}
|
||||
|
||||
kick_member {
|
||||
set_members::<T, I>();
|
||||
|
||||
let fellow2 = fellow::<T, I>(2);
|
||||
assert!(Alliance::<T, I>::is_member_of(&fellow2, MemberRole::Fellow));
|
||||
assert_eq!(DepositOf::<T, I>::get(&fellow2), Some(T::AllyDeposit::get()));
|
||||
|
||||
let fellow2_lookup = T::Lookup::unlookup(fellow2.clone());
|
||||
let call = Call::<T, I>::kick_member { who: fellow2_lookup };
|
||||
let origin = T::MembershipManager::successful_origin();
|
||||
}: { call.dispatch_bypass_filter(origin)? }
|
||||
verify {
|
||||
assert!(!Alliance::<T, I>::is_member(&fellow2));
|
||||
assert_eq!(DepositOf::<T, I>::get(&fellow2), None);
|
||||
assert_last_event::<T, I>(Event::MemberKicked {
|
||||
member: fellow2,
|
||||
slashed: Some(T::AllyDeposit::get())
|
||||
}.into());
|
||||
}
|
||||
|
||||
add_unscrupulous_items {
|
||||
let n in 0 .. T::MaxUnscrupulousItems::get();
|
||||
let l in 0 .. T::MaxWebsiteUrlLength::get();
|
||||
|
||||
set_members::<T, I>();
|
||||
|
||||
let accounts = (0 .. n)
|
||||
.map(|i| generate_unscrupulous_account::<T, I>(i))
|
||||
.collect::<Vec<_>>();
|
||||
let websites = (0 .. n).map(|i| -> BoundedVec<u8, T::MaxWebsiteUrlLength> {
|
||||
BoundedVec::try_from(vec![i as u8; l as usize]).unwrap()
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let mut unscrupulous_list = Vec::with_capacity(accounts.len() + websites.len());
|
||||
unscrupulous_list.extend(accounts.into_iter().map(UnscrupulousItem::AccountId));
|
||||
unscrupulous_list.extend(websites.into_iter().map(UnscrupulousItem::Website));
|
||||
|
||||
let call = Call::<T, I>::add_unscrupulous_items { items: unscrupulous_list.clone() };
|
||||
let origin = T::AnnouncementOrigin::successful_origin();
|
||||
}: { call.dispatch_bypass_filter(origin)? }
|
||||
verify {
|
||||
assert_last_event::<T, I>(Event::UnscrupulousItemAdded { items: unscrupulous_list }.into());
|
||||
}
|
||||
|
||||
remove_unscrupulous_items {
|
||||
let n in 0 .. T::MaxUnscrupulousItems::get();
|
||||
let l in 0 .. T::MaxWebsiteUrlLength::get();
|
||||
|
||||
set_members::<T, I>();
|
||||
|
||||
let mut accounts = (0 .. n)
|
||||
.map(|i| generate_unscrupulous_account::<T, I>(i))
|
||||
.collect::<Vec<_>>();
|
||||
accounts.sort();
|
||||
let accounts: BoundedVec<_, T::MaxUnscrupulousItems> = accounts.try_into().unwrap();
|
||||
UnscrupulousAccounts::<T, I>::put(accounts.clone());
|
||||
|
||||
let mut websites = (0 .. n).map(|i| -> BoundedVec<_, T::MaxWebsiteUrlLength>
|
||||
{ BoundedVec::try_from(vec![i as u8; l as usize]).unwrap() }).collect::<Vec<_>>();
|
||||
websites.sort();
|
||||
let websites: BoundedVec<_, T::MaxUnscrupulousItems> = websites.try_into().unwrap();
|
||||
UnscrupulousWebsites::<T, I>::put(websites.clone());
|
||||
|
||||
let mut unscrupulous_list = Vec::with_capacity(accounts.len() + websites.len());
|
||||
unscrupulous_list.extend(accounts.into_iter().map(UnscrupulousItem::AccountId));
|
||||
unscrupulous_list.extend(websites.into_iter().map(UnscrupulousItem::Website));
|
||||
|
||||
let call = Call::<T, I>::remove_unscrupulous_items { items: unscrupulous_list.clone() };
|
||||
let origin = T::AnnouncementOrigin::successful_origin();
|
||||
}: { call.dispatch_bypass_filter(origin)? }
|
||||
verify {
|
||||
assert_last_event::<T, I>(Event::UnscrupulousItemRemoved { items: unscrupulous_list }.into());
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Alliance, crate::mock::new_bench_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -20,17 +20,39 @@
|
||||
// Ensure we're `no_std` when compiling for Wasm.
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::RuntimeDebug;
|
||||
|
||||
pub use pallet::*;
|
||||
use xcm::prelude::*;
|
||||
|
||||
pub mod weights;
|
||||
|
||||
/// The log target of this pallet.
|
||||
pub const LOG_TARGET: &str = "runtime::bridge-assets-transfer";
|
||||
|
||||
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
|
||||
pub struct BridgeConfig {
|
||||
/// Contains location, which is able to bridge XCM messages to bridged network
|
||||
bridge_location: MultiLocation,
|
||||
/// Fee which could be needed to pay in `bridge_location`
|
||||
fee: Option<MultiAsset>,
|
||||
}
|
||||
|
||||
impl From<BridgeConfig> for (MultiLocation, Option<MultiAsset>) {
|
||||
fn from(bridge_config: BridgeConfig) -> (MultiLocation, Option<MultiAsset>) {
|
||||
(bridge_config.bridge_location, bridge_config.fee)
|
||||
}
|
||||
}
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use crate::weights::WeightInfo;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use xcm_builder::ExporterFor;
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub (super) trait Store)]
|
||||
@@ -45,16 +67,17 @@ pub mod pallet {
|
||||
/// XCM sender which sends messages to the BridgeHub
|
||||
type BridgeXcmSender: SendXcm;
|
||||
|
||||
// TODO: store as persistent and create add_bridge/remove_bridge - then we can have generic impl and dont need to hardcode NetworkId/ParaId in runtime
|
||||
/// Configuration for supported bridged networks
|
||||
type SupportedBridges: Get<
|
||||
sp_std::prelude::Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>,
|
||||
>;
|
||||
|
||||
/// Runtime's universal location
|
||||
type UniversalLocation: Get<InteriorMultiLocation>;
|
||||
|
||||
/// Weight information for extrinsics in this pallet.
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
#[pallet::storage]
|
||||
/// Details of configured bridges which are allowed for transfer.
|
||||
pub(super) type Bridges<T: Config> = StorageMap<_, Blake2_128Concat, NetworkId, BridgeConfig>;
|
||||
|
||||
#[pallet::error]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum Error<T> {
|
||||
@@ -69,6 +92,13 @@ pub mod pallet {
|
||||
// TODO: add here xcm_hash?
|
||||
/// Transfer was successfully entered to the system (does not mean already delivered)
|
||||
TransferInitiated(XcmHash),
|
||||
|
||||
/// New bridge configuration was added
|
||||
BridgeAdded,
|
||||
/// Bridge configuration was removed
|
||||
BridgeRemoved,
|
||||
/// Bridge configuration was updated
|
||||
BridgeUpdated,
|
||||
}
|
||||
|
||||
#[pallet::call]
|
||||
@@ -78,10 +108,8 @@ pub mod pallet {
|
||||
/// Parameters:
|
||||
///
|
||||
/// * `assets`:
|
||||
/// * `destination`: Different consensus location, where the assets will be deposited, e.g. Polkadot's Statemint: `X2(GlobalConsensus(NetworkId::Polkadot), Parachain(1000))`
|
||||
///
|
||||
// TODO: correct weigth
|
||||
#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
|
||||
/// * `destination`: Different consensus location, where the assets will be deposited, e.g. Polkadot's Statemint: `2, X2(GlobalConsensus(NetworkId::Polkadot), Parachain(1000))`
|
||||
#[pallet::weight(T::WeightInfo::transfer_asset_via_bridge())]
|
||||
pub fn transfer_asset_via_bridge(
|
||||
origin: OriginFor<T>,
|
||||
assets: VersionedMultiAssets,
|
||||
@@ -135,6 +163,67 @@ pub mod pallet {
|
||||
Self::deposit_event(Event::TransferInitiated(xcm_hash));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds new bridge configuration, which allows transfer to this `bridged_network`.
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * `bridged_network`: Network where we want to allow transfer funds
|
||||
/// * `bridge_config`: contains location for BridgeHub in our network + fee
|
||||
#[pallet::weight(T::WeightInfo::add_bridge_config())]
|
||||
pub fn add_bridge_config(
|
||||
origin: OriginFor<T>,
|
||||
bridged_network: NetworkId,
|
||||
bridge_config: BridgeConfig,
|
||||
) -> DispatchResult {
|
||||
let _ = ensure_root(origin)?;
|
||||
ensure!(!Bridges::<T>::contains_key(bridged_network), Error::<T>::InvalidConfiguration);
|
||||
|
||||
Bridges::<T>::insert(bridged_network, bridge_config);
|
||||
Self::deposit_event(Event::BridgeAdded);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove bridge configuration for specified `bridged_network`.
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * `bridged_network`: Network where we want to remove
|
||||
#[pallet::weight(T::WeightInfo::remove_bridge_config())]
|
||||
pub fn remove_bridge_config(
|
||||
origin: OriginFor<T>,
|
||||
bridged_network: NetworkId,
|
||||
) -> DispatchResult {
|
||||
let _ = ensure_root(origin)?;
|
||||
ensure!(Bridges::<T>::contains_key(bridged_network), Error::<T>::InvalidConfiguration);
|
||||
|
||||
Bridges::<T>::remove(bridged_network);
|
||||
Self::deposit_event(Event::BridgeRemoved);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates bridge configuration for specified `bridged_network`.
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * `bridged_network`: Network where we want to remove
|
||||
/// * `fee`: New fee to update
|
||||
#[pallet::weight(T::WeightInfo::update_bridge_config())]
|
||||
pub fn update_bridge_config(
|
||||
origin: OriginFor<T>,
|
||||
bridged_network: NetworkId,
|
||||
fee: Option<MultiAsset>,
|
||||
) -> DispatchResult {
|
||||
let _ = ensure_root(origin)?;
|
||||
ensure!(Bridges::<T>::contains_key(bridged_network), Error::<T>::InvalidConfiguration);
|
||||
|
||||
Bridges::<T>::try_mutate_exists(bridged_network, |bridge_config| {
|
||||
let deposit = bridge_config.as_mut().ok_or(Error::<T>::InvalidConfiguration)?;
|
||||
deposit.fee = fee;
|
||||
Self::deposit_event(Event::BridgeUpdated);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
@@ -156,10 +245,7 @@ pub mod pallet {
|
||||
.map_err(|_| Error::<T>::UnsupportedDestination)?;
|
||||
ensure!(local_network != remote_network, Error::<T>::UnsupportedDestination);
|
||||
ensure!(
|
||||
T::SupportedBridges::get()
|
||||
.iter()
|
||||
.find(|sb| sb.0 == remote_network)
|
||||
.is_some(),
|
||||
Bridges::<T>::contains_key(remote_network),
|
||||
Error::<T>::UnsupportedDestination
|
||||
);
|
||||
Ok(location)
|
||||
@@ -183,6 +269,16 @@ pub mod pallet {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ExporterFor for Pallet<T> {
|
||||
fn exporter_for(
|
||||
network: &NetworkId,
|
||||
_remote_location: &InteriorMultiLocation,
|
||||
_message: &Xcm<()>,
|
||||
) -> Option<(MultiLocation, Option<MultiAsset>)> {
|
||||
Bridges::<T>::get(network).map(Into::into)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -190,13 +286,15 @@ mod tests {
|
||||
use super::*;
|
||||
use crate as bridge_assets_transfer;
|
||||
|
||||
use frame_support::{parameter_types, sp_io, sp_tracing};
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok, dispatch::DispatchError, parameter_types, sp_io, sp_tracing,
|
||||
};
|
||||
use sp_runtime::{
|
||||
testing::{Header, H256},
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
};
|
||||
use sp_version::RuntimeVersion;
|
||||
use xcm_builder::{NetworkExportTable, UnpaidRemoteExporter};
|
||||
use xcm_builder::{ExporterFor, UnpaidRemoteExporter};
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<TestRuntime>;
|
||||
type Block = frame_system::mocking::MockBlock<TestRuntime>;
|
||||
@@ -297,17 +395,14 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Bridge router, which wraps and sends xcm to BridgeHub to be delivered to the different GlobalConsensus
|
||||
pub type TestBridgeXcmSender = UnpaidRemoteExporter<
|
||||
NetworkExportTable<TestBridgeTable>,
|
||||
ThreadLocalXcmRouter,
|
||||
UniversalLocation,
|
||||
>;
|
||||
pub type TestBridgeXcmSender =
|
||||
UnpaidRemoteExporter<BridgeAssetsTransfer, ThreadLocalXcmRouter, UniversalLocation>;
|
||||
|
||||
impl Config for TestRuntime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BridgeXcmSender = TestBridgeXcmSender;
|
||||
type SupportedBridges = TestBridgeTable;
|
||||
type UniversalLocation = UniversalLocation;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
|
||||
@@ -321,6 +416,13 @@ mod tests {
|
||||
#[test]
|
||||
fn test_ensure_remote_destination() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// insert bridge config
|
||||
assert_ok!(BridgeAssetsTransfer::add_bridge_config(
|
||||
RuntimeOrigin::root(),
|
||||
Wococo,
|
||||
BridgeConfig { bridge_location: (Parent, Parachain(1013)).into(), fee: None },
|
||||
));
|
||||
|
||||
// v2 not supported
|
||||
assert_eq!(
|
||||
BridgeAssetsTransfer::ensure_remote_destination(VersionedMultiLocation::V2(
|
||||
@@ -365,6 +467,13 @@ mod tests {
|
||||
#[test]
|
||||
fn test_transfer_asset_via_bridge_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// insert bridge config
|
||||
assert_ok!(BridgeAssetsTransfer::add_bridge_config(
|
||||
RuntimeOrigin::root(),
|
||||
Wococo,
|
||||
BridgeConfig { bridge_location: (Parent, Parachain(1013)).into(), fee: None },
|
||||
));
|
||||
|
||||
assert!(ROUTED_MESSAGE.with(|r| r.borrow().is_none()));
|
||||
|
||||
let assets = VersionedMultiAssets::V3(MultiAssets::default());
|
||||
@@ -382,4 +491,94 @@ mod tests {
|
||||
assert!(ROUTED_MESSAGE.with(|r| r.borrow().is_some()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bridge_config_management_works() {
|
||||
let bridged_network = Rococo;
|
||||
let bridged_config =
|
||||
BridgeConfig { bridge_location: (Parent, Parachain(1013)).into(), fee: None };
|
||||
let dummy_xcm = Xcm(vec![]);
|
||||
let dummy_remote_interior_multilocation = X1(Parachain(1234));
|
||||
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Bridges::<TestRuntime>::iter().count(), 0);
|
||||
|
||||
// should fail - just root is allowed
|
||||
assert_noop!(
|
||||
BridgeAssetsTransfer::add_bridge_config(
|
||||
RuntimeOrigin::signed(1),
|
||||
bridged_network,
|
||||
bridged_config.clone(),
|
||||
),
|
||||
DispatchError::BadOrigin
|
||||
);
|
||||
assert_eq!(Bridges::<TestRuntime>::iter().count(), 0);
|
||||
assert_eq!(
|
||||
BridgeAssetsTransfer::exporter_for(
|
||||
&bridged_network,
|
||||
&dummy_remote_interior_multilocation,
|
||||
&dummy_xcm
|
||||
),
|
||||
None
|
||||
);
|
||||
|
||||
// add with root
|
||||
assert_ok!(BridgeAssetsTransfer::add_bridge_config(
|
||||
RuntimeOrigin::root(),
|
||||
bridged_network,
|
||||
bridged_config.clone(),
|
||||
));
|
||||
assert_eq!(Bridges::<TestRuntime>::iter().count(), 1);
|
||||
assert_eq!(Bridges::<TestRuntime>::get(bridged_network), Some(bridged_config.clone()));
|
||||
assert_eq!(Bridges::<TestRuntime>::get(Wococo), None);
|
||||
assert_eq!(
|
||||
BridgeAssetsTransfer::exporter_for(
|
||||
&bridged_network,
|
||||
&dummy_remote_interior_multilocation,
|
||||
&dummy_xcm
|
||||
),
|
||||
Some(bridged_config.clone().into())
|
||||
);
|
||||
assert_eq!(
|
||||
BridgeAssetsTransfer::exporter_for(
|
||||
&Wococo,
|
||||
&dummy_remote_interior_multilocation,
|
||||
&dummy_xcm
|
||||
),
|
||||
None
|
||||
);
|
||||
|
||||
// update fee
|
||||
// remove
|
||||
assert_ok!(BridgeAssetsTransfer::update_bridge_config(
|
||||
RuntimeOrigin::root(),
|
||||
bridged_network,
|
||||
Some((Parent, 200u128).into()),
|
||||
));
|
||||
assert_eq!(Bridges::<TestRuntime>::iter().count(), 1);
|
||||
assert_eq!(
|
||||
Bridges::<TestRuntime>::get(bridged_network),
|
||||
Some(BridgeConfig {
|
||||
bridge_location: bridged_config.bridge_location.clone(),
|
||||
fee: Some((Parent, 200u128).into())
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
BridgeAssetsTransfer::exporter_for(
|
||||
&bridged_network,
|
||||
&dummy_remote_interior_multilocation,
|
||||
&dummy_xcm
|
||||
),
|
||||
Some((bridged_config.bridge_location, Some((Parent, 200u128).into())))
|
||||
);
|
||||
|
||||
// remove
|
||||
assert_ok!(BridgeAssetsTransfer::remove_bridge_config(
|
||||
RuntimeOrigin::root(),
|
||||
bridged_network,
|
||||
));
|
||||
assert_eq!(Bridges::<TestRuntime>::get(bridged_network), None);
|
||||
assert_eq!(Bridges::<TestRuntime>::iter().count(), 0);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Autogenerated weights for pallet_bridge_assets_transfer
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2022-09-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
|
||||
|
||||
// Executed Command:
|
||||
// /home/benchbot/cargo_target_dir/production/substrate
|
||||
// benchmark
|
||||
// pallet
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --extrinsic=*
|
||||
// --execution=wasm
|
||||
// --wasm-execution=compiled
|
||||
// --heap-pages=4096
|
||||
// --pallet=pallet_alliance
|
||||
// --chain=dev
|
||||
// --output=./frame/alliance/src/weights.rs
|
||||
// --template=./.maintain/frame-weight-template.hbs
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use sp_std::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for pallet_alliance.
|
||||
pub trait WeightInfo {
|
||||
fn transfer_asset_via_bridge(/* number of assets */) -> Weight;
|
||||
fn add_bridge_config() -> Weight;
|
||||
fn remove_bridge_config() -> Weight;
|
||||
fn update_bridge_config() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for pallet_alliance using the Substrate node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
fn transfer_asset_via_bridge() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
|
||||
fn add_bridge_config() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
|
||||
fn remove_bridge_config() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
|
||||
fn update_bridge_config() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
fn transfer_asset_via_bridge() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
|
||||
fn add_bridge_config() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
|
||||
fn remove_bridge_config() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
|
||||
fn update_bridge_config() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ cumulus-primitives-utility = { path = "../../../../primitives/utility", default-
|
||||
pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false }
|
||||
parachain-info = { path = "../../../pallets/parachain-info", default-features = false }
|
||||
parachains-common = { path = "../../../common", default-features = false }
|
||||
bridge-assets-transfer = { path = "../../../../parachains/pallets/bridge-assets-transfer", default-features = false }
|
||||
pallet-bridge-assets-transfer = { path = "../../../../parachains/pallets/bridge-assets-transfer", default-features = false }
|
||||
pallet-xcm-benchmarks = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, optional = true }
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ runtime-benchmarks = [
|
||||
"pallet-collator-selection/runtime-benchmarks",
|
||||
"cumulus-pallet-xcmp-queue/runtime-benchmarks",
|
||||
"pallet-xcm-benchmarks/runtime-benchmarks",
|
||||
"pallet-bridge-assets-transfer/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"cumulus-pallet-aura-ext/try-runtime",
|
||||
@@ -126,6 +127,7 @@ try-runtime = [
|
||||
"pallet-utility/try-runtime",
|
||||
"pallet-xcm/try-runtime",
|
||||
"parachain-info/try-runtime",
|
||||
"pallet-bridge-assets-transfer/try-runtime",
|
||||
]
|
||||
std = [
|
||||
"codec/std",
|
||||
@@ -178,5 +180,5 @@ std = [
|
||||
"pallet-collator-selection/std",
|
||||
"parachain-info/std",
|
||||
"parachains-common/std",
|
||||
"bridge-assets-transfer/std",
|
||||
"pallet-bridge-assets-transfer/std",
|
||||
]
|
||||
|
||||
@@ -74,7 +74,7 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate};
|
||||
use xcm::latest::BodyId;
|
||||
use xcm_executor::XcmExecutor;
|
||||
|
||||
use crate::xcm_config::{BridgeTable, UniversalLocation};
|
||||
use crate::xcm_config::UniversalLocation;
|
||||
use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
|
||||
|
||||
impl_opaque_keys! {
|
||||
@@ -570,11 +570,11 @@ impl pallet_uniques::Config for Runtime {
|
||||
type Locker = ();
|
||||
}
|
||||
|
||||
impl bridge_assets_transfer::Config for Runtime {
|
||||
impl pallet_bridge_assets_transfer::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BridgeXcmSender = BridgeXcmSender;
|
||||
type SupportedBridges = BridgeTable;
|
||||
type UniversalLocation = UniversalLocation;
|
||||
type WeightInfo = pallet_bridge_assets_transfer::weights::SubstrateWeight<Runtime>;
|
||||
}
|
||||
|
||||
// Create the runtime by composing the FRAME pallets that were previously configured.
|
||||
@@ -619,7 +619,7 @@ construct_runtime!(
|
||||
// The main stage.
|
||||
TrustBackedAssets: pallet_assets::<Instance1>::{Pallet, Call, Storage, Event<T>} = 50,
|
||||
Uniques: pallet_uniques::{Pallet, Call, Storage, Event<T>} = 51,
|
||||
BridgeAssetsTransfer: bridge_assets_transfer::{Pallet, Call, Event<T>} = 52,
|
||||
BridgeAssetsTransfer: pallet_bridge_assets_transfer::{Pallet, Call, Event<T>} = 52,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
// limitations under the License.
|
||||
|
||||
use super::{
|
||||
AccountId, AllPalletsWithSystem, AssetId, Authorship, Balance, Balances, ParachainInfo,
|
||||
ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
|
||||
AccountId, AllPalletsWithSystem, AssetId, Authorship, Balance, Balances, BridgeAssetsTransfer,
|
||||
ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
|
||||
TrustBackedAssets, TrustBackedAssetsInstance, WeightToFee, XcmpQueue,
|
||||
};
|
||||
use frame_support::{
|
||||
@@ -36,10 +36,10 @@ use xcm_builder::{
|
||||
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
|
||||
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex,
|
||||
ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete,
|
||||
NativeAsset, NetworkExportTable, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative,
|
||||
SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative,
|
||||
SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UnpaidRemoteExporter,
|
||||
UsingComponents, WeightInfoBounds,
|
||||
NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
|
||||
SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
|
||||
SovereignSignedViaLocation, TakeWeightCredit, UnpaidRemoteExporter, UsingComponents,
|
||||
WeightInfoBounds,
|
||||
};
|
||||
use xcm_executor::{traits::JustTry, XcmExecutor};
|
||||
|
||||
@@ -268,14 +268,5 @@ impl cumulus_pallet_xcm::Config for Runtime {
|
||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
/// BridgedNetworkConsensus + Multilocation-to-LocalGlobalConsensusBridgeHub + LocalGlobalConsensusBridgeHub
|
||||
pub BridgeTable: sp_std::prelude::Vec<(NetworkId, MultiLocation, Option<MultiAsset>)> = sp_std::vec![
|
||||
(NetworkId::Wococo, (Parent, Parachain(1013)).into(), None),
|
||||
(NetworkId::Polkadot, (Parent, Parachain(1003)).into(), None),
|
||||
];
|
||||
}
|
||||
|
||||
/// Bridge router, which wraps and sends xcm to BridgeHub to be delivered to the different GlobalConsensus
|
||||
pub type BridgeXcmSender =
|
||||
UnpaidRemoteExporter<NetworkExportTable<BridgeTable>, XcmRouter, UniversalLocation>;
|
||||
pub type BridgeXcmSender = UnpaidRemoteExporter<BridgeAssetsTransfer, XcmRouter, UniversalLocation>;
|
||||
|
||||
Reference in New Issue
Block a user