diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml index fb9be897..57721fc4 100644 --- a/.github/workflows/build-and-push.yml +++ b/.github/workflows/build-and-push.yml @@ -18,8 +18,8 @@ jobs: rustup target add wasm32-unknown-unknown --toolchain nightly-2023-02-07 rustup component add rust-src --toolchain nightly-2023-02-07-unknown-linux-gnu sudo apt-get install binaryen - cargo install cargo-dylint - cargo install dylint-link + cargo install cargo-dylint --version 2.1.11 --force --locked + cargo install dylint-link --version 2.1.11 --locked cargo install cargo-contract --version 1.5.0 --force --locked - name: Run tests run: | diff --git a/bucket/ddc_bucket/cluster/entity.rs b/bucket/ddc_bucket/cluster/entity.rs index 896dba5f..0c73fa3e 100644 --- a/bucket/ddc_bucket/cluster/entity.rs +++ b/bucket/ddc_bucket/cluster/entity.rs @@ -50,7 +50,14 @@ impl ink_storage::traits::PackedAllocate for Cluster { pub struct ClusterInfo { pub cluster_id: ClusterId, pub cluster: Cluster, - pub cluster_v_nodes: Vec, + pub cluster_v_nodes: Vec, +} + +#[derive(Clone, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug, scale_info::TypeInfo))] +pub struct NodeVNodesInfo { + pub node_key: NodeKey, + pub v_nodes: Vec, } pub const CLUSTER_PARAMS_MAX_LEN: usize = 100_000; diff --git a/bucket/ddc_bucket/cluster/messages.rs b/bucket/ddc_bucket/cluster/messages.rs index 11b6043d..c9320036 100644 --- a/bucket/ddc_bucket/cluster/messages.rs +++ b/bucket/ddc_bucket/cluster/messages.rs @@ -5,7 +5,7 @@ use ink_prelude::vec::Vec; use crate::ddc_bucket::bucket::entity::BucketId; use crate::ddc_bucket::cash::{Cash, Payable}; use crate::ddc_bucket::cdn_node::entity::{CdnNode, CdnNodeKey}; -use crate::ddc_bucket::cluster::entity::{ClusterInfo, KB_PER_GB}; +use crate::ddc_bucket::cluster::entity::{ClusterInfo, NodeVNodesInfo, KB_PER_GB}; use crate::ddc_bucket::node::entity::{Node, NodeKey, Resource}; use crate::ddc_bucket::perm::entity::Permission; use crate::ddc_bucket::topology::store::VNodeToken; @@ -412,7 +412,13 @@ impl DdcBucket { pub fn message_cluster_get(&self, cluster_id: ClusterId) -> Result { let cluster = self.clusters.get(cluster_id)?; - let cluster_v_nodes = self.topology.get_v_nodes_by_cluster(cluster_id); + + let mut cluster_v_nodes: Vec = Vec::new(); + for node_key in cluster.nodes_keys.clone() { + let v_nodes = self.topology.get_v_nodes_by_node(node_key.clone()); + let v_nodes_info = NodeVNodesInfo { node_key, v_nodes }; + cluster_v_nodes.push(v_nodes_info) + } Ok(ClusterInfo { cluster_id, @@ -428,11 +434,14 @@ impl DdcBucket { filter_manager_id: Option, ) -> (Vec, u32) { let mut clusters = Vec::with_capacity(limit as usize); - for cluster_id in offset..offset + limit { - let cluster = match self.clusters.clusters.get(cluster_id) { + for idx in offset..offset + limit { + let cluster_id = match self.clusters.clusters_ids.get(idx as usize) { None => break, // No more items, stop. - Some(cluster) => cluster, + Some(cluster_id) => *cluster_id, }; + + let cluster = self.clusters.clusters.get(cluster_id).unwrap(); + // Apply the filter if given. if let Some(manager_id) = filter_manager_id { if manager_id != cluster.manager_id { @@ -440,7 +449,12 @@ impl DdcBucket { } } - let cluster_v_nodes = self.topology.get_v_nodes_by_cluster(cluster_id); + let mut cluster_v_nodes: Vec = Vec::new(); + for node_key in cluster.nodes_keys.clone() { + let v_nodes = self.topology.get_v_nodes_by_node(node_key.clone()); + let v_nodes_info = NodeVNodesInfo { node_key, v_nodes }; + cluster_v_nodes.push(v_nodes_info) + } // Include the complete status of matched items. let cluster_info = ClusterInfo { @@ -451,7 +465,10 @@ impl DdcBucket { clusters.push(cluster_info); } - (clusters, self.clusters.next_cluster_id) + ( + clusters, + self.clusters.clusters_ids.len().try_into().unwrap(), + ) } pub fn message_cluster_distribute_revenues(&mut self, cluster_id: ClusterId) -> Result<()> { diff --git a/bucket/ddc_bucket/cluster/store.rs b/bucket/ddc_bucket/cluster/store.rs index a79f5b54..f5103c22 100644 --- a/bucket/ddc_bucket/cluster/store.rs +++ b/bucket/ddc_bucket/cluster/store.rs @@ -2,16 +2,22 @@ use super::entity::{Cluster, ClusterId, ClusterParams}; use crate::ddc_bucket::{AccountId, Error::*, Resource, Result}; +use ink_prelude::vec::Vec; use ink_storage::traits::{SpreadAllocate, SpreadLayout}; use ink_storage::Mapping; #[derive(SpreadAllocate, SpreadLayout, Default)] #[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout, Debug))] pub struct ClusterStore { - pub next_cluster_id: u32, + pub next_cluster_id: ClusterId, pub clusters: Mapping, + pub clusters_ids: Vec, } +// https://use.ink/datastructures/storage-layout#packed-vs-non-packed-layout +// There is a buffer with only limited capacity (around 16KB in the default configuration) available. +pub const MAX_CLUSTERS_LEN_IN_VEC: usize = 3900; + impl ClusterStore { pub fn create( &mut self, @@ -19,12 +25,17 @@ impl ClusterStore { cluster_params: ClusterParams, resource_per_v_node: Resource, ) -> Result { + if self.clusters_ids.len() + 1 > MAX_CLUSTERS_LEN_IN_VEC { + return Err(NodesSizeExceedsLimit); + } + let cluster_id = self.next_cluster_id; self.next_cluster_id = self.next_cluster_id + 1; let cluster = Cluster::new(manager_id, cluster_params, resource_per_v_node)?; self.clusters.insert(&cluster_id, &cluster); + self.clusters_ids.push(cluster_id); Ok(cluster_id) } @@ -43,5 +54,8 @@ impl ClusterStore { pub fn remove(&mut self, cluster_id: ClusterId) { self.clusters.remove(cluster_id); + if let Some(pos) = self.clusters_ids.iter().position(|x| *x == cluster_id) { + self.clusters_ids.remove(pos); + }; } } diff --git a/bucket/ddc_bucket/tests/test_cluster.rs b/bucket/ddc_bucket/tests/test_cluster.rs index 5d5ef9da..9865a7b3 100644 --- a/bucket/ddc_bucket/tests/test_cluster.rs +++ b/bucket/ddc_bucket/tests/test_cluster.rs @@ -39,7 +39,10 @@ fn cluster_create_ok() { // Check cluster Storage nodes let node0 = ctx.contract.node_get(ctx.node_key0)?; - let v_nodes0 = ctx.contract.get_v_nodes_by_node(ctx.node_key0); + let v_nodes0 = ctx + .contract + .get_v_nodes_by_node(ctx.cluster_id, ctx.node_key0) + .unwrap(); let v_nodes0_len: u32 = v_nodes0.len().try_into().unwrap(); assert_eq!( @@ -54,12 +57,15 @@ fn cluster_create_ok() { cluster_id: Some(ctx.cluster_id), status_in_cluster: Some(NodeStatusInCluster::ADDING), }, - v_nodes: v_nodes0 + v_nodes: v_nodes0.clone() } ); let node1 = ctx.contract.node_get(ctx.node_key1)?; - let v_nodes1 = ctx.contract.get_v_nodes_by_node(ctx.node_key1); + let v_nodes1 = ctx + .contract + .get_v_nodes_by_node(ctx.cluster_id, ctx.node_key1) + .unwrap(); let v_nodes1_len: u32 = v_nodes1.len().try_into().unwrap(); assert_eq!( @@ -74,12 +80,15 @@ fn cluster_create_ok() { cluster_id: Some(ctx.cluster_id), status_in_cluster: Some(NodeStatusInCluster::ADDING), }, - v_nodes: v_nodes1 + v_nodes: v_nodes1.clone() } ); let node2 = ctx.contract.node_get(ctx.node_key2)?; - let v_nodes2 = ctx.contract.get_v_nodes_by_node(ctx.node_key2); + let v_nodes2 = ctx + .contract + .get_v_nodes_by_node(ctx.cluster_id, ctx.node_key2) + .unwrap(); let v_nodes2_len: u32 = v_nodes2.len().try_into().unwrap(); assert_eq!( @@ -94,7 +103,7 @@ fn cluster_create_ok() { cluster_id: Some(ctx.cluster_id), status_in_cluster: Some(NodeStatusInCluster::ADDING), }, - v_nodes: v_nodes2 + v_nodes: v_nodes2.clone() } ); @@ -151,7 +160,26 @@ fn cluster_create_ok() { // Check the cluster let cluster = ctx.contract.cluster_get(ctx.cluster_id)?; - let cluster_v_nodes = ctx.contract.get_v_nodes_by_cluster(ctx.cluster_id); + let mut cluster_v_nodes: Vec = Vec::new(); + + let node_v_nodes_0 = NodeVNodesInfo { + node_key: ctx.node_key0, + v_nodes: v_nodes0, + }; + cluster_v_nodes.push(node_v_nodes_0); + + let node_v_nodes_1 = NodeVNodesInfo { + node_key: ctx.node_key1, + v_nodes: v_nodes1, + }; + cluster_v_nodes.push(node_v_nodes_1); + + let node_v_nodes_2 = NodeVNodesInfo { + node_key: ctx.node_key2, + v_nodes: v_nodes2, + }; + cluster_v_nodes.push(node_v_nodes_2); + let total_rent = ctx.rent_v_node_per_month0 * v_nodes0_len as Balance + ctx.rent_v_node_per_month1 * v_nodes1_len as Balance + ctx.rent_v_node_per_month2 * v_nodes2_len as Balance; @@ -481,8 +509,7 @@ fn cluster_add_node_ok() { new_v_nodes.clone(), ]; - let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; - cluster_info.cluster_v_nodes.sort(); + let cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; assert!(matches!(cluster_info.cluster.nodes_keys, _nodes_keys)); assert!(matches!(cluster_info.cluster_v_nodes, _cluster_v_nodes)); @@ -570,8 +597,7 @@ fn cluster_remove_node_ok_if_node_provider() { let _cluster_v_nodes = vec![ctx.v_nodes0.clone(), ctx.v_nodes2]; - let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; - cluster_info.cluster_v_nodes.sort(); + let cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; assert!(matches!(cluster_info.cluster.nodes_keys, _nodes_keys)); assert!(matches!(cluster_info.cluster_v_nodes, _cluster_v_nodes)); @@ -612,8 +638,7 @@ fn cluster_remove_node_ok_if_cluster_manager() { let _cluster_v_nodes = vec![ctx.v_nodes0, ctx.v_nodes1]; - let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; - cluster_info.cluster_v_nodes.sort(); + let cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; assert!(matches!(cluster_info.cluster.nodes_keys, _nodes_keys)); assert!(matches!(cluster_info.cluster_v_nodes, _cluster_v_nodes)); @@ -1118,25 +1143,46 @@ fn cluster_replace_node_ok() { } )); - let mut cluster_v_nodes = Vec::::new(); - cluster_v_nodes.extend(vec![2]); - cluster_v_nodes.extend(ctx.v_nodes1.clone()); - cluster_v_nodes.extend(ctx.v_nodes2.clone()); - cluster_v_nodes.extend(v_nodes_to_reasign.clone()); - cluster_v_nodes.sort(); + let mut cluster_v_nodes: Vec = Vec::new(); + let node_v_nodes_0 = NodeVNodesInfo { + node_key: ctx.node_key0, + v_nodes: vec![2], + }; + cluster_v_nodes.push(node_v_nodes_0); - let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; - cluster_info.cluster_v_nodes.sort(); + let node_v_nodes_1 = NodeVNodesInfo { + node_key: ctx.node_key1, + v_nodes: ctx.v_nodes1.clone(), + }; + cluster_v_nodes.push(node_v_nodes_1); + + let mut node_v_nodes_2 = NodeVNodesInfo { + node_key: ctx.node_key2, + v_nodes: ctx.v_nodes2.clone(), + }; + node_v_nodes_2.v_nodes.extend(v_nodes_to_reasign.clone()); + cluster_v_nodes.push(node_v_nodes_2); + + let cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; assert_eq!( &cluster_info.cluster_v_nodes, &cluster_v_nodes, "a v_node must be replaced" ); - let mut v_nodes0 = ctx.contract.get_v_nodes_by_node(ctx.node_key0.clone()); + let mut v_nodes0 = ctx + .contract + .get_v_nodes_by_node(ctx.cluster_id, ctx.node_key0.clone()) + .unwrap(); v_nodes0.sort(); - let mut v_nodes1 = ctx.contract.get_v_nodes_by_node(ctx.node_key1.clone()); + let mut v_nodes1 = ctx + .contract + .get_v_nodes_by_node(ctx.cluster_id, ctx.node_key1.clone()) + .unwrap(); v_nodes1.sort(); - let mut v_nodes2 = ctx.contract.get_v_nodes_by_node(ctx.node_key2.clone()); + let mut v_nodes2 = ctx + .contract + .get_v_nodes_by_node(ctx.cluster_id, ctx.node_key2.clone()) + .unwrap(); v_nodes2.sort(); assert_eq!( @@ -1300,24 +1346,45 @@ fn cluster_reset_node_ok() { } )); - let mut cluster_v_nodes = Vec::::new(); - cluster_v_nodes.extend(ctx.v_nodes0.clone()); - cluster_v_nodes.extend(vec![10, 11, 12]); - cluster_v_nodes.extend(ctx.v_nodes2.clone()); - cluster_v_nodes.sort(); + let mut cluster_v_nodes: Vec = Vec::new(); + let node_v_nodes_0 = NodeVNodesInfo { + node_key: ctx.node_key0, + v_nodes: ctx.v_nodes0.clone(), + }; + cluster_v_nodes.push(node_v_nodes_0); + + let node_v_nodes_1 = NodeVNodesInfo { + node_key: ctx.node_key1, + v_nodes: vec![10, 11, 12], + }; + cluster_v_nodes.push(node_v_nodes_1); + + let node_v_nodes_2 = NodeVNodesInfo { + node_key: ctx.node_key2, + v_nodes: ctx.v_nodes2.clone(), + }; + cluster_v_nodes.push(node_v_nodes_2); - let mut cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; - cluster_info.cluster_v_nodes.sort(); + let cluster_info = ctx.contract.cluster_get(ctx.cluster_id)?; assert_eq!( &cluster_info.cluster_v_nodes, &cluster_v_nodes, "a v_node must be replaced" ); - let mut v_nodes0 = ctx.contract.get_v_nodes_by_node(ctx.node_key0.clone()); + let mut v_nodes0 = ctx + .contract + .get_v_nodes_by_node(ctx.cluster_id, ctx.node_key0.clone()) + .unwrap(); v_nodes0.sort(); - let mut v_nodes1 = ctx.contract.get_v_nodes_by_node(ctx.node_key1.clone()); + let mut v_nodes1 = ctx + .contract + .get_v_nodes_by_node(ctx.cluster_id, ctx.node_key1.clone()) + .unwrap(); v_nodes1.sort(); - let mut v_nodes2 = ctx.contract.get_v_nodes_by_node(ctx.node_key2.clone()); + let mut v_nodes2 = ctx + .contract + .get_v_nodes_by_node(ctx.cluster_id, ctx.node_key2.clone()) + .unwrap(); v_nodes2.sort(); assert_eq!( @@ -1659,10 +1726,24 @@ fn cluster_get_err_if_cluster_does_not_exist() { fn cluster_get_ok() { let ctx = setup_cluster(); - let mut cluster_v_nodes1 = Vec::::new(); - cluster_v_nodes1.extend(ctx.v_nodes0.clone()); - cluster_v_nodes1.extend(ctx.v_nodes1.clone()); - cluster_v_nodes1.extend(ctx.v_nodes2.clone()); + let mut cluster_v_nodes: Vec = Vec::new(); + let node_v_nodes_0 = NodeVNodesInfo { + node_key: ctx.node_key0, + v_nodes: ctx.v_nodes0.clone(), + }; + cluster_v_nodes.push(node_v_nodes_0); + + let node_v_nodes_1 = NodeVNodesInfo { + node_key: ctx.node_key1, + v_nodes: ctx.v_nodes1.clone(), + }; + cluster_v_nodes.push(node_v_nodes_1); + + let node_v_nodes_2 = NodeVNodesInfo { + node_key: ctx.node_key2, + v_nodes: ctx.v_nodes2.clone(), + }; + cluster_v_nodes.push(node_v_nodes_2); let total_rent = ctx.rent_v_node_per_month0 * ctx.v_nodes0.len() as Balance + ctx.rent_v_node_per_month1 * ctx.v_nodes1.len() as Balance @@ -1685,7 +1766,7 @@ fn cluster_get_ok() { cdn_usd_per_gb: CDN_USD_PER_GB, cdn_revenues: Cash(0), }, - cluster_v_nodes: cluster_v_nodes1, + cluster_v_nodes, } }) ); @@ -1695,10 +1776,24 @@ fn cluster_get_ok() { fn cluster_list_ok() { let mut ctx = setup_cluster(); - let mut cluster_v_nodes1 = Vec::::new(); - cluster_v_nodes1.extend(ctx.v_nodes0.clone()); - cluster_v_nodes1.extend(ctx.v_nodes1.clone()); - cluster_v_nodes1.extend(ctx.v_nodes2.clone()); + let mut cluster_v_nodes: Vec = Vec::new(); + let node_v_nodes_0 = NodeVNodesInfo { + node_key: ctx.node_key0, + v_nodes: ctx.v_nodes0.clone(), + }; + cluster_v_nodes.push(node_v_nodes_0); + + let node_v_nodes_1 = NodeVNodesInfo { + node_key: ctx.node_key1, + v_nodes: ctx.v_nodes1.clone(), + }; + cluster_v_nodes.push(node_v_nodes_1); + + let node_v_nodes_2 = NodeVNodesInfo { + node_key: ctx.node_key2, + v_nodes: ctx.v_nodes2.clone(), + }; + cluster_v_nodes.push(node_v_nodes_2); let total_rent = ctx.rent_v_node_per_month0 * ctx.v_nodes0.len() as Balance + ctx.rent_v_node_per_month1 * ctx.v_nodes1.len() as Balance @@ -1718,7 +1813,7 @@ fn cluster_list_ok() { cdn_usd_per_gb: CDN_USD_PER_GB, cdn_revenues: Cash(0), }, - cluster_v_nodes: cluster_v_nodes1, + cluster_v_nodes, }; let cluster_params2 = ClusterParams::from("{}"); diff --git a/bucket/ddc_bucket/topology/messages.rs b/bucket/ddc_bucket/topology/messages.rs index 40f6f902..c1628d9e 100644 --- a/bucket/ddc_bucket/topology/messages.rs +++ b/bucket/ddc_bucket/topology/messages.rs @@ -1,6 +1,6 @@ //! The public interface to manage Nodes. -use crate::ddc_bucket::{ClusterId, DdcBucket, NodeKey, Result, VNodeToken}; +use crate::ddc_bucket::{ClusterId, DdcBucket, Error::*, NodeKey, Result, VNodeToken}; use ink_prelude::vec::Vec; impl DdcBucket { @@ -8,8 +8,18 @@ impl DdcBucket { self.topology.get_v_nodes_by_cluster(cluster_id) } - pub fn message_get_v_nodes_by_node(&self, node_key: NodeKey) -> Vec { - self.topology.get_v_nodes_by_node(node_key) + pub fn message_get_v_nodes_by_node( + &self, + cluster_id: ClusterId, + node_key: NodeKey, + ) -> Result> { + let node = self.nodes.get(node_key)?; + + if node.cluster_id != Some(cluster_id) { + return Err(NodeIsNotAddedToCluster(cluster_id)); + } + + Ok(self.topology.get_v_nodes_by_node(node_key)) } pub fn message_get_node_by_v_node( diff --git a/bucket/lib.rs b/bucket/lib.rs index f6463639..a3c53a7a 100755 --- a/bucket/lib.rs +++ b/bucket/lib.rs @@ -1685,8 +1685,12 @@ pub mod ddc_bucket { } #[ink(message)] - pub fn get_v_nodes_by_node(&self, node_key: NodeKey) -> Vec { - self.message_get_v_nodes_by_node(node_key) + pub fn get_v_nodes_by_node( + &self, + cluster_id: ClusterId, + node_key: NodeKey, + ) -> Result> { + self.message_get_v_nodes_by_node(cluster_id, node_key) } #[ink(message)]