Skip to content

Commit 8eaa3de

Browse files
authored
feat: billing loop cleaning and improvement #632 (#643)
1 parent bb09e8e commit 8eaa3de

File tree

7 files changed

+496
-53
lines changed

7 files changed

+496
-53
lines changed

substrate-node/pallets/pallet-smart-contract/src/lib.rs

Lines changed: 79 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ pub mod pallet {
354354
NodeNotAuthorizedToComputeReport,
355355
PricingPolicyNotExists,
356356
ContractIsNotUnique,
357+
ContractWrongBillingLoopIndex,
357358
NameExists,
358359
NameNotValid,
359360
InvalidContractType,
@@ -648,9 +649,7 @@ pub mod pallet {
648649
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
649650
fn offchain_worker(block_number: T::BlockNumber) {
650651
// Let offchain worker check if there are contracts on the map at current index
651-
// Index being current block number % (mod) Billing Frequency
652-
let current_index: u64 =
653-
block_number.saturated_into::<u64>() % BillingFrequency::<T>::get();
652+
let current_index = Self::get_current_billing_loop_index();
654653

655654
let contracts = ContractsToBillAt::<T>::get(current_index);
656655
if contracts.is_empty() {
@@ -856,12 +855,23 @@ impl<T: Config> Pallet<T> {
856855
let mut id = ContractID::<T>::get();
857856
id = id + 1;
858857

859-
if let types::ContractData::NodeContract(ref mut nc) = contract_type {
860-
Self::_reserve_ip(id, nc)?;
861-
};
862-
863858
Self::validate_solution_provider(solution_provider_id)?;
864859

860+
// Start billing frequency loop
861+
// Will always be block now + frequency
862+
match contract_type {
863+
types::ContractData::NodeContract(ref mut node_contract) => {
864+
Self::_reserve_ip(id, node_contract)?;
865+
866+
// Insert created node contract in billing loop now only
867+
// if there is at least one public ip attached to node
868+
if node_contract.public_ips > 0 {
869+
Self::insert_contract_in_billing_loop(id);
870+
}
871+
}
872+
_ => Self::insert_contract_in_billing_loop(id),
873+
};
874+
865875
let contract = types::Contract {
866876
version: CONTRACT_VERSION,
867877
twin_id,
@@ -871,10 +881,6 @@ impl<T: Config> Pallet<T> {
871881
solution_provider_id,
872882
};
873883

874-
// Start billing frequency loop
875-
// Will always be block now + frequency
876-
Self::insert_contract_to_bill(id);
877-
878884
// insert into contracts map
879885
Contracts::<T>::insert(id, &contract);
880886

@@ -996,10 +1002,15 @@ impl<T: Config> Pallet<T> {
9961002
);
9971003

9981004
// Do insert
999-
NodeContractResources::<T>::insert(
1000-
contract_resource.contract_id,
1001-
&contract_resource,
1002-
);
1005+
NodeContractResources::<T>::insert(contract.contract_id, &contract_resource);
1006+
1007+
// Start billing frequency loop
1008+
// Insert node contract in billing loop only if there
1009+
// are non empty resources pushed for the contract
1010+
if !contract_resource.used.is_empty() {
1011+
Self::insert_contract_in_billing_loop(contract.contract_id);
1012+
}
1013+
10031014
// deposit event
10041015
Self::deposit_event(Event::UpdatedUsedResources(contract_resource));
10051016
}
@@ -1119,20 +1130,13 @@ impl<T: Config> Pallet<T> {
11191130
return Err(<Error<T>>::OffchainSignedTxNoLocalAccountAvailable);
11201131
}
11211132

1122-
// Bills a contract (NodeContract or NameContract)
1133+
// Bills a contract (NodeContract, NameContract or RentContract)
11231134
// Calculates how much TFT is due by the user and distributes the rewards
11241135
fn bill_contract(contract_id: u64) -> DispatchResultWithPostInfo {
1125-
// Clean up contract from blling loop if it not exists anymore
1126-
if !Contracts::<T>::contains_key(contract_id) {
1136+
// Clean up contract from billing loop if it doesn't exist anymore
1137+
if Contracts::<T>::get(contract_id).is_none() {
11271138
log::debug!("cleaning up deleted contract from storage");
1128-
1129-
let index = Self::get_contract_index();
1130-
1131-
// Remove contract from billing list
1132-
let mut contracts = ContractsToBillAt::<T>::get(index);
1133-
contracts.retain(|&c| c != contract_id);
1134-
ContractsToBillAt::<T>::insert(index, contracts);
1135-
1139+
Self::remove_contract_from_billing_loop(contract_id)?;
11361140
return Ok(().into());
11371141
}
11381142

@@ -1584,26 +1588,54 @@ impl<T: Config> Pallet<T> {
15841588

15851589
// Inserts a contract in a list where the index is the current block % billing frequency
15861590
// This way, we don't need to reinsert the contract everytime it gets billed
1587-
pub fn insert_contract_to_bill(contract_id: u64) {
1591+
pub fn insert_contract_in_billing_loop(contract_id: u64) {
15881592
if contract_id == 0 {
15891593
return;
15901594
}
15911595

1592-
// Save the contract to be billed in (now -1 %(mod) BILLING_FREQUENCY_IN_BLOCKS)
1593-
let index = Self::get_contract_index().checked_sub(1).unwrap_or(0);
1594-
let mut contracts = ContractsToBillAt::<T>::get(index);
1596+
// Save the contract to be billed at previous billing loop index
1597+
// to avoid being billed at same block by the offchain worker
1598+
// First billing for the contract will happen after 1 billing cycle
1599+
let index = Self::get_previous_billing_loop_index();
1600+
let mut contract_ids = ContractsToBillAt::<T>::get(index);
15951601

1596-
if !contracts.contains(&contract_id) {
1597-
contracts.push(contract_id);
1598-
ContractsToBillAt::<T>::insert(index, &contracts);
1602+
if !contract_ids.contains(&contract_id) {
1603+
contract_ids.push(contract_id);
1604+
ContractsToBillAt::<T>::insert(index, &contract_ids);
15991605
log::debug!(
1600-
"Insert contracts: {:?}, to be billed at index {:?}",
1601-
contracts,
1606+
"Updated contracts after insertion: {:?}, to be billed at index {:?}",
1607+
contract_ids,
16021608
index
16031609
);
16041610
}
16051611
}
16061612

1613+
// Removes contract from billing loop at current index
1614+
pub fn remove_contract_from_billing_loop(
1615+
contract_id: u64,
1616+
) -> Result<(), DispatchErrorWithPostInfo> {
1617+
let index = Self::get_current_billing_loop_index();
1618+
let mut contract_ids = ContractsToBillAt::<T>::get(index);
1619+
1620+
// Remove contracts from billing loop should only occur after a call
1621+
// to bill_contract() done by the offchain worker for a specific block
1622+
// So contract id must be at current billing loop index
1623+
ensure!(
1624+
contract_ids.contains(&contract_id),
1625+
Error::<T>::ContractWrongBillingLoopIndex
1626+
);
1627+
1628+
contract_ids.retain(|&c| c != contract_id);
1629+
ContractsToBillAt::<T>::insert(index, &contract_ids);
1630+
log::debug!(
1631+
"Updated contracts after removal: {:?}, to be billed at index {:?}",
1632+
contract_ids,
1633+
index
1634+
);
1635+
1636+
Ok(())
1637+
}
1638+
16071639
// Helper function that updates the contract state and manages storage accordingly
16081640
pub fn _update_contract_state(
16091641
contract: &mut types::Contract<T>,
@@ -1876,9 +1908,18 @@ impl<T: Config> Pallet<T> {
18761908
Ok(().into())
18771909
}
18781910

1879-
pub fn get_contract_index() -> u64 {
1880-
let now = <frame_system::Pallet<T>>::block_number().saturated_into::<u64>();
1881-
now % BillingFrequency::<T>::get()
1911+
// Billing index is block number % (mod) Billing Frequency
1912+
pub fn get_current_billing_loop_index() -> u64 {
1913+
let current_block = <frame_system::Pallet<T>>::block_number().saturated_into::<u64>();
1914+
current_block % BillingFrequency::<T>::get()
1915+
}
1916+
1917+
pub fn get_previous_billing_loop_index() -> u64 {
1918+
let previous_block = <frame_system::Pallet<T>>::block_number()
1919+
.saturated_into::<u64>()
1920+
.checked_sub(1)
1921+
.unwrap_or(0);
1922+
previous_block % BillingFrequency::<T>::get()
18821923
}
18831924

18841925
pub fn _service_contract_create(
@@ -2332,7 +2373,6 @@ impl<T: Config> ChangeNode<LocationOf<T>, InterfaceOf<T>, SerialNumberOf<T>> for
23322373
&types::ContractState::Deleted(types::Cause::CanceledByUser),
23332374
);
23342375
let _ = Self::bill_contract(node_contract_id);
2335-
Self::remove_contract(node_contract_id);
23362376
}
23372377
}
23382378

@@ -2345,7 +2385,6 @@ impl<T: Config> ChangeNode<LocationOf<T>, InterfaceOf<T>, SerialNumberOf<T>> for
23452385
&types::ContractState::Deleted(types::Cause::CanceledByUser),
23462386
);
23472387
let _ = Self::bill_contract(contract.contract_id);
2348-
Self::remove_contract(contract.contract_id);
23492388
}
23502389
}
23512390
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod v6;
22
// v7 was a failed migration and was reworked to v8
33
//pub mod v8;
4+
pub mod v9;

0 commit comments

Comments
 (0)