From c4907e66c20a185912414395216c13ab5e4b2842 Mon Sep 17 00:00:00 2001 From: 1xstj <106580853+1xstj@users.noreply.github.com> Date: Mon, 5 Feb 2024 11:03:01 +0000 Subject: [PATCH] feat: Add time based fee to jobs based on ttl (#454) * feat: Add time based fee to jobs based on ttl * fix lint * fix lint * add storage fee per byte --- pallets/dkg/src/functions.rs | 10 ++++++-- pallets/dkg/src/tests.rs | 1 + pallets/dkg/src/types.rs | 8 ++++++ pallets/jobs/src/lib.rs | 20 ++++++++++++++- pallets/jobs/src/mock.rs | 3 +-- pallets/jobs/src/tests.rs | 45 +++++++++++++++++++++++++++++++++ pallets/zksaas/src/functions.rs | 12 +++++++-- pallets/zksaas/src/tests.rs | 6 +++-- pallets/zksaas/src/types.rs | 8 ++++++ 9 files changed, 104 insertions(+), 9 deletions(-) diff --git a/pallets/dkg/src/functions.rs b/pallets/dkg/src/functions.rs index 2f543a005..a10baa552 100644 --- a/pallets/dkg/src/functions.rs +++ b/pallets/dkg/src/functions.rs @@ -20,6 +20,7 @@ use frame_system::pallet_prelude::BlockNumberFor; use parity_scale_codec::Encode; use sp_core::{ecdsa, sr25519}; use sp_io::{crypto::sr25519_verify, hashing::keccak_256, EcdsaVerifyError}; +use sp_runtime::traits::Get; use sp_std::{default::Default, vec::Vec}; use tangle_primitives::jobs::*; @@ -58,9 +59,14 @@ impl Pallet { let validator_count = job.job_type.clone().get_participants().expect("checked_above").len(); let validator_fee = fee_info.dkg_validator_fee * (validator_count as u32).into(); - validator_fee.saturating_add(fee_info.base_fee) + let storage_fee = fee_info.storage_fee_per_byte * T::MaxKeyLen::get().into(); + validator_fee.saturating_add(fee_info.base_fee).saturating_add(storage_fee) } else { - fee_info.base_fee.saturating_add(fee_info.sig_validator_fee) + let storage_fee = fee_info.storage_fee_per_byte * T::MaxSignatureLen::get().into(); + fee_info + .base_fee + .saturating_add(fee_info.sig_validator_fee) + .saturating_add(storage_fee) } } diff --git a/pallets/dkg/src/tests.rs b/pallets/dkg/src/tests.rs index bc9f84fca..f60a152c2 100644 --- a/pallets/dkg/src/tests.rs +++ b/pallets/dkg/src/tests.rs @@ -57,6 +57,7 @@ fn set_fees_works() { dkg_validator_fee: 5, sig_validator_fee: 5, refresh_validator_fee: 5, + storage_fee_per_byte: 1, }; // Dispatch a signed extrinsic. diff --git a/pallets/dkg/src/types.rs b/pallets/dkg/src/types.rs index 4b50783e2..3a740421f 100644 --- a/pallets/dkg/src/types.rs +++ b/pallets/dkg/src/types.rs @@ -41,6 +41,9 @@ pub struct FeeInfo { /// The fee for refresh existing DKG. pub refresh_validator_fee: Balance, + + /// The storage fee per byts + pub storage_fee_per_byte: Balance, } impl FeeInfo { @@ -58,4 +61,9 @@ impl FeeInfo { pub fn get_sig_validator_fee(self) -> Balance { self.sig_validator_fee } + + /// Get the storage fee per byte + pub fn get_storage_fee_per_byte(self) -> Balance { + self.storage_fee_per_byte + } } diff --git a/pallets/jobs/src/lib.rs b/pallets/jobs/src/lib.rs index 3499b5a12..acc250c8c 100644 --- a/pallets/jobs/src/lib.rs +++ b/pallets/jobs/src/lib.rs @@ -63,6 +63,7 @@ pub use weights::WeightInfo; pub mod module { use super::*; use scale_info::prelude::fmt::Debug; + use sp_runtime::Saturating; use tangle_primitives::roles::RoleType; #[pallet::config] @@ -213,6 +214,10 @@ pub mod module { #[pallet::getter(fn next_job_id)] pub type NextJobId = StorageValue<_, JobId, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn time_fee)] + pub type TimeFeePerBlock = StorageValue<_, BalanceOf, ValueQuery>; + #[pallet::pallet] pub struct Pallet(_); @@ -309,7 +314,12 @@ pub mod module { ensure!(job.expiry > now, Error::::JobAlreadyExpired); // charge the user fee for job submission - let fee = T::JobToFee::job_to_fee(&job); + let processing_fee = T::JobToFee::job_to_fee(&job); + let ttl_u32: u32 = + job.ttl.try_into().map_err(|_| sp_runtime::ArithmeticError::Underflow)?; + let time_fee = Self::time_fee() * ttl_u32.into(); + let fee = processing_fee.saturating_add(time_fee); + T::Currency::transfer( &caller, &Self::rewards_account_id(), @@ -598,5 +608,13 @@ pub mod module { Ok(()) }) } + + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::withdraw_rewards())] + pub fn set_time_fee(origin: OriginFor, new_fee: BalanceOf) -> DispatchResult { + T::ForceOrigin::ensure_origin(origin)?; + TimeFeePerBlock::::set(new_fee); + Ok(()) + } } } diff --git a/pallets/jobs/src/mock.rs b/pallets/jobs/src/mock.rs index ed2f14836..d3b51c2d6 100644 --- a/pallets/jobs/src/mock.rs +++ b/pallets/jobs/src/mock.rs @@ -24,7 +24,6 @@ use frame_support::{ construct_runtime, parameter_types, traits::{ConstU128, ConstU32, ConstU64, Contains, Everything}, }; -use frame_system::EnsureSigned; use pallet_session::historical as pallet_session_historical; use sp_core::{ sr25519::{self}, @@ -352,7 +351,7 @@ parameter_types! { impl Config for Runtime { type RuntimeEvent = RuntimeEvent; - type ForceOrigin = EnsureSigned; + type ForceOrigin = frame_system::EnsureRoot; type Currency = Balances; type JobToFee = MockJobToFeeHandler; type RolesHandler = Roles; diff --git a/pallets/jobs/src/tests.rs b/pallets/jobs/src/tests.rs index 9a048814e..8d7d812da 100644 --- a/pallets/jobs/src/tests.rs +++ b/pallets/jobs/src/tests.rs @@ -1397,3 +1397,48 @@ fn switch_active_independent_profile_to_shared_should_work_if_active_restake_sum } }); } + +#[test] +fn test_fee_charged_for_jobs_submission() { + new_test_ext(vec![ALICE, BOB, CHARLIE, DAVE, EVE]).execute_with(|| { + System::set_block_number(1); + + // setup time fees + assert_ok!(Jobs::set_time_fee(RuntimeOrigin::root(), 1)); + + let threshold_signature_role_type = ThresholdSignatureRoleType::ZengoGG20Secp256k1; + + // all validators sign up in roles pallet + let profile = shared_profile(); + for validator in [ALICE, BOB, CHARLIE, DAVE, EVE] { + assert_ok!(Roles::create_profile( + RuntimeOrigin::signed(mock_pub_key(validator)), + profile.clone() + )); + } + + Balances::make_free_balance_be(&mock_pub_key(TEN), 100); + + let submission = JobSubmission { + expiry: 10, + ttl: 20, + job_type: JobType::DKGTSSPhaseOne(DKGTSSPhaseOneJobType { + participants: [ALICE, BOB, CHARLIE, DAVE, EVE] + .iter() + .map(|x| mock_pub_key(*x)) + .collect::>() + .try_into() + .unwrap(), + threshold: 3, + permitted_caller: Some(mock_pub_key(TEN)), + role_type: threshold_signature_role_type, + }), + }; + assert_ok!(Jobs::submit_job(RuntimeOrigin::signed(mock_pub_key(TEN)), submission)); + + // Fees charged + // 1. 1unit per participant + // 2. 1unit per ttl block (20) + assert_eq!(Balances::free_balance(mock_pub_key(TEN)), 100 - 5 - 20); + }); +} diff --git a/pallets/zksaas/src/functions.rs b/pallets/zksaas/src/functions.rs index 0dd29cb3b..a68d32568 100644 --- a/pallets/zksaas/src/functions.rs +++ b/pallets/zksaas/src/functions.rs @@ -17,6 +17,8 @@ use super::*; use crate::types::BalanceOf; use frame_support::{pallet_prelude::DispatchResult, sp_runtime::Saturating}; use frame_system::pallet_prelude::BlockNumberFor; +use sp_core::crypto::ByteArray; +use sp_runtime::traits::Get; use tangle_primitives::{jobs::*, verifier::*}; impl Pallet { @@ -47,9 +49,15 @@ impl Pallet { let validator_count = job.job_type.clone().get_participants().expect("checked_above").len(); let validator_fee = fee_info.circuit_fee * (validator_count as u32).into(); - validator_fee.saturating_add(fee_info.base_fee) + let data_stored: usize = sp_core::ecdsa::Public::LEN * validator_count; + let storage_fee = fee_info.storage_fee_per_byte * (data_stored as u32).into(); + validator_fee.saturating_add(fee_info.base_fee).saturating_add(storage_fee) } else { - fee_info.base_fee.saturating_add(fee_info.get_prove_fee()) + let storage_fee = fee_info.storage_fee_per_byte * T::MaxProofLen::get().into(); + fee_info + .base_fee + .saturating_add(fee_info.get_prove_fee()) + .saturating_add(storage_fee) } } diff --git a/pallets/zksaas/src/tests.rs b/pallets/zksaas/src/tests.rs index ed38ff67c..21c3b2863 100644 --- a/pallets/zksaas/src/tests.rs +++ b/pallets/zksaas/src/tests.rs @@ -39,7 +39,8 @@ type F = ark_bn254::Fr; #[test] fn set_fees_works() { new_test_ext().execute_with(|| { - let new_fee = FeeInfo { base_fee: 10, circuit_fee: 5, prove_fee: 5 }; + let new_fee = + FeeInfo { base_fee: 10, circuit_fee: 5, prove_fee: 5, storage_fee_per_byte: 1 }; // should fail for non update origin assert_noop!(ZKSaaS::set_fee(RuntimeOrigin::signed(10), new_fee.clone()), BadOrigin); @@ -54,7 +55,8 @@ fn set_fees_works() { #[test] fn proof_verification_works() { new_test_ext().execute_with(|| { - let new_fee = FeeInfo { base_fee: 10, circuit_fee: 5, prove_fee: 5 }; + let new_fee = + FeeInfo { base_fee: 10, circuit_fee: 5, prove_fee: 5, storage_fee_per_byte: 1 }; // Dispatch a signed extrinsic. assert_ok!(ZKSaaS::set_fee(RuntimeOrigin::signed(1), new_fee.clone())); diff --git a/pallets/zksaas/src/types.rs b/pallets/zksaas/src/types.rs index c3206142f..547744874 100644 --- a/pallets/zksaas/src/types.rs +++ b/pallets/zksaas/src/types.rs @@ -38,6 +38,9 @@ pub struct FeeInfo { /// The fee for Proof generation. pub prove_fee: Balance, + + /// The storage fee per byte + pub storage_fee_per_byte: Balance, } impl FeeInfo { @@ -55,4 +58,9 @@ impl FeeInfo { pub fn get_prove_fee(self) -> Balance { self.prove_fee } + + /// Get the storage fee per byte + pub fn get_storage_fee_per_byte(self) -> Balance { + self.storage_fee_per_byte + } }