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

feat: add task to let MSP charge user every X blocks #268

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

undercover-cactus
Copy link
Contributor

This PR add a NotifyPeriod event that would be triggered every X blocks. It helps notifyng the MSP task that X blocks has passed and that it can charge users for their service.

The amount of blocks (aka the msp charging frequency) is a configurable value that can be passed through the CLI.

if let Some(notify_period) = msp_charging_freq {
self.with_notify_period(notify_period.clone());
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not super happy for having to do this with all different setup function. It is a bit redundant with the builder strategy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. In fact all the functions that are not storage-layer-or-provider-type-specific (i.e. that they don't need to have a special implementation for a provider type and storage layer), I'd leave them out of this setup.

In other words, to make proper use of the builder pattern I would:

  1. Remove the use of with_notify_period from here, and call it in service.rs (in init_sh_builder()), in case the CLI parameter was passed.
  2. Do the same with with_max_storage_capacity, with_jump_capacity, with_retry_timeout, as they seem to be called in the same way, regarless of what the provider type and storage layer is. Well almost, in the User is different. But anyway, these tiny initialisation differences are exactly what the builder pattern is meant to be good for.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, I'd like to get @snowmead involved since he was heavily involved in this code.

.storage_hub_handler
.blockchain
.query_storage_provider_id(None)
.await?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't MSP task have access to their own msp id ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nicer to not have to do this, indeed.

But when starting up, the only known thing is the account - the provider id associated is known by the runtime.

}

// Verify that the MSP charged the users after the notified
await userApi.assert.eventPresent("paymentStreams", "PaymentStreamCharged");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably verify the amount charged to make sure it is correct.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

test/suites/integration/bsp/debt-collection.test.ts Outdated Show resolved Hide resolved
test/suites/integration/msp/debt-collection.test.ts Outdated Show resolved Hide resolved
}

const txs = [];
for (let i = 0; i < source.length; i++) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for readability this can just be for (const file of source) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use ´i´ as the index for the destination too. In order to use a for of loop we would need to modify a bit more.

test/suites/integration/msp/debt-collection.test.ts Outdated Show resolved Hide resolved
test/suites/integration/msp/debt-collection.test.ts Outdated Show resolved Hide resolved
test/suites/integration/msp/debt-collection.test.ts Outdated Show resolved Hide resolved
test/suites/integration/msp/debt-collection.test.ts Outdated Show resolved Hide resolved
@undercover-cactus undercover-cactus marked this pull request as ready for review November 25, 2024 12:18
Copy link
Collaborator

@timbrinded timbrinded left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates, tests look lots better. Had some follow up comments but low priority

test/suites/integration/msp/debt-collection.test.ts Outdated Show resolved Hide resolved
test/suites/integration/msp/debt-collection.test.ts Outdated Show resolved Hide resolved
invariant(fileKeys2.length === 2, "Expected 2 file keys");

// Allow time for the MSP to update the local forest root
await sleep(3000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an associated log line to wait on?

await userApi.sealBlock(txs, shUser);

// Allow time for the MSP to receive and store the file from the user
await sleep(3000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better to wait for the log line: File upload complete

Copy link
Contributor

@ffarall ffarall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly great job! Just a handful of minor suggestions and comments.

Comment on lines 1309 to 1315
fn check_for_notify(&self, block_number: &BlockNumber) {
if let Some(np) = self.notify_period {
if block_number % np == 0 {
self.emit(NotifyPeriod {});
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fits better in utils.rs

node/src/cli.rs Outdated
@@ -109,6 +109,12 @@ pub struct ProviderConfigurations {
/// Extrinsic retry timeout in seconds.
#[clap(long, default_value = "60")]
pub extrinsic_retry_timeout: u64,

/// MSP charging fees frequency.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to add more info like the unit of this parameter. For example, is it in number of blocks? And if so, could be helpful to give some parameter like "Setting it to 600, with 6 seconds blocks, means charging every 1 hour".

Also, wouldn't this be a period instead of a frequency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes period is better. I changed every 'freq' with 'period'.

test/suites/integration/msp/debt-collection.test.ts Outdated Show resolved Hide resolved
}

// Verify that the MSP charged the users after the notified
await userApi.assert.eventPresent("paymentStreams", "PaymentStreamCharged");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

node/src/tasks/msp_charge_fees.rs Outdated Show resolved Hide resolved
node/src/services/handler.rs Outdated Show resolved Hide resolved

match charging_result {
Ok(submitted_transaction) => {
info!(target: LOG_TARGET, "Submitted extrinsic to charge users with debt: {}", submitted_transaction.hash());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to make this a debug log.

let charging_result = self
.storage_hub_handler
.blockchain
.send_extrinsic(call, Tip::from(0))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to use watch_for_success here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For BSP, we are not watching for success. Do we want to have it there too ?

use crate::tasks::{FileStorageT, MspForestStorageHandlerT, StorageHubHandler};

const LOG_TARGET: &str = "msp-charge-fees-task";
const MIN_DEBT: Balance = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These constants and the growing number of configuration parameters for BSPs/MSPs makes it increasingly compelling to have a way to configure them through a configuration file (like a .yml/.toml or similar). If you feel like that's something you'd like to do, setup a task with @santikaplan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants