mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 18:37:59 +00:00
cargo +nightly fmt (#3540)
* cargo +nightly fmt * add cargo-fmt check to ci * update ci * fmt * fmt * skip macro * ignore bridges
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -16,24 +16,31 @@
|
||||
|
||||
//! Pallet to process claims from Ethereum addresses.
|
||||
|
||||
use sp_std::{prelude::*, fmt::Debug};
|
||||
use sp_io::{hashing::keccak_256, crypto::secp256k1_ecdsa_recover};
|
||||
use frame_support::{ensure, traits::{Currency, Get, VestingSchedule, IsSubType}, weights::Weight};
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use frame_support::{
|
||||
ensure,
|
||||
traits::{Currency, Get, IsSubType, VestingSchedule},
|
||||
weights::Weight,
|
||||
};
|
||||
pub use pallet::*;
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use primitives::v1::ValidityError;
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{self, Serialize, Deserialize, Serializer, Deserializer};
|
||||
use serde::{self, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use sp_io::{crypto::secp256k1_ecdsa_recover, hashing::keccak_256};
|
||||
#[cfg(feature = "std")]
|
||||
use sp_runtime::traits::Zero;
|
||||
use sp_runtime::{
|
||||
traits::{CheckedSub, SignedExtension, DispatchInfoOf}, RuntimeDebug,
|
||||
traits::{CheckedSub, DispatchInfoOf, SignedExtension},
|
||||
transaction_validity::{
|
||||
TransactionValidity, ValidTransaction, InvalidTransaction, TransactionValidityError,
|
||||
InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,
|
||||
},
|
||||
RuntimeDebug,
|
||||
};
|
||||
use primitives::v1::ValidityError;
|
||||
pub use pallet::*;
|
||||
use sp_std::{fmt::Debug, prelude::*};
|
||||
|
||||
type CurrencyOf<T> = <<T as Config>::VestingSchedule as VestingSchedule<<T as frame_system::Config>::AccountId>>::Currency;
|
||||
type CurrencyOf<T> = <<T as Config>::VestingSchedule as VestingSchedule<
|
||||
<T as frame_system::Config>::AccountId,
|
||||
>>::Currency;
|
||||
type BalanceOf<T> = <CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
|
||||
pub trait WeightInfo {
|
||||
@@ -46,11 +53,21 @@ pub trait WeightInfo {
|
||||
|
||||
pub struct TestWeightInfo;
|
||||
impl WeightInfo for TestWeightInfo {
|
||||
fn claim() -> Weight { 0 }
|
||||
fn mint_claim() -> Weight { 0 }
|
||||
fn claim_attest() -> Weight { 0 }
|
||||
fn attest() -> Weight { 0 }
|
||||
fn move_claim() -> Weight { 0 }
|
||||
fn claim() -> Weight {
|
||||
0
|
||||
}
|
||||
fn mint_claim() -> Weight {
|
||||
0
|
||||
}
|
||||
fn claim_attest() -> Weight {
|
||||
0
|
||||
}
|
||||
fn attest() -> Weight {
|
||||
0
|
||||
}
|
||||
fn move_claim() -> Weight {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind of statement an account needs to make for a claim to be valid.
|
||||
@@ -93,7 +110,10 @@ pub struct EthereumAddress([u8; 20]);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Serialize for EthereumAddress {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let hex: String = rustc_hex::ToHex::to_hex(&self.0[..]);
|
||||
serializer.serialize_str(&format!("0x{}", hex))
|
||||
}
|
||||
@@ -101,12 +121,17 @@ impl Serialize for EthereumAddress {
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'de> Deserialize<'de> for EthereumAddress {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let base_string = String::deserialize(deserializer)?;
|
||||
let offset = if base_string.starts_with("0x") { 2 } else { 0 };
|
||||
let s = &base_string[offset..];
|
||||
if s.len() != 40 {
|
||||
Err(serde::de::Error::custom("Bad length of Ethereum address (should be 42 including '0x')"))?;
|
||||
Err(serde::de::Error::custom(
|
||||
"Bad length of Ethereum address (should be 42 including '0x')",
|
||||
))?;
|
||||
}
|
||||
let raw: Vec<u8> = rustc_hex::FromHex::from_hex(s)
|
||||
.map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?;
|
||||
@@ -133,9 +158,9 @@ impl sp_std::fmt::Debug for EcdsaSignature {
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use super::*;
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
@@ -146,7 +171,7 @@ pub mod pallet {
|
||||
pub trait Config: frame_system::Config {
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
||||
type VestingSchedule: VestingSchedule<Self::AccountId, Moment=Self::BlockNumber>;
|
||||
type VestingSchedule: VestingSchedule<Self::AccountId, Moment = Self::BlockNumber>;
|
||||
#[pallet::constant]
|
||||
type Prefix: Get<&'static [u8]>;
|
||||
type MoveClaimOrigin: EnsureOrigin<Self::Origin>;
|
||||
@@ -192,11 +217,8 @@ pub mod pallet {
|
||||
/// The block number is when the vesting should start.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn vesting)]
|
||||
pub(super) type Vesting<T: Config> = StorageMap<
|
||||
_,
|
||||
Identity, EthereumAddress,
|
||||
(BalanceOf<T>, BalanceOf<T>, T::BlockNumber),
|
||||
>;
|
||||
pub(super) type Vesting<T: Config> =
|
||||
StorageMap<_, Identity, EthereumAddress, (BalanceOf<T>, BalanceOf<T>, T::BlockNumber)>;
|
||||
|
||||
/// The statement kind that must be signed, if any.
|
||||
#[pallet::storage]
|
||||
@@ -208,17 +230,15 @@ pub mod pallet {
|
||||
|
||||
#[pallet::genesis_config]
|
||||
pub struct GenesisConfig<T: Config> {
|
||||
pub claims: Vec<(EthereumAddress, BalanceOf<T>, Option<T::AccountId>, Option<StatementKind>)>,
|
||||
pub claims:
|
||||
Vec<(EthereumAddress, BalanceOf<T>, Option<T::AccountId>, Option<StatementKind>)>,
|
||||
pub vesting: Vec<(EthereumAddress, (BalanceOf<T>, BalanceOf<T>, T::BlockNumber))>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Config> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig {
|
||||
claims: Default::default(),
|
||||
vesting: Default::default(),
|
||||
}
|
||||
GenesisConfig { claims: Default::default(), vesting: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,23 +246,32 @@ pub mod pallet {
|
||||
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
|
||||
fn build(&self) {
|
||||
// build `Claims`
|
||||
self.claims.iter().map(|(a, b, _, _)| (a.clone(), b.clone())).for_each(|(a, b)| {
|
||||
Claims::<T>::insert(a, b);
|
||||
});
|
||||
self.claims
|
||||
.iter()
|
||||
.map(|(a, b, _, _)| (a.clone(), b.clone()))
|
||||
.for_each(|(a, b)| {
|
||||
Claims::<T>::insert(a, b);
|
||||
});
|
||||
// build `Total`
|
||||
Total::<T>::put(
|
||||
self.claims.iter().fold(Zero::zero(), |acc: BalanceOf<T>, &(_, b, _, _)| acc + b),
|
||||
self.claims
|
||||
.iter()
|
||||
.fold(Zero::zero(), |acc: BalanceOf<T>, &(_, b, _, _)| acc + b),
|
||||
);
|
||||
// build `Vesting`
|
||||
self.vesting.iter().for_each(|(k, v)| { Vesting::<T>::insert(k, v); });
|
||||
self.vesting.iter().for_each(|(k, v)| {
|
||||
Vesting::<T>::insert(k, v);
|
||||
});
|
||||
// build `Signing`
|
||||
self.claims.iter()
|
||||
self.claims
|
||||
.iter()
|
||||
.filter_map(|(a, _, _, s)| Some((a.clone(), s.clone()?)))
|
||||
.for_each(|(a, s)| {
|
||||
Signing::<T>::insert(a, s);
|
||||
});
|
||||
// build `Preclaims`
|
||||
self.claims.iter()
|
||||
self.claims
|
||||
.iter()
|
||||
.filter_map(|(a, _, i, _)| Some((i.clone()?, a.clone())))
|
||||
.for_each(|(i, a)| {
|
||||
Preclaims::<T>::insert(i, a);
|
||||
@@ -283,7 +312,7 @@ pub mod pallet {
|
||||
pub fn claim(
|
||||
origin: OriginFor<T>,
|
||||
dest: T::AccountId,
|
||||
ethereum_signature: EcdsaSignature
|
||||
ethereum_signature: EcdsaSignature,
|
||||
) -> DispatchResult {
|
||||
ensure_none(origin)?;
|
||||
|
||||
@@ -422,9 +451,13 @@ pub mod pallet {
|
||||
Claims::<T>::take(&old).map(|c| Claims::<T>::insert(&new, c));
|
||||
Vesting::<T>::take(&old).map(|c| Vesting::<T>::insert(&new, c));
|
||||
Signing::<T>::take(&old).map(|c| Signing::<T>::insert(&new, c));
|
||||
maybe_preclaim.map(|preclaim| Preclaims::<T>::mutate(&preclaim, |maybe_o|
|
||||
if maybe_o.as_ref().map_or(false, |o| o == &old) { *maybe_o = Some(new) }
|
||||
));
|
||||
maybe_preclaim.map(|preclaim| {
|
||||
Preclaims::<T>::mutate(&preclaim, |maybe_o| {
|
||||
if maybe_o.as_ref().map_or(false, |o| o == &old) {
|
||||
*maybe_o = Some(new)
|
||||
}
|
||||
})
|
||||
});
|
||||
Ok(Pays::No.into())
|
||||
}
|
||||
}
|
||||
@@ -443,19 +476,23 @@ pub mod pallet {
|
||||
Call::claim(account, ethereum_signature) => {
|
||||
let data = account.using_encoded(to_ascii_hex);
|
||||
(Self::eth_recover(ðereum_signature, &data, &[][..]), None)
|
||||
}
|
||||
},
|
||||
// <weight>
|
||||
// The weight of this logic is included in the `claim_attest` dispatchable.
|
||||
// </weight>
|
||||
Call::claim_attest(account, ethereum_signature, statement) => {
|
||||
let data = account.using_encoded(to_ascii_hex);
|
||||
(Self::eth_recover(ðereum_signature, &data, &statement), Some(statement.as_slice()))
|
||||
}
|
||||
(
|
||||
Self::eth_recover(ðereum_signature, &data, &statement),
|
||||
Some(statement.as_slice()),
|
||||
)
|
||||
},
|
||||
_ => return Err(InvalidTransaction::Call.into()),
|
||||
};
|
||||
|
||||
let signer = maybe_signer
|
||||
.ok_or(InvalidTransaction::Custom(ValidityError::InvalidEthereumSignature.into()))?;
|
||||
let signer = maybe_signer.ok_or(InvalidTransaction::Custom(
|
||||
ValidityError::InvalidEthereumSignature.into(),
|
||||
))?;
|
||||
|
||||
let e = InvalidTransaction::Custom(ValidityError::SignerHasNoClaim.into());
|
||||
ensure!(<Claims<T>>::contains_key(&signer), e);
|
||||
@@ -511,13 +548,13 @@ impl<T: Config> Pallet<T> {
|
||||
fn eth_recover(s: &EcdsaSignature, what: &[u8], extra: &[u8]) -> Option<EthereumAddress> {
|
||||
let msg = keccak_256(&Self::ethereum_signable_message(what, extra));
|
||||
let mut res = EthereumAddress::default();
|
||||
res.0.copy_from_slice(&keccak_256(&secp256k1_ecdsa_recover(&s.0, &msg).ok()?[..])[12..]);
|
||||
res.0
|
||||
.copy_from_slice(&keccak_256(&secp256k1_ecdsa_recover(&s.0, &msg).ok()?[..])[12..]);
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn process_claim(signer: EthereumAddress, dest: T::AccountId) -> sp_runtime::DispatchResult {
|
||||
let balance_due = <Claims<T>>::get(&signer)
|
||||
.ok_or(Error::<T>::SignerHasNoClaim)?;
|
||||
let balance_due = <Claims<T>>::get(&signer).ok_or(Error::<T>::SignerHasNoClaim)?;
|
||||
|
||||
let new_total = Self::total().checked_sub(&balance_due).ok_or(Error::<T>::PotUnderflow)?;
|
||||
|
||||
@@ -552,11 +589,13 @@ impl<T: Config> Pallet<T> {
|
||||
/// Validate `attest` calls prior to execution. Needed to avoid a DoS attack since they are
|
||||
/// otherwise free to place on chain.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
|
||||
pub struct PrevalidateAttests<T: Config + Send + Sync>(sp_std::marker::PhantomData<T>) where
|
||||
pub struct PrevalidateAttests<T: Config + Send + Sync>(sp_std::marker::PhantomData<T>)
|
||||
where
|
||||
<T as frame_system::Config>::Call: IsSubType<Call<T>>;
|
||||
|
||||
impl<T: Config + Send + Sync> Debug for PrevalidateAttests<T> where
|
||||
<T as frame_system::Config>::Call: IsSubType<Call<T>>
|
||||
impl<T: Config + Send + Sync> Debug for PrevalidateAttests<T>
|
||||
where
|
||||
<T as frame_system::Config>::Call: IsSubType<Call<T>>,
|
||||
{
|
||||
#[cfg(feature = "std")]
|
||||
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||
@@ -569,8 +608,9 @@ impl<T: Config + Send + Sync> Debug for PrevalidateAttests<T> where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config + Send + Sync> PrevalidateAttests<T> where
|
||||
<T as frame_system::Config>::Call: IsSubType<Call<T>>
|
||||
impl<T: Config + Send + Sync> PrevalidateAttests<T>
|
||||
where
|
||||
<T as frame_system::Config>::Call: IsSubType<Call<T>>,
|
||||
{
|
||||
/// Create new `SignedExtension` to check runtime version.
|
||||
pub fn new() -> Self {
|
||||
@@ -578,8 +618,9 @@ impl<T: Config + Send + Sync> PrevalidateAttests<T> where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config + Send + Sync> SignedExtension for PrevalidateAttests<T> where
|
||||
<T as frame_system::Config>::Call: IsSubType<Call<T>>
|
||||
impl<T: Config + Send + Sync> SignedExtension for PrevalidateAttests<T>
|
||||
where
|
||||
<T as frame_system::Config>::Call: IsSubType<Call<T>>,
|
||||
{
|
||||
type AccountId = T::AccountId;
|
||||
type Call = <T as frame_system::Config>::Call;
|
||||
@@ -627,8 +668,15 @@ mod secp_utils {
|
||||
res.0.copy_from_slice(&keccak_256(&public(secret).serialize()[1..65])[12..]);
|
||||
res
|
||||
}
|
||||
pub fn sig<T: Config>(secret: &libsecp256k1::SecretKey, what: &[u8], extra: &[u8]) -> EcdsaSignature {
|
||||
let msg = keccak_256(&<super::Pallet<T>>::ethereum_signable_message(&to_ascii_hex(what)[..], extra));
|
||||
pub fn sig<T: Config>(
|
||||
secret: &libsecp256k1::SecretKey,
|
||||
what: &[u8],
|
||||
extra: &[u8],
|
||||
) -> EcdsaSignature {
|
||||
let msg = keccak_256(&<super::Pallet<T>>::ethereum_signable_message(
|
||||
&to_ascii_hex(what)[..],
|
||||
extra,
|
||||
));
|
||||
let (sig, recovery_id) = libsecp256k1::sign(&libsecp256k1::Message::parse(&msg), secret);
|
||||
let mut r = [0u8; 65];
|
||||
r[0..64].copy_from_slice(&sig.serialize()[..]);
|
||||
@@ -639,27 +687,29 @@ mod secp_utils {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hex_literal::hex;
|
||||
use super::*;
|
||||
use hex_literal::hex;
|
||||
use secp_utils::*;
|
||||
|
||||
use sp_core::H256;
|
||||
use parity_scale_codec::Encode;
|
||||
use sp_core::H256;
|
||||
// The testing primitives are very useful for avoiding having to work with signatures
|
||||
// or public keys. `u64` is used as the `AccountId` and no `Signature`s are required.
|
||||
use sp_runtime::{
|
||||
traits::{BlakeTwo256, IdentityLookup, Identity},
|
||||
transaction_validity::TransactionLongevity,
|
||||
testing::Header,
|
||||
};
|
||||
use frame_support::{
|
||||
assert_ok, assert_err, assert_noop, parameter_types,
|
||||
ord_parameter_types, weights::{Pays, GetDispatchInfo}, traits::{ExistenceRequirement, GenesisBuild},
|
||||
dispatch::DispatchError::BadOrigin,
|
||||
};
|
||||
use pallet_balances;
|
||||
use crate::claims;
|
||||
use claims::Call as ClaimsCall;
|
||||
use frame_support::{
|
||||
assert_err, assert_noop, assert_ok,
|
||||
dispatch::DispatchError::BadOrigin,
|
||||
ord_parameter_types, parameter_types,
|
||||
traits::{ExistenceRequirement, GenesisBuild},
|
||||
weights::{GetDispatchInfo, Pays},
|
||||
};
|
||||
use pallet_balances;
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, Identity, IdentityLookup},
|
||||
transaction_validity::TransactionLongevity,
|
||||
};
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
@@ -734,7 +784,7 @@ mod tests {
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types!{
|
||||
parameter_types! {
|
||||
pub Prefix: &'static [u8] = b"Pay RUSTs to the TEST account:";
|
||||
}
|
||||
ord_parameter_types! {
|
||||
@@ -770,8 +820,10 @@ mod tests {
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
// We use default for brevity, but you can configure as desired if needed.
|
||||
pallet_balances::GenesisConfig::<Test>::default().assimilate_storage(&mut t).unwrap();
|
||||
claims::GenesisConfig::<Test>{
|
||||
pallet_balances::GenesisConfig::<Test>::default()
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
claims::GenesisConfig::<Test> {
|
||||
claims: vec![
|
||||
(eth(&alice()), 100, None, None),
|
||||
(eth(&dave()), 200, None, Some(StatementKind::Regular)),
|
||||
@@ -779,7 +831,9 @@ mod tests {
|
||||
(eth(&frank()), 400, Some(43), None),
|
||||
],
|
||||
vesting: vec![(eth(&alice()), (50, 10, 1))],
|
||||
}.assimilate_storage(&mut t).unwrap();
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
t.into()
|
||||
}
|
||||
|
||||
@@ -813,7 +867,11 @@ mod tests {
|
||||
fn claiming_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Balances::free_balance(42), 0);
|
||||
assert_ok!(Claims::claim(Origin::none(), 42, sig::<Test>(&alice(), &42u64.encode(), &[][..])));
|
||||
assert_ok!(Claims::claim(
|
||||
Origin::none(),
|
||||
42,
|
||||
sig::<Test>(&alice(), &42u64.encode(), &[][..])
|
||||
));
|
||||
assert_eq!(Balances::free_balance(&42), 100);
|
||||
assert_eq!(Vesting::vesting_balance(&42), Some(50));
|
||||
assert_eq!(Claims::total(), total_claims() - 100);
|
||||
@@ -824,10 +882,20 @@ mod tests {
|
||||
fn basic_claim_moving_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Balances::free_balance(42), 0);
|
||||
assert_noop!(Claims::move_claim(Origin::signed(1), eth(&alice()), eth(&bob()), None), BadOrigin);
|
||||
assert_noop!(
|
||||
Claims::move_claim(Origin::signed(1), eth(&alice()), eth(&bob()), None),
|
||||
BadOrigin
|
||||
);
|
||||
assert_ok!(Claims::move_claim(Origin::signed(6), eth(&alice()), eth(&bob()), None));
|
||||
assert_noop!(Claims::claim(Origin::none(), 42, sig::<Test>(&alice(), &42u64.encode(), &[][..])), Error::<Test>::SignerHasNoClaim);
|
||||
assert_ok!(Claims::claim(Origin::none(), 42, sig::<Test>(&bob(), &42u64.encode(), &[][..])));
|
||||
assert_noop!(
|
||||
Claims::claim(Origin::none(), 42, sig::<Test>(&alice(), &42u64.encode(), &[][..])),
|
||||
Error::<Test>::SignerHasNoClaim
|
||||
);
|
||||
assert_ok!(Claims::claim(
|
||||
Origin::none(),
|
||||
42,
|
||||
sig::<Test>(&bob(), &42u64.encode(), &[][..])
|
||||
));
|
||||
assert_eq!(Balances::free_balance(&42), 100);
|
||||
assert_eq!(Vesting::vesting_balance(&42), Some(50));
|
||||
assert_eq!(Claims::total(), total_claims() - 100);
|
||||
@@ -839,7 +907,12 @@ mod tests {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Claims::move_claim(Origin::signed(6), eth(&dave()), eth(&bob()), None));
|
||||
let s = sig::<Test>(&bob(), &42u64.encode(), StatementKind::Regular.to_text());
|
||||
assert_ok!(Claims::claim_attest(Origin::none(), 42, s, StatementKind::Regular.to_text().to_vec()));
|
||||
assert_ok!(Claims::claim_attest(
|
||||
Origin::none(),
|
||||
42,
|
||||
s,
|
||||
StatementKind::Regular.to_text().to_vec()
|
||||
));
|
||||
assert_eq!(Balances::free_balance(&42), 200);
|
||||
});
|
||||
}
|
||||
@@ -856,7 +929,11 @@ mod tests {
|
||||
#[test]
|
||||
fn claiming_does_not_bypass_signing() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Claims::claim(Origin::none(), 42, sig::<Test>(&alice(), &42u64.encode(), &[][..])));
|
||||
assert_ok!(Claims::claim(
|
||||
Origin::none(),
|
||||
42,
|
||||
sig::<Test>(&alice(), &42u64.encode(), &[][..])
|
||||
));
|
||||
assert_noop!(
|
||||
Claims::claim(Origin::none(), 42, sig::<Test>(&dave(), &42u64.encode(), &[][..])),
|
||||
Error::<Test>::InvalidStatement,
|
||||
@@ -865,7 +942,11 @@ mod tests {
|
||||
Claims::claim(Origin::none(), 42, sig::<Test>(&eve(), &42u64.encode(), &[][..])),
|
||||
Error::<Test>::InvalidStatement,
|
||||
);
|
||||
assert_ok!(Claims::claim(Origin::none(), 42, sig::<Test>(&frank(), &42u64.encode(), &[][..])));
|
||||
assert_ok!(Claims::claim(
|
||||
Origin::none(),
|
||||
42,
|
||||
sig::<Test>(&frank(), &42u64.encode(), &[][..])
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -874,21 +955,41 @@ mod tests {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Balances::free_balance(42), 0);
|
||||
let s = sig::<Test>(&dave(), &42u64.encode(), StatementKind::Saft.to_text());
|
||||
let r = Claims::claim_attest(Origin::none(), 42, s.clone(), StatementKind::Saft.to_text().to_vec());
|
||||
let r = Claims::claim_attest(
|
||||
Origin::none(),
|
||||
42,
|
||||
s.clone(),
|
||||
StatementKind::Saft.to_text().to_vec(),
|
||||
);
|
||||
assert_noop!(r, Error::<Test>::InvalidStatement);
|
||||
|
||||
let r = Claims::claim_attest(Origin::none(), 42, s, StatementKind::Regular.to_text().to_vec());
|
||||
let r = Claims::claim_attest(
|
||||
Origin::none(),
|
||||
42,
|
||||
s,
|
||||
StatementKind::Regular.to_text().to_vec(),
|
||||
);
|
||||
assert_noop!(r, Error::<Test>::SignerHasNoClaim);
|
||||
// ^^^ we use ecdsa_recover, so an invalid signature just results in a random signer id
|
||||
// being recovered, which realistically will never have a claim.
|
||||
|
||||
let s = sig::<Test>(&dave(), &42u64.encode(), StatementKind::Regular.to_text());
|
||||
assert_ok!(Claims::claim_attest(Origin::none(), 42, s, StatementKind::Regular.to_text().to_vec()));
|
||||
assert_ok!(Claims::claim_attest(
|
||||
Origin::none(),
|
||||
42,
|
||||
s,
|
||||
StatementKind::Regular.to_text().to_vec()
|
||||
));
|
||||
assert_eq!(Balances::free_balance(&42), 200);
|
||||
assert_eq!(Claims::total(), total_claims() - 200);
|
||||
|
||||
let s = sig::<Test>(&dave(), &42u64.encode(), StatementKind::Regular.to_text());
|
||||
let r = Claims::claim_attest(Origin::none(), 42, s, StatementKind::Regular.to_text().to_vec());
|
||||
let r = Claims::claim_attest(
|
||||
Origin::none(),
|
||||
42,
|
||||
s,
|
||||
StatementKind::Regular.to_text().to_vec(),
|
||||
);
|
||||
assert_noop!(r, Error::<Test>::SignerHasNoClaim);
|
||||
});
|
||||
}
|
||||
@@ -897,8 +998,14 @@ mod tests {
|
||||
fn attesting_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Balances::free_balance(42), 0);
|
||||
assert_noop!(Claims::attest(Origin::signed(69), StatementKind::Saft.to_text().to_vec()), Error::<Test>::SenderHasNoClaim);
|
||||
assert_noop!(Claims::attest(Origin::signed(42), StatementKind::Regular.to_text().to_vec()), Error::<Test>::InvalidStatement);
|
||||
assert_noop!(
|
||||
Claims::attest(Origin::signed(69), StatementKind::Saft.to_text().to_vec()),
|
||||
Error::<Test>::SenderHasNoClaim
|
||||
);
|
||||
assert_noop!(
|
||||
Claims::attest(Origin::signed(42), StatementKind::Regular.to_text().to_vec()),
|
||||
Error::<Test>::InvalidStatement
|
||||
);
|
||||
assert_ok!(Claims::attest(Origin::signed(42), StatementKind::Saft.to_text().to_vec()));
|
||||
assert_eq!(Balances::free_balance(&42), 300);
|
||||
assert_eq!(Claims::total(), total_claims() - 300);
|
||||
@@ -910,7 +1017,11 @@ mod tests {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Balances::free_balance(42), 0);
|
||||
// Alice's claim is 100
|
||||
assert_ok!(Claims::claim(Origin::none(), 42, sig::<Test>(&alice(), &42u64.encode(), &[][..])));
|
||||
assert_ok!(Claims::claim(
|
||||
Origin::none(),
|
||||
42,
|
||||
sig::<Test>(&alice(), &42u64.encode(), &[][..])
|
||||
));
|
||||
assert_eq!(Balances::free_balance(&42), 100);
|
||||
// Eve's claim is 300 through Account 42
|
||||
assert_ok!(Claims::attest(Origin::signed(42), StatementKind::Saft.to_text().to_vec()));
|
||||
@@ -970,7 +1081,11 @@ mod tests {
|
||||
);
|
||||
assert_ok!(Claims::mint_claim(Origin::root(), eth(&bob()), 200, None, None));
|
||||
assert_eq!(Claims::total(), total_claims() + 200);
|
||||
assert_ok!(Claims::claim(Origin::none(), 69, sig::<Test>(&bob(), &69u64.encode(), &[][..])));
|
||||
assert_ok!(Claims::claim(
|
||||
Origin::none(),
|
||||
69,
|
||||
sig::<Test>(&bob(), &69u64.encode(), &[][..])
|
||||
));
|
||||
assert_eq!(Balances::free_balance(&69), 200);
|
||||
assert_eq!(Vesting::vesting_balance(&69), None);
|
||||
assert_eq!(Claims::total(), total_claims());
|
||||
@@ -989,14 +1104,29 @@ mod tests {
|
||||
Claims::claim(Origin::none(), 69, sig::<Test>(&bob(), &69u64.encode(), &[][..])),
|
||||
Error::<Test>::SignerHasNoClaim,
|
||||
);
|
||||
assert_ok!(Claims::mint_claim(Origin::root(), eth(&bob()), 200, Some((50, 10, 1)), None));
|
||||
assert_ok!(Claims::claim(Origin::none(), 69, sig::<Test>(&bob(), &69u64.encode(), &[][..])));
|
||||
assert_ok!(Claims::mint_claim(
|
||||
Origin::root(),
|
||||
eth(&bob()),
|
||||
200,
|
||||
Some((50, 10, 1)),
|
||||
None
|
||||
));
|
||||
assert_ok!(Claims::claim(
|
||||
Origin::none(),
|
||||
69,
|
||||
sig::<Test>(&bob(), &69u64.encode(), &[][..])
|
||||
));
|
||||
assert_eq!(Balances::free_balance(&69), 200);
|
||||
assert_eq!(Vesting::vesting_balance(&69), Some(50));
|
||||
|
||||
// Make sure we can not transfer the vested balance.
|
||||
assert_err!(
|
||||
<Balances as Currency<_>>::transfer(&69, &80, 180, ExistenceRequirement::AllowDeath),
|
||||
<Balances as Currency<_>>::transfer(
|
||||
&69,
|
||||
&80,
|
||||
180,
|
||||
ExistenceRequirement::AllowDeath
|
||||
),
|
||||
pallet_balances::Error::<Test, _>::LiquidityRestrictions,
|
||||
);
|
||||
});
|
||||
@@ -1006,29 +1136,43 @@ mod tests {
|
||||
fn add_claim_with_statement_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_noop!(
|
||||
Claims::mint_claim(Origin::signed(42), eth(&bob()), 200, None, Some(StatementKind::Regular)),
|
||||
Claims::mint_claim(
|
||||
Origin::signed(42),
|
||||
eth(&bob()),
|
||||
200,
|
||||
None,
|
||||
Some(StatementKind::Regular)
|
||||
),
|
||||
sp_runtime::traits::BadOrigin,
|
||||
);
|
||||
assert_eq!(Balances::free_balance(42), 0);
|
||||
let signature = sig::<Test>(&bob(), &69u64.encode(), StatementKind::Regular.to_text());
|
||||
assert_noop!(
|
||||
Claims::claim_attest(
|
||||
Origin::none(), 69, signature.clone(), StatementKind::Regular.to_text().to_vec()
|
||||
Origin::none(),
|
||||
69,
|
||||
signature.clone(),
|
||||
StatementKind::Regular.to_text().to_vec()
|
||||
),
|
||||
Error::<Test>::SignerHasNoClaim
|
||||
);
|
||||
assert_ok!(Claims::mint_claim(Origin::root(), eth(&bob()), 200, None, Some(StatementKind::Regular)));
|
||||
assert_ok!(Claims::mint_claim(
|
||||
Origin::root(),
|
||||
eth(&bob()),
|
||||
200,
|
||||
None,
|
||||
Some(StatementKind::Regular)
|
||||
));
|
||||
assert_noop!(
|
||||
Claims::claim_attest(
|
||||
Origin::none(), 69, signature.clone(), vec![],
|
||||
),
|
||||
Claims::claim_attest(Origin::none(), 69, signature.clone(), vec![],),
|
||||
Error::<Test>::SignerHasNoClaim
|
||||
);
|
||||
assert_ok!(
|
||||
Claims::claim_attest(
|
||||
Origin::none(), 69, signature.clone(), StatementKind::Regular.to_text().to_vec()
|
||||
)
|
||||
);
|
||||
assert_ok!(Claims::claim_attest(
|
||||
Origin::none(),
|
||||
69,
|
||||
signature.clone(),
|
||||
StatementKind::Regular.to_text().to_vec()
|
||||
));
|
||||
assert_eq!(Balances::free_balance(&69), 200);
|
||||
});
|
||||
}
|
||||
@@ -1038,7 +1182,11 @@ mod tests {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Balances::free_balance(42), 0);
|
||||
assert_err!(
|
||||
Claims::claim(Origin::signed(42), 42, sig::<Test>(&alice(), &42u64.encode(), &[][..])),
|
||||
Claims::claim(
|
||||
Origin::signed(42),
|
||||
42,
|
||||
sig::<Test>(&alice(), &42u64.encode(), &[][..])
|
||||
),
|
||||
sp_runtime::traits::BadOrigin,
|
||||
);
|
||||
});
|
||||
@@ -1048,7 +1196,11 @@ mod tests {
|
||||
fn double_claiming_doesnt_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Balances::free_balance(42), 0);
|
||||
assert_ok!(Claims::claim(Origin::none(), 42, sig::<Test>(&alice(), &42u64.encode(), &[][..])));
|
||||
assert_ok!(Claims::claim(
|
||||
Origin::none(),
|
||||
42,
|
||||
sig::<Test>(&alice(), &42u64.encode(), &[][..])
|
||||
));
|
||||
assert_noop!(
|
||||
Claims::claim(Origin::none(), 42, sig::<Test>(&alice(), &42u64.encode(), &[][..])),
|
||||
Error::<Test>::SignerHasNoClaim
|
||||
@@ -1060,10 +1212,21 @@ mod tests {
|
||||
fn claiming_while_vested_doesnt_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// A user is already vested
|
||||
assert_ok!(<Test as Config>::VestingSchedule::add_vesting_schedule(&69, total_claims(), 100, 10));
|
||||
assert_ok!(<Test as Config>::VestingSchedule::add_vesting_schedule(
|
||||
&69,
|
||||
total_claims(),
|
||||
100,
|
||||
10
|
||||
));
|
||||
CurrencyOf::<Test>::make_free_balance_be(&69, total_claims());
|
||||
assert_eq!(Balances::free_balance(69), total_claims());
|
||||
assert_ok!(Claims::mint_claim(Origin::root(), eth(&bob()), 200, Some((50, 10, 1)), None));
|
||||
assert_ok!(Claims::mint_claim(
|
||||
Origin::root(),
|
||||
eth(&bob()),
|
||||
200,
|
||||
Some((50, 10, 1)),
|
||||
None
|
||||
));
|
||||
// New total
|
||||
assert_eq!(Claims::total(), total_claims() + 200);
|
||||
|
||||
@@ -1116,7 +1279,10 @@ mod tests {
|
||||
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(
|
||||
<Pallet<Test>>::validate_unsigned(source, &ClaimsCall::claim(1, sig::<Test>(&alice(), &1u64.encode(), &[][..]))),
|
||||
<Pallet<Test>>::validate_unsigned(
|
||||
source,
|
||||
&ClaimsCall::claim(1, sig::<Test>(&alice(), &1u64.encode(), &[][..]))
|
||||
),
|
||||
Ok(ValidTransaction {
|
||||
priority: 100,
|
||||
requires: vec![],
|
||||
@@ -1126,11 +1292,17 @@ mod tests {
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
<Pallet<Test>>::validate_unsigned(source, &ClaimsCall::claim(0, EcdsaSignature([0; 65]))),
|
||||
<Pallet<Test>>::validate_unsigned(
|
||||
source,
|
||||
&ClaimsCall::claim(0, EcdsaSignature([0; 65]))
|
||||
),
|
||||
InvalidTransaction::Custom(ValidityError::InvalidEthereumSignature.into()).into(),
|
||||
);
|
||||
assert_eq!(
|
||||
<Pallet<Test>>::validate_unsigned(source, &ClaimsCall::claim(1, sig::<Test>(&bob(), &1u64.encode(), &[][..]))),
|
||||
<Pallet<Test>>::validate_unsigned(
|
||||
source,
|
||||
&ClaimsCall::claim(1, sig::<Test>(&bob(), &1u64.encode(), &[][..]))
|
||||
),
|
||||
InvalidTransaction::Custom(ValidityError::SignerHasNoClaim.into()).into(),
|
||||
);
|
||||
let s = sig::<Test>(&dave(), &1u64.encode(), StatementKind::Regular.to_text());
|
||||
@@ -1148,8 +1320,11 @@ mod tests {
|
||||
assert_eq!(
|
||||
<Pallet<Test>>::validate_unsigned(
|
||||
source,
|
||||
&ClaimsCall::claim_attest(1, EcdsaSignature([0; 65]),
|
||||
StatementKind::Regular.to_text().to_vec())
|
||||
&ClaimsCall::claim_attest(
|
||||
1,
|
||||
EcdsaSignature([0; 65]),
|
||||
StatementKind::Regular.to_text().to_vec()
|
||||
)
|
||||
),
|
||||
InvalidTransaction::Custom(ValidityError::InvalidEthereumSignature.into()).into(),
|
||||
);
|
||||
@@ -1181,12 +1356,11 @@ mod tests {
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking {
|
||||
use super::*;
|
||||
use secp_utils::*;
|
||||
use frame_system::RawOrigin;
|
||||
use frame_benchmarking::{benchmarks, account};
|
||||
use sp_runtime::DispatchResult;
|
||||
use sp_runtime::traits::ValidateUnsigned;
|
||||
use crate::claims::Call;
|
||||
use frame_benchmarking::{account, benchmarks};
|
||||
use frame_system::RawOrigin;
|
||||
use secp_utils::*;
|
||||
use sp_runtime::{traits::ValidateUnsigned, DispatchResult};
|
||||
|
||||
const SEED: u32 = 0;
|
||||
|
||||
@@ -1197,7 +1371,13 @@ mod benchmarking {
|
||||
let secret_key = libsecp256k1::SecretKey::parse(&keccak_256(&input.encode())).unwrap();
|
||||
let eth_address = eth(&secret_key);
|
||||
let vesting = Some((100_000u32.into(), 1_000u32.into(), 100u32.into()));
|
||||
super::Pallet::<T>::mint_claim(RawOrigin::Root.into(), eth_address, VALUE.into(), vesting, None)?;
|
||||
super::Pallet::<T>::mint_claim(
|
||||
RawOrigin::Root.into(),
|
||||
eth_address,
|
||||
VALUE.into(),
|
||||
vesting,
|
||||
None,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1210,7 +1390,7 @@ mod benchmarking {
|
||||
eth_address,
|
||||
VALUE.into(),
|
||||
vesting,
|
||||
Some(Default::default())
|
||||
Some(Default::default()),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -49,33 +49,35 @@
|
||||
//! the parachain remains active. Users can withdraw their funds once the slot is completed and funds are
|
||||
//! returned to the crowdloan account.
|
||||
|
||||
use crate::{
|
||||
slot_range::SlotRange,
|
||||
traits::{Auctioneer, Registrar},
|
||||
};
|
||||
use frame_support::{
|
||||
ensure, Identity, PalletId,
|
||||
storage::{child, ChildTriePrefixIterator},
|
||||
traits::{
|
||||
Currency, ReservableCurrency, Get, ExistenceRequirement::AllowDeath
|
||||
},
|
||||
ensure,
|
||||
pallet_prelude::Weight,
|
||||
storage::{child, ChildTriePrefixIterator},
|
||||
traits::{Currency, ExistenceRequirement::AllowDeath, Get, ReservableCurrency},
|
||||
Identity, PalletId,
|
||||
};
|
||||
use sp_runtime::{
|
||||
RuntimeDebug, MultiSignature, MultiSigner,
|
||||
traits::{
|
||||
AccountIdConversion, Hash, Saturating, Zero, One, CheckedAdd, Verify, IdentifyAccount,
|
||||
},
|
||||
};
|
||||
use crate::traits::{Registrar, Auctioneer};
|
||||
use crate::slot_range::SlotRange;
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use sp_std::vec::Vec;
|
||||
use primitives::v1::Id as ParaId;
|
||||
pub use pallet::*;
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use primitives::v1::Id as ParaId;
|
||||
use sp_runtime::{
|
||||
traits::{
|
||||
AccountIdConversion, CheckedAdd, Hash, IdentifyAccount, One, Saturating, Verify, Zero,
|
||||
},
|
||||
MultiSignature, MultiSigner, RuntimeDebug,
|
||||
};
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
type CurrencyOf<T> = <<T as Config>::Auctioneer as Auctioneer>::Currency;
|
||||
type LeasePeriodOf<T> = <<T as Config>::Auctioneer as Auctioneer>::LeasePeriod;
|
||||
type BalanceOf<T> = <CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
|
||||
#[allow(dead_code)]
|
||||
type NegativeImbalanceOf<T> = <CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
|
||||
type NegativeImbalanceOf<T> =
|
||||
<CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
|
||||
|
||||
type TrieIndex = u32;
|
||||
|
||||
@@ -83,25 +85,43 @@ pub trait WeightInfo {
|
||||
fn create() -> Weight;
|
||||
fn contribute() -> Weight;
|
||||
fn withdraw() -> Weight;
|
||||
fn refund(k: u32, ) -> Weight;
|
||||
fn refund(k: u32) -> Weight;
|
||||
fn dissolve() -> Weight;
|
||||
fn edit() -> Weight;
|
||||
fn add_memo() -> Weight;
|
||||
fn on_initialize(n: u32, ) -> Weight;
|
||||
fn on_initialize(n: u32) -> Weight;
|
||||
fn poke() -> Weight;
|
||||
}
|
||||
|
||||
pub struct TestWeightInfo;
|
||||
impl WeightInfo for TestWeightInfo {
|
||||
fn create() -> Weight { 0 }
|
||||
fn contribute() -> Weight { 0 }
|
||||
fn withdraw() -> Weight { 0 }
|
||||
fn refund(_k: u32, ) -> Weight { 0 }
|
||||
fn dissolve() -> Weight { 0 }
|
||||
fn edit() -> Weight { 0 }
|
||||
fn add_memo() -> Weight { 0 }
|
||||
fn on_initialize(_n: u32, ) -> Weight { 0 }
|
||||
fn poke() -> Weight { 0 }
|
||||
fn create() -> Weight {
|
||||
0
|
||||
}
|
||||
fn contribute() -> Weight {
|
||||
0
|
||||
}
|
||||
fn withdraw() -> Weight {
|
||||
0
|
||||
}
|
||||
fn refund(_k: u32) -> Weight {
|
||||
0
|
||||
}
|
||||
fn dissolve() -> Weight {
|
||||
0
|
||||
}
|
||||
fn edit() -> Weight {
|
||||
0
|
||||
}
|
||||
fn add_memo() -> Weight {
|
||||
0
|
||||
}
|
||||
fn on_initialize(_n: u32) -> Weight {
|
||||
0
|
||||
}
|
||||
fn poke() -> Weight {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)]
|
||||
@@ -148,9 +168,9 @@ pub struct FundInfo<AccountId, Balance, BlockNumber, LeasePeriod> {
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::{pallet_prelude::*, ensure_signed, ensure_root};
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
@@ -178,13 +198,13 @@ pub mod pallet {
|
||||
|
||||
/// The parachain registrar type. We just use this to ensure that only the manager of a para is able to
|
||||
/// start a crowdloan for its slot.
|
||||
type Registrar: Registrar<AccountId=Self::AccountId>;
|
||||
type Registrar: Registrar<AccountId = Self::AccountId>;
|
||||
|
||||
/// The type representing the auctioning system.
|
||||
type Auctioneer: Auctioneer<
|
||||
AccountId=Self::AccountId,
|
||||
BlockNumber=Self::BlockNumber,
|
||||
LeasePeriod=Self::BlockNumber,
|
||||
AccountId = Self::AccountId,
|
||||
BlockNumber = Self::BlockNumber,
|
||||
LeasePeriod = Self::BlockNumber,
|
||||
>;
|
||||
|
||||
/// The maximum length for the memo attached to a crowdloan contribution.
|
||||
@@ -199,7 +219,8 @@ pub mod pallet {
|
||||
#[pallet::getter(fn funds)]
|
||||
pub(super) type Funds<T: Config> = StorageMap<
|
||||
_,
|
||||
Twox64Concat, ParaId,
|
||||
Twox64Concat,
|
||||
ParaId,
|
||||
FundInfo<T::AccountId, BalanceOf<T>, T::BlockNumber, LeasePeriodOf<T>>,
|
||||
>;
|
||||
|
||||
@@ -305,7 +326,9 @@ pub mod pallet {
|
||||
}
|
||||
let new_raise = NewRaise::<T>::take();
|
||||
let new_raise_len = new_raise.len() as u32;
|
||||
for (fund, para_id) in new_raise.into_iter().filter_map(|i| Self::funds(i).map(|f| (f, i))) {
|
||||
for (fund, para_id) in
|
||||
new_raise.into_iter().filter_map(|i| Self::funds(i).map(|f| (f, i)))
|
||||
{
|
||||
// Care needs to be taken by the crowdloan creator that this function will succeed given
|
||||
// the crowdloaning configuration. We do some checks ahead of time in crowdloan `create`.
|
||||
let result = T::Auctioneer::place_bid(
|
||||
@@ -349,9 +372,13 @@ pub mod pallet {
|
||||
.ok_or(Error::<T>::FirstPeriodTooFarInFuture)?;
|
||||
ensure!(last_period <= last_period_limit, Error::<T>::LastPeriodTooFarInFuture);
|
||||
ensure!(end > <frame_system::Pallet<T>>::block_number(), Error::<T>::CannotEndInPast);
|
||||
let last_possible_win_date = (first_period.saturating_add(One::one())).saturating_mul(T::Auctioneer::lease_period());
|
||||
let last_possible_win_date = (first_period.saturating_add(One::one()))
|
||||
.saturating_mul(T::Auctioneer::lease_period());
|
||||
ensure!(end <= last_possible_win_date, Error::<T>::EndTooFarInFuture);
|
||||
ensure!(first_period >= T::Auctioneer::lease_period_index(), Error::<T>::FirstPeriodInPast);
|
||||
ensure!(
|
||||
first_period >= T::Auctioneer::lease_period_index(),
|
||||
Error::<T>::FirstPeriodInPast
|
||||
);
|
||||
|
||||
// There should not be an existing fund.
|
||||
ensure!(!Funds::<T>::contains_key(index), Error::<T>::FundNotEnded);
|
||||
@@ -367,18 +394,21 @@ pub mod pallet {
|
||||
|
||||
CurrencyOf::<T>::reserve(&depositor, deposit)?;
|
||||
|
||||
Funds::<T>::insert(index, FundInfo {
|
||||
depositor,
|
||||
verifier,
|
||||
deposit,
|
||||
raised: Zero::zero(),
|
||||
end,
|
||||
cap,
|
||||
last_contribution: LastContribution::Never,
|
||||
first_period,
|
||||
last_period,
|
||||
trie_index,
|
||||
});
|
||||
Funds::<T>::insert(
|
||||
index,
|
||||
FundInfo {
|
||||
depositor,
|
||||
verifier,
|
||||
deposit,
|
||||
raised: Zero::zero(),
|
||||
end,
|
||||
cap,
|
||||
last_contribution: LastContribution::Never,
|
||||
first_period,
|
||||
last_period,
|
||||
trie_index,
|
||||
},
|
||||
);
|
||||
|
||||
NextTrieIndex::<T>::put(new_trie_index);
|
||||
// Add a lock to the para so that the configuration cannot be changed.
|
||||
@@ -401,7 +431,7 @@ pub mod pallet {
|
||||
|
||||
ensure!(value >= T::MinContribution::get(), Error::<T>::ContributionTooSmall);
|
||||
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
|
||||
fund.raised = fund.raised.checked_add(&value).ok_or(Error::<T>::Overflow)?;
|
||||
fund.raised = fund.raised.checked_add(&value).ok_or(Error::<T>::Overflow)?;
|
||||
ensure!(fund.raised <= fund.cap, Error::<T>::CapExceeded);
|
||||
|
||||
// Make sure crowdloan has not ended
|
||||
@@ -414,7 +444,10 @@ pub mod pallet {
|
||||
|
||||
// Make sure crowdloan has not already won.
|
||||
let fund_account = Self::fund_account_id(index);
|
||||
ensure!(!T::Auctioneer::has_won_an_auction(index, &fund_account), Error::<T>::BidOrLeaseActive);
|
||||
ensure!(
|
||||
!T::Auctioneer::has_won_an_auction(index, &fund_account),
|
||||
Error::<T>::BidOrLeaseActive
|
||||
);
|
||||
|
||||
// We disallow any crowdloan contributions during the VRF Period, so that people do not sneak their
|
||||
// contributions into the auction when it would not impact the outcome.
|
||||
@@ -425,7 +458,9 @@ pub mod pallet {
|
||||
if let Some(ref verifier) = fund.verifier {
|
||||
let signature = signature.ok_or(Error::<T>::InvalidSignature)?;
|
||||
let payload = (index, &who, old_balance, value);
|
||||
let valid = payload.using_encoded(|encoded| signature.verify(encoded, &verifier.clone().into_account()));
|
||||
let valid = payload.using_encoded(|encoded| {
|
||||
signature.verify(encoded, &verifier.clone().into_account())
|
||||
});
|
||||
ensure!(valid, Error::<T>::InvalidSignature);
|
||||
}
|
||||
|
||||
@@ -439,11 +474,11 @@ pub mod pallet {
|
||||
// In ending period; must ensure that we are in NewRaise.
|
||||
LastContribution::Ending(n) if n == now => {
|
||||
// do nothing - already in NewRaise
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
NewRaise::<T>::append(index);
|
||||
fund.last_contribution = LastContribution::Ending(now);
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
let endings_count = Self::endings_count();
|
||||
@@ -452,13 +487,13 @@ pub mod pallet {
|
||||
// Not in ending period and no auctions have ended ending since our
|
||||
// previous bid which was also not in an ending period.
|
||||
// `NewRaise` will contain our ID still: Do nothing.
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// Not in ending period; but an auction has been ending since our previous
|
||||
// bid, or we never had one to begin with. Add bid.
|
||||
NewRaise::<T>::append(index);
|
||||
fund.last_contribution = LastContribution::PreEnding(endings_count);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,7 +573,7 @@ pub mod pallet {
|
||||
if refund_count >= T::RemoveKeysLimit::get() {
|
||||
// Not everyone was able to be refunded this time around.
|
||||
all_refunded = false;
|
||||
break;
|
||||
break
|
||||
}
|
||||
CurrencyOf::<T>::transfer(&fund_account, &who, balance, AllowDeath)?;
|
||||
Self::contribution_kill(fund.trie_index, &who);
|
||||
@@ -602,18 +637,21 @@ pub mod pallet {
|
||||
|
||||
let fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
|
||||
|
||||
Funds::<T>::insert(index, FundInfo {
|
||||
depositor: fund.depositor,
|
||||
verifier,
|
||||
deposit: fund.deposit,
|
||||
raised: fund.raised,
|
||||
end,
|
||||
cap,
|
||||
last_contribution: fund.last_contribution,
|
||||
first_period,
|
||||
last_period,
|
||||
trie_index: fund.trie_index,
|
||||
});
|
||||
Funds::<T>::insert(
|
||||
index,
|
||||
FundInfo {
|
||||
depositor: fund.depositor,
|
||||
verifier,
|
||||
deposit: fund.deposit,
|
||||
raised: fund.raised,
|
||||
end,
|
||||
cap,
|
||||
last_contribution: fund.last_contribution,
|
||||
first_period,
|
||||
last_period,
|
||||
trie_index: fund.trie_index,
|
||||
},
|
||||
);
|
||||
|
||||
Self::deposit_event(Event::<T>::Edited(index));
|
||||
Ok(())
|
||||
@@ -669,15 +707,19 @@ impl<T: Config> Pallet<T> {
|
||||
child::ChildInfo::new_default(T::Hashing::hash(&buf[..]).as_ref())
|
||||
}
|
||||
|
||||
pub fn contribution_put(index: TrieIndex, who: &T::AccountId, balance: &BalanceOf<T>, memo: &[u8]) {
|
||||
pub fn contribution_put(
|
||||
index: TrieIndex,
|
||||
who: &T::AccountId,
|
||||
balance: &BalanceOf<T>,
|
||||
memo: &[u8],
|
||||
) {
|
||||
who.using_encoded(|b| child::put(&Self::id_from_index(index), b, &(balance, memo)));
|
||||
}
|
||||
|
||||
pub fn contribution_get(index: TrieIndex, who: &T::AccountId) -> (BalanceOf<T>, Vec<u8>) {
|
||||
who.using_encoded(|b| child::get_or_default::<(BalanceOf<T>, Vec<u8>)>(
|
||||
&Self::id_from_index(index),
|
||||
b,
|
||||
))
|
||||
who.using_encoded(|b| {
|
||||
child::get_or_default::<(BalanceOf<T>, Vec<u8>)>(&Self::id_from_index(index), b)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn contribution_kill(index: TrieIndex, who: &T::AccountId) {
|
||||
@@ -689,9 +731,12 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
|
||||
pub fn contribution_iterator(
|
||||
index: TrieIndex
|
||||
index: TrieIndex,
|
||||
) -> ChildTriePrefixIterator<(T::AccountId, (BalanceOf<T>, Vec<u8>))> {
|
||||
ChildTriePrefixIterator::<_>::with_prefix_over_key::<Identity>(&Self::id_from_index(index), &[])
|
||||
ChildTriePrefixIterator::<_>::with_prefix_over_key::<Identity>(
|
||||
&Self::id_from_index(index),
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
/// This function checks all conditions which would qualify a crowdloan has ended.
|
||||
@@ -701,40 +746,39 @@ impl<T: Config> Pallet<T> {
|
||||
fn ensure_crowdloan_ended(
|
||||
now: T::BlockNumber,
|
||||
fund_account: &T::AccountId,
|
||||
fund: &FundInfo<T::AccountId, BalanceOf<T>, T::BlockNumber, LeasePeriodOf<T>>
|
||||
fund: &FundInfo<T::AccountId, BalanceOf<T>, T::BlockNumber, LeasePeriodOf<T>>,
|
||||
) -> sp_runtime::DispatchResult {
|
||||
// `fund.end` can represent the end of a failed crowdloan or the beginning of retirement
|
||||
// If the current lease period is past the first period they are trying to bid for, then
|
||||
// it is already too late to win the bid.
|
||||
let current_lease_period = T::Auctioneer::lease_period_index();
|
||||
ensure!(now >= fund.end || current_lease_period > fund.first_period, Error::<T>::FundNotEnded);
|
||||
// free balance must greater than or equal amount raised, otherwise funds are being used
|
||||
// and a bid or lease must be active.
|
||||
ensure!(CurrencyOf::<T>::free_balance(&fund_account) >= fund.raised, Error::<T>::BidOrLeaseActive);
|
||||
// `fund.end` can represent the end of a failed crowdloan or the beginning of retirement
|
||||
// If the current lease period is past the first period they are trying to bid for, then
|
||||
// it is already too late to win the bid.
|
||||
let current_lease_period = T::Auctioneer::lease_period_index();
|
||||
ensure!(
|
||||
now >= fund.end || current_lease_period > fund.first_period,
|
||||
Error::<T>::FundNotEnded
|
||||
);
|
||||
// free balance must greater than or equal amount raised, otherwise funds are being used
|
||||
// and a bid or lease must be active.
|
||||
ensure!(
|
||||
CurrencyOf::<T>::free_balance(&fund_account) >= fund.raised,
|
||||
Error::<T>::BidOrLeaseActive
|
||||
);
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> crate::traits::OnSwap for Pallet<T> {
|
||||
fn on_swap(one: ParaId, other: ParaId) {
|
||||
Funds::<T>::mutate(one, |x|
|
||||
Funds::<T>::mutate(other, |y|
|
||||
sp_std::mem::swap(x, y)
|
||||
)
|
||||
)
|
||||
Funds::<T>::mutate(one, |x| Funds::<T>::mutate(other, |y| sp_std::mem::swap(x, y)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "runtime-benchmarks", test))]
|
||||
mod crypto {
|
||||
use sp_core::ed25519;
|
||||
use sp_io::crypto::{ed25519_sign, ed25519_generate};
|
||||
use sp_std::{
|
||||
vec::Vec,
|
||||
convert::TryFrom,
|
||||
};
|
||||
use sp_runtime::{MultiSigner, MultiSignature};
|
||||
use sp_io::crypto::{ed25519_generate, ed25519_sign};
|
||||
use sp_runtime::{MultiSignature, MultiSigner};
|
||||
use sp_std::{convert::TryFrom, vec::Vec};
|
||||
|
||||
pub fn create_ed25519_pubkey(seed: Vec<u8>) -> MultiSigner {
|
||||
ed25519_generate(0.into(), Some(seed)).into()
|
||||
@@ -751,24 +795,26 @@ mod crypto {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::{cell::RefCell, sync::Arc, collections::BTreeMap};
|
||||
use frame_support::{
|
||||
assert_ok, assert_noop, parameter_types,
|
||||
traits::{OnInitialize, OnFinalize},
|
||||
assert_noop, assert_ok, parameter_types,
|
||||
traits::{OnFinalize, OnInitialize},
|
||||
};
|
||||
use sp_core::H256;
|
||||
use primitives::v1::Id as ParaId;
|
||||
use sp_core::H256;
|
||||
use std::{cell::RefCell, collections::BTreeMap, sync::Arc};
|
||||
// The testing primitives are very useful for avoiding having to work with signatures
|
||||
// or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried.
|
||||
use sp_runtime::{
|
||||
testing::Header, traits::{BlakeTwo256, IdentityLookup}, DispatchResult,
|
||||
};
|
||||
use crate::{
|
||||
mock::TestRegistrar,
|
||||
traits::{OnSwap, AuctionStatus},
|
||||
crowdloan,
|
||||
mock::TestRegistrar,
|
||||
traits::{AuctionStatus, OnSwap},
|
||||
};
|
||||
use sp_keystore::{testing::KeyStore, KeystoreExt};
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
DispatchResult,
|
||||
};
|
||||
use sp_keystore::{KeystoreExt, testing::KeyStore};
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
@@ -840,7 +886,7 @@ mod tests {
|
||||
para: ParaId,
|
||||
first_period: u64,
|
||||
last_period: u64,
|
||||
amount: u64
|
||||
amount: u64,
|
||||
}
|
||||
thread_local! {
|
||||
static AUCTION: RefCell<Option<(u64, u64)>> = RefCell::new(None);
|
||||
@@ -875,7 +921,8 @@ mod tests {
|
||||
let account_id = Crowdloan::fund_account_id(para);
|
||||
if winner {
|
||||
let free_balance = Balances::free_balance(&account_id);
|
||||
Balances::reserve(&account_id, free_balance).expect("should be able to reserve free balance");
|
||||
Balances::reserve(&account_id, free_balance)
|
||||
.expect("should be able to reserve free balance");
|
||||
} else {
|
||||
let reserved_balance = Balances::reserved_balance(&account_id);
|
||||
Balances::unreserve(&account_id, reserved_balance);
|
||||
@@ -915,10 +962,10 @@ mod tests {
|
||||
let after_end = after_early_end - ending_period;
|
||||
// Optional VRF delay
|
||||
if after_end < vrf_delay() {
|
||||
return AuctionStatus::VrfDelay(after_end);
|
||||
return AuctionStatus::VrfDelay(after_end)
|
||||
} else {
|
||||
// VRF delay is done, so we just end the auction
|
||||
return AuctionStatus::NotStarted;
|
||||
return AuctionStatus::NotStarted
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -928,10 +975,19 @@ mod tests {
|
||||
para: ParaId,
|
||||
first_period: u64,
|
||||
last_period: u64,
|
||||
amount: u64
|
||||
amount: u64,
|
||||
) -> DispatchResult {
|
||||
let height = System::block_number();
|
||||
BIDS_PLACED.with(|p| p.borrow_mut().push(BidPlaced { height, bidder, para, first_period, last_period, amount }));
|
||||
BIDS_PLACED.with(|p| {
|
||||
p.borrow_mut().push(BidPlaced {
|
||||
height,
|
||||
bidder,
|
||||
para,
|
||||
first_period,
|
||||
last_period,
|
||||
amount,
|
||||
})
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -974,9 +1030,11 @@ mod tests {
|
||||
// our desired mockup.
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
pallet_balances::GenesisConfig::<Test>{
|
||||
pallet_balances::GenesisConfig::<Test> {
|
||||
balances: vec![(1, 1000), (2, 2000), (3, 3000), (4, 4000)],
|
||||
}.assimilate_storage(&mut t).unwrap();
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
let keystore = KeyStore::new();
|
||||
let mut t: sp_io::TestExternalities = t.into();
|
||||
t.register_extension(KeystoreExt(Arc::new(keystore)));
|
||||
@@ -986,9 +1044,16 @@ mod tests {
|
||||
fn new_para() -> ParaId {
|
||||
for i in 0.. {
|
||||
let para: ParaId = i.into();
|
||||
if TestRegistrar::<Test>::is_registered(para) { continue }
|
||||
assert_ok!(TestRegistrar::<Test>::register(1, para, Default::default(), Default::default()));
|
||||
return para;
|
||||
if TestRegistrar::<Test>::is_registered(para) {
|
||||
continue
|
||||
}
|
||||
assert_ok!(TestRegistrar::<Test>::register(
|
||||
1,
|
||||
para,
|
||||
Default::default(),
|
||||
Default::default()
|
||||
));
|
||||
return para
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
@@ -1023,7 +1088,14 @@ mod tests {
|
||||
|
||||
assert_eq!(bids(), vec![]);
|
||||
assert_ok!(TestAuctioneer::place_bid(1, 2.into(), 0, 3, 6));
|
||||
let b = BidPlaced { height: 0, bidder: 1, para: 2.into(), first_period: 0, last_period: 3, amount: 6 };
|
||||
let b = BidPlaced {
|
||||
height: 0,
|
||||
bidder: 1,
|
||||
para: 2.into(),
|
||||
first_period: 0,
|
||||
last_period: 3,
|
||||
amount: 6,
|
||||
};
|
||||
assert_eq!(bids(), vec![b]);
|
||||
assert_eq!(TestAuctioneer::auction_status(4), AuctionStatus::<u64>::StartingPeriod);
|
||||
assert_eq!(TestAuctioneer::auction_status(5), AuctionStatus::<u64>::EndingPeriod(0, 0));
|
||||
@@ -1069,7 +1141,15 @@ mod tests {
|
||||
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
|
||||
let para = new_para();
|
||||
// Now try to create a crowdloan campaign
|
||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 9, Some(pubkey.clone())));
|
||||
assert_ok!(Crowdloan::create(
|
||||
Origin::signed(1),
|
||||
para,
|
||||
1000,
|
||||
1,
|
||||
4,
|
||||
9,
|
||||
Some(pubkey.clone())
|
||||
));
|
||||
// This is what the initial `fund_info` should look like
|
||||
let fund_info = FundInfo {
|
||||
depositor: 1,
|
||||
@@ -1110,13 +1190,24 @@ mod tests {
|
||||
assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 9, 9, None), e);
|
||||
|
||||
// Cannot create a crowdloan without some deposit funds
|
||||
assert_ok!(TestRegistrar::<Test>::register(1337, ParaId::from(1234), Default::default(), Default::default()));
|
||||
assert_ok!(TestRegistrar::<Test>::register(
|
||||
1337,
|
||||
ParaId::from(1234),
|
||||
Default::default(),
|
||||
Default::default()
|
||||
));
|
||||
let e = BalancesError::<Test, _>::InsufficientBalance;
|
||||
assert_noop!(Crowdloan::create(Origin::signed(1337), ParaId::from(1234), 1000, 1, 3, 9, None), e);
|
||||
assert_noop!(
|
||||
Crowdloan::create(Origin::signed(1337), ParaId::from(1234), 1000, 1, 3, 9, None),
|
||||
e
|
||||
);
|
||||
|
||||
// Cannot create a crowdloan with nonsense end date
|
||||
// This crowdloan would end in lease period 2, but is bidding for some slot that starts in lease period 1.
|
||||
assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 41, None), Error::<Test>::EndTooFarInFuture);
|
||||
assert_noop!(
|
||||
Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 41, None),
|
||||
Error::<Test>::EndTooFarInFuture
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1156,30 +1247,59 @@ mod tests {
|
||||
let para = new_para();
|
||||
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
|
||||
// Set up a crowdloan
|
||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 9, Some(pubkey.clone())));
|
||||
assert_ok!(Crowdloan::create(
|
||||
Origin::signed(1),
|
||||
para,
|
||||
1000,
|
||||
1,
|
||||
4,
|
||||
9,
|
||||
Some(pubkey.clone())
|
||||
));
|
||||
|
||||
// No contributions yet
|
||||
assert_eq!(Crowdloan::contribution_get(u32::from(para), &1).0, 0);
|
||||
|
||||
// Missing signature
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 49, None), Error::<Test>::InvalidSignature);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(1), para, 49, None),
|
||||
Error::<Test>::InvalidSignature
|
||||
);
|
||||
|
||||
let payload = (0u32, 1u64, 0u64, 49u64);
|
||||
let valid_signature = crypto::create_ed25519_signature(&payload.encode(), pubkey.clone());
|
||||
let valid_signature =
|
||||
crypto::create_ed25519_signature(&payload.encode(), pubkey.clone());
|
||||
let invalid_signature = MultiSignature::default();
|
||||
|
||||
// Invalid signature
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 49, Some(invalid_signature)), Error::<Test>::InvalidSignature);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(1), para, 49, Some(invalid_signature)),
|
||||
Error::<Test>::InvalidSignature
|
||||
);
|
||||
|
||||
// Valid signature wrong parameter
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 50, Some(valid_signature.clone())), Error::<Test>::InvalidSignature);
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(2), para, 49, Some(valid_signature.clone())), Error::<Test>::InvalidSignature);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(1), para, 50, Some(valid_signature.clone())),
|
||||
Error::<Test>::InvalidSignature
|
||||
);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(2), para, 49, Some(valid_signature.clone())),
|
||||
Error::<Test>::InvalidSignature
|
||||
);
|
||||
|
||||
// Valid signature
|
||||
assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 49, Some(valid_signature.clone())));
|
||||
assert_ok!(Crowdloan::contribute(
|
||||
Origin::signed(1),
|
||||
para,
|
||||
49,
|
||||
Some(valid_signature.clone())
|
||||
));
|
||||
|
||||
// Reuse valid signature
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 49, Some(valid_signature)), Error::<Test>::InvalidSignature);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(1), para, 49, Some(valid_signature)),
|
||||
Error::<Test>::InvalidSignature
|
||||
);
|
||||
|
||||
let payload_2 = (0u32, 1u64, 49u64, 10u64);
|
||||
let valid_signature_2 = crypto::create_ed25519_signature(&payload_2.encode(), pubkey);
|
||||
@@ -1202,22 +1322,34 @@ mod tests {
|
||||
let para = new_para();
|
||||
|
||||
// Cannot contribute to non-existing fund
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 49, None), Error::<Test>::InvalidParaId);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(1), para, 49, None),
|
||||
Error::<Test>::InvalidParaId
|
||||
);
|
||||
// Cannot contribute below minimum contribution
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 9, None), Error::<Test>::ContributionTooSmall);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(1), para, 9, None),
|
||||
Error::<Test>::ContributionTooSmall
|
||||
);
|
||||
|
||||
// Set up a crowdloan
|
||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 9, None));
|
||||
assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 101, None));
|
||||
|
||||
// Cannot contribute past the limit
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(2), para, 900, None), Error::<Test>::CapExceeded);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(2), para, 900, None),
|
||||
Error::<Test>::CapExceeded
|
||||
);
|
||||
|
||||
// Move past end date
|
||||
run_to_block(10);
|
||||
|
||||
// Cannot contribute to ended fund
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 49, None), Error::<Test>::ContributionPeriodOver);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(1), para, 49, None),
|
||||
Error::<Test>::ContributionPeriodOver
|
||||
);
|
||||
|
||||
// If a crowdloan has already won, it should not allow contributions.
|
||||
let para_2 = new_para();
|
||||
@@ -1225,7 +1357,10 @@ mod tests {
|
||||
// Emulate a win by leasing out and putting a deposit. Slots pallet would normally do this.
|
||||
let crowdloan_account = Crowdloan::fund_account_id(para_2);
|
||||
set_winner(para_2, crowdloan_account, true);
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(1), para_2, 49, None), Error::<Test>::BidOrLeaseActive);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(1), para_2, 49, None),
|
||||
Error::<Test>::BidOrLeaseActive
|
||||
);
|
||||
|
||||
// Move past lease period 1, should not be allowed to have further contributions with a crowdloan
|
||||
// that has starting period 1.
|
||||
@@ -1233,7 +1368,10 @@ mod tests {
|
||||
assert_ok!(Crowdloan::create(Origin::signed(1), para_3, 1000, 1, 4, 40, None));
|
||||
run_to_block(40);
|
||||
assert_eq!(TestAuctioneer::lease_period_index(), 2);
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(1), para_3, 49, None), Error::<Test>::ContributionPeriodOver);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(1), para_3, 49, None),
|
||||
Error::<Test>::ContributionPeriodOver
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1249,7 +1387,15 @@ mod tests {
|
||||
assert_ok!(TestAuctioneer::new_auction(5, 0));
|
||||
|
||||
// Set up a crowdloan
|
||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, first_period, last_period, 20, None));
|
||||
assert_ok!(Crowdloan::create(
|
||||
Origin::signed(1),
|
||||
para,
|
||||
1000,
|
||||
first_period,
|
||||
last_period,
|
||||
20,
|
||||
None
|
||||
));
|
||||
|
||||
run_to_block(8);
|
||||
// Can def contribute when auction is running.
|
||||
@@ -1259,7 +1405,10 @@ mod tests {
|
||||
run_to_block(10);
|
||||
// Can't contribute when auction is in the VRF delay period.
|
||||
assert!(TestAuctioneer::auction_status(System::block_number()).is_vrf());
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(2), para, 250, None), Error::<Test>::VrfDelayInProgress);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(2), para, 250, None),
|
||||
Error::<Test>::VrfDelayInProgress
|
||||
);
|
||||
|
||||
run_to_block(15);
|
||||
// Its fine to contribute when no auction is running.
|
||||
@@ -1278,7 +1427,15 @@ mod tests {
|
||||
assert_ok!(TestAuctioneer::new_auction(5, 0));
|
||||
|
||||
// Set up a crowdloan
|
||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, first_period, last_period, 9, None));
|
||||
assert_ok!(Crowdloan::create(
|
||||
Origin::signed(1),
|
||||
para,
|
||||
1000,
|
||||
first_period,
|
||||
last_period,
|
||||
9,
|
||||
None
|
||||
));
|
||||
let bidder = Crowdloan::fund_account_id(para);
|
||||
|
||||
// Fund crowdloan
|
||||
@@ -1292,11 +1449,14 @@ mod tests {
|
||||
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 250, None));
|
||||
run_to_block(10);
|
||||
|
||||
assert_eq!(bids(), vec![
|
||||
BidPlaced { height: 5, amount: 250, bidder, para, first_period, last_period },
|
||||
BidPlaced { height: 6, amount: 450, bidder, para, first_period, last_period },
|
||||
BidPlaced { height: 9, amount: 700, bidder, para, first_period, last_period },
|
||||
]);
|
||||
assert_eq!(
|
||||
bids(),
|
||||
vec![
|
||||
BidPlaced { height: 5, amount: 250, bidder, para, first_period, last_period },
|
||||
BidPlaced { height: 6, amount: 450, bidder, para, first_period, last_period },
|
||||
BidPlaced { height: 9, amount: 700, bidder, para, first_period, last_period },
|
||||
]
|
||||
);
|
||||
|
||||
// Endings count incremented
|
||||
assert_eq!(Crowdloan::endings_count(), 1);
|
||||
@@ -1405,9 +1565,14 @@ mod tests {
|
||||
// Set up a crowdloan ending on 9
|
||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 100000, 1, 1, 9, None));
|
||||
// Make more contributions than our limit
|
||||
for i in 1 ..= RemoveKeysLimit::get() * 2 {
|
||||
for i in 1..=RemoveKeysLimit::get() * 2 {
|
||||
Balances::make_free_balance_be(&i.into(), (1000 * i).into());
|
||||
assert_ok!(Crowdloan::contribute(Origin::signed(i.into()), para, (i * 100).into(), None));
|
||||
assert_ok!(Crowdloan::contribute(
|
||||
Origin::signed(i.into()),
|
||||
para,
|
||||
(i * 100).into(),
|
||||
None
|
||||
));
|
||||
}
|
||||
|
||||
assert_eq!(Balances::free_balance(account_id), 21000);
|
||||
@@ -1427,7 +1592,7 @@ mod tests {
|
||||
// Funds are returned
|
||||
assert_eq!(Balances::free_balance(account_id), 0);
|
||||
// 1 deposit for the crowdloan which hasn't dissolved yet.
|
||||
for i in 1 ..= RemoveKeysLimit::get() * 2 {
|
||||
for i in 1..=RemoveKeysLimit::get() * 2 {
|
||||
assert_eq!(Balances::free_balance(&i.into()), i as u64 * 1000);
|
||||
}
|
||||
});
|
||||
@@ -1469,16 +1634,25 @@ mod tests {
|
||||
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
|
||||
|
||||
// Can't dissolve before it ends
|
||||
assert_noop!(Crowdloan::dissolve(Origin::signed(1), para), Error::<Test>::NotReadyToDissolve);
|
||||
assert_noop!(
|
||||
Crowdloan::dissolve(Origin::signed(1), para),
|
||||
Error::<Test>::NotReadyToDissolve
|
||||
);
|
||||
|
||||
run_to_block(10);
|
||||
set_winner(para, 1, true);
|
||||
// Can't dissolve when it won.
|
||||
assert_noop!(Crowdloan::dissolve(Origin::signed(1), para), Error::<Test>::NotReadyToDissolve);
|
||||
assert_noop!(
|
||||
Crowdloan::dissolve(Origin::signed(1), para),
|
||||
Error::<Test>::NotReadyToDissolve
|
||||
);
|
||||
set_winner(para, 1, false);
|
||||
|
||||
// Can't dissolve while it still has user funds
|
||||
assert_noop!(Crowdloan::dissolve(Origin::signed(1), para), Error::<Test>::NotReadyToDissolve);
|
||||
assert_noop!(
|
||||
Crowdloan::dissolve(Origin::signed(1), para),
|
||||
Error::<Test>::NotReadyToDissolve
|
||||
);
|
||||
|
||||
// All funds are refunded
|
||||
assert_ok!(Crowdloan::refund(Origin::signed(2), para));
|
||||
@@ -1508,7 +1682,10 @@ mod tests {
|
||||
assert_ok!(Balances::reserve(&account_id, 150));
|
||||
|
||||
run_to_block(19);
|
||||
assert_noop!(Crowdloan::withdraw(Origin::signed(2), 2, para), Error::<Test>::BidOrLeaseActive);
|
||||
assert_noop!(
|
||||
Crowdloan::withdraw(Origin::signed(2), 2, para),
|
||||
Error::<Test>::BidOrLeaseActive
|
||||
);
|
||||
|
||||
run_to_block(20);
|
||||
// simulate the unreserving of para's funds, now that the lease expired. this actually
|
||||
@@ -1566,7 +1743,6 @@ mod tests {
|
||||
Crowdloan::create(Origin::signed(1), para_1, 1000, 1, 1, 9, None),
|
||||
Error::<Test>::FundNotEnded,
|
||||
);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1648,16 +1824,13 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking {
|
||||
use super::{*, Pallet as Crowdloan};
|
||||
use super::{Pallet as Crowdloan, *};
|
||||
use frame_support::{assert_ok, traits::OnInitialize};
|
||||
use frame_system::RawOrigin;
|
||||
use frame_support::{
|
||||
assert_ok,
|
||||
traits::OnInitialize,
|
||||
};
|
||||
use sp_runtime::traits::{Bounded, CheckedSub};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
use frame_benchmarking::{benchmarks, whitelisted_caller, account, impl_benchmark_test_suite};
|
||||
use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller};
|
||||
|
||||
fn assert_last_event<T: Config>(generic_event: <T as Config>::Event) {
|
||||
let events = frame_system::Pallet::<T>::events();
|
||||
@@ -1671,7 +1844,8 @@ mod benchmarking {
|
||||
let cap = BalanceOf::<T>::max_value();
|
||||
let lease_period_index = T::Auctioneer::lease_period_index();
|
||||
let first_period = lease_period_index;
|
||||
let last_period = lease_period_index + ((SlotRange::LEASE_PERIODS_PER_SLOT as u32) - 1).into();
|
||||
let last_period =
|
||||
lease_period_index + ((SlotRange::LEASE_PERIODS_PER_SLOT as u32) - 1).into();
|
||||
let para_id = id.into();
|
||||
|
||||
let caller = account("fund_creator", id, 0);
|
||||
@@ -1706,7 +1880,12 @@ mod benchmarking {
|
||||
let payload = (index, &who, BalanceOf::<T>::default(), value);
|
||||
let sig = crypto::create_ed25519_signature(&payload.encode(), pubkey);
|
||||
|
||||
assert_ok!(Crowdloan::<T>::contribute(RawOrigin::Signed(who.clone()).into(), index, value, Some(sig)));
|
||||
assert_ok!(Crowdloan::<T>::contribute(
|
||||
RawOrigin::Signed(who.clone()).into(),
|
||||
index,
|
||||
value,
|
||||
Some(sig)
|
||||
));
|
||||
}
|
||||
|
||||
benchmarks! {
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
//! Code for elections.
|
||||
|
||||
use super::{BlockExecutionWeight, BlockLength, BlockWeights};
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
weights::{DispatchClass, Weight},
|
||||
};
|
||||
use sp_runtime::Perbill;
|
||||
use super::{BlockExecutionWeight, BlockLength, BlockWeights};
|
||||
|
||||
parameter_types! {
|
||||
/// A limit for off-chain phragmen unsigned solution submission.
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
//! Auxiliary `struct`/`enum`s for polkadot runtime.
|
||||
|
||||
use frame_support::traits::{OnUnbalanced, Imbalance, Currency};
|
||||
use crate::NegativeImbalance;
|
||||
use frame_support::traits::{Currency, Imbalance, OnUnbalanced};
|
||||
|
||||
/// Logic for the author to get a portion of fees.
|
||||
pub struct ToAuthor<R>(sp_std::marker::PhantomData<R>);
|
||||
@@ -31,8 +31,14 @@ where
|
||||
fn on_nonzero_unbalanced(amount: NegativeImbalance<R>) {
|
||||
let numeric_amount = amount.peek();
|
||||
let author = <pallet_authorship::Pallet<R>>::author();
|
||||
<pallet_balances::Pallet<R>>::resolve_creating(&<pallet_authorship::Pallet<R>>::author(), amount);
|
||||
<frame_system::Pallet<R>>::deposit_event(pallet_balances::Event::Deposit(author, numeric_amount));
|
||||
<pallet_balances::Pallet<R>>::resolve_creating(
|
||||
&<pallet_authorship::Pallet<R>>::author(),
|
||||
amount,
|
||||
);
|
||||
<frame_system::Pallet<R>>::deposit_event(pallet_balances::Event::Deposit(
|
||||
author,
|
||||
numeric_amount,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +51,7 @@ where
|
||||
<R as frame_system::Config>::AccountId: Into<primitives::v1::AccountId>,
|
||||
<R as frame_system::Config>::Event: From<pallet_balances::Event<R>>,
|
||||
{
|
||||
fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item=NegativeImbalance<R>>) {
|
||||
fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item = NegativeImbalance<R>>) {
|
||||
if let Some(fees) = fees_then_tips.next() {
|
||||
// for fees, 80% to treasury, 20% to author
|
||||
let mut split = fees.ration(80, 20);
|
||||
@@ -60,20 +66,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use frame_support::{parameter_types, traits::FindAuthor, weights::DispatchClass, PalletId};
|
||||
use frame_system::limits;
|
||||
use frame_support::{parameter_types, PalletId, weights::DispatchClass};
|
||||
use frame_support::traits::FindAuthor;
|
||||
use primitives::v1::AccountId;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
Perbill,
|
||||
};
|
||||
use primitives::v1::AccountId;
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
@@ -169,7 +173,8 @@ mod tests {
|
||||
pub struct OneAuthor;
|
||||
impl FindAuthor<AccountId> for OneAuthor {
|
||||
fn find_author<'a, I>(_: I) -> Option<AccountId>
|
||||
where I: 'a,
|
||||
where
|
||||
I: 'a,
|
||||
{
|
||||
Some(Default::default())
|
||||
}
|
||||
@@ -184,7 +189,9 @@ mod tests {
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
// We use default for brevity, but you can configure as desired if needed.
|
||||
pallet_balances::GenesisConfig::<Test>::default().assimilate_storage(&mut t).unwrap();
|
||||
pallet_balances::GenesisConfig::<Test>::default()
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
t.into()
|
||||
}
|
||||
|
||||
|
||||
@@ -16,33 +16,28 @@
|
||||
|
||||
//! Mocking utilities for testing with real pallets.
|
||||
|
||||
use sp_std::sync::Arc;
|
||||
use sp_io::TestExternalities;
|
||||
use sp_core::{H256, crypto::KeyTypeId};
|
||||
use sp_runtime::{
|
||||
traits::{
|
||||
BlakeTwo256, IdentityLookup, One,
|
||||
},
|
||||
use crate::{
|
||||
auctions, crowdloan, paras_registrar,
|
||||
slot_range::SlotRange,
|
||||
slots,
|
||||
traits::{AuctionStatus, Auctioneer, Registrar as RegistrarT},
|
||||
};
|
||||
use sp_keystore::{KeystoreExt, testing::KeyStore};
|
||||
use primitives::v1::{BlockNumber, Header, Id as ParaId, ValidationCode, HeadData, LOWEST_PUBLIC_ID};
|
||||
use frame_support::{
|
||||
parameter_types, assert_ok, assert_noop, PalletId,
|
||||
traits::{Currency, OnInitialize, OnFinalize, KeyOwnerProofSystem, GenesisBuild},
|
||||
};
|
||||
use frame_system::EnsureRoot;
|
||||
use runtime_parachains::{
|
||||
ParaLifecycle, Origin as ParaOrigin,
|
||||
paras, configuration, shared,
|
||||
assert_noop, assert_ok, parameter_types,
|
||||
traits::{Currency, GenesisBuild, KeyOwnerProofSystem, OnFinalize, OnInitialize},
|
||||
PalletId,
|
||||
};
|
||||
use frame_support_test::TestRandomness;
|
||||
use crate::{
|
||||
auctions, crowdloan, slots, paras_registrar,
|
||||
slot_range::SlotRange,
|
||||
traits::{
|
||||
Registrar as RegistrarT, Auctioneer, AuctionStatus,
|
||||
},
|
||||
use frame_system::EnsureRoot;
|
||||
use primitives::v1::{
|
||||
BlockNumber, HeadData, Header, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID,
|
||||
};
|
||||
use runtime_parachains::{configuration, paras, shared, Origin as ParaOrigin, ParaLifecycle};
|
||||
use sp_core::{crypto::KeyTypeId, H256};
|
||||
use sp_io::TestExternalities;
|
||||
use sp_keystore::{testing::KeyStore, KeystoreExt};
|
||||
use sp_runtime::traits::{BlakeTwo256, IdentityLookup, One};
|
||||
use sp_std::sync::Arc;
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
@@ -75,8 +70,7 @@ frame_support::construct_runtime!(
|
||||
}
|
||||
);
|
||||
|
||||
use crate::crowdloan::Error as CrowdloanError;
|
||||
use crate::auctions::Error as AuctionsError;
|
||||
use crate::{auctions::Error as AuctionsError, crowdloan::Error as CrowdloanError};
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u32 = 250;
|
||||
@@ -121,8 +115,10 @@ impl pallet_babe::Config for Test {
|
||||
type ExpectedBlockTime = ExpectedBlockTime;
|
||||
type EpochChangeTrigger = pallet_babe::ExternalTrigger;
|
||||
type KeyOwnerProofSystem = ();
|
||||
type KeyOwnerProof =
|
||||
<Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(KeyTypeId, pallet_babe::AuthorityId)>>::Proof;
|
||||
type KeyOwnerProof = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(
|
||||
KeyTypeId,
|
||||
pallet_babe::AuthorityId,
|
||||
)>>::Proof;
|
||||
type KeyOwnerIdentification = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(
|
||||
KeyTypeId,
|
||||
pallet_babe::AuthorityId,
|
||||
@@ -159,9 +155,9 @@ impl pallet_balances::Config for Test {
|
||||
type ReserveIdentifier = [u8; 8];
|
||||
}
|
||||
|
||||
impl configuration::Config for Test { }
|
||||
impl configuration::Config for Test {}
|
||||
|
||||
impl shared::Config for Test { }
|
||||
impl shared::Config for Test {}
|
||||
|
||||
impl paras::Config for Test {
|
||||
type Origin = Origin;
|
||||
@@ -236,14 +232,15 @@ pub fn new_test_ext() -> TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
GenesisBuild::<Test>::assimilate_storage(
|
||||
&configuration::GenesisConfig {
|
||||
config: configuration::HostConfiguration {
|
||||
max_code_size: 2 * 1024 * 1024, // 2 MB
|
||||
max_head_data_size: 1 * 1024 * 1024, // 1 MB
|
||||
..Default::default()
|
||||
}
|
||||
config: configuration::HostConfiguration {
|
||||
max_code_size: 2 * 1024 * 1024, // 2 MB
|
||||
max_head_data_size: 1 * 1024 * 1024, // 1 MB
|
||||
..Default::default()
|
||||
},
|
||||
&mut t
|
||||
).unwrap();
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
.unwrap();
|
||||
let keystore = KeyStore::new();
|
||||
let mut ext: sp_io::TestExternalities = t.into();
|
||||
ext.register_extension(KeystoreExt(Arc::new(keystore)));
|
||||
@@ -255,9 +252,7 @@ const BLOCKS_PER_SESSION: u32 = 10;
|
||||
|
||||
fn maybe_new_session(n: u32) {
|
||||
if n % BLOCKS_PER_SESSION == 0 {
|
||||
shared::Pallet::<Test>::set_session_index(
|
||||
shared::Pallet::<Test>::session_index() + 1
|
||||
);
|
||||
shared::Pallet::<Test>::set_session_index(shared::Pallet::<Test>::session_index() + 1);
|
||||
Paras::test_on_new_session();
|
||||
}
|
||||
}
|
||||
@@ -343,10 +338,10 @@ fn basic_end_to_end_works() {
|
||||
assert_ok!(Crowdloan::create(
|
||||
Origin::signed(2),
|
||||
ParaId::from(para_2),
|
||||
1_000, // Cap
|
||||
1_000, // Cap
|
||||
lease_period_index_start + 2, // First Slot
|
||||
lease_period_index_start + 3, // Last Slot
|
||||
200, // Block End
|
||||
200, // Block End
|
||||
None,
|
||||
));
|
||||
let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(para_2));
|
||||
@@ -361,10 +356,10 @@ fn basic_end_to_end_works() {
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(10),
|
||||
ParaId::from(para_1),
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
910, // Amount
|
||||
910, // Amount
|
||||
));
|
||||
|
||||
// User 2 will be a contribute to crowdloan for parachain 2
|
||||
@@ -378,10 +373,7 @@ fn basic_end_to_end_works() {
|
||||
crowdloan::Event::<Test>::HandleBidResult(ParaId::from(para_2), Ok(())).into(),
|
||||
);
|
||||
run_to_block(110);
|
||||
assert_eq!(
|
||||
last_event(),
|
||||
auctions::Event::<Test>::AuctionClosed(1).into(),
|
||||
);
|
||||
assert_eq!(last_event(), auctions::Event::<Test>::AuctionClosed(1).into(),);
|
||||
|
||||
// Paras should have won slots
|
||||
assert_eq!(
|
||||
@@ -392,19 +384,33 @@ fn basic_end_to_end_works() {
|
||||
assert_eq!(
|
||||
slots::Leases::<Test>::get(ParaId::from(para_2)),
|
||||
// -- 1 --- 2 --- 3 --- 4 --- 5 ---------------- 6 --------------------------- 7 ----------------
|
||||
vec![None, None, None, None, None, Some((crowdloan_account, 920)), Some((crowdloan_account, 920))],
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some((crowdloan_account, 920)),
|
||||
Some((crowdloan_account, 920))
|
||||
],
|
||||
);
|
||||
|
||||
// Should not be able to contribute to a winning crowdloan
|
||||
Balances::make_free_balance_be(&3, 1_000_000_000);
|
||||
assert_noop!(Crowdloan::contribute(Origin::signed(3), ParaId::from(2001), 10, None), CrowdloanError::<Test>::BidOrLeaseActive);
|
||||
assert_noop!(
|
||||
Crowdloan::contribute(Origin::signed(3), ParaId::from(2001), 10, None),
|
||||
CrowdloanError::<Test>::BidOrLeaseActive
|
||||
);
|
||||
|
||||
// New leases will start on block 400
|
||||
let lease_start_block = 400;
|
||||
run_to_block(lease_start_block);
|
||||
|
||||
// First slot, Para 1 should be transitioning to Parachain
|
||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::UpgradingParathread));
|
||||
assert_eq!(
|
||||
Paras::lifecycle(ParaId::from(para_1)),
|
||||
Some(ParaLifecycle::UpgradingParathread)
|
||||
);
|
||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
||||
|
||||
// Two sessions later, it has upgraded
|
||||
@@ -419,8 +425,14 @@ fn basic_end_to_end_works() {
|
||||
|
||||
// Third slot, Para 2 should be upgrading, and Para 1 is downgrading
|
||||
run_to_block(lease_start_block + 200);
|
||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::DowngradingParachain));
|
||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::UpgradingParathread));
|
||||
assert_eq!(
|
||||
Paras::lifecycle(ParaId::from(para_1)),
|
||||
Some(ParaLifecycle::DowngradingParachain)
|
||||
);
|
||||
assert_eq!(
|
||||
Paras::lifecycle(ParaId::from(para_2)),
|
||||
Some(ParaLifecycle::UpgradingParathread)
|
||||
);
|
||||
|
||||
// Two sessions later, they have transitioned
|
||||
run_to_block(lease_start_block + 220);
|
||||
@@ -435,7 +447,10 @@ fn basic_end_to_end_works() {
|
||||
// Fifth slot, Para 2 is downgrading
|
||||
run_to_block(lease_start_block + 400);
|
||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::DowngradingParachain));
|
||||
assert_eq!(
|
||||
Paras::lifecycle(ParaId::from(para_2)),
|
||||
Some(ParaLifecycle::DowngradingParachain)
|
||||
);
|
||||
|
||||
// Two sessions later, Para 2 is downgraded
|
||||
run_to_block(lease_start_block + 420);
|
||||
@@ -463,12 +478,10 @@ fn basic_errors_fail() {
|
||||
validation_code.clone(),
|
||||
));
|
||||
assert_ok!(Registrar::reserve(Origin::signed(2)));
|
||||
assert_noop!(Registrar::register(
|
||||
Origin::signed(2),
|
||||
para_id,
|
||||
genesis_head,
|
||||
validation_code,
|
||||
), paras_registrar::Error::<Test>::NotOwner);
|
||||
assert_noop!(
|
||||
Registrar::register(Origin::signed(2), para_id, genesis_head, validation_code,),
|
||||
paras_registrar::Error::<Test>::NotOwner
|
||||
);
|
||||
|
||||
// Start an auction
|
||||
let duration = 99u32;
|
||||
@@ -476,15 +489,18 @@ fn basic_errors_fail() {
|
||||
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
|
||||
|
||||
// Cannot create a crowdloan if you do not own the para
|
||||
assert_noop!(Crowdloan::create(
|
||||
Origin::signed(2),
|
||||
para_id,
|
||||
1_000, // Cap
|
||||
lease_period_index_start + 2, // First Slot
|
||||
lease_period_index_start + 3, // Last Slot
|
||||
200, // Block End
|
||||
None,
|
||||
), crowdloan::Error::<Test>::InvalidOrigin);
|
||||
assert_noop!(
|
||||
Crowdloan::create(
|
||||
Origin::signed(2),
|
||||
para_id,
|
||||
1_000, // Cap
|
||||
lease_period_index_start + 2, // First Slot
|
||||
lease_period_index_start + 3, // Last Slot
|
||||
200, // Block End
|
||||
None,
|
||||
),
|
||||
crowdloan::Error::<Test>::InvalidOrigin
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -497,7 +513,7 @@ fn competing_slots() {
|
||||
let para_id = LOWEST_PUBLIC_ID;
|
||||
|
||||
// Create n paras and owners
|
||||
for n in 1 ..= max_bids {
|
||||
for n in 1..=max_bids {
|
||||
Balances::make_free_balance_be(&n, 1_000_000_000);
|
||||
let genesis_head = Registrar::worst_head_data();
|
||||
let validation_code = Registrar::worst_validation_code();
|
||||
@@ -518,22 +534,22 @@ fn competing_slots() {
|
||||
// Paras should be onboarded
|
||||
run_to_block(20); // session 2
|
||||
|
||||
for n in 1 ..= max_bids {
|
||||
for n in 1..=max_bids {
|
||||
// Increment block number
|
||||
run_to_block(System::block_number() + 10);
|
||||
|
||||
Balances::make_free_balance_be(&(n * 10), n * 1_000);
|
||||
|
||||
let (start, end) = match n {
|
||||
1 => (0, 0),
|
||||
2 => (0, 1),
|
||||
3 => (0, 2),
|
||||
4 => (0, 3),
|
||||
5 => (1, 1),
|
||||
6 => (1, 2),
|
||||
7 => (1, 3),
|
||||
8 => (2, 2),
|
||||
9 => (2, 3),
|
||||
1 => (0, 0),
|
||||
2 => (0, 1),
|
||||
3 => (0, 2),
|
||||
4 => (0, 3),
|
||||
5 => (1, 1),
|
||||
6 => (1, 2),
|
||||
7 => (1, 3),
|
||||
8 => (2, 2),
|
||||
9 => (2, 3),
|
||||
10 => (3, 3),
|
||||
_ => panic!("test not meant for this"),
|
||||
};
|
||||
@@ -542,10 +558,10 @@ fn competing_slots() {
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(n * 10),
|
||||
para_id + n - 1,
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + start, // First Slot
|
||||
lease_period_index_start + end, // Last slot
|
||||
n * 900, // Amount
|
||||
lease_period_index_start + end, // Last slot
|
||||
n * 900, // Amount
|
||||
));
|
||||
}
|
||||
|
||||
@@ -582,7 +598,7 @@ fn competing_bids() {
|
||||
|
||||
let start_para = LOWEST_PUBLIC_ID - 1;
|
||||
// Create 3 paras and owners
|
||||
for n in 1 ..= 3 {
|
||||
for n in 1..=3 {
|
||||
Balances::make_free_balance_be(&n, 1_000_000_000);
|
||||
let genesis_head = Registrar::worst_head_data();
|
||||
let validation_code = Registrar::worst_validation_code();
|
||||
@@ -604,20 +620,20 @@ fn competing_bids() {
|
||||
let lease_period_index_start = 4u32;
|
||||
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
|
||||
|
||||
for n in 1 ..= 3 {
|
||||
for n in 1..=3 {
|
||||
// Create a crowdloan for each para
|
||||
assert_ok!(Crowdloan::create(
|
||||
Origin::signed(n),
|
||||
ParaId::from(start_para + n),
|
||||
100_000, // Cap
|
||||
100_000, // Cap
|
||||
lease_period_index_start + 2, // First Slot
|
||||
lease_period_index_start + 3, // Last Slot
|
||||
200, // Block End,
|
||||
200, // Block End,
|
||||
None,
|
||||
));
|
||||
}
|
||||
|
||||
for n in 1 ..= 9 {
|
||||
for n in 1..=9 {
|
||||
// Increment block number
|
||||
run_to_block(starting_block + n * 10);
|
||||
|
||||
@@ -630,10 +646,10 @@ fn competing_bids() {
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(n * 10),
|
||||
ParaId::from(para),
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
n * 900, // Amount
|
||||
n * 900, // Amount
|
||||
));
|
||||
} else {
|
||||
// User 20 will be a contribute to crowdloan for parachain 2
|
||||
@@ -654,7 +670,15 @@ fn competing_bids() {
|
||||
assert_eq!(
|
||||
slots::Leases::<Test>::get(ParaId::from(2000)),
|
||||
// -- 1 --- 2 --- 3 --- 4 --- 5 ------------- 6 ------------------------ 7 -------------
|
||||
vec![None, None, None, None, None, Some((crowdloan_2, 1812)), Some((crowdloan_2, 1812))],
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some((crowdloan_2, 1812)),
|
||||
Some((crowdloan_2, 1812))
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
slots::Leases::<Test>::get(ParaId::from(2002)),
|
||||
@@ -669,7 +693,7 @@ fn basic_swap_works() {
|
||||
// This test will test a swap between a parachain and parathread works successfully.
|
||||
new_test_ext().execute_with(|| {
|
||||
assert!(System::block_number().is_one()); // So events are emitted
|
||||
// User 1 and 2 will own paras
|
||||
// User 1 and 2 will own paras
|
||||
Balances::make_free_balance_be(&1, 1_000_000_000);
|
||||
Balances::make_free_balance_be(&2, 1_000_000_000);
|
||||
// First register 2 parathreads with different data
|
||||
@@ -706,17 +730,17 @@ fn basic_swap_works() {
|
||||
assert_ok!(Crowdloan::create(
|
||||
Origin::signed(1),
|
||||
ParaId::from(2000),
|
||||
1_000_000, // Cap
|
||||
1_000_000, // Cap
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 3, // Last Slot
|
||||
200, // Block End
|
||||
200, // Block End
|
||||
None,
|
||||
));
|
||||
let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(2000));
|
||||
|
||||
// Bunch of contributions
|
||||
let mut total = 0;
|
||||
for i in 10 .. 20 {
|
||||
for i in 10..20 {
|
||||
Balances::make_free_balance_be(&i, 1_000_000_000);
|
||||
assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(2000), 900 - i, None));
|
||||
total += 900 - i;
|
||||
@@ -750,8 +774,16 @@ fn basic_swap_works() {
|
||||
assert_eq!(Paras::lifecycle(ParaId::from(2001)), Some(ParaLifecycle::Parathread));
|
||||
|
||||
// Initiate a swap
|
||||
assert_ok!(Registrar::swap(para_origin(2000).into(), ParaId::from(2000), ParaId::from(2001)));
|
||||
assert_ok!(Registrar::swap(para_origin(2001).into(), ParaId::from(2001), ParaId::from(2000)));
|
||||
assert_ok!(Registrar::swap(
|
||||
para_origin(2000).into(),
|
||||
ParaId::from(2000),
|
||||
ParaId::from(2001)
|
||||
));
|
||||
assert_ok!(Registrar::swap(
|
||||
para_origin(2001).into(),
|
||||
ParaId::from(2001),
|
||||
ParaId::from(2000)
|
||||
));
|
||||
|
||||
assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::DowngradingParachain));
|
||||
assert_eq!(Paras::lifecycle(ParaId::from(2001)), Some(ParaLifecycle::UpgradingParathread));
|
||||
@@ -774,15 +806,21 @@ fn basic_swap_works() {
|
||||
assert!(!Slots::lease(ParaId::from(2001)).is_empty());
|
||||
|
||||
// Cant dissolve
|
||||
assert_noop!(Crowdloan::dissolve(Origin::signed(1), ParaId::from(2000)), CrowdloanError::<Test>::InvalidParaId);
|
||||
assert_noop!(Crowdloan::dissolve(Origin::signed(2), ParaId::from(2001)), CrowdloanError::<Test>::NotReadyToDissolve);
|
||||
assert_noop!(
|
||||
Crowdloan::dissolve(Origin::signed(1), ParaId::from(2000)),
|
||||
CrowdloanError::<Test>::InvalidParaId
|
||||
);
|
||||
assert_noop!(
|
||||
Crowdloan::dissolve(Origin::signed(2), ParaId::from(2001)),
|
||||
CrowdloanError::<Test>::NotReadyToDissolve
|
||||
);
|
||||
|
||||
// Go way in the future when the para is offboarded
|
||||
run_to_block(lease_start_block + 1000);
|
||||
|
||||
// Withdraw of contributions works
|
||||
assert_eq!(Balances::free_balance(&crowdloan_account), total);
|
||||
for i in 10 .. 20 {
|
||||
for i in 10..20 {
|
||||
assert_ok!(Crowdloan::withdraw(Origin::signed(i), i, ParaId::from(2001)));
|
||||
}
|
||||
assert_eq!(Balances::free_balance(&crowdloan_account), 0);
|
||||
@@ -802,7 +840,7 @@ fn basic_swap_works() {
|
||||
fn crowdloan_ending_period_bid() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert!(System::block_number().is_one()); // So events are emitted
|
||||
// User 1 and 2 will own paras
|
||||
// User 1 and 2 will own paras
|
||||
Balances::make_free_balance_be(&1, 1_000_000_000);
|
||||
Balances::make_free_balance_be(&2, 1_000_000_000);
|
||||
// First register 2 parathreads
|
||||
@@ -839,17 +877,17 @@ fn crowdloan_ending_period_bid() {
|
||||
assert_ok!(Crowdloan::create(
|
||||
Origin::signed(1),
|
||||
ParaId::from(2000),
|
||||
1_000_000, // Cap
|
||||
1_000_000, // Cap
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 3, // Last Slot
|
||||
200, // Block End
|
||||
200, // Block End
|
||||
None,
|
||||
));
|
||||
let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(2000));
|
||||
|
||||
// Bunch of contributions
|
||||
let mut total = 0;
|
||||
for i in 10 .. 20 {
|
||||
for i in 10..20 {
|
||||
Balances::make_free_balance_be(&i, 1_000_000_000);
|
||||
assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(2000), 900 - i, None));
|
||||
total += 900 - i;
|
||||
@@ -862,10 +900,10 @@ fn crowdloan_ending_period_bid() {
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(2),
|
||||
ParaId::from(2001),
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
900, // Amount
|
||||
900, // Amount
|
||||
));
|
||||
|
||||
// Go to beginning of ending period
|
||||
@@ -874,7 +912,8 @@ fn crowdloan_ending_period_bid() {
|
||||
assert_eq!(Auctions::auction_status(100), AuctionStatus::<u32>::EndingPeriod(0, 0));
|
||||
let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
|
||||
winning[SlotRange::ZeroOne as u8 as usize] = Some((2, ParaId::from(2001), 900));
|
||||
winning[SlotRange::ZeroThree as u8 as usize] = Some((crowdloan_account, ParaId::from(2000), total));
|
||||
winning[SlotRange::ZeroThree as u8 as usize] =
|
||||
Some((crowdloan_account, ParaId::from(2000), total));
|
||||
|
||||
assert_eq!(Auctions::winning(0), Some(winning));
|
||||
|
||||
@@ -887,7 +926,8 @@ fn crowdloan_ending_period_bid() {
|
||||
run_to_block(102);
|
||||
let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
|
||||
winning[SlotRange::ZeroOne as u8 as usize] = Some((2, ParaId::from(2001), 900));
|
||||
winning[SlotRange::ZeroThree as u8 as usize] = Some((crowdloan_account, ParaId::from(2000), total + 900));
|
||||
winning[SlotRange::ZeroThree as u8 as usize] =
|
||||
Some((crowdloan_account, ParaId::from(2000), total + 900));
|
||||
assert_eq!(Auctions::winning(2), Some(winning));
|
||||
})
|
||||
}
|
||||
@@ -904,14 +944,17 @@ fn auction_bid_requires_registered_para() {
|
||||
|
||||
// Can't bid with non-registered paras
|
||||
Balances::make_free_balance_be(&1, 1_000_000_000);
|
||||
assert_noop!(Auctions::bid(
|
||||
Origin::signed(1),
|
||||
ParaId::from(2000),
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
900, // Amount
|
||||
), AuctionsError::<Test>::ParaNotRegistered);
|
||||
assert_noop!(
|
||||
Auctions::bid(
|
||||
Origin::signed(1),
|
||||
ParaId::from(2000),
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
900, // Amount
|
||||
),
|
||||
AuctionsError::<Test>::ParaNotRegistered
|
||||
);
|
||||
|
||||
// Now we register the para
|
||||
assert_ok!(Registrar::reserve(Origin::signed(1)));
|
||||
@@ -923,14 +966,17 @@ fn auction_bid_requires_registered_para() {
|
||||
));
|
||||
|
||||
// Still can't bid until it is fully onboarded
|
||||
assert_noop!(Auctions::bid(
|
||||
Origin::signed(1),
|
||||
ParaId::from(2000),
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
900, // Amount
|
||||
), AuctionsError::<Test>::ParaNotRegistered);
|
||||
assert_noop!(
|
||||
Auctions::bid(
|
||||
Origin::signed(1),
|
||||
ParaId::from(2000),
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
900, // Amount
|
||||
),
|
||||
AuctionsError::<Test>::ParaNotRegistered
|
||||
);
|
||||
|
||||
// Onboarded on Session 2
|
||||
run_to_session(2);
|
||||
@@ -940,10 +986,10 @@ fn auction_bid_requires_registered_para() {
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(1),
|
||||
ParaId::from(2000),
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
900, // Amount
|
||||
900, // Amount
|
||||
));
|
||||
});
|
||||
}
|
||||
@@ -986,29 +1032,29 @@ fn gap_bids_work() {
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(10),
|
||||
ParaId::from(2000),
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 0, // Last slot
|
||||
100, // Amount
|
||||
100, // Amount
|
||||
));
|
||||
// Slot 4 for 400 from 10
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(10),
|
||||
ParaId::from(2000),
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 3, // First Slot
|
||||
lease_period_index_start + 3, // Last slot
|
||||
400, // Amount
|
||||
400, // Amount
|
||||
));
|
||||
|
||||
// A bid for another para is counted separately.
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(10),
|
||||
ParaId::from(2001),
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 1, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
555, // Amount
|
||||
555, // Amount
|
||||
));
|
||||
assert_eq!(Balances::reserved_balance(&10), 400 + 555);
|
||||
|
||||
@@ -1016,19 +1062,19 @@ fn gap_bids_work() {
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(20),
|
||||
ParaId::from(2000),
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 1, // First Slot
|
||||
lease_period_index_start + 1, // Last slot
|
||||
800, // Amount
|
||||
800, // Amount
|
||||
));
|
||||
// Slot 3 for 200 from 20
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(20),
|
||||
ParaId::from(2000),
|
||||
1, // Auction Index
|
||||
1, // Auction Index
|
||||
lease_period_index_start + 2, // First Slot
|
||||
lease_period_index_start + 2, // Last slot
|
||||
200, // Amount
|
||||
200, // Amount
|
||||
));
|
||||
|
||||
// Finish the auction
|
||||
@@ -1038,7 +1084,15 @@ fn gap_bids_work() {
|
||||
assert_eq!(
|
||||
slots::Leases::<Test>::get(ParaId::from(2000)),
|
||||
// -- 1 --- 2 --- 3 ---------- 4 -------------- 5 -------------- 6 -------------- 7 -------
|
||||
vec![None, None, None, Some((10, 100)), Some((20, 800)), Some((20, 200)), Some((10, 400))],
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some((10, 100)),
|
||||
Some((20, 800)),
|
||||
Some((20, 200)),
|
||||
Some((10, 400))
|
||||
],
|
||||
);
|
||||
// Appropriate amount is reserved (largest of the values)
|
||||
assert_eq!(Balances::reserved_balance(&10), 400);
|
||||
@@ -1125,17 +1179,17 @@ fn cant_bid_on_existing_lease_periods() {
|
||||
assert_ok!(Crowdloan::create(
|
||||
Origin::signed(1),
|
||||
ParaId::from(2000),
|
||||
1_000_000, // Cap
|
||||
1_000_000, // Cap
|
||||
lease_period_index_start + 0, // First Slot
|
||||
lease_period_index_start + 1, // Last Slot
|
||||
400, // Long block end
|
||||
400, // Long block end
|
||||
None,
|
||||
));
|
||||
let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(2000));
|
||||
|
||||
// Bunch of contributions
|
||||
let mut total = 0;
|
||||
for i in 10 .. 20 {
|
||||
for i in 10..20 {
|
||||
Balances::make_free_balance_be(&i, 1_000_000_000);
|
||||
assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(2000), 900 - i, None));
|
||||
total += 900 - i;
|
||||
@@ -1150,7 +1204,13 @@ fn cant_bid_on_existing_lease_periods() {
|
||||
assert_eq!(
|
||||
slots::Leases::<Test>::get(ParaId::from(2000)),
|
||||
// -- 1 --- 2 --- 3 ------------- 4 ------------------------ 5 -------------
|
||||
vec![None, None, None, Some((crowdloan_account, 8855)), Some((crowdloan_account, 8855))],
|
||||
vec![
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some((crowdloan_account, 8855)),
|
||||
Some((crowdloan_account, 8855))
|
||||
],
|
||||
);
|
||||
|
||||
// Let's start another auction for the same range
|
||||
@@ -1175,7 +1235,8 @@ fn cant_bid_on_existing_lease_periods() {
|
||||
lease_period_index_start + 0,
|
||||
lease_period_index_start + 1,
|
||||
100,
|
||||
), AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
),
|
||||
AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
);
|
||||
|
||||
assert_noop!(
|
||||
@@ -1186,7 +1247,8 @@ fn cant_bid_on_existing_lease_periods() {
|
||||
lease_period_index_start + 1,
|
||||
lease_period_index_start + 2,
|
||||
100,
|
||||
), AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
),
|
||||
AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
);
|
||||
|
||||
assert_noop!(
|
||||
@@ -1197,7 +1259,8 @@ fn cant_bid_on_existing_lease_periods() {
|
||||
lease_period_index_start - 1,
|
||||
lease_period_index_start + 0,
|
||||
100,
|
||||
), AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
),
|
||||
AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
);
|
||||
|
||||
assert_noop!(
|
||||
@@ -1208,7 +1271,8 @@ fn cant_bid_on_existing_lease_periods() {
|
||||
lease_period_index_start + 0,
|
||||
lease_period_index_start + 0,
|
||||
100,
|
||||
), AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
),
|
||||
AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
);
|
||||
|
||||
assert_noop!(
|
||||
@@ -1219,7 +1283,8 @@ fn cant_bid_on_existing_lease_periods() {
|
||||
lease_period_index_start + 1,
|
||||
lease_period_index_start + 1,
|
||||
100,
|
||||
), AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
),
|
||||
AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
);
|
||||
|
||||
assert_noop!(
|
||||
@@ -1230,19 +1295,18 @@ fn cant_bid_on_existing_lease_periods() {
|
||||
lease_period_index_start - 1,
|
||||
lease_period_index_start + 5,
|
||||
100,
|
||||
), AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
),
|
||||
AuctionsError::<Test>::AlreadyLeasedOut,
|
||||
);
|
||||
|
||||
// Will work when not overlapping
|
||||
assert_ok!(
|
||||
Auctions::bid(
|
||||
Origin::signed(crowdloan_account),
|
||||
ParaId::from(2000),
|
||||
2,
|
||||
lease_period_index_start + 2,
|
||||
lease_period_index_start + 3,
|
||||
100,
|
||||
)
|
||||
);
|
||||
assert_ok!(Auctions::bid(
|
||||
Origin::signed(crowdloan_account),
|
||||
ParaId::from(2000),
|
||||
2,
|
||||
lease_period_index_start + 2,
|
||||
lease_period_index_start + 3,
|
||||
100,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,47 +18,52 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub mod claims;
|
||||
pub mod slots;
|
||||
pub mod auctions;
|
||||
pub mod claims;
|
||||
pub mod crowdloan;
|
||||
pub mod purchase;
|
||||
pub mod elections;
|
||||
pub mod impls;
|
||||
pub mod paras_sudo_wrapper;
|
||||
pub mod paras_registrar;
|
||||
pub mod paras_sudo_wrapper;
|
||||
pub mod purchase;
|
||||
pub mod slot_range;
|
||||
pub mod slots;
|
||||
pub mod traits;
|
||||
pub mod xcm_sender;
|
||||
pub mod elections;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod integration_tests;
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
|
||||
use primitives::v1::{AssignmentId, BlockNumber, ValidatorId};
|
||||
use sp_runtime::{Perquintill, Perbill, FixedPointNumber};
|
||||
use frame_system::limits;
|
||||
use frame_support::{
|
||||
parameter_types, traits::{Currency, OneSessionHandler},
|
||||
weights::{Weight, constants::WEIGHT_PER_SECOND, DispatchClass},
|
||||
pub use frame_support::weights::constants::{
|
||||
BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight,
|
||||
};
|
||||
use pallet_transaction_payment::{TargetedFeeAdjustment, Multiplier};
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
traits::{Currency, OneSessionHandler},
|
||||
weights::{constants::WEIGHT_PER_SECOND, DispatchClass, Weight},
|
||||
};
|
||||
use frame_system::limits;
|
||||
use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment};
|
||||
use primitives::v1::{AssignmentId, BlockNumber, ValidatorId};
|
||||
use sp_runtime::{FixedPointNumber, Perbill, Perquintill};
|
||||
use static_assertions::const_assert;
|
||||
pub use frame_support::weights::constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
|
||||
|
||||
pub use elections::{OffchainSolutionLengthLimit, OffchainSolutionWeightLimit};
|
||||
pub use pallet_balances::Call as BalancesCall;
|
||||
#[cfg(feature = "std")]
|
||||
pub use pallet_staking::StakerStatus;
|
||||
pub use pallet_timestamp::Call as TimestampCall;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub use sp_runtime::BuildStorage;
|
||||
pub use pallet_timestamp::Call as TimestampCall;
|
||||
pub use pallet_balances::Call as BalancesCall;
|
||||
pub use elections::{OffchainSolutionLengthLimit, OffchainSolutionWeightLimit};
|
||||
|
||||
/// Implementations of some helper traits passed into runtime modules as associated types.
|
||||
pub use impls::ToAuthor;
|
||||
|
||||
pub type NegativeImbalance<T> = <pallet_balances::Pallet<T> as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
|
||||
pub type NegativeImbalance<T> = <pallet_balances::Pallet<T> as Currency<
|
||||
<T as frame_system::Config>::AccountId,
|
||||
>>::NegativeImbalance;
|
||||
|
||||
/// We assume that an on-initialize consumes 1% of the weight on average, hence a single extrinsic
|
||||
/// will not be allowed to consume more than `AvailableBlockRatio - 1%`.
|
||||
@@ -110,12 +115,8 @@ parameter_types! {
|
||||
|
||||
/// Parameterized slow adjusting fee updated based on
|
||||
/// https://w3f-research.readthedocs.io/en/latest/polkadot/Token%20Economics.html#-2.-slow-adjusting-mechanism
|
||||
pub type SlowAdjustingFeeUpdate<R> = TargetedFeeAdjustment<
|
||||
R,
|
||||
TargetBlockFullness,
|
||||
AdjustmentVariable,
|
||||
MinimumMultiplier
|
||||
>;
|
||||
pub type SlowAdjustingFeeUpdate<R> =
|
||||
TargetedFeeAdjustment<R, TargetBlockFullness, AdjustmentVariable, MinimumMultiplier>;
|
||||
|
||||
/// The type used for currency conversion.
|
||||
///
|
||||
@@ -130,25 +131,26 @@ impl<T> sp_runtime::BoundToRuntimeAppPublic for ParachainSessionKeyPlaceholder<T
|
||||
type Public = ValidatorId;
|
||||
}
|
||||
|
||||
impl<T: pallet_session::Config> OneSessionHandler<T::AccountId> for ParachainSessionKeyPlaceholder<T>
|
||||
impl<T: pallet_session::Config> OneSessionHandler<T::AccountId>
|
||||
for ParachainSessionKeyPlaceholder<T>
|
||||
{
|
||||
type Key = ValidatorId;
|
||||
|
||||
fn on_genesis_session<'a, I: 'a>(_validators: I) where
|
||||
fn on_genesis_session<'a, I: 'a>(_validators: I)
|
||||
where
|
||||
I: Iterator<Item = (&'a T::AccountId, ValidatorId)>,
|
||||
T::AccountId: 'a
|
||||
T::AccountId: 'a,
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
fn on_new_session<'a, I: 'a>(_changed: bool, _v: I, _q: I) where
|
||||
fn on_new_session<'a, I: 'a>(_changed: bool, _v: I, _q: I)
|
||||
where
|
||||
I: Iterator<Item = (&'a T::AccountId, ValidatorId)>,
|
||||
T::AccountId: 'a
|
||||
T::AccountId: 'a,
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
fn on_disabled(_: usize) { }
|
||||
fn on_disabled(_: usize) {}
|
||||
}
|
||||
|
||||
/// A placeholder since there is currently no provided session key handler for parachain validator
|
||||
@@ -158,25 +160,26 @@ impl<T> sp_runtime::BoundToRuntimeAppPublic for AssignmentSessionKeyPlaceholder<
|
||||
type Public = AssignmentId;
|
||||
}
|
||||
|
||||
impl<T: pallet_session::Config> OneSessionHandler<T::AccountId> for AssignmentSessionKeyPlaceholder<T>
|
||||
impl<T: pallet_session::Config> OneSessionHandler<T::AccountId>
|
||||
for AssignmentSessionKeyPlaceholder<T>
|
||||
{
|
||||
type Key = AssignmentId;
|
||||
|
||||
fn on_genesis_session<'a, I: 'a>(_validators: I) where
|
||||
fn on_genesis_session<'a, I: 'a>(_validators: I)
|
||||
where
|
||||
I: Iterator<Item = (&'a T::AccountId, AssignmentId)>,
|
||||
T::AccountId: 'a
|
||||
T::AccountId: 'a,
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
fn on_new_session<'a, I: 'a>(_changed: bool, _v: I, _q: I) where
|
||||
fn on_new_session<'a, I: 'a>(_changed: bool, _v: I, _q: I)
|
||||
where
|
||||
I: Iterator<Item = (&'a T::AccountId, AssignmentId)>,
|
||||
T::AccountId: 'a
|
||||
T::AccountId: 'a,
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
fn on_disabled(_: usize) { }
|
||||
fn on_disabled(_: usize) {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -186,7 +189,7 @@ mod multiplier_tests {
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, IdentityLookup, Convert, One},
|
||||
traits::{BlakeTwo256, Convert, IdentityLookup, One},
|
||||
Perbill,
|
||||
};
|
||||
|
||||
@@ -238,9 +241,14 @@ mod multiplier_tests {
|
||||
type OnSetCode = ();
|
||||
}
|
||||
|
||||
fn run_with_system_weight<F>(w: Weight, mut assertions: F) where F: FnMut() -> () {
|
||||
let mut t: sp_io::TestExternalities =
|
||||
frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap().into();
|
||||
fn run_with_system_weight<F>(w: Weight, mut assertions: F)
|
||||
where
|
||||
F: FnMut() -> (),
|
||||
{
|
||||
let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::default()
|
||||
.build_storage::<Runtime>()
|
||||
.unwrap()
|
||||
.into();
|
||||
t.execute_with(|| {
|
||||
System::set_block_consumed_resources(w, 0);
|
||||
assertions()
|
||||
@@ -266,9 +274,9 @@ mod multiplier_tests {
|
||||
// assume the multiplier is initially set to its minimum. We update it with values twice the
|
||||
//target (target is 25%, thus 50%) and we see at which point it reaches 1.
|
||||
let mut multiplier = MinimumMultiplier::get();
|
||||
let block_weight = TargetBlockFullness::get()
|
||||
* BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap()
|
||||
* 2;
|
||||
let block_weight = TargetBlockFullness::get() *
|
||||
BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap() *
|
||||
2;
|
||||
let mut blocks = 0;
|
||||
while multiplier <= Multiplier::one() {
|
||||
run_with_system_weight(block_weight, || {
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
//! Mocking utilities for testing.
|
||||
|
||||
use std::{cell::RefCell, collections::HashMap};
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use sp_runtime::traits::SaturatedConversion;
|
||||
use frame_support::dispatch::{DispatchError, DispatchResult};
|
||||
use primitives::v1::{HeadData, ValidationCode, Id as ParaId};
|
||||
use crate::traits::Registrar;
|
||||
use frame_support::dispatch::{DispatchError, DispatchResult};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use primitives::v1::{HeadData, Id as ParaId, ValidationCode};
|
||||
use sp_runtime::traits::SaturatedConversion;
|
||||
use std::{cell::RefCell, collections::HashMap};
|
||||
|
||||
thread_local! {
|
||||
static OPERATIONS: RefCell<Vec<(ParaId, u32, bool)>> = RefCell::new(Vec::new());
|
||||
@@ -130,9 +130,13 @@ impl<T: frame_system::Config> Registrar for TestRegistrar<T> {
|
||||
},
|
||||
}
|
||||
})?;
|
||||
OPERATIONS.with(|x| x.borrow_mut().push(
|
||||
(id, frame_system::Pallet::<T>::block_number().saturated_into(), true)
|
||||
));
|
||||
OPERATIONS.with(|x| {
|
||||
x.borrow_mut().push((
|
||||
id,
|
||||
frame_system::Pallet::<T>::block_number().saturated_into(),
|
||||
true,
|
||||
))
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
fn make_parathread(id: ParaId) -> DispatchResult {
|
||||
@@ -149,16 +153,21 @@ impl<T: frame_system::Config> Registrar for TestRegistrar<T> {
|
||||
PARATHREADS.with(|x| {
|
||||
let mut parathreads = x.borrow_mut();
|
||||
match parathreads.binary_search(&id) {
|
||||
Ok(_) => Err(DispatchError::Other("already parathread, so cannot `make_parathread`")),
|
||||
Ok(_) =>
|
||||
Err(DispatchError::Other("already parathread, so cannot `make_parathread`")),
|
||||
Err(i) => {
|
||||
parathreads.insert(i, id);
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
})?;
|
||||
OPERATIONS.with(|x| x.borrow_mut().push(
|
||||
(id, frame_system::Pallet::<T>::block_number().saturated_into(), false)
|
||||
));
|
||||
OPERATIONS.with(|x| {
|
||||
x.borrow_mut().push((
|
||||
id,
|
||||
frame_system::Pallet::<T>::block_number().saturated_into(),
|
||||
false,
|
||||
))
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -179,7 +188,8 @@ impl<T: frame_system::Config> Registrar for TestRegistrar<T> {
|
||||
|
||||
impl<T: frame_system::Config> TestRegistrar<T> {
|
||||
pub fn operations() -> Vec<(ParaId, T::BlockNumber, bool)> {
|
||||
OPERATIONS.with(|x| x.borrow().iter().map(|(p, b, c)| (*p, (*b).into(), *c)).collect::<Vec<_>>())
|
||||
OPERATIONS
|
||||
.with(|x| x.borrow().iter().map(|(p, b, c)| (*p, (*b).into(), *c)).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
@@ -17,31 +17,28 @@
|
||||
//! Pallet to handle parathread/parachain registration and related fund management.
|
||||
//! In essence this is a simple wrapper around `paras`.
|
||||
|
||||
use sp_std::{prelude::*, result};
|
||||
use frame_support::{
|
||||
ensure,
|
||||
dispatch::DispatchResult,
|
||||
traits::{Get, Currency, ReservableCurrency},
|
||||
ensure,
|
||||
pallet_prelude::Weight,
|
||||
traits::{Currency, Get, ReservableCurrency},
|
||||
};
|
||||
use frame_system::{self, ensure_root, ensure_signed};
|
||||
use primitives::v1::{
|
||||
Id as ParaId, ValidationCode, HeadData, LOWEST_PUBLIC_ID,
|
||||
};
|
||||
use primitives::v1::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID};
|
||||
use runtime_parachains::{
|
||||
paras::{
|
||||
self,
|
||||
ParaGenesisArgs,
|
||||
},
|
||||
configuration,
|
||||
ensure_parachain,
|
||||
configuration, ensure_parachain,
|
||||
paras::{self, ParaGenesisArgs},
|
||||
Origin, ParaLifecycle,
|
||||
};
|
||||
use sp_std::{prelude::*, result};
|
||||
|
||||
use crate::traits::{Registrar, OnSwap};
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use sp_runtime::{RuntimeDebug, traits::{Saturating, CheckedSub}};
|
||||
use crate::traits::{OnSwap, Registrar};
|
||||
pub use pallet::*;
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use sp_runtime::{
|
||||
traits::{CheckedSub, Saturating},
|
||||
RuntimeDebug,
|
||||
};
|
||||
|
||||
#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug)]
|
||||
pub struct ParaInfo<Account, Balance> {
|
||||
@@ -66,18 +63,28 @@ pub trait WeightInfo {
|
||||
|
||||
pub struct TestWeightInfo;
|
||||
impl WeightInfo for TestWeightInfo {
|
||||
fn reserve() -> Weight { 0 }
|
||||
fn register() -> Weight { 0 }
|
||||
fn force_register() -> Weight { 0 }
|
||||
fn deregister() -> Weight { 0 }
|
||||
fn swap() -> Weight { 0 }
|
||||
fn reserve() -> Weight {
|
||||
0
|
||||
}
|
||||
fn register() -> Weight {
|
||||
0
|
||||
}
|
||||
fn force_register() -> Weight {
|
||||
0
|
||||
}
|
||||
fn deregister() -> Weight {
|
||||
0
|
||||
}
|
||||
fn swap() -> Weight {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use super::*;
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
@@ -161,7 +168,8 @@ pub mod pallet {
|
||||
/// The given account ID is responsible for registering the code and initial head data, but may only do
|
||||
/// so if it isn't yet registered. (After that, it's up to governance to do so.)
|
||||
#[pallet::storage]
|
||||
pub type Paras<T: Config> = StorageMap<_, Twox64Concat, ParaId, ParaInfo<T::AccountId, BalanceOf<T>>>;
|
||||
pub type Paras<T: Config> =
|
||||
StorageMap<_, Twox64Concat, ParaId, ParaInfo<T::AccountId, BalanceOf<T>>>;
|
||||
|
||||
/// The next free `ParaId`.
|
||||
#[pallet::storage]
|
||||
@@ -363,8 +371,12 @@ impl<T: Config> Registrar for Pallet<T> {
|
||||
// Upgrade a registered parathread into a parachain.
|
||||
fn make_parachain(id: ParaId) -> DispatchResult {
|
||||
// Para backend should think this is a parathread...
|
||||
ensure!(paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parathread), Error::<T>::NotParathread);
|
||||
runtime_parachains::schedule_parathread_upgrade::<T>(id).map_err(|_| Error::<T>::CannotUpgrade)?;
|
||||
ensure!(
|
||||
paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parathread),
|
||||
Error::<T>::NotParathread
|
||||
);
|
||||
runtime_parachains::schedule_parathread_upgrade::<T>(id)
|
||||
.map_err(|_| Error::<T>::CannotUpgrade)?;
|
||||
// Once a para has upgraded to a parachain, it can no longer be managed by the owner.
|
||||
// Intentionally, the flag stays with the para even after downgrade.
|
||||
Self::apply_lock(id);
|
||||
@@ -374,8 +386,12 @@ impl<T: Config> Registrar for Pallet<T> {
|
||||
// Downgrade a registered para into a parathread.
|
||||
fn make_parathread(id: ParaId) -> DispatchResult {
|
||||
// Para backend should think this is a parachain...
|
||||
ensure!(paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parachain), Error::<T>::NotParachain);
|
||||
runtime_parachains::schedule_parachain_downgrade::<T>(id).map_err(|_| Error::<T>::CannotDowngrade)?;
|
||||
ensure!(
|
||||
paras::Pallet::<T>::lifecycle(id) == Some(ParaLifecycle::Parachain),
|
||||
Error::<T>::NotParachain
|
||||
);
|
||||
runtime_parachains::schedule_parachain_downgrade::<T>(id)
|
||||
.map_err(|_| Error::<T>::CannotDowngrade)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -397,9 +413,7 @@ impl<T: Config> Registrar for Pallet<T> {
|
||||
#[cfg(any(feature = "runtime-benchmarks", test))]
|
||||
fn execute_pending_transitions() {
|
||||
use runtime_parachains::shared;
|
||||
shared::Pallet::<T>::set_session_index(
|
||||
shared::Pallet::<T>::scheduled_session()
|
||||
);
|
||||
shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
|
||||
paras::Pallet::<T>::test_on_new_session();
|
||||
}
|
||||
}
|
||||
@@ -407,23 +421,28 @@ impl<T: Config> Registrar for Pallet<T> {
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Ensure the origin is one of Root, the `para` owner, or the `para` itself.
|
||||
/// If the origin is the `para` owner, the `para` must be unlocked.
|
||||
fn ensure_root_para_or_owner(origin: <T as frame_system::Config>::Origin, id: ParaId) -> DispatchResult {
|
||||
ensure_signed(origin.clone()).map_err(|e| e.into())
|
||||
.and_then(|who| -> DispatchResult {
|
||||
let para_info = Paras::<T>::get(id).ok_or(Error::<T>::NotRegistered)?;
|
||||
ensure!(!para_info.locked, Error::<T>::ParaLocked);
|
||||
ensure!(para_info.manager == who, Error::<T>::NotOwner);
|
||||
Ok(())
|
||||
})
|
||||
.or_else(|_| -> DispatchResult {
|
||||
// Else check if para origin...
|
||||
let caller_id = ensure_parachain(<T as Config>::Origin::from(origin.clone()))?;
|
||||
ensure!(caller_id == id, Error::<T>::NotOwner);
|
||||
Ok(())
|
||||
}).or_else(|_| -> DispatchResult {
|
||||
// Check if root...
|
||||
ensure_root(origin.clone()).map_err(|e| e.into())
|
||||
})
|
||||
fn ensure_root_para_or_owner(
|
||||
origin: <T as frame_system::Config>::Origin,
|
||||
id: ParaId,
|
||||
) -> DispatchResult {
|
||||
ensure_signed(origin.clone())
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|who| -> DispatchResult {
|
||||
let para_info = Paras::<T>::get(id).ok_or(Error::<T>::NotRegistered)?;
|
||||
ensure!(!para_info.locked, Error::<T>::ParaLocked);
|
||||
ensure!(para_info.manager == who, Error::<T>::NotOwner);
|
||||
Ok(())
|
||||
})
|
||||
.or_else(|_| -> DispatchResult {
|
||||
// Else check if para origin...
|
||||
let caller_id = ensure_parachain(<T as Config>::Origin::from(origin.clone()))?;
|
||||
ensure!(caller_id == id, Error::<T>::NotOwner);
|
||||
Ok(())
|
||||
})
|
||||
.or_else(|_| -> DispatchResult {
|
||||
// Check if root...
|
||||
ensure_root(origin.clone()).map_err(|e| e.into())
|
||||
})
|
||||
}
|
||||
|
||||
fn do_reserve(
|
||||
@@ -436,11 +455,7 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get);
|
||||
<T as Config>::Currency::reserve(&who, deposit)?;
|
||||
let info = ParaInfo {
|
||||
manager: who.clone(),
|
||||
deposit,
|
||||
locked: false,
|
||||
};
|
||||
let info = ParaInfo { manager: who.clone(), deposit, locked: false };
|
||||
|
||||
Paras::<T>::insert(id, info);
|
||||
Self::deposit_event(Event::<T>::Reserved(id, who));
|
||||
@@ -466,11 +481,8 @@ impl<T: Config> Pallet<T> {
|
||||
Default::default()
|
||||
};
|
||||
ensure!(paras::Pallet::<T>::lifecycle(id).is_none(), Error::<T>::AlreadyRegistered);
|
||||
let (genesis, deposit) = Self::validate_onboarding_data(
|
||||
genesis_head,
|
||||
validation_code,
|
||||
false
|
||||
)?;
|
||||
let (genesis, deposit) =
|
||||
Self::validate_onboarding_data(genesis_head, validation_code, false)?;
|
||||
let deposit = deposit_override.unwrap_or(deposit);
|
||||
|
||||
if let Some(additional) = deposit.checked_sub(&deposited) {
|
||||
@@ -478,11 +490,7 @@ impl<T: Config> Pallet<T> {
|
||||
} else if let Some(rebate) = deposited.checked_sub(&deposit) {
|
||||
<T as Config>::Currency::unreserve(&who, rebate);
|
||||
};
|
||||
let info = ParaInfo {
|
||||
manager: who.clone(),
|
||||
deposit,
|
||||
locked: false,
|
||||
};
|
||||
let info = ParaInfo { manager: who.clone(), deposit, locked: false };
|
||||
|
||||
Paras::<T>::insert(id, info);
|
||||
// We check above that para has no lifecycle, so this should not fail.
|
||||
@@ -497,9 +505,10 @@ impl<T: Config> Pallet<T> {
|
||||
match paras::Pallet::<T>::lifecycle(id) {
|
||||
// Para must be a parathread, or not exist at all.
|
||||
Some(ParaLifecycle::Parathread) | None => {},
|
||||
_ => return Err(Error::<T>::NotParathread.into())
|
||||
_ => return Err(Error::<T>::NotParathread.into()),
|
||||
}
|
||||
runtime_parachains::schedule_para_cleanup::<T>(id).map_err(|_| Error::<T>::CannotDeregister)?;
|
||||
runtime_parachains::schedule_para_cleanup::<T>(id)
|
||||
.map_err(|_| Error::<T>::CannotDeregister)?;
|
||||
|
||||
if let Some(info) = Paras::<T>::take(&id) {
|
||||
<T as Config>::Currency::unreserve(&info.manager, info.deposit);
|
||||
@@ -520,45 +529,40 @@ impl<T: Config> Pallet<T> {
|
||||
) -> Result<(ParaGenesisArgs, BalanceOf<T>), sp_runtime::DispatchError> {
|
||||
let config = configuration::Pallet::<T>::config();
|
||||
ensure!(validation_code.0.len() <= config.max_code_size as usize, Error::<T>::CodeTooLarge);
|
||||
ensure!(genesis_head.0.len() <= config.max_head_data_size as usize, Error::<T>::HeadDataTooLarge);
|
||||
ensure!(
|
||||
genesis_head.0.len() <= config.max_head_data_size as usize,
|
||||
Error::<T>::HeadDataTooLarge
|
||||
);
|
||||
|
||||
let per_byte_fee = T::DataDepositPerByte::get();
|
||||
let deposit = T::ParaDeposit::get()
|
||||
.saturating_add(
|
||||
per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into())
|
||||
).saturating_add(
|
||||
per_byte_fee.saturating_mul((validation_code.0.len() as u32).into())
|
||||
);
|
||||
.saturating_add(per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into()))
|
||||
.saturating_add(per_byte_fee.saturating_mul((validation_code.0.len() as u32).into()));
|
||||
|
||||
Ok((ParaGenesisArgs {
|
||||
genesis_head,
|
||||
validation_code,
|
||||
parachain,
|
||||
}, deposit))
|
||||
Ok((ParaGenesisArgs { genesis_head, validation_code, parachain }, deposit))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sp_io::TestExternalities;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
traits::{
|
||||
BlakeTwo256, IdentityLookup,
|
||||
}, Perbill,
|
||||
};
|
||||
use primitives::v1::{Balance, BlockNumber, Header};
|
||||
use frame_system::limits;
|
||||
use crate::{paras_registrar, traits::Registrar as RegistrarTrait};
|
||||
use frame_support::{
|
||||
traits::{OnInitialize, OnFinalize, GenesisBuild},
|
||||
assert_ok, assert_noop, parameter_types,
|
||||
assert_noop, assert_ok,
|
||||
error::BadOrigin,
|
||||
parameter_types,
|
||||
traits::{GenesisBuild, OnFinalize, OnInitialize},
|
||||
};
|
||||
use runtime_parachains::{configuration, shared};
|
||||
use frame_system::limits;
|
||||
use pallet_balances::Error as BalancesError;
|
||||
use crate::traits::Registrar as RegistrarTrait;
|
||||
use crate::paras_registrar;
|
||||
use primitives::v1::{Balance, BlockNumber, Header};
|
||||
use runtime_parachains::{configuration, shared};
|
||||
use sp_core::H256;
|
||||
use sp_io::TestExternalities;
|
||||
use sp_runtime::{
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
Perbill,
|
||||
};
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
@@ -636,7 +640,7 @@ mod tests {
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
impl configuration::Config for Test { }
|
||||
impl configuration::Config for Test {}
|
||||
|
||||
parameter_types! {
|
||||
pub const ParaDeposit: Balance = 10;
|
||||
@@ -661,17 +665,18 @@ mod tests {
|
||||
GenesisBuild::<Test>::assimilate_storage(
|
||||
&configuration::GenesisConfig {
|
||||
config: configuration::HostConfiguration {
|
||||
max_code_size: 2 * 1024 * 1024, // 2 MB
|
||||
max_code_size: 2 * 1024 * 1024, // 2 MB
|
||||
max_head_data_size: 1 * 1024 * 1024, // 1 MB
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
},
|
||||
&mut t
|
||||
).unwrap();
|
||||
&mut t,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
pallet_balances::GenesisConfig::<Test> {
|
||||
balances: vec![(1, 10_000_000), (2, 10_000_000)],
|
||||
}.assimilate_storage(&mut t).unwrap();
|
||||
pallet_balances::GenesisConfig::<Test> { balances: vec![(1, 10_000_000), (2, 10_000_000)] }
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
|
||||
t.into()
|
||||
}
|
||||
@@ -691,7 +696,7 @@ mod tests {
|
||||
// Session change every 3 blocks.
|
||||
if (b + 1) % BLOCKS_PER_SESSION == 0 {
|
||||
shared::Pallet::<Test>::set_session_index(
|
||||
shared::Pallet::<Test>::session_index() + 1
|
||||
shared::Pallet::<Test>::session_index() + 1,
|
||||
);
|
||||
Parachains::test_on_new_session();
|
||||
}
|
||||
@@ -765,10 +770,7 @@ mod tests {
|
||||
assert!(Parachains::is_parathread(para_id));
|
||||
assert!(!Parachains::is_parachain(para_id));
|
||||
// Deregister it
|
||||
assert_ok!(Registrar::deregister(
|
||||
Origin::root(),
|
||||
para_id,
|
||||
));
|
||||
assert_ok!(Registrar::deregister(Origin::root(), para_id,));
|
||||
run_to_session(8);
|
||||
// It is nothing
|
||||
assert!(!Parachains::is_parathread(para_id));
|
||||
@@ -794,7 +796,8 @@ mod tests {
|
||||
assert!(Parachains::is_parathread(para_id));
|
||||
assert_eq!(
|
||||
Balances::reserved_balance(&1),
|
||||
<Test as Config>::ParaDeposit::get() + 64 * <Test as Config>::DataDepositPerByte::get()
|
||||
<Test as Config>::ParaDeposit::get() +
|
||||
64 * <Test as Config>::DataDepositPerByte::get()
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -804,22 +807,28 @@ mod tests {
|
||||
new_test_ext().execute_with(|| {
|
||||
let para_id = LOWEST_PUBLIC_ID;
|
||||
|
||||
assert_noop!(Registrar::register(
|
||||
Origin::signed(1),
|
||||
para_id,
|
||||
test_genesis_head(max_head_size() as usize),
|
||||
test_validation_code(max_code_size() as usize),
|
||||
), Error::<Test>::NotReserved);
|
||||
assert_noop!(
|
||||
Registrar::register(
|
||||
Origin::signed(1),
|
||||
para_id,
|
||||
test_genesis_head(max_head_size() as usize),
|
||||
test_validation_code(max_code_size() as usize),
|
||||
),
|
||||
Error::<Test>::NotReserved
|
||||
);
|
||||
|
||||
// Successfully register para
|
||||
assert_ok!(Registrar::reserve(Origin::signed(1)));
|
||||
|
||||
assert_noop!(Registrar::register(
|
||||
Origin::signed(2),
|
||||
para_id,
|
||||
test_genesis_head(max_head_size() as usize),
|
||||
test_validation_code(max_code_size() as usize),
|
||||
), Error::<Test>::NotOwner);
|
||||
assert_noop!(
|
||||
Registrar::register(
|
||||
Origin::signed(2),
|
||||
para_id,
|
||||
test_genesis_head(max_head_size() as usize),
|
||||
test_validation_code(max_code_size() as usize),
|
||||
),
|
||||
Error::<Test>::NotOwner
|
||||
);
|
||||
|
||||
assert_ok!(Registrar::register(
|
||||
Origin::signed(1),
|
||||
@@ -833,32 +842,44 @@ mod tests {
|
||||
assert_ok!(Registrar::deregister(Origin::root(), para_id));
|
||||
|
||||
// Can't do it again
|
||||
assert_noop!(Registrar::register(
|
||||
Origin::signed(1),
|
||||
para_id,
|
||||
test_genesis_head(max_head_size() as usize),
|
||||
test_validation_code(max_code_size() as usize),
|
||||
), Error::<Test>::NotReserved);
|
||||
assert_noop!(
|
||||
Registrar::register(
|
||||
Origin::signed(1),
|
||||
para_id,
|
||||
test_genesis_head(max_head_size() as usize),
|
||||
test_validation_code(max_code_size() as usize),
|
||||
),
|
||||
Error::<Test>::NotReserved
|
||||
);
|
||||
|
||||
// Head Size Check
|
||||
assert_ok!(Registrar::reserve(Origin::signed(2)));
|
||||
assert_noop!(Registrar::register(
|
||||
Origin::signed(2),
|
||||
para_id + 1,
|
||||
test_genesis_head((max_head_size() + 1) as usize),
|
||||
test_validation_code(max_code_size() as usize),
|
||||
), Error::<Test>::HeadDataTooLarge);
|
||||
assert_noop!(
|
||||
Registrar::register(
|
||||
Origin::signed(2),
|
||||
para_id + 1,
|
||||
test_genesis_head((max_head_size() + 1) as usize),
|
||||
test_validation_code(max_code_size() as usize),
|
||||
),
|
||||
Error::<Test>::HeadDataTooLarge
|
||||
);
|
||||
|
||||
// Code Size Check
|
||||
assert_noop!(Registrar::register(
|
||||
Origin::signed(2),
|
||||
para_id + 1,
|
||||
test_genesis_head(max_head_size() as usize),
|
||||
test_validation_code((max_code_size() + 1) as usize),
|
||||
), Error::<Test>::CodeTooLarge);
|
||||
assert_noop!(
|
||||
Registrar::register(
|
||||
Origin::signed(2),
|
||||
para_id + 1,
|
||||
test_genesis_head(max_head_size() as usize),
|
||||
test_validation_code((max_code_size() + 1) as usize),
|
||||
),
|
||||
Error::<Test>::CodeTooLarge
|
||||
);
|
||||
|
||||
// Needs enough funds for deposit
|
||||
assert_noop!(Registrar::reserve(Origin::signed(1337)), BalancesError::<Test, _>::InsufficientBalance);
|
||||
assert_noop!(
|
||||
Registrar::reserve(Origin::signed(1337)),
|
||||
BalancesError::<Test, _>::InsufficientBalance
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -877,10 +898,7 @@ mod tests {
|
||||
));
|
||||
run_to_session(2);
|
||||
assert!(Parachains::is_parathread(para_id));
|
||||
assert_ok!(Registrar::deregister(
|
||||
Origin::root(),
|
||||
para_id,
|
||||
));
|
||||
assert_ok!(Registrar::deregister(Origin::root(), para_id,));
|
||||
run_to_session(4);
|
||||
assert!(paras::Pallet::<Test>::lifecycle(para_id).is_none());
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
@@ -903,17 +921,14 @@ mod tests {
|
||||
run_to_session(2);
|
||||
assert!(Parachains::is_parathread(para_id));
|
||||
// Owner check
|
||||
assert_noop!(Registrar::deregister(
|
||||
Origin::signed(2),
|
||||
para_id,
|
||||
), BadOrigin);
|
||||
assert_noop!(Registrar::deregister(Origin::signed(2), para_id,), BadOrigin);
|
||||
assert_ok!(Registrar::make_parachain(para_id));
|
||||
run_to_session(4);
|
||||
// Cant directly deregister parachain
|
||||
assert_noop!(Registrar::deregister(
|
||||
Origin::root(),
|
||||
para_id,
|
||||
), Error::<Test>::NotParathread);
|
||||
assert_noop!(
|
||||
Registrar::deregister(Origin::root(), para_id,),
|
||||
Error::<Test>::NotParathread
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -951,22 +966,17 @@ mod tests {
|
||||
assert!(Parachains::is_parathread(para_2));
|
||||
|
||||
// Both paras initiate a swap
|
||||
assert_ok!(Registrar::swap(
|
||||
para_origin(para_1),
|
||||
para_1,
|
||||
para_2,
|
||||
));
|
||||
assert_ok!(Registrar::swap(
|
||||
para_origin(para_2),
|
||||
para_2,
|
||||
para_1,
|
||||
));
|
||||
assert_ok!(Registrar::swap(para_origin(para_1), para_1, para_2,));
|
||||
assert_ok!(Registrar::swap(para_origin(para_2), para_2, para_1,));
|
||||
|
||||
run_to_session(6);
|
||||
|
||||
// Deregister a parathread that was originally a parachain
|
||||
assert_eq!(Parachains::lifecycle(para_1), Some(ParaLifecycle::Parathread));
|
||||
assert_ok!(Registrar::deregister(runtime_parachains::Origin::Parachain(para_1).into(), para_1));
|
||||
assert_ok!(Registrar::deregister(
|
||||
runtime_parachains::Origin::Parachain(para_1).into(),
|
||||
para_1
|
||||
));
|
||||
|
||||
run_to_block(21);
|
||||
|
||||
@@ -1010,14 +1020,14 @@ mod tests {
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking {
|
||||
use super::{*, Pallet as Registrar};
|
||||
use frame_system::RawOrigin;
|
||||
use super::{Pallet as Registrar, *};
|
||||
use crate::traits::Registrar as RegistrarT;
|
||||
use frame_support::assert_ok;
|
||||
use sp_runtime::traits::Bounded;
|
||||
use crate::traits::{Registrar as RegistrarT};
|
||||
use frame_system::RawOrigin;
|
||||
use runtime_parachains::{paras, shared, Origin as ParaOrigin};
|
||||
use sp_runtime::traits::Bounded;
|
||||
|
||||
use frame_benchmarking::{account, benchmarks, whitelisted_caller, impl_benchmark_test_suite};
|
||||
use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller};
|
||||
|
||||
fn assert_last_event<T: Config>(generic_event: <T as Config>::Event) {
|
||||
let events = frame_system::Pallet::<T>::events();
|
||||
@@ -1034,8 +1044,13 @@ mod benchmarking {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||
assert_ok!(Registrar::<T>::reserve(RawOrigin::Signed(caller.clone()).into()));
|
||||
assert_ok!(Registrar::<T>::register(RawOrigin::Signed(caller).into(), para, genesis_head, validation_code));
|
||||
return para;
|
||||
assert_ok!(Registrar::<T>::register(
|
||||
RawOrigin::Signed(caller).into(),
|
||||
para,
|
||||
genesis_head,
|
||||
validation_code
|
||||
));
|
||||
return para
|
||||
}
|
||||
|
||||
fn para_origin(id: u32) -> ParaOrigin {
|
||||
@@ -1044,9 +1059,7 @@ mod benchmarking {
|
||||
|
||||
// This function moves forward to the next scheduled session for parachain lifecycle upgrades.
|
||||
fn next_scheduled_session<T: Config>() {
|
||||
shared::Pallet::<T>::set_session_index(
|
||||
shared::Pallet::<T>::scheduled_session()
|
||||
);
|
||||
shared::Pallet::<T>::set_session_index(shared::Pallet::<T>::scheduled_session());
|
||||
paras::Pallet::<T>::test_on_new_session();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use runtime_parachains::{
|
||||
configuration, dmp, ump, hrmp,
|
||||
ParaLifecycle,
|
||||
paras::{self, ParaGenesisArgs},
|
||||
};
|
||||
use primitives::v1::Id as ParaId;
|
||||
use parity_scale_codec::Encode;
|
||||
pub use pallet::*;
|
||||
use parity_scale_codec::Encode;
|
||||
use primitives::v1::Id as ParaId;
|
||||
use runtime_parachains::{
|
||||
configuration, dmp, hrmp,
|
||||
paras::{self, ParaGenesisArgs},
|
||||
ump, ParaLifecycle,
|
||||
};
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
@@ -38,8 +38,9 @@ pub mod pallet {
|
||||
#[pallet::config]
|
||||
#[pallet::disable_frame_system_supertrait_check]
|
||||
pub trait Config:
|
||||
configuration::Config + paras::Config + dmp::Config + ump::Config + hrmp::Config {}
|
||||
|
||||
configuration::Config + paras::Config + dmp::Config + ump::Config + hrmp::Config
|
||||
{
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
@@ -84,13 +85,17 @@ pub mod pallet {
|
||||
#[pallet::weight((1_000, DispatchClass::Operational))]
|
||||
pub fn sudo_schedule_para_cleanup(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
runtime_parachains::schedule_para_cleanup::<T>(id).map_err(|_| Error::<T>::CouldntCleanup)?;
|
||||
runtime_parachains::schedule_para_cleanup::<T>(id)
|
||||
.map_err(|_| Error::<T>::CouldntCleanup)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Upgrade a parathread to a parachain
|
||||
#[pallet::weight((1_000, DispatchClass::Operational))]
|
||||
pub fn sudo_schedule_parathread_upgrade(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
|
||||
pub fn sudo_schedule_parathread_upgrade(
|
||||
origin: OriginFor<T>,
|
||||
id: ParaId,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
// Para backend should think this is a parathread...
|
||||
ensure!(
|
||||
@@ -104,7 +109,10 @@ pub mod pallet {
|
||||
|
||||
/// Downgrade a parachain to a parathread
|
||||
#[pallet::weight((1_000, DispatchClass::Operational))]
|
||||
pub fn sudo_schedule_parachain_downgrade(origin: OriginFor<T>, id: ParaId) -> DispatchResult {
|
||||
pub fn sudo_schedule_parachain_downgrade(
|
||||
origin: OriginFor<T>,
|
||||
id: ParaId,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
// Para backend should think this is a parachain...
|
||||
ensure!(
|
||||
@@ -129,11 +137,11 @@ pub mod pallet {
|
||||
ensure_root(origin)?;
|
||||
ensure!(<paras::Pallet<T>>::is_valid_para(id), Error::<T>::ParaDoesntExist);
|
||||
let config = <configuration::Pallet<T>>::config();
|
||||
<dmp::Pallet<T>>::queue_downward_message(&config, id, xcm.encode())
|
||||
.map_err(|e| match e {
|
||||
dmp::QueueDownwardMessageError::ExceedsMaxMessageSize =>
|
||||
Error::<T>::ExceedsMaxMessageSize.into(),
|
||||
})
|
||||
<dmp::Pallet<T>>::queue_downward_message(&config, id, xcm.encode()).map_err(|e| match e
|
||||
{
|
||||
dmp::QueueDownwardMessageError::ExceedsMaxMessageSize =>
|
||||
Error::<T>::ExceedsMaxMessageSize.into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Forcefully establish a channel from the sender to the recipient.
|
||||
|
||||
@@ -16,19 +16,22 @@
|
||||
|
||||
//! Pallet to process purchase of DOTs.
|
||||
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use sp_runtime::{Permill, RuntimeDebug, DispatchResult, DispatchError, AnySignature};
|
||||
use sp_runtime::traits::{Zero, CheckedAdd, Verify, Saturating};
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_support::traits::{
|
||||
EnsureOrigin, Currency, ExistenceRequirement, VestingSchedule, Get
|
||||
use frame_support::{
|
||||
pallet_prelude::*,
|
||||
traits::{Currency, EnsureOrigin, ExistenceRequirement, Get, VestingSchedule},
|
||||
};
|
||||
use frame_system::pallet_prelude::*;
|
||||
use sp_core::sr25519;
|
||||
use sp_std::prelude::*;
|
||||
pub use pallet::*;
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use sp_core::sr25519;
|
||||
use sp_runtime::{
|
||||
traits::{CheckedAdd, Saturating, Verify, Zero},
|
||||
AnySignature, DispatchError, DispatchResult, Permill, RuntimeDebug,
|
||||
};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
type BalanceOf<T> = <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
type BalanceOf<T> =
|
||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
|
||||
/// The kind of a statement an account needs to make for a claim to be valid.
|
||||
#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, RuntimeDebug)]
|
||||
@@ -99,7 +102,11 @@ pub mod pallet {
|
||||
type Currency: Currency<Self::AccountId>;
|
||||
|
||||
/// Vesting Pallet
|
||||
type VestingSchedule: VestingSchedule<Self::AccountId, Moment=Self::BlockNumber, Currency=Self::Currency>;
|
||||
type VestingSchedule: VestingSchedule<
|
||||
Self::AccountId,
|
||||
Moment = Self::BlockNumber,
|
||||
Currency = Self::Currency,
|
||||
>;
|
||||
|
||||
/// The origin allowed to set account status.
|
||||
type ValidityOrigin: EnsureOrigin<Self::Origin>;
|
||||
@@ -166,12 +173,8 @@ pub mod pallet {
|
||||
|
||||
// A map of all participants in the DOT purchase process.
|
||||
#[pallet::storage]
|
||||
pub(super) type Accounts<T: Config> = StorageMap<
|
||||
_,
|
||||
Blake2_128Concat, T::AccountId,
|
||||
AccountStatus<BalanceOf<T>>,
|
||||
ValueQuery,
|
||||
>;
|
||||
pub(super) type Accounts<T: Config> =
|
||||
StorageMap<_, Blake2_128Concat, T::AccountId, AccountStatus<BalanceOf<T>>, ValueQuery>;
|
||||
|
||||
// The account that will be used to payout participants of the DOT purchase process.
|
||||
#[pallet::storage]
|
||||
@@ -199,13 +202,16 @@ pub mod pallet {
|
||||
pub fn create_account(
|
||||
origin: OriginFor<T>,
|
||||
who: T::AccountId,
|
||||
signature: Vec<u8>
|
||||
signature: Vec<u8>,
|
||||
) -> DispatchResult {
|
||||
T::ValidityOrigin::ensure_origin(origin)?;
|
||||
// Account is already being tracked by the pallet.
|
||||
ensure!(!Accounts::<T>::contains_key(&who), Error::<T>::ExistingAccount);
|
||||
// Account should not have a vesting schedule.
|
||||
ensure!(T::VestingSchedule::vesting_balance(&who).is_none(), Error::<T>::VestingScheduleExists);
|
||||
ensure!(
|
||||
T::VestingSchedule::vesting_balance(&who).is_none(),
|
||||
Error::<T>::VestingScheduleExists
|
||||
);
|
||||
|
||||
// Verify the signature provided is valid for the statement.
|
||||
Self::verify_signature(&who, &signature)?;
|
||||
@@ -233,15 +239,21 @@ pub mod pallet {
|
||||
pub fn update_validity_status(
|
||||
origin: OriginFor<T>,
|
||||
who: T::AccountId,
|
||||
validity: AccountValidity
|
||||
validity: AccountValidity,
|
||||
) -> DispatchResult {
|
||||
T::ValidityOrigin::ensure_origin(origin)?;
|
||||
ensure!(Accounts::<T>::contains_key(&who), Error::<T>::InvalidAccount);
|
||||
Accounts::<T>::try_mutate(&who, |status: &mut AccountStatus<BalanceOf<T>>| -> DispatchResult {
|
||||
ensure!(status.validity != AccountValidity::Completed, Error::<T>::AlreadyCompleted);
|
||||
status.validity = validity;
|
||||
Ok(())
|
||||
})?;
|
||||
Accounts::<T>::try_mutate(
|
||||
&who,
|
||||
|status: &mut AccountStatus<BalanceOf<T>>| -> DispatchResult {
|
||||
ensure!(
|
||||
status.validity != AccountValidity::Completed,
|
||||
Error::<T>::AlreadyCompleted
|
||||
);
|
||||
status.validity = validity;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
Self::deposit_event(Event::<T>::ValidityUpdated(who, validity));
|
||||
Ok(())
|
||||
}
|
||||
@@ -261,16 +273,19 @@ pub mod pallet {
|
||||
) -> DispatchResult {
|
||||
T::ValidityOrigin::ensure_origin(origin)?;
|
||||
|
||||
Accounts::<T>::try_mutate(&who, |status: &mut AccountStatus<BalanceOf<T>>| -> DispatchResult {
|
||||
// Account has a valid status (not Invalid, Pending, or Completed)...
|
||||
ensure!(status.validity.is_valid(), Error::<T>::InvalidAccount);
|
||||
Accounts::<T>::try_mutate(
|
||||
&who,
|
||||
|status: &mut AccountStatus<BalanceOf<T>>| -> DispatchResult {
|
||||
// Account has a valid status (not Invalid, Pending, or Completed)...
|
||||
ensure!(status.validity.is_valid(), Error::<T>::InvalidAccount);
|
||||
|
||||
free_balance.checked_add(&locked_balance).ok_or(Error::<T>::Overflow)?;
|
||||
status.free_balance = free_balance;
|
||||
status.locked_balance = locked_balance;
|
||||
status.vat = vat;
|
||||
Ok(())
|
||||
})?;
|
||||
free_balance.checked_add(&locked_balance).ok_or(Error::<T>::Overflow)?;
|
||||
status.free_balance = free_balance;
|
||||
status.locked_balance = locked_balance;
|
||||
status.vat = vat;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
Self::deposit_event(Event::<T>::BalanceUpdated(who, free_balance, locked_balance));
|
||||
Ok(())
|
||||
}
|
||||
@@ -287,44 +302,59 @@ pub mod pallet {
|
||||
ensure!(payment_account == PaymentAccount::<T>::get(), DispatchError::BadOrigin);
|
||||
|
||||
// Account should not have a vesting schedule.
|
||||
ensure!(T::VestingSchedule::vesting_balance(&who).is_none(), Error::<T>::VestingScheduleExists);
|
||||
ensure!(
|
||||
T::VestingSchedule::vesting_balance(&who).is_none(),
|
||||
Error::<T>::VestingScheduleExists
|
||||
);
|
||||
|
||||
Accounts::<T>::try_mutate(&who, |status: &mut AccountStatus<BalanceOf<T>>| -> DispatchResult {
|
||||
// Account has a valid status (not Invalid, Pending, or Completed)...
|
||||
ensure!(status.validity.is_valid(), Error::<T>::InvalidAccount);
|
||||
Accounts::<T>::try_mutate(
|
||||
&who,
|
||||
|status: &mut AccountStatus<BalanceOf<T>>| -> DispatchResult {
|
||||
// Account has a valid status (not Invalid, Pending, or Completed)...
|
||||
ensure!(status.validity.is_valid(), Error::<T>::InvalidAccount);
|
||||
|
||||
// Transfer funds from the payment account into the purchasing user.
|
||||
let total_balance = status.free_balance
|
||||
.checked_add(&status.locked_balance)
|
||||
.ok_or(Error::<T>::Overflow)?;
|
||||
T::Currency::transfer(&payment_account, &who, total_balance, ExistenceRequirement::AllowDeath)?;
|
||||
|
||||
if !status.locked_balance.is_zero() {
|
||||
let unlock_block = UnlockBlock::<T>::get();
|
||||
// We allow some configurable portion of the purchased locked DOTs to be unlocked for basic usage.
|
||||
let unlocked = (T::UnlockedProportion::get() * status.locked_balance).min(T::MaxUnlocked::get());
|
||||
let locked = status.locked_balance.saturating_sub(unlocked);
|
||||
// We checked that this account has no existing vesting schedule. So this function should
|
||||
// never fail, however if it does, not much we can do about it at this point.
|
||||
let _ = T::VestingSchedule::add_vesting_schedule(
|
||||
// Apply vesting schedule to this user
|
||||
// Transfer funds from the payment account into the purchasing user.
|
||||
let total_balance = status
|
||||
.free_balance
|
||||
.checked_add(&status.locked_balance)
|
||||
.ok_or(Error::<T>::Overflow)?;
|
||||
T::Currency::transfer(
|
||||
&payment_account,
|
||||
&who,
|
||||
// For this much amount
|
||||
locked,
|
||||
// Unlocking the full amount after one block
|
||||
locked,
|
||||
// When everything unlocks
|
||||
unlock_block
|
||||
);
|
||||
}
|
||||
total_balance,
|
||||
ExistenceRequirement::AllowDeath,
|
||||
)?;
|
||||
|
||||
// Setting the user account to `Completed` ends the purchase process for this user.
|
||||
status.validity = AccountValidity::Completed;
|
||||
Self::deposit_event(
|
||||
Event::<T>::PaymentComplete(who.clone(), status.free_balance, status.locked_balance)
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
if !status.locked_balance.is_zero() {
|
||||
let unlock_block = UnlockBlock::<T>::get();
|
||||
// We allow some configurable portion of the purchased locked DOTs to be unlocked for basic usage.
|
||||
let unlocked = (T::UnlockedProportion::get() * status.locked_balance)
|
||||
.min(T::MaxUnlocked::get());
|
||||
let locked = status.locked_balance.saturating_sub(unlocked);
|
||||
// We checked that this account has no existing vesting schedule. So this function should
|
||||
// never fail, however if it does, not much we can do about it at this point.
|
||||
let _ = T::VestingSchedule::add_vesting_schedule(
|
||||
// Apply vesting schedule to this user
|
||||
&who,
|
||||
// For this much amount
|
||||
locked,
|
||||
// Unlocking the full amount after one block
|
||||
locked,
|
||||
// When everything unlocks
|
||||
unlock_block,
|
||||
);
|
||||
}
|
||||
|
||||
// Setting the user account to `Completed` ends the purchase process for this user.
|
||||
status.validity = AccountValidity::Completed;
|
||||
Self::deposit_event(Event::<T>::PaymentComplete(
|
||||
who.clone(),
|
||||
status.free_balance,
|
||||
status.locked_balance,
|
||||
));
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -348,7 +378,10 @@ pub mod pallet {
|
||||
#[pallet::weight(T::DbWeight::get().writes(1))]
|
||||
pub fn set_statement(origin: OriginFor<T>, statement: Vec<u8>) -> DispatchResult {
|
||||
T::ConfigurationOrigin::ensure_origin(origin)?;
|
||||
ensure!((statement.len() as u32) < T::MaxStatementLength::get(), Error::<T>::InvalidStatement);
|
||||
ensure!(
|
||||
(statement.len() as u32) < T::MaxStatementLength::get(),
|
||||
Error::<T>::InvalidStatement
|
||||
);
|
||||
// Possibly this is worse than having the caller account be the payment account?
|
||||
Statement::<T>::set(statement);
|
||||
Self::deposit_event(Event::<T>::StatementUpdated);
|
||||
@@ -359,9 +392,15 @@ pub mod pallet {
|
||||
///
|
||||
/// Origin must match the `ConfigurationOrigin`
|
||||
#[pallet::weight(T::DbWeight::get().writes(1))]
|
||||
pub fn set_unlock_block(origin: OriginFor<T>, unlock_block: T::BlockNumber) -> DispatchResult {
|
||||
pub fn set_unlock_block(
|
||||
origin: OriginFor<T>,
|
||||
unlock_block: T::BlockNumber,
|
||||
) -> DispatchResult {
|
||||
T::ConfigurationOrigin::ensure_origin(origin)?;
|
||||
ensure!(unlock_block > frame_system::Pallet::<T>::block_number(), Error::<T>::InvalidUnlockBlock);
|
||||
ensure!(
|
||||
unlock_block > frame_system::Pallet::<T>::block_number(),
|
||||
Error::<T>::InvalidUnlockBlock
|
||||
);
|
||||
// Possibly this is worse than having the caller account be the payment account?
|
||||
UnlockBlock::<T>::set(unlock_block);
|
||||
Self::deposit_event(Event::<T>::UnlockBlockUpdated(unlock_block));
|
||||
@@ -392,7 +431,8 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
// This function converts a 32 byte AccountId to its byte-array equivalent form.
|
||||
fn account_to_bytes<AccountId>(account: &AccountId) -> Result<[u8; 32], DispatchError>
|
||||
where AccountId: Encode,
|
||||
where
|
||||
AccountId: Encode,
|
||||
{
|
||||
let account_vec = account.encode();
|
||||
ensure!(account_vec.len() == 32, "AccountId must be 32 bytes.");
|
||||
@@ -404,7 +444,8 @@ fn account_to_bytes<AccountId>(account: &AccountId) -> Result<[u8; 32], Dispatch
|
||||
/// WARNING: Executing this function will clear all storage used by this pallet.
|
||||
/// Be sure this is what you want...
|
||||
pub fn remove_pallet<T>() -> frame_support::weights::Weight
|
||||
where T: frame_system::Config
|
||||
where
|
||||
T: frame_system::Config,
|
||||
{
|
||||
use frame_support::migration::remove_storage_prefix;
|
||||
remove_storage_prefix(b"Purchase", b"Accounts", b"");
|
||||
@@ -419,21 +460,20 @@ pub fn remove_pallet<T>() -> frame_support::weights::Weight
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use sp_core::{H256, Pair, Public, crypto::AccountId32, ed25519};
|
||||
use sp_core::{crypto::AccountId32, ed25519, Pair, Public, H256};
|
||||
// The testing primitives are very useful for avoiding having to work with signatures
|
||||
// or public keys. `u64` is used as the `AccountId` and no `Signature`s are required.
|
||||
use sp_runtime::{
|
||||
MultiSignature,
|
||||
traits::{BlakeTwo256, IdentityLookup, Identity, Verify, IdentifyAccount, Dispatchable},
|
||||
testing::Header
|
||||
};
|
||||
use frame_support::{
|
||||
assert_ok, assert_noop, parameter_types,
|
||||
ord_parameter_types, dispatch::DispatchError::BadOrigin,
|
||||
};
|
||||
use frame_support::traits::Currency;
|
||||
use pallet_balances::Error as BalancesError;
|
||||
use crate::purchase;
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok, dispatch::DispatchError::BadOrigin, ord_parameter_types,
|
||||
parameter_types, traits::Currency,
|
||||
};
|
||||
use pallet_balances::Error as BalancesError;
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, Dispatchable, IdentifyAccount, Identity, IdentityLookup, Verify},
|
||||
MultiSignature,
|
||||
};
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
@@ -547,7 +587,8 @@ mod tests {
|
||||
let unlock_block = 100;
|
||||
Purchase::set_statement(Origin::signed(configuration_origin()), statement).unwrap();
|
||||
Purchase::set_unlock_block(Origin::signed(configuration_origin()), unlock_block).unwrap();
|
||||
Purchase::set_payment_account(Origin::signed(configuration_origin()), payment_account()).unwrap();
|
||||
Purchase::set_payment_account(Origin::signed(configuration_origin()), payment_account())
|
||||
.unwrap();
|
||||
Balances::make_free_balance_be(&payment_account(), 100_000);
|
||||
}
|
||||
|
||||
@@ -561,8 +602,9 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Helper function to generate an account ID from seed
|
||||
fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where
|
||||
AccountPublic: From<<TPublic::Pair as Pair>::Public>
|
||||
fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
|
||||
where
|
||||
AccountPublic: From<<TPublic::Pair as Pair>::Public>,
|
||||
{
|
||||
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
|
||||
}
|
||||
@@ -622,7 +664,10 @@ mod tests {
|
||||
Error::<Test>::InvalidStatement,
|
||||
);
|
||||
// Just right...
|
||||
assert_ok!(Purchase::set_statement(Origin::signed(configuration_origin()), statement.clone()));
|
||||
assert_ok!(Purchase::set_statement(
|
||||
Origin::signed(configuration_origin()),
|
||||
statement.clone()
|
||||
));
|
||||
assert_eq!(Statement::<Test>::get(), statement);
|
||||
});
|
||||
}
|
||||
@@ -640,11 +685,17 @@ mod tests {
|
||||
let bad_unlock_block = 50;
|
||||
System::set_block_number(bad_unlock_block);
|
||||
assert_noop!(
|
||||
Purchase::set_unlock_block(Origin::signed(configuration_origin()), bad_unlock_block),
|
||||
Purchase::set_unlock_block(
|
||||
Origin::signed(configuration_origin()),
|
||||
bad_unlock_block
|
||||
),
|
||||
Error::<Test>::InvalidUnlockBlock,
|
||||
);
|
||||
// Just right...
|
||||
assert_ok!(Purchase::set_unlock_block(Origin::signed(configuration_origin()), unlock_block));
|
||||
assert_ok!(Purchase::set_unlock_block(
|
||||
Origin::signed(configuration_origin()),
|
||||
unlock_block
|
||||
));
|
||||
assert_eq!(UnlockBlock::<Test>::get(), unlock_block);
|
||||
});
|
||||
}
|
||||
@@ -659,7 +710,10 @@ mod tests {
|
||||
BadOrigin,
|
||||
);
|
||||
// Just right...
|
||||
assert_ok!(Purchase::set_payment_account(Origin::signed(configuration_origin()), payment_account.clone()));
|
||||
assert_ok!(Purchase::set_payment_account(
|
||||
Origin::signed(configuration_origin()),
|
||||
payment_account.clone()
|
||||
));
|
||||
assert_eq!(PaymentAccount::<Test>::get(), payment_account);
|
||||
});
|
||||
}
|
||||
@@ -672,8 +726,14 @@ mod tests {
|
||||
assert_ok!(Purchase::verify_signature(&bob(), &bob_signature()));
|
||||
|
||||
// Mixing and matching fails
|
||||
assert_noop!(Purchase::verify_signature(&alice(), &bob_signature()), Error::<Test>::InvalidSignature);
|
||||
assert_noop!(Purchase::verify_signature(&bob(), &alice_signature()), Error::<Test>::InvalidSignature);
|
||||
assert_noop!(
|
||||
Purchase::verify_signature(&alice(), &bob_signature()),
|
||||
Error::<Test>::InvalidSignature
|
||||
);
|
||||
assert_noop!(
|
||||
Purchase::verify_signature(&bob(), &alice_signature()),
|
||||
Error::<Test>::InvalidSignature
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -704,13 +764,21 @@ mod tests {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Wrong Origin
|
||||
assert_noop!(
|
||||
Purchase::create_account(Origin::signed(alice()), alice(), alice_signature().to_vec()),
|
||||
Purchase::create_account(
|
||||
Origin::signed(alice()),
|
||||
alice(),
|
||||
alice_signature().to_vec()
|
||||
),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// Wrong Account/Signature
|
||||
assert_noop!(
|
||||
Purchase::create_account(Origin::signed(validity_origin()), alice(), bob_signature().to_vec()),
|
||||
Purchase::create_account(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
bob_signature().to_vec()
|
||||
),
|
||||
Error::<Test>::InvalidSignature,
|
||||
);
|
||||
|
||||
@@ -722,16 +790,26 @@ mod tests {
|
||||
50
|
||||
));
|
||||
assert_noop!(
|
||||
Purchase::create_account(Origin::signed(validity_origin()), alice(), alice_signature().to_vec()),
|
||||
Purchase::create_account(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
alice_signature().to_vec()
|
||||
),
|
||||
Error::<Test>::VestingScheduleExists,
|
||||
);
|
||||
|
||||
// Duplicate Purchasing Account
|
||||
assert_ok!(
|
||||
Purchase::create_account(Origin::signed(validity_origin()), bob(), bob_signature().to_vec())
|
||||
);
|
||||
assert_ok!(Purchase::create_account(
|
||||
Origin::signed(validity_origin()),
|
||||
bob(),
|
||||
bob_signature().to_vec()
|
||||
));
|
||||
assert_noop!(
|
||||
Purchase::create_account(Origin::signed(validity_origin()), bob(), bob_signature().to_vec()),
|
||||
Purchase::create_account(
|
||||
Origin::signed(validity_origin()),
|
||||
bob(),
|
||||
bob_signature().to_vec()
|
||||
),
|
||||
Error::<Test>::ExistingAccount,
|
||||
);
|
||||
});
|
||||
@@ -791,17 +869,23 @@ mod tests {
|
||||
fn update_validity_status_handles_basic_errors() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Wrong Origin
|
||||
assert_noop!(Purchase::update_validity_status(
|
||||
Origin::signed(alice()),
|
||||
alice(),
|
||||
AccountValidity::Pending,
|
||||
), BadOrigin);
|
||||
assert_noop!(
|
||||
Purchase::update_validity_status(
|
||||
Origin::signed(alice()),
|
||||
alice(),
|
||||
AccountValidity::Pending,
|
||||
),
|
||||
BadOrigin
|
||||
);
|
||||
// Inactive Account
|
||||
assert_noop!(Purchase::update_validity_status(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
AccountValidity::Pending,
|
||||
), Error::<Test>::InvalidAccount);
|
||||
assert_noop!(
|
||||
Purchase::update_validity_status(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
AccountValidity::Pending,
|
||||
),
|
||||
Error::<Test>::InvalidAccount
|
||||
);
|
||||
// Already Completed
|
||||
assert_ok!(Purchase::create_account(
|
||||
Origin::signed(validity_origin()),
|
||||
@@ -813,11 +897,14 @@ mod tests {
|
||||
alice(),
|
||||
AccountValidity::Completed,
|
||||
));
|
||||
assert_noop!(Purchase::update_validity_status(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
AccountValidity::Pending,
|
||||
), Error::<Test>::AlreadyCompleted);
|
||||
assert_noop!(
|
||||
Purchase::update_validity_status(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
AccountValidity::Pending,
|
||||
),
|
||||
Error::<Test>::AlreadyCompleted
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -828,8 +915,8 @@ mod tests {
|
||||
assert_ok!(Purchase::create_account(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
alice_signature().to_vec())
|
||||
);
|
||||
alice_signature().to_vec()
|
||||
));
|
||||
// And approved for basic contribution
|
||||
assert_ok!(Purchase::update_validity_status(
|
||||
Origin::signed(validity_origin()),
|
||||
@@ -879,29 +966,32 @@ mod tests {
|
||||
fn update_balance_handles_basic_errors() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Wrong Origin
|
||||
assert_noop!(Purchase::update_balance(
|
||||
Origin::signed(alice()),
|
||||
alice(),
|
||||
50,
|
||||
50,
|
||||
Permill::zero(),
|
||||
), BadOrigin);
|
||||
assert_noop!(
|
||||
Purchase::update_balance(Origin::signed(alice()), alice(), 50, 50, Permill::zero(),),
|
||||
BadOrigin
|
||||
);
|
||||
// Inactive Account
|
||||
assert_noop!(Purchase::update_balance(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
50,
|
||||
50,
|
||||
Permill::zero(),
|
||||
), Error::<Test>::InvalidAccount);
|
||||
assert_noop!(
|
||||
Purchase::update_balance(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
50,
|
||||
50,
|
||||
Permill::zero(),
|
||||
),
|
||||
Error::<Test>::InvalidAccount
|
||||
);
|
||||
// Overflow
|
||||
assert_noop!(Purchase::update_balance(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
u64::MAX,
|
||||
u64::MAX,
|
||||
Permill::zero(),
|
||||
), Error::<Test>::InvalidAccount);
|
||||
assert_noop!(
|
||||
Purchase::update_balance(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
u64::MAX,
|
||||
u64::MAX,
|
||||
Permill::zero(),
|
||||
),
|
||||
Error::<Test>::InvalidAccount
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -912,13 +1002,13 @@ mod tests {
|
||||
assert_ok!(Purchase::create_account(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
alice_signature().to_vec())
|
||||
);
|
||||
alice_signature().to_vec()
|
||||
));
|
||||
assert_ok!(Purchase::create_account(
|
||||
Origin::signed(validity_origin()),
|
||||
bob(),
|
||||
bob_signature().to_vec())
|
||||
);
|
||||
bob_signature().to_vec()
|
||||
));
|
||||
// Alice is approved for basic contribution
|
||||
assert_ok!(Purchase::update_validity_status(
|
||||
Origin::signed(validity_origin()),
|
||||
@@ -947,14 +1037,8 @@ mod tests {
|
||||
Permill::zero(),
|
||||
));
|
||||
// Now we call payout for Alice and Bob.
|
||||
assert_ok!(Purchase::payout(
|
||||
Origin::signed(payment_account()),
|
||||
alice(),
|
||||
));
|
||||
assert_ok!(Purchase::payout(
|
||||
Origin::signed(payment_account()),
|
||||
bob(),
|
||||
));
|
||||
assert_ok!(Purchase::payout(Origin::signed(payment_account()), alice(),));
|
||||
assert_ok!(Purchase::payout(Origin::signed(payment_account()), bob(),));
|
||||
// Payment is made.
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(&payment_account()), 99_650);
|
||||
assert_eq!(<Test as Config>::Currency::free_balance(&alice()), 100);
|
||||
@@ -1003,33 +1087,30 @@ mod tests {
|
||||
fn payout_handles_basic_errors() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Wrong Origin
|
||||
assert_noop!(Purchase::payout(
|
||||
Origin::signed(alice()),
|
||||
alice(),
|
||||
), BadOrigin);
|
||||
assert_noop!(Purchase::payout(Origin::signed(alice()), alice(),), BadOrigin);
|
||||
// Account with Existing Vesting Schedule
|
||||
assert_ok!(<Test as Config>::VestingSchedule::add_vesting_schedule(
|
||||
&bob(), 100, 1, 50,
|
||||
));
|
||||
assert_noop!(Purchase::payout(
|
||||
Origin::signed(payment_account()),
|
||||
bob(),
|
||||
), Error::<Test>::VestingScheduleExists);
|
||||
assert_ok!(
|
||||
<Test as Config>::VestingSchedule::add_vesting_schedule(&bob(), 100, 1, 50,)
|
||||
);
|
||||
assert_noop!(
|
||||
Purchase::payout(Origin::signed(payment_account()), bob(),),
|
||||
Error::<Test>::VestingScheduleExists
|
||||
);
|
||||
// Invalid Account (never created)
|
||||
assert_noop!(Purchase::payout(
|
||||
Origin::signed(payment_account()),
|
||||
alice(),
|
||||
), Error::<Test>::InvalidAccount);
|
||||
assert_noop!(
|
||||
Purchase::payout(Origin::signed(payment_account()), alice(),),
|
||||
Error::<Test>::InvalidAccount
|
||||
);
|
||||
// Invalid Account (created, but not valid)
|
||||
assert_ok!(Purchase::create_account(
|
||||
Origin::signed(validity_origin()),
|
||||
alice(),
|
||||
alice_signature().to_vec())
|
||||
alice_signature().to_vec()
|
||||
));
|
||||
assert_noop!(
|
||||
Purchase::payout(Origin::signed(payment_account()), alice(),),
|
||||
Error::<Test>::InvalidAccount
|
||||
);
|
||||
assert_noop!(Purchase::payout(
|
||||
Origin::signed(payment_account()),
|
||||
alice(),
|
||||
), Error::<Test>::InvalidAccount);
|
||||
// Not enough funds in payment account
|
||||
assert_ok!(Purchase::update_validity_status(
|
||||
Origin::signed(validity_origin()),
|
||||
@@ -1043,10 +1124,10 @@ mod tests {
|
||||
100_000,
|
||||
Permill::zero(),
|
||||
));
|
||||
assert_noop!(Purchase::payout(
|
||||
Origin::signed(payment_account()),
|
||||
alice(),
|
||||
), BalancesError::<Test, _>::InsufficientBalance);
|
||||
assert_noop!(
|
||||
Purchase::payout(Origin::signed(payment_account()), alice(),),
|
||||
BalancesError::<Test, _>::InsufficientBalance
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,16 @@
|
||||
//! The `SlotRange` struct which succinctly handles the 36 values that
|
||||
//! represent all sub ranges between 0 and 7 inclusive.
|
||||
|
||||
slot_range_helper::generate_slot_range!(Zero(0), One(1), Two(2), Three(3), Four(4), Five(5), Six(6), Seven(7));
|
||||
slot_range_helper::generate_slot_range!(
|
||||
Zero(0),
|
||||
One(1),
|
||||
Two(2),
|
||||
Three(3),
|
||||
Four(4),
|
||||
Five(5),
|
||||
Six(6),
|
||||
Seven(7)
|
||||
);
|
||||
|
||||
// Will generate:
|
||||
// pub enum SlotRange {
|
||||
|
||||
@@ -209,9 +209,7 @@ pub mod pallet {
|
||||
// We can try to onboard it.
|
||||
Some(Some(_lease_info)) => T::Registrar::make_parachain(para)?,
|
||||
// Otherwise, it does not have a lease.
|
||||
Some(None) | None => {
|
||||
return Err(Error::<T>::ParaNotOnboarding.into());
|
||||
}
|
||||
Some(None) | None => return Err(Error::<T>::ParaNotOnboarding.into()),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
@@ -232,7 +230,7 @@ impl<T: Config> Pallet<T> {
|
||||
let mut parachains = Vec::new();
|
||||
for (para, mut lease_periods) in Leases::<T>::iter() {
|
||||
if lease_periods.is_empty() {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
// ^^ should never be empty since we would have deleted the entry otherwise.
|
||||
|
||||
@@ -307,16 +305,15 @@ impl<T: Config> Pallet<T> {
|
||||
let mut tracker = sp_std::collections::btree_map::BTreeMap::new();
|
||||
Leases::<T>::get(para).into_iter().for_each(|lease| match lease {
|
||||
Some((who, amount)) => match tracker.get(&who) {
|
||||
Some(prev_amount) => {
|
||||
Some(prev_amount) =>
|
||||
if amount > *prev_amount {
|
||||
tracker.insert(who, amount);
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
tracker.insert(who, amount);
|
||||
}
|
||||
},
|
||||
},
|
||||
None => {}
|
||||
None => {},
|
||||
});
|
||||
|
||||
tracker.into_iter().collect()
|
||||
@@ -375,7 +372,7 @@ impl<T: Config> Leaser for Pallet<T> {
|
||||
// attempt.
|
||||
//
|
||||
// We bail, not giving any lease and leave it for governance to sort out.
|
||||
return Err(LeaseError::AlreadyLeased);
|
||||
return Err(LeaseError::AlreadyLeased)
|
||||
}
|
||||
} else if d.len() == i {
|
||||
// Doesn't exist. This is usual.
|
||||
@@ -423,13 +420,12 @@ impl<T: Config> Leaser for Pallet<T> {
|
||||
Leases::<T>::get(para)
|
||||
.into_iter()
|
||||
.map(|lease| match lease {
|
||||
Some((who, amount)) => {
|
||||
Some((who, amount)) =>
|
||||
if &who == leaser {
|
||||
amount
|
||||
} else {
|
||||
Zero::zero()
|
||||
}
|
||||
}
|
||||
},
|
||||
None => Zero::zero(),
|
||||
})
|
||||
.max()
|
||||
@@ -471,7 +467,7 @@ impl<T: Config> Leaser for Pallet<T> {
|
||||
for slot in offset..=offset + period_count {
|
||||
if let Some(Some(_)) = leases.get(slot) {
|
||||
// If there exists any lease period, we exit early and return true.
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
//! Traits used across pallets for Polkadot.
|
||||
|
||||
use sp_std::vec::*;
|
||||
use primitives::v1::{HeadData, ValidationCode, Id as ParaId};
|
||||
use frame_support::{
|
||||
dispatch::DispatchResult,
|
||||
traits::{Currency, ReservableCurrency},
|
||||
};
|
||||
use primitives::v1::{HeadData, Id as ParaId, ValidationCode};
|
||||
use sp_std::vec::*;
|
||||
|
||||
/// Parachain registration API.
|
||||
pub trait Registrar {
|
||||
@@ -128,7 +128,10 @@ pub trait Leaser {
|
||||
|
||||
/// Return the amount of balance currently held in reserve on `leaser`'s account for leasing `para`. This won't
|
||||
/// go down outside of a lease period.
|
||||
fn deposit_held(para: ParaId, leaser: &Self::AccountId) -> <Self::Currency as Currency<Self::AccountId>>::Balance;
|
||||
fn deposit_held(
|
||||
para: ParaId,
|
||||
leaser: &Self::AccountId,
|
||||
) -> <Self::Currency as Currency<Self::AccountId>>::Balance;
|
||||
|
||||
/// The lease period. This is constant, but can't be a `const` due to it being a runtime configurable quantity.
|
||||
fn lease_period() -> Self::LeasePeriod;
|
||||
@@ -141,7 +144,7 @@ pub trait Leaser {
|
||||
fn already_leased(
|
||||
para_id: ParaId,
|
||||
first_period: Self::LeasePeriod,
|
||||
last_period: Self::LeasePeriod
|
||||
last_period: Self::LeasePeriod,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
@@ -204,7 +207,10 @@ pub trait Auctioneer {
|
||||
/// This can only happen when there isn't already an auction in progress. Accepts the `duration`
|
||||
/// of this auction and the `lease_period_index` of the initial lease period of the four that
|
||||
/// are to be auctioned.
|
||||
fn new_auction(duration: Self::BlockNumber, lease_period_index: Self::LeasePeriod) -> DispatchResult;
|
||||
fn new_auction(
|
||||
duration: Self::BlockNumber,
|
||||
lease_period_index: Self::LeasePeriod,
|
||||
) -> DispatchResult;
|
||||
|
||||
/// Given the current block number, return the current auction status.
|
||||
fn auction_status(now: Self::BlockNumber) -> AuctionStatus<Self::BlockNumber>;
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
//! XCM sender for relay chain.
|
||||
|
||||
use parity_scale_codec::Encode;
|
||||
use sp_std::marker::PhantomData;
|
||||
use xcm::opaque::{VersionedXcm, v0::{SendXcm, MultiLocation, Junction, Xcm, Result, Error}};
|
||||
use runtime_parachains::{configuration, dmp};
|
||||
use sp_std::marker::PhantomData;
|
||||
use xcm::opaque::{
|
||||
v0::{Error, Junction, MultiLocation, Result, SendXcm, Xcm},
|
||||
VersionedXcm,
|
||||
};
|
||||
|
||||
/// XCM sender for relay chain. It only sends downward message.
|
||||
pub struct ChildParachainRouter<T>(PhantomData<T>);
|
||||
@@ -34,9 +37,10 @@ impl<T: configuration::Config + dmp::Config> SendXcm for ChildParachainRouter<T>
|
||||
&config,
|
||||
id.into(),
|
||||
VersionedXcm::from(msg).encode(),
|
||||
).map_err(Into::<Error>::into)?;
|
||||
)
|
||||
.map_err(Into::<Error>::into)?;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
d => Err(Error::CannotReachDestination(d, msg)),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user