@@ -354,6 +354,7 @@ pub mod pallet {
354
354
NodeNotAuthorizedToComputeReport ,
355
355
PricingPolicyNotExists ,
356
356
ContractIsNotUnique ,
357
+ ContractWrongBillingLoopIndex ,
357
358
NameExists ,
358
359
NameNotValid ,
359
360
InvalidContractType ,
@@ -648,9 +649,7 @@ pub mod pallet {
648
649
impl < T : Config > Hooks < BlockNumberFor < T > > for Pallet < T > {
649
650
fn offchain_worker ( block_number : T :: BlockNumber ) {
650
651
// 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 ( ) ;
654
653
655
654
let contracts = ContractsToBillAt :: < T > :: get ( current_index) ;
656
655
if contracts. is_empty ( ) {
@@ -856,12 +855,23 @@ impl<T: Config> Pallet<T> {
856
855
let mut id = ContractID :: < T > :: get ( ) ;
857
856
id = id + 1 ;
858
857
859
- if let types:: ContractData :: NodeContract ( ref mut nc) = contract_type {
860
- Self :: _reserve_ip ( id, nc) ?;
861
- } ;
862
-
863
858
Self :: validate_solution_provider ( solution_provider_id) ?;
864
859
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
+
865
875
let contract = types:: Contract {
866
876
version : CONTRACT_VERSION ,
867
877
twin_id,
@@ -871,10 +881,6 @@ impl<T: Config> Pallet<T> {
871
881
solution_provider_id,
872
882
} ;
873
883
874
- // Start billing frequency loop
875
- // Will always be block now + frequency
876
- Self :: insert_contract_to_bill ( id) ;
877
-
878
884
// insert into contracts map
879
885
Contracts :: < T > :: insert ( id, & contract) ;
880
886
@@ -996,10 +1002,15 @@ impl<T: Config> Pallet<T> {
996
1002
) ;
997
1003
998
1004
// 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
+
1003
1014
// deposit event
1004
1015
Self :: deposit_event ( Event :: UpdatedUsedResources ( contract_resource) ) ;
1005
1016
}
@@ -1119,20 +1130,13 @@ impl<T: Config> Pallet<T> {
1119
1130
return Err ( <Error < T > >:: OffchainSignedTxNoLocalAccountAvailable ) ;
1120
1131
}
1121
1132
1122
- // Bills a contract (NodeContract or NameContract )
1133
+ // Bills a contract (NodeContract, NameContract or RentContract )
1123
1134
// Calculates how much TFT is due by the user and distributes the rewards
1124
1135
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 ( ) {
1127
1138
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) ?;
1136
1140
return Ok ( ( ) . into ( ) ) ;
1137
1141
}
1138
1142
@@ -1584,26 +1588,54 @@ impl<T: Config> Pallet<T> {
1584
1588
1585
1589
// Inserts a contract in a list where the index is the current block % billing frequency
1586
1590
// 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 ) {
1588
1592
if contract_id == 0 {
1589
1593
return ;
1590
1594
}
1591
1595
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) ;
1595
1601
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 ) ;
1599
1605
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 ,
1602
1608
index
1603
1609
) ;
1604
1610
}
1605
1611
}
1606
1612
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
+
1607
1639
// Helper function that updates the contract state and manages storage accordingly
1608
1640
pub fn _update_contract_state (
1609
1641
contract : & mut types:: Contract < T > ,
@@ -1876,9 +1908,18 @@ impl<T: Config> Pallet<T> {
1876
1908
Ok ( ( ) . into ( ) )
1877
1909
}
1878
1910
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 ( )
1882
1923
}
1883
1924
1884
1925
pub fn _service_contract_create (
@@ -2332,7 +2373,6 @@ impl<T: Config> ChangeNode<LocationOf<T>, InterfaceOf<T>, SerialNumberOf<T>> for
2332
2373
& types:: ContractState :: Deleted ( types:: Cause :: CanceledByUser ) ,
2333
2374
) ;
2334
2375
let _ = Self :: bill_contract ( node_contract_id) ;
2335
- Self :: remove_contract ( node_contract_id) ;
2336
2376
}
2337
2377
}
2338
2378
@@ -2345,7 +2385,6 @@ impl<T: Config> ChangeNode<LocationOf<T>, InterfaceOf<T>, SerialNumberOf<T>> for
2345
2385
& types:: ContractState :: Deleted ( types:: Cause :: CanceledByUser ) ,
2346
2386
) ;
2347
2387
let _ = Self :: bill_contract ( contract. contract_id ) ;
2348
- Self :: remove_contract ( contract. contract_id ) ;
2349
2388
}
2350
2389
}
2351
2390
}
0 commit comments