From 33855091d89ac3173aea7145bc96acaa661b88d4 Mon Sep 17 00:00:00 2001 From: Erwan Renaut <73958772+renauter@users.noreply.github.com> Date: Tue, 14 Nov 2023 17:02:32 -0300 Subject: [PATCH] feat(pallet-smart-contract): allow collective approval to cancel contract (#886) --- .../0017-rework_cancel_contracts.md | 16 ++ substrate-node/Cargo.lock | 1 + .../pallet-dao/creating_motion_council.md | 1 + .../pallets/pallet-smart-contract/Cargo.toml | 2 + .../pallets/pallet-smart-contract/readme.md | 66 +++--- .../pallet-smart-contract/src/benchmarking.rs | 22 ++ .../src/grid_contract.rs | 18 +- .../pallets/pallet-smart-contract/src/lib.rs | 10 + .../pallets/pallet-smart-contract/src/mock.rs | 37 +++- .../pallet-smart-contract/src/tests.rs | 195 ++++++------------ .../pallet-smart-contract/src/types.rs | 1 + .../pallet-smart-contract/src/weights.rs | 67 ++++++ 12 files changed, 265 insertions(+), 171 deletions(-) create mode 100644 docs/architecture/0017-rework_cancel_contracts.md diff --git a/docs/architecture/0017-rework_cancel_contracts.md b/docs/architecture/0017-rework_cancel_contracts.md new file mode 100644 index 000000000..56693ea05 --- /dev/null +++ b/docs/architecture/0017-rework_cancel_contracts.md @@ -0,0 +1,16 @@ +# 17. Allow collective approval to cancel contracts + +Date: 2023-11-06 + +## Status + +Accepted + +## Context + +See [here](https://github.com/threefoldtech/tfchain/issues/884) for more details. + +## Decision + +In `pallet-smart-contract`, add `cancel_contract_collective()` extrinsic to allow a collective approval (council or farmers) to cancel a contract. +For this purpose we also add a new entry `CanceledByCollective` in `Cause` enum to better qualify the cancelation cause. diff --git a/substrate-node/Cargo.lock b/substrate-node/Cargo.lock index 2f624b2cf..80baf3d22 100644 --- a/substrate-node/Cargo.lock +++ b/substrate-node/Cargo.lock @@ -4835,6 +4835,7 @@ dependencies = [ "log", "pallet-authorship", "pallet-balances", + "pallet-collective", "pallet-session", "pallet-tfgrid", "pallet-tft-price", diff --git a/substrate-node/pallets/pallet-dao/creating_motion_council.md b/substrate-node/pallets/pallet-dao/creating_motion_council.md index fdac23139..55a63c7d3 100644 --- a/substrate-node/pallets/pallet-dao/creating_motion_council.md +++ b/substrate-node/pallets/pallet-dao/creating_motion_council.md @@ -73,6 +73,7 @@ Once the motion is closed it is removed from list and the `proposal` extrinsic i ### smartContractModule +* `cancelContract()` (cancels a contract) * `approveSolutionProvider()` (approves a solution provider) * `changeBillingFrequency()` (changes the billing frequency) diff --git a/substrate-node/pallets/pallet-smart-contract/Cargo.toml b/substrate-node/pallets/pallet-smart-contract/Cargo.toml index f9eace51c..1b1393391 100644 --- a/substrate-node/pallets/pallet-smart-contract/Cargo.toml +++ b/substrate-node/pallets/pallet-smart-contract/Cargo.toml @@ -31,6 +31,7 @@ substrate-fixed = { git = 'https://github.com/encointer/substrate-fixed.git', re pallet-balances.workspace = true frame-support.workspace = true frame-system.workspace = true +pallet-collective.workspace = true sp-runtime.workspace = true sp-std.workspace = true pallet-timestamp.workspace = true @@ -66,6 +67,7 @@ std = [ 'scale-info/std', 'frame-benchmarking/std', 'sp-io/std', + "pallet-collective/std", 'frame-try-runtime/std', 'sp-core/std', 'pallet-authorship/std', diff --git a/substrate-node/pallets/pallet-smart-contract/readme.md b/substrate-node/pallets/pallet-smart-contract/readme.md index da0034e62..cfefe6823 100644 --- a/substrate-node/pallets/pallet-smart-contract/readme.md +++ b/substrate-node/pallets/pallet-smart-contract/readme.md @@ -8,12 +8,12 @@ This module is tightly coupled with the [Tfgrid module](../pallet-tfgrid/readme. The smart contract module provides functions for: -- Creating / Updating / Canceling a smart contract -- Reporting resource usage for a smart contract -- Creating / Approving solution providers -- Creating / Updating / Canceling a Service Contract -- Setting an extra price for a dedicated ZOS node -- Billing all sorts of contracts based on a pricing policy defined in the [Tfgrid module](../pallet-tfgrid/readme.md) and a TFT price fetched in the [Tft Price Module](../pallet-tft-price/readme.md).. +* Creating / Updating / Canceling a smart contract +* Reporting resource usage for a smart contract +* Creating / Approving solution providers +* Creating / Updating / Canceling a Service Contract +* Setting an extra price for a dedicated ZOS node +* Billing all sorts of contracts based on a pricing policy defined in the [Tfgrid module](../pallet-tfgrid/readme.md) and a TFT price fetched in the [Tft Price Module](../pallet-tft-price/readme.md).. The billing is triggered by an offchain worker that runs after a block is created. This offchain worker relies on the `aura` key in the keystore to sign the transaction triggers the billing. This means that only valid block creators can sign this transaction. Given this configuration this pallet will only work in an Aura / Grandpa based chains. @@ -21,35 +21,35 @@ For a more in depth view of this module, see [spec](./spec.md). ## Terminology -- [ZOS](https://github.com/threefoldtech/zos): a Zero-OS node that is running on a physical machine. -- Smart Contract: an agreement between a user and a ZOS node that is enforced by the blockchain. This can be any of: - - `NodeContract`: An agreement between a user and a ZOS node for the usage of resources on that node. - - `NameContract`: An agreement between a user and a ZOS node for the usage of a dns name on that node using a gateway. - - `RentContract`: An agreement between a user and a ZOS node for the usage of the entire node. - - `ServiceContract`: An agreement between a user and an external service provider for the usage of a service. -- Solution provider: a provider of a solution, see [solution provider](./solution_provider.md) +* [ZOS](https://github.com/threefoldtech/zos): a Zero-OS node that is running on a physical machine. +* Smart Contract: an agreement between a user and a ZOS node that is enforced by the blockchain. This can be any of: + * `NodeContract`: An agreement between a user and a ZOS node for the usage of resources on that node. + * `NameContract`: An agreement between a user and a ZOS node for the usage of a dns name on that node using a gateway. + * `RentContract`: An agreement between a user and a ZOS node for the usage of the entire node. + * `ServiceContract`: An agreement between a user and an external service provider for the usage of a service. +* Solution provider: a provider of a solution, see [solution provider](./solution_provider.md) ## Interface Dispatchable functions of this pallet. -- `create_node_contract` - Create a node contract -- `update_node_contract` - Update a node contract -- `create_name_contract` - Create a name contract -- `create_rent_contract` - Create a rent contract -- `cancel_contract`: Cancel a contract (any of the smart contract type) -- `add_nru_reports`: Reports network resource usage from ZOS to the chain -- `report_contract_resources`: Reports a `NodeContract` used resources (nru, cru, mru, sru, ipu) to the chain. -- `create_solution_provider`: Create a solution provider -- `approve_solution_provider`: Approve a solution provider, the origin for this call is a configurable origin. -- `bill_contract_for_block`: Triggers the billing of a contract on this block. -- `service_contract_create`: Create a service contract -- `service_contract_set_metadata`: Set metadata for a service contract -- `service_contract_set_fees`: Set fees for a service contract -- `service_contract_approve`: Approve a service contract -- `service_contract_reject`: Reject a service contract -- `service_contract_cancel`: Cancel a service contract -- `service_contract_bill`: Bill a service contract -- `change_billing_frequency`: Change the billing frequency of all contracts, the origin for this call is a configurable origin. -- `attach_solution_provider_id`: Attach a solution provider id to a contract -- `set_dedicated_node_extra_fee`: Set an extra fee for a dedicated node \ No newline at end of file +* `create_node_contract` - Create a node contract +* `update_node_contract` - Update a node contract +* `create_name_contract` - Create a name contract +* `create_rent_contract` - Create a rent contract +* `cancel_contract`: Cancel a contract (for any of the smart contract type and callable by the node the contract is on or a collective council/farmers approval) +* `add_nru_reports`: Reports network resource usage from ZOS to the chain +* `report_contract_resources`: Reports a `NodeContract` used resources (nru, cru, mru, sru, ipu) to the chain. +* `create_solution_provider`: Create a solution provider +* `approve_solution_provider`: Approve a solution provider, the origin for this call is a configurable origin. +* `bill_contract_for_block`: Triggers the billing of a contract on this block. +* `service_contract_create`: Create a service contract +* `service_contract_set_metadata`: Set metadata for a service contract +* `service_contract_set_fees`: Set fees for a service contract +* `service_contract_approve`: Approve a service contract +* `service_contract_reject`: Reject a service contract +* `service_contract_cancel`: Cancel a service contract +* `service_contract_bill`: Bill a service contract +* `change_billing_frequency`: Change the billing frequency of all contracts, the origin for this call is a configurable origin. +* `attach_solution_provider_id`: Attach a solution provider id to a contract +* `set_dedicated_node_extra_fee`: Set an extra fee for a dedicated node diff --git a/substrate-node/pallets/pallet-smart-contract/src/benchmarking.rs b/substrate-node/pallets/pallet-smart-contract/src/benchmarking.rs index 5c787ee34..8e6ff1d37 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/benchmarking.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/benchmarking.rs @@ -481,6 +481,28 @@ benchmarks! { ); } + // cancel_contract_collective() + cancel_contract_collective { + let farmer: T::AccountId = account("Alice", 0, 0); + _prepare_farm_with_node::(farmer.clone()); + + let user: T::AccountId = whitelisted_caller(); + _create_twin::(user.clone()); + _create_node_contract::(user.clone()); + let contract_id = 1; + + }: _(RawOrigin::Root, contract_id) + verify { + assert!(SmartContractModule::::contracts(contract_id).is_none()); + let node_id = 1; + let twin_id = 2; + assert_last_event::(Event::NodeContractCanceled { + contract_id, + node_id, + twin_id, + }.into()); + } + // Calling the `impl_benchmark_test_suite` macro inside the `benchmarks` // block will generate one #[test] function per benchmark impl_benchmark_test_suite!(SmartContractModule, crate::mock::new_test_ext(), crate::mock::TestRuntime) diff --git a/substrate-node/pallets/pallet-smart-contract/src/grid_contract.rs b/substrate-node/pallets/pallet-smart-contract/src/grid_contract.rs index ada8dcaa0..cd3faf7e7 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/grid_contract.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/grid_contract.rs @@ -290,6 +290,22 @@ impl Pallet { Error::::TwinNotAuthorizedToCancelContract ); + Self::do_cancel_contract(&mut contract, cause) + } + + pub fn _cancel_contract_collective( + contract_id: u64, + cause: types::Cause, + ) -> DispatchResultWithPostInfo { + let mut contract = Contracts::::get(contract_id).ok_or(Error::::ContractNotExists)?; + + Self::do_cancel_contract(&mut contract, cause) + } + + fn do_cancel_contract( + contract: &mut types::Contract, + cause: types::Cause, + ) -> DispatchResultWithPostInfo { // If it's a rent contract and it still has active workloads, don't allow cancellation. if matches!( &contract.contract_type, @@ -303,7 +319,7 @@ impl Pallet { ); } - Self::update_contract_state(&mut contract, &types::ContractState::Deleted(cause))?; + Self::update_contract_state(contract, &types::ContractState::Deleted(cause))?; Self::bill_contract(contract.contract_id)?; Ok(().into()) diff --git a/substrate-node/pallets/pallet-smart-contract/src/lib.rs b/substrate-node/pallets/pallet-smart-contract/src/lib.rs index 1207ff61d..9b8fea12e 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/lib.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/lib.rs @@ -645,6 +645,16 @@ pub mod pallet { let account_id = ensure_signed(origin)?; Self::_set_dedicated_node_extra_fee(account_id, node_id, extra_fee) } + + #[pallet::call_index(21)] + #[pallet::weight(::WeightInfo::cancel_contract_collective())] + pub fn cancel_contract_collective( + origin: OriginFor, + contract_id: u64, + ) -> DispatchResultWithPostInfo { + ::RestrictedOrigin::ensure_origin(origin)?; + Self::_cancel_contract_collective(contract_id, types::Cause::CanceledByCollective) + } } #[pallet::hooks] diff --git a/substrate-node/pallets/pallet-smart-contract/src/mock.rs b/substrate-node/pallets/pallet-smart-contract/src/mock.rs index c786d6487..a79e65af6 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/mock.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/mock.rs @@ -6,7 +6,7 @@ use frame_support::{ dispatch::DispatchErrorWithPostInfo, dispatch::PostDispatchInfo, parameter_types, - traits::{ConstU32, GenesisBuild}, + traits::{ConstU32, EitherOfDiverse, GenesisBuild}, BoundedVec, }; use frame_system::EnsureRoot; @@ -114,6 +114,7 @@ construct_runtime!( Authorship: pallet_authorship::{Pallet, Storage}, ValidatorSet: substrate_validator_set::{Pallet, Call, Storage, Event, Config}, Session: pallet_session::{Pallet, Call, Storage, Event, Config}, + Council: pallet_collective::::{Pallet, Call, Origin, Event, Config}, } ); @@ -267,6 +268,11 @@ parameter_types! { pub(crate) type TestNameContractName = NameContractName; +type EnsureRootOrCouncilApproval = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + use weights; impl pallet_smart_contract::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; @@ -281,7 +287,7 @@ impl pallet_smart_contract::Config for TestRuntime { type NodeChanged = NodeChanged; type MaxNameContractNameLength = MaxNameContractNameLength; type NameContractName = TestNameContractName; - type RestrictedOrigin = EnsureRoot; + type RestrictedOrigin = EnsureRootOrCouncilApproval; type MaxDeploymentDataLength = MaxDeploymentDataLength; type MaxNodeContractPublicIps = MaxNodeContractPublicIPs; type AuthorityId = pallet_smart_contract::crypto::AuthId; @@ -365,7 +371,26 @@ impl pallet_session::Config for TestRuntime { type WeightInfo = (); } -type AccountPublic = ::Signer; +pub type BlockNumber = u32; +parameter_types! { + pub const CouncilMotionDuration: BlockNumber = 4; + pub const CouncilMaxProposals: u32 = 100; + pub const CouncilMaxMembers: u32 = 100; +} + +pub type CouncilCollective = pallet_collective::Instance1; +impl pallet_collective::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = CouncilMotionDuration; + type MaxProposals = CouncilMaxProposals; + type MaxMembers = CouncilMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = (); +} pub(crate) fn get_name_contract_name(contract_name_input: &[u8]) -> TestNameContractName { NameContractName::try_from(contract_name_input.to_vec()).expect("Invalid farm input.") @@ -445,6 +470,8 @@ where } } +type AccountPublic = ::Signer; + /// Helper function to generate an account ID from seed fn get_account_id_from_seed(seed: &str) -> AccountId where @@ -503,12 +530,12 @@ pub fn new_test_ext() -> sp_io::TestExternalities { }; session_genesis.assimilate_storage(&mut storage).unwrap(); - let genesis = pallet_tft_price::GenesisConfig:: { + let price_genesis = pallet_tft_price::GenesisConfig:: { min_tft_price: 10, max_tft_price: 1000, _data: PhantomData, }; - genesis.assimilate_storage(&mut storage).unwrap(); + price_genesis.assimilate_storage(&mut storage).unwrap(); let t = sp_io::TestExternalities::from(storage); diff --git a/substrate-node/pallets/pallet-smart-contract/src/tests.rs b/substrate-node/pallets/pallet-smart-contract/src/tests.rs index a9b3b94d9..fad3debb3 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/tests.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/tests.rs @@ -36,17 +36,7 @@ const VARIABLE_AMOUNT: u64 = 100; fn test_create_node_contract_works() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); - let node_id = 1; - - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); + prepare_farm_node_and_node_contract(); let contract_id = 1; // Ensure contract_id is stored at right billing loop index @@ -223,17 +213,8 @@ fn test_create_node_contract_which_was_canceled_before_works() { fn test_update_node_contract_works() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); + prepare_farm_node_and_node_contract(); let node_id = 1; - - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); let contract_id = 1; let new_hash = generate_deployment_hash(); @@ -300,17 +281,7 @@ fn test_update_node_contract_not_exists_fails() { fn test_update_node_contract_wrong_twins_fails() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); - let node_id = 1; - - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); + prepare_farm_node_and_node_contract(); let contract_id = 1; assert_noop!( @@ -326,32 +297,56 @@ fn test_update_node_contract_wrong_twins_fails() { } #[test] -fn test_cancel_node_contract_works() { +fn test_cancel_contract_by_node_works() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); + prepare_farm_node_and_node_contract(); let node_id = 1; + let contract_id = 1; - assert_ok!(SmartContractModule::create_node_contract( + assert_ok!(SmartContractModule::cancel_contract( RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None + contract_id )); + + assert_eq!(SmartContractModule::contracts(contract_id), None); + assert_eq!(SmartContractModule::active_node_contracts(node_id).len(), 0); + }); +} + +#[test] +fn test_cancel_contract_collective_by_dao_approval_works() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + prepare_farm_node_and_node_contract(); + let node_id = 1; let contract_id = 1; - assert_ok!(SmartContractModule::cancel_contract( - RuntimeOrigin::signed(alice()), + assert_ok!(SmartContractModule::cancel_contract_collective( + RawOrigin::Root.into(), contract_id )); - let node_contract = SmartContractModule::contracts(1); - assert_eq!(node_contract, None); + assert_eq!(SmartContractModule::contracts(contract_id), None); + assert_eq!(SmartContractModule::active_node_contracts(node_id).len(), 0); + }); +} - let contracts = SmartContractModule::active_node_contracts(1); - assert_eq!(contracts.len(), 0); +#[test] +fn test_cancel_contract_collective_by_council_approval_works() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + prepare_farm_node_and_node_contract(); + let node_id = 1; + let contract_id = 1; + + assert_ok!(SmartContractModule::cancel_contract_collective( + pallet_collective::RawOrigin::Members(3, 5).into(), + contract_id + )); + + assert_eq!(SmartContractModule::contracts(contract_id), None); + assert_eq!(SmartContractModule::active_node_contracts(node_id).len(), 0); }); } @@ -454,17 +449,7 @@ fn test_cancel_node_contract_not_exists_fails() { fn test_cancel_node_contract_wrong_twins_fails() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); - let node_id = 1; - - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); + prepare_farm_node_and_node_contract(); let contract_id = 1; assert_noop!( @@ -478,17 +463,7 @@ fn test_cancel_node_contract_wrong_twins_fails() { fn test_cancel_node_contract_and_remove_from_billing_loop_works() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); - let node_id = 1; - - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); + prepare_farm_node_and_node_contract(); let contract_id = 1; run_to_block(6, None); @@ -519,17 +494,7 @@ fn test_cancel_node_contract_and_remove_from_billing_loop_works() { fn test_remove_from_billing_loop_wrong_index_fails() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); - let node_id = 1; - - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); + prepare_farm_node_and_node_contract(); let contract_id = 1; // Ensure contract_id is stored at right billing loop index @@ -757,18 +722,9 @@ fn test_cancel_rent_contract_works() { fn test_create_rent_contract_on_node_in_use_fails() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); + prepare_farm_node_and_node_contract(); let node_id = 1; - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 1, - None - )); - assert_noop!( SmartContractModule::create_rent_contract(RuntimeOrigin::signed(bob()), node_id, None), Error::::NodeNotAvailableToDeploy @@ -3507,17 +3463,7 @@ fn test_change_billing_frequency_fails_if_frequency_lower() { fn test_attach_solution_provider_id() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); - let node_id = 1; - - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); + prepare_farm_node_and_node_contract(); let contract_id = 1; let ctr = SmartContractModule::contracts(contract_id).unwrap(); @@ -3541,17 +3487,7 @@ fn test_attach_solution_provider_id() { fn test_attach_solution_provider_id_wrong_origin_fails() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); - let node_id = 1; - - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); + prepare_farm_node_and_node_contract(); let contract_id = 1; let ctr = SmartContractModule::contracts(contract_id).unwrap(); @@ -3575,17 +3511,7 @@ fn test_attach_solution_provider_id_wrong_origin_fails() { fn test_attach_solution_provider_id_not_approved_fails() { new_test_ext().execute_with(|| { run_to_block(1, None); - prepare_farm_and_node(); - let node_id = 1; - - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(alice()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); + prepare_farm_node_and_node_contract(); let contract_id = 1; let ctr = SmartContractModule::contracts(1).unwrap(); @@ -3684,18 +3610,9 @@ fn test_set_dedicated_node_extra_fee_unauthorized_fails() { #[test] fn test_set_dedicated_node_extra_fee_with_active_node_contract_fails() { new_test_ext().execute_with(|| { - prepare_farm_and_node(); + prepare_farm_node_and_node_contract(); let node_id = 1; - assert_ok!(SmartContractModule::create_node_contract( - RuntimeOrigin::signed(bob()), - node_id, - generate_deployment_hash(), - get_deployment_data(), - 0, - None - )); - let extra_fee = 100000; assert_noop!( SmartContractModule::set_dedicated_node_extra_fee( @@ -4251,6 +4168,20 @@ pub fn prepare_farm_and_node() { .unwrap(); } +pub fn prepare_farm_node_and_node_contract() { + prepare_farm_and_node(); + let node_id = 1; + + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(alice()), + node_id, + generate_deployment_hash(), + get_deployment_data(), + 0, + None + )); +} + pub fn prepare_dedicated_farm_and_node() { TFTPriceModule::set_prices(RuntimeOrigin::signed(alice()), 50, 101).unwrap(); create_farming_policies(); diff --git a/substrate-node/pallets/pallet-smart-contract/src/types.rs b/substrate-node/pallets/pallet-smart-contract/src/types.rs index ba554a121..797ea010c 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/types.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/types.rs @@ -134,6 +134,7 @@ pub enum ContractState { #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] pub enum Cause { + CanceledByCollective, CanceledByUser, OutOfFunds, } diff --git a/substrate-node/pallets/pallet-smart-contract/src/weights.rs b/substrate-node/pallets/pallet-smart-contract/src/weights.rs index 2e240bc52..cb4119f02 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/weights.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/weights.rs @@ -55,6 +55,7 @@ pub trait WeightInfo { fn change_billing_frequency() -> Weight; fn attach_solution_provider_id() -> Weight; fn set_dedicated_node_extra_fee() -> Weight; + fn cancel_contract_collective() -> Weight; } /// Weights for pallet_smart_contract using the Substrate node and recommended hardware. @@ -497,6 +498,39 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: SmartContractModule Contracts (r:1 w:1) + /// Proof Skipped: SmartContractModule Contracts (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule ActiveNodeContracts (r:1 w:1) + /// Proof Skipped: SmartContractModule ActiveNodeContracts (max_values: None, max_size: None, mode: Measured) + /// Storage: TfgridModule Twins (r:1 w:0) + /// Proof Skipped: TfgridModule Twins (max_values: None, max_size: None, mode: Measured) + /// Storage: TfgridModule TwinBoundedAccountID (r:1 w:0) + /// Proof Skipped: TfgridModule TwinBoundedAccountID (max_values: None, max_size: None, mode: Measured) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: SmartContractModule ContractLock (r:1 w:1) + /// Proof Skipped: SmartContractModule ContractLock (max_values: None, max_size: None, mode: Measured) + /// Storage: TfgridModule PricingPolicies (r:1 w:0) + /// Proof Skipped: TfgridModule PricingPolicies (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule BillingFrequency (r:1 w:0) + /// Proof Skipped: SmartContractModule BillingFrequency (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: SmartContractModule ContractsToBillAt (r:1 w:1) + /// Proof Skipped: SmartContractModule ContractsToBillAt (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule ContractBillingInformationByID (r:0 w:1) + /// Proof Skipped: SmartContractModule ContractBillingInformationByID (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule NodeContractResources (r:0 w:1) + /// Proof Skipped: SmartContractModule NodeContractResources (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule ContractIDByNodeIDAndHash (r:0 w:1) + /// Proof Skipped: SmartContractModule ContractIDByNodeIDAndHash (max_values: None, max_size: None, mode: Measured) + fn cancel_contract_collective() -> Weight { + // Proof Size summary in bytes: + // Measured: `1203` + // Estimated: `4668` + // Minimum execution time: 90_735_000 picoseconds. + Weight::from_parts(92_827_000, 4668) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } } // For backwards compatibility and tests @@ -938,4 +972,37 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: SmartContractModule Contracts (r:1 w:1) + /// Proof Skipped: SmartContractModule Contracts (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule ActiveNodeContracts (r:1 w:1) + /// Proof Skipped: SmartContractModule ActiveNodeContracts (max_values: None, max_size: None, mode: Measured) + /// Storage: TfgridModule Twins (r:1 w:0) + /// Proof Skipped: TfgridModule Twins (max_values: None, max_size: None, mode: Measured) + /// Storage: TfgridModule TwinBoundedAccountID (r:1 w:0) + /// Proof Skipped: TfgridModule TwinBoundedAccountID (max_values: None, max_size: None, mode: Measured) + /// Storage: Timestamp Now (r:1 w:0) + /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) + /// Storage: SmartContractModule ContractLock (r:1 w:1) + /// Proof Skipped: SmartContractModule ContractLock (max_values: None, max_size: None, mode: Measured) + /// Storage: TfgridModule PricingPolicies (r:1 w:0) + /// Proof Skipped: TfgridModule PricingPolicies (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule BillingFrequency (r:1 w:0) + /// Proof Skipped: SmartContractModule BillingFrequency (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: SmartContractModule ContractsToBillAt (r:1 w:1) + /// Proof Skipped: SmartContractModule ContractsToBillAt (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule ContractBillingInformationByID (r:0 w:1) + /// Proof Skipped: SmartContractModule ContractBillingInformationByID (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule NodeContractResources (r:0 w:1) + /// Proof Skipped: SmartContractModule NodeContractResources (max_values: None, max_size: None, mode: Measured) + /// Storage: SmartContractModule ContractIDByNodeIDAndHash (r:0 w:1) + /// Proof Skipped: SmartContractModule ContractIDByNodeIDAndHash (max_values: None, max_size: None, mode: Measured) + fn cancel_contract_collective() -> Weight { + // Proof Size summary in bytes: + // Measured: `1203` + // Estimated: `4668` + // Minimum execution time: 90_735_000 picoseconds. + Weight::from_parts(92_827_000, 4668) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } } \ No newline at end of file