From bffb20997214d42c1237342d5336783b32d9cadc Mon Sep 17 00:00:00 2001 From: Milosz Muszynski Date: Tue, 14 Nov 2023 14:37:45 +0100 Subject: [PATCH] Getting CRS from host via HTTP with CRS hash in header (#39) --- integration-tests/tests/blockchain/get_crs.rs | 33 ++++++++++++ integration-tests/tests/blockchain/mod.rs | 1 + .../tests/citadel/int_test_user.rs | 26 ++++----- moat-cli-user/src/command.rs | 11 ++-- moat-cli/src/command.rs | 11 ++-- moat-core/Cargo.toml | 2 +- .../src/blockchain_queries/crs_getter.rs | 53 +++++++++++++++++++ moat-core/src/blockchain_queries/mod.rs | 2 + moat-core/src/error.rs | 18 +++++++ moat-core/src/lib.rs | 2 +- 10 files changed, 135 insertions(+), 24 deletions(-) create mode 100644 integration-tests/tests/blockchain/get_crs.rs create mode 100644 moat-core/src/blockchain_queries/crs_getter.rs diff --git a/integration-tests/tests/blockchain/get_crs.rs b/integration-tests/tests/blockchain/get_crs.rs new file mode 100644 index 0000000..f94e1e3 --- /dev/null +++ b/integration-tests/tests/blockchain/get_crs.rs @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +use dusk_wallet::RuskHttpClient; +use moat_core::{CrsGetter, Error}; +use toml_base_config::BaseConfig; +use tracing::trace; +use wallet_accessor::BlockchainAccessConfig; + +const MIN_CRS_SIZE: usize = 10 * 1024 * 1024; + +#[tokio::test(flavor = "multi_thread")] +#[cfg_attr(not(feature = "int_tests"), ignore)] +async fn get_crs() -> Result<(), Error> { + let config_path = + concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml"); + + let cfg = BlockchainAccessConfig::load_path(config_path)?; + + let client = RuskHttpClient::new(cfg.rusk_address); + + let crs = CrsGetter::get_crs(&client).await?; + + assert!(crs.len() >= MIN_CRS_SIZE); + + trace!("crs={}...", hex::encode(&crs[0..64])); + trace!("crs length={}", crs.len()); + + Ok(()) +} diff --git a/integration-tests/tests/blockchain/mod.rs b/integration-tests/tests/blockchain/mod.rs index 2b6fa07..9fc5224 100644 --- a/integration-tests/tests/blockchain/mod.rs +++ b/integration-tests/tests/blockchain/mod.rs @@ -4,5 +4,6 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +mod get_crs; mod retrieve_txs; mod stake_add_owner; diff --git a/integration-tests/tests/citadel/int_test_user.rs b/integration-tests/tests/citadel/int_test_user.rs index 70a6535..1232d61 100644 --- a/integration-tests/tests/citadel/int_test_user.rs +++ b/integration-tests/tests/citadel/int_test_user.rs @@ -26,7 +26,7 @@ use dusk_plonk::prelude::*; use dusk_wallet::{RuskHttpClient, WalletPath}; use license_provider::{LicenseIssuer, ReferenceLP}; use moat_core::{ - BcInquirer, CitadelInquirer, Error, JsonLoader, LicenseCircuit, + BcInquirer, CitadelInquirer, CrsGetter, Error, JsonLoader, LicenseCircuit, LicenseSessionId, LicenseUser, PayloadRetriever, RequestCreator, RequestJson, RequestSender, TxAwaiter, }; @@ -46,7 +46,6 @@ const GAS_LIMIT: u64 = 5_000_000_000; const GAS_PRICE: u64 = 1; static LABEL: &[u8] = b"dusk-network"; -const CAPACITY: usize = 17; // capacity required for the setup /// Calls license contract's issue license method. /// Awaits for confirmation of the contract-calling transaction. @@ -122,9 +121,19 @@ async fn user_round_trip() -> Result<(), Error> { // PUB_PARAMS initialization code let mut rng = StdRng::seed_from_u64(0xbeef); - info!("performing setup"); - let pp = PublicParameters::setup(1 << CAPACITY, &mut rng) - .expect("Initializing public parameters should succeed"); + let blockchain_config_path = + concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml"); + + let blockchain_config = + BlockchainAccessConfig::load_path(blockchain_config_path)?; + + let client = RuskHttpClient::new(blockchain_config.rusk_address.clone()); + + info!("obtaining CRS"); + let pp_vec = CrsGetter::get_crs(&client).await?; + let pp = + // SAFETY: CRS vector is checked by the hash check when it is received from the node + unsafe { PublicParameters::from_slice_unchecked(pp_vec.as_slice()) }; info!("compiling circuit"); let (prover, verifier) = Compiler::compile::(&pp, LABEL) @@ -132,23 +141,16 @@ async fn user_round_trip() -> Result<(), Error> { let request_path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/request/request.json"); - let blockchain_config_path = - concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/config.toml"); let lp_config_path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/config/lp2.json"); let reference_lp = ReferenceLP::create(&lp_config_path)?; - let blockchain_config = - BlockchainAccessConfig::load_path(blockchain_config_path)?; - let wallet_path = WalletPath::from( PathBuf::from(WALLET_PATH).as_path().join("wallet.dat"), ); - let client = RuskHttpClient::new(blockchain_config.rusk_address.clone()); - // create request let request_json: RequestJson = RequestJson::from_file(request_path)?; let ssk_user_bytes = hex::decode(request_json.user_ssk.clone())?; diff --git a/moat-cli-user/src/command.rs b/moat-cli-user/src/command.rs index d45e8fa..d69240c 100644 --- a/moat-cli-user/src/command.rs +++ b/moat-cli-user/src/command.rs @@ -16,7 +16,7 @@ use dusk_pki::{PublicSpendKey, SecretSpendKey}; use dusk_plonk::prelude::*; use dusk_wallet::{RuskHttpClient, WalletPath}; use moat_core::{ - BcInquirer, CitadelInquirer, Error, LicenseCircuit, LicenseUser, + BcInquirer, CitadelInquirer, CrsGetter, Error, LicenseCircuit, LicenseUser, RequestCreator, RequestJson, RequestScanner, RequestSender, TxAwaiter, }; use rand::rngs::StdRng; @@ -41,7 +41,6 @@ pub(crate) enum Command { } static LABEL: &[u8] = b"dusk-network"; -const CAPACITY: usize = 17; // capacity required for the setup impl Command { #[allow(clippy::too_many_arguments)] @@ -340,9 +339,11 @@ impl Command { let setup_holder = match sh_opt { Some(sh) => sh, _ => { - println!("performing setup"); - let pp = PublicParameters::setup(1 << CAPACITY, &mut rng) - .expect("Initializing public parameters should succeed"); + println!("obtaining setup"); + let pp_vec = CrsGetter::get_crs(&client).await?; + let pp = + // SAFETY: CRS vector is checked by the hash check when it is received from the node + unsafe { PublicParameters::from_slice_unchecked(pp_vec.as_slice()) }; println!("compiling circuit"); let (prover, verifier) = Compiler::compile::(&pp, LABEL) diff --git a/moat-cli/src/command.rs b/moat-cli/src/command.rs index 1bda2b4..905143e 100644 --- a/moat-cli/src/command.rs +++ b/moat-cli/src/command.rs @@ -18,7 +18,7 @@ use dusk_plonk::prelude::*; use dusk_wallet::{RuskHttpClient, WalletPath}; use license_provider::{LicenseIssuer, ReferenceLP}; use moat_core::{ - BcInquirer, CitadelInquirer, Error, JsonLoader, LicenseCircuit, + BcInquirer, CitadelInquirer, CrsGetter, Error, JsonLoader, LicenseCircuit, LicenseSessionId, LicenseUser, RequestCreator, RequestJson, RequestScanner, RequestSender, TxAwaiter, }; @@ -57,7 +57,6 @@ pub(crate) enum Command { } static LABEL: &[u8] = b"dusk-network"; -const CAPACITY: usize = 17; // capacity required for the setup impl Command { #[allow(clippy::too_many_arguments)] @@ -504,9 +503,11 @@ impl Command { let setup_holder = match sh_opt { Some(sh) => sh, _ => { - println!("performing setup"); - let pp = PublicParameters::setup(1 << CAPACITY, &mut rng) - .expect("Initializing public parameters should succeed"); + println!("obtaining setup"); + let pp_vec = CrsGetter::get_crs(&client).await?; + let pp = + // SAFETY: CRS vector is checked by the hash check when it is received from the node + unsafe { PublicParameters::from_slice_unchecked(pp_vec.as_slice()) }; println!("compiling circuit"); let (prover, verifier) = Compiler::compile::(&pp, LABEL) diff --git a/moat-core/Cargo.toml b/moat-core/Cargo.toml index 1c6c6ab..aec3a18 100644 --- a/moat-core/Cargo.toml +++ b/moat-core/Cargo.toml @@ -37,7 +37,7 @@ code-hasher={ path = "../macros/code-hasher" } tracing = "0.1" bytes = "1.4" reqwest = "0.11" +sha2 = "0.10" [dev-dependencies] tokio = { version = "1.15", features = ["rt-multi-thread", "time", "fs", "macros"] } -sha2 = "0.10" diff --git a/moat-core/src/blockchain_queries/crs_getter.rs b/moat-core/src/blockchain_queries/crs_getter.rs new file mode 100644 index 0000000..ca57eeb --- /dev/null +++ b/moat-core/src/blockchain_queries/crs_getter.rs @@ -0,0 +1,53 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +use crate::Error; +use dusk_wallet::{RuskHttpClient, RuskRequest}; +use reqwest::Response; +use sha2::{Digest, Sha256}; + +pub struct CrsGetter; + +type CRSHash = [u8; 32]; +const CRS_HASH_HEADER: &str = "crs-hash"; + +impl CrsGetter { + pub async fn get_crs(client: &RuskHttpClient) -> Result, Error> { + let crs_request = RuskRequest::new("crs", vec![]); + let result = client.call_raw(2, "rusk", &crs_request, false).await; + match result { + Ok(response) => { + let received_hash = Self::hash_from_header(&response)?; + let crs = response.bytes().await?; + let this_hash = Self::hash_of_bytes(crs.as_ref()); + if received_hash != this_hash { + return Err(Error::CRS(Box::from("corrupted CRS"))); + } + Ok(crs.to_vec()) + } + Err(err) => Err(err.into()), + } + } + + fn hash_from_header(response: &Response) -> Result { + let crs_hash = response + .headers() + .get(CRS_HASH_HEADER) + .ok_or(Error::CRS(Box::from("missing CRS hash header")))?; + let crs_hash = crs_hash.to_str().map_err(|_| { + Error::CRS(Box::from("failed CRS hash header string conversion")) + })?; + let mut h = CRSHash::default(); + hex::decode_to_slice(crs_hash, h.as_mut_slice())?; + Ok(h) + } + + fn hash_of_bytes>(bytes: T) -> CRSHash { + let mut hasher = Sha256::new(); + hasher.update(bytes.as_ref()); + hasher.finalize().into() + } +} diff --git a/moat-core/src/blockchain_queries/mod.rs b/moat-core/src/blockchain_queries/mod.rs index a6ff26f..b7d93c9 100644 --- a/moat-core/src/blockchain_queries/mod.rs +++ b/moat-core/src/blockchain_queries/mod.rs @@ -5,9 +5,11 @@ // Copyright (c) DUSK NETWORK. All rights reserved. mod bc_inquirer; +mod crs_getter; mod tx_awaiter; mod tx_inquirer; pub use bc_inquirer::BcInquirer; +pub use crs_getter::CrsGetter; pub use tx_awaiter::TxAwaiter; pub use tx_inquirer::TxInquirer; diff --git a/moat-core/src/error.rs b/moat-core/src/error.rs index 2c03921..f600477 100644 --- a/moat-core/src/error.rs +++ b/moat-core/src/error.rs @@ -39,6 +39,12 @@ pub enum Error { Transaction(Box), #[error("Stream item not present or stream error: {0:?}")] Stream(Box), + #[error("A PLONK error occurred: {0:?}")] + Plonk(Arc), + #[error("A CRS error occurred: {0:?}")] + CRS(Box), + #[error(transparent)] + HttpClient(Arc), } impl From for Error { @@ -82,3 +88,15 @@ impl From for Error { Error::WebSocket(Arc::from(e)) } } + +impl From for Error { + fn from(e: dusk_plonk::error::Error) -> Self { + Error::Plonk(Arc::from(e)) + } +} + +impl From for Error { + fn from(e: reqwest::Error) -> Self { + Error::HttpClient(Arc::from(e)) + } +} diff --git a/moat-core/src/lib.rs b/moat-core/src/lib.rs index a22abe8..73f279b 100644 --- a/moat-core/src/lib.rs +++ b/moat-core/src/lib.rs @@ -27,7 +27,7 @@ pub use bc_types::*; pub use blockchain_payloads::{ PayloadExtractor, PayloadRetriever, PayloadSender, }; -pub use blockchain_queries::{BcInquirer, TxAwaiter, TxInquirer}; +pub use blockchain_queries::{BcInquirer, CrsGetter, TxAwaiter, TxInquirer}; pub use circuit::*; pub use citadel_licenses::LicenseUser; pub use citadel_queries::{