feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
[package]
|
||||
name = "pezpallet-lottery"
|
||||
version = "28.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "FRAME Participation Lottery Pallet"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive"], workspace = true }
|
||||
pezframe-benchmarking = { optional = true, workspace = true }
|
||||
pezframe-support = { workspace = true }
|
||||
pezframe-system = { workspace = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pezframe-support-test = { workspace = true }
|
||||
pezpallet-balances = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true, default-features = true }
|
||||
pezsp-io = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"pezframe-benchmarking?/std",
|
||||
"pezframe-support-test/std",
|
||||
"pezframe-support/std",
|
||||
"pezframe-system/std",
|
||||
"pezpallet-balances/std",
|
||||
"scale-info/std",
|
||||
"pezsp-core/std",
|
||||
"pezsp-io/std",
|
||||
"pezsp-runtime/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"pezframe-benchmarking/runtime-benchmarks",
|
||||
"pezframe-support-test/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezframe-system/runtime-benchmarks",
|
||||
"pezpallet-balances/runtime-benchmarks",
|
||||
"pezsp-io/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"pezframe-support-test/try-runtime",
|
||||
"pezframe-support/try-runtime",
|
||||
"pezframe-system/try-runtime",
|
||||
"pezpallet-balances/try-runtime",
|
||||
"pezsp-runtime/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1,218 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Lottery pallet benchmarking.
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::Pallet as Lottery;
|
||||
use alloc::{boxed::Box, vec};
|
||||
use pezframe_benchmarking::{
|
||||
v1::{account, whitelisted_caller, BenchmarkError},
|
||||
v2::*,
|
||||
};
|
||||
use pezframe_support::{
|
||||
storage::bounded_vec::BoundedVec,
|
||||
traits::{EnsureOrigin, OnInitialize},
|
||||
};
|
||||
use pezframe_system::RawOrigin;
|
||||
use pezsp_runtime::traits::{Bounded, Zero};
|
||||
|
||||
// Set up and start a lottery
|
||||
fn setup_lottery<T: Config>(repeat: bool) -> Result<(), &'static str> {
|
||||
let price = T::Currency::minimum_balance();
|
||||
let length = 10u32.into();
|
||||
let delay = 5u32.into();
|
||||
// Calls will be maximum length...
|
||||
let mut calls = vec![
|
||||
pezframe_system::Call::<T>::set_code { code: vec![] }.into();
|
||||
T::MaxCalls::get().saturating_sub(1) as usize
|
||||
];
|
||||
// Last call will be the match for worst case scenario.
|
||||
calls.push(pezframe_system::Call::<T>::remark { remark: vec![] }.into());
|
||||
let origin = T::ManagerOrigin::try_successful_origin()
|
||||
.expect("ManagerOrigin has no successful origin required for the benchmark");
|
||||
Lottery::<T>::set_calls(origin.clone(), calls)?;
|
||||
Lottery::<T>::start_lottery(origin, price, length, delay, repeat)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn buy_ticket() -> Result<(), BenchmarkError> {
|
||||
let caller = whitelisted_caller();
|
||||
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||
setup_lottery::<T>(false)?;
|
||||
// force user to have a long vec of calls participating
|
||||
let set_code_index: CallIndex = Lottery::<T>::call_to_index(
|
||||
&pezframe_system::Call::<T>::set_code { code: vec![] }.into(),
|
||||
)?;
|
||||
let already_called: (u32, BoundedVec<CallIndex, T::MaxCalls>) = (
|
||||
LotteryIndex::<T>::get(),
|
||||
BoundedVec::<CallIndex, T::MaxCalls>::try_from(vec![
|
||||
set_code_index;
|
||||
T::MaxCalls::get().saturating_sub(1)
|
||||
as usize
|
||||
])
|
||||
.unwrap(),
|
||||
);
|
||||
Participants::<T>::insert(&caller, already_called);
|
||||
|
||||
let call = pezframe_system::Call::<T>::remark { remark: vec![] };
|
||||
|
||||
#[extrinsic_call]
|
||||
_(RawOrigin::Signed(caller), Box::new(call.into()));
|
||||
|
||||
assert_eq!(TicketsCount::<T>::get(), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn set_calls(n: Linear<0, { T::MaxCalls::get() }>) -> Result<(), BenchmarkError> {
|
||||
let calls = vec![pezframe_system::Call::<T>::remark { remark: vec![] }.into(); n as usize];
|
||||
let origin =
|
||||
T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
|
||||
assert!(CallIndices::<T>::get().is_empty());
|
||||
|
||||
#[extrinsic_call]
|
||||
_(origin as T::RuntimeOrigin, calls);
|
||||
|
||||
if !n.is_zero() {
|
||||
assert!(!CallIndices::<T>::get().is_empty());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn start_lottery() -> Result<(), BenchmarkError> {
|
||||
let price = BalanceOf::<T>::max_value();
|
||||
let end = 10u32.into();
|
||||
let payout = 5u32.into();
|
||||
let origin =
|
||||
T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
|
||||
|
||||
#[extrinsic_call]
|
||||
_(origin as T::RuntimeOrigin, price, end, payout, true);
|
||||
|
||||
assert!(crate::Lottery::<T>::get().is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn stop_repeat() -> Result<(), BenchmarkError> {
|
||||
setup_lottery::<T>(true)?;
|
||||
assert_eq!(crate::Lottery::<T>::get().unwrap().repeat, true);
|
||||
let origin =
|
||||
T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
|
||||
|
||||
#[extrinsic_call]
|
||||
_(origin as T::RuntimeOrigin);
|
||||
|
||||
assert_eq!(crate::Lottery::<T>::get().unwrap().repeat, false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn on_initialize_end() -> Result<(), BenchmarkError> {
|
||||
setup_lottery::<T>(false)?;
|
||||
let winner = account("winner", 0, 0);
|
||||
// User needs more than min balance to get ticket
|
||||
T::Currency::make_free_balance_be(&winner, T::Currency::minimum_balance() * 10u32.into());
|
||||
// Make sure lottery account has at least min balance too
|
||||
let lottery_account = Lottery::<T>::account_id();
|
||||
T::Currency::make_free_balance_be(
|
||||
&lottery_account,
|
||||
T::Currency::minimum_balance() * 10u32.into(),
|
||||
);
|
||||
// Buy a ticket
|
||||
let call = pezframe_system::Call::<T>::remark { remark: vec![] };
|
||||
Lottery::<T>::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?;
|
||||
// Kill user account for worst case
|
||||
T::Currency::make_free_balance_be(&winner, 0u32.into());
|
||||
// Assert that lotto is set up for winner
|
||||
assert_eq!(TicketsCount::<T>::get(), 1);
|
||||
assert!(!Lottery::<T>::pot().1.is_zero());
|
||||
|
||||
#[block]
|
||||
{
|
||||
// Generate `MaxGenerateRandom` numbers for worst case scenario
|
||||
for i in 0..T::MaxGenerateRandom::get() {
|
||||
Lottery::<T>::generate_random_number(i);
|
||||
}
|
||||
// Start lottery has block 15 configured for payout
|
||||
Lottery::<T>::on_initialize(15u32.into());
|
||||
}
|
||||
|
||||
assert!(crate::Lottery::<T>::get().is_none());
|
||||
assert_eq!(TicketsCount::<T>::get(), 0);
|
||||
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
|
||||
assert!(!T::Currency::free_balance(&winner).is_zero());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn on_initialize_repeat() -> Result<(), BenchmarkError> {
|
||||
setup_lottery::<T>(true)?;
|
||||
let winner = account("winner", 0, 0);
|
||||
// User needs more than min balance to get ticket
|
||||
T::Currency::make_free_balance_be(&winner, T::Currency::minimum_balance() * 10u32.into());
|
||||
// Make sure lottery account has at least min balance too
|
||||
let lottery_account = Lottery::<T>::account_id();
|
||||
T::Currency::make_free_balance_be(
|
||||
&lottery_account,
|
||||
T::Currency::minimum_balance() * 10u32.into(),
|
||||
);
|
||||
// Buy a ticket
|
||||
let call = pezframe_system::Call::<T>::remark { remark: vec![] };
|
||||
Lottery::<T>::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?;
|
||||
// Kill user account for worst case
|
||||
T::Currency::make_free_balance_be(&winner, 0u32.into());
|
||||
// Assert that lotto is set up for winner
|
||||
assert_eq!(TicketsCount::<T>::get(), 1);
|
||||
assert!(!Lottery::<T>::pot().1.is_zero());
|
||||
|
||||
#[block]
|
||||
{
|
||||
// Generate `MaxGenerateRandom` numbers for worst case scenario
|
||||
for i in 0..T::MaxGenerateRandom::get() {
|
||||
Lottery::<T>::generate_random_number(i);
|
||||
}
|
||||
// Start lottery has block 15 configured for payout
|
||||
Lottery::<T>::on_initialize(15u32.into());
|
||||
}
|
||||
|
||||
assert!(crate::Lottery::<T>::get().is_some());
|
||||
assert_eq!(LotteryIndex::<T>::get(), 2);
|
||||
assert_eq!(TicketsCount::<T>::get(), 0);
|
||||
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
|
||||
assert!(!T::Currency::free_balance(&winner).is_zero());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Lottery, crate::mock::new_test_ext(), crate::mock::Test);
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! A lottery pallet that uses participation in the network to purchase tickets.
|
||||
//!
|
||||
//! With this pallet, you can configure a lottery, which is a pot of money that
|
||||
//! users contribute to, and that is reallocated to a single user at the end of
|
||||
//! the lottery period. Just like a normal lottery system, to participate, you
|
||||
//! need to "buy a ticket", which is used to fund the pot.
|
||||
//!
|
||||
//! The unique feature of this lottery system is that tickets can only be
|
||||
//! purchased by making a "valid call" dispatched through this pallet.
|
||||
//! By configuring certain calls to be valid for the lottery, you can encourage
|
||||
//! users to make those calls on your network. An example of how this could be
|
||||
//! used is to set validator nominations as a valid lottery call. If the lottery
|
||||
//! is set to repeat every month, then users would be encouraged to re-nominate
|
||||
//! validators every month. A user can only purchase one ticket per valid call
|
||||
//! per lottery.
|
||||
//!
|
||||
//! This pallet can be configured to use dynamically set calls or statically set
|
||||
//! calls. Call validation happens through the `ValidateCall` implementation.
|
||||
//! This pallet provides one implementation of this using the `CallIndices`
|
||||
//! storage item. You can also make your own implementation at the runtime level
|
||||
//! which can contain much more complex logic, such as validation of the
|
||||
//! parameters, which this pallet alone cannot do.
|
||||
//!
|
||||
//! This pallet uses the modulus operator to pick a random winner. It is known
|
||||
//! that this might introduce a bias if the random number chosen in a range that
|
||||
//! is not perfectly divisible by the total number of participants. The
|
||||
//! `MaxGenerateRandom` configuration can help mitigate this by generating new
|
||||
//! numbers until we hit the limit or we find a "fair" number. This is best
|
||||
//! effort only.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
mod benchmarking;
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod weights;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use codec::{Decode, Encode};
|
||||
use pezframe_support::{
|
||||
dispatch::{DispatchResult, GetDispatchInfo},
|
||||
ensure,
|
||||
pezpallet_prelude::MaxEncodedLen,
|
||||
storage::bounded_vec::BoundedVec,
|
||||
traits::{Currency, ExistenceRequirement::KeepAlive, Get, Randomness, ReservableCurrency},
|
||||
PalletId,
|
||||
};
|
||||
pub use pallet::*;
|
||||
use pezsp_runtime::{
|
||||
traits::{AccountIdConversion, Dispatchable, Saturating, Zero},
|
||||
ArithmeticError, DispatchError, RuntimeDebug,
|
||||
};
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
type BalanceOf<T> =
|
||||
<<T as Config>::Currency as Currency<<T as pezframe_system::Config>::AccountId>>::Balance;
|
||||
|
||||
// Any runtime call can be encoded into two bytes which represent the pallet and call index.
|
||||
// We use this to uniquely match someone's incoming call with the calls configured for the lottery.
|
||||
type CallIndex = (u8, u8);
|
||||
|
||||
#[derive(
|
||||
Encode, Decode, Default, Eq, PartialEq, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen,
|
||||
)]
|
||||
pub struct LotteryConfig<BlockNumber, Balance> {
|
||||
/// Price per entry.
|
||||
price: Balance,
|
||||
/// Starting block of the lottery.
|
||||
start: BlockNumber,
|
||||
/// Length of the lottery (start + length = end).
|
||||
length: BlockNumber,
|
||||
/// Delay for choosing the winner of the lottery. (start + length + delay = payout).
|
||||
/// Randomness in the "payout" block will be used to determine the winner.
|
||||
delay: BlockNumber,
|
||||
/// Whether this lottery will repeat after it completes.
|
||||
repeat: bool,
|
||||
}
|
||||
|
||||
pub trait ValidateCall<T: Config> {
|
||||
fn validate_call(call: &<T as Config>::RuntimeCall) -> bool;
|
||||
}
|
||||
|
||||
impl<T: Config> ValidateCall<T> for () {
|
||||
fn validate_call(_: &<T as Config>::RuntimeCall) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> ValidateCall<T> for Pallet<T> {
|
||||
fn validate_call(call: &<T as Config>::RuntimeCall) -> bool {
|
||||
let valid_calls = CallIndices::<T>::get();
|
||||
let call_index = match Self::call_to_index(call) {
|
||||
Ok(call_index) => call_index,
|
||||
Err(_) => return false,
|
||||
};
|
||||
valid_calls.iter().any(|c| call_index == *c)
|
||||
}
|
||||
}
|
||||
|
||||
#[pezframe_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use pezframe_support::pezpallet_prelude::*;
|
||||
use pezframe_system::pezpallet_prelude::*;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
/// The pallet's config trait.
|
||||
#[pallet::config]
|
||||
pub trait Config: pezframe_system::Config {
|
||||
/// The Lottery's pallet id
|
||||
#[pallet::constant]
|
||||
type PalletId: Get<PalletId>;
|
||||
|
||||
/// A dispatchable call.
|
||||
type RuntimeCall: Parameter
|
||||
+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
|
||||
+ GetDispatchInfo
|
||||
+ From<pezframe_system::Call<Self>>;
|
||||
|
||||
/// The currency trait.
|
||||
type Currency: ReservableCurrency<Self::AccountId>;
|
||||
|
||||
/// Something that provides randomness in the runtime.
|
||||
type Randomness: Randomness<Self::Hash, BlockNumberFor<Self>>;
|
||||
|
||||
/// The overarching event type.
|
||||
#[allow(deprecated)]
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
|
||||
|
||||
/// The manager origin.
|
||||
type ManagerOrigin: EnsureOrigin<Self::RuntimeOrigin>;
|
||||
|
||||
/// The max number of calls available in a single lottery.
|
||||
#[pallet::constant]
|
||||
type MaxCalls: Get<u32>;
|
||||
|
||||
/// Used to determine if a call would be valid for purchasing a ticket.
|
||||
///
|
||||
/// Be conscious of the implementation used here. We assume at worst that
|
||||
/// a vector of `MaxCalls` indices are queried for any call validation.
|
||||
/// You may need to provide a custom benchmark if this assumption is broken.
|
||||
type ValidateCall: ValidateCall<Self>;
|
||||
|
||||
/// Number of time we should try to generate a random number that has no modulo bias.
|
||||
/// The larger this number, the more potential computation is used for picking the winner,
|
||||
/// but also the more likely that the chosen winner is done fairly.
|
||||
#[pallet::constant]
|
||||
type MaxGenerateRandom: Get<u32>;
|
||||
|
||||
/// Weight information for extrinsics in this pallet.
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// A lottery has been started!
|
||||
LotteryStarted,
|
||||
/// A new set of calls have been set!
|
||||
CallsUpdated,
|
||||
/// A winner has been chosen!
|
||||
Winner { winner: T::AccountId, lottery_balance: BalanceOf<T> },
|
||||
/// A ticket has been bought!
|
||||
TicketBought { who: T::AccountId, call_index: CallIndex },
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// A lottery has not been configured.
|
||||
NotConfigured,
|
||||
/// A lottery is already in progress.
|
||||
InProgress,
|
||||
/// A lottery has already ended.
|
||||
AlreadyEnded,
|
||||
/// The call is not valid for an open lottery.
|
||||
InvalidCall,
|
||||
/// You are already participating in the lottery with this call.
|
||||
AlreadyParticipating,
|
||||
/// Too many calls for a single lottery.
|
||||
TooManyCalls,
|
||||
/// Failed to encode calls
|
||||
EncodingFailed,
|
||||
}
|
||||
|
||||
#[pallet::storage]
|
||||
pub(crate) type LotteryIndex<T> = StorageValue<_, u32, ValueQuery>;
|
||||
|
||||
/// The configuration for the current lottery.
|
||||
#[pallet::storage]
|
||||
pub(crate) type Lottery<T: Config> =
|
||||
StorageValue<_, LotteryConfig<BlockNumberFor<T>, BalanceOf<T>>>;
|
||||
|
||||
/// Users who have purchased a ticket. (Lottery Index, Tickets Purchased)
|
||||
#[pallet::storage]
|
||||
pub(crate) type Participants<T: Config> = StorageMap<
|
||||
_,
|
||||
Twox64Concat,
|
||||
T::AccountId,
|
||||
(u32, BoundedVec<CallIndex, T::MaxCalls>),
|
||||
ValueQuery,
|
||||
>;
|
||||
|
||||
/// Total number of tickets sold.
|
||||
#[pallet::storage]
|
||||
pub(crate) type TicketsCount<T> = StorageValue<_, u32, ValueQuery>;
|
||||
|
||||
/// Each ticket's owner.
|
||||
///
|
||||
/// May have residual storage from previous lotteries. Use `TicketsCount` to see which ones
|
||||
/// are actually valid ticket mappings.
|
||||
#[pallet::storage]
|
||||
pub(crate) type Tickets<T: Config> = StorageMap<_, Twox64Concat, u32, T::AccountId>;
|
||||
|
||||
/// The calls stored in this pallet to be used in an active lottery if configured
|
||||
/// by `Config::ValidateCall`.
|
||||
#[pallet::storage]
|
||||
pub(crate) type CallIndices<T: Config> =
|
||||
StorageValue<_, BoundedVec<CallIndex, T::MaxCalls>, ValueQuery>;
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||
fn on_initialize(n: BlockNumberFor<T>) -> Weight {
|
||||
Lottery::<T>::mutate(|mut lottery| -> Weight {
|
||||
if let Some(config) = &mut lottery {
|
||||
let payout_block =
|
||||
config.start.saturating_add(config.length).saturating_add(config.delay);
|
||||
if payout_block <= n {
|
||||
let (lottery_account, lottery_balance) = Self::pot();
|
||||
|
||||
let winner = Self::choose_account().unwrap_or(lottery_account);
|
||||
// Not much we can do if this fails...
|
||||
let res = T::Currency::transfer(
|
||||
&Self::account_id(),
|
||||
&winner,
|
||||
lottery_balance,
|
||||
KeepAlive,
|
||||
);
|
||||
debug_assert!(res.is_ok());
|
||||
|
||||
Self::deposit_event(Event::<T>::Winner { winner, lottery_balance });
|
||||
|
||||
TicketsCount::<T>::kill();
|
||||
|
||||
if config.repeat {
|
||||
// If lottery should repeat, increment index by 1.
|
||||
LotteryIndex::<T>::mutate(|index| *index = index.saturating_add(1));
|
||||
// Set a new start with the current block.
|
||||
config.start = n;
|
||||
return T::WeightInfo::on_initialize_repeat();
|
||||
} else {
|
||||
// Else, kill the lottery storage.
|
||||
*lottery = None;
|
||||
return T::WeightInfo::on_initialize_end();
|
||||
}
|
||||
// We choose not need to kill Participants and Tickets to avoid a large
|
||||
// number of writes at one time. Instead, data persists between lotteries,
|
||||
// but is not used if it is not relevant.
|
||||
}
|
||||
}
|
||||
T::DbWeight::get().reads(1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Buy a ticket to enter the lottery.
|
||||
///
|
||||
/// This extrinsic acts as a passthrough function for `call`. In all
|
||||
/// situations where `call` alone would succeed, this extrinsic should
|
||||
/// succeed.
|
||||
///
|
||||
/// If `call` is successful, then we will attempt to purchase a ticket,
|
||||
/// which may fail silently. To detect success of a ticket purchase, you
|
||||
/// should listen for the `TicketBought` event.
|
||||
///
|
||||
/// This extrinsic must be called by a signed origin.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(
|
||||
T::WeightInfo::buy_ticket()
|
||||
.saturating_add(call.get_dispatch_info().call_weight)
|
||||
)]
|
||||
pub fn buy_ticket(
|
||||
origin: OriginFor<T>,
|
||||
call: Box<<T as Config>::RuntimeCall>,
|
||||
) -> DispatchResult {
|
||||
let caller = ensure_signed(origin.clone())?;
|
||||
call.clone().dispatch(origin).map_err(|e| e.error)?;
|
||||
|
||||
let _ = Self::do_buy_ticket(&caller, &call);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set calls in storage which can be used to purchase a lottery ticket.
|
||||
///
|
||||
/// This function only matters if you use the `ValidateCall` implementation
|
||||
/// provided by this pallet, which uses storage to determine the valid calls.
|
||||
///
|
||||
/// This extrinsic must be called by the Manager origin.
|
||||
#[pallet::call_index(1)]
|
||||
#[pallet::weight(T::WeightInfo::set_calls(calls.len() as u32))]
|
||||
pub fn set_calls(
|
||||
origin: OriginFor<T>,
|
||||
calls: Vec<<T as Config>::RuntimeCall>,
|
||||
) -> DispatchResult {
|
||||
T::ManagerOrigin::ensure_origin(origin)?;
|
||||
ensure!(calls.len() <= T::MaxCalls::get() as usize, Error::<T>::TooManyCalls);
|
||||
if calls.is_empty() {
|
||||
CallIndices::<T>::kill();
|
||||
} else {
|
||||
let indices = Self::calls_to_indices(&calls)?;
|
||||
CallIndices::<T>::put(indices);
|
||||
}
|
||||
Self::deposit_event(Event::<T>::CallsUpdated);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start a lottery using the provided configuration.
|
||||
///
|
||||
/// This extrinsic must be called by the `ManagerOrigin`.
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * `price`: The cost of a single ticket.
|
||||
/// * `length`: How long the lottery should run for starting at the current block.
|
||||
/// * `delay`: How long after the lottery end we should wait before picking a winner.
|
||||
/// * `repeat`: If the lottery should repeat when completed.
|
||||
#[pallet::call_index(2)]
|
||||
#[pallet::weight(T::WeightInfo::start_lottery())]
|
||||
pub fn start_lottery(
|
||||
origin: OriginFor<T>,
|
||||
price: BalanceOf<T>,
|
||||
length: BlockNumberFor<T>,
|
||||
delay: BlockNumberFor<T>,
|
||||
repeat: bool,
|
||||
) -> DispatchResult {
|
||||
T::ManagerOrigin::ensure_origin(origin)?;
|
||||
Lottery::<T>::try_mutate(|lottery| -> DispatchResult {
|
||||
ensure!(lottery.is_none(), Error::<T>::InProgress);
|
||||
let index = LotteryIndex::<T>::get();
|
||||
let new_index = index.checked_add(1).ok_or(ArithmeticError::Overflow)?;
|
||||
let start = pezframe_system::Pallet::<T>::block_number();
|
||||
// Use new_index to more easily track everything with the current state.
|
||||
*lottery = Some(LotteryConfig { price, start, length, delay, repeat });
|
||||
LotteryIndex::<T>::put(new_index);
|
||||
Ok(())
|
||||
})?;
|
||||
// Make sure pot exists.
|
||||
let lottery_account = Self::account_id();
|
||||
if T::Currency::total_balance(&lottery_account).is_zero() {
|
||||
let _ =
|
||||
T::Currency::deposit_creating(&lottery_account, T::Currency::minimum_balance());
|
||||
}
|
||||
Self::deposit_event(Event::<T>::LotteryStarted);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If a lottery is repeating, you can use this to stop the repeat.
|
||||
/// The lottery will continue to run to completion.
|
||||
///
|
||||
/// This extrinsic must be called by the `ManagerOrigin`.
|
||||
#[pallet::call_index(3)]
|
||||
#[pallet::weight(T::WeightInfo::stop_repeat())]
|
||||
pub fn stop_repeat(origin: OriginFor<T>) -> DispatchResult {
|
||||
T::ManagerOrigin::ensure_origin(origin)?;
|
||||
Lottery::<T>::mutate(|mut lottery| {
|
||||
if let Some(config) = &mut lottery {
|
||||
config.repeat = false
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// The account ID of the lottery pot.
|
||||
///
|
||||
/// This actually does computation. If you need to keep using it, then make sure you cache the
|
||||
/// value and only call this once.
|
||||
pub fn account_id() -> T::AccountId {
|
||||
T::PalletId::get().into_account_truncating()
|
||||
}
|
||||
|
||||
/// Return the pot account and amount of money in the pot.
|
||||
/// The existential deposit is not part of the pot so lottery account never gets deleted.
|
||||
fn pot() -> (T::AccountId, BalanceOf<T>) {
|
||||
let account_id = Self::account_id();
|
||||
let balance =
|
||||
T::Currency::free_balance(&account_id).saturating_sub(T::Currency::minimum_balance());
|
||||
|
||||
(account_id, balance)
|
||||
}
|
||||
|
||||
/// Converts a vector of calls into a vector of call indices.
|
||||
fn calls_to_indices(
|
||||
calls: &[<T as Config>::RuntimeCall],
|
||||
) -> Result<BoundedVec<CallIndex, T::MaxCalls>, DispatchError> {
|
||||
let mut indices = BoundedVec::<CallIndex, T::MaxCalls>::with_bounded_capacity(calls.len());
|
||||
for c in calls.iter() {
|
||||
let index = Self::call_to_index(c)?;
|
||||
indices.try_push(index).map_err(|_| Error::<T>::TooManyCalls)?;
|
||||
}
|
||||
Ok(indices)
|
||||
}
|
||||
|
||||
/// Convert a call to it's call index by encoding the call and taking the first two bytes.
|
||||
fn call_to_index(call: &<T as Config>::RuntimeCall) -> Result<CallIndex, DispatchError> {
|
||||
let encoded_call = call.encode();
|
||||
if encoded_call.len() < 2 {
|
||||
return Err(Error::<T>::EncodingFailed.into());
|
||||
}
|
||||
Ok((encoded_call[0], encoded_call[1]))
|
||||
}
|
||||
|
||||
/// Logic for buying a ticket.
|
||||
fn do_buy_ticket(caller: &T::AccountId, call: &<T as Config>::RuntimeCall) -> DispatchResult {
|
||||
// Check the call is valid lottery
|
||||
let config = Lottery::<T>::get().ok_or(Error::<T>::NotConfigured)?;
|
||||
let block_number = pezframe_system::Pallet::<T>::block_number();
|
||||
ensure!(
|
||||
block_number < config.start.saturating_add(config.length),
|
||||
Error::<T>::AlreadyEnded
|
||||
);
|
||||
ensure!(T::ValidateCall::validate_call(call), Error::<T>::InvalidCall);
|
||||
let call_index = Self::call_to_index(call)?;
|
||||
let ticket_count = TicketsCount::<T>::get();
|
||||
let new_ticket_count = ticket_count.checked_add(1).ok_or(ArithmeticError::Overflow)?;
|
||||
// Try to update the participant status
|
||||
Participants::<T>::try_mutate(
|
||||
&caller,
|
||||
|(lottery_index, participating_calls)| -> DispatchResult {
|
||||
let index = LotteryIndex::<T>::get();
|
||||
// If lottery index doesn't match, then reset participating calls and index.
|
||||
if *lottery_index != index {
|
||||
*participating_calls = Default::default();
|
||||
*lottery_index = index;
|
||||
} else {
|
||||
// Check that user is not already participating under this call.
|
||||
ensure!(
|
||||
!participating_calls.iter().any(|c| call_index == *c),
|
||||
Error::<T>::AlreadyParticipating
|
||||
);
|
||||
}
|
||||
participating_calls.try_push(call_index).map_err(|_| Error::<T>::TooManyCalls)?;
|
||||
// Check user has enough funds and send it to the Lottery account.
|
||||
T::Currency::transfer(caller, &Self::account_id(), config.price, KeepAlive)?;
|
||||
// Create a new ticket.
|
||||
TicketsCount::<T>::put(new_ticket_count);
|
||||
Tickets::<T>::insert(ticket_count, caller.clone());
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
Self::deposit_event(Event::<T>::TicketBought { who: caller.clone(), call_index });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Randomly choose a winning ticket and return the account that purchased it.
|
||||
/// The more tickets an account bought, the higher are its chances of winning.
|
||||
/// Returns `None` if there is no winner.
|
||||
fn choose_account() -> Option<T::AccountId> {
|
||||
match Self::choose_ticket(TicketsCount::<T>::get()) {
|
||||
None => None,
|
||||
Some(ticket) => Tickets::<T>::get(ticket),
|
||||
}
|
||||
}
|
||||
|
||||
/// Randomly choose a winning ticket from among the total number of tickets.
|
||||
/// Returns `None` if there are no tickets.
|
||||
fn choose_ticket(total: u32) -> Option<u32> {
|
||||
if total == 0 {
|
||||
return None;
|
||||
}
|
||||
let mut random_number = Self::generate_random_number(0);
|
||||
|
||||
// Best effort attempt to remove bias from modulus operator.
|
||||
for i in 1..T::MaxGenerateRandom::get() {
|
||||
if random_number < u32::MAX - u32::MAX % total {
|
||||
break;
|
||||
}
|
||||
|
||||
random_number = Self::generate_random_number(i);
|
||||
}
|
||||
|
||||
Some(random_number % total)
|
||||
}
|
||||
|
||||
/// Generate a random number from a given seed.
|
||||
/// Note that there is potential bias introduced by using modulus operator.
|
||||
/// You should call this function with different seed values until the random
|
||||
/// number lies within `u32::MAX - u32::MAX % n`.
|
||||
/// TODO: deal with randomness freshness
|
||||
/// https://github.com/pezkuwichain/kurdistan-sdk/issues/33
|
||||
fn generate_random_number(seed: u32) -> u32 {
|
||||
let (random_seed, _) = T::Randomness::random(&(T::PalletId::get(), seed).encode());
|
||||
let random_number = <u32>::decode(&mut random_seed.as_ref())
|
||||
.expect("secure hashes should always be bigger than u32; qed");
|
||||
random_number
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Test utilities
|
||||
|
||||
use super::*;
|
||||
use crate as pezpallet_lottery;
|
||||
|
||||
use pezframe_support::{derive_impl, parameter_types, traits::ConstU32};
|
||||
use pezframe_support_test::TestRandomness;
|
||||
use pezframe_system::EnsureRoot;
|
||||
use pezsp_runtime::{BuildStorage, Perbill};
|
||||
|
||||
type Block = pezframe_system::mocking::MockBlock<Test>;
|
||||
|
||||
pezframe_support::construct_runtime!(
|
||||
pub enum Test
|
||||
{
|
||||
System: pezframe_system,
|
||||
Balances: pezpallet_balances,
|
||||
Lottery: pezpallet_lottery,
|
||||
}
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||
}
|
||||
|
||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
||||
impl pezframe_system::Config for Test {
|
||||
type Block = Block;
|
||||
type AccountData = pezpallet_balances::AccountData<u64>;
|
||||
}
|
||||
|
||||
#[derive_impl(pezpallet_balances::config_preludes::TestDefaultConfig)]
|
||||
impl pezpallet_balances::Config for Test {
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const LotteryPalletId: PalletId = PalletId(*b"py/lotto");
|
||||
}
|
||||
|
||||
impl Config for Test {
|
||||
type PalletId = LotteryPalletId;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type Currency = Balances;
|
||||
type Randomness = TestRandomness<Self>;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type ManagerOrigin = EnsureRoot<u64>;
|
||||
type MaxCalls = ConstU32<2>;
|
||||
type ValidateCall = Lottery;
|
||||
type MaxGenerateRandom = ConstU32<10>;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
pub type SystemCall = pezframe_system::Call<Test>;
|
||||
pub type BalancesCall = pezpallet_balances::Call<Test>;
|
||||
|
||||
pub fn new_test_ext() -> pezsp_io::TestExternalities {
|
||||
let mut t = pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
pezpallet_balances::GenesisConfig::<Test> {
|
||||
balances: vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)],
|
||||
..Default::default()
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
t.into()
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Tests for the module.
|
||||
|
||||
use crate::{
|
||||
mock::{Lottery, *},
|
||||
*,
|
||||
};
|
||||
use pezframe_support::{assert_noop, assert_ok, assert_storage_noop};
|
||||
use pezsp_runtime::{traits::BadOrigin, TokenError};
|
||||
|
||||
#[test]
|
||||
fn initial_state() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Balances::free_balance(Lottery::account_id()), 0);
|
||||
assert!(crate::Lottery::<Test>::get().is_none());
|
||||
assert_eq!(Participants::<Test>::get(&1), (0, Default::default()));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||
assert!(Tickets::<Test>::get(0).is_none());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_end_to_end_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let price = 10;
|
||||
let length = 20;
|
||||
let delay = 5;
|
||||
let calls = vec![
|
||||
RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }),
|
||||
RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }),
|
||||
];
|
||||
|
||||
// Set calls for the lottery
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls));
|
||||
|
||||
// Start lottery, it repeats
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), price, length, delay, true));
|
||||
assert!(crate::Lottery::<Test>::get().is_some());
|
||||
|
||||
assert_eq!(Balances::free_balance(&1), 100);
|
||||
let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death {
|
||||
dest: 2,
|
||||
value: 20,
|
||||
}));
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone()));
|
||||
// 20 from the transfer, 10 from buying a ticket
|
||||
assert_eq!(Balances::free_balance(&1), 100 - 20 - 10);
|
||||
assert_eq!(Participants::<Test>::get(&1).1.len(), 1);
|
||||
assert_eq!(TicketsCount::<Test>::get(), 1);
|
||||
// 1 owns the 0 ticket
|
||||
assert_eq!(Tickets::<Test>::get(0), Some(1));
|
||||
|
||||
// More ticket purchases
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(2), call.clone()));
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(3), call.clone()));
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(4), call.clone()));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 4);
|
||||
|
||||
// Go to end
|
||||
System::run_to_block::<AllPalletsWithSystem>(20);
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(5), call.clone()));
|
||||
// Ticket isn't bought
|
||||
assert_eq!(TicketsCount::<Test>::get(), 4);
|
||||
|
||||
// Go to payout
|
||||
System::run_to_block::<AllPalletsWithSystem>(25);
|
||||
// User 1 wins
|
||||
assert_eq!(Balances::free_balance(&1), 70 + 40);
|
||||
// Lottery is reset and restarted
|
||||
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||
assert_eq!(LotteryIndex::<Test>::get(), 2);
|
||||
assert_eq!(
|
||||
crate::Lottery::<Test>::get().unwrap(),
|
||||
LotteryConfig { price, start: 25, length, delay, repeat: true }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Only the manager can stop the Lottery from repeating via `stop_repeat`.
|
||||
#[test]
|
||||
fn stop_repeat_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let price = 10;
|
||||
let length = 20;
|
||||
let delay = 5;
|
||||
|
||||
// Set no calls for the lottery.
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), vec![]));
|
||||
// Start lottery, it repeats.
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), price, length, delay, true));
|
||||
|
||||
// Non-manager fails to `stop_repeat`.
|
||||
assert_noop!(Lottery::stop_repeat(RuntimeOrigin::signed(1)), DispatchError::BadOrigin);
|
||||
// Manager can `stop_repeat`, even twice.
|
||||
assert_ok!(Lottery::stop_repeat(RuntimeOrigin::root()));
|
||||
assert_ok!(Lottery::stop_repeat(RuntimeOrigin::root()));
|
||||
|
||||
// Lottery still exists.
|
||||
assert!(crate::Lottery::<Test>::get().is_some());
|
||||
// End and pick a winner.
|
||||
System::run_to_block::<AllPalletsWithSystem>(length + delay);
|
||||
|
||||
// Lottery stays dead and does not repeat.
|
||||
assert!(crate::Lottery::<Test>::get().is_none());
|
||||
System::run_to_block::<AllPalletsWithSystem>(length + delay + 1);
|
||||
assert!(crate::Lottery::<Test>::get().is_none());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_calls_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert!(!CallIndices::<Test>::exists());
|
||||
|
||||
let calls = vec![
|
||||
RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }),
|
||||
RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }),
|
||||
];
|
||||
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls));
|
||||
assert!(CallIndices::<Test>::exists());
|
||||
|
||||
let too_many_calls = vec![
|
||||
RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }),
|
||||
RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }),
|
||||
RuntimeCall::System(SystemCall::remark { remark: vec![] }),
|
||||
];
|
||||
|
||||
assert_noop!(
|
||||
Lottery::set_calls(RuntimeOrigin::root(), too_many_calls),
|
||||
Error::<Test>::TooManyCalls,
|
||||
);
|
||||
|
||||
// Clear calls
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), vec![]));
|
||||
assert!(CallIndices::<Test>::get().is_empty());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_to_indices_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let calls = vec![
|
||||
RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }),
|
||||
RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }),
|
||||
];
|
||||
let indices = Lottery::calls_to_indices(&calls).unwrap().into_inner();
|
||||
// Only comparing the length since it is otherwise dependant on the API
|
||||
// of `BalancesCall`.
|
||||
assert_eq!(indices.len(), calls.len());
|
||||
|
||||
let too_many_calls = vec![
|
||||
RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }),
|
||||
RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }),
|
||||
RuntimeCall::System(SystemCall::remark { remark: vec![] }),
|
||||
];
|
||||
assert_noop!(Lottery::calls_to_indices(&too_many_calls), Error::<Test>::TooManyCalls);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start_lottery_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let price = 10;
|
||||
let length = 20;
|
||||
let delay = 5;
|
||||
|
||||
// Setup ignores bad origin
|
||||
assert_noop!(
|
||||
Lottery::start_lottery(RuntimeOrigin::signed(1), price, length, delay, false),
|
||||
BadOrigin,
|
||||
);
|
||||
|
||||
// All good
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), price, length, delay, false));
|
||||
|
||||
// Can't open another one if lottery is already present
|
||||
assert_noop!(
|
||||
Lottery::start_lottery(RuntimeOrigin::root(), price, length, delay, false),
|
||||
Error::<Test>::InProgress,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buy_ticket_works_as_simple_passthrough() {
|
||||
// This test checks that even if the user could not buy a ticket, that `buy_ticket` acts
|
||||
// as a simple passthrough to the real call.
|
||||
new_test_ext().execute_with(|| {
|
||||
// No lottery set up
|
||||
let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death {
|
||||
dest: 2,
|
||||
value: 20,
|
||||
}));
|
||||
// This is just a basic transfer then
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone()));
|
||||
assert_eq!(Balances::free_balance(&1), 100 - 20);
|
||||
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||
|
||||
// Lottery is set up, but too expensive to enter, so `do_buy_ticket` fails.
|
||||
let calls = vec![
|
||||
RuntimeCall::Balances(BalancesCall::force_transfer { source: 0, dest: 0, value: 0 }),
|
||||
RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }),
|
||||
];
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls));
|
||||
|
||||
// Ticket price of 60 would kill the user's account
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 60, 10, 5, false));
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone()));
|
||||
assert_eq!(Balances::free_balance(&1), 100 - 20 - 20);
|
||||
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||
|
||||
// If call would fail, the whole thing still fails the same
|
||||
let fail_call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death {
|
||||
dest: 2,
|
||||
value: 1000,
|
||||
}));
|
||||
assert_noop!(
|
||||
Lottery::buy_ticket(RuntimeOrigin::signed(1), fail_call),
|
||||
ArithmeticError::Underflow,
|
||||
);
|
||||
|
||||
let bad_origin_call = Box::new(RuntimeCall::Balances(BalancesCall::force_transfer {
|
||||
source: 0,
|
||||
dest: 0,
|
||||
value: 0,
|
||||
}));
|
||||
assert_noop!(Lottery::buy_ticket(RuntimeOrigin::signed(1), bad_origin_call), BadOrigin,);
|
||||
|
||||
// User can call other txs, but doesn't get a ticket
|
||||
let remark_call =
|
||||
Box::new(RuntimeCall::System(SystemCall::remark { remark: b"hello, world!".to_vec() }));
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(2), remark_call));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||
|
||||
let successful_call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death {
|
||||
dest: 2,
|
||||
value: 1,
|
||||
}));
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(2), successful_call));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buy_ticket_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
// Set calls for the lottery.
|
||||
let calls = vec![
|
||||
RuntimeCall::System(SystemCall::remark { remark: vec![] }),
|
||||
RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 }),
|
||||
];
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls));
|
||||
|
||||
// Can't buy ticket before start
|
||||
let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death {
|
||||
dest: 2,
|
||||
value: 1,
|
||||
}));
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone()));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||
|
||||
// Start lottery
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 1, 20, 5, false));
|
||||
|
||||
// Go to start, buy ticket for transfer
|
||||
System::run_to_block::<AllPalletsWithSystem>(5);
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 1);
|
||||
|
||||
// Can't buy another of the same ticket (even if call is slightly changed)
|
||||
let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death {
|
||||
dest: 3,
|
||||
value: 30,
|
||||
}));
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 1);
|
||||
|
||||
// Buy ticket for remark
|
||||
let call =
|
||||
Box::new(RuntimeCall::System(SystemCall::remark { remark: b"hello, world!".to_vec() }));
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone()));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 2);
|
||||
|
||||
// Go to end, can't buy tickets anymore
|
||||
System::run_to_block::<AllPalletsWithSystem>(20);
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(2), call.clone()));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 2);
|
||||
|
||||
// Go to payout, can't buy tickets when there is no lottery open
|
||||
System::run_to_block::<AllPalletsWithSystem>(25);
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(2), call.clone()));
|
||||
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||
assert_eq!(LotteryIndex::<Test>::get(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
/// Test that `do_buy_ticket` returns an `AlreadyParticipating` error.
|
||||
/// Errors of `do_buy_ticket` are ignored by `buy_ticket`, therefore this white-box test.
|
||||
#[test]
|
||||
fn do_buy_ticket_already_participating() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let calls =
|
||||
vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })];
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone()));
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 1, 10, 10, false));
|
||||
|
||||
// Buying once works.
|
||||
assert_ok!(Lottery::do_buy_ticket(&1, &calls[0]));
|
||||
// Buying the same ticket again fails.
|
||||
assert_noop!(Lottery::do_buy_ticket(&1, &calls[0]), Error::<Test>::AlreadyParticipating);
|
||||
});
|
||||
}
|
||||
|
||||
/// `buy_ticket` is a storage noop when called with the same ticket again.
|
||||
#[test]
|
||||
fn buy_ticket_already_participating() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let calls =
|
||||
vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })];
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone()));
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 1, 10, 10, false));
|
||||
|
||||
// Buying once works.
|
||||
let call = Box::new(calls[0].clone());
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone()));
|
||||
|
||||
// Buying the same ticket again returns Ok, but changes nothing.
|
||||
assert_storage_noop!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call).unwrap());
|
||||
|
||||
// Exactly one ticket exists.
|
||||
assert_eq!(TicketsCount::<Test>::get(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
/// `buy_ticket` is a storage noop when called with insufficient balance.
|
||||
#[test]
|
||||
fn buy_ticket_insufficient_balance() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let calls =
|
||||
vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })];
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone()));
|
||||
// Price set to 100.
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 100, 10, 10, false));
|
||||
let call = Box::new(calls[0].clone());
|
||||
|
||||
// Buying a ticket returns Ok, but changes nothing.
|
||||
assert_storage_noop!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call).unwrap());
|
||||
assert!(TicketsCount::<Test>::get().is_zero());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn do_buy_ticket_insufficient_balance() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let calls =
|
||||
vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })];
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone()));
|
||||
// Price set to 101.
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 101, 10, 10, false));
|
||||
|
||||
// Buying fails with InsufficientBalance.
|
||||
assert_noop!(Lottery::do_buy_ticket(&1, &calls[0]), TokenError::FundsUnavailable,);
|
||||
assert!(TicketsCount::<Test>::get().is_zero());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn do_buy_ticket_keep_alive() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let calls =
|
||||
vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })];
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone()));
|
||||
// Price set to 100.
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 100, 10, 10, false));
|
||||
|
||||
assert_noop!(Lottery::do_buy_ticket(&1, &calls[0]), TokenError::NotExpendable);
|
||||
assert!(TicketsCount::<Test>::get().is_zero());
|
||||
});
|
||||
}
|
||||
|
||||
/// The lottery handles the case that no one participated.
|
||||
#[test]
|
||||
fn no_participants_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let length = 20;
|
||||
let delay = 5;
|
||||
|
||||
// Set no calls for the lottery.
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), vec![]));
|
||||
// Start lottery.
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 10, length, delay, false));
|
||||
|
||||
// End the lottery, no one wins.
|
||||
System::run_to_block::<AllPalletsWithSystem>(length + delay);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start_lottery_will_create_account() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let price = 10;
|
||||
let length = 20;
|
||||
let delay = 5;
|
||||
|
||||
assert_eq!(Balances::total_balance(&Lottery::account_id()), 0);
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), price, length, delay, false));
|
||||
assert_eq!(Balances::total_balance(&Lottery::account_id()), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn choose_ticket_trivial_cases() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert!(Lottery::choose_ticket(0).is_none());
|
||||
assert_eq!(Lottery::choose_ticket(1).unwrap(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn choose_account_one_participant() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let calls =
|
||||
vec![RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: 0, value: 0 })];
|
||||
assert_ok!(Lottery::set_calls(RuntimeOrigin::root(), calls.clone()));
|
||||
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 10, 10, 10, false));
|
||||
let call = Box::new(calls[0].clone());
|
||||
|
||||
// Buy one ticket with account 1.
|
||||
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call));
|
||||
// Account 1 is always the winner.
|
||||
assert_eq!(Lottery::choose_account().unwrap(), 1);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Autogenerated weights for `pezpallet_lottery`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 32.0.0
|
||||
//! DATE: 2025-02-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `4563561839a5`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
|
||||
//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024`
|
||||
|
||||
// Executed Command:
|
||||
// frame-omni-bencher
|
||||
// v1
|
||||
// benchmark
|
||||
// pallet
|
||||
// --extrinsic=*
|
||||
// --runtime=target/production/wbuild/kitchensink-runtime/kitchensink_runtime.wasm
|
||||
// --pallet=pezpallet_lottery
|
||||
// --header=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/HEADER-APACHE2
|
||||
// --output=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/pezframe/lottery/src/weights.rs
|
||||
// --wasm-execution=compiled
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --heap-pages=4096
|
||||
// --template=bizinikiwi/.maintain/frame-weight-template.hbs
|
||||
// --no-storage-info
|
||||
// --no-min-squares
|
||||
// --no-median-slopes
|
||||
// --genesis-builder-policy=none
|
||||
// --exclude-pallets=pezpallet_xcm,pezpallet_xcm_benchmarks::fungible,pezpallet_xcm_benchmarks::generic,pezpallet_nomination_pools,pezpallet_remark,pezpallet_transaction_storage,pezpallet_election_provider_multi_block,pezpallet_election_provider_multi_block::signed,pezpallet_election_provider_multi_block::unsigned,pezpallet_election_provider_multi_block::verifier
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use pezframe_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for `pezpallet_lottery`.
|
||||
pub trait WeightInfo {
|
||||
fn buy_ticket() -> Weight;
|
||||
fn set_calls(n: u32, ) -> Weight;
|
||||
fn start_lottery() -> Weight;
|
||||
fn stop_repeat() -> Weight;
|
||||
fn on_initialize_end() -> Weight;
|
||||
fn on_initialize_repeat() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for `pezpallet_lottery` using the Bizinikiwi node and recommended hardware.
|
||||
pub struct BizinikiwiWeight<T>(PhantomData<T>);
|
||||
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
|
||||
/// Storage: `SafeMode::EnteredUntil` (r:1 w:0)
|
||||
/// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `TxPause::PausedCalls` (r:1 w:0)
|
||||
/// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:0)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::CallIndices` (r:1 w:0)
|
||||
/// Proof: `Lottery::CallIndices` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::TicketsCount` (r:1 w:1)
|
||||
/// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Participants` (r:1 w:1)
|
||||
/// Proof: `Lottery::Participants` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::LotteryIndex` (r:1 w:0)
|
||||
/// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:1 w:1)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Tickets` (r:0 w:1)
|
||||
/// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
|
||||
fn buy_ticket() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `289`
|
||||
// Estimated: `3997`
|
||||
// Minimum execution time: 57_591_000 picoseconds.
|
||||
Weight::from_parts(59_527_000, 3997)
|
||||
.saturating_add(T::DbWeight::get().reads(8_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(4_u64))
|
||||
}
|
||||
/// Storage: `Lottery::CallIndices` (r:0 w:1)
|
||||
/// Proof: `Lottery::CallIndices` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`)
|
||||
/// The range of component `n` is `[0, 10]`.
|
||||
fn set_calls(n: u32, ) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 3_908_000 picoseconds.
|
||||
Weight::from_parts(4_528_048, 0)
|
||||
// Standard Error: 3_135
|
||||
.saturating_add(Weight::from_parts(281_533, 0).saturating_mul(n.into()))
|
||||
.saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
}
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:1)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::LotteryIndex` (r:1 w:1)
|
||||
/// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:1 w:1)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
fn start_lottery() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `3593`
|
||||
// Minimum execution time: 23_155_000 picoseconds.
|
||||
Weight::from_parts(23_832_000, 3593)
|
||||
.saturating_add(T::DbWeight::get().reads(3_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(3_u64))
|
||||
}
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:1)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
fn stop_repeat() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `94`
|
||||
// Estimated: `1514`
|
||||
// Minimum execution time: 5_354_000 picoseconds.
|
||||
Weight::from_parts(5_544_000, 1514)
|
||||
.saturating_add(T::DbWeight::get().reads(1_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(1_u64))
|
||||
}
|
||||
/// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0)
|
||||
/// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:1)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::TicketsCount` (r:1 w:1)
|
||||
/// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Tickets` (r:1 w:0)
|
||||
/// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
|
||||
fn on_initialize_end() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `266`
|
||||
// Estimated: `6196`
|
||||
// Minimum execution time: 57_804_000 picoseconds.
|
||||
Weight::from_parts(58_907_000, 6196)
|
||||
.saturating_add(T::DbWeight::get().reads(6_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(4_u64))
|
||||
}
|
||||
/// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0)
|
||||
/// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:1)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::TicketsCount` (r:1 w:1)
|
||||
/// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Tickets` (r:1 w:0)
|
||||
/// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::LotteryIndex` (r:1 w:1)
|
||||
/// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
fn on_initialize_repeat() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `266`
|
||||
// Estimated: `6196`
|
||||
// Minimum execution time: 59_553_000 picoseconds.
|
||||
Weight::from_parts(60_608_000, 6196)
|
||||
.saturating_add(T::DbWeight::get().reads(7_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(5_u64))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests.
|
||||
impl WeightInfo for () {
|
||||
/// Storage: `SafeMode::EnteredUntil` (r:1 w:0)
|
||||
/// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `TxPause::PausedCalls` (r:1 w:0)
|
||||
/// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:0)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::CallIndices` (r:1 w:0)
|
||||
/// Proof: `Lottery::CallIndices` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::TicketsCount` (r:1 w:1)
|
||||
/// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Participants` (r:1 w:1)
|
||||
/// Proof: `Lottery::Participants` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::LotteryIndex` (r:1 w:0)
|
||||
/// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:1 w:1)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Tickets` (r:0 w:1)
|
||||
/// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
|
||||
fn buy_ticket() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `289`
|
||||
// Estimated: `3997`
|
||||
// Minimum execution time: 57_591_000 picoseconds.
|
||||
Weight::from_parts(59_527_000, 3997)
|
||||
.saturating_add(RocksDbWeight::get().reads(8_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(4_u64))
|
||||
}
|
||||
/// Storage: `Lottery::CallIndices` (r:0 w:1)
|
||||
/// Proof: `Lottery::CallIndices` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`)
|
||||
/// The range of component `n` is `[0, 10]`.
|
||||
fn set_calls(n: u32, ) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `0`
|
||||
// Minimum execution time: 3_908_000 picoseconds.
|
||||
Weight::from_parts(4_528_048, 0)
|
||||
// Standard Error: 3_135
|
||||
.saturating_add(Weight::from_parts(281_533, 0).saturating_mul(n.into()))
|
||||
.saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
}
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:1)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::LotteryIndex` (r:1 w:1)
|
||||
/// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:1 w:1)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
fn start_lottery() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `0`
|
||||
// Estimated: `3593`
|
||||
// Minimum execution time: 23_155_000 picoseconds.
|
||||
Weight::from_parts(23_832_000, 3593)
|
||||
.saturating_add(RocksDbWeight::get().reads(3_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||
}
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:1)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
fn stop_repeat() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `94`
|
||||
// Estimated: `1514`
|
||||
// Minimum execution time: 5_354_000 picoseconds.
|
||||
Weight::from_parts(5_544_000, 1514)
|
||||
.saturating_add(RocksDbWeight::get().reads(1_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(1_u64))
|
||||
}
|
||||
/// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0)
|
||||
/// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:1)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::TicketsCount` (r:1 w:1)
|
||||
/// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Tickets` (r:1 w:0)
|
||||
/// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
|
||||
fn on_initialize_end() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `266`
|
||||
// Estimated: `6196`
|
||||
// Minimum execution time: 57_804_000 picoseconds.
|
||||
Weight::from_parts(58_907_000, 6196)
|
||||
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(4_u64))
|
||||
}
|
||||
/// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0)
|
||||
/// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Lottery` (r:1 w:1)
|
||||
/// Proof: `Lottery::Lottery` (`max_values`: Some(1), `max_size`: Some(29), added: 524, mode: `MaxEncodedLen`)
|
||||
/// Storage: `System::Account` (r:2 w:2)
|
||||
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::TicketsCount` (r:1 w:1)
|
||||
/// Proof: `Lottery::TicketsCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::Tickets` (r:1 w:0)
|
||||
/// Proof: `Lottery::Tickets` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
|
||||
/// Storage: `Lottery::LotteryIndex` (r:1 w:1)
|
||||
/// Proof: `Lottery::LotteryIndex` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||
fn on_initialize_repeat() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `266`
|
||||
// Estimated: `6196`
|
||||
// Minimum execution time: 59_553_000 picoseconds.
|
||||
Weight::from_parts(60_608_000, 6196)
|
||||
.saturating_add(RocksDbWeight::get().reads(7_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(5_u64))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user