diff --git a/Cargo.lock b/Cargo.lock index 52d3cfc98..66d8d13da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1666,6 +1666,8 @@ version = "0.1.0" dependencies = [ "ddc-primitives", "frame-system", + "parity-scale-codec", + "scale-info", "sp-runtime", ] diff --git a/pallets/ddc-payouts/src/mock.rs b/pallets/ddc-payouts/src/mock.rs index 092af4004..b68c17b6f 100644 --- a/pallets/ddc-payouts/src/mock.rs +++ b/pallets/ddc-payouts/src/mock.rs @@ -141,6 +141,8 @@ pub const VALIDATOR3_ACCOUNT_ID: AccountId = 333; pub const PARTIAL_CHARGE: u128 = 100; pub const USER3_BALANCE: u128 = 1000; +pub const FREE_CLUSTER_ID: ClusterId = ClusterId::zero(); + pub const PRICING_PARAMS: ClusterPricingParams = ClusterPricingParams { unit_per_mb_streamed: 2_000_000, unit_per_mb_stored: 3_000_000, @@ -154,6 +156,12 @@ pub const PRICING_FEES: ClusterFeesParams = ClusterFeesParams { cluster_reserve_share: Perbill::from_percent(2), }; +pub const PRICING_FEES_ZERO: ClusterFeesParams = ClusterFeesParams { + treasury_share: Perbill::from_percent(0), + validators_share: Perbill::from_percent(0), + cluster_reserve_share: Perbill::from_percent(0), +}; + pub struct TestTreasuryVisitor; impl PalletVisitor for TestTreasuryVisitor { fn get_account_id() -> T::AccountId { @@ -231,6 +239,14 @@ impl SortedListProvider for TestValidator } } +pub fn get_fees(cluster_id: &ClusterId) -> Result { + if *cluster_id == FREE_CLUSTER_ID { + Ok(PRICING_FEES_ZERO) + } else { + Ok(PRICING_FEES) + } +} + pub struct TestClusterVisitor; impl ClusterVisitor for TestClusterVisitor { fn cluster_has_node(_cluster_id: &ClusterId, _node_pub_key: &NodePubKey) -> bool { @@ -264,8 +280,8 @@ impl ClusterVisitor for TestClusterVisitor { Ok(PRICING_PARAMS) } - fn get_fees_params(_cluster_id: &ClusterId) -> Result { - Ok(PRICING_FEES) + fn get_fees_params(cluster_id: &ClusterId) -> Result { + get_fees(cluster_id) } fn get_reserve_account_id( diff --git a/pallets/ddc-payouts/src/tests.rs b/pallets/ddc-payouts/src/tests.rs index de241a7d9..9fa6a4006 100644 --- a/pallets/ddc-payouts/src/tests.rs +++ b/pallets/ddc-payouts/src/tests.rs @@ -629,6 +629,117 @@ fn end_charging_customers_works() { }) } +#[test] +fn end_charging_customers_works_zero_fees() { + ExtBuilder.build_and_execute(|| { + System::set_block_number(1); + + let dac_account = 123u64; + let user1 = 1u64; + let cluster_id = ClusterId::zero(); + let era = 100; + let max_batch_index = 0; + let batch_index = 0; + let usage1 = CustomerUsage { + transferred_bytes: 23452345, + stored_bytes: 3345234523, + number_of_puts: 4456456345234523, + number_of_gets: 523423, + }; + let payers = vec![(user1, usage1.clone())]; + + assert_ok!(DdcPayouts::set_authorised_caller(RuntimeOrigin::root(), dac_account)); + + assert_ok!(DdcPayouts::begin_billing_report( + RuntimeOrigin::signed(dac_account), + cluster_id, + era, + )); + + assert_ok!(DdcPayouts::begin_charging_customers( + RuntimeOrigin::signed(dac_account), + cluster_id, + era, + max_batch_index, + )); + + assert_ok!(DdcPayouts::send_charging_customers_batch( + RuntimeOrigin::signed(dac_account), + cluster_id, + era, + batch_index, + payers, + )); + + let report_before = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); + let charge = calculate_charge(usage1); + System::assert_last_event( + Event::Charged { cluster_id, era, customer_id: user1, amount: charge }.into(), + ); + + let mut balance = Balances::free_balance(DdcPayouts::sub_account_id(cluster_id, era)); + assert_eq!(balance, charge); + assert_eq!(System::events().len(), 4 + 3); // 3 for Currency::transfer + + assert_ok!(DdcPayouts::end_charging_customers( + RuntimeOrigin::signed(dac_account), + cluster_id, + era, + )); + + System::assert_has_event(Event::ChargingFinished { cluster_id, era }.into()); + assert_eq!(System::events().len(), 7 + 1); + + let report_after = DdcPayouts::active_billing_reports(cluster_id, era).unwrap(); + assert_eq!(report_after.state, State::CustomersChargedWithFees); + + let fees = get_fees(&cluster_id).unwrap(); + + let total_left_from_one = (fees.treasury_share + + fees.validators_share + + fees.cluster_reserve_share) + .left_from_one(); + + assert_eq!(total_left_from_one, Perbill::one()); + + assert_eq!(fees.treasury_share, Perbill::zero()); + assert_eq!(fees.validators_share, Perbill::zero()); + assert_eq!(fees.cluster_reserve_share, Perbill::zero()); + + balance = Balances::free_balance(TREASURY_ACCOUNT_ID); + assert_eq!(balance, 0); + + balance = Balances::free_balance(RESERVE_ACCOUNT_ID); + assert_eq!(balance, 0); + + balance = Balances::free_balance(VALIDATOR1_ACCOUNT_ID); + assert_eq!(balance, 0); + + balance = Balances::free_balance(VALIDATOR2_ACCOUNT_ID); + assert_eq!(balance, 0); + + balance = Balances::free_balance(VALIDATOR3_ACCOUNT_ID); + assert_eq!(balance, 0); + + assert_eq!( + report_after.total_customer_charge.transfer, + total_left_from_one * report_before.total_customer_charge.transfer + ); + assert_eq!( + report_after.total_customer_charge.storage, + total_left_from_one * report_before.total_customer_charge.storage + ); + assert_eq!( + report_after.total_customer_charge.puts, + total_left_from_one * report_before.total_customer_charge.puts + ); + assert_eq!( + report_after.total_customer_charge.gets, + total_left_from_one * report_before.total_customer_charge.gets + ); + }) +} + #[test] fn begin_rewarding_providers_fails_uninitialised() { ExtBuilder.build_and_execute(|| { diff --git a/traits/Cargo.toml b/traits/Cargo.toml index 9b7fa403f..fd2ad1076 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } ddc-primitives = { path = "../primitives", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.30", default-features = false } diff --git a/traits/src/cluster.rs b/traits/src/cluster.rs index e8cec5e50..b5f12508d 100644 --- a/traits/src/cluster.rs +++ b/traits/src/cluster.rs @@ -1,5 +1,8 @@ +use codec::{Decode, Encode}; use ddc_primitives::{ClusterFeesParams, ClusterId, ClusterPricingParams, NodePubKey, NodeType}; use frame_system::Config; +use sp_runtime::RuntimeDebug; +use scale_info::TypeInfo; pub trait ClusterVisitor { fn cluster_has_node(cluster_id: &ClusterId, node_pub_key: &NodePubKey) -> bool; @@ -30,6 +33,7 @@ pub trait ClusterVisitor { ) -> Result; } +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub enum ClusterVisitorError { ClusterDoesNotExist, ClusterGovParamsNotSet, diff --git a/traits/src/customer.rs b/traits/src/customer.rs index 06cac510c..09fd25ea4 100644 --- a/traits/src/customer.rs +++ b/traits/src/customer.rs @@ -1,3 +1,7 @@ +use codec::{Decode, Encode}; +use sp_runtime::RuntimeDebug; +use scale_info::TypeInfo; + pub trait CustomerCharger { fn charge_content_owner( content_owner: T::AccountId, @@ -6,6 +10,7 @@ pub trait CustomerCharger { ) -> Result; } +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo, PartialEq)] pub enum CustomerChargerError { NotOwner, ArithmeticUnderflow,