diff --git a/pallets/pass/src/benchmarking.rs b/pallets/pass/src/benchmarking.rs index cac8773..38f750f 100644 --- a/pallets/pass/src/benchmarking.rs +++ b/pallets/pass/src/benchmarking.rs @@ -3,6 +3,7 @@ use super::*; use crate::Pallet; use frame_benchmarking::v2::*; +use frame_support::traits::OriginTrait; use sp_runtime::traits::Hash; type RuntimeEventFor = >::RuntimeEvent; @@ -11,6 +12,7 @@ fn assert_has_event, I: 'static>(generic_event: RuntimeEventFor::assert_has_event(generic_event.into()); } +#[allow(dead_code)] fn setup_signers() -> (T::AccountId, T::AccountId) { ( frame_benchmarking::account("signer", 0, 0), @@ -28,6 +30,7 @@ where #[instance_benchmarks( where T: frame_system::Config + crate::Config, + OriginFor: From>, T::Hash: Into, RuntimeEventFor: From>, )] @@ -37,14 +40,14 @@ mod benchmarks { #[benchmark] pub fn register() -> Result<(), BenchmarkError> { // Setup code - let (one, _) = setup_signers::(); + let origin = T::BenchmarkHelper::register_origin(); let user_id = hash::(&*b"my-account"); let account_id = Pallet::::account_id_for(user_id)?; let device_id = [0u8; 32]; #[extrinsic_call] _( - RawOrigin::Signed(one), + origin.into_caller(), user_id, T::BenchmarkHelper::device_attestation(device_id), ); diff --git a/pallets/pass/src/lib.rs b/pallets/pass/src/lib.rs index 85056e0..ff6a078 100644 --- a/pallets/pass/src/lib.rs +++ b/pallets/pass/src/lib.rs @@ -67,7 +67,11 @@ pub mod pallet { #[pallet::constant] type MaxSessionDuration: Get>; - type RegisterOrigin: EnsureOriginWithArg; + type RegisterOrigin: EnsureOriginWithArg< + Self::RuntimeOrigin, + HashedUserId, + Success = Option>, + >; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper: BenchmarkHelper; @@ -127,13 +131,16 @@ pub mod pallet { user: HashedUserId, attestation: DeviceAttestationOf, ) -> DispatchResult { - T::RegisterOrigin::ensure_origin(origin, &user)?; + let maybe_deposit_info = T::RegisterOrigin::ensure_origin(origin, &user)?; let account_id = Self::account_id_for(user)?; ensure!( !Self::account_exists(&account_id), Error::::AccountAlreadyRegistered ); + if let Some(deposit_info) = maybe_deposit_info { + Self::charge_register_deposit(deposit_info)?; + } Self::create_account(&account_id)?; Self::deposit_event(Event::::Registered { who: account_id.clone(), @@ -224,13 +231,11 @@ impl, I: 'static> Pallet { #[allow(dead_code)] pub(crate) fn charge_register_deposit( - who: &T::AccountId, - amount: BalanceOf, - beneficiary: &T::AccountId, + (source, amount, dest): DepositInformation, ) -> DispatchResult { T::Currency::transfer( - who, - beneficiary, + &source, + &dest, amount, frame_support::traits::tokens::Preservation::Expendable, ) diff --git a/pallets/pass/src/mock.rs b/pallets/pass/src/mock.rs index c0abf4c..5b3c86d 100644 --- a/pallets/pass/src/mock.rs +++ b/pallets/pass/src/mock.rs @@ -1,16 +1,16 @@ //! Test environment for pallet pass. -use crate::{self as pallet_pass, Config, CredentialOf, DeviceAttestationOf}; +use crate::{self as pallet_pass, Config}; pub use authenticators::*; use codec::{Decode, Encode, MaxEncodedLen}; use fc_traits_authn::{composite_authenticators, util::AuthorityFromPalletId, Challenger}; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU32, ConstU64, EqualPrivilegeOnly, OnInitialize}, + traits::{ConstU32, ConstU64, EitherOf, EqualPrivilegeOnly, OnInitialize}, weights::Weight, DebugNoBound, EqNoBound, PalletId, }; -use frame_system::{EnsureRoot, EnsureSigned}; +use frame_system::{pallet_prelude::OriginFor, EnsureRoot, EnsureRootWithSuccess}; use scale_info::TypeInfo; use sp_core::{blake2_256, H256}; use sp_io::TestExternalities; @@ -81,7 +81,9 @@ impl pallet_scheduler::Config for Test { } parameter_types! { + pub const RootAccount: AccountId = AccountId::new([0u8; 32]); pub PassPalletId: PalletId = PalletId(*b"py/pass_"); + pub RootDoesNotPay: Option> = None; } composite_authenticators! { @@ -96,7 +98,12 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type Authenticator = PassAuthenticator; - type RegisterOrigin = EnsureSigned; + type RegisterOrigin = EitherOf< + // Root does not pay + EnsureRootWithSuccess, + // Anyone else pays + pallet_pass::EnsureSignedPays, RootAccount>, + >; type RuntimeCall = RuntimeCall; type PalletId = PassPalletId; type PalletsOrigin = OriginCaller; @@ -105,11 +112,18 @@ impl Config for Test { type BenchmarkHelper = BenchmarkHelper; } +#[cfg(feature = "runtime-benchmarks")] +use pallet_pass::{CredentialOf, DeviceAttestationOf}; + #[cfg(feature = "runtime-benchmarks")] pub struct BenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] impl pallet_pass::BenchmarkHelper for BenchmarkHelper { + fn register_origin() -> OriginFor { + RuntimeOrigin::root() + } + fn device_attestation(device_id: DeviceId) -> DeviceAttestationOf { PassDeviceAttestation::AuthenticatorAAuthenticator(authenticator_a::DeviceAttestation { device_id, diff --git a/pallets/pass/src/tests.rs b/pallets/pass/src/tests.rs index cee2a52..2bfd23d 100644 --- a/pallets/pass/src/tests.rs +++ b/pallets/pass/src/tests.rs @@ -3,8 +3,9 @@ use super::{Error, Event}; use crate::mock::*; use fc_traits_authn::{Challenger, HashedUserId}; -use frame_support::{assert_noop, assert_ok, parameter_types}; +use frame_support::{assert_noop, assert_ok, parameter_types, traits::fungible::Mutate}; use sp_core::Hasher; +use sp_runtime::ArithmeticError; const SIGNER: AccountId = AccountId::new([0u8; 32]); const OTHER: AccountId = AccountId::new([1u8; 32]); @@ -22,7 +23,6 @@ parameter_types! { } mod register { - use super::*; #[test] @@ -48,9 +48,43 @@ mod register { }); } + #[test] + fn register_deposit_logic_works() { + new_test_ext().execute_with(|| { + assert_ok!(Pass::register( + RuntimeOrigin::root(), + AccountNameA::get(), + PassDeviceAttestation::AuthenticatorAAuthenticator( + authenticator_a::DeviceAttestation { + device_id: THE_DEVICE, + challenge: authenticator_a::Authenticator::generate(&()), + } + ), + )); + }); + + new_test_ext().execute_with(|| { + assert_noop!( + Pass::register( + RuntimeOrigin::signed(SIGNER), + AccountNameA::get(), + PassDeviceAttestation::AuthenticatorAAuthenticator( + authenticator_a::DeviceAttestation { + device_id: THE_DEVICE, + challenge: authenticator_a::Authenticator::generate(&()), + } + ), + ), + ArithmeticError::Underflow, + ); + }); + } + #[test] fn fail_if_attestation_is_invalid() { new_test_ext().execute_with(|| { + assert_ok!(Balances::mint_into(&SIGNER, 2)); + assert_noop!( Pass::register( RuntimeOrigin::signed(SIGNER), @@ -68,6 +102,8 @@ mod register { #[test] fn it_works() { new_test_ext().execute_with(|| { + assert_ok!(Balances::mint_into(&SIGNER, 2)); + let account_id = Pass::account_id_for(AccountNameA::get()).expect("account exists; qed"); @@ -102,6 +138,7 @@ mod register { fn prepare(user_id: HashedUserId) -> sp_io::TestExternalities { let mut t = new_test_ext(); t.execute_with(|| { + assert_ok!(Balances::mint_into(&SIGNER, 2)); assert_ok!(Pass::register( RuntimeOrigin::signed(SIGNER), user_id, @@ -160,6 +197,8 @@ mod authenticate { #[test] fn fail_if_attestation_is_invalid() { new_test_ext().execute_with(|| { + assert_ok!(Balances::mint_into(&SIGNER, 2)); + assert_ok!(Pass::register( RuntimeOrigin::signed(SIGNER), AccountNameA::get(), diff --git a/pallets/pass/src/types.rs b/pallets/pass/src/types.rs index b8e2e1f..f3926f3 100644 --- a/pallets/pass/src/types.rs +++ b/pallets/pass/src/types.rs @@ -1,11 +1,13 @@ use crate::Config; -use fc_traits_authn::HashedUserId; -use frame_support::traits::fungible::Inspect; -use sp_runtime::traits::StaticLookup; +use frame_support::traits::{fungible::Inspect, MapSuccess}; +use frame_system::{pallet_prelude::OriginFor, EnsureSigned}; +use sp_core::TypedGet; +use sp_runtime::{morph_types, traits::StaticLookup}; // pub type HashedUserId = ::Hash; +type AccountIdOf = ::AccountId; pub type ContextOf = - <<>::Authenticator as fc_traits_authn::Authenticator>::Challenger as fc_traits_authn::Challenger>::Context; +<<>::Authenticator as fc_traits_authn::Authenticator>::Challenger as fc_traits_authn::Challenger>::Context; pub type DeviceOf = <>::Authenticator as fc_traits_authn::Authenticator>::Device; pub type CredentialOf = as fc_traits_authn::UserAuthenticator>::Credential; @@ -14,13 +16,34 @@ pub type DeviceAttestationOf = pub type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; pub type BalanceOf = <>::Currency as Inspect<::AccountId>>::Balance; +pub type DepositInformation = ( + ::AccountId, + BalanceOf, + ::AccountId, +); + +morph_types! { + pub type PaymentForCreate< + AccountId, + GetAmount: TypedGet, + GetReceiver: TypedGet + >: Morph = |sender: AccountId| -> Option<(AccountId, GetAmount::Type, GetReceiver::Type)> { + Some((sender, GetAmount::get(), GetReceiver::get())) + }; +} +pub type EnsureSignedPays = + MapSuccess>, PaymentForCreate, Amount, Beneficiary>>; + +#[cfg(feature = "runtime-benchmarks")] +use fc_traits_authn::HashedUserId; #[cfg(feature = "runtime-benchmarks")] pub trait BenchmarkHelper where T: Config, I: 'static, { + fn register_origin() -> OriginFor; fn device_attestation(device_id: fc_traits_authn::DeviceId) -> DeviceAttestationOf; fn credential(user_id: HashedUserId) -> CredentialOf; }