threefoldtech / tfchain

Threefold Chain.
Apache License 2.0
15 stars 11 forks source link

refactor: Billing refactor #992

Closed sameh-farouk closed 1 month ago

sameh-farouk commented 2 months ago

Description

This PR addresses several issues and introduces significant changes to the billing in the pallet-smart-contract module. Here are the key changes and the issues they resolve:

High-Level Changes:

Find below more details

Key Changes and Improvements:

Refactoring Billing Logic:

The billing logic has been refactored to improve the billing feature for smart contracts. This includes more precise tracking of contract payments, handling of overdrafts, and fund reservations.

Improved off-chain worker logic

Updated off-chain worker logic to handle billing more effectively. is_next_block_author function was removed and the verification is now happening at runtime, ensuring that transaction fees are reliably waived for validators and still preventing duplicate transactions. It also implements a guaranteed execution layer for OCWs by enabling redundancy.

Benefits:

Additionally, the improved logging ensures that the off-chain worker function works correctly without silent failures.

Introduction of ContractPaymentState:

Introducing ContractPaymentState aligns with the need to track and manage contract payment states accurately, especially during grace periods and when handling overdue payments. It fixes the issue where the amount tracked in the contract lock is incorrectly divergent from the lock on the user balance, leading to illiquidity issues when it's time to distribute rewards.

Improved Error Handling:

Enhanced error-handling mechanisms have been introduced to ensure that errors during billing are logged and appropriately managed, reducing the likelihood of contracts entering a dirty state.

Also, New error types were added RewardDistributionError and ContractPaymentStateNotExists.

Enhanced Events:

New events have been added, ContractGracePeriodElapsed, ContractPaymentOverdrafted, and RewardDistributed, to provide better visibility into the billing and payment processes.

ContractPaymentOverdrawn: This event is closely related to the ContractBilled event. Both events provide information about the amount due for the previous billing period. The ContractBilled event indicates that the due amount was successfully charged to the user's account. In contrast, the ContractPaymentOverdrawn event signifies that the due amount was not fully covered and has been added to the contract's overdraft. This distinction helps in accurately tracking the financial state of contracts.

RewardDistributed: This event signifies the successful distribution of rewards after the billing cycle, providing transparency and traceability for reward payouts.

ContractGracePeriodElapsed: This event is triggered when a contract has exceeded its grace period without sufficient funds, leading to its transition to a deleted state.

Other Events changes:

Issues Addressed:

Issue #996

Issue #991:

Issue #990:

Issue #979:

Issue #969 :

Issue #932 :

Issue #834:

Issue #994:

Overall, this PR enhances the billing capabilities of the pallet-smart-contract module, making it more robust and reliable.

TODO

Some reconsideration might be needed, such as:

Checklist:

iwanbk commented 1 month ago

Looks like this PR is quite big and contains many things.

Is it possible to split the PR into smaller things? Smaller PR will make it easier to test and review.

sameh-farouk commented 1 month ago

Looks like this PR is quite big and contains many things.

Is it possible to split the PR into smaller things? Smaller PR will make it easier to test and review.

Hi @iwanbk Thank you for your feedback. I understand the concern about the size of the PR. However, this PR is a comprehensive refactor of a large function, which includes modified structure, function names, and numerous fixes. Splitting it into smaller PRs would complicate the process for several reasons:

I believe keeping the PR as a single unit will ensure a smoother review and testing process. I am, of course, open to any specific suggestions you might have to improve the PR.

sameh-farouk commented 1 month ago

Thank you @renauter for your review! I have either addressed or replied to your notes. Please review and let me know if you have any further comments.

sameh-farouk commented 1 month ago
[2024-08-25T23:16:53Z INFO  pallet_smart_contract::migrations::v12]  >>> Unused Smart Contract pallet V12 migration
[2024-08-25T23:16:53Z INFO  try-runtime::cli] PoV size (zstd-compressed compact proof): 579.5 KB. For parachains, it's your responsibility to verify that a PoV of this size fits within any relaychain constraints.
[2024-08-25T23:16:53Z INFO  try-runtime::cli] Consumed ref_time: 1.464225s (73.21% of max 2s)
[2024-08-25T23:16:53Z INFO  try-runtime::cli] āœ… No weight safety issues detected. Please note this does not guarantee a successful runtime upgrade. Always test your runtime upgrade with recent state, and ensure that the weight usage of your migrations will not drastically differ between testing and actual on-chain execution.

Storage migration tested on Mainnet with try-runtime

renauter commented 1 month ago

Before starting to distribute rewards (which funds should come from previously reserved amounts) I feel a lack of extra check similar to ensure( account_reserved >= standard_rewards + additional_rewards).

Maybe located here: https://github.com/threefoldtech/tfchain/blob/b4431147e74987f9a1119f61910855973eae2f71/substrate-node/pallets/pallet-smart-contract/src/billing.rs#L507 With some comment like // At this point we don't expect any distribution failure since every fund to transfer should have been accumulated in account reserve along previous billing cycles

It would prevent some failure in billing logic, since funds reservation is at different phase in code (after some cycles of reseration everything reserved is transfered), and add better readability to code.

What do you think ?

sameh-farouk commented 1 month ago

Before starting to distribute rewards (which funds should come from previously reserved amounts) I feel a lack of extra check similar to ensure( account_reserved >= standard_rewards + additional_rewards).

Maybe located here:

https://github.com/threefoldtech/tfchain/blob/b4431147e74987f9a1119f61910855973eae2f71/substrate-node/pallets/pallet-smart-contract/src/billing.rs#L507

With some comment like // At this point we don't expect any distribution failure since every fund to transfer should have been accumulated in account reserve along previous billing cycles It would prevent some failure in billing logic, since funds reservation is at different phase in code (after some cycles of reseration everything reserved is transfered), and add better readability to code.

What do you think ?

I added the comment for clarity.

sameh-farouk commented 1 month ago

One last change I switched from using ValueQuery to OptionQuery for the ContractPaymentState. It is more prone to error since it always returns a default value from storage. Although we ensure that every contract has a payment state, the default value does not make sense here. Imagine calculating the cost for a contract where the last updated time in the payment state is 0!

I prefer to have a None returned and fail gracefully, rather than having a default value.

renauter commented 1 month ago

I switched from using ValueQuery to OptionQuery for the ContractPaymentState. It is more prone to error since it always returns a default value from storage.

Indeed, if we don't make a specific usage of a default value it is more convenient to use OptionQuery.

sameh-farouk commented 1 month ago

@renauter Thank you so much for reviewing! It was really helpful.

renauter commented 1 month ago

@sameh-farouk Thank you for your tremendous work!! This refactoring was a very hard task šŸ‘šŸ»šŸ‘šŸ»šŸ‘šŸ»