Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: pay_outstanding_debt extrinsic of pallet-payment-streams #264

Merged
merged 6 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api-augment/dist/interfaces/lookup.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api-augment/dist/interfaces/lookup.js.map

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions api-augment/dist/types/interfaces/augment-api-runtime.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ declare module "@polkadot/api-base/types/calls" {
};
/** 0x1078d7ac24a07b0e/1 */
paymentStreamsApi: {
/**
* Get the Providers that have at least one payment stream with a specific user.
**/
getProvidersWithPaymentStreamsWithUser: AugmentedCall<
ApiType,
(userAccount: AccountId | string | Uint8Array) => Observable<Vec<ProviderId>>
>;
/**
* Get the payment streams of a provider.
**/
Expand Down
23 changes: 13 additions & 10 deletions api-augment/dist/types/interfaces/augment-api-tx.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2446,28 +2446,31 @@ declare module "@polkadot/api-base/types/submittable" {
[H256, AccountId32]
>;
/**
* Dispatchable extrinsic that allows a user flagged as without funds to pay up to `amount_of_streams_to_pay`
* remaining payment streams to be able to recover its deposits.
* Dispatchable extrinsic that allows a user flagged as without funds to pay the Providers that still have payment streams
* with it, in order to recover as much of its deposits as possible.
*
* The dispatch origin for this call must be Signed.
* The origin must be the User that has been flagged as without funds.
*
* This extrinsic will perform the following checks and logic:
* 1. Check that the extrinsic was signed and get the signer.
* 2. Check that the user has been flagged as without funds.
* 3. Release the user's funds that were held as a deposit for each payment stream.
* 4. Get the payment streams of the user and charge them, paying the Providers for the services.
* 3. Release the user's funds that were held as a deposit for each payment stream to be paid.
* 4. Get the payment streams that the user has with the provided list of Providers, and pay them for the services.
* 5. Delete the charged payment streams of the user.
*
* Emits a 'UserPaidAllDebts' event when successful.
* Emits a 'UserPaidSomeDebts' event when successful if the user has remaining debts. If the user has successfully paid all its debts,
* it emits a 'UserPaidAllDebts' event.
*
* Notes: this extrinsic iterates over payment streams of the user and charges them, so it can be expensive in terms of weight.
* The fee to execute it should be high enough to compensate for the weight of the extrinsic, without being too high that the user
* finds more convenient to wait for Providers to get its deposits one by one instead.
* Notes: this extrinsic iterates over the provided list of Providers, getting the payment streams they have with the user and charging
* them, so the execution could get expensive. It's recommended to provide a list of Providers that the user actually has payment streams with,
* which can be obtained by calling the `get_providers_with_payment_streams_with_user` runtime API.
* There was an idea to limit the amount of Providers that can be received by this extrinsic using a constant in the configuration of this pallet,
* but the correct benchmarking of this extrinsic should be enough to avoid any potential abuse.
**/
payOutstandingDebt: AugmentedSubmittable<
(amountOfStreamsToPay: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>,
[u32]
(providers: Vec<H256> | (H256 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>,
[Vec<H256>]
>;
/**
* Dispatchable extrinsic that allows root to update an existing dynamic-rate payment stream between a User and a Provider.
Expand Down
2 changes: 1 addition & 1 deletion api-augment/dist/types/interfaces/lookup.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3514,7 +3514,7 @@ declare const _default: {
userAccounts: string;
};
pay_outstanding_debt: {
amountOfStreamsToPay: string;
providers: string;
};
clear_insolvent_flag: string;
};
Expand Down
2 changes: 1 addition & 1 deletion api-augment/dist/types/interfaces/types-lookup.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4287,7 +4287,7 @@ declare module "@polkadot/types/lookup" {
} & Struct;
readonly isPayOutstandingDebt: boolean;
readonly asPayOutstandingDebt: {
readonly amountOfStreamsToPay: u32;
readonly providers: Vec<H256>;
} & Struct;
readonly isClearInsolventFlag: boolean;
readonly type:
Expand Down
7 changes: 7 additions & 0 deletions api-augment/src/interfaces/augment-api-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,13 @@ declare module "@polkadot/api-base/types/calls" {
};
/** 0x1078d7ac24a07b0e/1 */
paymentStreamsApi: {
/**
* Get the Providers that have at least one payment stream with a specific user.
**/
getProvidersWithPaymentStreamsWithUser: AugmentedCall<
ApiType,
(userAccount: AccountId | string | Uint8Array) => Observable<Vec<ProviderId>>
>;
/**
* Get the payment streams of a provider.
**/
Expand Down
23 changes: 13 additions & 10 deletions api-augment/src/interfaces/augment-api-tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2104,28 +2104,31 @@ declare module "@polkadot/api-base/types/submittable" {
[H256, AccountId32]
>;
/**
* Dispatchable extrinsic that allows a user flagged as without funds to pay up to `amount_of_streams_to_pay`
* remaining payment streams to be able to recover its deposits.
* Dispatchable extrinsic that allows a user flagged as without funds to pay the Providers that still have payment streams
* with it, in order to recover as much of its deposits as possible.
*
* The dispatch origin for this call must be Signed.
* The origin must be the User that has been flagged as without funds.
*
* This extrinsic will perform the following checks and logic:
* 1. Check that the extrinsic was signed and get the signer.
* 2. Check that the user has been flagged as without funds.
* 3. Release the user's funds that were held as a deposit for each payment stream.
* 4. Get the payment streams of the user and charge them, paying the Providers for the services.
* 3. Release the user's funds that were held as a deposit for each payment stream to be paid.
* 4. Get the payment streams that the user has with the provided list of Providers, and pay them for the services.
* 5. Delete the charged payment streams of the user.
*
* Emits a 'UserPaidAllDebts' event when successful.
* Emits a 'UserPaidSomeDebts' event when successful if the user has remaining debts. If the user has successfully paid all its debts,
* it emits a 'UserPaidAllDebts' event.
*
* Notes: this extrinsic iterates over payment streams of the user and charges them, so it can be expensive in terms of weight.
* The fee to execute it should be high enough to compensate for the weight of the extrinsic, without being too high that the user
* finds more convenient to wait for Providers to get its deposits one by one instead.
* Notes: this extrinsic iterates over the provided list of Providers, getting the payment streams they have with the user and charging
* them, so the execution could get expensive. It's recommended to provide a list of Providers that the user actually has payment streams with,
* which can be obtained by calling the `get_providers_with_payment_streams_with_user` runtime API.
* There was an idea to limit the amount of Providers that can be received by this extrinsic using a constant in the configuration of this pallet,
* but the correct benchmarking of this extrinsic should be enough to avoid any potential abuse.
**/
payOutstandingDebt: AugmentedSubmittable<
(amountOfStreamsToPay: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>,
[u32]
(providers: Vec<H256> | (H256 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>,
[Vec<H256>]
>;
/**
* Dispatchable extrinsic that allows root to update an existing dynamic-rate payment stream between a User and a Provider.
Expand Down
2 changes: 1 addition & 1 deletion api-augment/src/interfaces/lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3590,7 +3590,7 @@ export default {
userAccounts: "Vec<AccountId32>"
},
pay_outstanding_debt: {
amountOfStreamsToPay: "u32"
providers: "Vec<H256>"
},
clear_insolvent_flag: "Null"
}
Expand Down
2 changes: 1 addition & 1 deletion api-augment/src/interfaces/types-lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4498,7 +4498,7 @@ declare module "@polkadot/types/lookup" {
} & Struct;
readonly isPayOutstandingDebt: boolean;
readonly asPayOutstandingDebt: {
readonly amountOfStreamsToPay: u32;
readonly providers: Vec<H256>;
} & Struct;
readonly isClearInsolventFlag: boolean;
readonly type:
Expand Down
2 changes: 1 addition & 1 deletion api-augment/storagehub.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pallets/payment-streams/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ sp_api::decl_runtime_apis! {
{
fn get_users_with_debt_over_threshold(provider_id: &ProviderId, threshold: Balance) -> Result<Vec<AccountId>, GetUsersWithDebtOverThresholdError>;
fn get_users_of_payment_streams_of_provider(provider_id: &ProviderId) -> Vec<AccountId>;
fn get_providers_with_payment_streams_with_user(user_account: &AccountId) -> Vec<ProviderId>;
}
}

Expand Down
63 changes: 45 additions & 18 deletions pallets/payment-streams/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ mod benchmarks {
let expected_event =
<T as pallet::Config>::RuntimeEvent::from(Event::<T>::FixedRatePaymentStreamCreated {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
rate: rate.into(),
});
frame_system::Pallet::<T>::assert_last_event(expected_event.into());
Expand Down Expand Up @@ -274,7 +274,7 @@ mod benchmarks {
let charge_event =
<T as pallet::Config>::RuntimeEvent::from(Event::<T>::PaymentStreamCharged {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
amount: amount_charged,
last_tick_charged: frame_system::Pallet::<T>::block_number(),
charged_at_tick: frame_system::Pallet::<T>::block_number(),
Expand All @@ -284,7 +284,7 @@ mod benchmarks {
let expected_event =
<T as pallet::Config>::RuntimeEvent::from(Event::<T>::FixedRatePaymentStreamUpdated {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
new_rate: new_rate.into(),
});
frame_system::Pallet::<T>::assert_last_event(expected_event.into());
Expand Down Expand Up @@ -348,7 +348,7 @@ mod benchmarks {
let charge_event =
<T as pallet::Config>::RuntimeEvent::from(Event::<T>::PaymentStreamCharged {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
amount: rate.into(),
last_tick_charged: frame_system::Pallet::<T>::block_number(),
charged_at_tick: frame_system::Pallet::<T>::block_number(),
Expand All @@ -358,7 +358,7 @@ mod benchmarks {
let expected_event =
<T as pallet::Config>::RuntimeEvent::from(Event::<T>::FixedRatePaymentStreamDeleted {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
});
frame_system::Pallet::<T>::assert_last_event(expected_event.into());

Expand Down Expand Up @@ -404,7 +404,7 @@ mod benchmarks {
let expected_event = <T as pallet::Config>::RuntimeEvent::from(
Event::<T>::DynamicRatePaymentStreamCreated {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
amount_provided: amount_provided.into(),
},
);
Expand Down Expand Up @@ -495,7 +495,7 @@ mod benchmarks {
let charge_event =
<T as pallet::Config>::RuntimeEvent::from(Event::<T>::PaymentStreamCharged {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
amount: amount_charged,
last_tick_charged: frame_system::Pallet::<T>::block_number(),
charged_at_tick: frame_system::Pallet::<T>::block_number(),
Expand All @@ -505,7 +505,7 @@ mod benchmarks {
let expected_event = <T as pallet::Config>::RuntimeEvent::from(
Event::<T>::DynamicRatePaymentStreamUpdated {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
new_amount_provided: new_amount_provided.into(),
},
);
Expand Down Expand Up @@ -588,7 +588,7 @@ mod benchmarks {
let charge_event =
<T as pallet::Config>::RuntimeEvent::from(Event::<T>::PaymentStreamCharged {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
amount: amount_charged,
last_tick_charged: frame_system::Pallet::<T>::block_number(),
charged_at_tick: frame_system::Pallet::<T>::block_number(),
Expand All @@ -599,7 +599,7 @@ mod benchmarks {
let expected_event = <T as pallet::Config>::RuntimeEvent::from(
Event::<T>::DynamicRatePaymentStreamDeleted {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
},
);
frame_system::Pallet::<T>::assert_last_event(expected_event.into());
Expand Down Expand Up @@ -676,7 +676,7 @@ mod benchmarks {
let charge_event =
<T as pallet::Config>::RuntimeEvent::from(Event::<T>::PaymentStreamCharged {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
amount: amount_charged,
last_tick_charged: frame_system::Pallet::<T>::block_number(),
charged_at_tick: frame_system::Pallet::<T>::block_number(),
Expand Down Expand Up @@ -764,7 +764,7 @@ mod benchmarks {
let charge_event =
<T as pallet::Config>::RuntimeEvent::from(Event::<T>::PaymentStreamCharged {
user_account: user_account.clone(),
provider_id: provider_id.clone(),
provider_id,
amount: amount_charged,
last_tick_charged: frame_system::Pallet::<T>::block_number(),
charged_at_tick: frame_system::Pallet::<T>::block_number(),
Expand All @@ -780,7 +780,7 @@ mod benchmarks {
/*********** Setup initial conditions: ***********/
// Set up an account with some balance.
let user_account: T::AccountId = account("Alice", 0, 0);
let user_balance = match 1_000_000_000_000_000u128.try_into() {
let user_balance = match 1_000_000_000_000_000_000u128.try_into() {
Ok(balance) => balance,
Err(_) => return Err(BenchmarkError::Stop("Balance conversion failed.")),
};
Expand All @@ -789,21 +789,25 @@ mod benchmarks {
user_balance,
));

// Since we have to create a payment stream per iteration, the easiest way is to set up
// Since we have to create a dynamic-rate and a fixed-rate payment stream per iteration
// to analyze the worst case scenario, the easiest way is to set up
// a Provider with an account with some balance per iteration number.
let n: u32 = n.into();
let mut provider_ids: Vec<ProviderIdFor<T>> = Vec::new();
for i in 0..n {
let (_provider_account, provider_id) = register_provider::<T>(i)?;
let provider_id: ProviderIdFor<T> = provider_id.into();
let amount_provided = 1000u32;
let rate = 100u32;

// Ensure that a payment stream between the user and this provider does not exist
// Ensure that a dynamic-rate payment stream between the user and this provider does not exist
let dynamic_rate_stream =
DynamicRatePaymentStreams::<T>::get(provider_id, user_account.clone());
match dynamic_rate_stream {
Some(_) => {
return Err(BenchmarkError::Stop("Payment stream already exists."));
return Err(BenchmarkError::Stop(
"Dynamic-rate payment stream already exists.",
));
}
None => {}
}
Expand All @@ -819,6 +823,29 @@ mod benchmarks {
BenchmarkError::Stop("Dynamic rate payment stream not created successfully.")
})?;

// Ensure that a fixed-rate payment stream between the user and this provider does not exist
let fixed_rate_stream =
FixedRatePaymentStreams::<T>::get(provider_id, user_account.clone());
match fixed_rate_stream {
Some(_) => {
return Err(BenchmarkError::Stop(
"Fixed-rate payment stream already exists.",
));
}
None => {}
}

// Create the fixed-rate payment stream
Pallet::<T>::create_fixed_rate_payment_stream(
RawOrigin::Root.into(),
provider_id,
user_account.clone(),
rate.into(),
)
.map_err(|_| {
BenchmarkError::Stop("Fixed rate payment stream not created successfully.")
})?;

provider_ids.push(provider_id);
}

Expand All @@ -828,7 +855,7 @@ mod benchmarks {
last_chargeable_tick: frame_system::Pallet::<T>::block_number(),
price_index: AccumulatedPriceIndex::<T>::get(),
};
for provider_id in provider_ids {
for provider_id in provider_ids.clone() {
update_last_chargeable_info::<T>(provider_id, new_last_chargeable_info.clone());
}

Expand All @@ -840,7 +867,7 @@ mod benchmarks {

/*********** Call the extrinsic to benchmark: ***********/
#[extrinsic_call]
_(RawOrigin::Signed(user_account.clone()), n);
_(RawOrigin::Signed(user_account.clone()), provider_ids);

/*********** Post-benchmark checks: ***********/
// Verify that the user paid all debts event was emitted for the user
Expand Down
Loading
Loading