From 3aa692a9ccc3a200c7cc3daf0dbabde5d911d1ba Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Fri, 9 Oct 2020 00:44:10 +0200 Subject: [PATCH] Basic end to end test --- Cargo.lock | 2 - frame/name-service/Cargo.toml | 8 +- frame/name-service/src/lib.rs | 14 +++- frame/name-service/src/mock.rs | 138 ++++++++++++++++++++++++++++++++ frame/name-service/src/tests.rs | 94 ++++++++++++++++++++++ 5 files changed, 244 insertions(+), 12 deletions(-) create mode 100644 frame/name-service/src/mock.rs create mode 100644 frame/name-service/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index f4df78a4fb5f8..ebe9c05a1aca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4650,10 +4650,8 @@ dependencies = [ "frame-system", "pallet-balances", "parity-scale-codec", - "serde", "sp-core", "sp-io", - "sp-keyring", "sp-runtime", "sp-std", ] diff --git a/frame/name-service/Cargo.toml b/frame/name-service/Cargo.toml index e1c5993a79146..656b66e7e6847 100644 --- a/frame/name-service/Cargo.toml +++ b/frame/name-service/Cargo.toml @@ -13,30 +13,26 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-keyring = { version = "2.0.0", optional = true, path = "../../primitives/keyring" } sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } frame-support = { version = "2.0.0", default-features = false, path = "../support" } frame-system = { version = "2.0.0", default-features = false, path = "../system" } +# Benchmarking dependencies frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] +sp-io = { version = "2.0.0", path = "../../primitives/io" } pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] std = [ - "serde", - "sp-keyring", "codec/std", "sp-core/std", "sp-std/std", - "sp-io/std", "frame-support/std", "sp-runtime/std", "frame-system/std", diff --git a/frame/name-service/src/lib.rs b/frame/name-service/src/lib.rs index f4eec16b2c8e9..a1e50e948db27 100644 --- a/frame/name-service/src/lib.rs +++ b/frame/name-service/src/lib.rs @@ -31,10 +31,14 @@ use frame_support::traits::{ use frame_system::ensure_signed; use codec::{Encode, Decode}; +mod mock; +mod tests; + type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; pub trait WeightInfo {} +impl WeightInfo for () {} /// The module's config trait. pub trait Trait: frame_system::Trait { @@ -185,7 +189,7 @@ decl_module! { #[weight = 0] fn bid(origin, name: Name, new_bid: BalanceOf) { let new_bidder = ensure_signed(origin)?; - ensure!(new_bid > T::MinBid::get(), Error::::InvalidBid); + ensure!(new_bid >= T::MinBid::get(), Error::::InvalidBid); let block_number = frame_system::Module::::block_number(); let new_bid_end = block_number.saturating_add(T::BiddingPeriod::get()); @@ -247,7 +251,7 @@ decl_module! { NameStatus::Available | NameStatus::Owned { .. } => Err(Error::::InvalidClaim)?, NameStatus::Bidding { who: current_bidder, bid_end, amount } => { ensure!(caller == *current_bidder, Error::::NotBidder); - ensure!(*bid_end < block_number, Error::::NotExpired); + ensure!(*bid_end <= block_number, Error::::NotExpired); // If user only wants 1 period, just slash the reserve we already have. let mut credit = if num_of_periods == 1 { NegativeImbalanceOf::::zero() @@ -268,7 +272,9 @@ decl_module! { credit.subsume(T::Currency::slash_reserved(current_bidder, *amount).0); T::PaymentDestination::on_unbalanced(credit); // Grant ownership - let ownership_expiration = T::OwnershipPeriod::get().saturating_mul(num_of_periods.into()); + let ownership_expiration = block_number.saturating_add( + T::OwnershipPeriod::get().saturating_mul(num_of_periods.into()) + ); *state = NameStatus::Owned { who: current_bidder.clone(), expiration: Some(ownership_expiration), @@ -307,7 +313,7 @@ decl_module! { NameStatus::Owned { who: current_owner, expiration: maybe_expiration } => { if let Some(expiration) = maybe_expiration { if caller != *current_owner { - ensure!(*expiration < block_number, Error::::NotExpired); + ensure!(*expiration <= block_number, Error::::NotExpired); } *state = NameStatus::Available; Ok(()) diff --git a/frame/name-service/src/mock.rs b/frame/name-service/src/mock.rs new file mode 100644 index 0000000000000..d8c2a83de88f4 --- /dev/null +++ b/frame/name-service/src/mock.rs @@ -0,0 +1,138 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 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 + +#![cfg(test)] + +use sp_runtime::testing::Header; +use sp_runtime::Perbill; +use sp_core::H256; +use frame_support::{impl_outer_origin, impl_outer_event, parameter_types, ord_parameter_types, weights::Weight}; +use crate::{self as pallet_name_service, Module, Trait, ExtensionConfig}; +use frame_system::EnsureSignedBy; + +impl_outer_origin!{ + pub enum Origin for Test where system = frame_system {} +} +impl_outer_event!{ + pub enum Event for Test { + frame_system, + pallet_balances, + pallet_name_service, + } +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Test; + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +type BlockNumber = u64; +type Balance = u64; + +impl frame_system::Trait for Test { + type BaseCallFilter = (); + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = BlockNumber; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = NameService; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type DbWeight = (); + type BlockExecutionWeight = (); + type ExtrinsicBaseWeight = (); + type MaximumExtrinsicWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type PalletInfo = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Trait for Test { + type MaxLocks = (); + type Balance = Balance; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + + +parameter_types! { + pub const BiddingPeriod: BlockNumber = 10; + pub const ClaimPeriod: BlockNumber = 5; + pub const OwnershipPeriod: BlockNumber = 100; + pub const MinBid: Balance = 5; + pub ExtensionsOn: ExtensionConfig = ExtensionConfig { + enabled: true, + extension_period: 100, + extension_fee: 5, + }; +} + +ord_parameter_types! { + pub const Manager: u64 = 100; + pub const Permanence: u64 = 200; +} + +impl Trait for Test { + type Currency = Balances; + type Event = Event; + type ManagerOrigin = EnsureSignedBy; + type PermanenceOrigin = EnsureSignedBy; + type BiddingPeriod = BiddingPeriod; + type ClaimPeriod = ClaimPeriod; + type OwnershipPeriod = OwnershipPeriod; + type PaymentDestination = (); + type MinBid = MinBid; + type ExtensionConfig = ExtensionsOn; + type WeightInfo = (); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig::{ + balances: vec![(1, 100), (2, 200), (3, 300), (4, 400), (5, 500), (6, 600)], + }.assimilate_storage(&mut t).unwrap(); + t.into() +} + +pub type System = frame_system::Module; +pub type Balances = pallet_balances::Module; +pub type NameService = Module; diff --git a/frame/name-service/src/tests.rs b/frame/name-service/src/tests.rs new file mode 100644 index 0000000000000..989b4855221d6 --- /dev/null +++ b/frame/name-service/src/tests.rs @@ -0,0 +1,94 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 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. + +#![cfg(test)] + +use super::*; +use super::mock::*; +use sp_core::blake2_256; +use frame_support::{ + assert_ok, assert_noop, + traits::{OnInitialize, OnFinalize} +}; +use pallet_balances::Error as BalancesError; + +fn run_to_block(n: u64) { + while System::block_number() < n { + NameService::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + NameService::on_initialize(System::block_number()); + } +} + +#[test] +fn basic_setup_works() { + new_test_ext().execute_with(|| { + assert_eq!(Balances::free_balance(&1), 100); + assert_eq!(Balances::free_balance(&2), 200); + }); +} + +#[test] +fn end_to_end_should_work() { + new_test_ext().execute_with(|| { + // This is the name we will bid on. + let name = b"shawntabrizi"; + let name_hash = blake2_256(name); + + // Name is totally available. + assert!(!Registration::::contains_key(name_hash)); + assert!(!Lookup::::contains_key(name_hash)); + + // User 1 can make an initial bid. + assert_ok!(NameService::bid(Origin::signed(1), name_hash, 5)); + assert_eq!(Balances::free_balance(&1), 95); + + // User 2 can be outbid. + run_to_block(9); + assert_ok!(NameService::bid(Origin::signed(2), name_hash, 10)); + assert_eq!(Balances::free_balance(&1), 100); + assert_eq!(Balances::free_balance(&2), 190); + + // User 2 can win bid. (others cant bid anymore) + run_to_block(19); + assert_noop!(NameService::bid(Origin::signed(1), name_hash, 15), Error::::InvalidBid); + + // User 2 can claim bid. 2 ^ 2 = 4 * 10 = 40 total cost + assert_ok!(NameService::claim(Origin::signed(2), name_hash, 2)); + assert_eq!(Balances::free_balance(&2), 160); + + // User 2 can assign their name + assert_ok!(NameService::assign(Origin::signed(2), name_hash, Some(2))); + + // Name is totally taken. + assert!(Registration::::contains_key(name_hash)); + assert_eq!(Lookup::::get(name_hash), Some(2)); + + // Name can be used instead of AccountId + assert_ok!(Balances::transfer(Origin::signed(1), MultiAddress::Hash256(name_hash), 40)); + assert_eq!(Balances::free_balance(&2), 200); + + // Name can expire + run_to_block(219); + assert_ok!(NameService::free(Origin::signed(1), name_hash)); + + // Name can be bid for again. + assert_ok!(NameService::bid(Origin::signed(1), name_hash, 5)); + }); +}