diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2830e4aa1..db3ee2780 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -50,7 +50,7 @@ jobs: - name: Rust Cache uses: Swatinem/rust-cache@v2 - name: Install try-runtime - run: cargo install --git https://github.com/paritytech/try-runtime-cli --locked + run: cargo install --git https://github.com/paritytech/try-runtime-cli --tag v0.7.0 --locked - name: Check Build run: | cargo build --release --features try-runtime diff --git a/CHANGELOG.md b/CHANGELOG.md index df784a084..66a68e691 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - [C,D] `pallet-ddc-verification`: Introduction of the Verification pallet to ensure the secure posting and retrieval of verification keys to and from the blockchain. +- [C,D] `pallet-ddc-clusters`: New `join_cluster` extrinsic. ## [5.4.0] diff --git a/pallets/ddc-clusters/src/benchmarking.rs b/pallets/ddc-clusters/src/benchmarking.rs index e6728ba74..9e4a61fd4 100644 --- a/pallets/ddc-clusters/src/benchmarking.rs +++ b/pallets/ddc-clusters/src/benchmarking.rs @@ -66,6 +66,19 @@ benchmarks! { assert!(ClustersNodes::::contains_key(cluster_id, node_pub_key)); } + join_cluster { + let bytes = [0u8; 32]; + let node_pub_key = NodePubKey::StoragePubKey(AccountId32::from(bytes)); + let cluster_id = ClusterId::from([1; 20]); + let user = account::("user", USER_SEED, 0u32); + let balance = ::Currency::minimum_balance() * 1_000_000u32.into(); + let _ = ::Currency::make_free_balance_be(&user, balance); + let _ = config_cluster_and_node::(user.clone(), node_pub_key.clone(), cluster_id); + }: _(RawOrigin::Signed(user.clone()), cluster_id, node_pub_key.clone()) + verify { + assert!(ClustersNodes::::contains_key(cluster_id, node_pub_key)); + } + remove_node { let bytes = [0u8; 32]; let node_pub_key = NodePubKey::StoragePubKey(AccountId32::from(bytes)); diff --git a/pallets/ddc-clusters/src/lib.rs b/pallets/ddc-clusters/src/lib.rs index 0f7b149d1..78ecb63cf 100644 --- a/pallets/ddc-clusters/src/lib.rs +++ b/pallets/ddc-clusters/src/lib.rs @@ -119,6 +119,7 @@ pub mod pallet { AttemptToRemoveNonExistentNode, AttemptToRemoveNotAssignedNode, OnlyClusterManager, + OnlyNodeProvider, NodeIsNotAuthorized, NodeHasNoActivatedStake, NodeStakeIsInvalid, @@ -311,23 +312,9 @@ pub mod pallet { ensure!(!has_chilling_attempt, Error::::NodeChillingIsProhibited); // Node with this node with this public key exists. - let node = T::NodeRepository::get(node_pub_key.clone()) + T::NodeRepository::get(node_pub_key.clone()) .map_err(|_| Error::::AttemptToAddNonExistentNode)?; - // Cluster extension smart contract allows joining. - if let Some(address) = cluster.props.node_provider_auth_contract.clone() { - let auth_contract = NodeProviderAuthContract::::new(address, caller_id); - - let is_authorized = auth_contract - .is_authorized( - node.get_provider_id().to_owned(), - node.get_pub_key(), - node.get_type(), - ) - .map_err(Into::>::into)?; - ensure!(is_authorized, Error::::NodeIsNotAuthorized); - }; - Self::do_add_node(cluster, node_pub_key, node_kind) } @@ -394,6 +381,55 @@ pub mod pallet { Self::do_validate_node(cluster_id, node_pub_key, succeeded) } + + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::join_cluster())] + pub fn join_cluster( + origin: OriginFor, + cluster_id: ClusterId, + node_pub_key: NodePubKey, + ) -> DispatchResult { + let caller_id = ensure_signed(origin)?; + + // Cluster with a given id exists and has an auth smart contract. + let cluster = + Clusters::::try_get(cluster_id).map_err(|_| Error::::ClusterDoesNotExist)?; + let node_provider_auth_contract_address = cluster + .props + .node_provider_auth_contract + .clone() + .ok_or(Error::::NodeIsNotAuthorized)?; + + // Node with this public key exists and belongs to the caller. + let node = T::NodeRepository::get(node_pub_key.clone()) + .map_err(|_| Error::::AttemptToAddNonExistentNode)?; + ensure!(*node.get_provider_id() == caller_id, Error::::OnlyNodeProvider); + + // Sufficient funds are locked at the DDC Staking module. + let has_activated_stake = + T::StakingVisitor::has_activated_stake(&node_pub_key, &cluster_id) + .map_err(Into::>::into)?; + ensure!(has_activated_stake, Error::::NodeHasNoActivatedStake); + + // Candidate is not planning to pause operations any time soon. + let has_chilling_attempt = T::StakingVisitor::has_chilling_attempt(&node_pub_key) + .map_err(Into::>::into)?; + ensure!(!has_chilling_attempt, Error::::NodeChillingIsProhibited); + + // Cluster auth smart contract allows joining. + let auth_contract = + NodeProviderAuthContract::::new(node_provider_auth_contract_address, caller_id); + let is_authorized = auth_contract + .is_authorized( + node.get_provider_id().to_owned(), + node.get_pub_key(), + node.get_type(), + ) + .map_err(Into::>::into)?; + ensure!(is_authorized, Error::::NodeIsNotAuthorized); + + Self::do_join_cluster(cluster, node_pub_key) + } } impl Pallet { @@ -538,6 +574,44 @@ pub mod pallet { Ok(()) } + fn do_join_cluster( + cluster: Cluster, + node_pub_key: NodePubKey, + ) -> DispatchResult { + ensure!(cluster.can_manage_nodes(), Error::::UnexpectedClusterStatus); + + let mut node: pallet_ddc_nodes::Node = T::NodeRepository::get(node_pub_key.clone()) + .map_err(|_| Error::::AttemptToAddNonExistentNode)?; + ensure!(node.get_cluster_id().is_none(), Error::::AttemptToAddAlreadyAssignedNode); + + node.set_cluster_id(Some(cluster.cluster_id)); + T::NodeRepository::update(node).map_err(|_| Error::::AttemptToAddNonExistentNode)?; + + ClustersNodes::::insert( + cluster.cluster_id, + node_pub_key.clone(), + ClusterNodeState { + kind: ClusterNodeKind::External, + status: ClusterNodeStatus::ValidationSucceeded, + added_at: frame_system::Pallet::::block_number(), + }, + ); + Self::deposit_event(Event::::ClusterNodeAdded { + cluster_id: cluster.cluster_id, + node_pub_key, + }); + + let mut current_stats = ClustersNodesStats::::try_get(cluster.cluster_id) + .map_err(|_| Error::::ClusterDoesNotExist)?; + current_stats.validation_succeeded = current_stats + .validation_succeeded + .checked_add(1) + .ok_or(Error::::ArithmeticOverflow)?; + ClustersNodesStats::::insert(cluster.cluster_id, current_stats); + + Ok(()) + } + fn do_remove_node( cluster: Cluster, node_pub_key: NodePubKey, diff --git a/pallets/ddc-clusters/src/node_provider_auth.rs b/pallets/ddc-clusters/src/node_provider_auth.rs index 1e9dee165..6f293e36f 100644 --- a/pallets/ddc-clusters/src/node_provider_auth.rs +++ b/pallets/ddc-clusters/src/node_provider_auth.rs @@ -63,7 +63,7 @@ where .result .map_err(|_| NodeProviderAuthContractError::ContractCallFailed)? .data - .first() + .get(1) .is_some_and(|x| *x == 1); Ok(is_authorized) diff --git a/pallets/ddc-clusters/src/test_data/metadata.json b/pallets/ddc-clusters/src/test_data/metadata.json index 4ffe526d6..f05b952e2 100644 --- a/pallets/ddc-clusters/src/test_data/metadata.json +++ b/pallets/ddc-clusters/src/test_data/metadata.json @@ -1,8 +1,17 @@ { "source": { - "hash": "0x26d3338336a945f457e19992f37947659b0b7e4b2962369fe2f97ca7ebb95a09", - "language": "ink! 3.4.0", - "compiler": "rustc 1.69.0-nightly" + "hash": "0xa41834e9d696d32cbc6ba6e83d7e904ad494bf41016f41d9e0186979de6965c3", + "language": "ink! 4.3.0", + "compiler": "rustc 1.76.0", + "build_info": { + "build_mode": "Release", + "cargo_contract_version": "2.2.1", + "rust_toolchain": "stable-x86_64-unknown-linux-gnu", + "wasm_opt_settings": { + "keep_debug_symbols": false, + "optimization_passes": "Z" + } + } }, "contract": { "name": "node_provider_auth_white_list", @@ -13,353 +22,620 @@ "description": "Node provider authorization layer based on admin approval", "license": "Apache-2.0" }, - "V3": { - "spec": { - "constructors": [ - { - "args": [], - "docs": [], - "label": "new", - "payable": false, - "selector": "0x9bae9d5e" - } - ], - "docs": [], - "events": [], - "messages": [ - { - "args": [ - { - "label": "_node_provider", - "type": { - "displayName": [ - "AccountId" - ], - "type": 0 - } - }, - { - "label": "node_pub_key", - "type": { - "displayName": [ - "NodePubKey" - ], - "type": 4 - } - }, - { - "label": "_node_variant", - "type": { - "displayName": [ - "NodeType" - ], - "type": 2 - } - } + "spec": { + "constructors": [ + { + "args": [], + "default": false, + "docs": [], + "label": "new", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" ], - "docs": [], - "label": "is_authorized", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "bool" - ], - "type": 5 - }, - "selector": "0x96b0453e" + "type": 4 }, - { - "args": [ - { - "label": "node_pub_key", - "type": { - "displayName": [ - "NodePubKey" - ], - "type": 4 - } + "selector": "0x9bae9d5e" + } + ], + "docs": [], + "environment": { + "accountId": { + "displayName": [ + "AccountId" + ], + "type": 0 + }, + "balance": { + "displayName": [ + "Balance" + ], + "type": 13 + }, + "blockNumber": { + "displayName": [ + "BlockNumber" + ], + "type": 16 + }, + "chainExtension": { + "displayName": [ + "ChainExtension" + ], + "type": 17 + }, + "hash": { + "displayName": [ + "Hash" + ], + "type": 14 + }, + "maxEventTopics": 4, + "timestamp": { + "displayName": [ + "Timestamp" + ], + "type": 15 + } + }, + "events": [], + "lang_error": { + "displayName": [ + "ink", + "LangError" + ], + "type": 6 + }, + "messages": [ + { + "args": [ + { + "label": "_node_provider", + "type": { + "displayName": [ + "AccountId" + ], + "type": 0 } - ], - "docs": [], - "label": "add_node_pub_key", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 7 }, - "selector": "0x7a04093d" - }, - { - "args": [ - { - "label": "node_pub_key", - "type": { - "displayName": [ - "NodePubKey" - ], - "type": 4 - } + { + "label": "node_pub_key", + "type": { + "displayName": [ + "NodePubKey" + ], + "type": 7 } - ], - "docs": [], - "label": "remove_node_pub_key", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "Result" - ], - "type": 7 }, - "selector": "0xed3668b6" - }, - { - "args": [ - { - "label": "node_pub_key", - "type": { - "displayName": [ - "NodePubKey" - ], - "type": 4 - } + { + "label": "_node_variant", + "type": { + "displayName": [ + "NodeType" + ], + "type": 2 } + } + ], + "default": false, + "docs": [], + "label": "is_authorized", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" ], - "docs": [], - "label": "has_node_pub_key", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "bool" - ], - "type": 5 - }, - "selector": "0x9b868ecf" + "type": 8 }, - { - "args": [], - "docs": [], - "label": "get_admin", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "AccountId" - ], - "type": 0 - }, - "selector": "0x57b8a8a7" - } - ] - }, - "storage": { - "struct": { - "fields": [ - { - "layout": { - "cell": { - "key": "0x0000000000000000000000000000000000000000000000000000000000000000", - "ty": 0 - } - }, - "name": "admin" - }, - { - "layout": { - "cell": { - "key": "0x0100000000000000000000000000000000000000000000000000000000000000", - "ty": 3 - } - }, - "name": "list" - } - ] - } - }, - "types": [ + "selector": "0x96b0453e" + }, { - "id": 0, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 1, - "typeName": "[u8; 32]" - } - ] + "args": [ + { + "label": "node_pub_key", + "type": { + "displayName": [ + "NodePubKey" + ], + "type": 7 } - }, - "path": [ - "ink_env", - "types", - "AccountId" - ] - } + } + ], + "default": false, + "docs": [], + "label": "add_node_pub_key", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 9 + }, + "selector": "0x7a04093d" }, { - "id": 1, - "type": { - "def": { - "array": { - "len": 32, - "type": 2 + "args": [ + { + "label": "node_pub_key", + "type": { + "displayName": [ + "NodePubKey" + ], + "type": 7 } } - } + ], + "default": false, + "docs": [], + "label": "remove_node_pub_key", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 9 + }, + "selector": "0xed3668b6" }, { - "id": 2, - "type": { - "def": { - "primitive": "u8" + "args": [ + { + "label": "node_pub_key", + "type": { + "displayName": [ + "NodePubKey" + ], + "type": 7 + } } - } + ], + "default": false, + "docs": [], + "label": "has_node_pub_key", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 8 + }, + "selector": "0x9b868ecf" }, { - "id": 3, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "offset_key", - "type": 6, - "typeName": "Key" - } - ] - } - }, - "params": [ + "args": [], + "default": false, + "docs": [], + "label": "get_admin", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 12 + }, + "selector": "0x57b8a8a7" + } + ] + }, + "storage": { + "root": { + "layout": { + "struct": { + "fields": [ { - "name": "K", - "type": 4 + "layout": { + "leaf": { + "key": "0x00000000", + "ty": 0 + } + }, + "name": "admin" }, { - "name": "V", - "type": 5 + "layout": { + "root": { + "layout": { + "leaf": { + "key": "0x8e4a1f5b", + "ty": 3 + } + }, + "root_key": "0x8e4a1f5b" + } + }, + "name": "list" } ], - "path": [ - "ink_storage", - "lazy", - "mapping", - "Mapping" - ] + "name": "WhiteListAuthContract" } }, - { - "id": 4, - "type": { - "def": { - "sequence": { - "type": 2 - } + "root_key": "0x00000000" + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 1, + "typeName": "[u8; 32]" + } + ] } - } - }, - { - "id": 5, - "type": { - "def": { - "primitive": "bool" + }, + "path": [ + "ink_primitives", + "types", + "AccountId" + ] + } + }, + { + "id": 1, + "type": { + "def": { + "array": { + "len": 32, + "type": 2 } } - }, - { - "id": 6, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 1, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_primitives", - "Key" - ] + } + }, + { + "id": 2, + "type": { + "def": { + "primitive": "u8" } - }, - { - "id": 7, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 8 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 9 - } - ], - "index": 1, - "name": "Err" - } - ] - } + } + }, + { + "id": 3, + "type": { + "def": { + "primitive": "bool" + } + } + }, + { + "id": 4, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 5 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 5 }, - "params": [ - { - "name": "T", - "type": 8 - }, - { - "name": "E", - "type": 9 - } - ], - "path": [ - "Result" - ] + { + "name": "E", + "type": 6 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 5, + "type": { + "def": { + "tuple": [] } - }, - { - "id": 8, - "type": { - "def": { - "tuple": [] + } + }, + { + "id": 6, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "CouldNotReadInput" + } + ] + } + }, + "path": [ + "ink_primitives", + "LangError" + ] + } + }, + { + "id": 7, + "type": { + "def": { + "sequence": { + "type": 2 } } - }, - { - "id": 9, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "OnlyAdmin" - } - ] - } + } + }, + { + "id": 8, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 3 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 3 }, - "path": [ - "node_provider_auth_white_list", - "node_provider_auth_white_list", - "Error" - ] + { + "name": "E", + "type": 6 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 9, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 10 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 10 + }, + { + "name": "E", + "type": 6 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 10, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 5 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 11 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 5 + }, + { + "name": "E", + "type": 11 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 11, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "OnlyAdmin" + } + ] + } + }, + "path": [ + "node_provider_auth_white_list", + "node_provider_auth_white_list", + "Error" + ] + } + }, + { + "id": 12, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 0 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 6 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 0 + }, + { + "name": "E", + "type": 6 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 13, + "type": { + "def": { + "primitive": "u128" } } - ] - } + }, + { + "id": 14, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 1, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "Hash" + ] + } + }, + { + "id": 15, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 16, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 17, + "type": { + "def": { + "variant": {} + }, + "path": [ + "ink_env", + "types", + "NoChainExtension" + ] + } + } + ], + "version": "4" } \ No newline at end of file diff --git a/pallets/ddc-clusters/src/test_data/node_provider_auth_white_list.wasm b/pallets/ddc-clusters/src/test_data/node_provider_auth_white_list.wasm index 39d305f7f..5c3394495 100644 Binary files a/pallets/ddc-clusters/src/test_data/node_provider_auth_white_list.wasm and b/pallets/ddc-clusters/src/test_data/node_provider_auth_white_list.wasm differ diff --git a/pallets/ddc-clusters/src/tests.rs b/pallets/ddc-clusters/src/tests.rs index a27ca0c5d..56c9d8237 100644 --- a/pallets/ddc-clusters/src/tests.rs +++ b/pallets/ddc-clusters/src/tests.rs @@ -126,7 +126,7 @@ fn create_cluster_works() { } #[test] -fn add_and_delete_node_works() { +fn add_join_and_delete_node_works() { ExtBuilder.build_and_execute(|| { System::set_block_number(1); @@ -134,6 +134,7 @@ fn add_and_delete_node_works() { let cluster_manager_id = AccountId::from([1; 32]); let cluster_reserve_id = AccountId::from([2; 32]); let node_pub_key = AccountId::from([3; 32]); + let node_pub_key2 = AccountId::from([4; 32]); let contract_id = deploy_contract(); @@ -147,6 +148,14 @@ fn add_and_delete_node_works() { ), Error::::ClusterDoesNotExist ); + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), + ), + Error::::ClusterDoesNotExist + ); // Creating 1 cluster should work fine assert_ok!(DdcClusters::create_cluster( @@ -154,7 +163,7 @@ fn add_and_delete_node_works() { cluster_id, cluster_reserve_id.clone(), ClusterParams { - node_provider_auth_contract: Some(cluster_manager_id.clone()), + node_provider_auth_contract: None, erasure_coding_required: 4, erasure_coding_total: 6, replication_total: 3 @@ -173,6 +182,28 @@ fn add_and_delete_node_works() { } )); + // Cluster has no auth smart contract + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), + ), + Error::::NodeIsNotAuthorized + ); + + // Set an incorrect address for auth contract + assert_ok!(DdcClusters::set_cluster_params( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + ClusterParams { + node_provider_auth_contract: Some(cluster_manager_id.clone()), + erasure_coding_required: 4, + erasure_coding_total: 6, + replication_total: 3 + }, + )); + // Not Cluster Manager assert_noop!( DdcClusters::add_node( @@ -194,6 +225,14 @@ fn add_and_delete_node_works() { ), Error::::AttemptToAddNonExistentNode ); + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), + ), + Error::::AttemptToAddNonExistentNode + ); let storage_node_params = StorageNodeParams { mode: StorageNodeMode::Storage, @@ -209,18 +248,17 @@ fn add_and_delete_node_works() { assert_ok!(DdcNodes::create_node( RuntimeOrigin::signed(cluster_manager_id.clone()), NodePubKey::StoragePubKey(node_pub_key.clone()), - NodeParams::StorageParams(storage_node_params) + NodeParams::StorageParams(storage_node_params.clone()), )); - // Node doesn't exist + // Not node provider assert_noop!( - DdcClusters::add_node( - RuntimeOrigin::signed(cluster_manager_id.clone()), + DdcClusters::join_cluster( + RuntimeOrigin::signed(node_pub_key.clone()), cluster_id, NodePubKey::StoragePubKey(node_pub_key.clone()), - ClusterNodeKind::Genesis ), - Error::::NodeAuthContractCallFailed + Error::::OnlyNodeProvider ); // Set the correct address for auth contract @@ -237,7 +275,22 @@ fn add_and_delete_node_works() { assert_ok!(DdcClusters::bond_cluster(&cluster_id)); - // Node added succesfully + // Node is not authorized to join + assert_ok!(DdcNodes::create_node( + RuntimeOrigin::signed(cluster_manager_id.clone()), + NodePubKey::StoragePubKey(node_pub_key2.clone()), + NodeParams::StorageParams(storage_node_params.clone()), + )); + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key2.clone()), + ), + Error::::NodeIsNotAuthorized + ); + + // Node added successfully assert_ok!(DdcClusters::add_node( RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, @@ -261,6 +314,14 @@ fn add_and_delete_node_works() { ), Error::::AttemptToAddAlreadyAssignedNode ); + assert_noop!( + DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), + ), + Error::::AttemptToAddAlreadyAssignedNode + ); // Checking that event was emitted System::assert_last_event( @@ -290,13 +351,35 @@ fn add_and_delete_node_works() { // Remove node should fail assert_noop!( DdcClusters::remove_node( - RuntimeOrigin::signed(cluster_manager_id), + RuntimeOrigin::signed(cluster_manager_id.clone()), cluster_id, - NodePubKey::StoragePubKey(node_pub_key), + NodePubKey::StoragePubKey(node_pub_key.clone()), ), Error::::AttemptToRemoveNotAssignedNode ); + // Node joined successfully + assert_ok!(DdcClusters::join_cluster( + RuntimeOrigin::signed(cluster_manager_id.clone()), + cluster_id, + NodePubKey::StoragePubKey(node_pub_key.clone()), + )); + + assert!(>::contains_node( + &cluster_id, + &NodePubKey::StoragePubKey(node_pub_key.clone()), + None + )); + + // Checking that event was emitted + System::assert_last_event( + Event::ClusterNodeAdded { + cluster_id, + node_pub_key: NodePubKey::StoragePubKey(node_pub_key.clone()), + } + .into(), + ); + pub const CTOR_SELECTOR: [u8; 4] = hex!("9bae9d5e"); fn encode_constructor() -> Vec { diff --git a/pallets/ddc-clusters/src/weights.rs b/pallets/ddc-clusters/src/weights.rs index df5c5957e..83ce438dc 100644 --- a/pallets/ddc-clusters/src/weights.rs +++ b/pallets/ddc-clusters/src/weights.rs @@ -1,7 +1,7 @@ //! Autogenerated weights for pallet_ddc_clusters //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2024-07-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bench`, CPU: `AMD EPYC-Milan Processor` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -30,6 +30,7 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn create_cluster() -> Weight; fn add_node() -> Weight; + fn join_cluster() -> Weight; fn remove_node() -> Weight; fn set_cluster_params() -> Weight; fn validate_node() -> Weight; @@ -61,6 +62,27 @@ impl WeightInfo for SubstrateWeight { // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) // Storage: `DdcNodes::StorageNodes` (r:1 w:1) // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:0 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn add_node() -> Weight { + Weight::from_parts(82_055_000_u64, 0) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Bonded` (r:1 w:0) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:0) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) // Storage: `Contracts::MigrationInProgress` (r:1 w:0) // Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) // Storage: `System::Account` (r:1 w:0) @@ -79,12 +101,12 @@ impl WeightInfo for SubstrateWeight { // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) // Storage: `DdcClusters::ClustersNodes` (r:0 w:1) // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) - // Storage: UNKNOWN KEY `0x89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3` (r:1 w:0) - // Proof: UNKNOWN KEY `0x89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3` (r:1 w:0) - // Storage: UNKNOWN KEY `0xc3ad1d87683b6ac25f2e809346840d7a7ed0c05653ee606dba68aba3bdb5d957` (r:1 w:0) - // Proof: UNKNOWN KEY `0xc3ad1d87683b6ac25f2e809346840d7a7ed0c05653ee606dba68aba3bdb5d957` (r:1 w:0) - fn add_node() -> Weight { - Weight::from_parts(655_639_000_u64, 0) + // Storage: UNKNOWN KEY `0x11d2df4e979aa105cf552e9544ebd2b500000000` (r:1 w:0) + // Proof: UNKNOWN KEY `0x11d2df4e979aa105cf552e9544ebd2b500000000` (r:1 w:0) + // Storage: UNKNOWN KEY `0xee61cd03857d4d6515cbe7367c56239d5b1f4a8e800000000000000000000000` (r:1 w:0) + // Proof: UNKNOWN KEY `0xee61cd03857d4d6515cbe7367c56239d5b1f4a8e800000000000000000000000` (r:1 w:0) + fn join_cluster() -> Weight { + Weight::from_parts(656_290_000_u64, 0) .saturating_add(T::DbWeight::get().reads(17_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -146,6 +168,27 @@ impl WeightInfo for () { // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) // Storage: `DdcNodes::StorageNodes` (r:1 w:1) // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodesStats` (r:1 w:1) + // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcClusters::ClustersNodes` (r:0 w:1) + // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn add_node() -> Weight { + Weight::from_parts(82_055_000_u64, 0) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + // Storage: `DdcClusters::Clusters` (r:1 w:0) + // Proof: `DdcClusters::Clusters` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcNodes::StorageNodes` (r:1 w:1) + // Proof: `DdcNodes::StorageNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Nodes` (r:1 w:0) + // Proof: `DdcStaking::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Storages` (r:1 w:0) + // Proof: `DdcStaking::Storages` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Bonded` (r:1 w:0) + // Proof: `DdcStaking::Bonded` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `DdcStaking::Ledger` (r:1 w:0) + // Proof: `DdcStaking::Ledger` (`max_values`: None, `max_size`: None, mode: `Measured`) // Storage: `Contracts::MigrationInProgress` (r:1 w:0) // Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `MaxEncodedLen`) // Storage: `System::Account` (r:1 w:0) @@ -164,12 +207,12 @@ impl WeightInfo for () { // Proof: `DdcClusters::ClustersNodesStats` (`max_values`: None, `max_size`: None, mode: `Measured`) // Storage: `DdcClusters::ClustersNodes` (r:0 w:1) // Proof: `DdcClusters::ClustersNodes` (`max_values`: None, `max_size`: None, mode: `Measured`) - // Storage: UNKNOWN KEY `0x89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3` (r:1 w:0) - // Proof: UNKNOWN KEY `0x89eb0d6a8a691dae2cd15ed0369931ce0a949ecafa5c3f93f8121833646e15c3` (r:1 w:0) - // Storage: UNKNOWN KEY `0xc3ad1d87683b6ac25f2e809346840d7a7ed0c05653ee606dba68aba3bdb5d957` (r:1 w:0) - // Proof: UNKNOWN KEY `0xc3ad1d87683b6ac25f2e809346840d7a7ed0c05653ee606dba68aba3bdb5d957` (r:1 w:0) - fn add_node() -> Weight { - Weight::from_parts(655_639_000_u64, 0) + // Storage: UNKNOWN KEY `0x11d2df4e979aa105cf552e9544ebd2b500000000` (r:1 w:0) + // Proof: UNKNOWN KEY `0x11d2df4e979aa105cf552e9544ebd2b500000000` (r:1 w:0) + // Storage: UNKNOWN KEY `0xee61cd03857d4d6515cbe7367c56239d5b1f4a8e800000000000000000000000` (r:1 w:0) + // Proof: UNKNOWN KEY `0xee61cd03857d4d6515cbe7367c56239d5b1f4a8e800000000000000000000000` (r:1 w:0) + fn join_cluster() -> Weight { + Weight::from_parts(656_290_000_u64, 0) .saturating_add(RocksDbWeight::get().reads(17_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } diff --git a/runtime/cere-dev/src/lib.rs b/runtime/cere-dev/src/lib.rs index db1e01a09..b2a7278ac 100644 --- a/runtime/cere-dev/src/lib.rs +++ b/runtime/cere-dev/src/lib.rs @@ -149,7 +149,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 54127, + spec_version: 54128, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 19, diff --git a/runtime/cere/src/lib.rs b/runtime/cere/src/lib.rs index 7470590f1..72b65bc1b 100644 --- a/runtime/cere/src/lib.rs +++ b/runtime/cere/src/lib.rs @@ -143,7 +143,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 54127, + spec_version: 541278, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 19,