From 15bc2e75dcf6bc0dde9d5109b7ff41f0d753afc5 Mon Sep 17 00:00:00 2001 From: Asem Abdelhady Date: Thu, 11 Jan 2024 12:39:09 +0300 Subject: [PATCH 1/6] [feature] #3453: Implement Store set, remove, get operations in Client CLI (#4163) * [add] store set, remove and get operations feature in client cli Signed-off-by: Asem-Abdelhady --- client_cli/src/main.rs | 172 +++++++++++++++++++++++++++++++++++++++- data_model/src/asset.rs | 12 ++- 2 files changed, 179 insertions(+), 5 deletions(-) diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index 7f22f2dbb89..ab2f2b5ce5e 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -20,7 +20,7 @@ use iroha_client::{ data_model::prelude::*, }; use iroha_config_base::proxy::{LoadFromDisk, LoadFromEnv, Override}; -use iroha_primitives::addr::SocketAddr; +use iroha_primitives::addr::{Ipv4Addr, Ipv6Addr, SocketAddr}; /// Re-usable clap `--metadata ` (`-m`) argument. /// Should be combined with `#[command(flatten)]` attr. @@ -55,6 +55,24 @@ impl MetadataArgs { Ok(value.unwrap_or_default()) } } +/// Wrapper around Value to accept possible values and fallback to json +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ValueArg(Value); + +impl FromStr for ValueArg { + type Err = Error; + + fn from_str(s: &str) -> Result { + s.parse::() + .map(Value::Bool) + .or_else(|_| s.parse::().map(Value::Ipv4Addr)) + .or_else(|_| s.parse::().map(Value::Ipv6Addr)) + .or_else(|_| s.parse::().map(Value::Numeric)) + .or_else(|_| s.parse::().map(Value::PublicKey)) + .or_else(|_| serde_json::from_str::(s).map_err(|e| e.into())) + .map(ValueArg) + } +} /// Iroha CLI Client provides an ability to interact with Iroha Peers Web API without direct network usage. #[derive(clap::Parser, Debug)] @@ -663,14 +681,17 @@ mod account { } mod asset { - use iroha_client::client::{self, asset, Client}; + use iroha_client::{ + client::{self, asset, Client}, + data_model::{asset::AssetDefinition, name::Name}, + }; use super::*; /// Subcommand for dealing with asset #[derive(clap::Subcommand, Debug)] pub enum Args { - /// Register subcommand of asset + /// Command for Registering a new asset Register(Register), /// Command for minting asset in existing Iroha account Mint(Mint), @@ -683,13 +704,19 @@ mod asset { /// List assets #[clap(subcommand)] List(List), + /// Set a key-value entry in a Store asset + SetKeyValue(SetKeyValue), + /// Remove a key-value entry from a Store asset + RemoveKeyValue(RemoveKeyValue), + /// Get a value from a Store asset + GetKeyValue(GetKeyValue), } impl RunArgs for Args { fn run(self, context: &mut dyn RunContext) -> Result<()> { match_all!( (self, context), - { Args::Register, Args::Mint, Args::Burn, Args::Transfer, Args::Get, Args::List } + { Args::Register, Args::Mint, Args::Burn, Args::Transfer, Args::Get, Args::List, Args::SetKeyValue, Args::RemoveKeyValue, Args::GetKeyValue} ) } } @@ -888,6 +915,80 @@ mod asset { Ok(()) } } + + #[derive(clap::Args, Debug)] + pub struct SetKeyValue { + /// AssetId for the Store asset (in form of `asset##account@domain_name') + #[clap(long)] + pub asset_id: AssetId, + /// The key for the store value + #[clap(long)] + pub key: Name, + /// The value to be associated with the specified key. + /// The following types are supported: + /// Numbers: with a suffix, e.g. 42_u32 or 1000_u128 + /// Booleans: false/true + /// IPv4/IPv6: e.g. 127.0.0.1, ::1 + /// Iroha Public Key Multihash: e.g. ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0 + /// JSON: e.g. {"Vec":[{"String":"a"},{"String":"b"}]} + #[clap(long)] + pub value: ValueArg, + } + + impl RunArgs for SetKeyValue { + fn run(self, context: &mut dyn RunContext) -> Result<()> { + let Self { + asset_id, + key, + value: ValueArg(value), + } = self; + + let set = iroha_client::data_model::isi::SetKeyValue::asset(asset_id, key, value); + submit([set], UnlimitedMetadata::default(), context)?; + Ok(()) + } + } + #[derive(clap::Args, Debug)] + pub struct RemoveKeyValue { + /// AssetId for the Store asset (in form of `asset##account@domain_name') + #[clap(long)] + pub asset_id: AssetId, + /// The key for the store value + #[clap(long)] + pub key: Name, + } + + impl RunArgs for RemoveKeyValue { + fn run(self, context: &mut dyn RunContext) -> Result<()> { + let Self { asset_id, key } = self; + let remove = iroha_client::data_model::isi::RemoveKeyValue::asset(asset_id, key); + submit([remove], UnlimitedMetadata::default(), context)?; + Ok(()) + } + } + + #[derive(clap::Args, Debug)] + pub struct GetKeyValue { + /// AssetId for the Store asset (in form of `asset##account@domain_name') + #[clap(long)] + pub asset_id: AssetId, + /// The key for the store value + #[clap(long)] + pub key: Name, + } + + impl RunArgs for GetKeyValue { + fn run(self, context: &mut dyn RunContext) -> Result<()> { + let Self { asset_id, key } = self; + let client = Client::new(context.configuration())?; + let find_key_value = FindAssetKeyValueByIdAndKey::new(asset_id, key); + let asset = client + .request(find_key_value) + .wrap_err("Failed to get key-value")?; + context.print_data(&asset)?; + Ok(()) + } + } } mod peer { @@ -1023,3 +1124,66 @@ mod json { } } } +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use iroha_client::data_model::Value; + + use super::*; + + #[test] + fn parse_value_arg_cases() { + macro_rules! case { + ($input:expr, $expected:expr) => { + let ValueArg(actual) = + ValueArg::from_str($input).expect("should not fail with valid input"); + assert_eq!(actual, $expected); + }; + } + + // IPv4 address + case!( + "192.168.0.1", + Value::Ipv4Addr(Ipv4Addr::new([192, 168, 0, 1])) + ); + + // IPv6 address + case!( + "::1", + Value::Ipv6Addr(Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 1])) + ); + + // Boolean values + case!("true", Value::Bool(true)); + case!("false", Value::Bool(false)); + + // Numeric values + case!("123_u32", Value::Numeric(NumericValue::U32(123))); + case!("123_u64", Value::Numeric(NumericValue::U64(123))); + case!("123_u128", Value::Numeric(NumericValue::U128(123))); + + let expected_fixed = NumericValue::Fixed(123.0.try_into().unwrap()); + case!("123.0_fx", Value::Numeric(expected_fixed)); + + // Public Key + let public_key_str = + "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"; + case!( + public_key_str, + Value::PublicKey(PublicKey::from_str(public_key_str).unwrap()) + ); + + // JSON Value + let json_str = r#"{"Vec":[{"String":"a"},{"String":"b"}]}"#; + let expected_json: Value = serde_json::from_str(json_str).unwrap(); + case!(json_str, expected_json); + } + + #[test] + fn error_parse_invalid_value() { + let invalid_str = "not_a_valid_value"; + let _invalid_value = ValueArg::from_str(invalid_str) + .expect_err("Should fail invalid type from string but passed"); + } +} diff --git a/data_model/src/asset.rs b/data_model/src/asset.rs index 25c19006e56..ba693a8c3f8 100644 --- a/data_model/src/asset.rs +++ b/data_model/src/asset.rs @@ -494,7 +494,7 @@ impl FromStr for AssetId { })?, account_id.domain_id.clone()) } else { - return Err(ParseError { reason: "The `definition_id` part of the `asset_id` failed to parse. Ensure that you have it in the right format: `name#domain_of_asset#account_name@domain_of_account`." }); + return Err(ParseError { reason: "The `definition_id` part of the `asset_id` failed to parse. Ensure that you have it in the right format: `name#domain_of_asset#account_name@domain_of_account` or `name##account_name@domain_of_account` in case of same domain" }); } }; Ok(Self { @@ -572,3 +572,13 @@ pub mod prelude { NewAssetDefinition, }; } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_error_for_asset_id() { + let _invalid_asset_id = AssetId::from_str("store#alice@wonderland") + .expect_err("store#alice@wonderland should not be a valid AssetId"); + } +} From a3fad9b02b64cbc67e99f0fbe19a4802dda841c8 Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Fri, 12 Jan 2024 10:49:34 +0300 Subject: [PATCH 2/6] [fix] #4192: Fix musl archiver name in Docker (#4193) Signed-off-by: Daniil Polyakov --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 7c6d9eced09..c7926a3c3eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,7 @@ RUN rustup component add rust-src RUN wget -c http://musl.cc/x86_64-linux-musl-native.tgz -O - | tar -xz RUN ln -s /x86_64-linux-musl-native/bin/x86_64-linux-musl-g++ /x86_64-linux-musl-native/bin/musl-g++ RUN ln -s /x86_64-linux-musl-native/bin/x86_64-linux-musl-gcc-ar /x86_64-linux-musl-native/bin/musl-ar +RUN ln -s /x86_64-linux-musl-native/bin/x86_64-linux-musl-gcc-ar /x86_64-linux-musl-native/bin/x86_64-linux-musl-ar RUN ln -s /x86_64-linux-musl-native/bin/x86_64-linux-musl-gcc-ranlib /x86_64-linux-musl-native/bin/musl-ranlib ENV PATH="$PATH:/x86_64-linux-musl-native/bin" ENV RUSTFLAGS="-C link-arg=-static" From 05228d1156b0a34f7375beed2f231eb30249eca9 Mon Sep 17 00:00:00 2001 From: Stukalov-A-M Date: Thu, 28 Dec 2023 13:14:22 +0100 Subject: [PATCH 3/6] [fix] #4177: Fixed (get|set)_config 401 HTTP. Added Client's headers to the requests Signed-off-by: Stukalov-A-M --- client/src/client.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/client.rs b/client/src/client.rs index 3a4c7615397..66a7ada3373 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -1091,6 +1091,7 @@ impl Client { .join(crate::config::torii::CONFIGURATION) .expect("Valid URI"), ) + .headers(&self.headers) .header(http::header::CONTENT_TYPE, APPLICATION_JSON) .build()? .send()?; @@ -1116,6 +1117,7 @@ impl Client { .join(crate::config::torii::CONFIGURATION) .expect("Valid URI"); let resp = DefaultRequestBuilder::new(HttpMethod::POST, url) + .headers(&self.headers) .header(http::header::CONTENT_TYPE, APPLICATION_JSON) .body(body) .build()? From 1320cb23430b8bf5d0b8c75a06fb964fb3e4f71d Mon Sep 17 00:00:00 2001 From: BAStos525 <66615487+BAStos525@users.noreply.github.com> Date: Sat, 13 Jan 2024 15:43:44 +0300 Subject: [PATCH 4/6] [ci] #4184: Bump to 2024-01-12 toolchain & Revert to custom Coveralls settings (#4196) [ci] #4184: Bump to 2024-01-12 toolchain & revert to custom Coveralls settings Signed-off-by: BAStos525 --- .github/workflows/iroha2-ci-image.yml | 2 +- .github/workflows/iroha2-dev-nightly.yml | 2 +- .github/workflows/iroha2-dev-pr-static.yml | 6 +++--- .github/workflows/iroha2-dev-pr-ui.yml | 2 +- .github/workflows/iroha2-dev-pr-wasm.yaml | 4 ++-- .github/workflows/iroha2-dev-pr.yml | 17 ++++++++--------- .github/workflows/iroha2-dev.yml | 6 +++--- .github/workflows/iroha2-release-pr.yml | 8 ++++---- .github/workflows/iroha2-release.yml | 4 ++-- Dockerfile | 6 +++--- Dockerfile.build | 4 ++-- Dockerfile.build.glibc | 4 ++-- Dockerfile.glibc | 4 ++-- wasm_builder/src/lib.rs | 2 +- 14 files changed, 35 insertions(+), 36 deletions(-) diff --git a/.github/workflows/iroha2-ci-image.yml b/.github/workflows/iroha2-ci-image.yml index c7721b6e6d2..d6b03258647 100644 --- a/.github/workflows/iroha2-ci-image.yml +++ b/.github/workflows/iroha2-ci-image.yml @@ -15,7 +15,7 @@ jobs: uses: docker/build-push-action@v5 with: push: true - tags: hyperledger/iroha2-ci:nightly-2023-06-25 + tags: hyperledger/iroha2-ci:nightly-2024-01-12 labels: commit=${{ github.sha }} file: Dockerfile.build # This context specification is required diff --git a/.github/workflows/iroha2-dev-nightly.yml b/.github/workflows/iroha2-dev-nightly.yml index 94f98f711c8..f22bf0133f5 100644 --- a/.github/workflows/iroha2-dev-nightly.yml +++ b/.github/workflows/iroha2-dev-nightly.yml @@ -6,7 +6,7 @@ jobs: dockerhub: runs-on: ubuntu-latest container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - uses: docker/login-action@v3 diff --git a/.github/workflows/iroha2-dev-pr-static.yml b/.github/workflows/iroha2-dev-pr-static.yml index c89ef6976d8..06effe3e687 100644 --- a/.github/workflows/iroha2-dev-pr-static.yml +++ b/.github/workflows/iroha2-dev-pr-static.yml @@ -14,13 +14,13 @@ concurrency: cancel-in-progress: true env: - RUSTUP_TOOLCHAIN: nightly-2023-06-25 + RUSTUP_TOOLCHAIN: nightly-2024-01-12 jobs: smart_contracts_analysis: runs-on: ubuntu-latest container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 @@ -38,7 +38,7 @@ jobs: workspace_analysis: runs-on: ubuntu-latest container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/iroha2-dev-pr-ui.yml b/.github/workflows/iroha2-dev-pr-ui.yml index 603b1331c5a..ef5521a23e4 100644 --- a/.github/workflows/iroha2-dev-pr-ui.yml +++ b/.github/workflows/iroha2-dev-pr-ui.yml @@ -20,7 +20,7 @@ jobs: test: runs-on: ubuntu-latest container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 timeout-minutes: 60 strategy: matrix: diff --git a/.github/workflows/iroha2-dev-pr-wasm.yaml b/.github/workflows/iroha2-dev-pr-wasm.yaml index a5019907ccd..aad3bfb085c 100644 --- a/.github/workflows/iroha2-dev-pr-wasm.yaml +++ b/.github/workflows/iroha2-dev-pr-wasm.yaml @@ -19,13 +19,13 @@ concurrency: cancel-in-progress: true env: - RUSTUP_TOOLCHAIN: nightly-2023-06-25 + RUSTUP_TOOLCHAIN: nightly-2024-01-12 jobs: tests: runs-on: ubuntu-latest #[self-hosted, Linux] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/iroha2-dev-pr.yml b/.github/workflows/iroha2-dev-pr.yml index 4ddc04e98e8..900b5397679 100644 --- a/.github/workflows/iroha2-dev-pr.yml +++ b/.github/workflows/iroha2-dev-pr.yml @@ -21,7 +21,7 @@ jobs: consistency: runs-on: [self-hosted, Linux, iroha2ci] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 @@ -44,7 +44,7 @@ jobs: with_coverage: runs-on: [self-hosted, Linux, iroha2ci] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v3 # TODO Remove this step #2165 @@ -66,15 +66,14 @@ jobs: uses: coverallsapp/github-action@v2 with: file: lcov.info - git-branch: ${{ github.base_ref }} + compare-ref: ${{ github.base_ref }} + compare-sha: ${{ github.event.pull_request.base.sha}} github-token: ${{ secrets.GITHUB_TOKEN }} - allow-empty: true - fail_ci_if_error: true integration: runs-on: [self-hosted, Linux, iroha2ci] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 timeout-minutes: 30 steps: - uses: actions/checkout@v3 @@ -87,7 +86,7 @@ jobs: unstable: runs-on: [self-hosted, Linux, iroha2ci] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 timeout-minutes: 60 steps: - uses: actions/checkout@v4 @@ -101,7 +100,7 @@ jobs: if: startsWith(github.head_ref, 'iroha2-pr-deploy/') runs-on: [self-hosted, Linux, iroha2-dev-push] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - name: Login to Soramitsu Harbor @@ -131,7 +130,7 @@ jobs: client-cli-tests: runs-on: [self-hosted, Linux, iroha2ci] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 timeout-minutes: 60 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/iroha2-dev.yml b/.github/workflows/iroha2-dev.yml index 995581a61af..311d376632a 100644 --- a/.github/workflows/iroha2-dev.yml +++ b/.github/workflows/iroha2-dev.yml @@ -11,7 +11,7 @@ jobs: registry: runs-on: [self-hosted, Linux, iroha2-dev-push] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - uses: docker/login-action@v3 @@ -49,7 +49,7 @@ jobs: archive_binaries_and_schema: runs-on: ubuntu-latest container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 @@ -85,7 +85,7 @@ jobs: if: false runs-on: ubuntu-latest container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/iroha2-release-pr.yml b/.github/workflows/iroha2-release-pr.yml index 6a0e0d9c87e..cd1a94b8623 100644 --- a/.github/workflows/iroha2-release-pr.yml +++ b/.github/workflows/iroha2-release-pr.yml @@ -18,7 +18,7 @@ jobs: client-cli-tests: runs-on: [self-hosted, Linux, iroha2ci] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 timeout-minutes: 60 steps: - uses: actions/checkout@v4 @@ -60,7 +60,7 @@ jobs: bench: runs-on: ubuntu-latest #[self-hosted, Linux] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - name: Maximize build space run: | @@ -76,7 +76,7 @@ jobs: java-api: runs-on: ubuntu-latest #[self-hosted, Linux] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - name: Maximize build space run: | @@ -130,7 +130,7 @@ jobs: long: runs-on: ubuntu-latest #[self-hosted, Linux] container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - name: Maximize build space uses: jlumbroso/free-disk-space@v1.3.1 diff --git a/.github/workflows/iroha2-release.yml b/.github/workflows/iroha2-release.yml index 8395c54af48..f704b75af4e 100644 --- a/.github/workflows/iroha2-release.yml +++ b/.github/workflows/iroha2-release.yml @@ -11,7 +11,7 @@ jobs: registry: runs-on: ubuntu-latest container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx @@ -59,7 +59,7 @@ jobs: configs: runs-on: ubuntu-latest container: - image: hyperledger/iroha2-ci:nightly-2023-06-25 + image: hyperledger/iroha2-ci:nightly-2024-01-12 permissions: contents: write steps: diff --git a/Dockerfile b/Dockerfile index c7926a3c3eb..5b78c51c074 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,8 @@ RUN pacman -Syu --noconfirm --disable-download-timeout # Set up Rust toolchain RUN pacman -S rustup mold musl rust-musl wget --noconfirm --disable-download-timeout -RUN rustup toolchain install nightly-2023-06-25 -RUN rustup default nightly-2023-06-25 +RUN rustup toolchain install nightly-2024-01-12 +RUN rustup default nightly-2024-01-12 RUN rustup target add x86_64-unknown-linux-musl wasm32-unknown-unknown RUN rustup component add rust-src @@ -33,7 +33,7 @@ RUN cargo build --target x86_64-unknown-linux-musl --profile deploy # final image -FROM alpine:3.18 +FROM alpine:3.19 ARG STORAGE=/storage ARG TARGET_DIR=/iroha/target/x86_64-unknown-linux-musl/deploy diff --git a/Dockerfile.build b/Dockerfile.build index 4169fe29bd5..571944a3a3d 100644 --- a/Dockerfile.build +++ b/Dockerfile.build @@ -18,8 +18,8 @@ WORKDIR /client_cli/pytests COPY /client_cli/pytests/pyproject.toml /client_cli/pytests/poetry.lock $WORKDIR RUN poetry install -RUN rustup toolchain install nightly-2023-06-25-x86_64-unknown-linux-gnu -RUN rustup default nightly-2023-06-25-x86_64-unknown-linux-gnu +RUN rustup toolchain install nightly-2024-01-12-x86_64-unknown-linux-gnu +RUN rustup default nightly-2024-01-12-x86_64-unknown-linux-gnu RUN rustup component add llvm-tools-preview clippy RUN rustup component add rust-src RUN rustup component add rustfmt diff --git a/Dockerfile.build.glibc b/Dockerfile.build.glibc index 799b9bfa69d..66a90d24543 100644 --- a/Dockerfile.build.glibc +++ b/Dockerfile.build.glibc @@ -18,8 +18,8 @@ WORKDIR /client_cli/pytests COPY /client_cli/pytests/pyproject.toml /client_cli/pytests/poetry.lock $WORKDIR RUN poetry install -RUN rustup toolchain install nightly-2023-06-25-x86_64-unknown-linux-gnu -RUN rustup default nightly-2023-06-25-x86_64-unknown-linux-gnu +RUN rustup toolchain install nightly-2024-01-12_64-unknown-linux-gnu +RUN rustup default nightly-2024-01-12-x86_64-unknown-linux-gnu RUN rustup component add llvm-tools-preview clippy RUN rustup component add rust-src RUN rustup component add rustfmt diff --git a/Dockerfile.glibc b/Dockerfile.glibc index 352dc3ef3ea..25eb2e0068a 100644 --- a/Dockerfile.glibc +++ b/Dockerfile.glibc @@ -11,8 +11,8 @@ RUN pacman -Syu --noconfirm --disable-download-timeout # Set up Rust toolchain RUN pacman -S rustup mold wget --noconfirm --disable-download-timeout -RUN rustup toolchain install nightly-2023-06-25 -RUN rustup default nightly-2023-06-25 +RUN rustup toolchain install nightly-2024-01-12 +RUN rustup default nightly-2024-01-12 RUN rustup target add wasm32-unknown-unknown RUN rustup component add rust-src diff --git a/wasm_builder/src/lib.rs b/wasm_builder/src/lib.rs index e5857900636..d5e9ce861ef 100644 --- a/wasm_builder/src/lib.rs +++ b/wasm_builder/src/lib.rs @@ -14,7 +14,7 @@ use eyre::{bail, eyre, Context as _, Result}; use path_absolutize::Absolutize; /// Current toolchain used to build smartcontracts -const TOOLCHAIN: &str = "+nightly-2023-06-25"; +const TOOLCHAIN: &str = "+nightly-2024-01-12"; /// WASM Builder for smartcontracts (e.g. triggers and executors). /// From 5d4082ffe3c20d1a871b26f76eb50569be53659b Mon Sep 17 00:00:00 2001 From: Vladislav Date: Mon, 15 Jan 2024 09:23:27 +0300 Subject: [PATCH 5/6] [refactor]: update iroha_telemetry_derive to use syn2 (#4168) Signed-off-by: VAmuzing --- Cargo.lock | 5 +- telemetry/derive/Cargo.toml | 5 +- telemetry/derive/src/lib.rs | 125 +++++++++++------- telemetry/derive/tests/ui_fail/not_execute.rs | 1 + .../derive/tests/ui_fail/not_execute.stderr | 4 +- .../derive/tests/ui_fail/not_return_result.rs | 7 +- .../tests/ui_fail/not_return_result.stderr | 8 +- .../derive/tests/ui_fail/return_nothing.rs | 3 +- .../tests/ui_fail/return_nothing.stderr | 4 +- 9 files changed, 100 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca93d68e91c..5aecd17db3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3176,10 +3176,11 @@ name = "iroha_telemetry_derive" version = "2.0.0-pre-rc.20" dependencies = [ "iroha_core", - "proc-macro-error", + "iroha_macro_utils", + "manyhow", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.41", "trybuild", ] diff --git a/telemetry/derive/Cargo.toml b/telemetry/derive/Cargo.toml index b2c53970a87..de03e2bb488 100644 --- a/telemetry/derive/Cargo.toml +++ b/telemetry/derive/Cargo.toml @@ -19,10 +19,11 @@ is-it-maintained-open-issues = { repository = "https://github.com/hyperledger/ir maintenance = { status = "actively-developed" } [dependencies] -syn = { workspace = true, features = ["default", "full"] } +syn2 = { workspace = true } quote = { workspace = true } proc-macro2 = { workspace = true } -proc-macro-error = { workspace = true } +manyhow = { workspace = true } +iroha_macro_utils = { workspace = true } [dev-dependencies] iroha_core = { workspace = true } diff --git a/telemetry/derive/src/lib.rs b/telemetry/derive/src/lib.rs index 779b3cdba0b..eff6d509c62 100644 --- a/telemetry/derive/src/lib.rs +++ b/telemetry/derive/src/lib.rs @@ -1,15 +1,11 @@ //! Attribute-like macro for instrumenting `isi` for `prometheus` //! metrics. See [`macro@metrics`] for more details. -use proc_macro::TokenStream; -#[cfg(feature = "metric-instrumentation")] -use proc_macro2::TokenStream as TokenStream2; -use proc_macro_error::{abort, proc_macro_error}; +use iroha_macro_utils::Emitter; +use manyhow::{emit, manyhow, Result}; +use proc_macro2::TokenStream; use quote::{quote, ToTokens}; -use syn::{ - parse::Parse, parse_macro_input, punctuated::Punctuated, token::Comma, FnArg, ItemFn, LitStr, - Path, Type, -}; +use syn2::{parse::Parse, punctuated::Punctuated, token::Comma, FnArg, LitStr, Path, Type}; // TODO: export these as soon as proc-macro crates are able to export // anything other than proc-macros. @@ -43,19 +39,19 @@ fn type_has_metrics_field(ty: &Type) -> bool { /// /// # Errors /// If no argument is of type `WorldStateView`. -fn arg_metrics(input: &Punctuated) -> Result> { +fn arg_metrics(input: &Punctuated) -> Result> { input .iter() .find(|arg| match arg { FnArg::Typed(typ) => match &*typ.ty { - syn::Type::Reference(typ) => type_has_metrics_field(&typ.elem), + syn2::Type::Reference(typ) => type_has_metrics_field(&typ.elem), _ => false, }, _ => false, }) .and_then(|arg| match arg { FnArg::Typed(typ) => match *typ.pat.clone() { - syn::Pat::Ident(ident) => Some(ident.ident), + syn2::Pat::Ident(ident) => Some(ident.ident), _ => None, }, _ => None, @@ -67,7 +63,7 @@ fn arg_metrics(input: &Punctuated) -> Result); // `HashSet` — idiomatic; slow impl Parse for MetricSpecs { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn2::parse::ParseStream) -> syn2::Result { let vars = Punctuated::::parse_terminated(input)?; Ok(Self(vars.into_iter().collect())) } @@ -80,14 +76,14 @@ struct MetricSpec { } impl Parse for MetricSpec { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let _timing = ::parse(input).is_ok(); - let metric_name_lit = syn::Lit::parse(input)?; + fn parse(input: syn2::parse::ParseStream) -> syn2::Result { + let _timing = ::parse(input).is_ok(); + let metric_name_lit = syn2::Lit::parse(input)?; let metric_name = match metric_name_lit { - syn::Lit::Str(lit_str) => { + syn2::Lit::Str(lit_str) => { if lit_str.value().contains(' ') { - return Err(syn::Error::new( + return Err(syn2::Error::new( proc_macro2::Span::call_site(), "Spaces are not allowed. Use underscores '_'", )); @@ -95,7 +91,7 @@ impl Parse for MetricSpec { lit_str } _ => { - return Err(syn::Error::new( + return Err(syn2::Error::new( proc_macro2::Span::call_site(), "Must be a string literal. Format `[+]\"name_of_metric\"`.", )) @@ -145,54 +141,91 @@ impl ToTokens for MetricSpec { /// Ok(()) /// } /// ``` -#[proc_macro_error] +#[manyhow] #[proc_macro_attribute] pub fn metrics(attr: TokenStream, item: TokenStream) -> TokenStream { - let ItemFn { + let mut emitter = Emitter::new(); + + let Some(func): Option = emitter.handle(syn2::parse2(item)) else { + return emitter.finish_token_stream(); + }; + + // This is a good sanity check. Possibly redundant. + if func.sig.ident != "execute" { + emit!( + emitter, + func.sig.ident, + "Function should be an `impl execute`" + ); + } + + if func.sig.inputs.is_empty() { + emit!( + emitter, + func.sig, + "Function must have at least one argument of type `WorldStateView`." + ); + return emitter.finish_token_stream(); + } + + let Some(metric_specs): Option = emitter.handle(syn2::parse2(attr)) else { + return emitter.finish_token_stream(); + }; + + let result = impl_metrics(&mut emitter, &metric_specs, &func); + + emitter.finish_token_stream_with(result) +} + +fn impl_metrics(emitter: &mut Emitter, _specs: &MetricSpecs, func: &syn2::ItemFn) -> TokenStream { + let syn2::ItemFn { attrs, vis, sig, block, - }: ItemFn = parse_macro_input!(item as ItemFn); + } = func; - // This is a good sanity check. Possibly redundant. - if sig.ident != "execute" { - abort!(sig.ident, "Function should be an `impl execute`"); - } match sig.output.clone() { - syn::ReturnType::Default => abort!( + syn2::ReturnType::Default => emit!( + emitter, sig.output, "`Fn` must return `Result`. Returns nothing instead. " ), #[allow(clippy::string_to_string)] - syn::ReturnType::Type(_, typ) => match *typ { + syn2::ReturnType::Type(_, typ) => match *typ { Type::Path(pth) => { let Path { segments, .. } = pth.path; let type_name = &segments.last().expect("non-empty path").ident; if *type_name != "Result" { - abort!( + emit!( + emitter, type_name, - format!("Should return `Result`. Found {type_name}") + "Should return `Result`. Found {}", + type_name ); } } - _ => abort!( + _ => emit!( + emitter, typ, "Should return `Result`. Non-path result type specification found" ), }, } - if sig.inputs.is_empty() { - abort!( - sig, - "Function must have at least one argument of type `WorldStateView`." - ); - } - let _specs = parse_macro_input!(attr as MetricSpecs); + // Again this may seem fragile, but if we move the metrics from // the `WorldStateView`, we'd need to refactor many things anyway - let _metric_arg_ident = arg_metrics(&sig.inputs) - .unwrap_or_else(|args| abort!(args, "At least one argument must be a `WorldStateView`.")); + let _metric_arg_ident = match arg_metrics(&sig.inputs) { + Ok(ident) => ident, + Err(args) => { + emit!( + emitter, + args, + "At least one argument must be a `WorldStateView`." + ); + return quote!(); + } + }; #[cfg(feature = "metric-instrumentation")] let res = { @@ -210,8 +243,7 @@ pub fn metrics(attr: TokenStream, item: TokenStream) -> TokenStream { #successes }; res - }) - .into(); + }); }; #[cfg(not(feature = "metric-instrumentation"))] @@ -219,8 +251,7 @@ pub fn metrics(attr: TokenStream, item: TokenStream) -> TokenStream { #(#attrs)* #vis #sig { #block } - ) - .into(); + ); res } @@ -228,7 +259,7 @@ pub fn metrics(attr: TokenStream, item: TokenStream) -> TokenStream { fn write_metrics( metric_arg_ident: proc_macro2::Ident, specs: MetricSpecs, -) -> (TokenStream2, TokenStream2, TokenStream2) { +) -> (TokenStream, TokenStream, TokenStream) { let inc_metric = |spec: &MetricSpec, kind: &str| { quote!( #metric_arg_ident @@ -246,17 +277,17 @@ fn write_metrics( .observe((end_time.as_millis() - start_time.as_millis()) as f64); ) }; - let totals: TokenStream2 = specs + let totals: TokenStream = specs .0 .iter() .map(|spec| inc_metric(spec, "total")) .collect(); - let successes: TokenStream2 = specs + let successes: TokenStream = specs .0 .iter() .map(|spec| inc_metric(spec, "success")) .collect(); - let times: TokenStream2 = specs + let times: TokenStream = specs .0 .iter() .filter(|spec| spec.timing) diff --git a/telemetry/derive/tests/ui_fail/not_execute.rs b/telemetry/derive/tests/ui_fail/not_execute.rs index 7a63c17d08f..ddd566862bd 100644 --- a/telemetry/derive/tests/ui_fail/not_execute.rs +++ b/telemetry/derive/tests/ui_fail/not_execute.rs @@ -1,4 +1,5 @@ use iroha_telemetry_derive::metrics; +use iroha_core::prelude::WorldStateView; #[metrics(+"test_query", "another_test_query_without_timing")] fn exequte(wsv: &WorldStateView) -> Result<(), ()> { diff --git a/telemetry/derive/tests/ui_fail/not_execute.stderr b/telemetry/derive/tests/ui_fail/not_execute.stderr index 7f4ab728dae..4146a4ab62b 100644 --- a/telemetry/derive/tests/ui_fail/not_execute.stderr +++ b/telemetry/derive/tests/ui_fail/not_execute.stderr @@ -1,5 +1,5 @@ error: Function should be an `impl execute` - --> tests/ui_fail/not_execute.rs:4:4 + --> tests/ui_fail/not_execute.rs:5:4 | -4 | fn exequte(wsv: &WorldStateView) -> Result<(), ()> { +5 | fn exequte(wsv: &WorldStateView) -> Result<(), ()> { | ^^^^^^^ diff --git a/telemetry/derive/tests/ui_fail/not_return_result.rs b/telemetry/derive/tests/ui_fail/not_return_result.rs index ca779d8e5ec..b5620e99f63 100644 --- a/telemetry/derive/tests/ui_fail/not_return_result.rs +++ b/telemetry/derive/tests/ui_fail/not_return_result.rs @@ -1,8 +1,11 @@ use iroha_telemetry_derive::metrics; +use iroha_core::prelude::WorldStateView; + +type MyNotResult = Option; #[metrics(+"test_query", "another_test_query_without_timing")] -fn execute(_wsv: &WorldStateView) -> iroha_core::RESULT { - Ok(()) +fn execute(_wsv: &WorldStateView) -> MyNotResult { + None } fn main() { diff --git a/telemetry/derive/tests/ui_fail/not_return_result.stderr b/telemetry/derive/tests/ui_fail/not_return_result.stderr index 6652f72014d..09daedb0d51 100644 --- a/telemetry/derive/tests/ui_fail/not_return_result.stderr +++ b/telemetry/derive/tests/ui_fail/not_return_result.stderr @@ -1,5 +1,5 @@ -error: Should return `Result`. Found RESULT - --> tests/ui_fail/not_return_result.rs:4:50 +error: Should return `Result`. Found MyNotResult + --> tests/ui_fail/not_return_result.rs:7:38 | -4 | fn execute(_wsv: &WorldStateView) -> iroha_core::RESULT { - | ^^^^^^ +7 | fn execute(_wsv: &WorldStateView) -> MyNotResult { + | ^^^^^^^^^^^ diff --git a/telemetry/derive/tests/ui_fail/return_nothing.rs b/telemetry/derive/tests/ui_fail/return_nothing.rs index 419325ac0ba..6058f160b19 100644 --- a/telemetry/derive/tests/ui_fail/return_nothing.rs +++ b/telemetry/derive/tests/ui_fail/return_nothing.rs @@ -1,8 +1,9 @@ use iroha_telemetry_derive::metrics; +use iroha_core::prelude::WorldStateView; #[metrics(+"test_query", "another_test_query_without_timing")] fn execute(wsv: &WorldStateView) { - Ok(()) + Ok::<(), ()>(()); } fn main() { diff --git a/telemetry/derive/tests/ui_fail/return_nothing.stderr b/telemetry/derive/tests/ui_fail/return_nothing.stderr index 93385e20c54..f92d23ee205 100644 --- a/telemetry/derive/tests/ui_fail/return_nothing.stderr +++ b/telemetry/derive/tests/ui_fail/return_nothing.stderr @@ -1,7 +1,7 @@ error: `Fn` must return `Result`. Returns nothing instead. - --> tests/ui_fail/return_nothing.rs:3:1 + --> tests/ui_fail/return_nothing.rs:4:1 | -3 | #[metrics(+"test_query", "another_test_query_without_timing")] +4 | #[metrics(+"test_query", "another_test_query_without_timing")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the attribute macro `metrics` (in Nightly builds, run with -Z macro-backtrace for more info) From 66072b4f79526f892ffef4dea0fdd65a2d4b1168 Mon Sep 17 00:00:00 2001 From: Vladislav Date: Mon, 15 Jan 2024 10:55:33 +0300 Subject: [PATCH 6/6] [feature] #3974: Add subcommands into iroha_client_cli to edit domain metadata (#4175) Signed-off-by: Vladimir Pesterev <8786922+pesterev@users.noreply.github.com> Signed-off-by: VAmuzing Co-authored-by: Vladimir Pesterev <8786922+pesterev@users.noreply.github.com> --- client_cli/src/main.rs | 75 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index ab2f2b5ce5e..bf818f1ead0 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -69,7 +69,7 @@ impl FromStr for ValueArg { .or_else(|_| s.parse::().map(Value::Ipv6Addr)) .or_else(|_| s.parse::().map(Value::Numeric)) .or_else(|_| s.parse::().map(Value::PublicKey)) - .or_else(|_| serde_json::from_str::(s).map_err(|e| e.into())) + .or_else(|_| serde_json::from_str::(s).map_err(std::convert::Into::into)) .map(ValueArg) } } @@ -393,11 +393,14 @@ mod domain { List(List), /// Transfer domain Transfer(Transfer), + /// Edit domain metadata + #[clap(subcommand)] + Metadata(metadata::Args), } impl RunArgs for Args { fn run(self, context: &mut dyn RunContext) -> Result<()> { - match_all!((self, context), { Args::Register, Args::List, Args::Transfer }) + match_all!((self, context), { Args::Register, Args::List, Args::Transfer, Args::Metadata, }) } } @@ -476,6 +479,74 @@ mod domain { .wrap_err("Failed to transfer domain") } } + + mod metadata { + use iroha_client::data_model::domain::DomainId; + + use super::*; + + /// Edit domain subcommands + #[derive(Debug, Clone, clap::Subcommand)] + pub enum Args { + /// Set domain metadata + Set(Set), + /// Remove domain metadata + Remove(Remove), + } + + impl RunArgs for Args { + fn run(self, context: &mut dyn RunContext) -> Result<()> { + match_all!((self, context), { Args::Set, Args::Remove, }) + } + } + + /// Set metadata into domain + #[derive(Debug, Clone, clap::Args)] + pub struct Set { + /// A domain id from which metadata is to be removed + #[arg(short, long)] + id: DomainId, + /// A key of metadata + #[arg(short, long)] + key: Name, + /// A value of metadata + #[arg(short, long)] + value: ValueArg, + } + + impl RunArgs for Set { + fn run(self, context: &mut dyn RunContext) -> Result<()> { + let Self { + id, + key, + value: ValueArg(value), + } = self; + let set_key_value = SetKeyValue::domain(id, key, value); + submit([set_key_value], UnlimitedMetadata::new(), context) + .wrap_err("Failed to submit Set instruction") + } + } + + /// Remove metadata into domain by key + #[derive(Debug, Clone, clap::Args)] + pub struct Remove { + /// A domain id from which metadata is to be removed + #[arg(short, long)] + id: DomainId, + /// A key of metadata + #[arg(short, long)] + key: Name, + } + + impl RunArgs for Remove { + fn run(self, context: &mut dyn RunContext) -> Result<()> { + let Self { id, key } = self; + let remove_key_value = RemoveKeyValue::domain(id, key); + submit([remove_key_value], UnlimitedMetadata::new(), context) + .wrap_err("Failed to submit Remove instruction") + } + } + } } mod account {