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: new generic service-consumer contract for pallet-smart-contract #495

Closed
Show file tree
Hide file tree
Changes from 71 commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
2ec5aad
FEAT: moved contract billing to offchain worker #269
brandonpille Oct 3, 2022
fba68f7
FEAT: fixing tests #269
brandonpille Oct 3, 2022
5ed07fe
FEAT: modified pallet-smart-contract tests #269
brandonpille Aug 3, 2022
0d097f9
FEAT: Move registering extensions to offchainify function #269
brandonpille Aug 18, 2022
30640b9
wip
DylanVerstraete Aug 3, 2022
31a7e94
FEAT: fixed failing test #269
brandonpille Aug 3, 2022
452298c
FEAT: fixed all tests #269
brandonpille Aug 5, 2022
113fff3
FEAT:wrote our own TransactionPool mock that allows you to tell what …
brandonpille Aug 11, 2022
50b98b6
FEAT:rewrote some tests + inline documentation + better mocking #269
brandonpille Aug 12, 2022
25a44dc
FEAT: removed unused imports #269
brandonpille Aug 12, 2022
8146d20
FEAT: fixed a small mistake + commented the tests #269
brandonpille Aug 17, 2022
b29d5c2
FEAT: fixed merge conflicts #269
brandonpille Aug 18, 2022
b751c1e
FEAT: fixed transaction pool error handeling behavior #269
brandonpille Aug 18, 2022
ed4de4a
FEAT: cleaning up code #269
brandonpille Aug 19, 2022
c642031
FEAT: last log improvement #269
brandonpille Aug 19, 2022
99c59e6
chore: extend scripts
DylanVerstraete Aug 25, 2022
f0f7583
FEAT: remove duplicates before reinserting for next block #269
brandonpille Sep 5, 2022
25a1804
FEAT: more proper way to avoid dupplicates #269
brandonpille Sep 5, 2022
461bcea
FEAT: fixed tests #269
brandonpille Sep 5, 2022
15c0907
FEAT: debug logging when amount is 0 #269
brandonpille Sep 5, 2022
970d10f
FEAT: implementation unauthorized billing #269
brandonpille Sep 6, 2022
e7815f0
FEAT: check if signer can sign before sending transaction #269
brandonpille Sep 7, 2022
41bd1a1
Revert "FEAT: implementation unauthorized billing #269"
DylanVerstraete Sep 20, 2022
35b1524
chore: reinstate one line of code
DylanVerstraete Sep 20, 2022
2ce047a
feat: review comments #269
brandonpille Oct 3, 2022
b63dfac
feat: fixed issue due to rebase #269
brandonpille Oct 3, 2022
668ef5f
feat: rework contract billing storage (#477)
DylanVerstraete Oct 12, 2022
e990ad9
chore: cleanup
DylanVerstraete Oct 12, 2022
052d7a1
chore: update chart
DylanVerstraete Oct 12, 2022
6f690d1
chore: merge development
DylanVerstraete Oct 12, 2022
880185c
chore: add adr for changes
DylanVerstraete Oct 12, 2022
5714a03
wip
DylanVerstraete Oct 5, 2022
ee92733
more wip
DylanVerstraete Oct 6, 2022
d5160a5
chore: some cleaning
DylanVerstraete Oct 7, 2022
fc5b015
chore: test artifact upload
DylanVerstraete Oct 7, 2022
53836f7
chore: fix yaml
DylanVerstraete Oct 7, 2022
3be2d5f
chore: fix yaml again
DylanVerstraete Oct 7, 2022
7b13f78
chore: upload artifact if job failed
DylanVerstraete Oct 7, 2022
4caf435
chore: wait x blocks after cancel
DylanVerstraete Oct 10, 2022
4320ee0
chore: move get index to function
DylanVerstraete Oct 10, 2022
a5cce78
chore: refactor
DylanVerstraete Oct 10, 2022
0321751
fix: billing
DylanVerstraete Oct 10, 2022
74bfc0d
feat: initial commit #473
brandonpille Oct 4, 2022
b621682
feat: added events and extrinsic for creating deployment contract #47…
brandonpille Oct 5, 2022
1d916a3
feat: major implementation for feature #473
brandonpille Oct 7, 2022
62c2027
feat: made update_node_contract deprecated #473
brandonpille Oct 7, 2022
8a44388
feat: fixed tests #473
brandonpille Oct 7, 2022
78731ca
feat: fixed integration tests #473
brandonpille Oct 7, 2022
f3b6277
feat: lets try fixing int tests #473
brandonpille Oct 7, 2022
a38b72d
feat: modified event name #473
brandonpille Oct 10, 2022
690775f
feat: added extra tests #473
brandonpille Oct 10, 2022
77f9cb8
feat: extra tests and cleanup code #473
brandonpille Oct 11, 2022
0a9e165
feat: fixed mergeconflicts #473
brandonpille Oct 11, 2022
611d6ff
feat: implementation of contract policy (aka grouping of contracts) #…
brandonpille Oct 11, 2022
4aad91f
feat: fixed merge conflicts #473
brandonpille Oct 12, 2022
aceb176
feat: fixed integration tests #473
brandonpille Oct 12, 2022
7e536d1
feat: implementation done and code can be build
brandonpille Oct 18, 2022
8cbd6a9
fixed most tests
brandonpille Oct 18, 2022
e22f18c
fixed all tests
brandonpille Oct 18, 2022
bedc58c
improved tests
brandonpille Oct 20, 2022
d45fe28
fixed unit tests
brandonpille Oct 21, 2022
793e67c
tests on grouping
brandonpille Oct 21, 2022
b0aa44a
added unit tests on public config filtering
brandonpille Oct 24, 2022
57b62bb
fixed integration tests
brandonpille Oct 25, 2022
8758e06
fixed integration tests
brandonpille Oct 25, 2022
75bb96e
fixed unit tests
brandonpille Oct 25, 2022
251f387
feat: add new service contract type
renauter Oct 26, 2022
f62819d
feat: evolving on service contract approval flow
renauter Oct 27, 2022
0ea8829
test: add service contract basic tests and samll improvements
renauter Oct 28, 2022
986304a
feat: contract approval complete cycle and test improvements
renauter Nov 3, 2022
189f0e1
feat: wip service send bill report
renauter Nov 7, 2022
d5ae226
test: some updates after changing base branch
renauter Nov 7, 2022
01e02a0
test: typo correction
renauter Nov 7, 2022
1e46023
feat service contract billing extrinsic
renauter Nov 8, 2022
a9af8f7
test: fix after changing base branch
renauter Nov 8, 2022
98dbab4
feat: update cost of service contract regarding bill
renauter Nov 8, 2022
d9d2ca2
feat: update time strategy for billing
renauter Nov 8, 2022
bbd3ce8
feat: wip rework service billing
renauter Nov 16, 2022
2b0088e
feat: simplifying service contract billing
renauter Nov 16, 2022
48e870e
feat: simplifying service contract billing (2)
renauter Nov 16, 2022
1234563
feat: handle service contract cancelation
renauter Nov 16, 2022
2488082
Merge branch 'feature/473-implement-power-management-and-capacity-pla…
renauter Nov 17, 2022
36a44f4
fix: resolve conflicts after merge
renauter Nov 17, 2022
e64bb5a
Merge branch 'feature/473-implement-power-management-and-capacity-pla…
renauter Nov 17, 2022
7771c1d
fix: resolve conflicts after merge
renauter Nov 17, 2022
089e5f5
test: service contract cancelation and billing
renauter Nov 18, 2022
4216733
test: refine service contract testing (1)
renauter Nov 18, 2022
c0a8263
test: refine service contract testing (2)
renauter Nov 18, 2022
c769369
test: refine service contract testing (3)
renauter Nov 18, 2022
b7816c7
fix out of funds test and do some cleanup
renauter Nov 18, 2022
aa5d7ef
Merge branch 'feature/473-implement-power-management-and-capacity-pla…
renauter Nov 21, 2022
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
12 changes: 12 additions & 0 deletions .github/workflows/build_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,15 @@ jobs:
cd substrate-node/tests
robot -d _output_tests/ .

- uses: actions/upload-artifact@v3
if: failure()
with:
name: integration test output
path: substrate-node/tests/_output_tests/

- uses: actions/upload-artifact@v3
if: always()
with:
name: integration test output
path: substrate-node/tests/_output_tests/

28 changes: 28 additions & 0 deletions docs/architecture/0002-smart-contract-billing-changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 2. Changed smart contract billing workflow

Date: 2022-10-12

## Status

Accepted

## Context

TFchain bills contract using an internal feature of susbtrate called `on_finalize`. This function / hook is executed after every block, in this function the time to compute is limited since this runs before the block production is finished. We are worried that if the usage of the grid grows, this on finalize method would need to do too much computations and that the block time would be impacted.

## Decision

We searched for an alternative to this `on_finalize` method within the substrate framework. We found a hook that fires after a block is produced, but in that hook the result of some computation must be submitted onchain with an extrinsic. This offchain worker cannot modify chain storage directly, rather it can only do that through an extrinsic.

## Consequences

### the good

- The billing for a contract is now executed after a block is produced and not within that same block.
- Atleast one offchain worker must run with the `smct` keytype in order to bill contracts.
- Contracts billing cycles do not rely anymore on a previously successful bill.
- External users can call `bill_contract` but don't need to.

### the worrying

- If no validators run an offchain worker with the `smct` keytype, no contracts will be billed.
3 changes: 3 additions & 0 deletions substrate-node/Cargo.lock

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

2 changes: 1 addition & 1 deletion substrate-node/charts/substrate-node/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ apiVersion: v2
name: substrate-node
description: Tfchain node
type: application
version: 0.2.4
version: 0.2.5
appVersion: "2.1.1"
19 changes: 19 additions & 0 deletions substrate-node/charts/substrate-node/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,25 @@ spec:
- name: keys
mountPath: /keys
readOnly: true
- name: insert-smct-key
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args: [
"key",
"insert",
"--keystore-path=/keystore",
"--key-type", "smct",
"--suri","/keys/scmt",
"--scheme=sr25519"
]
volumeMounts:
- name: keystore
mountPath: /keystore
- name: keys
mountPath: /keys
readOnly: true
{{ end }}
volumes:
- name: keystore
Expand Down
3 changes: 3 additions & 0 deletions substrate-node/charts/substrate-node/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ keys: []
# secret: 1a...
# - name: tft
# secret: "kkjghjfkkj kjhgkkhhgg"
## Smart contract billing offchain worker key
# - name: smct
# secret: "fsfdsfsdf"

# boot_node: "/ip4/10.42.1.134/tcp/30333/p2p/12D3KooWGX8JFxZu2dDmGVpa9t9enZnFCLhH4NUBA7PDuhEVQTMg"

Expand Down
6 changes: 3 additions & 3 deletions substrate-node/pallets/pallet-dao/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,10 +523,10 @@ impl<T: Config> ChangeNode<PubConfigOf<T>, InterfaceOf<T>> for Pallet<T> {
old_node: Option<&Node<PubConfigOf<T>, InterfaceOf<T>>>,
new_node: &Node<PubConfigOf<T>, InterfaceOf<T>>,
) {
let new_node_weight = Self::get_node_weight(new_node.resources);
let new_node_weight = Self::get_node_weight(new_node.resources.total_resources);
match old_node {
Some(node) => {
let old_node_weight = Self::get_node_weight(node.resources);
let old_node_weight = Self::get_node_weight(node.resources.total_resources);

if node.farm_id != new_node.farm_id {
let mut old_farm_weight = FarmWeight::<T>::get(node.farm_id);
Expand Down Expand Up @@ -554,7 +554,7 @@ impl<T: Config> ChangeNode<PubConfigOf<T>, InterfaceOf<T>> for Pallet<T> {
}

fn node_deleted(node: &Node<PubConfigOf<T>, InterfaceOf<T>>) {
let node_weight = Self::get_node_weight(node.resources);
let node_weight = Self::get_node_weight(node.resources.total_resources);
let mut farm_weight = FarmWeight::<T>::get(node.farm_id);
farm_weight = farm_weight.checked_sub(node_weight).unwrap_or(0);
FarmWeight::<T>::insert(node.farm_id, farm_weight);
Expand Down
5 changes: 5 additions & 0 deletions substrate-node/pallets/pallet-smart-contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-
# Benchmarking
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false, optional = true }

[dev-dependencies]
parking_lot = '0.12.1'
sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false }
env_logger = "*"

[features]
default = ['std']
std = [
Expand Down
105 changes: 44 additions & 61 deletions substrate-node/pallets/pallet-smart-contract/src/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,58 +53,47 @@ impl<T: Config> Contract<T> {
seconds_elapsed: u64,
) -> Result<u64, DispatchErrorWithPostInfo> {
let total_cost = match &self.contract_type {
// Calculate total cost for a node contract
types::ContractData::NodeContract(node_contract) => {
types::ContractData::DeploymentContract(_) => {
// cost is calculated in capacity reservation contracts
0
}
types::ContractData::CapacityReservationContract(capacity_reservation_contract) => {
// Get the contract billing info to view the amount unbilled for NRU (network resource units)
let contract_billing_info = self.get_billing_info();
// Get the node
if !pallet_tfgrid::Nodes::<T>::contains_key(node_contract.node_id) {
return Err(DispatchErrorWithPostInfo::from(Error::<T>::NodeNotExists));
}

// We know the contract is using resources, now calculate the cost for each used resource

let node_contract_resources =
pallet::Pallet::<T>::node_contract_resources(self.contract_id);

let mut bill_resources = true;
// If this node contract is deployed on a node which has a rent contract
// We can ignore billing for the resources used by this node contract
if pallet::ActiveRentContractForNode::<T>::contains_key(node_contract.node_id) {
bill_resources = false
}
let node = pallet_tfgrid::Nodes::<T>::get(capacity_reservation_contract.node_id)
.ok_or(Error::<T>::NodeNotExists)?;

let contract_cost = calculate_resources_cost::<T>(
node_contract_resources.used,
node_contract.public_ips,
capacity_reservation_contract.resources.total_resources,
capacity_reservation_contract.public_ips,
seconds_elapsed,
&pricing_policy,
bill_resources,
);
contract_cost + contract_billing_info.amount_unbilled
}
types::ContractData::RentContract(rent_contract) => {
if !pallet_tfgrid::Nodes::<T>::contains_key(rent_contract.node_id) {
return Err(DispatchErrorWithPostInfo::from(Error::<T>::NodeNotExists));
if node.resources.total_resources
== capacity_reservation_contract.resources.total_resources
{
Percent::from_percent(pricing_policy.discount_for_dedication_nodes)
* contract_cost
+ contract_billing_info.amount_unbilled
} else {
contract_cost + contract_billing_info.amount_unbilled
}
let node = pallet_tfgrid::Nodes::<T>::get(rent_contract.node_id).unwrap();

let contract_cost = calculate_resources_cost::<T>(
node.resources,
0,
seconds_elapsed,
&pricing_policy,
true,
);
Percent::from_percent(pricing_policy.discount_for_dedication_nodes) * contract_cost
}
// Calculate total cost for a name contract
types::ContractData::NameContract(_) => {
// bill user for name usage for number of seconds elapsed
let total_cost_u64f64 = (U64F64::from_num(pricing_policy.unique_name.value) / 3600)
* U64F64::from_num(seconds_elapsed);
total_cost_u64f64.to_num::<u64>()
}
types::ContractData::ServiceContract(service_contract) => {
// bill user for service usage for number of seconds elapsed
let bill_window = 0; // TODO
let contract_cost = (U64F64::from_num(service_contract.base_fee * bill_window)
/ 3600)
+ U64F64::from_num(service_contract.variable_fee);
contract_cost.to_num::<u64>()
}
};

Ok(total_cost)
Expand All @@ -117,32 +106,26 @@ pub fn calculate_resources_cost<T: Config>(
ipu: u32,
seconds_elapsed: u64,
pricing_policy: &pallet_tfgrid_types::PricingPolicy<T::AccountId>,
bill_resources: bool,
) -> u64 {
let mut total_cost = U64F64::from_num(0);

if bill_resources {
let hru = U64F64::from_num(resources.hru) / pricing_policy.su.factor_base_1024();
let sru = U64F64::from_num(resources.sru) / pricing_policy.su.factor_base_1024();
let mru = U64F64::from_num(resources.mru) / pricing_policy.cu.factor_base_1024();
let cru = U64F64::from_num(resources.cru);

let su_used = hru / 1200 + sru / 200;
// the pricing policy su cost value is expressed in 1 hours or 3600 seconds.
// we bill every 3600 seconds but here we need to calculate the cost per second and multiply it by the seconds elapsed.
let su_cost = (U64F64::from_num(pricing_policy.su.value) / 3600)
* U64F64::from_num(seconds_elapsed)
* su_used;
log::debug!("su cost: {:?}", su_cost);

let cu = calculate_cu(cru, mru);

let cu_cost = (U64F64::from_num(pricing_policy.cu.value) / 3600)
* U64F64::from_num(seconds_elapsed)
* cu;
log::debug!("cu cost: {:?}", cu_cost);
total_cost = su_cost + cu_cost;
}
let hru = U64F64::from_num(resources.hru) / pricing_policy.su.factor_base_1024();
let sru = U64F64::from_num(resources.sru) / pricing_policy.su.factor_base_1024();
let mru = U64F64::from_num(resources.mru) / pricing_policy.cu.factor_base_1024();
let cru = U64F64::from_num(resources.cru);

let su_used = hru / 1200 + sru / 200;
// the pricing policy su cost value is expressed in 1 hours or 3600 seconds.
// we bill every 3600 seconds but here we need to calculate the cost per second and multiply it by the seconds elapsed.
let su_cost = (U64F64::from_num(pricing_policy.su.value) / 3600)
* U64F64::from_num(seconds_elapsed)
* su_used;
log::debug!("su cost: {:?}", su_cost);

let cu = calculate_cu(cru, mru);

let cu_cost =
(U64F64::from_num(pricing_policy.cu.value) / 3600) * U64F64::from_num(seconds_elapsed) * cu;
log::debug!("cu cost: {:?}", cu_cost);
let mut total_cost = su_cost + cu_cost;

if ipu > 0 {
let total_ip_cost = U64F64::from_num(ipu)
Expand Down
Loading