From 0325e95239974cf957e7041ec751b79922175821 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 24 Oct 2024 16:58:19 +0500 Subject: [PATCH 01/37] Add aggregator activity API protobuf definitions --- .../src/protos/activity.proto | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 pallets/ddc-verification/src/protos/activity.proto diff --git a/pallets/ddc-verification/src/protos/activity.proto b/pallets/ddc-verification/src/protos/activity.proto new file mode 100644 index 000000000..4d1bc4993 --- /dev/null +++ b/pallets/ddc-verification/src/protos/activity.proto @@ -0,0 +1,154 @@ +// Acquired from Cerebellum-Network/ddc-storage-node at 4a3d710. +// https://github.com/Cerebellum-Network/ddc-storage-node/tree/4a3d710. +syntax = "proto3"; + +package activity; + +message ChallengeResponse { + repeated Proof proofs = 1; + + message Proof { + uint32 merkleTreeNodeId = 1; + Aggregate usage = 2; + repeated bytes path = 3; + + repeated Leaf leaves = 4; + + message Leaf { + // Repeated oneof is not supported. + // See also: https://github.com/protocolbuffers/protobuf/issues/2592. + oneof leafVariant { + Record record = 1; + RecordLink link = 2; + } + } + + message Record { + ActivityRecord record = 1; + int64 stored = 2; + uint64 delivered = 3; + } + + message RecordLink { + Link link = 1; + int64 stored = 2; + uint64 delivered = 3; + + message Link { + bytes nodeId = 1; + uint64 bucketId = 2; + bytes recordId = 3; + } + } + } +} + +message Aggregate { + int64 stored = 1; // can be negative in case amount of deleted data is more than newly stored + uint64 delivered = 2; + uint64 puts = 3; + uint64 gets = 4; +} + +message ActivityRecord { + bytes id = 1; + ActivityFulfillment upstream = 2; + repeated ActivityFulfillment downstream = 3; + uint64 timestamp = 4; + Signature signature = 5; + optional AuthToken authToken = 6; // set to authorize record in case if owner delegated access to bucket +} + +message ActivityRequest { + optional ActivityRequest parentRequest = 1; + string requestId = 2; + + enum RequestType { + REQUEST_TYPE_PUT = 0; + REQUEST_TYPE_GET = 1; + REQUEST_TYPE_DELETE = 2; + } + + enum ContentType { + CONTENT_TYPE_PIECE = 0; + CONTENT_TYPE_SEGMENT = 1; + CONTENT_TYPE_MERKLE_TREE = 2; + CONTENT_TYPE_METADATA = 3; + } + + RequestType requestType = 3; + ContentType contentType = 4; + uint64 bucketId = 5; // set only when content type is PIECE + bytes pieceCid = 6; + + uint64 offset = 7; // offset of data requested (set only when RecordType = GET) + uint64 size = 8; // size of content stored or delivered + + uint64 timestamp = 9; + Signature signature = 10; +} + +// we will get this on the server side streaming + +message ActivityAcknowledgment { + string requestId = 1; + uint64 bytesStoredOrDelivered = 2; + uint64 timestamp = 3; + Signature signature = 4; +} + +message ActivityResponse { + Status status = 1; + uint32 time = 2; // response time measured by client (start before the request sent and end after the response received) + bytes peerID = 3; + + enum Status { + UNKNOWN = 0; + OK = 1; + NOT_FOUND = 2; // server doesn't have requested resource (e.g. node could miss piece metadata) + INTERNAL = 3; // error in a server response + UNAVAILABLE = 4; // no response from server + ABORTED = 5; // request aborted by a client (e.g. storage node asked for piece metadata a node, but after some time had to initialise parallel request to other node and one of the requests can be aborted once metadata received) + } +} + +message ActivityFulfillment { + ActivityRequest request = 1; + optional ActivityAcknowledgment ack = 2; + optional ActivityResponse response = 3; +} + +message AuthToken { + Signature signature = 1; // signature signer is an issuer. issuer of first token should have an access on pallet level and subsequent tokens can skip 'issuer' and take 'subject' from previous token to verify signature + Payload payload = 2; +} + +message Payload { + optional AuthToken prev = 1; // prev token in trust chain (based on known use cases max depth can be limited to 3 or increase to 5 to support more potential use cases) + optional bytes subject = 2; // whom. every except last token should be non empty. next token should be signed by this subject + optional bool canDelegate = 3; // subject can be prohibited to delegate access to anyone else (next token should be last) + + optional uint64 bucketId = 4; // mentioned only once in trust chain (or even not mentioned at all if bucket owner decided to share access to all his buckets) + repeated Operation operations = 5; // each next token in trust chain should have less or equal privileges (e.g. token restricted to 'get' operation can't have 'put' in next token) + optional int64 expiresAt = 6; // each next token should expires earlier or at the same time as previous one (e.g. token can't have lower expiresAt than in next token) + optional bytes pieceCid = 7; // mentioned only once in trust chain (in DAG API nested pieces can be accessed by path) +} + +enum Operation { + UNKNOWN = 0; + PUT = 1; + GET = 2; + DELETE = 3; +} + +message Signature { + Algorithm algorithm = 1; + bytes signer = 2; + bytes value = 3; + + enum Algorithm { + ED_25519 = 0; + SR_25519 = 1; + } +} + From a2bb827e51932cbd56515ce9a290618327099ae9 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 24 Oct 2024 17:00:30 +0500 Subject: [PATCH 02/37] Compile protobuf --- pallets/ddc-verification/Cargo.toml | 3 +++ pallets/ddc-verification/build.rs | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 pallets/ddc-verification/build.rs diff --git a/pallets/ddc-verification/Cargo.toml b/pallets/ddc-verification/Cargo.toml index 909237ec7..8566ff52c 100644 --- a/pallets/ddc-verification/Cargo.toml +++ b/pallets/ddc-verification/Cargo.toml @@ -44,6 +44,9 @@ pallet-timestamp = { workspace = true } sp-core = { workspace = true, default-features = true } sp-keystore = { workspace = true } +[build-dependencies] +prost-build = "0.13.3" + [features] default = ["std"] std = [ diff --git a/pallets/ddc-verification/build.rs b/pallets/ddc-verification/build.rs new file mode 100644 index 000000000..d0f81c79e --- /dev/null +++ b/pallets/ddc-verification/build.rs @@ -0,0 +1,6 @@ +use std::io::Result; + +fn main() -> Result<()> { + prost_build::compile_protos(&["src/protos/activity.proto"], &["src/"])?; + Ok(()) +} From f6a9561b54b6d1959d66d6a4a6b0547c2336dfa3 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 24 Oct 2024 17:03:05 +0500 Subject: [PATCH 03/37] Use protobuf generated definitions --- pallets/ddc-verification/Cargo.toml | 1 + pallets/ddc-verification/src/aggregator_client.rs | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 pallets/ddc-verification/src/aggregator_client.rs diff --git a/pallets/ddc-verification/Cargo.toml b/pallets/ddc-verification/Cargo.toml index 8566ff52c..f9202fc8f 100644 --- a/pallets/ddc-verification/Cargo.toml +++ b/pallets/ddc-verification/Cargo.toml @@ -24,6 +24,7 @@ hex = { workspace = true } itertools = { workspace = true } log = { workspace = true } polkadot-ckb-merkle-mountain-range = { workspace = true } +prost = { version = "0.13", default-features = false, features = ["prost-derive"] } rand = { workspace = true, features = ["small_rng", "alloc"], default-features = false } scale-info = { workspace = true } serde = { workspace = true } diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs new file mode 100644 index 000000000..4ef0b0a55 --- /dev/null +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -0,0 +1,3 @@ +pub mod proto { + include!(concat!(env!("OUT_DIR"),"/activity.rs")); +} From 004a4f7924fd92852884d1c2e40528b0c966b954 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 25 Oct 2024 15:27:03 +0500 Subject: [PATCH 04/37] Add activity aggregator challenge client --- .../ddc-verification/src/aggregator_client.rs | 97 ++++++++++++++++++- pallets/ddc-verification/src/lib.rs | 8 +- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index 4ef0b0a55..fbb0a7d88 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -1,3 +1,96 @@ -pub mod proto { - include!(concat!(env!("OUT_DIR"),"/activity.rs")); +use prost::Message; + +use sp_std::prelude::Vec; +use sp_runtime::AccountId32; +use sp_runtime::offchain::{http, Duration}; +use sp_io::offchain::timestamp; + +use ddc_primitives::{BucketId, DdcEra}; + +use super::*; + +pub struct AggregatorClient<'a> { + pub base_url: &'a str, + timeout: Duration, +} + +impl<'a> AggregatorClient<'a> { + pub fn new( + base_url: &'a str, + timeout: Duration, + ) -> Self { + Self { + base_url, + timeout, + } + } + + pub fn challenge_bucket_sub_aggregate( + &self, + era_id: DdcEra, + bucket_id: BucketId, + node_id: &str, + merkle_tree_node_id: Vec, + ) -> Result { + let merkle_tree_nodes_ids = merkle_tree_node_id + .iter() + .map(|x| format!("{}", x.clone())) + .collect::>() + .join(","); + let url = format!( + "{}/activity/buckets/{}/challenge?eraId={}&merkleTreeNodeId={}&nodeId={}", + self.base_url, + bucket_id, + era_id, + merkle_tree_nodes_ids, + node_id, + ); + let response = self.get_proto(&url)?; + let body = response.body().collect::>(); + let proto_response = proto::ChallengeResponse::decode(body.as_slice()).map_err(|_| http::Error::Unknown)?; + + Ok(proto_response) + } + + pub fn challenge_node_aggregate( + &self, + era_id: DdcEra, + node_id: &str, + merkle_tree_node_id: Vec, + ) -> Result { + let merkle_tree_nodes_ids = merkle_tree_node_id + .iter() + .map(|x| format!("{}", x.clone())) + .collect::>() + .join(","); + let url = format!( + "{}/activity/nodes/{}/challenge?eraId={}&merkleTreeNodeId={}", + self.base_url, + node_id, + era_id, + merkle_tree_nodes_ids, + ); + let response = self.get_proto(&url)?; + let body = response.body().collect::>(); + let proto_response = proto::ChallengeResponse::decode(body.as_slice()).map_err(|_| http::Error::Unknown)?; + + Ok(proto_response) + } + + fn get_proto(&self, url: &str) -> Result { + let deadline = timestamp().add(self.timeout); + let response = http::Request::get(url) + .add_header("Accept", "application/protobuf") + .deadline(deadline) + .send() + .map_err(|_| http::Error::IoError)? + .try_wait(deadline) + .map_err(|_| http::Error::DeadlineReached)??; + + if response.code != 200 { + return Err(http::Error::Unknown); + } + + Ok(response) + } } diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 158e79eb9..76b9216b8 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -58,6 +58,12 @@ mod tests; pub mod migrations; +mod aggregator_client; + +pub mod proto { + include!(concat!(env!("OUT_DIR"),"/activity.rs")); +} + #[frame_support::pallet] pub mod pallet { @@ -1727,7 +1733,7 @@ pub mod pallet { let excessive_aggregate = consolidated_aggregate.aggregate.clone(); log::warn!( - "⚠️ Number of consistent aggregates with key {:?} exceeds the redundancy factor", + "⚠️ Number of consistent aggregates with key {:?} exceeds the redundancy factor", aggregate_key ); From 5f65d310ade893adbbc87f3417fab7787271897f Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 25 Oct 2024 15:27:31 +0500 Subject: [PATCH 05/37] Update Cargo.lock --- Cargo.lock | 81 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 184ce86c1..f1e8fa07a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5411,6 +5411,8 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "polkadot-ckb-merkle-mountain-range", + "prost 0.13.3", + "prost-build 0.13.3", "rand 0.8.5", "scale-info", "serde", @@ -6616,7 +6618,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +dependencies = [ + "bytes", + "prost-derive 0.13.3", ] [[package]] @@ -6633,14 +6645,35 @@ dependencies = [ "multimap", "petgraph", "prettyplease 0.1.25", - "prost", - "prost-types", + "prost 0.11.9", + "prost-types 0.11.9", "regex", "syn 1.0.109", "tempfile", "which", ] +[[package]] +name = "prost-build" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15" +dependencies = [ + "bytes", + "heck 0.5.0", + "itertools 0.13.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease 0.2.22", + "prost 0.13.3", + "prost-types 0.13.3", + "regex", + "syn 2.0.79", + "tempfile", +] + [[package]] name = "prost-derive" version = "0.11.9" @@ -6654,13 +6687,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "prost-types" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "prost", + "prost 0.11.9", +] + +[[package]] +name = "prost-types" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" +dependencies = [ + "prost 0.13.3", ] [[package]] @@ -7280,8 +7335,8 @@ dependencies = [ "log", "multihash", "parity-scale-codec", - "prost", - "prost-build", + "prost 0.11.9", + "prost-build 0.11.9", "rand 0.8.5", "sc-client-api", "sc-network", @@ -7766,8 +7821,8 @@ dependencies = [ "futures", "libp2p-identity", "log", - "prost", - "prost-build", + "prost 0.11.9", + "prost-build 0.11.9", "sc-client-api", "sc-network", "sp-blockchain", @@ -7786,7 +7841,7 @@ dependencies = [ "futures", "libp2p-identity", "parity-scale-codec", - "prost-build", + "prost-build 0.11.9", "sc-consensus", "sp-consensus", "sp-consensus-grandpa", @@ -7822,8 +7877,8 @@ dependencies = [ "libp2p-identity", "log", "parity-scale-codec", - "prost", - "prost-build", + "prost 0.11.9", + "prost-build 0.11.9", "sc-client-api", "sc-network", "sp-blockchain", @@ -7847,8 +7902,8 @@ dependencies = [ "log", "mockall", "parity-scale-codec", - "prost", - "prost-build", + "prost 0.11.9", + "prost-build 0.11.9", "sc-client-api", "sc-consensus", "sc-network", From 1a60e87bb3f16d41e39a992dce5910ed770e010f Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 25 Oct 2024 15:35:05 +0500 Subject: [PATCH 06/37] Remove unnecessary imports --- pallets/ddc-verification/src/aggregator_client.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index fbb0a7d88..e7f2ea741 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -1,7 +1,5 @@ use prost::Message; -use sp_std::prelude::Vec; -use sp_runtime::AccountId32; use sp_runtime::offchain::{http, Duration}; use sp_io::offchain::timestamp; From 82565be49f8333f505137e70abf77aef12f36943 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 25 Oct 2024 15:35:28 +0500 Subject: [PATCH 07/37] Add aggregator client unit tests --- pallets/ddc-verification/src/tests.rs | 99 ++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index 82f573130..a396b8a82 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -1,3 +1,5 @@ +use prost::Message; + use ddc_primitives::{ AggregatorInfo, ClusterId, MergeActivityHash, StorageNodeMode, StorageNodeParams, StorageNodePubKey, KEY_TYPE, @@ -12,7 +14,7 @@ use sp_core::{ }; use sp_io::TestExternalities; use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt}; -use sp_runtime::AccountId32; +use sp_runtime::{offchain::Duration, AccountId32}; use crate::{mock::*, Error, NodeAggregateResponse, *}; @@ -3080,3 +3082,98 @@ fn challenge_bucket_sub_aggregate_works() { }); } + +use crate::aggregator_client::AggregatorClient; + +#[test] +fn aggregator_client_challenge_bucket_sub_aggregate_works() { + let mut ext = TestExternalities::default(); + let (offchain, offchain_state) = TestOffchainExt::new(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(Box::new(offchain))); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + + let base_url = "http://example.com"; + let bucket_id = 1; + let era_id = 1; + let merkle_tree_node_id = "2,6"; + let node_id = "0x0ac7cb9c53594e9f538d9950c6bcf28f0c0c7b8385deea2ebe24062bc640e7be"; + + let expected_response = proto::ChallengeResponse{ + proofs: vec![ + proto::challenge_response::Proof{ + merkle_tree_node_id: 3, + ..Default::default() + } + ], + }; + let mut expected_response_serialized = Vec::new(); + let _ = expected_response.encode(&mut expected_response_serialized).unwrap(); + + let expected = PendingRequest { + method: "GET".into(), + headers: vec![("Accept".into(), "application/protobuf".into())], + uri: format!("{}/activity/buckets/{}/challenge?eraId={}&merkleTreeNodeId={}&nodeId={}", base_url, bucket_id, era_id, merkle_tree_node_id, node_id), + response: Some(expected_response_serialized), + sent: true, + ..Default::default() + }; + offchain_state.expect_request(expected); + drop(offchain_state); + + let client = AggregatorClient::new(base_url, Duration::from_millis(1_000)); + + let result = client.challenge_bucket_sub_aggregate(era_id, bucket_id, node_id, vec![2, 6]); + assert_eq!(result, Ok(expected_response)); + }) +} + +#[test] +fn aggregator_client_challenge_node_aggregate_works() { + let mut ext = TestExternalities::default(); + let (offchain, offchain_state) = TestOffchainExt::new(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(Box::new(offchain))); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + + let base_url = "http://example.com"; + let era_id = 1; + let merkle_tree_node_id = "2,6"; + let node_id = "0x0ac7cb9c53594e9f538d9950c6bcf28f0c0c7b8385deea2ebe24062bc640e7be"; + + let expected_response = proto::ChallengeResponse{ + proofs: vec![ + proto::challenge_response::Proof{ + merkle_tree_node_id: 3, + ..Default::default() + } + ], + }; + let mut expected_response_serialized = Vec::new(); + let _ = expected_response.encode(&mut expected_response_serialized).unwrap(); + + let expected = PendingRequest { + method: "GET".into(), + headers: vec![("Accept".into(), "application/protobuf".into())], + uri: format!("{}/activity/nodes/{}/challenge?eraId={}&merkleTreeNodeId={}", base_url, node_id, era_id, merkle_tree_node_id), + response: Some(expected_response_serialized), + sent: true, + ..Default::default() + }; + offchain_state.expect_request(expected); + drop(offchain_state); + + let client = AggregatorClient::new(base_url, Duration::from_millis(1_000)); + + let result = client.challenge_node_aggregate(era_id, node_id, vec![2, 6]); + assert_eq!(result, Ok(expected_response)); + }) +} From 2972a98c1976da4d61028475bebd682d3fbfc797 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 25 Oct 2024 15:58:39 +0500 Subject: [PATCH 08/37] Better test fixture --- pallets/ddc-verification/src/tests.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index a396b8a82..0b87fe1bc 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -3103,12 +3103,17 @@ fn aggregator_client_challenge_bucket_sub_aggregate_works() { let merkle_tree_node_id = "2,6"; let node_id = "0x0ac7cb9c53594e9f538d9950c6bcf28f0c0c7b8385deea2ebe24062bc640e7be"; - let expected_response = proto::ChallengeResponse{ + let expected_response = proto::ChallengeResponse { proofs: vec![ - proto::challenge_response::Proof{ - merkle_tree_node_id: 3, + proto::challenge_response::Proof { + merkle_tree_node_id: 2, + usage: Some(proto::Aggregate{stored: 4, delivered: 3, puts: 2, gets: 1}), ..Default::default() - } + }, proto::challenge_response::Proof { + merkle_tree_node_id: 6, + usage: Some(proto::Aggregate{stored: 8, delivered: 7, puts: 6, gets: 5}), + ..Default::default() + }, ], }; let mut expected_response_serialized = Vec::new(); @@ -3149,12 +3154,17 @@ fn aggregator_client_challenge_node_aggregate_works() { let merkle_tree_node_id = "2,6"; let node_id = "0x0ac7cb9c53594e9f538d9950c6bcf28f0c0c7b8385deea2ebe24062bc640e7be"; - let expected_response = proto::ChallengeResponse{ + let expected_response = proto::ChallengeResponse { proofs: vec![ - proto::challenge_response::Proof{ - merkle_tree_node_id: 3, + proto::challenge_response::Proof { + merkle_tree_node_id: 2, + usage: Some(proto::Aggregate{stored: 4, delivered: 3, puts: 2, gets: 1}), + ..Default::default() + }, proto::challenge_response::Proof { + merkle_tree_node_id: 6, + usage: Some(proto::Aggregate{stored: 8, delivered: 7, puts: 6, gets: 5}), ..Default::default() - } + }, ], }; let mut expected_response_serialized = Vec::new(); From e157006b5476407d6637892dd749295f4d134b5f Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 25 Oct 2024 16:06:22 +0500 Subject: [PATCH 09/37] cargo fmt --- pallets/ddc-verification/build.rs | 4 +- .../ddc-verification/src/aggregator_client.rs | 141 ++++++------ pallets/ddc-verification/src/lib.rs | 2 +- pallets/ddc-verification/src/tests.rs | 201 +++++++++--------- 4 files changed, 171 insertions(+), 177 deletions(-) diff --git a/pallets/ddc-verification/build.rs b/pallets/ddc-verification/build.rs index d0f81c79e..988993c3a 100644 --- a/pallets/ddc-verification/build.rs +++ b/pallets/ddc-verification/build.rs @@ -1,6 +1,6 @@ use std::io::Result; fn main() -> Result<()> { - prost_build::compile_protos(&["src/protos/activity.proto"], &["src/"])?; - Ok(()) + prost_build::compile_protos(&["src/protos/activity.proto"], &["src/"])?; + Ok(()) } diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index e7f2ea741..2bf60dec1 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -1,94 +1,81 @@ +use ddc_primitives::{BucketId, DdcEra}; use prost::Message; - -use sp_runtime::offchain::{http, Duration}; use sp_io::offchain::timestamp; - -use ddc_primitives::{BucketId, DdcEra}; +use sp_runtime::offchain::{http, Duration}; use super::*; pub struct AggregatorClient<'a> { - pub base_url: &'a str, - timeout: Duration, + pub base_url: &'a str, + timeout: Duration, } impl<'a> AggregatorClient<'a> { - pub fn new( - base_url: &'a str, - timeout: Duration, - ) -> Self { - Self { - base_url, - timeout, - } - } + pub fn new(base_url: &'a str, timeout: Duration) -> Self { + Self { base_url, timeout } + } - pub fn challenge_bucket_sub_aggregate( - &self, - era_id: DdcEra, - bucket_id: BucketId, - node_id: &str, - merkle_tree_node_id: Vec, - ) -> Result { - let merkle_tree_nodes_ids = merkle_tree_node_id - .iter() - .map(|x| format!("{}", x.clone())) - .collect::>() - .join(","); - let url = format!( - "{}/activity/buckets/{}/challenge?eraId={}&merkleTreeNodeId={}&nodeId={}", - self.base_url, - bucket_id, - era_id, - merkle_tree_nodes_ids, - node_id, - ); - let response = self.get_proto(&url)?; - let body = response.body().collect::>(); - let proto_response = proto::ChallengeResponse::decode(body.as_slice()).map_err(|_| http::Error::Unknown)?; + pub fn challenge_bucket_sub_aggregate( + &self, + era_id: DdcEra, + bucket_id: BucketId, + node_id: &str, + merkle_tree_node_id: Vec, + ) -> Result { + let merkle_tree_nodes_ids = merkle_tree_node_id + .iter() + .map(|x| format!("{}", x.clone())) + .collect::>() + .join(","); + let url = format!( + "{}/activity/buckets/{}/challenge?eraId={}&merkleTreeNodeId={}&nodeId={}", + self.base_url, bucket_id, era_id, merkle_tree_nodes_ids, node_id, + ); + let response = self.get_proto(&url)?; + let body = response.body().collect::>(); + let proto_response = + proto::ChallengeResponse::decode(body.as_slice()).map_err(|_| http::Error::Unknown)?; - Ok(proto_response) - } + Ok(proto_response) + } - pub fn challenge_node_aggregate( - &self, - era_id: DdcEra, - node_id: &str, - merkle_tree_node_id: Vec, - ) -> Result { - let merkle_tree_nodes_ids = merkle_tree_node_id - .iter() - .map(|x| format!("{}", x.clone())) - .collect::>() - .join(","); - let url = format!( - "{}/activity/nodes/{}/challenge?eraId={}&merkleTreeNodeId={}", - self.base_url, - node_id, - era_id, - merkle_tree_nodes_ids, - ); - let response = self.get_proto(&url)?; - let body = response.body().collect::>(); - let proto_response = proto::ChallengeResponse::decode(body.as_slice()).map_err(|_| http::Error::Unknown)?; + pub fn challenge_node_aggregate( + &self, + era_id: DdcEra, + node_id: &str, + merkle_tree_node_id: Vec, + ) -> Result { + let merkle_tree_nodes_ids = merkle_tree_node_id + .iter() + .map(|x| format!("{}", x.clone())) + .collect::>() + .join(","); + let url = format!( + "{}/activity/nodes/{}/challenge?eraId={}&merkleTreeNodeId={}", + self.base_url, node_id, era_id, merkle_tree_nodes_ids, + ); + let response = self.get_proto(&url)?; + let body = response.body().collect::>(); + let proto_response = + proto::ChallengeResponse::decode(body.as_slice()).map_err(|_| http::Error::Unknown)?; - Ok(proto_response) - } + Ok(proto_response) + } - fn get_proto(&self, url: &str) -> Result { - let deadline = timestamp().add(self.timeout); - let response = http::Request::get(url) - .add_header("Accept", "application/protobuf") - .deadline(deadline) - .send() - .map_err(|_| http::Error::IoError)? - .try_wait(deadline) - .map_err(|_| http::Error::DeadlineReached)??; + fn get_proto(&self, url: &str) -> Result { + let deadline = timestamp().add(self.timeout); + let response = http::Request::get(url) + .add_header("Accept", "application/protobuf") + .deadline(deadline) + .send() + .map_err(|_| http::Error::IoError)? + .try_wait(deadline) + .map_err(|_| http::Error::DeadlineReached)??; - if response.code != 200 { - return Err(http::Error::Unknown); - } + if response.code != 200 { + return Err(http::Error::Unknown); + } - Ok(response) - } + Ok(response) + } } diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 76b9216b8..3949900f9 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -61,7 +61,7 @@ pub mod migrations; mod aggregator_client; pub mod proto { - include!(concat!(env!("OUT_DIR"),"/activity.rs")); + include!(concat!(env!("OUT_DIR"), "/activity.rs")); } #[frame_support::pallet] diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index 0b87fe1bc..5b1dd7ba7 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -1,10 +1,9 @@ -use prost::Message; - use ddc_primitives::{ AggregatorInfo, ClusterId, MergeActivityHash, StorageNodeMode, StorageNodeParams, StorageNodePubKey, KEY_TYPE, }; use frame_support::{assert_noop, assert_ok}; +use prost::Message; use sp_core::{ offchain::{ testing::{PendingRequest, TestOffchainExt, TestTransactionPoolExt}, @@ -3087,103 +3086,111 @@ use crate::aggregator_client::AggregatorClient; #[test] fn aggregator_client_challenge_bucket_sub_aggregate_works() { - let mut ext = TestExternalities::default(); - let (offchain, offchain_state) = TestOffchainExt::new(); - - ext.register_extension(OffchainWorkerExt::new(offchain.clone())); - ext.register_extension(OffchainDbExt::new(Box::new(offchain))); - - ext.execute_with(|| { - let mut offchain_state = offchain_state.write(); - offchain_state.timestamp = Timestamp::from_unix_millis(0); - - let base_url = "http://example.com"; - let bucket_id = 1; - let era_id = 1; - let merkle_tree_node_id = "2,6"; - let node_id = "0x0ac7cb9c53594e9f538d9950c6bcf28f0c0c7b8385deea2ebe24062bc640e7be"; - - let expected_response = proto::ChallengeResponse { - proofs: vec![ - proto::challenge_response::Proof { - merkle_tree_node_id: 2, - usage: Some(proto::Aggregate{stored: 4, delivered: 3, puts: 2, gets: 1}), - ..Default::default() - }, proto::challenge_response::Proof { - merkle_tree_node_id: 6, - usage: Some(proto::Aggregate{stored: 8, delivered: 7, puts: 6, gets: 5}), - ..Default::default() - }, - ], - }; - let mut expected_response_serialized = Vec::new(); - let _ = expected_response.encode(&mut expected_response_serialized).unwrap(); - - let expected = PendingRequest { - method: "GET".into(), - headers: vec![("Accept".into(), "application/protobuf".into())], - uri: format!("{}/activity/buckets/{}/challenge?eraId={}&merkleTreeNodeId={}&nodeId={}", base_url, bucket_id, era_id, merkle_tree_node_id, node_id), - response: Some(expected_response_serialized), - sent: true, - ..Default::default() - }; - offchain_state.expect_request(expected); - drop(offchain_state); - - let client = AggregatorClient::new(base_url, Duration::from_millis(1_000)); - - let result = client.challenge_bucket_sub_aggregate(era_id, bucket_id, node_id, vec![2, 6]); - assert_eq!(result, Ok(expected_response)); - }) + let mut ext = TestExternalities::default(); + let (offchain, offchain_state) = TestOffchainExt::new(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(Box::new(offchain))); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + + let base_url = "http://example.com"; + let bucket_id = 1; + let era_id = 1; + let merkle_tree_node_id = "2,6"; + let node_id = "0x0ac7cb9c53594e9f538d9950c6bcf28f0c0c7b8385deea2ebe24062bc640e7be"; + + let expected_response = proto::ChallengeResponse { + proofs: vec![ + proto::challenge_response::Proof { + merkle_tree_node_id: 2, + usage: Some(proto::Aggregate { stored: 4, delivered: 3, puts: 2, gets: 1 }), + ..Default::default() + }, + proto::challenge_response::Proof { + merkle_tree_node_id: 6, + usage: Some(proto::Aggregate { stored: 8, delivered: 7, puts: 6, gets: 5 }), + ..Default::default() + }, + ], + }; + let mut expected_response_serialized = Vec::new(); + let _ = expected_response.encode(&mut expected_response_serialized).unwrap(); + + let expected = PendingRequest { + method: "GET".into(), + headers: vec![("Accept".into(), "application/protobuf".into())], + uri: format!( + "{}/activity/buckets/{}/challenge?eraId={}&merkleTreeNodeId={}&nodeId={}", + base_url, bucket_id, era_id, merkle_tree_node_id, node_id + ), + response: Some(expected_response_serialized), + sent: true, + ..Default::default() + }; + offchain_state.expect_request(expected); + drop(offchain_state); + + let client = AggregatorClient::new(base_url, Duration::from_millis(1_000)); + + let result = client.challenge_bucket_sub_aggregate(era_id, bucket_id, node_id, vec![2, 6]); + assert_eq!(result, Ok(expected_response)); + }) } #[test] fn aggregator_client_challenge_node_aggregate_works() { - let mut ext = TestExternalities::default(); - let (offchain, offchain_state) = TestOffchainExt::new(); - - ext.register_extension(OffchainWorkerExt::new(offchain.clone())); - ext.register_extension(OffchainDbExt::new(Box::new(offchain))); - - ext.execute_with(|| { - let mut offchain_state = offchain_state.write(); - offchain_state.timestamp = Timestamp::from_unix_millis(0); - - let base_url = "http://example.com"; - let era_id = 1; - let merkle_tree_node_id = "2,6"; - let node_id = "0x0ac7cb9c53594e9f538d9950c6bcf28f0c0c7b8385deea2ebe24062bc640e7be"; - - let expected_response = proto::ChallengeResponse { - proofs: vec![ - proto::challenge_response::Proof { - merkle_tree_node_id: 2, - usage: Some(proto::Aggregate{stored: 4, delivered: 3, puts: 2, gets: 1}), - ..Default::default() - }, proto::challenge_response::Proof { - merkle_tree_node_id: 6, - usage: Some(proto::Aggregate{stored: 8, delivered: 7, puts: 6, gets: 5}), - ..Default::default() - }, - ], - }; - let mut expected_response_serialized = Vec::new(); - let _ = expected_response.encode(&mut expected_response_serialized).unwrap(); - - let expected = PendingRequest { - method: "GET".into(), - headers: vec![("Accept".into(), "application/protobuf".into())], - uri: format!("{}/activity/nodes/{}/challenge?eraId={}&merkleTreeNodeId={}", base_url, node_id, era_id, merkle_tree_node_id), - response: Some(expected_response_serialized), - sent: true, - ..Default::default() - }; - offchain_state.expect_request(expected); - drop(offchain_state); - - let client = AggregatorClient::new(base_url, Duration::from_millis(1_000)); - - let result = client.challenge_node_aggregate(era_id, node_id, vec![2, 6]); - assert_eq!(result, Ok(expected_response)); - }) + let mut ext = TestExternalities::default(); + let (offchain, offchain_state) = TestOffchainExt::new(); + + ext.register_extension(OffchainWorkerExt::new(offchain.clone())); + ext.register_extension(OffchainDbExt::new(Box::new(offchain))); + + ext.execute_with(|| { + let mut offchain_state = offchain_state.write(); + offchain_state.timestamp = Timestamp::from_unix_millis(0); + + let base_url = "http://example.com"; + let era_id = 1; + let merkle_tree_node_id = "2,6"; + let node_id = "0x0ac7cb9c53594e9f538d9950c6bcf28f0c0c7b8385deea2ebe24062bc640e7be"; + + let expected_response = proto::ChallengeResponse { + proofs: vec![ + proto::challenge_response::Proof { + merkle_tree_node_id: 2, + usage: Some(proto::Aggregate { stored: 4, delivered: 3, puts: 2, gets: 1 }), + ..Default::default() + }, + proto::challenge_response::Proof { + merkle_tree_node_id: 6, + usage: Some(proto::Aggregate { stored: 8, delivered: 7, puts: 6, gets: 5 }), + ..Default::default() + }, + ], + }; + let mut expected_response_serialized = Vec::new(); + let _ = expected_response.encode(&mut expected_response_serialized).unwrap(); + + let expected = PendingRequest { + method: "GET".into(), + headers: vec![("Accept".into(), "application/protobuf".into())], + uri: format!( + "{}/activity/nodes/{}/challenge?eraId={}&merkleTreeNodeId={}", + base_url, node_id, era_id, merkle_tree_node_id + ), + response: Some(expected_response_serialized), + sent: true, + ..Default::default() + }; + offchain_state.expect_request(expected); + drop(offchain_state); + + let client = AggregatorClient::new(base_url, Duration::from_millis(1_000)); + + let result = client.challenge_node_aggregate(era_id, node_id, vec![2, 6]); + assert_eq!(result, Ok(expected_response)); + }) } From 6c146e14016200039581166cb8b4c4bbd4daea85 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 25 Oct 2024 16:29:07 +0500 Subject: [PATCH 10/37] Allow protobuf optional fields --- pallets/ddc-verification/build.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/ddc-verification/build.rs b/pallets/ddc-verification/build.rs index 988993c3a..ba49aa58b 100644 --- a/pallets/ddc-verification/build.rs +++ b/pallets/ddc-verification/build.rs @@ -1,6 +1,8 @@ use std::io::Result; fn main() -> Result<()> { - prost_build::compile_protos(&["src/protos/activity.proto"], &["src/"])?; + let mut prost_build = prost_build::Config::new(); + prost_build.protoc_arg("--experimental_allow_proto3_optional"); + prost_build.compile_protos(&["src/protos/activity.proto"], &["src/"])?; Ok(()) } From 69831642c9ced9423cfdc91a9a4057e0143db83c Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Mon, 28 Oct 2024 10:33:45 +0500 Subject: [PATCH 11/37] Request param making fn --- .../ddc-verification/src/aggregator_client.rs | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index 2bf60dec1..51a8369a7 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -22,14 +22,13 @@ impl<'a> AggregatorClient<'a> { node_id: &str, merkle_tree_node_id: Vec, ) -> Result { - let merkle_tree_nodes_ids = merkle_tree_node_id - .iter() - .map(|x| format!("{}", x.clone())) - .collect::>() - .join(","); let url = format!( "{}/activity/buckets/{}/challenge?eraId={}&merkleTreeNodeId={}&nodeId={}", - self.base_url, bucket_id, era_id, merkle_tree_nodes_ids, node_id, + self.base_url, + bucket_id, + era_id, + Self::merkle_tree_node_id_param(merkle_tree_node_id.as_slice()), + node_id, ); let response = self.get_proto(&url)?; let body = response.body().collect::>(); @@ -45,14 +44,12 @@ impl<'a> AggregatorClient<'a> { node_id: &str, merkle_tree_node_id: Vec, ) -> Result { - let merkle_tree_nodes_ids = merkle_tree_node_id - .iter() - .map(|x| format!("{}", x.clone())) - .collect::>() - .join(","); let url = format!( "{}/activity/nodes/{}/challenge?eraId={}&merkleTreeNodeId={}", - self.base_url, node_id, era_id, merkle_tree_nodes_ids, + self.base_url, + node_id, + era_id, + Self::merkle_tree_node_id_param(merkle_tree_node_id.as_slice()), ); let response = self.get_proto(&url)?; let body = response.body().collect::>(); @@ -62,6 +59,14 @@ impl<'a> AggregatorClient<'a> { Ok(proto_response) } + fn merkle_tree_node_id_param(merkle_tree_node_id: &[u32]) -> String { + merkle_tree_node_id + .iter() + .map(|x| format!("{}", x.clone())) + .collect::>() + .join(",") + } + fn get_proto(&self, url: &str) -> Result { let deadline = timestamp().add(self.timeout); let response = http::Request::get(url) From 05b8cb78692d005107cfe93a6c0cb1b0490de0af Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Tue, 29 Oct 2024 16:37:50 +0500 Subject: [PATCH 12/37] Add activity signature verification module --- pallets/ddc-verification/src/lib.rs | 2 + pallets/ddc-verification/src/signature.rs | 159 ++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 pallets/ddc-verification/src/signature.rs diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 3949900f9..e7e13a9c0 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -64,6 +64,8 @@ pub mod proto { include!(concat!(env!("OUT_DIR"), "/activity.rs")); } +mod signature; + #[frame_support::pallet] pub mod pallet { diff --git a/pallets/ddc-verification/src/signature.rs b/pallets/ddc-verification/src/signature.rs new file mode 100644 index 000000000..5fcb54345 --- /dev/null +++ b/pallets/ddc-verification/src/signature.rs @@ -0,0 +1,159 @@ +use prost::Message; +use sp_core::ed25519::{Public, Signature}; +use sp_io::crypto::ed25519_verify; + +use super::*; + +pub trait Verify { + fn verify(&self) -> bool; +} + +impl Verify for proto::ActivityAcknowledgment { + fn verify(&self) -> bool { + let signature = match &self.signature { + Some(s) => s.clone(), + None => return false, + }; + let sig = match Signature::try_from(signature.clone().value.as_slice()) { + Ok(s) => s, + Err(_) => return false, + }; + + let mut msg = self.clone(); + msg.signature = None; + let payload = msg.encode_to_vec(); + + let pub_key = match Public::try_from(signature.clone().signer.as_slice()) { + Ok(p) => p, + Err(_) => return false, + }; + + if !ed25519_verify(&sig, payload.as_slice(), &pub_key) { + return false; + } + + true + } +} + +impl Verify for proto::ActivityRecord { + fn verify(&self) -> bool { + let signature = match &self.signature { + Some(s) => s.clone(), + None => return false, + }; + let sig = match Signature::try_from(signature.clone().value.as_slice()) { + Ok(s) => s, + Err(_) => return false, + }; + + let mut msg = self.clone(); + msg.signature = None; + let payload = msg.encode_to_vec(); + + let pub_key = match Public::try_from(signature.clone().signer.as_slice()) { + Ok(p) => p, + Err(_) => return false, + }; + + if !ed25519_verify(&sig, payload.as_slice(), &pub_key) { + return false; + } + + for downstream in &self.downstream { + if !downstream.verify() { + return false; + } + } + + if let Some(upstream) = &self.upstream { + if !upstream.verify() { + return false; + } + } + + true + } +} + +impl Verify for proto::ActivityRequest { + fn verify(&self) -> bool { + let signature = match &self.signature { + Some(s) => s.clone(), + None => return false, + }; + let sig = match Signature::try_from(signature.clone().value.as_slice()) { + Ok(s) => s, + Err(_) => return false, + }; + + let mut msg = self.clone(); + msg.signature = None; + let payload = msg.encode_to_vec(); + + let pub_key = match Public::try_from(signature.clone().signer.as_slice()) { + Ok(p) => p, + Err(_) => return false, + }; + + if !ed25519_verify(&sig, payload.as_slice(), &pub_key) { + return false; + } + + if let Some(ref parent_request) = self.parent_request { + if !parent_request.verify() { + return false; + } + } + + true + } +} + +impl Verify for proto::ActivityFulfillment { + fn verify(&self) -> bool { + if let Some(request) = &self.request { + if !request.verify() { + return false; + } + } + + if let Some(ack) = &self.ack { + if !ack.verify() { + return false; + } + } + + true + } +} + +impl Verify for proto::challenge_response::proof::Record { + fn verify(&self) -> bool { + if let Some(record) = &self.record { + return record.verify(); + } + + true + } +} + +impl Verify for proto::ChallengeResponse { + fn verify(&self) -> bool { + for proof in self.proofs.iter() { + for leaf in proof.leaves.iter() { + if let Some(leaf) = &leaf.leaf_variant { + match leaf { + proto::challenge_response::proof::leaf::LeafVariant::Record(record) => + if !record.verify() { + return false; + }, + _ => {}, + } + } + } + } + + true + } +} From 41d06930bb619f395afc7b3155d3081d3651d8dd Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Tue, 29 Oct 2024 16:44:10 +0500 Subject: [PATCH 13/37] Disable parent request verification --- pallets/ddc-verification/src/signature.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pallets/ddc-verification/src/signature.rs b/pallets/ddc-verification/src/signature.rs index 5fcb54345..94c5c20e7 100644 --- a/pallets/ddc-verification/src/signature.rs +++ b/pallets/ddc-verification/src/signature.rs @@ -100,11 +100,12 @@ impl Verify for proto::ActivityRequest { return false; } - if let Some(ref parent_request) = self.parent_request { - if !parent_request.verify() { - return false; - } - } + // TODO(khssnv): parent requests are expected to have an invalid signature. + // if let Some(ref parent_request) = self.parent_request { + // if !parent_request.verify() { + // return false; + // } + // } true } From 6a5b6b7be6c96e67eb0ab08fd16e8e1e4050ad6d Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Wed, 30 Oct 2024 11:23:52 +0500 Subject: [PATCH 14/37] Remove repeated inner signature verification --- pallets/ddc-verification/src/signature.rs | 109 ++++++++++------------ 1 file changed, 48 insertions(+), 61 deletions(-) diff --git a/pallets/ddc-verification/src/signature.rs b/pallets/ddc-verification/src/signature.rs index 94c5c20e7..c48189882 100644 --- a/pallets/ddc-verification/src/signature.rs +++ b/pallets/ddc-verification/src/signature.rs @@ -10,53 +10,13 @@ pub trait Verify { impl Verify for proto::ActivityAcknowledgment { fn verify(&self) -> bool { - let signature = match &self.signature { - Some(s) => s.clone(), - None => return false, - }; - let sig = match Signature::try_from(signature.clone().value.as_slice()) { - Ok(s) => s, - Err(_) => return false, - }; - - let mut msg = self.clone(); - msg.signature = None; - let payload = msg.encode_to_vec(); - - let pub_key = match Public::try_from(signature.clone().signer.as_slice()) { - Ok(p) => p, - Err(_) => return false, - }; - - if !ed25519_verify(&sig, payload.as_slice(), &pub_key) { - return false; - } - - true + verify_signature(self.clone()) } } impl Verify for proto::ActivityRecord { fn verify(&self) -> bool { - let signature = match &self.signature { - Some(s) => s.clone(), - None => return false, - }; - let sig = match Signature::try_from(signature.clone().value.as_slice()) { - Ok(s) => s, - Err(_) => return false, - }; - - let mut msg = self.clone(); - msg.signature = None; - let payload = msg.encode_to_vec(); - - let pub_key = match Public::try_from(signature.clone().signer.as_slice()) { - Ok(p) => p, - Err(_) => return false, - }; - - if !ed25519_verify(&sig, payload.as_slice(), &pub_key) { + if !verify_signature(self.clone()) { return false; } @@ -78,25 +38,7 @@ impl Verify for proto::ActivityRecord { impl Verify for proto::ActivityRequest { fn verify(&self) -> bool { - let signature = match &self.signature { - Some(s) => s.clone(), - None => return false, - }; - let sig = match Signature::try_from(signature.clone().value.as_slice()) { - Ok(s) => s, - Err(_) => return false, - }; - - let mut msg = self.clone(); - msg.signature = None; - let payload = msg.encode_to_vec(); - - let pub_key = match Public::try_from(signature.clone().signer.as_slice()) { - Ok(p) => p, - Err(_) => return false, - }; - - if !ed25519_verify(&sig, payload.as_slice(), &pub_key) { + if !verify_signature(self.clone()) { return false; } @@ -158,3 +100,48 @@ impl Verify for proto::ChallengeResponse { true } } + + +trait Signed { + fn get_signature(&self) -> Option<&proto::Signature>; + fn reset_signature(&mut self); +} + +/// Implements Signed trait for given types. +macro_rules! impl_signed { + (for $($t:ty),+) => { + $(impl Signed for $t { + fn get_signature(&self) -> Option<&proto::Signature> { + return self.signature.as_ref() + } + + fn reset_signature(&mut self) { + self.signature = None; + } + })* + } +} + +impl_signed!(for proto::ActivityAcknowledgment, proto::ActivityRecord, proto::ActivityRequest); + +fn verify_signature(signed: impl Clone + Message + Signed) -> bool { + let signature = match signed.get_signature() { + Some(s) => s.clone(), + None => return false, + }; + let sig = match Signature::try_from(signature.clone().value.as_slice()) { + Ok(s) => s, + Err(_) => return false, + }; + + let mut msg = signed.clone(); + msg.reset_signature(); + let payload = msg.encode_to_vec(); + + let pub_key = match Public::try_from(signature.clone().signer.as_slice()) { + Ok(p) => p, + Err(_) => return false, + }; + + ed25519_verify(&sig, payload.as_slice(), &pub_key) +} From f1e994cca388489c18b84d16e372be8d586d5d42 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Wed, 30 Oct 2024 11:51:47 +0500 Subject: [PATCH 15/37] Less cloning --- pallets/ddc-verification/src/signature.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pallets/ddc-verification/src/signature.rs b/pallets/ddc-verification/src/signature.rs index c48189882..995499ae2 100644 --- a/pallets/ddc-verification/src/signature.rs +++ b/pallets/ddc-verification/src/signature.rs @@ -101,7 +101,6 @@ impl Verify for proto::ChallengeResponse { } } - trait Signed { fn get_signature(&self) -> Option<&proto::Signature>; fn reset_signature(&mut self); @@ -124,21 +123,20 @@ macro_rules! impl_signed { impl_signed!(for proto::ActivityAcknowledgment, proto::ActivityRecord, proto::ActivityRequest); -fn verify_signature(signed: impl Clone + Message + Signed) -> bool { +fn verify_signature(mut signed: impl Clone + Message + Signed) -> bool { let signature = match signed.get_signature() { Some(s) => s.clone(), None => return false, }; - let sig = match Signature::try_from(signature.clone().value.as_slice()) { + let sig = match Signature::try_from(signature.value.as_slice()) { Ok(s) => s, Err(_) => return false, }; - let mut msg = signed.clone(); - msg.reset_signature(); - let payload = msg.encode_to_vec(); + signed.reset_signature(); + let payload = signed.encode_to_vec(); - let pub_key = match Public::try_from(signature.clone().signer.as_slice()) { + let pub_key = match Public::try_from(signature.signer.as_slice()) { Ok(p) => p, Err(_) => return false, }; From 91d8db1fb09bc15ffa42600bc8021dafb776620f Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Wed, 30 Oct 2024 12:57:39 +0500 Subject: [PATCH 16/37] Signature verification test --- pallets/ddc-verification/src/signature.rs | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/pallets/ddc-verification/src/signature.rs b/pallets/ddc-verification/src/signature.rs index 995499ae2..426e49164 100644 --- a/pallets/ddc-verification/src/signature.rs +++ b/pallets/ddc-verification/src/signature.rs @@ -143,3 +143,52 @@ fn verify_signature(mut signed: impl Clone + Message + Signed) -> bool { ed25519_verify(&sig, payload.as_slice(), &pub_key) } + +#[cfg(test)] +mod tests { + use sp_core::Pair; + + use super::*; + + #[test] + fn verify_signature_works() { + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct SignedProtoMsg { + #[prost(string, tag = "1")] + pub foo: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub signature: ::core::option::Option, + } + impl_signed!(for SignedProtoMsg); + + let none_signature_msg = + SignedProtoMsg { foo: "none_signature_msg".to_string(), signature: None }; + assert!(!verify_signature(none_signature_msg)); + + let mut invalid_signature_msg = + SignedProtoMsg { foo: "invalid_signature_msg".to_string(), signature: None }; + let invalid_signature_msg_signer = sp_core::ed25519::Pair::generate().0; + let invalid_signature_msg_signature = + invalid_signature_msg_signer.sign(invalid_signature_msg.encode_to_vec().as_slice()); + let mut invalid_signature_msg_signature_vec = invalid_signature_msg_signature.0.to_vec(); + invalid_signature_msg_signature_vec[0] = invalid_signature_msg_signature_vec[0] + 1; + invalid_signature_msg.signature = Some(proto::Signature { + algorithm: proto::signature::Algorithm::Ed25519 as i32, + value: invalid_signature_msg_signature_vec, + signer: invalid_signature_msg_signer.public().0.to_vec(), + }); + assert!(!verify_signature(invalid_signature_msg)); + + let mut valid_signature_msg = + SignedProtoMsg { foo: "valid_signature_msg".to_string(), signature: None }; + let valid_signature_msg_signer = sp_core::ed25519::Pair::generate().0; + let valid_signature_msg_signature = + valid_signature_msg_signer.sign(valid_signature_msg.encode_to_vec().as_slice()); + valid_signature_msg.signature = Some(proto::Signature { + algorithm: proto::signature::Algorithm::Ed25519 as i32, + value: valid_signature_msg_signature.0.to_vec(), + signer: valid_signature_msg_signer.public().0.to_vec(), + }); + assert!(verify_signature(valid_signature_msg)); + } +} From 2e46caee877d606c463648045b7fd16ce517d49e Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Wed, 30 Oct 2024 13:55:51 +0500 Subject: [PATCH 17/37] Aggregate challenge signature verification test --- pallets/ddc-verification/src/signature.rs | 9 +++++++++ .../src/test_data/challenge_response.pb | Bin 0 -> 3491 bytes 2 files changed, 9 insertions(+) create mode 100644 pallets/ddc-verification/src/test_data/challenge_response.pb diff --git a/pallets/ddc-verification/src/signature.rs b/pallets/ddc-verification/src/signature.rs index 426e49164..0c258b8da 100644 --- a/pallets/ddc-verification/src/signature.rs +++ b/pallets/ddc-verification/src/signature.rs @@ -191,4 +191,13 @@ mod tests { }); assert!(verify_signature(valid_signature_msg)); } + + #[test] + fn verify_challenge_response_works() { + let challenge_response_serialized = + include_bytes!("./test_data/challenge_response.pb").as_slice(); + let challenge_response = proto::ChallengeResponse::decode(challenge_response_serialized) + .expect("protobuf fixture decoding failed, fix the test data"); + assert!(challenge_response.verify()); + } } diff --git a/pallets/ddc-verification/src/test_data/challenge_response.pb b/pallets/ddc-verification/src/test_data/challenge_response.pb new file mode 100644 index 0000000000000000000000000000000000000000..d43e443f6e4a56908346b74a114573d623f275d1 GIT binary patch literal 3491 zcmdUx>pztH8ph3N#u$f~&@>v&VydAY&GXDzu_lL|%$8#HQK6c7W}a3Nu|uJVoN_Fu zq$yGel|qO;h?SzU970wqDkYXIkx1-`PrWtk#eT7V|G<6Uzw7$m*Ns1+gF_S4aUml= zYlXZFCZZ@P5+&g7)<;XI=C&)Gb)^LZ)op2!z3HnWMCE%iz8x!TtPdMzYT%_B_*4zN zmeLj-GDvo?4b^#G8k!>NA#|hh|3wj~bTPsaA_xm$Az}%@1lcS=$YDzW5rYS@=xhuM5St zytI-%D6>APtv2JLfApdEyZ37!^?P~+2bMQo4;fuetLO_F^6*MCaJlaq(>{bzC#0e9 z3iYog|1{!nbDbF~)LHOtjJB!u#`f&;D>+?=+vDzR;f5Y>jcWsdf6*YShI#tE^PL_W zAKLoXbo+g}v?B?T4>B@uTKE3yW?<=VG^|O=QO1A$=l?I2#pCekA`t{|5H15?a#%b- zBtbv`VnKAKh|Q9)7(BTX(F&TQ{D(R44Nd2rc5b=o6qmmBUe#!Vx_V$(`1ALsGAee> zJ^ou6iEnm@6x#rzmzO}}ZRN6vr|aHX zT>kk@lk}$u;q9U|O^C&7)m!Q$Dozf_f_3th@h6o3@gPtcT)G(MN+bY}4$6gsVId&m zNaz5YA=ik>VKdpV_}j`*OelUx2*vjAh!qo;$y@o{^siWFYoiz2pqELhMdEE56Xg}@ zC6gzN!fglzw{+6#e@p%puI)D4)Y%ygpRAqu!zJZ8N)=OJ|3FDWv4&9iTKUT2&|wA} zWJ&;zL_`OeY)ArdSqKk+K`~bZ!gL`W=Kddto#`)kov_5!J!yXQ){yt|Y&kM&=NsLO z6L@A^c~VQiJmhz~^+VTIpvpadSfD&+Tiez7D)U}4cK2r2xH`Tagd*LFMv@mp z|G~@as5WCW;>X*@Rfn_Fkj@4(Z0v=DZ|c*X89Av`%x0+#iSHPYm)!_g8x@{AX|W|c zE8xy2^Gwa4f4Qb5X;rsc;!$v*hZQqnOuRt8ej_N_X~6YiP7+&xVqBe| zMo}_Bq3IbPLMyOD`Cc}>ipA$(@fldW7SyaWrP5}n64GX=iWO+F36~e)qm=R0a>^J+0U!@+|uuWCE~V-+o&Q&a8!4?>G;H%%n%E0o7VCK_rN?XA z%Inp!(aSHa%456d>2yWjzAAgFdlNmjBRcGqqv0Mg#T!GQN?-{Wq%(K`n}vt~CIb=6 zLzXE7U?IWKFx>Gy}JTdZ@5C@cYzx(T7{(}KgH$T%l;atsoCNunF<&ckYtpqNrK50S{k{y zFK7_mQW-u2dgO+^r50SRH1KZNS+MI|u}Q5mAyuBXiVUK4LYWvn*VwnPUD&!(8g8J2 zFqz9rDDpAqgznrsWYXH+(<_}Z_?*`8We zF?30Bq*_GF?&24%!Q|}`XX@DXuW@GfoG^`&I+dMO<8Hy88J8E$bAp=ZWbS^EyB2?5 z1*by5&)(=2kEga3ORsY&E3d`-p$97cTkkGE%-`= zyNmE4^8g~S5Msk16JUvXAi(5uUz+x^agyc*4JAexviE{v^{tW<0d49C2 zd?&+SNH@B#hy9)?K~=u`DxSi=ndfm?xCqDvI9!$lWOH~7AtYAxjNwOV+vfb}eqnzX z=R55EIAp#%W)H7{7bEyMqHFBA+YV>kFuED|<*BuB*}uuoN8n|@w{6T27dAIPnpRsf ztaH09sp4Tvb&xC#H3yy|187BQ6LaSpQw%(mlrgL~D(iE|%0E>eD9s-Wn}WX>Oicf& zohWMTTvvB);z|F}<=yQayZvrJzmm}zU7h0AncVS)zdlt}MjuizwJGy|r5K)8cI7Fy zoaR_hzmaD;G_btoi2V^KcjM*ZT$W3Lp6_T{@T-b+L3YwXx9#_}W|eRUFoJQ3ZT4t9$)E>V4-DvoWGfW2hj4WYWq_`>M(VYs>bl981orvD-i4 jp7j~0(~r}|Jh~Vy*tD^5{j1Z5>~Y`mJohcezT^2n7Qo1~ literal 0 HcmV?d00001 From 124dbcd58ac7864bc933534b4a5601662964f801 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 13:05:26 +0500 Subject: [PATCH 18/37] Protobuf challenge integration --- pallets/ddc-verification/src/lib.rs | 109 +++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index e7e13a9c0..f9a78ab60 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -38,7 +38,7 @@ use serde::{Deserialize, Serialize}; use sp_application_crypto::RuntimeAppPublic; use sp_runtime::{ offchain as rt_offchain, - offchain::{http, StorageKind}, + offchain::{http, Duration, StorageKind}, traits::Hash, Percent, }; @@ -1763,6 +1763,8 @@ pub mod pallet { // todo: run an intensive challenge for deviating aggregate // let is_passed = Self::_challenge_aggregate(_cluster_id, _era_id, // &defective_aggregate)?; + // let is_passed = Self::_challenge_aggregate_proto(_cluster_id, _era_id, + // &defective_aggregate)?; if is_passed { // we assume all aggregates are valid at the moment, so we just take the // aggregate to payouts stage @@ -1881,6 +1883,58 @@ pub mod pallet { } } + pub(crate) fn _challenge_aggregate_proto( + cluster_id: &ClusterId, + era_id: DdcEra, + aggregate: &A, + ) -> Result> { + let number_of_identifiers = T::MAX_MERKLE_NODE_IDENTIFIER; + + log::info!( + "🚀 Challenge process starts when bucket sub aggregates are not in consensus!" + ); + + let aggregate_key = aggregate.get_key(); + let merkle_node_ids = Self::_find_random_merkle_node_ids( + number_of_identifiers.into(), + aggregate.get_number_of_leaves(), + aggregate_key.clone(), + ); + + log::info!( + "🚀 Merkle Node Identifiers for aggregate key: {:?} identifiers: {:?}", + aggregate_key, + merkle_node_ids + ); + + let aggregator = aggregate.get_aggregator(); + + let challenge_response = Self::_fetch_challenge_responses_proto( + cluster_id, + era_id, + aggregate_key.clone(), + merkle_node_ids.iter().map(|id| *id as u32).collect(), + aggregator.clone(), + ) + .map_err(|err| vec![err])?; + + log::info!( + "🚀 Fetched challenge response for aggregate key: {:?}, challenge_response: {:?}", + aggregate_key, + challenge_response + ); + + let are_signatures_valid = signature::Verify::verify(&challenge_response); + + if are_signatures_valid { + log::info!("👍 Valid challenge signatures for aggregate key: {:?}", aggregate_key,); + } else { + log::info!("👎 Invalid challenge signatures at aggregate key: {:?}", aggregate_key,); + } + + return Ok(are_signatures_valid); + } + pub(crate) fn _get_hash_from_merkle_path( challenge_response: ChallengeAggregateResponse, cluster_id: &ClusterId, @@ -3135,6 +3189,30 @@ pub mod pallet { Ok(response) } + /// Challenge node aggregate or bucket sub-aggregate. + pub(crate) fn _fetch_challenge_responses_proto( + cluster_id: &ClusterId, + era_id: DdcEra, + aggregate_key: AggregateKey, + merkle_tree_node_id: Vec, + aggregator: AggregatorInfo, + ) -> Result { + let response = Self::_fetch_challenge_response_proto( + era_id, + aggregate_key.clone(), + merkle_tree_node_id.clone(), + &aggregator.node_params, + ) + .map_err(|_| OCWError::ChallengeResponseRetrievalError { + cluster_id: *cluster_id, + era_id, + aggregate_key, + aggregator: aggregator.node_pub_key, + })?; + + Ok(response) + } + /// Fetch challenge response. /// /// Parameters: @@ -3183,6 +3261,35 @@ pub mod pallet { serde_json::from_slice(&body).map_err(|_| http::Error::Unknown) } + /// Fetch protobuf challenge response. + pub(crate) fn _fetch_challenge_response_proto( + era_id: DdcEra, + aggregate_key: AggregateKey, + merkle_tree_node_id: Vec, + node_params: &StorageNodeParams, + ) -> Result { + let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; + let base_url = format!("http://{}:{}", host, node_params.http_port); + let client = aggregator_client::AggregatorClient::new( + &base_url, + Duration::from_millis(RESPONSE_TIMEOUT), + ); + + let challenge_response = match aggregate_key { + AggregateKey::BucketSubAggregateKey(bucket_id, node_id) => client + .challenge_bucket_sub_aggregate( + era_id, + bucket_id, + &node_id, + merkle_tree_node_id, + ), + AggregateKey::NodeAggregateKey(node_id) => + client.challenge_node_aggregate(era_id, &node_id, merkle_tree_node_id), + }; + + challenge_response + } + /// Fetch traverse response. /// /// Parameters: From 54e2226348e8df4f6ee57c33282ec72a2ca5ce2a Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 14:57:28 +0500 Subject: [PATCH 19/37] Retries for aggregator challenge requests --- .../ddc-verification/src/aggregator_client.rs | 51 +++++++++++++++---- pallets/ddc-verification/src/lib.rs | 1 + pallets/ddc-verification/src/tests.rs | 4 +- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index 51a8369a7..c2472d268 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -8,11 +8,12 @@ use super::*; pub struct AggregatorClient<'a> { pub base_url: &'a str, timeout: Duration, + retries: u32, } impl<'a> AggregatorClient<'a> { - pub fn new(base_url: &'a str, timeout: Duration) -> Self { - Self { base_url, timeout } + pub fn new(base_url: &'a str, timeout: Duration, retries: u32) -> Self { + Self { base_url, timeout, retries } } pub fn challenge_bucket_sub_aggregate( @@ -68,14 +69,46 @@ impl<'a> AggregatorClient<'a> { } fn get_proto(&self, url: &str) -> Result { + let mut maybe_response = None; + let deadline = timestamp().add(self.timeout); - let response = http::Request::get(url) - .add_header("Accept", "application/protobuf") - .deadline(deadline) - .send() - .map_err(|_| http::Error::IoError)? - .try_wait(deadline) - .map_err(|_| http::Error::DeadlineReached)??; + let mut error = None; + + for _ in 0..self.retries { + let maybe_pending = http::Request::get(url) + .add_header("Accept", "application/protobuf") + .deadline(deadline) + .send(); + + let pending = match maybe_pending { + Ok(p) => p, + Err(_) => { + error = Some(http::Error::IoError); + continue; + }, + }; + + match pending.try_wait(deadline) { + Ok(Ok(r)) => { + maybe_response = Some(r); + error = None; + break; + }, + Ok(Err(_)) | Err(_) => { + error = Some(http::Error::DeadlineReached); + continue; + }, + } + } + + if let Some(e) = error { + return Err(e); + } + + let response = match maybe_response { + Some(r) => r, + None => return Err(http::Error::Unknown), + }; if response.code != 200 { return Err(http::Error::Unknown); diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index f9a78ab60..1fb4197eb 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -3273,6 +3273,7 @@ pub mod pallet { let client = aggregator_client::AggregatorClient::new( &base_url, Duration::from_millis(RESPONSE_TIMEOUT), + 3, ); let challenge_response = match aggregate_key { diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index 5b1dd7ba7..9c7073607 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -3133,7 +3133,7 @@ fn aggregator_client_challenge_bucket_sub_aggregate_works() { offchain_state.expect_request(expected); drop(offchain_state); - let client = AggregatorClient::new(base_url, Duration::from_millis(1_000)); + let client = AggregatorClient::new(base_url, Duration::from_millis(1_000), 1); let result = client.challenge_bucket_sub_aggregate(era_id, bucket_id, node_id, vec![2, 6]); assert_eq!(result, Ok(expected_response)); @@ -3188,7 +3188,7 @@ fn aggregator_client_challenge_node_aggregate_works() { offchain_state.expect_request(expected); drop(offchain_state); - let client = AggregatorClient::new(base_url, Duration::from_millis(1_000)); + let client = AggregatorClient::new(base_url, Duration::from_millis(1_000), 1); let result = client.challenge_node_aggregate(era_id, node_id, vec![2, 6]); assert_eq!(result, Ok(expected_response)); From 7c18b21591c80de533d9e28f495e6236635fa3e9 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 15:12:32 +0500 Subject: [PATCH 20/37] Optional challenge for total usage --- pallets/ddc-verification/src/lib.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 1fb4197eb..26bbbb666 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -1534,8 +1534,12 @@ pub mod pallet { aggregators_quorum, ); - let total_buckets_usage = - Self::get_total_usage(cluster_id, era_activity.id, buckets_sub_aggregates_groups)?; + let total_buckets_usage = Self::get_total_usage( + cluster_id, + era_activity.id, + buckets_sub_aggregates_groups, + true, + )?; let customer_activity_hashes: Vec = total_buckets_usage.clone().into_iter().map(|c| c.hash::()).collect(); @@ -1594,7 +1598,7 @@ pub mod pallet { ); let total_nodes_usage = - Self::get_total_usage(cluster_id, era_activity.id, nodes_aggregates_groups)?; + Self::get_total_usage(cluster_id, era_activity.id, nodes_aggregates_groups, true)?; let node_activity_hashes: Vec = total_nodes_usage.clone().into_iter().map(|c| c.hash::()).collect(); @@ -1666,6 +1670,7 @@ pub mod pallet { cluster_id: &ClusterId, era_id: DdcEra, consistency_groups: ConsistencyGroups, + should_challenge: bool, ) -> Result, Vec> { let mut total_usage = vec![]; let mut total_usage_keys = vec![]; @@ -1699,6 +1704,7 @@ pub mod pallet { era_id, consistency_groups, &mut total_usage_keys, + should_challenge, )?; if !verified_usage.is_empty() { @@ -1713,6 +1719,7 @@ pub mod pallet { _era_id: DdcEra, consistency_groups: ConsistencyGroups, accepted_keys: &mut Vec, + should_challenge: bool, ) -> Result, Vec> { let redundancy_factor = T::DAC_REDUNDANCY_FACTOR; let mut verified_usage: Vec = vec![]; @@ -1759,12 +1766,17 @@ pub mod pallet { defective_aggregate.hash::() ); - let is_passed = true; + let mut is_passed = true; // todo: run an intensive challenge for deviating aggregate // let is_passed = Self::_challenge_aggregate(_cluster_id, _era_id, // &defective_aggregate)?; - // let is_passed = Self::_challenge_aggregate_proto(_cluster_id, _era_id, - // &defective_aggregate)?; + if should_challenge { + is_passed = Self::_challenge_aggregate_proto( + _cluster_id, + _era_id, + &defective_aggregate, + )?; + } if is_passed { // we assume all aggregates are valid at the moment, so we just take the // aggregate to payouts stage From 5b0ddc4758a7d7d81e4e8072d28d93714fa85d2d Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 15:13:16 +0500 Subject: [PATCH 21/37] Fix tests regression --- pallets/ddc-verification/src/tests.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index 9c7073607..e9f60825f 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -395,7 +395,7 @@ fn buckets_sub_aggregates_in_consensus_merged() { assert_eq!(groups.quorum.len(), 0); assert_eq!(groups.others.len(), 0); - let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups, false); assert!(result.is_ok()); let usages = result.unwrap(); @@ -519,7 +519,7 @@ fn buckets_sub_aggregates_in_quorum_merged() { assert_eq!(groups.quorum.len(), 1); // 2 consistent aggregates merged into 1 in 'quorum' assert_eq!(groups.others.len(), 1); // 1 inconsistent aggregate goes to 'others' - let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups, false); assert!(result.is_ok()); let usages = result.unwrap(); @@ -644,7 +644,7 @@ fn buckets_sub_aggregates_in_others_merged() { assert_eq!(groups.quorum.len(), 0); assert_eq!(groups.others.len(), 2); - let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups, false); assert!(result.is_ok()); let usages = result.unwrap(); @@ -769,7 +769,7 @@ fn buckets_sub_aggregates_in_others_merged_2() { assert_eq!(groups.quorum.len(), 0); assert_eq!(groups.others.len(), 2); // 2 inconsistent aggregates - let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups, false); assert!(result.is_ok()); let usages = result.unwrap(); @@ -878,7 +878,7 @@ fn nodes_aggregates_in_consensus_merged() { assert_eq!(groups.quorum.len(), 0); assert_eq!(groups.others.len(), 0); - let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups, false); assert!(result.is_ok()); let usages = result.unwrap(); @@ -981,7 +981,7 @@ fn nodes_aggregates_in_quorum_merged() { assert_eq!(groups.quorum.len(), 1); // 2 consistent aggregates merged into 1 in 'quorum' assert_eq!(groups.others.len(), 1); // 1 inconsistent aggregate goes to 'others' - let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups, false); assert!(result.is_ok()); let usages = result.unwrap(); @@ -1085,7 +1085,7 @@ fn nodes_aggregates_in_others_merged() { assert_eq!(groups.quorum.len(), 0); assert_eq!(groups.others.len(), 2); - let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups, false); assert!(result.is_ok()); let usages = result.unwrap(); @@ -1189,7 +1189,7 @@ fn nodes_aggregates_in_others_merged_2() { assert_eq!(groups.quorum.len(), 0); assert_eq!(groups.others.len(), 3); // 3 inconsistent aggregates - let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups); + let result = DdcVerification::get_total_usage(&cluster_id, era_id, groups, false); assert!(result.is_ok()); let usages = result.unwrap(); From 1e372eab65eaf5cdf8cf9273710993a8d6aa469d Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 15:55:51 +0500 Subject: [PATCH 22/37] Allow specifying accept header --- .../ddc-verification/src/aggregator_client.rs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index c2472d268..c318ef26b 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -31,7 +31,7 @@ impl<'a> AggregatorClient<'a> { Self::merkle_tree_node_id_param(merkle_tree_node_id.as_slice()), node_id, ); - let response = self.get_proto(&url)?; + let response = self.get(&url, Accept::Protobuf)?; let body = response.body().collect::>(); let proto_response = proto::ChallengeResponse::decode(body.as_slice()).map_err(|_| http::Error::Unknown)?; @@ -52,7 +52,7 @@ impl<'a> AggregatorClient<'a> { era_id, Self::merkle_tree_node_id_param(merkle_tree_node_id.as_slice()), ); - let response = self.get_proto(&url)?; + let response = self.get(&url, Accept::Protobuf)?; let body = response.body().collect::>(); let proto_response = proto::ChallengeResponse::decode(body.as_slice()).map_err(|_| http::Error::Unknown)?; @@ -68,19 +68,20 @@ impl<'a> AggregatorClient<'a> { .join(",") } - fn get_proto(&self, url: &str) -> Result { + fn get(&self, url: &str, accept: Accept) -> Result { let mut maybe_response = None; let deadline = timestamp().add(self.timeout); let mut error = None; for _ in 0..self.retries { - let maybe_pending = http::Request::get(url) - .add_header("Accept", "application/protobuf") - .deadline(deadline) - .send(); + let mut request = http::Request::get(url).deadline(deadline); + request = match accept { + Accept::Any => request, + Accept::Protobuf => request.add_header("Accept", "application/protobuf"), + }; - let pending = match maybe_pending { + let pending = match request.send() { Ok(p) => p, Err(_) => { error = Some(http::Error::IoError); @@ -117,3 +118,8 @@ impl<'a> AggregatorClient<'a> { Ok(response) } } + +enum Accept { + Any, + Protobuf, +} From 259be96dce0008f67f83fd54868d16b490ec66b0 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 17:02:43 +0500 Subject: [PATCH 23/37] Activity tree traversal methods for agg client --- .../ddc-verification/src/aggregator_client.rs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index c318ef26b..35d6eb288 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -60,6 +60,45 @@ impl<'a> AggregatorClient<'a> { Ok(proto_response) } + pub fn traverse_bucket_sub_aggregate( + &self, + era_id: DdcEra, + bucket_id: BucketId, + node_id: &str, + merkle_tree_node_id: u32, + levels: u16, + ) -> Result { + let url = format!( + "{}/activity/buckets/{}/traverse?eraId={}&nodeId={}&merkleTreeNodeId={}&levels={}", + self.base_url, bucket_id, era_id, node_id, merkle_tree_node_id, levels, + ); + + let response = self.get(&url, Accept::Any)?; + let body = response.body().collect::>(); + let json_response = serde_json::from_slice(&body).map_err(|_| http::Error::Unknown)?; + + Ok(json_response) + } + + pub fn traverse_node_aggregate( + &self, + era_id: DdcEra, + node_id: &str, + merkle_tree_node_id: u32, + levels: u16, + ) -> Result { + let url = format!( + "{}/activity/nodes/{}/traverse?eraId={}&merkleTreeNodeId={}&levels={}", + self.base_url, node_id, era_id, merkle_tree_node_id, levels, + ); + + let response = self.get(&url, Accept::Any)?; + let body = response.body().collect::>(); + let json_response = serde_json::from_slice(&body).map_err(|_| http::Error::Unknown)?; + + Ok(json_response) + } + fn merkle_tree_node_id_param(merkle_tree_node_id: &[u32]) -> String { merkle_tree_node_id .iter() From eecc11be0155c03e428c68617fab10a97fee341a Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 17:03:50 +0500 Subject: [PATCH 24/37] Use aggregator client for activity tree traversal --- pallets/ddc-verification/src/lib.rs | 111 ++++++++++++---------------- 1 file changed, 48 insertions(+), 63 deletions(-) diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 26bbbb666..dcc7b84c3 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -685,7 +685,7 @@ pub mod pallet { Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, )] pub(crate) struct Proof { - pub merkle_tree_node_id: u64, + pub merkle_tree_node_id: u32, pub usage: Usage, pub path: Vec, //todo! add base64 deserialization pub leafs: Vec, @@ -769,7 +769,7 @@ pub mod pallet { Debug, Serialize, Deserialize, Clone, Hash, Ord, PartialOrd, PartialEq, Eq, Encode, Decode, )] pub(crate) struct MerkleTreeNodeResponse { - merkle_tree_node_id: u64, + merkle_tree_node_id: u32, hash: String, stored_bytes: i64, transferred_bytes: u64, @@ -1843,10 +1843,10 @@ pub mod pallet { calculated_merkle_root ); - let traverse_response = Self::_fetch_traverse_response( + let root_merkle_node = Self::_fetch_traverse_response( era_id, aggregate_key.clone(), - vec![1], + 1, 1, &aggregator.node_params, ) @@ -1859,40 +1859,36 @@ pub mod pallet { }] })?; - if let Some(root_merkle_node) = traverse_response.first() { - let mut merkle_root_buf = [0u8; _BUF_SIZE]; - let bytes = - Base64::decode(root_merkle_node.hash.clone(), &mut merkle_root_buf).unwrap(); // todo! remove unwrap - let traversed_merkle_root = ActivityHash::from(sp_core::H256::from_slice(bytes)); + let mut merkle_root_buf = [0u8; _BUF_SIZE]; + let bytes = + Base64::decode(root_merkle_node.hash.clone(), &mut merkle_root_buf).unwrap(); // todo! remove unwrap + let traversed_merkle_root = ActivityHash::from(sp_core::H256::from_slice(bytes)); + + log::info!( + "🚀 Fetched merkle root for aggregate key: {:?} traversed_merkle_root: {:?}", + aggregate_key, + traversed_merkle_root + ); + let is_matched = if calculated_merkle_root == traversed_merkle_root { log::info!( - "🚀 Fetched merkle root for aggregate key: {:?} traversed_merkle_root: {:?}", + "🚀👍 The aggregate with hash {:?} and key {:?} has passed the challenge.", + aggregate.hash::(), aggregate_key, - traversed_merkle_root ); - let is_matched = if calculated_merkle_root == traversed_merkle_root { - log::info!( - "🚀👍 The aggregate with hash {:?} and key {:?} has passed the challenge.", - aggregate.hash::(), - aggregate_key, - ); - - true - } else { - log::info!( - "🚀👎 The aggregate with hash {:?} and key {:?} has not passed the challenge.", - aggregate.hash::(), - aggregate_key, - ); + true + } else { + log::info!( + "🚀👎 The aggregate with hash {:?} and key {:?} has not passed the challenge.", + aggregate.hash::(), + aggregate_key, + ); - false - }; + false + }; - Ok(is_matched) - } else { - Ok(false) - } + Ok(is_matched) } pub(crate) fn _challenge_aggregate_proto( @@ -3314,43 +3310,32 @@ pub mod pallet { pub(crate) fn _fetch_traverse_response( era_id: DdcEra, aggregate_key: AggregateKey, - merkle_node_identifiers: Vec, + merkle_tree_node_id: u32, levels: u16, node_params: &StorageNodeParams, - ) -> Result, http::Error> { - let scheme = "http"; + ) -> Result { let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; + let base_url = format!("http://{}:{}", host, node_params.http_port); + let client = aggregator_client::AggregatorClient::new( + &base_url, + Duration::from_millis(RESPONSE_TIMEOUT), + 3, + ); - let ids = merkle_node_identifiers - .iter() - .map(|x| format!("{}", x.clone())) - .collect::>() - .join(","); - - let url = match aggregate_key { - AggregateKey::NodeAggregateKey(node_id) => format!( - "{}://{}:{}/activity/nodes/{}/traverse?eraId={}&merkleTreeNodeId={}&levels={}", - scheme, host, node_params.http_port, node_id, era_id, ids, levels - ), - AggregateKey::BucketSubAggregateKey(bucket_id, node_id) => format!( - "{}://{}:{}/activity/buckets/{}/traverse?eraId={}&nodeId={}&merkleTreeNodeId={}&levels={}", - scheme, host, node_params.http_port, bucket_id, era_id, node_id, ids, levels - ), - }; - - let request = http::Request::get(&url); - let timeout = sp_io::offchain::timestamp() - .add(sp_runtime::offchain::Duration::from_millis(RESPONSE_TIMEOUT)); - let pending = request.deadline(timeout).send().map_err(|_| http::Error::IoError)?; - - let response = - pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; - if response.code != SUCCESS_CODE { - return Err(http::Error::Unknown); - } + let response = match aggregate_key { + AggregateKey::BucketSubAggregateKey(bucket_id, node_id) => client + .traverse_bucket_sub_aggregate( + era_id, + bucket_id, + &node_id, + merkle_tree_node_id, + levels, + ), + AggregateKey::NodeAggregateKey(node_id) => + client.traverse_node_aggregate(era_id, &node_id, merkle_tree_node_id, levels), + }?; - let body = response.body().collect::>(); - serde_json::from_slice(&body).map_err(|_| http::Error::Unknown) + Ok(response) } /// Fetch processed era. From d7637482f17a005a861c499c9992a78510019036 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 17:04:11 +0500 Subject: [PATCH 25/37] Fix test regression --- pallets/ddc-verification/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index e9f60825f..e79393f4e 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -3034,7 +3034,7 @@ fn challenge_bucket_sub_aggregate_works() { let pending_request2 = PendingRequest { method: "GET".to_string(), uri: format!("http://{}:{}/activity/buckets/123229/traverse?eraId=5757773&nodeId=0x1f50f1455f60f5774564233d321a116ca45ae3188b2200999445706d04839d72&merkleTreeNodeId=1&levels=1", host1, port), - response: Some(br#"[{"merkle_tree_node_id":2,"hash":"hkujtYgWP21CrXdRP1rhRPrYR2ooIYCnP5zwCERTePI=","stored_bytes":20913291,"transferred_bytes":20913291,"number_of_puts":61,"number_of_gets":3},{"merkle_tree_node_id":3,"hash":"ZgWwK2LgWkHpx5JlXZn/Rouq6uE9DhOnRH6EA1+QO6o=","stored_bytes":23778084,"transferred_bytes":23778084,"number_of_puts":46,"number_of_gets":2}]"#.to_vec()), + response: Some(br#"{"merkle_tree_node_id":2,"hash":"hkujtYgWP21CrXdRP1rhRPrYR2ooIYCnP5zwCERTePI=","stored_bytes":20913291,"transferred_bytes":20913291,"number_of_puts":61,"number_of_gets":3}"#.to_vec()), sent: true, ..Default::default() }; From 83bbfb663834a79462c230b190461a2cce3b3bec Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 17:12:52 +0500 Subject: [PATCH 26/37] Aggregator client method for `activity/eras` --- pallets/ddc-verification/src/aggregator_client.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index 35d6eb288..bd7917ebf 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -60,6 +60,15 @@ impl<'a> AggregatorClient<'a> { Ok(proto_response) } + pub fn eras(&self) -> Result, http::Error> { + let url = format!("{}/activity/eras", self.base_url); + let response = self.get(&url, Accept::Any)?; + let body = response.body().collect::>(); + let json_response = serde_json::from_slice(&body).map_err(|_| http::Error::Unknown)?; + + Ok(json_response) + } + pub fn traverse_bucket_sub_aggregate( &self, era_id: DdcEra, From 9ddaab99262c5c683b47bf2a948fa5e8f7ee0af2 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 17:14:00 +0500 Subject: [PATCH 27/37] Use aggregator client for activity eras --- pallets/ddc-verification/src/lib.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index dcc7b84c3..6af89d30a 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -3346,23 +3346,15 @@ pub mod pallet { pub(crate) fn fetch_processed_eras( node_params: &StorageNodeParams, ) -> Result, http::Error> { - let scheme = "http"; let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; - let url = format!("{}://{}:{}/activity/eras", scheme, host, node_params.http_port); - let request = http::Request::get(&url); - let timeout = sp_io::offchain::timestamp() - .add(sp_runtime::offchain::Duration::from_millis(RESPONSE_TIMEOUT)); - let pending = request.deadline(timeout).send().map_err(|_| http::Error::IoError)?; - - let response = - pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; - if response.code != SUCCESS_CODE { - return Err(http::Error::Unknown); - } + let base_url = format!("http://{}:{}", host, node_params.http_port); + let client = aggregator_client::AggregatorClient::new( + &base_url, + Duration::from_millis(RESPONSE_TIMEOUT), + 3, + ); - let body = response.body().collect::>(); - let res: Vec = - serde_json::from_slice(&body).map_err(|_| http::Error::Unknown)?; + let res = client.eras()?; let processed_status = String::from("PROCESSED"); Ok(res.into_iter().filter(|e| e.status == processed_status).collect::>()) From 663568d455e2779247c73b73491412469ba59873 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 17:27:34 +0500 Subject: [PATCH 28/37] Aggregator client method for fetching aggregates --- .../ddc-verification/src/aggregator_client.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index bd7917ebf..851b28b93 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -16,6 +16,30 @@ impl<'a> AggregatorClient<'a> { Self { base_url, timeout, retries } } + pub fn buckets_aggregates( + &self, + era_id: DdcEra, + ) -> Result, http::Error> { + let url = format!("{}/activity/buckets?eraId={}", self.base_url, era_id); + let response = self.get(&url, Accept::Any)?; + let body = response.body().collect::>(); + let json_response = serde_json::from_slice(&body).map_err(|_| http::Error::Unknown)?; + + Ok(json_response) + } + + pub fn nodes_aggregates( + &self, + era_id: DdcEra, + ) -> Result, http::Error> { + let url = format!("{}/activity/nodes?eraId={}", self.base_url, era_id); + let response = self.get(&url, Accept::Any)?; + let body = response.body().collect::>(); + let json_response = serde_json::from_slice(&body).map_err(|_| http::Error::Unknown)?; + + Ok(json_response) + } + pub fn challenge_bucket_sub_aggregate( &self, era_id: DdcEra, From cf535d8656d87903df0fe328fe296cdaa50f8bb2 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 17:28:09 +0500 Subject: [PATCH 29/37] Use aggregator client to fetch aggregates --- pallets/ddc-verification/src/lib.rs | 46 ++++++++--------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 6af89d30a..0ec6467e7 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -3370,26 +3370,15 @@ pub mod pallet { era_id: DdcEra, node_params: &StorageNodeParams, ) -> Result, http::Error> { - let scheme = "http"; let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; - let url = format!( - "{}://{}:{}/activity/buckets?eraId={}", - scheme, host, node_params.http_port, era_id + let base_url = format!("http://{}:{}", host, node_params.http_port); + let client = aggregator_client::AggregatorClient::new( + &base_url, + Duration::from_millis(RESPONSE_TIMEOUT), + 3, ); - let request = http::Request::get(&url); - let timeout = sp_io::offchain::timestamp() - .add(sp_runtime::offchain::Duration::from_millis(RESPONSE_TIMEOUT)); - let pending = request.deadline(timeout).send().map_err(|_| http::Error::IoError)?; - - let response = - pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; - if response.code != SUCCESS_CODE { - return Err(http::Error::Unknown); - } - - let body = response.body().collect::>(); - serde_json::from_slice(&body).map_err(|_| http::Error::Unknown) + client.buckets_aggregates(era_id) } /// Fetch node usage. @@ -3403,26 +3392,15 @@ pub mod pallet { era_id: DdcEra, node_params: &StorageNodeParams, ) -> Result, http::Error> { - let scheme = "http"; let host = str::from_utf8(&node_params.host).map_err(|_| http::Error::Unknown)?; - let url = format!( - "{}://{}:{}/activity/nodes?eraId={}", - scheme, host, node_params.http_port, era_id + let base_url = format!("http://{}:{}", host, node_params.http_port); + let client = aggregator_client::AggregatorClient::new( + &base_url, + Duration::from_millis(RESPONSE_TIMEOUT), + 3, ); - let request = http::Request::get(&url); - let timeout = sp_io::offchain::timestamp() - .add(rt_offchain::Duration::from_millis(RESPONSE_TIMEOUT)); - let pending = request.deadline(timeout).send().map_err(|_| http::Error::IoError)?; - - let response = - pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; - if response.code != SUCCESS_CODE { - return Err(http::Error::Unknown); - } - - let body = response.body().collect::>(); - serde_json::from_slice(&body).map_err(|_| http::Error::Unknown) + client.nodes_aggregates(era_id) } /// Fetch DAC nodes of a cluster. From f5824a5e6a835215ad2742bfe3691bfd5e8c6614 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 17:49:25 +0500 Subject: [PATCH 30/37] Unify query parameters order --- pallets/ddc-verification/src/aggregator_client.rs | 4 ++-- pallets/ddc-verification/src/tests.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index 851b28b93..986de0771 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -48,12 +48,12 @@ impl<'a> AggregatorClient<'a> { merkle_tree_node_id: Vec, ) -> Result { let url = format!( - "{}/activity/buckets/{}/challenge?eraId={}&merkleTreeNodeId={}&nodeId={}", + "{}/activity/buckets/{}/challenge?eraId={}&nodeId={}&merkleTreeNodeId={}", self.base_url, bucket_id, era_id, - Self::merkle_tree_node_id_param(merkle_tree_node_id.as_slice()), node_id, + Self::merkle_tree_node_id_param(merkle_tree_node_id.as_slice()), ); let response = self.get(&url, Accept::Protobuf)?; let body = response.body().collect::>(); diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index e79393f4e..5498fb2a2 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -3123,8 +3123,8 @@ fn aggregator_client_challenge_bucket_sub_aggregate_works() { method: "GET".into(), headers: vec![("Accept".into(), "application/protobuf".into())], uri: format!( - "{}/activity/buckets/{}/challenge?eraId={}&merkleTreeNodeId={}&nodeId={}", - base_url, bucket_id, era_id, merkle_tree_node_id, node_id + "{}/activity/buckets/{}/challenge?eraId={}&nodeId={}&merkleTreeNodeId={}", + base_url, bucket_id, era_id, node_id, merkle_tree_node_id ), response: Some(expected_response_serialized), sent: true, From 2ae69e4da8ec80b16c98f8a7e5242aa6a5e8266a Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 31 Oct 2024 17:57:45 +0500 Subject: [PATCH 31/37] Remove redundant string alloc --- pallets/ddc-verification/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 0ec6467e7..4b9349f95 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -3354,10 +3354,9 @@ pub mod pallet { 3, ); - let res = client.eras()?; + let response = client.eras()?; - let processed_status = String::from("PROCESSED"); - Ok(res.into_iter().filter(|e| e.status == processed_status).collect::>()) + Ok(response.into_iter().filter(|e| e.status == "PROCESSED").collect::>()) } /// Fetch customer usage. /// From ded69fb603cd5ecb28fa9274a8f6847f86b5ada0 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 1 Nov 2024 11:14:43 +0500 Subject: [PATCH 32/37] Add pagination for aggregates fetching --- .../ddc-verification/src/aggregator_client.rs | 20 +++++++++++++++++-- pallets/ddc-verification/src/lib.rs | 4 ++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index 986de0771..aa6e24b50 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -19,8 +19,16 @@ impl<'a> AggregatorClient<'a> { pub fn buckets_aggregates( &self, era_id: DdcEra, + limit: Option, + prev_token: Option, ) -> Result, http::Error> { - let url = format!("{}/activity/buckets?eraId={}", self.base_url, era_id); + let mut url = format!("{}/activity/buckets?eraId={}", self.base_url, era_id); + if let Some(limit) = limit { + url = format!("{}&limit={}", url, limit); + } + if let Some(prev_token) = prev_token { + url = format!("{}&prevToken={}", url, prev_token); + } let response = self.get(&url, Accept::Any)?; let body = response.body().collect::>(); let json_response = serde_json::from_slice(&body).map_err(|_| http::Error::Unknown)?; @@ -31,8 +39,16 @@ impl<'a> AggregatorClient<'a> { pub fn nodes_aggregates( &self, era_id: DdcEra, + limit: Option, + prev_token: Option, // node_id hex string ) -> Result, http::Error> { - let url = format!("{}/activity/nodes?eraId={}", self.base_url, era_id); + let mut url = format!("{}/activity/nodes?eraId={}", self.base_url, era_id); + if let Some(limit) = limit { + url = format!("{}&limit={}", url, limit); + } + if let Some(prev_token) = prev_token { + url = format!("{}&prevToken={}", url, prev_token); + } let response = self.get(&url, Accept::Any)?; let body = response.body().collect::>(); let json_response = serde_json::from_slice(&body).map_err(|_| http::Error::Unknown)?; diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 4b9349f95..9a0318b3c 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -3377,7 +3377,7 @@ pub mod pallet { 3, ); - client.buckets_aggregates(era_id) + client.buckets_aggregates(era_id, None, None) } /// Fetch node usage. @@ -3399,7 +3399,7 @@ pub mod pallet { 3, ); - client.nodes_aggregates(era_id) + client.nodes_aggregates(era_id, None, None) } /// Fetch DAC nodes of a cluster. From 991b581535751d67c6475d5b9b768c99074f0fd3 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 1 Nov 2024 12:55:41 +0500 Subject: [PATCH 33/37] Use pagination for aggregates fetching --- pallets/ddc-verification/src/lib.rs | 46 +++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 9a0318b3c..bb66c4aed 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -83,6 +83,8 @@ pub mod pallet { const SUCCESS_CODE: u16 = 200; const _BUF_SIZE: usize = 128; const RESPONSE_TIMEOUT: u64 = 20000; + const BUCKETS_AGGREGATES_FETCH_BATCH_SIZE: usize = 100; + const NODES_AGGREGATES_FETCH_BATCH_SIZE: usize = 10; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -3377,7 +3379,27 @@ pub mod pallet { 3, ); - client.buckets_aggregates(era_id, None, None) + let mut buckets_aggregates = Vec::new(); + let mut prev_token = None; + + loop { + let response = client.buckets_aggregates( + era_id, + Some(BUCKETS_AGGREGATES_FETCH_BATCH_SIZE as u32), + prev_token, + )?; + + let response_len = response.len(); + prev_token = response.last().map(|a| a.bucket_id.clone()); + + buckets_aggregates.extend(response); + + if response_len < BUCKETS_AGGREGATES_FETCH_BATCH_SIZE { + break; + } + } + + Ok(buckets_aggregates) } /// Fetch node usage. @@ -3399,7 +3421,27 @@ pub mod pallet { 3, ); - client.nodes_aggregates(era_id, None, None) + let mut nodes_aggregates = Vec::new(); + let mut prev_token = None; + + loop { + let response = client.nodes_aggregates( + era_id, + Some(NODES_AGGREGATES_FETCH_BATCH_SIZE as u32), + prev_token, + )?; + + let response_len = response.len(); + prev_token = response.last().map(|a| a.node_id.clone()); + + nodes_aggregates.extend(response); + + if response_len < NODES_AGGREGATES_FETCH_BATCH_SIZE { + break; + } + } + + Ok(nodes_aggregates) } /// Fetch DAC nodes of a cluster. From 0de8ec8e7385a119a413bc3b54de94538288e2b0 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Fri, 1 Nov 2024 13:24:49 +0500 Subject: [PATCH 34/37] Fix tests regression --- pallets/ddc-verification/src/lib.rs | 4 +- pallets/ddc-verification/src/tests.rs | 60 +++++++++++++-------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index bb66c4aed..4eac18ecd 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -83,8 +83,8 @@ pub mod pallet { const SUCCESS_CODE: u16 = 200; const _BUF_SIZE: usize = 128; const RESPONSE_TIMEOUT: u64 = 20000; - const BUCKETS_AGGREGATES_FETCH_BATCH_SIZE: usize = 100; - const NODES_AGGREGATES_FETCH_BATCH_SIZE: usize = 10; + pub const BUCKETS_AGGREGATES_FETCH_BATCH_SIZE: usize = 100; + pub const NODES_AGGREGATES_FETCH_BATCH_SIZE: usize = 10; #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index 5498fb2a2..2129d5488 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -137,7 +137,7 @@ fn fetch_node_aggregates_works() { // Mock HTTP request and response let pending_request = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId={}", host, port, era_id), + uri: format!("http://{}:{}/activity/nodes?eraId={}&limit={}", host, port, era_id, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(nodes_activity_json.as_bytes().to_vec()), sent: true, ..Default::default() @@ -225,7 +225,7 @@ fn fetch_bucket_aggregates_works() { // Mock HTTP request and response let pending_request = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId={}", host, port, era_id), + uri: format!("http://{}:{}/activity/buckets?eraId={}&limit={}", host, port, era_id, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(customers_activity_json.as_bytes().to_vec()), sent: true, ..Default::default() @@ -1596,7 +1596,7 @@ fn bucket_sub_aggregates_are_fetched_and_grouped() { let pending_request1 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=476817", host1, port), + uri: format!("http://{}:{}/activity/buckets?eraId=476817&limit={}", host1, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318","stored_bytes":578,"transferred_bytes":578,"number_of_puts":2,"number_of_gets":0}]},{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa319","stored_bytes":0,"transferred_bytes":505,"number_of_puts":0,"number_of_gets":1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -1604,7 +1604,7 @@ fn bucket_sub_aggregates_are_fetched_and_grouped() { let pending_request2 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=476817", host2, port), + uri: format!("http://{}:{}/activity/buckets?eraId=476817&limit={}", host2, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318","stored_bytes":578,"transferred_bytes":578,"number_of_puts":2,"number_of_gets":0}]},{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa319","stored_bytes":0,"transferred_bytes":506,"number_of_puts":0,"number_of_gets":1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -1612,7 +1612,7 @@ fn bucket_sub_aggregates_are_fetched_and_grouped() { let pending_request3 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=476817", host3, port), + uri: format!("http://{}:{}/activity/buckets?eraId=476817&limit={}", host3, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318","stored_bytes":578,"transferred_bytes":578,"number_of_puts":2,"number_of_gets":0}]},{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa319","stored_bytes":0,"transferred_bytes":505,"number_of_puts":0,"number_of_gets":1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -1620,7 +1620,7 @@ fn bucket_sub_aggregates_are_fetched_and_grouped() { let pending_request4 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=476817", host4, port), + uri: format!("http://{}:{}/activity/buckets?eraId=476817&limit={}", host4, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[]}]"#.to_vec()), sent: true, ..Default::default() @@ -1628,7 +1628,7 @@ fn bucket_sub_aggregates_are_fetched_and_grouped() { let pending_request5 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=476817", host5, port), + uri: format!("http://{}:{}/activity/buckets?eraId=476817&limit={}", host5, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"bucket_id":90235,"stored_bytes":0,"transferred_bytes":38,"number_of_puts":0,"number_of_gets":1,"sub_aggregates":[{"NodeID":"0xb6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa320","stored_bytes":578,"transferred_bytes":578,"number_of_puts":2,"number_of_gets":0}]}]"#.to_vec()), sent: true, ..Default::default() @@ -1861,7 +1861,7 @@ fn node_aggregates_are_fetched_and_grouped() { let pending_request1 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=476817", host1, port), + uri: format!("http://{}:{}/activity/nodes?eraId=476817&limit={}", host1, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -1869,7 +1869,7 @@ fn node_aggregates_are_fetched_and_grouped() { let pending_request2 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=476817", host2, port), + uri: format!("http://{}:{}/activity/nodes?eraId=476817&limit={}", host2, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 48,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -1877,7 +1877,7 @@ fn node_aggregates_are_fetched_and_grouped() { let pending_request3 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=476817", host3, port), + uri: format!("http://{}:{}/activity/nodes?eraId=476817&limit={}", host3, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97}]"#.to_vec()), sent: true, ..Default::default() @@ -1885,7 +1885,7 @@ fn node_aggregates_are_fetched_and_grouped() { let pending_request4 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=476817", host4, port), + uri: format!("http://{}:{}/activity/nodes?eraId=476817&limit={}", host4, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -1893,7 +1893,7 @@ fn node_aggregates_are_fetched_and_grouped() { let pending_request5 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=476817", host5, port), + uri: format!("http://{}:{}/activity/nodes?eraId=476817&limit={}", host5, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0xfc28d5f5bb10212077a8654f62c4f8f0b5ab985fc322a51f5a3c75943b29194b","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97}]"#.to_vec()), sent: true, ..Default::default() @@ -2646,7 +2646,7 @@ fn test_single_ocw_pallet_integration() { let node_pending_request1 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=5738616", host1, port), + uri: format!("http://{}:{}/activity/nodes?eraId=5738616&limit={}", host1, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -2654,7 +2654,7 @@ fn test_single_ocw_pallet_integration() { let node_pending_request2 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=5738616", host2, port), + uri: format!("http://{}:{}/activity/nodes?eraId=5738616&limit={}", host2, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -2662,7 +2662,7 @@ fn test_single_ocw_pallet_integration() { let node_pending_request3 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=5738616", host3, port), + uri: format!("http://{}:{}/activity/nodes?eraId=5738616&limit={}", host3, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -2670,7 +2670,7 @@ fn test_single_ocw_pallet_integration() { let node_pending_request4 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=5738616", host4, port), + uri: format!("http://{}:{}/activity/nodes?eraId=5738616&limit={}", host4, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -2678,7 +2678,7 @@ fn test_single_ocw_pallet_integration() { let node_pending_request5 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=5738616", host5, port), + uri: format!("http://{}:{}/activity/nodes?eraId=5738616&limit={}", host5, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -2686,7 +2686,7 @@ fn test_single_ocw_pallet_integration() { let node_pending_request6 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=5738616", host6, port), + uri: format!("http://{}:{}/activity/nodes?eraId=5738616&limit={}", host6, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -2694,7 +2694,7 @@ fn test_single_ocw_pallet_integration() { let node_pending_request7 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=5738616", host7, port), + uri: format!("http://{}:{}/activity/nodes?eraId=5738616&limit={}", host7, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -2702,7 +2702,7 @@ fn test_single_ocw_pallet_integration() { let node_pending_request8 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=5738616", host8, port), + uri: format!("http://{}:{}/activity/nodes?eraId=5738616&limit={}", host8, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -2710,7 +2710,7 @@ fn test_single_ocw_pallet_integration() { let node_pending_request9 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/nodes?eraId=5738616", host9, port), + uri: format!("http://{}:{}/activity/nodes?eraId=5738616&limit={}", host9, port, pallet::NODES_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"node_id": "0x48594f1fd4f05135914c42b03e63b61f6a3e4c537ccee3dbac555ef6df371b7e","stored_bytes": 675613289,"transferred_bytes": 1097091579,"number_of_puts": 889,"number_of_gets": 97},{"node_id": "0x9ef98ad9c3626ba725e78d76cfcfc4b4d07e84f0388465bc7eb992e3e117234a","stored_bytes": 0, "transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]"#.to_vec()), sent: true, ..Default::default() @@ -2718,7 +2718,7 @@ fn test_single_ocw_pallet_integration() { let bucket_pending_request1 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=5738616", host1, port), + uri: format!("http://{}:{}/activity/buckets?eraId=5738616&limit={}", host1, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"bucket_id": 90235,"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -2726,7 +2726,7 @@ fn test_single_ocw_pallet_integration() { let bucket_pending_request2 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=5738616", host2, port), + uri: format!("http://{}:{}/activity/buckets?eraId=5738616&limit={}", host2, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -2734,7 +2734,7 @@ fn test_single_ocw_pallet_integration() { let bucket_pending_request3 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=5738616", host3, port), + uri: format!("http://{}:{}/activity/buckets?eraId=5738616&limit={}", host3, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -2742,7 +2742,7 @@ fn test_single_ocw_pallet_integration() { let bucket_pending_request4 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=5738616", host4, port), + uri: format!("http://{}:{}/activity/buckets?eraId=5738616&limit={}", host4, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -2750,7 +2750,7 @@ fn test_single_ocw_pallet_integration() { let bucket_pending_request5 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=5738616", host5, port), + uri: format!("http://{}:{}/activity/buckets?eraId=5738616&limit={}", host5, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -2758,7 +2758,7 @@ fn test_single_ocw_pallet_integration() { let bucket_pending_request6 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=5738616", host6, port), + uri: format!("http://{}:{}/activity/buckets?eraId=5738616&limit={}", host6, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -2766,7 +2766,7 @@ fn test_single_ocw_pallet_integration() { let bucket_pending_request7 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=5738616", host7, port), + uri: format!("http://{}:{}/activity/buckets?eraId=5738616&limit={}", host7, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"bucket_id": 90235,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -2774,7 +2774,7 @@ fn test_single_ocw_pallet_integration() { let bucket_pending_request8 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=5738616", host8, port), + uri: format!("http://{}:{}/activity/buckets?eraId=5738616&limit={}", host8, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"bucket_id": 90235,"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), sent: true, ..Default::default() @@ -2782,7 +2782,7 @@ fn test_single_ocw_pallet_integration() { let bucket_pending_request9 = PendingRequest { method: "GET".to_string(), - uri: format!("http://{}:{}/activity/buckets?eraId=5738616", host9, port), + uri: format!("http://{}:{}/activity/buckets?eraId=5738616&limit={}", host9, port, pallet::BUCKETS_AGGREGATES_FETCH_BATCH_SIZE), response: Some(br#"[{"bucket_id": 90235,"stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1,"sub_aggregates": [{"NodeID": "0xbe26b2458fb0c9df4ec26ec5ba083051402b2a3b9d4a7fe6106fe9f8b5efde2c","stored_bytes": 0,"transferred_bytes": 38,"number_of_puts": 0,"number_of_gets": 1}]}]"#.to_vec()), sent: true, ..Default::default() From ed10b1aae007efa185583eb19b87e8de985d3539 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Wed, 6 Nov 2024 12:56:28 +0500 Subject: [PATCH 35/37] cargo clippy --fix --- Cargo.lock | 6 +++--- pallets/ddc-verification/src/aggregator_client.rs | 2 ++ pallets/ddc-verification/src/lib.rs | 15 ++++++--------- pallets/ddc-verification/src/signature.rs | 14 ++++++-------- pallets/ddc-verification/src/tests.rs | 4 ++-- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21f43eb85..eb221b766 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7110,11 +7110,11 @@ dependencies = [ "multimap", "once_cell", "petgraph", - "prettyplease 0.2.22", + "prettyplease 0.2.25", "prost 0.13.3", "prost-types 0.13.3", "regex", - "syn 2.0.79", + "syn 2.0.85", "tempfile", ] @@ -7141,7 +7141,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] diff --git a/pallets/ddc-verification/src/aggregator_client.rs b/pallets/ddc-verification/src/aggregator_client.rs index aa6e24b50..3acd0dc38 100644 --- a/pallets/ddc-verification/src/aggregator_client.rs +++ b/pallets/ddc-verification/src/aggregator_client.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use ddc_primitives::{BucketId, DdcEra}; use prost::Message; use sp_io::offchain::timestamp; diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index c1da40011..55372136f 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -37,7 +37,6 @@ use scale_info::prelude::{format, string::String}; use serde::{Deserialize, Serialize}; use sp_application_crypto::RuntimeAppPublic; use sp_runtime::{ - offchain as rt_offchain, offchain::{http, Duration, StorageKind}, traits::Hash, Percent, @@ -80,7 +79,7 @@ pub mod pallet { const STORAGE_VERSION: frame_support::traits::StorageVersion = frame_support::traits::StorageVersion::new(1); - const SUCCESS_CODE: u16 = 200; + const _SUCCESS_CODE: u16 = 200; const _BUF_SIZE: usize = 128; const RESPONSE_TIMEOUT: u64 = 20000; pub const BUCKETS_AGGREGATES_FETCH_BATCH_SIZE: usize = 100; @@ -1942,7 +1941,7 @@ pub mod pallet { log::info!("👎 Invalid challenge signatures at aggregate key: {:?}", aggregate_key,); } - return Ok(are_signatures_valid); + Ok(are_signatures_valid) } pub(crate) fn _get_hash_from_merkle_path( @@ -3263,7 +3262,7 @@ pub mod pallet { let response = pending.try_wait(timeout).map_err(|_| http::Error::DeadlineReached)??; - if response.code != SUCCESS_CODE { + if response.code != _SUCCESS_CODE { return Err(http::Error::Unknown); } @@ -3286,7 +3285,7 @@ pub mod pallet { 3, ); - let challenge_response = match aggregate_key { + match aggregate_key { AggregateKey::BucketSubAggregateKey(bucket_id, node_id) => client .challenge_bucket_sub_aggregate( era_id, @@ -3296,9 +3295,7 @@ pub mod pallet { ), AggregateKey::NodeAggregateKey(node_id) => client.challenge_node_aggregate(era_id, &node_id, merkle_tree_node_id), - }; - - challenge_response + } } /// Fetch traverse response. @@ -3390,7 +3387,7 @@ pub mod pallet { )?; let response_len = response.len(); - prev_token = response.last().map(|a| a.bucket_id.clone()); + prev_token = response.last().map(|a| a.bucket_id); buckets_aggregates.extend(response); diff --git a/pallets/ddc-verification/src/signature.rs b/pallets/ddc-verification/src/signature.rs index 0c258b8da..74dce7a55 100644 --- a/pallets/ddc-verification/src/signature.rs +++ b/pallets/ddc-verification/src/signature.rs @@ -85,13 +85,11 @@ impl Verify for proto::ChallengeResponse { fn verify(&self) -> bool { for proof in self.proofs.iter() { for leaf in proof.leaves.iter() { - if let Some(leaf) = &leaf.leaf_variant { - match leaf { - proto::challenge_response::proof::leaf::LeafVariant::Record(record) => - if !record.verify() { - return false; - }, - _ => {}, + if let Some(proto::challenge_response::proof::leaf::LeafVariant::Record(record)) = + &leaf.leaf_variant + { + if !record.verify() { + return false; } } } @@ -171,7 +169,7 @@ mod tests { let invalid_signature_msg_signature = invalid_signature_msg_signer.sign(invalid_signature_msg.encode_to_vec().as_slice()); let mut invalid_signature_msg_signature_vec = invalid_signature_msg_signature.0.to_vec(); - invalid_signature_msg_signature_vec[0] = invalid_signature_msg_signature_vec[0] + 1; + invalid_signature_msg_signature_vec[0] += 1; invalid_signature_msg.signature = Some(proto::Signature { algorithm: proto::signature::Algorithm::Ed25519 as i32, value: invalid_signature_msg_signature_vec, diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index d295b29ff..d6a154e10 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -3130,7 +3130,7 @@ fn aggregator_client_challenge_bucket_sub_aggregate_works() { ], }; let mut expected_response_serialized = Vec::new(); - let _ = expected_response.encode(&mut expected_response_serialized).unwrap(); + expected_response.encode(&mut expected_response_serialized).unwrap(); let expected = PendingRequest { method: "GET".into(), @@ -3185,7 +3185,7 @@ fn aggregator_client_challenge_node_aggregate_works() { ], }; let mut expected_response_serialized = Vec::new(); - let _ = expected_response.encode(&mut expected_response_serialized).unwrap(); + expected_response.encode(&mut expected_response_serialized).unwrap(); let expected = PendingRequest { method: "GET".into(), From 5d6c8fbcb178d9b32014bd5d98367a7c5328ea9d Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 7 Nov 2024 09:24:03 +0500 Subject: [PATCH 36/37] Remove `_` prefix from used functions --- pallets/ddc-verification/src/lib.rs | 10 +++++----- pallets/ddc-verification/src/tests.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/ddc-verification/src/lib.rs b/pallets/ddc-verification/src/lib.rs index 420065dfb..f1d3adcc1 100644 --- a/pallets/ddc-verification/src/lib.rs +++ b/pallets/ddc-verification/src/lib.rs @@ -1768,7 +1768,7 @@ pub mod pallet { // let is_passed = Self::_challenge_aggregate(_cluster_id, _era_id, // &defective_aggregate)?; if should_challenge { - is_passed = Self::_challenge_aggregate_proto( + is_passed = Self::challenge_aggregate_proto( _cluster_id, _era_id, &defective_aggregate, @@ -1798,7 +1798,7 @@ pub mod pallet { ); let aggregate_key = aggregate.get_key(); - let merkle_node_ids = Self::_find_random_merkle_node_ids( + let merkle_node_ids = Self::find_random_merkle_node_ids( number_of_identifiers.into(), aggregate.get_number_of_leaves(), aggregate_key.clone(), @@ -1888,7 +1888,7 @@ pub mod pallet { Ok(is_matched) } - pub(crate) fn _challenge_aggregate_proto( + pub(crate) fn challenge_aggregate_proto( cluster_id: &ClusterId, era_id: DdcEra, aggregate: &A, @@ -1900,7 +1900,7 @@ pub mod pallet { ); let aggregate_key = aggregate.get_key(); - let merkle_node_ids = Self::_find_random_merkle_node_ids( + let merkle_node_ids = Self::find_random_merkle_node_ids( number_of_identifiers.into(), aggregate.get_number_of_leaves(), aggregate_key.clone(), @@ -2006,7 +2006,7 @@ pub mod pallet { Ok(resulting_hash) } - pub(crate) fn _find_random_merkle_node_ids( + pub(crate) fn find_random_merkle_node_ids( number_of_identifiers: usize, number_of_leaves: u64, aggregate_key: AggregateKey, diff --git a/pallets/ddc-verification/src/tests.rs b/pallets/ddc-verification/src/tests.rs index df1a99e4b..add71a6f4 100644 --- a/pallets/ddc-verification/src/tests.rs +++ b/pallets/ddc-verification/src/tests.rs @@ -2978,7 +2978,7 @@ fn test_find_random_merkle_node_ids() { let number_of_leaves = deffective_bucket_sub_aggregate.get_number_of_leaves(); - let ids = DdcVerification::_find_random_merkle_node_ids( + let ids = DdcVerification::find_random_merkle_node_ids( 3, number_of_leaves, deffective_bucket_sub_aggregate.get_key(), From 0005656f96b2ebcdaa8f45623c22e352fe8986f6 Mon Sep 17 00:00:00 2001 From: "Alisher A. Khassanov" Date: Thu, 7 Nov 2024 09:24:25 +0500 Subject: [PATCH 37/37] Bump `spec_version` --- runtime/cere-dev/src/lib.rs | 2 +- runtime/cere/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/cere-dev/src/lib.rs b/runtime/cere-dev/src/lib.rs index 646a7c2df..b57a7cfd3 100644 --- a/runtime/cere-dev/src/lib.rs +++ b/runtime/cere-dev/src/lib.rs @@ -153,7 +153,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: 61001, + spec_version: 61002, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 21, diff --git a/runtime/cere/src/lib.rs b/runtime/cere/src/lib.rs index 587b8c7d1..6521464b0 100644 --- a/runtime/cere/src/lib.rs +++ b/runtime/cere/src/lib.rs @@ -147,7 +147,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: 61001, + spec_version: 61002, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 21,