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(pallet): use unit mUSD for setting service contracts prices #746

Merged
merged 2 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 18 additions & 0 deletions docs/architecture/0003-service-consumer-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 3. Service consumer contract

Date: 2022-10-17

## Status

Accepted

## Context

See [here](https://github.com/threefoldtech/tfchain/issues/445) for more details.

## Decision

It is now possible to create generic contract between two `TFChain` users (without restriction of account type) for some "away from the grid" service and bill for it.

The service consumer contract specs are described [here](../../substrate-node/pallets/pallet-smart-contract/service_consumer_contract_specs.md).
See [here](../../substrate-node/pallets/pallet-smart-contract/service_consumer_contract_flow.md) how to use this feature.
25 changes: 0 additions & 25 deletions docs/architecture/0003-third-party-service-contract.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Service/Consumer contracts

It is now possible to create generic contract between two TFChain users (without restriction of account type) for some service and bill for it.
It is now possible to create generic contract between two TFChain users (without restriction of account type) for some "away from the grid" service and bill for it.


## How does it work?
Expand Down Expand Up @@ -38,7 +38,7 @@ Be aware that calling the extrinsic a second time will create a new contract wit

## Step 2: Fill contract

Once created, the contract must be filled with its relative `per hour` fees (only service can set fees):
Once created, the contract must be filled with its relative fees in `mUSD / hour` (only service can set fees):

~~~rust
service_contract_set_fees(
Expand Down Expand Up @@ -112,7 +112,7 @@ where `T` is the elapsed time, in seconds and bounded by 3600 (see above), since
Note that if `variable_amount` is too high (i.e `variable_amount > variable_fee * T / 3600`) the billing extrinsic will fail.
The `variable_fee` value in the contract is interpreted as being "per hour" and acts as a protection mechanism to avoid consumer draining.
Indeed, as it is technically possible for the service to send a bill every second, there would be no gain for that (unless overloading the chain uselessly).
So it is also the service responsability to set a suitable `variable_amount` according to the billing frequency!
So it is also the service responsability to set a suitable `variable_amount` (in mUSD) according to the billing frequency!

Also be aware that if the consumer is out of funds the billing will fail AND the contract will be canceled.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ A contract will work simple client - server principle (i.e. the "buyer" and the

- consumer twin id
- service twin id
- base fee, this is the fixed amount which will be billed hourly
- variable fee, this is the maximum amount which can be billed on top of the base fee (for variable consumption metrics, to be defined by the service)
- base fee, this is the fixed amount (in mUSD) which will be billed hourly
- variable fee, this is the maximum amount (in mUSD) which can be billed on top of the base fee (for variable consumption metrics, to be defined by the service)
- metadata, a field which just holds some bytes. The service can use this any way it likes (including having stuff set by the user). We limit this field to some size now, suggested 64 bytes (2 public keys generally)

Additionally, we also keep track of some metadata, which will be:
Expand All @@ -22,7 +22,7 @@ Additionally, we also keep track of some metadata, which will be:

Once a contract is accepted by both the consumer and the service, the chain can start accepting "bill reports" from the service for the contract. Only the twin of the service can send these, as specified in the contract. The bill contains the following:

- Variable amount which is billed. The chain checks that this is less than or equal to the variable amount as specified in the contract, if it is higher the bill is rejected for overcharging. Technically the service could send a report every second to drain the user. To protect against this, the max variable amount in the contract is interpreted as being "per hour", and the value set in the contract is divided by 3600, multiplied by window size, to find the actual maximum which can be billed by the contract.
- Variable amount (in mUSD) which is billed. The chain checks that this is less than or equal to the variable amount as specified in the contract, if it is higher the bill is rejected for overcharging. Technically the service could send a report every second to drain the user. To protect against this, the max variable amount in the contract is interpreted as being "per hour", and the value set in the contract is divided by 3600, multiplied by window size, to find the actual maximum which can be billed by the contract.
- Window, this is the amount of time (in seconds) covered since last contract. The chain verifies that `current time - window >= contract.last_bill`, such that no bills overlap to avoid overcharging. Combined with the previous limit to variable amount this prevents the service from overcharging the user.
- Some optional metadata, this will again just be some bytes (the service decides how this can be interpreted). For now we'll limit this to 50 bytes or so.

Expand All @@ -38,7 +38,7 @@ Once a contract is accepted by both the consumer and the service, the chain can

### Callable by service

- `set_fees(base, variable)`: Sets the base fee and variable fee on the contract
- `set_fees(base, variable)`: Sets the base fee and variable fee on the contract (both in mUSD)
- `reject_by_service()`: Rejects the contract, deleting it.
- `approve_by_service()`: Sets the `service_accepted` flag on the contract. After this, no more modifications to fees or metadata can be done

Expand Down
8 changes: 6 additions & 2 deletions substrate-node/pallets/pallet-smart-contract/src/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,14 @@ impl ServiceContract {
&self,
service_bill: ServiceContractBill,
) -> u64 {
// Convert fees from mUSD to units USD
let base_fee_units_usd = self.base_fee * 10000;
let variable_amount_units_usd = service_bill.variable_amount * 10000;
// bill user for service usage for elpased usage (window) in seconds
let contract_cost = U64F64::from_num(self.base_fee) * U64F64::from_num(service_bill.window)
let contract_cost = U64F64::from_num(base_fee_units_usd)
* U64F64::from_num(service_bill.window)
/ U64F64::from_num(T::BillingReferencePeriod::get())
+ U64F64::from_num(service_bill.variable_amount);
+ U64F64::from_num(variable_amount_units_usd);
contract_cost.round().to_num::<u64>()
}
}
Expand Down