From 10b8c5184e2ad4940971f81ed61eaf4ac06f1504 Mon Sep 17 00:00:00 2001 From: DaughterOfMars Date: Fri, 26 Jul 2024 04:22:25 -0400 Subject: [PATCH] fix(CI): Fix cargo deny (#1106) * chore(dependencies): Update jsonrpsee and related dependencies * replace todos * update dependencies * dprint * feature * update baselines and license * Switch from ethers to alloy * merge * Fix mock provider and a bug in the ws impl * update arrow/parquet * fix tokio dependency msim compatability * install crypto provider and other misc * update pcg crate usage and use vendored openssl * test fixes * more fixes and clippy * baselines and more clippy * disable mysticeti simtests too * fix test and fmt * roll back gcp-bigquery-client version * fix bug in mock provider and cleanup * remove outdated doc comment * replace B256 with TxHash * simplify * comment * remove merge problem * fix graphql-rpc job * Add prost to git allow * clippy * update dependencies * revert commented line * fix borked merge * move rustfmt skip * Reviews * Add comment explaining TlsAcceptor * remove axum macros feature usage * review and update dependencies * remove yaml_rust RUSTSEC ignore * reviews * cleanup helper functions * move import --------- Co-authored-by: Thibault Martinez --- crates/iota-analytics-indexer/Cargo.toml | 9 +- crates/iota-analytics-indexer/src/lib.rs | 4 +- crates/iota-analytics-indexer/src/tables.rs | 2 +- crates/iota-aws-orchestrator/Cargo.toml | 1 + .../iota-aws-orchestrator/src/client/aws.rs | 26 +- crates/iota-benchmark/Cargo.toml | 1 - crates/iota-benchmark/src/options.rs | 2 +- .../src/workloads/adversarial.rs | 5 +- crates/iota-core/src/authority.rs | 2 +- crates/iota-core/src/generate_format.rs | 8 +- crates/iota-core/tests/staged/iota.yaml | 851 +++++++++--------- crates/iota-cost/Cargo.toml | 1 - .../tests/empirical_transaction_cost.rs | 2 +- .../src/workers/archival.rs | 4 +- .../iota-data-ingestion/src/workers/blob.rs | 4 +- .../src/workers/kv_store.rs | 2 +- crates/iota-faucet/src/main.rs | 5 +- .../src/stardust/native_token/package_data.rs | 8 +- crates/iota-graphql-rpc/Cargo.toml | 3 + .../schema/current_progress_schema.graphql | 2 + .../src/extensions/query_limits_checker.rs | 6 +- crates/iota-graphql-rpc/src/server/builder.rs | 42 +- crates/iota-graphql-rpc/src/server/version.rs | 24 +- .../iota-graphql-rpc/src/types/checkpoint.rs | 183 ++-- crates/iota-graphql-rpc/src/types/epoch.rs | 90 +- .../iota-graphql-rpc/src/types/move_value.rs | 2 +- .../snapshot_tests__schema_sdl_export.snap | 2 + crates/iota-indexer/src/apis/extended_api.rs | 12 +- .../iota-indexer/src/apis/governance_api.rs | 4 +- crates/iota-indexer/src/apis/indexer_api.rs | 118 +-- crates/iota-indexer/src/apis/read_api.rs | 29 +- crates/iota-indexer/src/apis/write_api.rs | 11 +- crates/iota-indexer/src/errors.rs | 11 +- crates/iota-indexer/src/indexer_reader.rs | 2 +- crates/iota-indexer/src/lib.rs | 3 +- crates/iota-indexer/src/metrics.rs | 4 +- crates/iota-json-rpc-api/src/indexer.rs | 4 +- crates/iota-json-rpc-api/src/lib.rs | 20 + .../tests/routing_tests.rs | 76 +- .../iota-json-rpc-types/src/iota_extended.rs | 4 +- crates/iota-json-rpc-types/src/iota_move.rs | 24 +- crates/iota-json-rpc-types/src/iota_object.rs | 209 ++--- .../src/iota_transaction.rs | 2 +- crates/iota-json-rpc/Cargo.toml | 1 + crates/iota-json-rpc/src/axum_router.rs | 167 ++-- crates/iota-json-rpc/src/coin_api.rs | 331 +++---- crates/iota-json-rpc/src/error.rs | 142 ++- crates/iota-json-rpc/src/governance_api.rs | 79 +- crates/iota-json-rpc/src/indexer_api.rs | 633 +++++++------ crates/iota-json-rpc/src/lib.rs | 41 +- crates/iota-json-rpc/src/logger.rs | 210 ++++- crates/iota-json-rpc/src/metrics.rs | 9 +- crates/iota-json-rpc/src/move_utils.rs | 202 +++-- crates/iota-json-rpc/src/read_api.rs | 581 ++++++------ .../src/transaction_builder_api.rs | 86 +- .../src/transaction_execution_api.rs | 58 +- crates/iota-light-client/src/main.rs | 3 +- crates/iota-metric-checker/Cargo.toml | 2 +- crates/iota-metric-checker/src/lib.rs | 8 +- crates/iota-move-build/Cargo.toml | 1 - crates/iota-move-build/src/lib.rs | 8 +- crates/iota-node/src/admin.rs | 6 +- crates/iota-node/src/lib.rs | 42 +- crates/iota-package-resolver/src/lib.rs | 4 +- crates/iota-replay/src/types.rs | 2 +- crates/iota-rest-api/Cargo.toml | 1 + crates/iota-rest-api/src/accept.rs | 11 +- crates/iota-rest-api/src/lib.rs | 4 +- crates/iota-rosetta/Cargo.toml | 4 +- crates/iota-rosetta/src/errors.rs | 3 +- crates/iota-rosetta/src/lib.rs | 20 +- crates/iota-rosetta/src/main.rs | 6 +- crates/iota-rosetta/src/types.rs | 2 +- crates/iota-rosetta/tests/rosetta_client.rs | 12 +- crates/iota-rpc-loadgen/Cargo.toml | 1 - crates/iota-rpc-loadgen/src/payload/mod.rs | 2 +- crates/iota-sdk/src/error.rs | 4 +- crates/iota-sdk/src/json_rpc_error.rs | 17 +- crates/iota-sdk/src/lib.rs | 9 +- crates/iota-single-node-benchmark/Cargo.toml | 1 - .../iota-single-node-benchmark/src/command.rs | 2 +- .../iota-source-validation-service/Cargo.toml | 6 +- .../iota-source-validation-service/src/lib.rs | 37 +- .../src/main.rs | 4 +- .../tests/tests.rs | 7 +- crates/iota-storage/Cargo.toml | 4 +- .../iota-storage/src/http_key_value_store.rs | 18 +- crates/iota-storage/src/indexes.rs | 2 +- .../iota-storage/src/object_store/http/mod.rs | 4 +- crates/iota-storage/src/object_store/mod.rs | 4 +- crates/iota-test-validator/src/main.rs | 5 +- crates/iota-tool/Cargo.toml | 1 - crates/iota-tool/src/db_tool/db_dump.rs | 2 +- crates/iota-tool/src/lib.rs | 6 +- crates/iota-types/Cargo.toml | 2 - crates/iota-types/src/crypto.rs | 4 +- crates/iota-types/src/error.rs | 2 +- crates/iota-types/src/transaction.rs | 7 +- .../tests/tests.rs | 2 +- crates/iota/Cargo.toml | 4 +- crates/iota/src/keytool.rs | 60 +- crates/iota/tests/ptb_files_tests.rs | 4 +- crates/mysten-metrics/src/lib.rs | 14 +- crates/mysten-network/Cargo.toml | 3 +- crates/mysten-network/src/client.rs | 13 +- crates/mysten-network/src/metrics.rs | 6 +- crates/mysten-network/src/server.rs | 6 +- crates/telemetry-subscribers/Cargo.toml | 16 +- .../src/file_exporter.rs | 6 +- crates/telemetry-subscribers/src/lib.rs | 29 +- iota-execution/cut/src/plan.rs | 42 +- narwhal/network/src/admin.rs | 2 +- narwhal/node/Cargo.toml | 2 + narwhal/node/src/generate_format.rs | 9 +- narwhal/node/src/metrics.rs | 4 +- narwhal/node/tests/formats.rs | 5 +- narwhal/node/tests/staged/narwhal.yaml | 152 ++-- 117 files changed, 2756 insertions(+), 2277 deletions(-) diff --git a/crates/iota-analytics-indexer/Cargo.toml b/crates/iota-analytics-indexer/Cargo.toml index bc092323bbe..fada4a1a947 100644 --- a/crates/iota-analytics-indexer/Cargo.toml +++ b/crates/iota-analytics-indexer/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] anyhow.workspace = true -arrow = { version = "50.0.0" } +arrow.workspace = true arrow-array.workspace = true async-trait.workspace = true axum.workspace = true @@ -20,7 +20,7 @@ clap.workspace = true csv.workspace = true eyre.workspace = true fastcrypto = { workspace = true, features = ["copy_key"] } -gcp-bigquery-client = "0.18.0" +gcp-bigquery-client = "0.20.0" iota-analytics-indexer-derive.workspace = true iota-config.workspace = true iota-indexer.workspace = true @@ -41,10 +41,9 @@ rocksdb.workspace = true serde.workspace = true serde_json.workspace = true simulacrum.workspace = true -snowflake-api = { version = "0.6.0" } +snowflake-api = { version = "0.9.0" } strum.workspace = true -strum_macros.workspace = true -tap = { version = "1.0.1", features = [] } +tap = "1.0.1" telemetry-subscribers.workspace = true tempfile.workspace = true thiserror.workspace = true diff --git a/crates/iota-analytics-indexer/src/lib.rs b/crates/iota-analytics-indexer/src/lib.rs index 1a94ca327ac..9ef6acd05b7 100644 --- a/crates/iota-analytics-indexer/src/lib.rs +++ b/crates/iota-analytics-indexer/src/lib.rs @@ -22,7 +22,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive}; use object_store::path::Path; use serde::{Deserialize, Serialize}; use snowflake_api::{QueryResult, SnowflakeApi}; -use strum_macros::EnumIter; +use strum::EnumIter; use tracing::info; use crate::{ @@ -270,7 +270,7 @@ impl MaxCheckpointReader for NoOpCheckpointReader { Eq, PartialEq, Parser, - strum_macros::Display, + strum::Display, ValueEnum, Serialize, Deserialize, diff --git a/crates/iota-analytics-indexer/src/tables.rs b/crates/iota-analytics-indexer/src/tables.rs index 82437ce1218..5a5cd566d32 100644 --- a/crates/iota-analytics-indexer/src/tables.rs +++ b/crates/iota-analytics-indexer/src/tables.rs @@ -6,7 +6,7 @@ use iota_analytics_indexer_derive::SerializeParquet; use iota_types::dynamic_field::DynamicFieldType; use serde::Serialize; -use strum_macros::Display; +use strum::Display; use crate::{ParquetSchema, ParquetValue}; diff --git a/crates/iota-aws-orchestrator/Cargo.toml b/crates/iota-aws-orchestrator/Cargo.toml index 4e77830326f..03522a32ef4 100644 --- a/crates/iota-aws-orchestrator/Cargo.toml +++ b/crates/iota-aws-orchestrator/Cargo.toml @@ -9,6 +9,7 @@ publish = false [dependencies] async-trait.workspace = true aws-config.workspace = true +aws-runtime.workspace = true aws-sdk-ec2.workspace = true aws-smithy-http.workspace = true aws-smithy-runtime-api.workspace = true diff --git a/crates/iota-aws-orchestrator/src/client/aws.rs b/crates/iota-aws-orchestrator/src/client/aws.rs index c88521291c5..427fe646c50 100644 --- a/crates/iota-aws-orchestrator/src/client/aws.rs +++ b/crates/iota-aws-orchestrator/src/client/aws.rs @@ -7,7 +7,7 @@ use std::{ fmt::{Debug, Display}, }; -use aws_config::profile::profile_file::{ProfileFileKind, ProfileFiles}; +use aws_runtime::env_config::file::{EnvConfigFileKind, EnvConfigFiles}; use aws_sdk_ec2::{ config::Region, primitives::Blob, @@ -16,7 +16,7 @@ use aws_sdk_ec2::{ TagSpecification, VolumeType, }, }; -use aws_smithy_http::result::SdkError; +use aws_smithy_runtime_api::client::result::SdkError; use serde::Serialize; use super::{Instance, ServerProviderClient}; @@ -56,9 +56,9 @@ impl AwsClient { /// Make a new AWS client. pub async fn new(settings: Settings) -> Self { - let profile_files = ProfileFiles::builder() - .with_file(ProfileFileKind::Credentials, &settings.token_file) - .with_contents(ProfileFileKind::Config, "[default]\noutput=json") + let profile_files = EnvConfigFiles::builder() + .with_file(EnvConfigFileKind::Credentials, &settings.token_file) + .with_contents(EnvConfigFileKind::Config, "[default]\noutput=json") .build(); let mut clients = HashMap::new(); @@ -146,7 +146,7 @@ impl AwsClient { // Parse the response to select the first returned image id. response .images() - .and_then(|images| images.first()) + .first() .ok_or_else(|| CloudProviderError::RequestError("Cannot find image id".into()))? .image_id .clone() @@ -223,7 +223,7 @@ impl AwsClient { let response = request.send().await?; // Return true if the response contains references to NVMe drives. - if let Some(info) = response.instance_types().and_then(|x| x.first()) { + if let Some(info) = response.instance_types().first() { if let Some(info) = info.instance_storage_info() { if info.nvme_support() == Some(&EphemeralNvmeSupport::Required) { return Ok(true); @@ -247,13 +247,9 @@ impl ServerProviderClient for AwsClient { let mut instances = Vec::new(); for (region, client) in &self.clients { let request = client.describe_instances().filters(filter.clone()); - if let Some(reservations) = request.send().await?.reservations() { - for reservation in reservations { - if let Some(aws_instances) = reservation.instances() { - for instance in aws_instances { - instances.push(self.make_instance(region.clone(), instance)); - } - } + for reservation in request.send().await?.reservations() { + for instance in reservation.instances() { + instances.push(self.make_instance(region.clone(), instance)); } } } @@ -355,7 +351,7 @@ impl ServerProviderClient for AwsClient { let response = request.send().await?; let instance = &response .instances() - .and_then(|x| x.first()) + .first() .expect("AWS instances list should contain instances"); Ok(self.make_instance(region, instance)) diff --git a/crates/iota-benchmark/Cargo.toml b/crates/iota-benchmark/Cargo.toml index 64b2b7a5547..3ae86dcd3b0 100644 --- a/crates/iota-benchmark/Cargo.toml +++ b/crates/iota-benchmark/Cargo.toml @@ -36,7 +36,6 @@ roaring.workspace = true serde.workspace = true serde_json.workspace = true strum.workspace = true -strum_macros.workspace = true telemetry-subscribers.workspace = true tokio = { workspace = true, features = ["full"] } tokio-util.workspace = true diff --git a/crates/iota-benchmark/src/options.rs b/crates/iota-benchmark/src/options.rs index 757de7feed0..01c181bcfdb 100644 --- a/crates/iota-benchmark/src/options.rs +++ b/crates/iota-benchmark/src/options.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use clap::*; -use strum_macros::EnumString; +use strum::EnumString; use crate::drivers::Interval; diff --git a/crates/iota-benchmark/src/workloads/adversarial.rs b/crates/iota-benchmark/src/workloads/adversarial.rs index bbcf6bfec82..27dd8af1cc9 100644 --- a/crates/iota-benchmark/src/workloads/adversarial.rs +++ b/crates/iota-benchmark/src/workloads/adversarial.rs @@ -23,8 +23,7 @@ use rand::{ Rng, }; use regex::Regex; -use strum::{EnumCount, IntoEnumIterator}; -use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; +use strum::{EnumCount, EnumIter, IntoEnumIterator}; use tracing::debug; use super::{ @@ -45,7 +44,7 @@ const NUM_VECTORS: u64 = 1_000; // TODO: Need to fix Large* workloads, which are currently failing due to // InsufficientGas -#[derive(Debug, EnumCountMacro, EnumIter, Clone)] +#[derive(Debug, EnumCount, EnumIter, Clone)] pub enum AdversarialPayloadType { Random = 0, LargeObjects, diff --git a/crates/iota-core/src/authority.rs b/crates/iota-core/src/authority.rs index facc68f4c82..f915f5b7fbd 100644 --- a/crates/iota-core/src/authority.rs +++ b/crates/iota-core/src/authority.rs @@ -4293,7 +4293,7 @@ impl AuthorityState { desired_upgrades.sort(); desired_upgrades .into_iter() - .group_by(|(packages, _authority)| packages.clone()) + .chunk_by(|(packages, _authority)| packages.clone()) .into_iter() .find_map(|(packages, group)| { // should have been filtered out earlier. diff --git a/crates/iota-core/src/generate_format.rs b/crates/iota-core/src/generate_format.rs index 8b3e9dceb0e..58c9176c546 100644 --- a/crates/iota-core/src/generate_format.rs +++ b/crates/iota-core/src/generate_format.rs @@ -36,13 +36,15 @@ use iota_types::{ }, utils::DEFAULT_ADDRESS_SEED, }; +use move_bytecode_utils::layout::YamlRegistry; use move_core_types::language_storage::{StructTag, TypeTag}; use pretty_assertions::assert_str_eq; use rand::{rngs::StdRng, SeedableRng}; -use serde_reflection::{Registry, Result, Samples, Tracer, TracerConfig}; +use serde_reflection::{Result, Samples, Tracer, TracerConfig}; use shared_crypto::intent::{Intent, IntentMessage, PersonalMessage}; use typed_store::TypedStoreError; -fn get_registry() -> Result { + +fn get_registry() -> Result { let config = TracerConfig::default() .record_samples_for_structs(true) .record_samples_for_newtype_structs(true); @@ -176,7 +178,7 @@ fn get_registry() -> Result { tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; - tracer.registry() + Ok(YamlRegistry(tracer.registry()?)) } #[derive(Debug, Parser, Clone, Copy, ValueEnum)] diff --git a/crates/iota-core/tests/staged/iota.yaml b/crates/iota-core/tests/staged/iota.yaml index c2e0d35178b..3bf92815236 100644 --- a/crates/iota-core/tests/staged/iota.yaml +++ b/crates/iota-core/tests/staged/iota.yaml @@ -1,4 +1,3 @@ ---- AccountAddress: NEWTYPESTRUCT: TUPLEARRAY: @@ -6,11 +5,11 @@ AccountAddress: SIZE: 32 ActiveJwk: STRUCT: - - jwk_id: - TYPENAME: JwkId - - jwk: - TYPENAME: JWK - - epoch: U64 + - jwk_id: + TYPENAME: JwkId + - jwk: + TYPENAME: JWK + - epoch: U64 Argument: ENUM: 0: @@ -24,22 +23,22 @@ Argument: 3: NestedResult: TUPLE: - - U16 - - U16 + - U16 + - U16 AuthenticatorStateExpire: STRUCT: - - min_epoch: U64 - - authenticator_obj_initial_shared_version: - TYPENAME: SequenceNumber + - min_epoch: U64 + - authenticator_obj_initial_shared_version: + TYPENAME: SequenceNumber AuthenticatorStateUpdate: STRUCT: - - epoch: U64 - - round: U64 - - new_active_jwks: - SEQ: - TYPENAME: ActiveJwk - - authenticator_obj_initial_shared_version: - TYPENAME: SequenceNumber + - epoch: U64 + - round: U64 + - new_active_jwks: + SEQ: + TYPENAME: ActiveJwk + - authenticator_obj_initial_shared_version: + TYPENAME: SequenceNumber AuthorityPublicKeyBytes: NEWTYPESTRUCT: BYTES CallArg: @@ -54,22 +53,22 @@ CallArg: TYPENAME: ObjectArg ChangeEpoch: STRUCT: - - epoch: U64 - - protocol_version: - TYPENAME: ProtocolVersion - - storage_charge: U64 - - computation_charge: U64 - - storage_rebate: U64 - - non_refundable_storage_fee: U64 - - epoch_start_timestamp_ms: U64 - - system_packages: - SEQ: - TUPLE: - - TYPENAME: SequenceNumber - - SEQ: - SEQ: U8 - - SEQ: - TYPENAME: ObjectID + - epoch: U64 + - protocol_version: + TYPENAME: ProtocolVersion + - storage_charge: U64 + - computation_charge: U64 + - storage_rebate: U64 + - non_refundable_storage_fee: U64 + - epoch_start_timestamp_ms: U64 + - system_packages: + SEQ: + TUPLE: + - TYPENAME: SequenceNumber + - SEQ: + SEQ: U8 + - SEQ: + TYPENAME: ObjectID CheckpointCommitment: ENUM: 0: @@ -87,37 +86,37 @@ CheckpointContentsDigest: TYPENAME: Digest CheckpointContentsV1: STRUCT: - - transactions: - SEQ: - TYPENAME: ExecutionDigests - - user_signatures: + - transactions: + SEQ: + TYPENAME: ExecutionDigests + - user_signatures: + SEQ: SEQ: - SEQ: - TYPENAME: GenericSignature + TYPENAME: GenericSignature CheckpointDigest: NEWTYPESTRUCT: TYPENAME: Digest CheckpointSummary: STRUCT: - - epoch: U64 - - sequence_number: U64 - - network_total_transactions: U64 - - content_digest: - TYPENAME: CheckpointContentsDigest - - previous_digest: - OPTION: - TYPENAME: CheckpointDigest - - epoch_rolling_gas_cost_summary: - TYPENAME: GasCostSummary - - timestamp_ms: U64 - - checkpoint_commitments: - SEQ: - TYPENAME: CheckpointCommitment - - end_of_epoch_data: - OPTION: - TYPENAME: EndOfEpochData - - version_specific_data: - SEQ: U8 + - epoch: U64 + - sequence_number: U64 + - network_total_transactions: U64 + - content_digest: + TYPENAME: CheckpointContentsDigest + - previous_digest: + OPTION: + TYPENAME: CheckpointDigest + - epoch_rolling_gas_cost_summary: + TYPENAME: GasCostSummary + - timestamp_ms: U64 + - checkpoint_commitments: + SEQ: + TYPENAME: CheckpointCommitment + - end_of_epoch_data: + OPTION: + TYPENAME: EndOfEpochData + - version_specific_data: + SEQ: U8 Command: ENUM: 0: @@ -127,44 +126,44 @@ Command: 1: TransferObjects: TUPLE: - - SEQ: - TYPENAME: Argument - - TYPENAME: Argument + - SEQ: + TYPENAME: Argument + - TYPENAME: Argument 2: SplitCoins: TUPLE: - - TYPENAME: Argument - - SEQ: - TYPENAME: Argument + - TYPENAME: Argument + - SEQ: + TYPENAME: Argument 3: MergeCoins: TUPLE: - - TYPENAME: Argument - - SEQ: - TYPENAME: Argument + - TYPENAME: Argument + - SEQ: + TYPENAME: Argument 4: Publish: TUPLE: - - SEQ: - SEQ: U8 - - SEQ: - TYPENAME: ObjectID + - SEQ: + SEQ: U8 + - SEQ: + TYPENAME: ObjectID 5: MakeMoveVec: TUPLE: - - OPTION: - TYPENAME: TypeTag - - SEQ: - TYPENAME: Argument + - OPTION: + TYPENAME: TypeTag + - SEQ: + TYPENAME: Argument 6: Upgrade: TUPLE: - - SEQ: - SEQ: U8 - - SEQ: - TYPENAME: ObjectID - - TYPENAME: ObjectID - - TYPENAME: Argument + - SEQ: + SEQ: U8 + - SEQ: + TYPENAME: ObjectID + - TYPENAME: ObjectID + - TYPENAME: Argument CommandArgumentError: ENUM: 0: @@ -178,16 +177,16 @@ CommandArgumentError: 4: IndexOutOfBounds: STRUCT: - - idx: U16 + - idx: U16 5: SecondaryIndexOutOfBounds: STRUCT: - - result_idx: U16 - - secondary_idx: U16 + - result_idx: U16 + - secondary_idx: U16 6: InvalidResultArity: STRUCT: - - result_idx: U16 + - result_idx: U16 7: InvalidGasCoinUsage: UNIT 8: @@ -227,16 +226,16 @@ ConsensusCommitDigest: TYPENAME: Digest ConsensusCommitPrologue: STRUCT: - - epoch: U64 - - round: U64 - - commit_timestamp_ms: U64 + - epoch: U64 + - round: U64 + - commit_timestamp_ms: U64 ConsensusCommitPrologueV2: STRUCT: - - epoch: U64 - - round: U64 - - commit_timestamp_ms: U64 - - consensus_commit_digest: - TYPENAME: ConsensusCommitDigest + - epoch: U64 + - round: U64 + - commit_timestamp_ms: U64 + - consensus_commit_digest: + TYPENAME: ConsensusCommitDigest Data: ENUM: 0: @@ -259,33 +258,33 @@ Digest: NEWTYPESTRUCT: BYTES ECMHLiveObjectSetDigest: STRUCT: - - digest: - TYPENAME: Digest + - digest: + TYPENAME: Digest EffectsAuxDataDigest: NEWTYPESTRUCT: TYPENAME: Digest EffectsObjectChange: STRUCT: - - input_state: - TYPENAME: ObjectIn - - output_state: - TYPENAME: ObjectOut - - id_operation: - TYPENAME: IDOperation + - input_state: + TYPENAME: ObjectIn + - output_state: + TYPENAME: ObjectOut + - id_operation: + TYPENAME: IDOperation EmptySignInfo: STRUCT: [] EndOfEpochData: STRUCT: - - nextEpochCommittee: - SEQ: - TUPLE: - - TYPENAME: AuthorityPublicKeyBytes - - U64 - - nextEpochProtocolVersion: - TYPENAME: ProtocolVersion - - epochCommitments: - SEQ: - TYPENAME: CheckpointCommitment + - nextEpochCommittee: + SEQ: + TUPLE: + - TYPENAME: AuthorityPublicKeyBytes + - U64 + - nextEpochProtocolVersion: + TYPENAME: ProtocolVersion + - epochCommitments: + SEQ: + TYPENAME: CheckpointCommitment EndOfEpochTransactionKind: ENUM: 0: @@ -304,22 +303,22 @@ EndOfEpochTransactionKind: DenyListStateCreate: UNIT Envelope: STRUCT: - - data: - TYPENAME: SenderSignedData - - auth_signature: - TYPENAME: EmptySignInfo + - data: + TYPENAME: SenderSignedData + - auth_signature: + TYPENAME: EmptySignInfo ExecutionData: STRUCT: - - transaction: - TYPENAME: Envelope - - effects: - TYPENAME: TransactionEffects + - transaction: + TYPENAME: Envelope + - effects: + TYPENAME: TransactionEffects ExecutionDigests: STRUCT: - - transaction: - TYPENAME: TransactionDigest - - effects: - TYPENAME: TransactionEffectsDigest + - transaction: + TYPENAME: TransactionDigest + - effects: + TYPENAME: TransactionEffectsDigest ExecutionFailureStatus: ENUM: 0: @@ -333,18 +332,18 @@ ExecutionFailureStatus: 4: MoveObjectTooBig: STRUCT: - - object_size: U64 - - max_object_size: U64 + - object_size: U64 + - max_object_size: U64 5: MovePackageTooBig: STRUCT: - - object_size: U64 - - max_object_size: U64 + - object_size: U64 + - max_object_size: U64 6: CircularObjectOwnership: STRUCT: - - object: - TYPENAME: ObjectID + - object: + TYPENAME: ObjectID 7: InsufficientCoinBalance: UNIT 8: @@ -360,8 +359,8 @@ ExecutionFailureStatus: 12: MoveAbort: TUPLE: - - TYPENAME: MoveLocation - - U64 + - TYPENAME: MoveLocation + - U64 13: VMVerificationOrDeserializationError: UNIT 14: @@ -377,31 +376,31 @@ ExecutionFailureStatus: 19: CommandArgumentError: STRUCT: - - arg_idx: U16 - - kind: - TYPENAME: CommandArgumentError + - arg_idx: U16 + - kind: + TYPENAME: CommandArgumentError 20: TypeArgumentError: STRUCT: - - argument_idx: U16 - - kind: - TYPENAME: TypeArgumentError + - argument_idx: U16 + - kind: + TYPENAME: TypeArgumentError 21: UnusedValueWithoutDrop: STRUCT: - - result_idx: U16 - - secondary_idx: U16 + - result_idx: U16 + - secondary_idx: U16 22: InvalidPublicFunctionReturnType: STRUCT: - - idx: U16 + - idx: U16 23: InvalidTransferObject: UNIT 24: EffectsTooLarge: STRUCT: - - current_size: U64 - - max_size: U64 + - current_size: U64 + - max_size: U64 25: PublishUpgradeMissingDependency: UNIT 26: @@ -409,13 +408,13 @@ ExecutionFailureStatus: 27: PackageUpgradeError: STRUCT: - - upgrade_error: - TYPENAME: PackageUpgradeError + - upgrade_error: + TYPENAME: PackageUpgradeError 28: WrittenObjectsTooLarge: STRUCT: - - current_size: U64 - - max_size: U64 + - current_size: U64 + - max_size: U64 29: CertificateDenied: UNIT 30: @@ -431,37 +430,37 @@ ExecutionStatus: 1: Failure: STRUCT: - - error: - TYPENAME: ExecutionFailureStatus - - command: - OPTION: U64 + - error: + TYPENAME: ExecutionFailureStatus + - command: + OPTION: U64 FullCheckpointContents: STRUCT: - - transactions: - SEQ: - TYPENAME: ExecutionData - - user_signatures: + - transactions: + SEQ: + TYPENAME: ExecutionData + - user_signatures: + SEQ: SEQ: - SEQ: - TYPENAME: GenericSignature + TYPENAME: GenericSignature GasCostSummary: STRUCT: - - computationCost: U64 - - storageCost: U64 - - storageRebate: U64 - - nonRefundableStorageFee: U64 + - computationCost: U64 + - storageCost: U64 + - storageRebate: U64 + - nonRefundableStorageFee: U64 GasData: STRUCT: - - payment: - SEQ: - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - owner: - TYPENAME: IotaAddress - - price: U64 - - budget: U64 + - payment: + SEQ: + TUPLE: + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - owner: + TYPENAME: IotaAddress + - price: U64 + - budget: U64 GenericSignature: NEWTYPESTRUCT: SEQ: U8 @@ -470,15 +469,15 @@ GenesisObject: 0: RawObject: STRUCT: - - data: - TYPENAME: Data - - owner: - TYPENAME: Owner + - data: + TYPENAME: Data + - owner: + TYPENAME: Owner GenesisTransaction: STRUCT: - - objects: - SEQ: - TYPENAME: GenesisObject + - objects: + SEQ: + TYPENAME: GenesisObject IDOperation: ENUM: 0: @@ -491,15 +490,15 @@ Identifier: NEWTYPESTRUCT: STR Intent: STRUCT: - - scope: U8 - - version: U8 - - app_id: U8 + - scope: U8 + - version: U8 + - app_id: U8 IntentMessage: STRUCT: - - intent: - TYPENAME: Intent - - value: - TYPENAME: TransactionData + - intent: + TYPENAME: Intent + - value: + TYPENAME: TransactionData IotaAddress: NEWTYPESTRUCT: TUPLEARRAY: @@ -507,40 +506,40 @@ IotaAddress: SIZE: 32 JWK: STRUCT: - - kty: STR - - e: STR - - n: STR - - alg: STR + - kty: STR + - e: STR + - n: STR + - alg: STR JwkId: STRUCT: - - iss: STR - - kid: STR + - iss: STR + - kid: STR ModuleId: STRUCT: - - address: - TYPENAME: AccountAddress - - name: - TYPENAME: Identifier + - address: + TYPENAME: AccountAddress + - name: + TYPENAME: Identifier MoveLocation: STRUCT: - - module: - TYPENAME: ModuleId - - function: U16 - - instruction: U16 - - function_name: - OPTION: STR + - module: + TYPENAME: ModuleId + - function: U16 + - instruction: U16 + - function_name: + OPTION: STR MoveLocationOpt: NEWTYPESTRUCT: OPTION: TYPENAME: MoveLocation MoveObject: STRUCT: - - type_: - TYPENAME: MoveObjectType - - has_public_transfer: BOOL - - version: - TYPENAME: SequenceNumber - - contents: BYTES + - type_: + TYPENAME: MoveObjectType + - has_public_transfer: BOOL + - version: + TYPENAME: SequenceNumber + - contents: BYTES MoveObjectType: NEWTYPESTRUCT: TYPENAME: MoveObjectType_ @@ -560,63 +559,63 @@ MoveObjectType_: TYPENAME: TypeTag MovePackage: STRUCT: - - id: - TYPENAME: ObjectID - - version: - TYPENAME: SequenceNumber - - module_map: - MAP: - KEY: STR - VALUE: BYTES - - type_origin_table: - SEQ: - TYPENAME: TypeOrigin - - linkage_table: - MAP: - KEY: - TYPENAME: ObjectID - VALUE: - TYPENAME: UpgradeInfo + - id: + TYPENAME: ObjectID + - version: + TYPENAME: SequenceNumber + - module_map: + MAP: + KEY: STR + VALUE: BYTES + - type_origin_table: + SEQ: + TYPENAME: TypeOrigin + - linkage_table: + MAP: + KEY: + TYPENAME: ObjectID + VALUE: + TYPENAME: UpgradeInfo MultiSig: STRUCT: - - sigs: - SEQ: - TYPENAME: CompressedSignature - - bitmap: U16 - - multisig_pk: - TYPENAME: MultiSigPublicKey + - sigs: + SEQ: + TYPENAME: CompressedSignature + - bitmap: U16 + - multisig_pk: + TYPENAME: MultiSigPublicKey MultiSigPublicKey: STRUCT: - - pk_map: - SEQ: - TUPLE: - - TYPENAME: PublicKey - - U8 - - threshold: U16 + - pk_map: + SEQ: + TUPLE: + - TYPENAME: PublicKey + - U8 + - threshold: U16 ObjectArg: ENUM: 0: ImmOrOwnedObject: NEWTYPE: TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest 1: SharedObject: STRUCT: - - id: - TYPENAME: ObjectID - - initial_shared_version: - TYPENAME: SequenceNumber - - mutable: BOOL + - id: + TYPENAME: ObjectID + - initial_shared_version: + TYPENAME: SequenceNumber + - mutable: BOOL 2: Receiving: NEWTYPE: TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest ObjectDigest: NEWTYPESTRUCT: TYPENAME: Digest @@ -631,10 +630,10 @@ ObjectIn: Exist: NEWTYPE: TUPLE: - - TUPLE: - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - TYPENAME: Owner + - TUPLE: + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - TYPENAME: Owner ObjectInfoRequestKind: ENUM: 0: @@ -651,14 +650,14 @@ ObjectOut: ObjectWrite: NEWTYPE: TUPLE: - - TYPENAME: ObjectDigest - - TYPENAME: Owner + - TYPENAME: ObjectDigest + - TYPENAME: Owner 2: PackageWrite: NEWTYPE: TUPLE: - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest Owner: ENUM: 0: @@ -672,8 +671,8 @@ Owner: 2: Shared: STRUCT: - - initial_shared_version: - TYPENAME: SequenceNumber + - initial_shared_version: + TYPENAME: SequenceNumber 3: Immutable: UNIT PackageUpgradeError: @@ -681,53 +680,53 @@ PackageUpgradeError: 0: UnableToFetchPackage: STRUCT: - - package_id: - TYPENAME: ObjectID + - package_id: + TYPENAME: ObjectID 1: NotAPackage: STRUCT: - - object_id: - TYPENAME: ObjectID + - object_id: + TYPENAME: ObjectID 2: IncompatibleUpgrade: UNIT 3: DigestDoesNotMatch: STRUCT: - - digest: - SEQ: U8 + - digest: + SEQ: U8 4: UnknownUpgradePolicy: STRUCT: - - policy: U8 + - policy: U8 5: PackageIDDoesNotMatch: STRUCT: - - package_id: - TYPENAME: ObjectID - - ticket_id: - TYPENAME: ObjectID + - package_id: + TYPENAME: ObjectID + - ticket_id: + TYPENAME: ObjectID ProgrammableMoveCall: STRUCT: - - package: - TYPENAME: ObjectID - - module: - TYPENAME: Identifier - - function: - TYPENAME: Identifier - - type_arguments: - SEQ: - TYPENAME: TypeTag - - arguments: - SEQ: - TYPENAME: Argument + - package: + TYPENAME: ObjectID + - module: + TYPENAME: Identifier + - function: + TYPENAME: Identifier + - type_arguments: + SEQ: + TYPENAME: TypeTag + - arguments: + SEQ: + TYPENAME: Argument ProgrammableTransaction: STRUCT: - - inputs: - SEQ: - TYPENAME: CallArg - - commands: - SEQ: - TYPENAME: Command + - inputs: + SEQ: + TYPENAME: CallArg + - commands: + SEQ: + TYPENAME: Command ProtocolVersion: NEWTYPESTRUCT: U64 PublicKey: @@ -758,37 +757,37 @@ RandomnessRound: NEWTYPESTRUCT: U64 RandomnessStateUpdate: STRUCT: - - epoch: U64 - - randomness_round: - TYPENAME: RandomnessRound - - random_bytes: - SEQ: U8 - - randomness_obj_initial_shared_version: - TYPENAME: SequenceNumber + - epoch: U64 + - randomness_round: + TYPENAME: RandomnessRound + - random_bytes: + SEQ: U8 + - randomness_obj_initial_shared_version: + TYPENAME: SequenceNumber SenderSignedData: NEWTYPESTRUCT: SEQ: TYPENAME: SenderSignedTransaction SenderSignedTransaction: STRUCT: - - intent_message: - TYPENAME: IntentMessage - - tx_signatures: - SEQ: - TYPENAME: GenericSignature + - intent_message: + TYPENAME: IntentMessage + - tx_signatures: + SEQ: + TYPENAME: GenericSignature SequenceNumber: NEWTYPESTRUCT: U64 StructTag: STRUCT: - - address: - TYPENAME: AccountAddress - - module: - TYPENAME: Identifier - - name: - TYPENAME: Identifier - - type_args: - SEQ: - TYPENAME: TypeTag + - address: + TYPENAME: AccountAddress + - module: + TYPENAME: Identifier + - name: + TYPENAME: Identifier + - type_args: + SEQ: + TYPENAME: TypeTag TransactionData: ENUM: 0: @@ -797,14 +796,14 @@ TransactionData: TYPENAME: TransactionDataV1 TransactionDataV1: STRUCT: - - kind: - TYPENAME: TransactionKind - - sender: - TYPENAME: IotaAddress - - gas_data: - TYPENAME: GasData - - expiration: - TYPENAME: TransactionExpiration + - kind: + TYPENAME: TransactionKind + - sender: + TYPENAME: IotaAddress + - gas_data: + TYPENAME: GasData + - expiration: + TYPENAME: TransactionExpiration TransactionDigest: NEWTYPESTRUCT: TYPENAME: Digest @@ -823,111 +822,111 @@ TransactionEffectsDigest: TYPENAME: Digest TransactionEffectsV1: STRUCT: - - status: - TYPENAME: ExecutionStatus - - executed_epoch: U64 - - gas_used: - TYPENAME: GasCostSummary - - modified_at_versions: - SEQ: - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - shared_objects: - SEQ: - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - transaction_digest: - TYPENAME: TransactionDigest - - created: - SEQ: - TUPLE: - - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - TYPENAME: Owner - - mutated: - SEQ: - TUPLE: - - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - TYPENAME: Owner - - unwrapped: - SEQ: - TUPLE: - - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - TYPENAME: Owner - - deleted: - SEQ: - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - unwrapped_then_deleted: - SEQ: - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - wrapped: - SEQ: - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - gas_object: + - status: + TYPENAME: ExecutionStatus + - executed_epoch: U64 + - gas_used: + TYPENAME: GasCostSummary + - modified_at_versions: + SEQ: TUPLE: - - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest - - TYPENAME: Owner - - events_digest: - OPTION: - TYPENAME: TransactionEventsDigest - - dependencies: - SEQ: - TYPENAME: TransactionDigest + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - shared_objects: + SEQ: + TUPLE: + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - transaction_digest: + TYPENAME: TransactionDigest + - created: + SEQ: + TUPLE: + - TUPLE: + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - TYPENAME: Owner + - mutated: + SEQ: + TUPLE: + - TUPLE: + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - TYPENAME: Owner + - unwrapped: + SEQ: + TUPLE: + - TUPLE: + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - TYPENAME: Owner + - deleted: + SEQ: + TUPLE: + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - unwrapped_then_deleted: + SEQ: + TUPLE: + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - wrapped: + SEQ: + TUPLE: + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - gas_object: + TUPLE: + - TUPLE: + - TYPENAME: ObjectID + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest + - TYPENAME: Owner + - events_digest: + OPTION: + TYPENAME: TransactionEventsDigest + - dependencies: + SEQ: + TYPENAME: TransactionDigest TransactionEffectsV2: STRUCT: - - status: - TYPENAME: ExecutionStatus - - executed_epoch: U64 - - gas_used: - TYPENAME: GasCostSummary - - transaction_digest: + - status: + TYPENAME: ExecutionStatus + - executed_epoch: U64 + - gas_used: + TYPENAME: GasCostSummary + - transaction_digest: + TYPENAME: TransactionDigest + - gas_object_index: + OPTION: U32 + - events_digest: + OPTION: + TYPENAME: TransactionEventsDigest + - dependencies: + SEQ: TYPENAME: TransactionDigest - - gas_object_index: - OPTION: U32 - - events_digest: - OPTION: - TYPENAME: TransactionEventsDigest - - dependencies: - SEQ: - TYPENAME: TransactionDigest - - lamport_version: - TYPENAME: SequenceNumber - - changed_objects: - SEQ: - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: EffectsObjectChange - - unchanged_shared_objects: - SEQ: - TUPLE: - - TYPENAME: ObjectID - - TYPENAME: UnchangedSharedKind - - aux_data_digest: - OPTION: - TYPENAME: EffectsAuxDataDigest + - lamport_version: + TYPENAME: SequenceNumber + - changed_objects: + SEQ: + TUPLE: + - TYPENAME: ObjectID + - TYPENAME: EffectsObjectChange + - unchanged_shared_objects: + SEQ: + TUPLE: + - TYPENAME: ObjectID + - TYPENAME: UnchangedSharedKind + - aux_data_digest: + OPTION: + TYPENAME: EffectsAuxDataDigest TransactionEventsDigest: NEWTYPESTRUCT: TYPENAME: Digest @@ -981,10 +980,10 @@ TypeArgumentError: ConstraintNotSatisfied: UNIT TypeOrigin: STRUCT: - - module_name: STR - - struct_name: STR - - package: - TYPENAME: ObjectID + - module_name: STR + - struct_name: STR + - package: + TYPENAME: ObjectID TypeTag: ENUM: 0: @@ -1036,8 +1035,8 @@ UnchangedSharedKind: ReadOnlyRoot: NEWTYPE: TUPLE: - - TYPENAME: SequenceNumber - - TYPENAME: ObjectDigest + - TYPENAME: SequenceNumber + - TYPENAME: ObjectDigest 1: MutateDeleted: NEWTYPE: @@ -1048,10 +1047,10 @@ UnchangedSharedKind: TYPENAME: SequenceNumber UpgradeInfo: STRUCT: - - upgraded_id: - TYPENAME: ObjectID - - upgraded_version: - TYPENAME: SequenceNumber + - upgraded_id: + TYPENAME: ObjectID + - upgraded_version: + TYPENAME: SequenceNumber ZkLoginAuthenticatorAsBytes: NEWTYPESTRUCT: SEQ: U8 diff --git a/crates/iota-cost/Cargo.toml b/crates/iota-cost/Cargo.toml index dcd6383168f..4e687788eaa 100644 --- a/crates/iota-cost/Cargo.toml +++ b/crates/iota-cost/Cargo.toml @@ -12,7 +12,6 @@ bcs.workspace = true iota-types.workspace = true serde.workspace = true strum.workspace = true -strum_macros.workspace = true tokio = { workspace = true, features = ["full"] } [dev-dependencies] diff --git a/crates/iota-cost/tests/empirical_transaction_cost.rs b/crates/iota-cost/tests/empirical_transaction_cost.rs index 03494cb30e4..b245d6883fb 100644 --- a/crates/iota-cost/tests/empirical_transaction_cost.rs +++ b/crates/iota-cost/tests/empirical_transaction_cost.rs @@ -19,7 +19,7 @@ use iota_types::{ IOTA_FRAMEWORK_PACKAGE_ID, }; use serde::{Deserialize, Serialize}; -use strum_macros::{Display, EnumString}; +use strum::{Display, EnumString}; use test_cluster::{TestCluster, TestClusterBuilder}; #[derive( diff --git a/crates/iota-data-ingestion/src/workers/archival.rs b/crates/iota-data-ingestion/src/workers/archival.rs index ae1db5def99..a20c07f010d 100644 --- a/crates/iota-data-ingestion/src/workers/archival.rs +++ b/crates/iota-data-ingestion/src/workers/archival.rs @@ -125,7 +125,7 @@ impl ArchivalWorker { let bytes = finalize_manifest(manifest)?; self.remote_store - .put(&Path::from("MANIFEST"), bytes) + .put(&Path::from("MANIFEST"), bytes.into()) .await?; Ok(()) } @@ -140,7 +140,7 @@ impl ArchivalWorker { let mut cursor = Cursor::new(buffer); compress(&mut cursor, &mut compressed_buffer)?; self.remote_store - .put(&location, Bytes::from(compressed_buffer.clone())) + .put(&location, Bytes::from(compressed_buffer.clone()).into()) .await?; Ok(Bytes::from(compressed_buffer)) } diff --git a/crates/iota-data-ingestion/src/workers/blob.rs b/crates/iota-data-ingestion/src/workers/blob.rs index b7ddee2a9ce..6200a92d7c4 100644 --- a/crates/iota-data-ingestion/src/workers/blob.rs +++ b/crates/iota-data-ingestion/src/workers/blob.rs @@ -38,7 +38,9 @@ impl Worker for BlobWorker { "{}.chk", checkpoint.checkpoint_summary.sequence_number )); - self.remote_store.put(&location, Bytes::from(bytes)).await?; + self.remote_store + .put(&location, Bytes::from(bytes).into()) + .await?; Ok(()) } } diff --git a/crates/iota-data-ingestion/src/workers/kv_store.rs b/crates/iota-data-ingestion/src/workers/kv_store.rs index 4c923e5056e..eba8a4bc716 100644 --- a/crates/iota-data-ingestion/src/workers/kv_store.rs +++ b/crates/iota-data-ingestion/src/workers/kv_store.rs @@ -106,7 +106,7 @@ impl KVStoreWorker { "bcs", AttributeValue::B(Blob::new(bcs::to_bytes(value.borrow())?)), ) - .build(), + .build()?, )) .build(); items.push(item); diff --git a/crates/iota-faucet/src/main.rs b/crates/iota-faucet/src/main.rs index 275085c7c75..60fcf4af319 100644 --- a/crates/iota-faucet/src/main.rs +++ b/crates/iota-faucet/src/main.rs @@ -123,9 +123,8 @@ async fn main() -> Result<(), anyhow::Error> { let addr = SocketAddr::new(IpAddr::V4(host_ip), port); info!("listening on {}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await?; + let listener = tokio::net::TcpListener::bind(addr).await?; + axum::serve(listener, app.into_make_service()).await?; Ok(()) } diff --git a/crates/iota-genesis-builder/src/stardust/native_token/package_data.rs b/crates/iota-genesis-builder/src/stardust/native_token/package_data.rs index 4a39f2d4b95..180f63d604a 100644 --- a/crates/iota-genesis-builder/src/stardust/native_token/package_data.rs +++ b/crates/iota-genesis-builder/src/stardust/native_token/package_data.rs @@ -14,7 +14,7 @@ use iota_types::stardust::error::StardustError; use move_compiler::parser::keywords; use rand::distributions::{Alphanumeric, DistString}; use rand_pcg::Pcg64; -use rand_seeder::Seeder; +use rand_seeder::{rand_core::RngCore, Seeder, SipRng}; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -231,9 +231,13 @@ fn derive_foundry_package_lowercase_identifier(input: &str, seed: &[u8]) -> Stri let additional_part = if is_valid { refined_identifier } else { + let mut rng: SipRng = Seeder::from(seed).make_rng(); + fn next_u128(rng: &mut SipRng) -> u128 { + (rng.next_u64() as u128) << 64 | rng.next_u64() as u128 + } // Generate a new valid random identifier if the identifier is empty. Alphanumeric - .sample_string(&mut Pcg64::from(Seeder::from(seed).make_rng()), 7) + .sample_string(&mut Pcg64::new(next_u128(&mut rng), next_u128(&mut rng)), 7) .to_lowercase() }; final_identifier.push_str(&additional_part); diff --git a/crates/iota-graphql-rpc/Cargo.toml b/crates/iota-graphql-rpc/Cargo.toml index 40b61053949..f64470eb4ac 100644 --- a/crates/iota-graphql-rpc/Cargo.toml +++ b/crates/iota-graphql-rpc/Cargo.toml @@ -13,6 +13,7 @@ async-graphql-axum.workspace = true async-graphql-value.workspace = true async-trait.workspace = true axum.workspace = true +axum-extra = { workspace = true, features = ["typed-header"] } chrono.workspace = true clap.workspace = true const-str.workspace = true @@ -24,7 +25,9 @@ futures.workspace = true git-version.workspace = true hex.workspace = true http.workspace = true +http-body-util.workspace = true hyper.workspace = true +hyper-util = { workspace = true, features = ["http2", "server", "tokio"] } im.workspace = true iota-sdk.workspace = true iota-types.workspace = true diff --git a/crates/iota-graphql-rpc/schema/current_progress_schema.graphql b/crates/iota-graphql-rpc/schema/current_progress_schema.graphql index 1cb4669df03..616f6261d52 100644 --- a/crates/iota-graphql-rpc/schema/current_progress_schema.graphql +++ b/crates/iota-graphql-rpc/schema/current_progress_schema.graphql @@ -4346,6 +4346,8 @@ type ZkLoginVerifyResult { errors: [String!]! } +directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT schema { query: Query mutation: Mutation diff --git a/crates/iota-graphql-rpc/src/extensions/query_limits_checker.rs b/crates/iota-graphql-rpc/src/extensions/query_limits_checker.rs index c6247ef09ab..e4cb738b784 100644 --- a/crates/iota-graphql-rpc/src/extensions/query_limits_checker.rs +++ b/crates/iota-graphql-rpc/src/extensions/query_limits_checker.rs @@ -17,10 +17,8 @@ use async_graphql::{ value, Name, Pos, Positioned, Response, ServerResult, Value, Variables, }; use async_graphql_value::Value as GqlValue; -use axum::{ - headers, - http::{HeaderName, HeaderValue}, -}; +use axum::http::{HeaderName, HeaderValue}; +use axum_extra::headers; use iota_graphql_rpc_headers::LIMITS_HEADER; use once_cell::sync::Lazy; use tokio::sync::Mutex; diff --git a/crates/iota-graphql-rpc/src/server/builder.rs b/crates/iota-graphql-rpc/src/server/builder.rs index 3a1227091df..7d8a131ee4b 100644 --- a/crates/iota-graphql-rpc/src/server/builder.rs +++ b/crates/iota-graphql-rpc/src/server/builder.rs @@ -20,22 +20,22 @@ use async_graphql::{ }; use async_graphql_axum::{GraphQLRequest, GraphQLResponse}; use axum::{ - extract::{connect_info::IntoMakeServiceWithConnectInfo, ConnectInfo, FromRef, State}, - headers::Header, + body::Body, + extract::{ConnectInfo, FromRef, State}, http::{HeaderMap, StatusCode}, middleware::{self}, response::IntoResponse, routing::{post, MethodRouter, Route}, Router, }; +use axum_extra::headers::Header as _; use http::{HeaderValue, Method, Request}; -use hyper::{server::conn::AddrIncoming as HyperAddrIncoming, Body, Server as HyperServer}; use iota_graphql_rpc_headers::{LIMITS_HEADER, VERSION_HEADER}; use iota_package_resolver::{PackageStoreWithLruCache, Resolver}; use iota_sdk::IotaClientBuilder; use mysten_metrics::spawn_monitored_task; use mysten_network::callback::{CallbackLayer, MakeCallbackHandler, ResponseHandler}; -use tokio::{join, sync::OnceCell}; +use tokio::{join, net::TcpListener, sync::OnceCell}; use tokio_util::sync::CancellationToken; use tower::{Layer, Service}; use tower_http::cors::{AllowOrigin, CorsLayer}; @@ -70,7 +70,8 @@ use crate::{ }; pub(crate) struct Server { - pub server: HyperServer>, + address: SocketAddr, + router: Router, /// The following fields are internally used for background tasks checkpoint_watermark: CheckpointWatermark, state: AppState, @@ -107,14 +108,21 @@ impl Server { let server_task = { info!("Starting graphql service"); let cancellation_token = self.state.cancellation_token.clone(); + let address = self.address; + let router = self.router; spawn_monitored_task!(async move { - self.server - .with_graceful_shutdown(async { - cancellation_token.cancelled().await; - info!("Shutdown signal received, terminating graphql service"); - }) - .await - .map_err(|e| Error::Internal(format!("Server run failed: {}", e))) + axum::serve( + TcpListener::bind(address) + .await + .map_err(|e| Error::Internal(format!("listener bind failed: {}", e)))?, + router.into_make_service_with_connect_info::(), + ) + .with_graceful_shutdown(async move { + cancellation_token.cancelled().await; + info!("Shutdown signal received, terminating graphql service"); + }) + .await + .map_err(|e| Error::Internal(format!("Server run failed: {}", e))) }) }; @@ -320,12 +328,10 @@ impl ServerBuilder { .layer(Self::cors()?); Ok(Server { - server: axum::Server::bind( - &address - .parse() - .map_err(|_| Error::Internal(format!("Failed to parse address {}", address)))?, - ) - .serve(app.into_make_service_with_connect_info::()), + address: address + .parse() + .map_err(|_| Error::Internal(format!("Failed to parse address {}", address)))?, + router: app, checkpoint_watermark, state, db_reader, diff --git a/crates/iota-graphql-rpc/src/server/version.rs b/crates/iota-graphql-rpc/src/server/version.rs index 516e416e2f0..3e429c7f9c6 100644 --- a/crates/iota-graphql-rpc/src/server/version.rs +++ b/crates/iota-graphql-rpc/src/server/version.rs @@ -3,13 +3,12 @@ // SPDX-License-Identifier: Apache-2.0 use axum::{ - extract::State, - headers, - http::{HeaderName, HeaderValue, Request, StatusCode}, + extract::{Request, State}, + http::{HeaderName, HeaderValue, StatusCode}, middleware::Next, response::{IntoResponse, Response}, - TypedHeader, }; +use axum_extra::{headers, TypedHeader}; use crate::{ config::Version, @@ -51,11 +50,11 @@ impl headers::Header for IotaRpcVersion { /// constraint. Each RPC instance only supports one version of the RPC /// software, and it is the responsibility of the load balancer to make sure /// version constraints are met. -pub(crate) async fn check_version_middleware( +pub(crate) async fn check_version_middleware( user_version: Option>, State(version): State, - request: Request, - next: Next, + request: Request, + next: Next, ) -> Response { if let Some(TypedHeader(IotaRpcVersion(req_version, rest))) = user_version { if !rest.is_empty() { @@ -111,10 +110,10 @@ pub(crate) async fn check_version_middleware( /// Mark every outgoing response with a header indicating the precise version of /// the RPC that was used (including the patch version and sha). -pub(crate) async fn set_version_middleware( +pub(crate) async fn set_version_middleware( State(version): State, - request: Request, - next: Next, + request: Request, + next: Next, ) -> Response { let mut response = next.run(request).await; let headers = response.headers_mut(); @@ -146,6 +145,7 @@ mod tests { use axum::{body::Body, middleware, routing::get, Router}; use expect_test::expect; + use http_body_util::BodyExt; use mysten_metrics; use tokio_util::sync::CancellationToken; use tower::ServiceExt; @@ -202,8 +202,8 @@ mod tests { } async fn response_body(response: Response) -> String { - let bytes = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let value: serde_json::Value = serde_json::from_slice(bytes.as_ref()).unwrap(); + let bytes = response.into_body().collect().await.unwrap(); + let value: serde_json::Value = serde_json::from_slice(bytes.to_bytes().as_ref()).unwrap(); serde_json::to_string_pretty(&value).unwrap() } diff --git a/crates/iota-graphql-rpc/src/types/checkpoint.rs b/crates/iota-graphql-rpc/src/types/checkpoint.rs index f5d19b7273c..7bf2e2d8dd6 100644 --- a/crates/iota-graphql-rpc/src/types/checkpoint.rs +++ b/crates/iota-graphql-rpc/src/types/checkpoint.rs @@ -11,6 +11,7 @@ use async_graphql::{ }; use diesel::{CombineDsl, ExpressionMethods, OptionalExtension, QueryDsl}; use fastcrypto::encoding::{Base58, Encoding}; +use futures::Future; use iota_indexer::{ models::checkpoints::StoredCheckpoint, schema::{checkpoints, objects_snapshot}, @@ -465,106 +466,118 @@ impl Checkpointed for Cursor { } } -#[async_trait::async_trait] +#[allow(clippy::manual_async_fn)] impl Loader for Db { type Value = Checkpoint; type Error = Error; - async fn load(&self, keys: &[SeqNumKey]) -> Result, Error> { - use checkpoints::dsl; - - let checkpoint_ids: BTreeSet<_> = keys - .iter() - .filter_map(|key| { - if let Some(viewed_at) = key.checkpoint_viewed_at { - // Filter out keys querying for checkpoints after their own consistency cursor. - (viewed_at >= key.sequence_number).then_some(key.sequence_number as i64) - } else { - Some(key.sequence_number as i64) - } - }) - .collect(); + fn load( + &self, + keys: &[SeqNumKey], + ) -> impl Future, Self::Error>> + Send { + async { + use checkpoints::dsl; + + let checkpoint_ids: BTreeSet<_> = keys + .iter() + .filter_map(|key| { + if let Some(viewed_at) = key.checkpoint_viewed_at { + // Filter out keys querying for checkpoints after their own consistency + // cursor. + (viewed_at >= key.sequence_number).then_some(key.sequence_number as i64) + } else { + Some(key.sequence_number as i64) + } + }) + .collect(); - let checkpoints: Vec = self - .execute(move |conn| { - conn.results(move || { - dsl::checkpoints - .filter(dsl::sequence_number.eq_any(checkpoint_ids.iter().cloned())) + let checkpoints: Vec = self + .execute(move |conn| { + conn.results(move || { + dsl::checkpoints + .filter(dsl::sequence_number.eq_any(checkpoint_ids.iter().cloned())) + }) }) - }) - .await - .map_err(|e| Error::Internal(format!("Failed to fetch checkpoints: {e}")))?; - - let checkpoint_id_to_stored: BTreeMap<_, _> = checkpoints - .into_iter() - .map(|stored| (stored.sequence_number as u64, stored)) - .collect(); - - Ok(keys - .iter() - .filter_map(|key| { - let stored = checkpoint_id_to_stored.get(&key.sequence_number).cloned()?; - let checkpoint = Checkpoint { - stored, - checkpoint_viewed_at: key.checkpoint_viewed_at, - }; - - let digest = &checkpoint.stored.checkpoint_digest; - if matches!(key.digest, Some(d) if d.as_slice() != digest) { - None - } else { - Some((*key, checkpoint)) - } - }) - .collect()) + .await + .map_err(|e| Error::Internal(format!("Failed to fetch checkpoints: {e}")))?; + + let checkpoint_id_to_stored: BTreeMap<_, _> = checkpoints + .into_iter() + .map(|stored| (stored.sequence_number as u64, stored)) + .collect(); + + Ok(keys + .iter() + .filter_map(|key| { + let stored = checkpoint_id_to_stored.get(&key.sequence_number).cloned()?; + let checkpoint = Checkpoint { + stored, + checkpoint_viewed_at: key.checkpoint_viewed_at, + }; + + let digest = &checkpoint.stored.checkpoint_digest; + if matches!(key.digest, Some(d) if d.as_slice() != digest) { + None + } else { + Some((*key, checkpoint)) + } + }) + .collect()) + } } } -#[async_trait::async_trait] +#[allow(clippy::manual_async_fn)] impl Loader for Db { type Value = Checkpoint; type Error = Error; - async fn load(&self, keys: &[DigestKey]) -> Result, Error> { - use checkpoints::dsl; + fn load( + &self, + keys: &[DigestKey], + ) -> impl Future, Self::Error>> + Send { + async { + use checkpoints::dsl; - let digests: BTreeSet<_> = keys.iter().map(|key| key.digest.to_vec()).collect(); + let digests: BTreeSet<_> = keys.iter().map(|key| key.digest.to_vec()).collect(); - let checkpoints: Vec = self - .execute(move |conn| { - conn.results(move || { - dsl::checkpoints.filter(dsl::checkpoint_digest.eq_any(digests.iter().cloned())) + let checkpoints: Vec = self + .execute(move |conn| { + conn.results(move || { + dsl::checkpoints + .filter(dsl::checkpoint_digest.eq_any(digests.iter().cloned())) + }) }) - }) - .await - .map_err(|e| Error::Internal(format!("Failed to fetch checkpoints: {e}")))?; - - let checkpoint_id_to_stored: BTreeMap<_, _> = checkpoints - .into_iter() - .map(|stored| (stored.checkpoint_digest.clone(), stored)) - .collect(); - - Ok(keys - .iter() - .filter_map(|key| { - let stored = checkpoint_id_to_stored - .get(key.digest.as_slice()) - .cloned()?; - let checkpoint = Checkpoint { - stored, - checkpoint_viewed_at: key.checkpoint_viewed_at, - }; - - // Filter by key's checkpoint viewed at here. Doing this in memory because it - // should be quite rare that this query actually filters - // something, but encoding it in SQL is complicated. - let seq_num = checkpoint.stored.sequence_number as u64; - if matches!(key.checkpoint_viewed_at, Some(cp) if cp < seq_num) { - None - } else { - Some((*key, checkpoint)) - } - }) - .collect()) + .await + .map_err(|e| Error::Internal(format!("Failed to fetch checkpoints: {e}")))?; + + let checkpoint_id_to_stored: BTreeMap<_, _> = checkpoints + .into_iter() + .map(|stored| (stored.checkpoint_digest.clone(), stored)) + .collect(); + + Ok(keys + .iter() + .filter_map(|key| { + let stored = checkpoint_id_to_stored + .get(key.digest.as_slice()) + .cloned()?; + let checkpoint = Checkpoint { + stored, + checkpoint_viewed_at: key.checkpoint_viewed_at, + }; + + // Filter by key's checkpoint viewed at here. Doing this in memory because it + // should be quite rare that this query actually filters + // something, but encoding it in SQL is complicated. + let seq_num = checkpoint.stored.sequence_number as u64; + if matches!(key.checkpoint_viewed_at, Some(cp) if cp < seq_num) { + None + } else { + Some((*key, checkpoint)) + } + }) + .collect()) + } } } diff --git a/crates/iota-graphql-rpc/src/types/epoch.rs b/crates/iota-graphql-rpc/src/types/epoch.rs index a158f651da5..390669484db 100644 --- a/crates/iota-graphql-rpc/src/types/epoch.rs +++ b/crates/iota-graphql-rpc/src/types/epoch.rs @@ -11,6 +11,7 @@ use async_graphql::{ }; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper}; use fastcrypto::encoding::{Base58, Encoding}; +use futures::Future; use iota_indexer::{models::epoch::QueryableEpochInfo, schema::epochs}; use iota_types::messages_checkpoint::CheckpointCommitment as EpochCommitment; @@ -342,52 +343,57 @@ impl Epoch { } } -#[async_trait::async_trait] +#[allow(clippy::manual_async_fn)] impl Loader for Db { type Value = Epoch; type Error = Error; - async fn load(&self, keys: &[EpochKey]) -> Result, Error> { - use epochs::dsl; - - let epoch_ids: BTreeSet<_> = keys.iter().map(|key| key.epoch_id as i64).collect(); - let epochs: Vec = self - .execute_repeatable(move |conn| { - conn.results(move || { - dsl::epochs - .select(QueryableEpochInfo::as_select()) - .filter(dsl::epoch.eq_any(epoch_ids.iter().cloned())) + fn load( + &self, + keys: &[EpochKey], + ) -> impl Future, Self::Error>> + Send { + async { + use epochs::dsl; + + let epoch_ids: BTreeSet<_> = keys.iter().map(|key| key.epoch_id as i64).collect(); + let epochs: Vec = self + .execute_repeatable(move |conn| { + conn.results(move || { + dsl::epochs + .select(QueryableEpochInfo::as_select()) + .filter(dsl::epoch.eq_any(epoch_ids.iter().cloned())) + }) }) - }) - .await - .map_err(|e| Error::Internal(format!("Failed to fetch epochs: {e}")))?; - - let epoch_id_to_stored: BTreeMap<_, _> = epochs - .into_iter() - .map(|stored| (stored.epoch as u64, stored)) - .collect(); - - Ok(keys - .iter() - .filter_map(|key| { - let stored = epoch_id_to_stored.get(&key.epoch_id).cloned()?; - let epoch = Epoch { - stored, - checkpoint_viewed_at: key.checkpoint_viewed_at, - }; - - // We filter by checkpoint viewed at in memory because it should be quite rare - // that this query actually filters something (only in edge - // cases), and not trying to encode it in the SQL query makes - // the query much simpler and therefore easier for - // the DB to plan. - let start = epoch.stored.first_checkpoint_id as u64; - if matches!(key.checkpoint_viewed_at, Some(cp) if cp < start) { - None - } else { - Some((*key, epoch)) - } - }) - .collect()) + .await + .map_err(|e| Error::Internal(format!("Failed to fetch epochs: {e}")))?; + + let epoch_id_to_stored: BTreeMap<_, _> = epochs + .into_iter() + .map(|stored| (stored.epoch as u64, stored)) + .collect(); + + Ok(keys + .iter() + .filter_map(|key| { + let stored = epoch_id_to_stored.get(&key.epoch_id).cloned()?; + let epoch = Epoch { + stored, + checkpoint_viewed_at: key.checkpoint_viewed_at, + }; + + // We filter by checkpoint viewed at in memory because it should be quite rare + // that this query actually filters something (only in edge + // cases), and not trying to encode it in the SQL query makes + // the query much simpler and therefore easier for + // the DB to plan. + let start = epoch.stored.first_checkpoint_id as u64; + if matches!(key.checkpoint_viewed_at, Some(cp) if cp < start) { + None + } else { + Some((*key, epoch)) + } + }) + .collect()) + } } } diff --git a/crates/iota-graphql-rpc/src/types/move_value.rs b/crates/iota-graphql-rpc/src/types/move_value.rs index 8e4d33b8daf..d613f26467f 100644 --- a/crates/iota-graphql-rpc/src/types/move_value.rs +++ b/crates/iota-graphql-rpc/src/types/move_value.rs @@ -901,7 +901,7 @@ mod tests { .unwrap(); let expect = expect![[ - r#"{baz: null,qux: [{quy: 44,quz: "Hello, world!",frob: "0x0000000000000000000000000000000000000000000000000000000000000045"},{quy: 46,quz: null,frob: "0x0000000000000000000000000000000000000000000000000000000000000047"}]}"# + r#"{baz: null, qux: [{quy: 44, quz: "Hello, world!", frob: "0x0000000000000000000000000000000000000000000000000000000000000045"}, {quy: 46, quz: null, frob: "0x0000000000000000000000000000000000000000000000000000000000000047"}]}"# ]]; expect.assert_eq(&format!("{v}")); } diff --git a/crates/iota-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap b/crates/iota-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap index d6cf62b6d09..24d4617eb8c 100644 --- a/crates/iota-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap +++ b/crates/iota-graphql-rpc/tests/snapshots/snapshot_tests__schema_sdl_export.snap @@ -4350,6 +4350,8 @@ type ZkLoginVerifyResult { errors: [String!]! } +directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT schema { query: Query mutation: Mutation diff --git a/crates/iota-indexer/src/apis/extended_api.rs b/crates/iota-indexer/src/apis/extended_api.rs index 2f954d3fd4b..ce1d8d267ac 100644 --- a/crates/iota-indexer/src/apis/extended_api.rs +++ b/crates/iota-indexer/src/apis/extended_api.rs @@ -3,7 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 use iota_json_rpc::IotaRpcModule; -use iota_json_rpc_api::{validate_limit, ExtendedApiServer, QUERY_MAX_RESULT_LIMIT_CHECKPOINTS}; +use iota_json_rpc_api::{ + internal_error, validate_limit, ExtendedApiServer, QUERY_MAX_RESULT_LIMIT_CHECKPOINTS, +}; use iota_json_rpc_types::{ CheckpointedObjectID, EpochInfo, EpochPage, IotaObjectResponseQuery, Page, QueryObjectsPage, }; @@ -31,7 +33,8 @@ impl ExtendedApiServer for ExtendedApi { limit: Option, descending_order: Option, ) -> RpcResult { - let limit = validate_limit(limit, QUERY_MAX_RESULT_LIMIT_CHECKPOINTS)?; + let limit = + validate_limit(limit, QUERY_MAX_RESULT_LIMIT_CHECKPOINTS).map_err(internal_error)?; let mut epochs = self .inner .spawn_blocking(move |this| { @@ -67,10 +70,7 @@ impl ExtendedApiServer for ExtendedApi { _cursor: Option, _limit: Option, ) -> RpcResult { - Err(jsonrpsee::types::error::CallError::Custom( - jsonrpsee::types::error::ErrorCode::MethodNotFound.into(), - ) - .into()) + Err(jsonrpsee::types::error::ErrorCode::MethodNotFound.into()) } async fn get_total_transactions(&self) -> RpcResult> { diff --git a/crates/iota-indexer/src/apis/governance_api.rs b/crates/iota-indexer/src/apis/governance_api.rs index 37a83d081c1..e94720e8131 100644 --- a/crates/iota-indexer/src/apis/governance_api.rs +++ b/crates/iota-indexer/src/apis/governance_api.rs @@ -296,7 +296,7 @@ fn stake_status( /// 1, it will be cleared when the epoch changes. rates are in descending order /// by epoch. #[cached( - type = "SizedCache>", + ty = "SizedCache>", create = "{ SizedCache::with_size(1) }", convert = "{ system_state_summary.epoch }", result = true @@ -391,7 +391,7 @@ async fn exchange_rates( /// Cache a map representing the validators' APYs for this epoch #[cached( - type = "SizedCache>", + ty = "SizedCache>", create = "{ SizedCache::with_size(1) }", convert = " {apys.epoch} " )] diff --git a/crates/iota-indexer/src/apis/indexer_api.rs b/crates/iota-indexer/src/apis/indexer_api.rs index 6ef245698f8..5edb575239a 100644 --- a/crates/iota-indexer/src/apis/indexer_api.rs +++ b/crates/iota-indexer/src/apis/indexer_api.rs @@ -7,10 +7,11 @@ use iota_json_rpc::{ name_service::{Domain, NameRecord, NameServiceConfig}, IotaRpcModule, }; -use iota_json_rpc_api::{cap_page_limit, IndexerApiServer}; +use iota_json_rpc_api::{cap_page_limit, internal_error, IndexerApiServer}; use iota_json_rpc_types::{ - DynamicFieldPage, EventFilter, EventPage, IotaObjectResponse, IotaObjectResponseQuery, - IotaTransactionBlockResponseQuery, ObjectsPage, Page, TransactionBlocksPage, TransactionFilter, + DynamicFieldPage, EventFilter, EventPage, IotaObjectData, IotaObjectDataOptions, + IotaObjectResponse, IotaObjectResponseQuery, IotaTransactionBlockResponseQuery, ObjectsPage, + Page, TransactionBlocksPage, TransactionFilter, }; use iota_open_rpc::Module; use iota_types::{ @@ -22,11 +23,7 @@ use iota_types::{ object::ObjectRead, TypeTag, }; -use jsonrpsee::{ - core::RpcResult, - types::{SubscriptionEmptyError, SubscriptionResult}, - RpcModule, SubscriptionSink, -}; +use jsonrpsee::{core::RpcResult, PendingSubscriptionSink, RpcModule}; use crate::{indexer_reader::IndexerReader, IndexerError}; @@ -70,52 +67,65 @@ impl IndexerApi { objects.truncate(limit); let next_cursor = objects.last().map(|o_read| o_read.object_id()); - let mut parallel_tasks = vec![]; - for o in objects { - let inner_clone = self.inner.clone(); - let options = options.clone(); - parallel_tasks.push(tokio::task::spawn(async move { - match o { - ObjectRead::NotExists(id) => Ok(IotaObjectResponse::new_with_error( - IotaObjectResponseError::NotExists { object_id: id }, - )), - ObjectRead::Exists(object_ref, o, layout) => { - if options.show_display { - match inner_clone.get_display_fields(&o, &layout).await { - Ok(rendered_fields) => Ok(IotaObjectResponse::new_with_data( - (object_ref, o, layout, options, Some(rendered_fields)) - .try_into()?, - )), - Err(e) => Ok(IotaObjectResponse::new( - Some((object_ref, o, layout, options, None).try_into()?), - Some(IotaObjectResponseError::DisplayError { - error: e.to_string(), - }), - )), + let mut parallel_tasks = Vec::with_capacity(objects.len()); + async fn check_read_obj( + obj: ObjectRead, + reader: IndexerReader, + options: IotaObjectDataOptions, + ) -> anyhow::Result { + match obj { + ObjectRead::NotExists(id) => Ok(IotaObjectResponse::new_with_error( + IotaObjectResponseError::NotExists { object_id: id }, + )), + ObjectRead::Exists(object_ref, o, layout) => { + if options.show_display { + match reader.get_display_fields(&o, &layout).await { + Ok(rendered_fields) => { + Ok(IotaObjectResponse::new_with_data(IotaObjectData::new( + object_ref, + o, + layout, + options, + rendered_fields, + )?)) } - } else { - Ok(IotaObjectResponse::new_with_data( - (object_ref, o, layout, options, None).try_into()?, - )) + Err(e) => Ok(IotaObjectResponse::new( + Some(IotaObjectData::new(object_ref, o, layout, options, None)?), + Some(IotaObjectResponseError::DisplayError { + error: e.to_string(), + }), + )), } + } else { + Ok(IotaObjectResponse::new_with_data(IotaObjectData::new( + object_ref, o, layout, options, None, + )?)) } - ObjectRead::Deleted((object_id, version, digest)) => Ok( - IotaObjectResponse::new_with_error(IotaObjectResponseError::Deleted { - object_id, - version, - digest, - }), - ), } - })); + ObjectRead::Deleted((object_id, version, digest)) => Ok( + IotaObjectResponse::new_with_error(IotaObjectResponseError::Deleted { + object_id, + version, + digest, + }), + ), + } + } + for obj in objects { + parallel_tasks.push(tokio::task::spawn(check_read_obj( + obj, + self.inner.clone(), + options.clone(), + ))); } let data = futures::future::join_all(parallel_tasks) .await .into_iter() .collect::, _>>() - .map_err(|e: tokio::task::JoinError| anyhow::anyhow!(e))? + .map_err(internal_error)? .into_iter() - .collect::, anyhow::Error>>()?; + .collect::, _>>() + .map_err(internal_error)?; Ok(Page { data, @@ -162,8 +172,7 @@ impl IndexerApiServer for IndexerApi { limit + 1, descending_order.unwrap_or(false), ) - .await - .map_err(|e: IndexerError| anyhow::anyhow!(e))?; + .await?; let has_next_page = results.len() > limit; results.truncate(limit); @@ -252,7 +261,8 @@ impl IndexerApiServer for IndexerApi { | iota_types::object::ObjectRead::Deleted(_) => {} iota_types::object::ObjectRead::Exists(object_ref, o, layout) => { return Ok(IotaObjectResponse::new_with_data( - (object_ref, o, layout, options, None).try_into()?, + IotaObjectData::new(object_ref, o, layout, options, None) + .map_err(internal_error)?, )); } } @@ -276,7 +286,8 @@ impl IndexerApiServer for IndexerApi { | iota_types::object::ObjectRead::Deleted(_) => {} iota_types::object::ObjectRead::Exists(object_ref, o, layout) => { return Ok(IotaObjectResponse::new_with_data( - (object_ref, o, layout, options, None).try_into()?, + IotaObjectData::new(object_ref, o, layout, options, None) + .map_err(internal_error)?, )); } } @@ -286,16 +297,13 @@ impl IndexerApiServer for IndexerApi { )) } - fn subscribe_event(&self, _sink: SubscriptionSink, _filter: EventFilter) -> SubscriptionResult { - Err(SubscriptionEmptyError) - } + async fn subscribe_event(&self, _sink: PendingSubscriptionSink, _filter: EventFilter) {} - fn subscribe_transaction( + async fn subscribe_transaction( &self, - _sink: SubscriptionSink, + _sink: PendingSubscriptionSink, _filter: TransactionFilter, - ) -> SubscriptionResult { - Err(SubscriptionEmptyError) + ) { } async fn resolve_name_service_address(&self, name: String) -> RpcResult> { diff --git a/crates/iota-indexer/src/apis/read_api.rs b/crates/iota-indexer/src/apis/read_api.rs index 1cf86ea9c78..4d36f377051 100644 --- a/crates/iota-indexer/src/apis/read_api.rs +++ b/crates/iota-indexer/src/apis/read_api.rs @@ -4,10 +4,10 @@ use async_trait::async_trait; use iota_json_rpc::{error::IotaRpcInputError, IotaRpcModule}; -use iota_json_rpc_api::{ReadApiServer, QUERY_MAX_RESULT_LIMIT}; +use iota_json_rpc_api::{internal_error, ReadApiServer, QUERY_MAX_RESULT_LIMIT}; use iota_json_rpc_types::{ Checkpoint, CheckpointId, CheckpointPage, IotaEvent, IotaGetPastObjectRequest, - IotaLoadedChildObjectsResponse, IotaObjectDataOptions, IotaObjectResponse, + IotaLoadedChildObjectsResponse, IotaObjectData, IotaObjectDataOptions, IotaObjectResponse, IotaPastObjectResponse, IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, ProtocolConfigResponse, }; @@ -84,7 +84,10 @@ impl ReadApiServer for ReadApi { Ok(rendered_fields) => display_fields = Some(rendered_fields), Err(e) => { return Ok(IotaObjectResponse::new( - Some((object_ref, o, layout, options, None).try_into()?), + Some( + IotaObjectData::new(object_ref, o, layout, options, None) + .map_err(internal_error)?, + ), Some(IotaObjectResponseError::DisplayError { error: e.to_string(), }), @@ -93,7 +96,8 @@ impl ReadApiServer for ReadApi { } } Ok(IotaObjectResponse::new_with_data( - (object_ref, o, layout, options, display_fields).try_into()?, + IotaObjectData::new(object_ref, o, layout, options, display_fields) + .map_err(internal_error)?, )) } ObjectRead::Deleted((object_id, version, digest)) => Ok( @@ -179,10 +183,7 @@ impl ReadApiServer for ReadApi { _version: SequenceNumber, _options: Option, ) -> RpcResult { - Err(jsonrpsee::types::error::CallError::Custom( - jsonrpsee::types::error::ErrorCode::MethodNotFound.into(), - ) - .into()) + Err(jsonrpsee::types::error::ErrorCode::MethodNotFound.into()) } async fn try_multi_get_past_objects( @@ -190,10 +191,7 @@ impl ReadApiServer for ReadApi { _past_objects: Vec, _options: Option, ) -> RpcResult> { - Err(jsonrpsee::types::error::CallError::Custom( - jsonrpsee::types::error::ErrorCode::MethodNotFound.into(), - ) - .into()) + Err(jsonrpsee::types::error::ErrorCode::MethodNotFound.into()) } async fn get_latest_checkpoint_sequence_number(&self) -> RpcResult> { @@ -202,7 +200,7 @@ impl ReadApiServer for ReadApi { } async fn get_checkpoint(&self, id: CheckpointId) -> RpcResult { - self.get_checkpoint(id).await.map_err(Into::into) + Ok(self.get_checkpoint(id).await?) } async fn get_checkpoints( @@ -260,10 +258,7 @@ impl ReadApiServer for ReadApi { &self, _digest: TransactionDigest, ) -> RpcResult { - Err(jsonrpsee::types::error::CallError::Custom( - jsonrpsee::types::error::ErrorCode::MethodNotFound.into(), - ) - .into()) + Err(jsonrpsee::types::error::ErrorCode::MethodNotFound.into()) } async fn get_protocol_config( diff --git a/crates/iota-indexer/src/apis/write_api.rs b/crates/iota-indexer/src/apis/write_api.rs index 40d57a3ca8c..e00f50c579d 100644 --- a/crates/iota-indexer/src/apis/write_api.rs +++ b/crates/iota-indexer/src/apis/write_api.rs @@ -5,7 +5,7 @@ use async_trait::async_trait; use fastcrypto::encoding::Base64; use iota_json_rpc::IotaRpcModule; -use iota_json_rpc_api::{WriteApiClient, WriteApiServer}; +use iota_json_rpc_api::{error_object_from_rpc, WriteApiClient, WriteApiServer}; use iota_json_rpc_types::{ DevInspectArgs, DevInspectResults, DryRunTransactionBlockResponse, IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, @@ -42,7 +42,8 @@ impl WriteApiServer for WriteApi { let iota_transaction_response = self .fullnode .execute_transaction_block(tx_bytes, signatures, options.clone(), request_type) - .await?; + .await + .map_err(error_object_from_rpc)?; Ok(IotaTransactionBlockResponseWithOptions { response: iota_transaction_response, options: options.unwrap_or_default(), @@ -67,13 +68,17 @@ impl WriteApiServer for WriteApi { additional_args, ) .await + .map_err(error_object_from_rpc) } async fn dry_run_transaction_block( &self, tx_bytes: Base64, ) -> RpcResult { - self.fullnode.dry_run_transaction_block(tx_bytes).await + self.fullnode + .dry_run_transaction_block(tx_bytes) + .await + .map_err(error_object_from_rpc) } } diff --git a/crates/iota-indexer/src/errors.rs b/crates/iota-indexer/src/errors.rs index 6f0299d1269..64226abd85d 100644 --- a/crates/iota-indexer/src/errors.rs +++ b/crates/iota-indexer/src/errors.rs @@ -4,11 +4,12 @@ use fastcrypto::error::FastCryptoError; use iota_json_rpc::name_service::NameServiceError; +use iota_json_rpc_api::{error_object_from_rpc, internal_error}; use iota_types::{ base_types::ObjectIDParseError, error::{IotaError, IotaObjectResponseError, UserInputError}, }; -use jsonrpsee::{core::Error as RpcError, types::error::CallError}; +use jsonrpsee::{core::ClientError as RpcError, types::ErrorObjectOwned}; use thiserror::Error; #[derive(Debug, Error)] @@ -146,7 +147,13 @@ impl Context for Result { impl From for RpcError { fn from(e: IndexerError) -> Self { - RpcError::Call(CallError::Failed(e.into())) + RpcError::Call(internal_error(e)) + } +} + +impl From for ErrorObjectOwned { + fn from(value: IndexerError) -> Self { + error_object_from_rpc(value.into()) } } diff --git a/crates/iota-indexer/src/indexer_reader.rs b/crates/iota-indexer/src/indexer_reader.rs index 05f96e35eaa..974f980dd94 100644 --- a/crates/iota-indexer/src/indexer_reader.rs +++ b/crates/iota-indexer/src/indexer_reader.rs @@ -1690,7 +1690,7 @@ impl move_bytecode_utils::module_cache::GetModule for IndexerReader { } #[cached( - type = "SizedCache>", + ty = "SizedCache>", create = "{ SizedCache::with_size(10000) }", convert = r#"{ format!("{}{}", package_id, obj_type) }"#, result = true diff --git a/crates/iota-indexer/src/lib.rs b/crates/iota-indexer/src/lib.rs index 05a2446d845..6431e147a2a 100644 --- a/crates/iota-indexer/src/lib.rs +++ b/crates/iota-indexer/src/lib.rs @@ -173,8 +173,7 @@ fn get_http_client(rpc_client_url: &str) -> Result { headers.insert(CLIENT_SDK_TYPE_HEADER, HeaderValue::from_static("indexer")); HttpClientBuilder::default() - .max_request_body_size(2 << 30) - .max_concurrent_requests(usize::MAX) + .max_request_size(2 << 30) .set_headers(headers.clone()) .build(rpc_client_url) .map_err(|e| { diff --git a/crates/iota-indexer/src/metrics.rs b/crates/iota-indexer/src/metrics.rs index 73cd36ae07b..804bd8c9e7f 100644 --- a/crates/iota-indexer/src/metrics.rs +++ b/crates/iota-indexer/src/metrics.rs @@ -38,8 +38,8 @@ pub fn start_prometheus_server( .layer(Extension(registry_service.clone())); tokio::spawn(async move { - axum::Server::bind(&addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app.into_make_service()) .await .unwrap(); }); diff --git a/crates/iota-json-rpc-api/src/indexer.rs b/crates/iota-json-rpc-api/src/indexer.rs index 6a2a9adf93a..99a1465ec2c 100644 --- a/crates/iota-json-rpc-api/src/indexer.rs +++ b/crates/iota-json-rpc-api/src/indexer.rs @@ -71,7 +71,7 @@ pub trait IndexerApi { /// Subscribe to a stream of Iota event #[rustfmt::skip] #[subscription(name = "subscribeEvent", item = IotaEvent)] - fn subscribe_event( + async fn subscribe_event( &self, /// The filter criteria of the event stream. See [Event filter](https://docs.iota.io/build/event_api#event-filters) documentation for examples. filter: EventFilter, @@ -79,7 +79,7 @@ pub trait IndexerApi { /// Subscribe to a stream of Iota transaction effects #[subscription(name = "subscribeTransaction", item = IotaTransactionBlockEffects)] - fn subscribe_transaction(&self, filter: TransactionFilter); + async fn subscribe_transaction(&self, filter: TransactionFilter); /// Return the list of dynamic field objects owned by an object. #[rustfmt::skip] diff --git a/crates/iota-json-rpc-api/src/lib.rs b/crates/iota-json-rpc-api/src/lib.rs index 4fc64c6fd18..e44501ff3de 100644 --- a/crates/iota-json-rpc-api/src/lib.rs +++ b/crates/iota-json-rpc-api/src/lib.rs @@ -7,6 +7,13 @@ pub use coin::{CoinReadApiClient, CoinReadApiOpenRpc, CoinReadApiServer}; pub use extended::{ExtendedApiClient, ExtendedApiOpenRpc, ExtendedApiServer}; pub use governance::{GovernanceReadApiClient, GovernanceReadApiOpenRpc, GovernanceReadApiServer}; pub use indexer::{IndexerApiClient, IndexerApiOpenRpc, IndexerApiServer}; +use jsonrpsee::{ + core::ClientError, + types::{ + error::{INTERNAL_ERROR_CODE, UNKNOWN_ERROR_CODE}, + ErrorObjectOwned, + }, +}; pub use move_utils::{MoveUtilsClient, MoveUtilsOpenRpc, MoveUtilsServer}; use mysten_metrics::histogram::Histogram; use once_cell::sync::Lazy; @@ -287,3 +294,16 @@ pub const CLIENT_TARGET_API_VERSION_HEADER: &str = "client-target-api-version"; pub const TRANSIENT_ERROR_CODE: i32 = -32050; pub const TRANSACTION_EXECUTION_CLIENT_ERROR_CODE: i32 = -32002; + +/// Convert a jsonrpsee client error into a generic error object. +pub fn error_object_from_rpc(rpc_err: ClientError) -> ErrorObjectOwned { + match rpc_err { + ClientError::Call(e) => ErrorObjectOwned::owned(e.code(), e.message().to_owned(), e.data()), + _ => ErrorObjectOwned::owned::<()>(UNKNOWN_ERROR_CODE, rpc_err.to_string(), None), + } +} + +/// Convert an internal error into a generic error object. +pub fn internal_error(err: impl ToString) -> ErrorObjectOwned { + ErrorObjectOwned::owned::<()>(INTERNAL_ERROR_CODE, err.to_string(), None) +} diff --git a/crates/iota-json-rpc-tests/tests/routing_tests.rs b/crates/iota-json-rpc-tests/tests/routing_tests.rs index 39044109fb4..c5a09fba202 100644 --- a/crates/iota-json-rpc-tests/tests/routing_tests.rs +++ b/crates/iota-json-rpc-tests/tests/routing_tests.rs @@ -35,7 +35,7 @@ async fn test_rpc_backward_compatibility() { // try to access old method directly should fail let client = HttpClientBuilder::default().build(&url).unwrap(); - let response: RpcResult = client.request("test_foo_1_5", rpc_params!("string")).await; + let response: Result = client.request("test_foo_1_5", rpc_params!("string")).await; assert!(response.is_err()); // Test with versioned client, version > backward compatible method version @@ -109,7 +109,7 @@ async fn test_disable_routing() { // try to access old method directly should fail let client = HttpClientBuilder::default().build(&url).unwrap(); - let response: RpcResult = client.request("test_foo_1_5", rpc_params!("string")).await; + let response: Result = client.request("test_foo_1_5", rpc_params!("string")).await; assert!(response.is_err()); // Test with versioned client, version = backward compatible method version, @@ -124,7 +124,7 @@ async fn test_disable_routing() { .build(&url) .unwrap(); - let response: RpcResult = client_with_new_header + let response: Result = client_with_new_header .request( "test_foo", rpc_params!("old version expect string as input"), @@ -133,76 +133,6 @@ async fn test_disable_routing() { assert!(response.is_err()); } -// TODO(chris): clean up this after March 27th, 2023 -// #[tokio::test] -// async fn test_rpc_backward_compatibility_batched_request() { -// let mut builder = JsonRpcServerBuilder::new("1.5", &Registry::new()); -// builder.register_module(TestApiModule).unwrap(); - -// let port = get_available_port("0.0.0.0"); -// let handle = builder -// .start(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port))) -// .await -// .unwrap(); -// let url = format!("http://0.0.0.0:{}", port); - -// // Test with un-versioned client -// let client = HttpClientBuilder::default().build(&url).unwrap(); - -// let mut builder = BatchRequestBuilder::default(); -// builder.insert("test_foo", rpc_params!(true)).unwrap(); -// builder.insert("test_foo", rpc_params!(true)).unwrap(); -// builder.insert("test_foo", rpc_params!(true)).unwrap(); - -// let response = client.batch_request::(builder).await.unwrap(); -// assert_eq!(3, response.num_successful_calls()); - -// // try to access old method directly should fail -// let mut builder = BatchRequestBuilder::default(); -// builder.insert("test_foo_1_5", rpc_params!(true)).unwrap(); -// builder.insert("test_foo", rpc_params!(true)).unwrap(); -// builder.insert("test_foo", rpc_params!(true)).unwrap(); - -// let response = client.batch_request::(builder).await.unwrap(); -// assert_eq!(2, response.num_successful_calls()); - -// // One malformed request shouldn't fail the whole batch -// let client = Client::new(); -// let response = client -// .post(format!("http://127.0.0.1:{}/", port)) -// .json(&vec![ -// json!(&Request { -// jsonrpc: Default::default(), -// id: Id::Number(1), -// method: "test_foo".into(), -// params: -// Some(&JsonRawValue::from_string("[true]".into()).unwrap()), }), -// json!("Bad json input"), -// ]) -// .send() -// .await -// .unwrap(); - -// let responses = response.text().await.unwrap(); -// let responses: Vec<&JsonRawValue> = -// serde_json::from_str(&responses).unwrap(); - -// // Should have 2 results -// assert_eq!(2, responses.len()); - -// // First response should success -// let response = -// serde_json::from_str::>(responses[0].get()); assert! -// (matches!(response, Ok(result) if result.result == "Some string")); - -// // Second response should fail -// let response = serde_json::from_str::(responses[1].get()); -// assert!(matches!(response, Ok(result) if result.error_object().message() -// == "Invalid request")); - -// handle.stop().unwrap() -// } - #[open_rpc(namespace = "test")] #[rpc(server, client, namespace = "test")] trait TestApi { diff --git a/crates/iota-json-rpc-types/src/iota_extended.rs b/crates/iota-json-rpc-types/src/iota_extended.rs index bf4e440d6b9..76580170e95 100644 --- a/crates/iota-json-rpc-types/src/iota_extended.rs +++ b/crates/iota-json-rpc-types/src/iota_extended.rs @@ -22,7 +22,7 @@ use crate::Page; pub type EpochPage = Page>; #[serde_as] -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct EpochInfo { /// epoch number @@ -58,7 +58,7 @@ impl EpochInfo { } #[serde_as] -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct EndOfEpochInfo { #[schemars(with = "BigInt")] diff --git a/crates/iota-json-rpc-types/src/iota_move.rs b/crates/iota-json-rpc-types/src/iota_move.rs index df886696c16..46be46da5df 100644 --- a/crates/iota-json-rpc-types/src/iota_move.rs +++ b/crates/iota-json-rpc-types/src/iota_move.rs @@ -39,7 +39,7 @@ pub type IotaMoveTypeParameterIndex = u16; #[path = "unit_tests/iota_move_tests.rs"] mod iota_move_tests; -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema)] pub enum IotaMoveAbility { Copy, Drop, @@ -47,33 +47,33 @@ pub enum IotaMoveAbility { Key, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] pub struct IotaMoveAbilitySet { pub abilities: Vec, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema)] pub enum IotaMoveVisibility { Private, Public, Friend, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct IotaMoveStructTypeParameter { pub constraints: IotaMoveAbilitySet, pub is_phantom: bool, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] pub struct IotaMoveNormalizedField { pub name: String, #[serde(rename = "type")] pub type_: IotaMoveNormalizedType, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct IotaMoveNormalizedStruct { pub abilities: IotaMoveAbilitySet, @@ -81,7 +81,7 @@ pub struct IotaMoveNormalizedStruct { pub fields: Vec, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] pub enum IotaMoveNormalizedType { Bool, U8, @@ -105,7 +105,7 @@ pub enum IotaMoveNormalizedType { MutableReference(Box), } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct IotaMoveNormalizedFunction { pub visibility: IotaMoveVisibility, @@ -115,13 +115,13 @@ pub struct IotaMoveNormalizedFunction { pub return_: Vec, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] pub struct IotaMoveModuleId { address: String, name: String, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct IotaMoveNormalizedModule { pub file_format_version: u32, @@ -292,14 +292,14 @@ impl From for IotaMoveAbilitySet { } } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema)] pub enum ObjectValueKind { ByImmutableReference, ByMutableReference, ByValue, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Copy, Clone, Debug, JsonSchema)] pub enum MoveFunctionArgType { Pure, Object(ObjectValueKind), diff --git a/crates/iota-json-rpc-types/src/iota_object.rs b/crates/iota-json-rpc-types/src/iota_object.rs index 5ab605d1b2b..a29fba82a80 100644 --- a/crates/iota-json-rpc-types/src/iota_object.rs +++ b/crates/iota-json-rpc-types/src/iota_object.rs @@ -219,6 +219,88 @@ pub struct IotaObjectData { } impl IotaObjectData { + pub fn new( + object_ref: ObjectRef, + obj: Object, + layout: impl Into>, + options: IotaObjectDataOptions, + display_fields: impl Into>, + ) -> anyhow::Result { + let layout = layout.into(); + let display_fields = display_fields.into(); + let show_display = options.show_display; + let IotaObjectDataOptions { + show_type, + show_owner, + show_previous_transaction, + show_content, + show_bcs, + show_storage_rebate, + .. + } = options; + + let (object_id, version, digest) = object_ref; + let type_ = if show_type { + Some(Into::::into(&obj)) + } else { + None + }; + + let bcs: Option = if show_bcs { + let data = match obj.data.clone() { + Data::Move(m) => { + let layout = layout.clone().ok_or_else(|| { + anyhow!("Layout is required to convert Move object to json") + })?; + IotaRawData::try_from_object(m, layout)? + } + Data::Package(p) => IotaRawData::try_from_package(p) + .map_err(|e| anyhow!("Error getting raw data from package: {e:#?}"))?, + }; + Some(data) + } else { + None + }; + + let obj = obj.into_inner(); + + let content: Option = if show_content { + let data = match obj.data { + Data::Move(m) => { + let layout = layout.ok_or_else(|| { + anyhow!("Layout is required to convert Move object to json") + })?; + IotaParsedData::try_from_object(m, layout)? + } + Data::Package(p) => IotaParsedData::try_from_package(p)?, + }; + Some(data) + } else { + None + }; + + Ok(IotaObjectData { + object_id, + version, + digest, + type_, + owner: if show_owner { Some(obj.owner) } else { None }, + storage_rebate: if show_storage_rebate { + Some(obj.storage_rebate) + } else { + None + }, + previous_transaction: if show_previous_transaction { + Some(obj.previous_transaction) + } else { + None + }, + content, + bcs, + display: if show_display { display_fields } else { None }, + }) + } + pub fn object_ref(&self) -> ObjectRef { (self.object_id, self.version, self.digest) } @@ -426,10 +508,9 @@ impl TryFrom<(ObjectRead, IotaObjectDataOptions)> for IotaObjectResponse { ObjectRead::NotExists(id) => Ok(IotaObjectResponse::new_with_error( IotaObjectResponseError::NotExists { object_id: id }, )), - ObjectRead::Exists(object_ref, o, layout) => { - let data = (object_ref, o, layout, options).try_into()?; - Ok(IotaObjectResponse::new_with_data(data)) - } + ObjectRead::Exists(object_ref, o, layout) => Ok(IotaObjectResponse::new_with_data( + IotaObjectData::new(object_ref, o, layout, options, None)?, + )), ObjectRead::Deleted((object_id, version, digest)) => Ok( IotaObjectResponse::new_with_error(IotaObjectResponseError::Deleted { object_id, @@ -470,126 +551,6 @@ impl TryFrom<(ObjectInfo, IotaObjectDataOptions)> for IotaObjectResponse { } } -impl - TryFrom<( - ObjectRef, - Object, - Option, - IotaObjectDataOptions, - )> for IotaObjectData -{ - type Error = anyhow::Error; - - fn try_from( - (object_ref, o, layout, options): ( - ObjectRef, - Object, - Option, - IotaObjectDataOptions, - ), - ) -> Result { - let IotaObjectDataOptions { - show_type, - show_owner, - show_previous_transaction, - show_content, - show_bcs, - show_storage_rebate, - .. - } = options; - - let (object_id, version, digest) = object_ref; - let type_ = if show_type { - Some(Into::::into(&o)) - } else { - None - }; - - let bcs: Option = if show_bcs { - let data = match o.data.clone() { - Data::Move(m) => { - let layout = layout.clone().ok_or_else(|| { - anyhow!("Layout is required to convert Move object to json") - })?; - IotaRawData::try_from_object(m, layout)? - } - Data::Package(p) => IotaRawData::try_from_package(p) - .map_err(|e| anyhow!("Error getting raw data from package: {e:#?}"))?, - }; - Some(data) - } else { - None - }; - - let o = o.into_inner(); - - let content: Option = if show_content { - let data = match o.data { - Data::Move(m) => { - let layout = layout.ok_or_else(|| { - anyhow!("Layout is required to convert Move object to json") - })?; - IotaParsedData::try_from_object(m, layout)? - } - Data::Package(p) => IotaParsedData::try_from_package(p)?, - }; - Some(data) - } else { - None - }; - - Ok(IotaObjectData { - object_id, - version, - digest, - type_, - owner: if show_owner { Some(o.owner) } else { None }, - storage_rebate: if show_storage_rebate { - Some(o.storage_rebate) - } else { - None - }, - previous_transaction: if show_previous_transaction { - Some(o.previous_transaction) - } else { - None - }, - content, - bcs, - display: None, - }) - } -} - -impl - TryFrom<( - ObjectRef, - Object, - Option, - IotaObjectDataOptions, - Option, - )> for IotaObjectData -{ - type Error = anyhow::Error; - - fn try_from( - (object_ref, o, layout, options, display_fields): ( - ObjectRef, - Object, - Option, - IotaObjectDataOptions, - Option, - ), - ) -> Result { - let show_display = options.show_display; - let mut data: IotaObjectData = (object_ref, o, layout, options).try_into()?; - if show_display { - data.display = display_fields; - } - Ok(data) - } -} - impl IotaObjectResponse { /// Returns a reference to the object if there is any, otherwise an Err if /// the object does not exist or is deleted. diff --git a/crates/iota-json-rpc-types/src/iota_transaction.rs b/crates/iota-json-rpc-types/src/iota_transaction.rs index eadfe254f19..a54e0a84581 100644 --- a/crates/iota-json-rpc-types/src/iota_transaction.rs +++ b/crates/iota-json-rpc-types/src/iota_transaction.rs @@ -1950,7 +1950,7 @@ pub struct MoveCallParams { } #[serde_as] -#[derive(Serialize, Deserialize, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct TransactionBlockBytes { /// BCS serialized transaction data bytes without its type tag, as base-64 diff --git a/crates/iota-json-rpc/Cargo.toml b/crates/iota-json-rpc/Cargo.toml index 4b00943549d..6cc8a070668 100644 --- a/crates/iota-json-rpc/Cargo.toml +++ b/crates/iota-json-rpc/Cargo.toml @@ -15,6 +15,7 @@ bcs.workspace = true eyre.workspace = true fastcrypto.workspace = true futures.workspace = true +http-body = "1.0.0" hyper.workspace = true indexmap.workspace = true itertools.workspace = true diff --git a/crates/iota-json-rpc/src/axum_router.rs b/crates/iota-json-rpc/src/axum_router.rs index 462d79eb8d0..07daa3d4b9d 100644 --- a/crates/iota-json-rpc/src/axum_router.rs +++ b/crates/iota-json-rpc/src/axum_router.rs @@ -4,27 +4,27 @@ use std::sync::Arc; -use axum::extract::{Json, State}; -use futures::StreamExt; +use axum::{ + body::Body, + extract::{Json, State}, +}; use hyper::HeaderMap; use iota_json_rpc_api::CLIENT_TARGET_API_VERSION_HEADER; use jsonrpsee::{ - core::server::{ - helpers::{BoundedSubscriptions, MethodResponse, MethodSink}, - rpc_module::{MethodKind, Methods}, - }, - server::{ - logger::{self, Logger, TransportProtocol}, - RandomIntegerIdProvider, - }, + core::server::{helpers::MethodSink, Methods}, + server::RandomIntegerIdProvider, types::{ error::{ErrorCode, BATCHES_NOT_SUPPORTED_CODE, BATCHES_NOT_SUPPORTED_MSG}, ErrorObject, Id, InvalidRequest, Params, Request, }, + BoundedSubscriptions, ConnectionId, Extensions, MethodCallback, MethodKind, MethodResponse, }; use serde_json::value::RawValue; -use crate::routing_layer::RpcRouter; +use crate::{ + logger::{Logger, TransportProtocol}, + routing_layer::RpcRouter, +}; pub const MAX_RESPONSE_SIZE: u32 = 2 << 30; @@ -36,15 +36,17 @@ pub struct JsonRpcService { /// Registered server methods. methods: Methods, + extensions: Extensions, rpc_router: RpcRouter, } impl JsonRpcService { - pub fn new(methods: Methods, rpc_router: RpcRouter, logger: L) -> Self { + pub fn new(methods: Methods, rpc_router: RpcRouter, logger: L, extensions: Extensions) -> Self { Self { methods, rpc_router, logger, + extensions, id_provider: Arc::new(RandomIntegerIdProvider), } } @@ -56,6 +58,7 @@ impl JsonRpcService { logger: &self.logger, methods: &self.methods, rpc_router: &self.rpc_router, + extensions: &self.extensions, max_response_body_size: MAX_RESPONSE_SIZE, request_start: self.logger.on_request(TransportProtocol::Http), } @@ -69,6 +72,7 @@ impl JsonRpcService { ws::WsCallData { logger: &self.logger, methods: &self.methods, + extensions: &self.extensions, max_response_body_size: MAX_RESPONSE_SIZE, request_start: self.logger.on_request(TransportProtocol::Http), bounded_subscriptions, @@ -79,11 +83,11 @@ impl JsonRpcService { } /// Create a response body. -fn from_template>( +fn from_template>( status: hyper::StatusCode, body: S, content_type: &'static str, -) -> hyper::Response { +) -> hyper::Response { hyper::Response::builder() .status(status) .header( @@ -97,7 +101,7 @@ fn from_template>( } /// Create a valid JSON response. -pub(crate) fn ok_response(body: String) -> hyper::Response { +pub(crate) fn ok_response(body: String) -> hyper::Response { const JSON: &str = "application/json; charset=utf-8"; from_template(hyper::StatusCode::OK, body, JSON) } @@ -113,7 +117,7 @@ pub async fn json_rpc_handler( .and_then(|h| h.to_str().ok()); let response = process_raw_request(&service, api_version, raw_request.get()).await; - ok_response(response.result) + ok_response(response.into_result()) } async fn process_raw_request( @@ -126,7 +130,7 @@ async fn process_raw_request( } else if let Ok(_batch) = serde_json::from_str::>(raw_request) { MethodResponse::error( Id::Null, - ErrorObject::borrowed(BATCHES_NOT_SUPPORTED_CODE, &BATCHES_NOT_SUPPORTED_MSG, None), + ErrorObject::borrowed(BATCHES_NOT_SUPPORTED_CODE, BATCHES_NOT_SUPPORTED_MSG, None), ) } else { let (id, code) = prepare_error(raw_request); @@ -143,12 +147,13 @@ async fn process_request( methods, rpc_router, logger, + extensions, max_response_body_size, request_start, } = call; - let conn_id = 0; // unused + let conn_id = ConnectionId(0); // unused - let params = Params::new(req.params.map(|params| params.get())); + let params = Params::new(req.params.as_ref().map(|params| params.get())); let name = rpc_router.route(&req.method, api_version); let id = req.id; @@ -157,39 +162,51 @@ async fn process_request( logger.on_call( name, params.clone(), - logger::MethodKind::Unknown, + MethodKind::NotFound, TransportProtocol::Http, ); MethodResponse::error(id, ErrorObject::from(ErrorCode::MethodNotFound)) } - Some((name, method)) => match method.inner() { - MethodKind::Sync(callback) => { + Some((name, method)) => match method { + MethodCallback::Sync(callback) => { logger.on_call( name, params.clone(), - logger::MethodKind::MethodCall, + MethodKind::MethodCall, TransportProtocol::Http, ); - (callback)(id, params, max_response_body_size as usize) + (callback)( + id, + params, + max_response_body_size as usize, + extensions.clone(), + ) } - MethodKind::Async(callback) => { + MethodCallback::Async(callback) => { logger.on_call( name, params.clone(), - logger::MethodKind::MethodCall, + MethodKind::MethodCall, TransportProtocol::Http, ); let id = id.into_owned(); let params = params.into_owned(); - (callback)(id, params, conn_id, max_response_body_size as usize, None).await + (callback)( + id, + params, + conn_id, + max_response_body_size as usize, + extensions.clone(), + ) + .await } - MethodKind::Subscription(_) | MethodKind::Unsubscription(_) => { + MethodCallback::Subscription(_) | MethodCallback::Unsubscription(_) => { logger.on_call( name, params.clone(), - logger::MethodKind::Unknown, + MethodKind::NotFound, TransportProtocol::Http, ); // Subscriptions not supported on HTTP @@ -200,8 +217,8 @@ async fn process_request( logger.on_result( name, - response.success, - response.error_code, + response.is_success(), + response.as_error_code(), request_start, TransportProtocol::Http, ); @@ -222,6 +239,7 @@ pub(crate) struct CallData<'a, L: Logger> { logger: &'a L, methods: &'a Methods, rpc_router: &'a RpcRouter, + extensions: &'a Extensions, max_response_body_size: u32, request_start: L::Instant, } @@ -234,23 +252,22 @@ pub mod ws { }, response::Response, }; - use futures::channel::mpsc; use jsonrpsee::{ - core::server::{ - helpers::{BoundedSubscriptions, MethodSink}, - rpc_module::ConnState, - }, - server::IdProvider, - types::error::reject_too_many_subscriptions, + core::server::helpers::MethodSink, server::IdProvider, + types::error::reject_too_many_subscriptions, SubscriptionState, }; + use tokio::sync::mpsc; use super::*; + const MAX_WS_MESSAGE_BUFFER: usize = 100; + #[derive(Debug, Clone)] pub(crate) struct WsCallData<'a, L: Logger> { pub bounded_subscriptions: BoundedSubscriptions, pub id_provider: &'a dyn IdProvider, pub methods: &'a Methods, + pub extensions: &'a Extensions, pub max_response_body_size: u32, pub sink: &'a MethodSink, pub logger: &'a L, @@ -270,8 +287,8 @@ pub mod ws { async fn ws_json_rpc_handler(mut socket: WebSocket, service: JsonRpcService) { #[allow(clippy::disallowed_methods)] - let (tx, mut rx) = mpsc::unbounded::(); - let sink = MethodSink::new_with_limit(tx, MAX_RESPONSE_SIZE, MAX_RESPONSE_SIZE); + let (tx, mut rx) = mpsc::channel::(MAX_WS_MESSAGE_BUFFER); + let sink = MethodSink::new_with_limit(tx, MAX_RESPONSE_SIZE); let bounded_subscriptions = BoundedSubscriptions::new(100); loop { @@ -282,14 +299,14 @@ pub mod ws { let response = process_raw_request(&service, &msg, bounded_subscriptions.clone(), &sink).await; if let Some(response) = response { - let _ = sink.send_raw(response.result); + sink.send(response.into_result()).await.ok(); } } } else { break; } }, - Some(response) = rx.next() => { + Some(response) = rx.recv() => { if socket.send(Message::Text(response)).await.is_err() { break; } @@ -309,7 +326,7 @@ pub mod ws { } else if let Ok(_batch) = serde_json::from_str::>(raw_request) { Some(MethodResponse::error( Id::Null, - ErrorObject::borrowed(BATCHES_NOT_SUPPORTED_CODE, &BATCHES_NOT_SUPPORTED_MSG, None), + ErrorObject::borrowed(BATCHES_NOT_SUPPORTED_CODE, BATCHES_NOT_SUPPORTED_MSG, None), )) } else { let (id, code) = prepare_error(raw_request); @@ -324,15 +341,16 @@ pub mod ws { let WsCallData { methods, logger, + extensions, max_response_body_size, request_start, bounded_subscriptions, id_provider, sink, } = call; - let conn_id = 0; // unused + let conn_id = ConnectionId(0); // unused - let params = Params::new(req.params.map(|params| params.get())); + let params = Params::new(req.params.as_ref().map(|params| params.get())); let name = &req.method; let id = req.id; @@ -341,7 +359,7 @@ pub mod ws { logger.on_call( name, params.clone(), - logger::MethodKind::Unknown, + MethodKind::NotFound, TransportProtocol::Http, ); Some(MethodResponse::error( @@ -349,47 +367,67 @@ pub mod ws { ErrorObject::from(ErrorCode::MethodNotFound), )) } - Some((name, method)) => match method.inner() { - MethodKind::Sync(callback) => { + Some((name, method)) => match method { + MethodCallback::Sync(callback) => { logger.on_call( name, params.clone(), - logger::MethodKind::MethodCall, + MethodKind::MethodCall, TransportProtocol::Http, ); - Some((callback)(id, params, max_response_body_size as usize)) + tracing::info!("calling {name} sync"); + Some((callback)( + id, + params, + max_response_body_size as usize, + extensions.clone(), + )) } - MethodKind::Async(callback) => { + MethodCallback::Async(callback) => { logger.on_call( name, params.clone(), - logger::MethodKind::MethodCall, + MethodKind::MethodCall, TransportProtocol::Http, ); let id = id.into_owned(); let params = params.into_owned(); + tracing::info!("calling {name} async"); Some( - (callback)(id, params, conn_id, max_response_body_size as usize, None) - .await, + (callback)( + id, + params, + conn_id, + max_response_body_size as usize, + extensions.clone(), + ) + .await, ) } - MethodKind::Subscription(callback) => { + MethodCallback::Subscription(callback) => { logger.on_call( name, params.clone(), - logger::MethodKind::Subscription, + MethodKind::Subscription, TransportProtocol::WebSocket, ); - if let Some(cn) = bounded_subscriptions.acquire() { - let conn_state = ConnState { + if let Some(subscription_permit) = bounded_subscriptions.acquire() { + let conn_state = SubscriptionState { conn_id, - close_notify: cn, + subscription_permit, id_provider, }; - callback(id.clone(), params, sink.clone(), conn_state, None).await; + (callback)( + id.clone(), + params, + sink.clone(), + conn_state, + extensions.clone(), + ) + .await; None } else { Some(MethodResponse::error( @@ -399,11 +437,11 @@ pub mod ws { } } - MethodKind::Unsubscription(callback) => { + MethodCallback::Unsubscription(callback) => { logger.on_call( name, params.clone(), - logger::MethodKind::Unsubscription, + MethodKind::Unsubscription, TransportProtocol::WebSocket, ); @@ -412,6 +450,7 @@ pub mod ws { params, conn_id, max_response_body_size as usize, + extensions.clone(), )) } }, @@ -420,8 +459,8 @@ pub mod ws { if let Some(response) = &response { logger.on_result( name, - response.success, - response.error_code, + response.is_success(), + response.as_error_code(), request_start, TransportProtocol::WebSocket, ); diff --git a/crates/iota-json-rpc/src/coin_api.rs b/crates/iota-json-rpc/src/coin_api.rs index 8781f055de5..238e6ba1ff4 100644 --- a/crates/iota-json-rpc/src/coin_api.rs +++ b/crates/iota-json-rpc/src/coin_api.rs @@ -27,12 +27,13 @@ use mockall::automock; use move_core_types::language_storage::{StructTag, TypeTag}; use mysten_metrics::spawn_monitored_task; use tap::TapFallible; -use tracing::{debug, info, instrument}; +use tracing::{debug, instrument}; use crate::{ authority_state::StateRead, error::{Error, IotaRpcInputError, RpcInterimResult}, - with_tracing, IotaRpcModule, + logger::with_tracing, + IotaRpcModule, }; pub fn parse_to_struct_tag(coin_type: &str) -> Result { @@ -89,22 +90,26 @@ impl CoinReadApiServer for CoinReadApi { cursor: Option, limit: Option, ) -> RpcResult { - with_tracing!(async move { - let coin_type_tag = parse_to_type_tag(coin_type)?; - - let cursor = match cursor { - Some(c) => (coin_type_tag.to_string(), c), - // If cursor is not specified, we need to start from the beginning of the coin type, - // which is the minimal possible ObjectID. - None => (coin_type_tag.to_string(), ObjectID::ZERO), - }; - - self.internal - .get_coins_iterator( - owner, cursor, limit, true, // only care about one type of coin - ) - .await - }) + with_tracing( + async move { + let coin_type_tag = parse_to_type_tag(coin_type)?; + + let cursor = match cursor { + Some(c) => (coin_type_tag.to_string(), c), + // If cursor is not specified, we need to start from the beginning of the coin + // type, which is the minimal possible ObjectID. + None => (coin_type_tag.to_string(), ObjectID::ZERO), + }; + + self.internal + .get_coins_iterator( + owner, cursor, limit, true, // only care about one type of coin + ) + .await + }, + None, + ) + .await } #[instrument(skip(self))] @@ -115,41 +120,45 @@ impl CoinReadApiServer for CoinReadApi { cursor: Option, limit: Option, ) -> RpcResult { - with_tracing!(async move { - let cursor = match cursor { - Some(object_id) => { - let obj = self.internal.get_object(&object_id).await?; - match obj { - Some(obj) => { - let coin_type = obj.coin_type_maybe(); - if coin_type.is_none() { - Err(IotaRpcInputError::GenericInvalid( - "cursor is not a coin".to_string(), - )) - } else { - Ok((coin_type.unwrap().to_string(), object_id)) + with_tracing( + async move { + let cursor = match cursor { + Some(object_id) => { + let obj = self.internal.get_object(&object_id).await?; + match obj { + Some(obj) => { + let coin_type = obj.coin_type_maybe(); + if coin_type.is_none() { + Err(IotaRpcInputError::GenericInvalid( + "cursor is not a coin".to_string(), + )) + } else { + Ok((coin_type.unwrap().to_string(), object_id)) + } } + None => Err(IotaRpcInputError::GenericInvalid( + "cursor not found".to_string(), + )), } - None => Err(IotaRpcInputError::GenericInvalid( - "cursor not found".to_string(), - )), } - } - None => { - // If cursor is None, start from the beginning - Ok((String::from_utf8([0u8].to_vec()).unwrap(), ObjectID::ZERO)) - } - }?; + None => { + // If cursor is None, start from the beginning + Ok((String::from_utf8([0u8].to_vec()).unwrap(), ObjectID::ZERO)) + } + }?; - let coins = self - .internal - .get_coins_iterator( - owner, cursor, limit, false, // return all types of coins - ) - .await?; + let coins = self + .internal + .get_coins_iterator( + owner, cursor, limit, false, // return all types of coins + ) + .await?; - Ok(coins) - }) + Ok(coins) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -158,95 +167,111 @@ impl CoinReadApiServer for CoinReadApi { owner: IotaAddress, coin_type: Option, ) -> RpcResult { - with_tracing!(async move { - let coin_type_tag = parse_to_type_tag(coin_type)?; - let balance = self - .internal - .get_balance(owner, coin_type_tag.clone()) - .await - .tap_err(|e| { - debug!(?owner, "Failed to get balance with error: {:?}", e); - })?; - Ok(Balance { - coin_type: coin_type_tag.to_string(), - coin_object_count: balance.num_coins as usize, - total_balance: balance.balance as u128, - // note: LockedCoin is deprecated - locked_balance: Default::default(), - }) - }) + with_tracing( + async move { + let coin_type_tag = parse_to_type_tag(coin_type)?; + let balance = self + .internal + .get_balance(owner, coin_type_tag.clone()) + .await + .tap_err(|e| { + debug!(?owner, "Failed to get balance with error: {:?}", e); + })?; + Ok(Balance { + coin_type: coin_type_tag.to_string(), + coin_object_count: balance.num_coins as usize, + total_balance: balance.balance as u128, + // note: LockedCoin is deprecated + locked_balance: Default::default(), + }) + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_all_balances(&self, owner: IotaAddress) -> RpcResult> { - with_tracing!(async move { - let all_balance = self.internal.get_all_balance(owner).await.tap_err(|e| { - debug!(?owner, "Failed to get all balance with error: {:?}", e); - })?; - Ok(all_balance - .iter() - .map(|(coin_type, balance)| { - Balance { - coin_type: coin_type.to_string(), - coin_object_count: balance.num_coins as usize, - total_balance: balance.balance as u128, - // note: LockedCoin is deprecated - locked_balance: Default::default(), - } - }) - .collect()) - }) + with_tracing( + async move { + let all_balance = self.internal.get_all_balance(owner).await.tap_err(|e| { + debug!(?owner, "Failed to get all balance with error: {:?}", e); + })?; + Ok(all_balance + .iter() + .map(|(coin_type, balance)| { + Balance { + coin_type: coin_type.to_string(), + coin_object_count: balance.num_coins as usize, + total_balance: balance.balance as u128, + // note: LockedCoin is deprecated + locked_balance: Default::default(), + } + }) + .collect()) + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_coin_metadata(&self, coin_type: String) -> RpcResult> { - with_tracing!(async move { - let coin_struct = parse_to_struct_tag(&coin_type)?; - let metadata_object = self - .internal - .find_package_object( - &coin_struct.address.into(), - CoinMetadata::type_(coin_struct), - ) - .await - .ok(); - Ok(metadata_object.and_then(|v: Object| v.try_into().ok())) - }) + with_tracing( + async move { + let coin_struct = parse_to_struct_tag(&coin_type)?; + let metadata_object = self + .internal + .find_package_object( + &coin_struct.address.into(), + CoinMetadata::type_(coin_struct), + ) + .await + .ok(); + Ok(metadata_object.and_then(|v: Object| v.try_into().ok())) + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_total_supply(&self, coin_type: String) -> RpcResult { - with_tracing!(async move { - let coin_struct = parse_to_struct_tag(&coin_type)?; - Ok(if GAS::is_gas(&coin_struct) { - let system_state_summary = self - .internal - .get_state() - .get_system_state()? - .into_iota_system_state_summary(); - Supply { - value: system_state_summary.iota_total_supply, - } - } else { - let treasury_cap_object = self - .internal - .find_package_object( - &coin_struct.address.into(), - TreasuryCap::type_(coin_struct), + with_tracing( + async move { + let coin_struct = parse_to_struct_tag(&coin_type)?; + Ok(if GAS::is_gas(&coin_struct) { + let system_state_summary = self + .internal + .get_state() + .get_system_state()? + .into_iota_system_state_summary(); + Supply { + value: system_state_summary.iota_total_supply, + } + } else { + let treasury_cap_object = self + .internal + .find_package_object( + &coin_struct.address.into(), + TreasuryCap::type_(coin_struct), + ) + .await?; + let treasury_cap = TreasuryCap::from_bcs_bytes( + treasury_cap_object.data.try_as_move().unwrap().contents(), ) - .await?; - let treasury_cap = TreasuryCap::from_bcs_bytes( - treasury_cap_object.data.try_as_move().unwrap().contents(), - ) - .map_err(Error::from)?; - treasury_cap.total_supply - }) - }) + .map_err(Error::from)?; + treasury_cap.total_supply + }) + }, + None, + ) + .await } } #[cached( - type = "SizedCache", + ty = "SizedCache", create = "{ SizedCache::with_size(10000) }", convert = r#"{ format!("{}{}", package_id, object_struct_tag) }"#, result = true @@ -428,7 +453,6 @@ mod tests { utils::create_fake_transaction, TypeTag, }; - use jsonrpsee::types::ErrorObjectOwned; use mockall::{mock, predicate}; use move_core_types::{account_address::AccountAddress, language_storage::StructTag}; @@ -567,7 +591,6 @@ mod tests { } mod get_coins_tests { - use jsonrpsee::types::ErrorObjectOwned; use super::{super::*, *}; @@ -735,13 +758,12 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); let expected = expect!["-32602"]; - expected.assert_eq(&error_object.code().to_string()); + expected.assert_eq(&error_result.code().to_string()); let expected = expect![ "Invalid struct type: 0x2::invalid::struct::tag. Got error: Expected end of token stream. Got: ::" ]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } #[tokio::test] @@ -756,12 +778,11 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); let expected = expect!["-32602"]; - expected.assert_eq(&error_object.code().to_string()); + expected.assert_eq(&error_result.code().to_string()); let expected = expect!["Invalid struct type: 0x2::iota:🤵. Got error: unrecognized token: :🤵"]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } // Unexpected error scenarios @@ -784,13 +805,12 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); assert_eq!( - error_object.code(), + error_result.code(), jsonrpsee::types::error::INVALID_PARAMS_CODE ); let expected = expect!["Index store not available on this Fullnode."]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } #[tokio::test] @@ -810,13 +830,12 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); assert_eq!( - error_object.code(), + error_result.code(), jsonrpsee::types::error::INTERNAL_ERROR_CODE ); let expected = expect!["Storage error: mock rocksdb error"]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } } @@ -913,12 +932,11 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); - assert_eq!(error_object.code(), -32602); + assert_eq!(error_result.code(), -32602); let expected = expect!["-32602"]; - expected.assert_eq(&error_object.code().to_string()); + expected.assert_eq(&error_result.code().to_string()); let expected = expect!["cursor is not a coin"]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } #[tokio::test] @@ -935,16 +953,14 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); let expected = expect!["-32602"]; - expected.assert_eq(&error_object.code().to_string()); + expected.assert_eq(&error_result.code().to_string()); let expected = expect!["cursor not found"]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } } mod get_balance_tests { - use jsonrpsee::types::ErrorObjectOwned; use super::{super::*, *}; // Success scenarios @@ -1031,13 +1047,12 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); let expected = expect!["-32602"]; - expected.assert_eq(&error_object.code().to_string()); + expected.assert_eq(&error_result.code().to_string()); let expected = expect![ "Invalid struct type: 0x2::invalid::struct::tag. Got error: Expected end of token stream. Got: ::" ]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } // Unexpected error scenarios @@ -1058,13 +1073,12 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); assert_eq!( - error_object.code(), + error_result.code(), jsonrpsee::types::error::INVALID_PARAMS_CODE ); let expected = expect!["Index store not available on this Fullnode."]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } #[tokio::test] @@ -1084,19 +1098,17 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); assert_eq!( - error_object.code(), + error_result.code(), jsonrpsee::types::error::INTERNAL_ERROR_CODE ); let expected = expect!["Error executing mock db error"]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } } mod get_all_balances_tests { - use jsonrpsee::types::ErrorObjectOwned; use super::{super::*, *}; @@ -1176,13 +1188,12 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); assert_eq!( - error_object.code(), + error_result.code(), jsonrpsee::types::error::INVALID_PARAMS_CODE ); let expected = expect!["Index store not available on this Fullnode."]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } } @@ -1377,13 +1388,12 @@ mod tests { assert!(response.is_err()); let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); let expected = expect!["-32602"]; - expected.assert_eq(&error_object.code().to_string()); + expected.assert_eq(&error_result.code().to_string()); let expected = expect![ "Cannot find object [0x2::coin::TreasuryCap<0xf::test_coin::TEST_COIN>] from [0x000000000000000000000000000000000000000000000000000000000000000f] package event." ]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } #[tokio::test] @@ -1419,15 +1429,14 @@ mod tests { let response = coin_read_api.get_total_supply(coin_name.clone()).await; let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); assert_eq!( - error_object.code(), + error_result.code(), jsonrpsee::types::error::CALL_EXECUTION_FAILED_CODE ); let expected = expect![ "Failure deserializing object in the requested format: \"Unable to deserialize TreasuryCap object: remaining input\"" ]; - expected.assert_eq(error_object.message()); + expected.assert_eq(error_result.message()); } fn default_system_state() -> IotaSystemStateInnerV1 { diff --git a/crates/iota-json-rpc/src/error.rs b/crates/iota-json-rpc/src/error.rs index c5ed40942fc..80b3235a5e2 100644 --- a/crates/iota-json-rpc/src/error.rs +++ b/crates/iota-json-rpc/src/error.rs @@ -6,17 +6,19 @@ use std::collections::BTreeMap; use fastcrypto::error::FastCryptoError; use hyper::header::InvalidHeaderValue; -use iota_json_rpc_api::{TRANSACTION_EXECUTION_CLIENT_ERROR_CODE, TRANSIENT_ERROR_CODE}; +use iota_json_rpc_api::{ + error_object_from_rpc, TRANSACTION_EXECUTION_CLIENT_ERROR_CODE, TRANSIENT_ERROR_CODE, +}; use iota_types::{ error::{IotaError, IotaObjectResponseError, UserInputError}, quorum_driver_types::QuorumDriverError, }; use itertools::Itertools; use jsonrpsee::{ - core::Error as RpcError, + core::{ClientError as RpcError, RegisterMethodError}, types::{ - error::{CallError, INTERNAL_ERROR_CODE}, - ErrorObject, + error::{ErrorCode, CALL_EXECUTION_FAILED_CODE, INTERNAL_ERROR_CODE}, + ErrorObject, ErrorObjectOwned, }, }; use thiserror::Error; @@ -40,7 +42,9 @@ pub enum Error { UnexpectedError(String), #[error(transparent)] - RPCServerError(#[from] jsonrpsee::core::Error), + RPCServerError(#[from] RpcError), + #[error(transparent)] + RPCRegisterMethodError(#[from] RegisterMethodError), #[error(transparent)] InvalidHeaderValue(#[from] InvalidHeaderValue), @@ -95,16 +99,25 @@ impl From for RpcError { /// `InvalidParams`/`INVALID_PARAMS_CODE` for client errors. fn from(e: Error) -> RpcError { match e { - Error::UserInputError(_) => RpcError::Call(CallError::InvalidParams(e.into())), - Error::UnsupportedFeature(_) => RpcError::Call(CallError::InvalidParams(e.into())), + Error::UserInputError(_) | Error::UnsupportedFeature(_) => RpcError::Call( + ErrorObject::owned::<()>(ErrorCode::InvalidRequest.code(), e.to_string(), None), + ), Error::IotaObjectResponseError(err) => match err { IotaObjectResponseError::NotExists { .. } | IotaObjectResponseError::DynamicFieldNotFound { .. } | IotaObjectResponseError::Deleted { .. } | IotaObjectResponseError::DisplayError { .. } => { - RpcError::Call(CallError::InvalidParams(err.into())) + RpcError::Call(ErrorObject::owned::<()>( + ErrorCode::InvalidParams.code(), + err.to_string(), + None, + )) } - _ => RpcError::Call(CallError::Failed(err.into())), + _ => RpcError::Call(ErrorObject::owned::<()>( + CALL_EXECUTION_FAILED_CODE, + err.to_string(), + None, + )), }, Error::NameServiceError(err) => match err { NameServiceError::ExceedsMaxLength { .. } @@ -113,28 +126,52 @@ impl From for RpcError { | NameServiceError::InvalidUnderscore { .. } | NameServiceError::LabelsEmpty { .. } | NameServiceError::InvalidSeparator { .. } => { - RpcError::Call(CallError::InvalidParams(err.into())) + RpcError::Call(ErrorObject::owned::<()>( + ErrorCode::InvalidParams.code(), + err.to_string(), + None, + )) } - _ => RpcError::Call(CallError::Failed(err.into())), + _ => RpcError::Call(ErrorObject::owned::<()>( + CALL_EXECUTION_FAILED_CODE, + err.to_string(), + None, + )), }, - Error::IotaRpcInputError(err) => RpcError::Call(CallError::InvalidParams(err.into())), + Error::IotaRpcInputError(err) => RpcError::Call(ErrorObject::owned::<()>( + ErrorCode::InvalidParams.code(), + err.to_string(), + None, + )), Error::IotaError(iota_error) => match iota_error { IotaError::TransactionNotFound { .. } | IotaError::TransactionsNotFound { .. } | IotaError::TransactionEventsNotFound { .. } => { - RpcError::Call(CallError::InvalidParams(iota_error.into())) + RpcError::Call(ErrorObject::owned::<()>( + ErrorCode::InvalidParams.code(), + iota_error.to_string(), + None, + )) } - _ => RpcError::Call(CallError::Failed(iota_error.into())), + _ => RpcError::Call(ErrorObject::owned::<()>( + CALL_EXECUTION_FAILED_CODE, + iota_error.to_string(), + None, + )), }, Error::StateReadError(err) => match err { - StateReadError::Client(_) => RpcError::Call(CallError::InvalidParams(err.into())), + StateReadError::Client(_) => RpcError::Call(ErrorObject::owned::<()>( + ErrorCode::InvalidParams.code(), + err.to_string(), + None, + )), _ => { - let error_object = ErrorObject::owned( + let error_object = ErrorObject::owned::<()>( jsonrpsee::types::error::INTERNAL_ERROR_CODE, err.to_string(), - None::<()>, + None, ); - RpcError::Call(CallError::Custom(error_object)) + RpcError::Call(error_object) } }, Error::QuorumDriverError(err) => { @@ -149,26 +186,26 @@ impl From for RpcError { let error_message = format!("Invalid user signature: {inner_error_str}"); - let error_object = ErrorObject::owned( + let error_object = ErrorObject::owned::<()>( TRANSACTION_EXECUTION_CLIENT_ERROR_CODE, error_message, - None::<()>, + None, ); - RpcError::Call(CallError::Custom(error_object)) + RpcError::Call(error_object) } QuorumDriverError::TxAlreadyFinalizedWithDifferentUserSignatures => { - let error_object = ErrorObject::owned( + let error_object = ErrorObject::owned::<()>( TRANSACTION_EXECUTION_CLIENT_ERROR_CODE, "The transaction is already finalized but with different user signatures", - None::<()>, + None, ); - RpcError::Call(CallError::Custom(error_object)) + RpcError::Call(error_object) } QuorumDriverError::TimeoutBeforeFinality | QuorumDriverError::FailedWithTransientErrorAfterMaximumAttempts { .. } => { let error_object = - ErrorObject::owned(TRANSIENT_ERROR_CODE, err.to_string(), None::<()>); - RpcError::Call(CallError::Custom(error_object)) + ErrorObject::owned::<()>(TRANSIENT_ERROR_CODE, err.to_string(), None); + RpcError::Call(error_object) } QuorumDriverError::ObjectsDoubleUsed { conflicting_txes, @@ -195,7 +232,7 @@ impl From for RpcError { error_message, Some(new_map), ); - RpcError::Call(CallError::Custom(error_object)) + RpcError::Call(error_object) } QuorumDriverError::NonRecoverableTransactionError { errors } => { let new_errors: Vec = errors @@ -238,30 +275,34 @@ impl From for RpcError { error_list ); - let error_object = ErrorObject::owned( + let error_object = ErrorObject::owned::<()>( TRANSACTION_EXECUTION_CLIENT_ERROR_CODE, error_msg, - None::<()>, + None, ); - RpcError::Call(CallError::Custom(error_object)) + RpcError::Call(error_object) } QuorumDriverError::QuorumDriverInternalError(_) => { - let error_object = ErrorObject::owned( + let error_object = ErrorObject::owned::<()>( INTERNAL_ERROR_CODE, "Internal error occurred while executing transaction.", - None::<()>, + None, ); - RpcError::Call(CallError::Custom(error_object)) + RpcError::Call(error_object) } QuorumDriverError::SystemOverload { .. } | QuorumDriverError::SystemOverloadRetryAfter { .. } => { let error_object = - ErrorObject::owned(TRANSIENT_ERROR_CODE, err.to_string(), None::<()>); - RpcError::Call(CallError::Custom(error_object)) + ErrorObject::owned::<()>(TRANSIENT_ERROR_CODE, err.to_string(), None); + RpcError::Call(error_object) } } } - _ => RpcError::Call(CallError::Failed(e.into())), + _ => RpcError::Call(ErrorObject::owned::<()>( + CALL_EXECUTION_FAILED_CODE, + e.to_string(), + None, + )), } } } @@ -309,7 +350,17 @@ pub enum IotaRpcInputError { impl From for RpcError { fn from(e: IotaRpcInputError) -> Self { - RpcError::Call(CallError::InvalidParams(e.into())) + RpcError::Call(ErrorObject::owned::<()>( + ErrorCode::InvalidParams.code(), + e.to_string(), + None, + )) + } +} + +impl From for ErrorObjectOwned { + fn from(value: IotaRpcInputError) -> Self { + error_object_from_rpc(value.into()) } } @@ -322,7 +373,6 @@ mod tests { crypto::{AuthorityPublicKey, AuthorityPublicKeyBytes}, digests::{ObjectDigest, TransactionDigest}, }; - use jsonrpsee::types::ErrorObjectOwned; use super::*; @@ -346,7 +396,7 @@ mod tests { let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); - let error_object: ErrorObjectOwned = rpc_error.into(); + let error_object = error_object_from_rpc(rpc_error); let expected_code = expect!["-32002"]; expected_code.assert_eq(&error_object.code().to_string()); let expected_message = expect![ @@ -361,7 +411,7 @@ mod tests { let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); - let error_object: ErrorObjectOwned = rpc_error.into(); + let error_object = error_object_from_rpc(rpc_error); let expected_code = expect!["-32050"]; expected_code.assert_eq(&error_object.code().to_string()); let expected_message = expect!["Transaction timed out before reaching finality"]; @@ -377,7 +427,7 @@ mod tests { let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); - let error_object: ErrorObjectOwned = rpc_error.into(); + let error_object = error_object_from_rpc(rpc_error); let expected_code = expect!["-32050"]; expected_code.assert_eq(&error_object.code().to_string()); let expected_message = expect![ @@ -407,7 +457,7 @@ mod tests { let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); - let error_object: ErrorObjectOwned = rpc_error.into(); + let error_object = error_object_from_rpc(rpc_error); let expected_code = expect!["-32002"]; expected_code.assert_eq(&error_object.code().to_string()); let expected_message = expect![ @@ -450,7 +500,7 @@ mod tests { let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); - let error_object: ErrorObjectOwned = rpc_error.into(); + let error_object = error_object_from_rpc(rpc_error); let expected_code = expect!["-32002"]; expected_code.assert_eq(&error_object.code().to_string()); let expected_message = expect![ @@ -483,7 +533,7 @@ mod tests { let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); - let error_object: ErrorObjectOwned = rpc_error.into(); + let error_object = error_object_from_rpc(rpc_error); let expected_code = expect!["-32002"]; expected_code.assert_eq(&error_object.code().to_string()); let expected_message = expect![ @@ -499,7 +549,7 @@ mod tests { let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); - let error_object: ErrorObjectOwned = rpc_error.into(); + let error_object = error_object_from_rpc(rpc_error); let expected_code = expect!["-32603"]; expected_code.assert_eq(&error_object.code().to_string()); let expected_message = expect!["Internal error occurred while executing transaction."]; @@ -515,7 +565,7 @@ mod tests { let rpc_error: RpcError = Error::QuorumDriverError(quorum_driver_error).into(); - let error_object: ErrorObjectOwned = rpc_error.into(); + let error_object = error_object_from_rpc(rpc_error); let expected_code = expect!["-32050"]; expected_code.assert_eq(&error_object.code().to_string()); let expected_message = expect![ diff --git a/crates/iota-json-rpc/src/governance_api.rs b/crates/iota-json-rpc/src/governance_api.rs index a442fd95911..81a6e319326 100644 --- a/crates/iota-json-rpc/src/governance_api.rs +++ b/crates/iota-json-rpc/src/governance_api.rs @@ -7,7 +7,9 @@ use std::{cmp::max, collections::BTreeMap, sync::Arc}; use async_trait::async_trait; use cached::{proc_macro::cached, SizedCache}; use iota_core::authority::AuthorityState; -use iota_json_rpc_api::{GovernanceReadApiOpenRpc, GovernanceReadApiServer, JsonRpcMetrics}; +use iota_json_rpc_api::{ + error_object_from_rpc, GovernanceReadApiOpenRpc, GovernanceReadApiServer, JsonRpcMetrics, +}; use iota_json_rpc_types::{ DelegatedStake, DelegatedTimelockedStake, IotaCommittee, Stake, StakeStatus, TimelockedStake, ValidatorApy, ValidatorApys, @@ -36,7 +38,8 @@ use tracing::{info, instrument}; use crate::{ authority_state::StateRead, error::{Error, IotaRpcInputError, RpcInterimResult}, - with_tracing, IotaRpcModule, ObjectProvider, + logger::with_tracing, + IotaRpcModule, ObjectProvider, }; #[derive(Clone)] @@ -350,12 +353,16 @@ impl GovernanceReadApiServer for GovernanceReadApi { &self, staked_iota_ids: Vec, ) -> RpcResult> { - with_tracing!(async move { self.get_stakes_by_ids(staked_iota_ids).await }) + with_tracing( + async move { self.get_stakes_by_ids(staked_iota_ids).await }, + None, + ) + .await } #[instrument(skip(self))] async fn get_stakes(&self, owner: IotaAddress) -> RpcResult> { - with_tracing!(async move { self.get_stakes(owner).await }) + with_tracing(async move { self.get_stakes(owner).await }, None).await } #[instrument(skip(self))] @@ -363,10 +370,14 @@ impl GovernanceReadApiServer for GovernanceReadApi { &self, timelocked_staked_iota_ids: Vec, ) -> RpcResult> { - with_tracing!(async move { - self.get_timelocked_stakes_by_ids(timelocked_staked_iota_ids) - .await - }) + with_tracing( + async move { + self.get_timelocked_stakes_by_ids(timelocked_staked_iota_ids) + .await + }, + None, + ) + .await } #[instrument(skip(self))] @@ -374,36 +385,48 @@ impl GovernanceReadApiServer for GovernanceReadApi { &self, owner: IotaAddress, ) -> RpcResult> { - with_tracing!(async move { self.get_timelocked_stakes(owner).await }) + with_tracing(async move { self.get_timelocked_stakes(owner).await }, None).await } #[instrument(skip(self))] async fn get_committee_info(&self, epoch: Option>) -> RpcResult { - with_tracing!(async move { - self.state - .get_or_latest_committee(epoch) - .map(|committee| committee.into()) - .map_err(Error::from) - }) + with_tracing( + async move { + self.state + .get_or_latest_committee(epoch) + .map(|committee| committee.into()) + .map_err(Error::from) + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_latest_iota_system_state(&self) -> RpcResult { - with_tracing!(async move { - Ok(self - .state - .get_system_state() - .map_err(Error::from)? - .into_iota_system_state_summary()) - }) + with_tracing( + async move { + Ok(self + .state + .get_system_state() + .map_err(Error::from)? + .into_iota_system_state_summary()) + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_reference_gas_price(&self) -> RpcResult> { - with_tracing!(async move { - let epoch_store = self.state.load_epoch_store_one_call_per_task(); - Ok(epoch_store.reference_gas_price().into()) - }) + with_tracing( + async move { + let epoch_store = self.state.load_epoch_store_one_call_per_task(); + Ok(epoch_store.reference_gas_price().into()) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -414,7 +437,7 @@ impl GovernanceReadApiServer for GovernanceReadApi { let exchange_rate_table = exchange_rates(&self.state, system_state_summary.epoch) .await - .map_err(Error::from)?; + .map_err(|e| error_object_from_rpc(e.into()))?; let apys = calculate_apys( system_state_summary.stake_subsidy_start_epoch, @@ -538,7 +561,7 @@ fn stake_status( /// 1, it will be cleared when the epoch changes. rates are in descending order /// by epoch. #[cached( - type = "SizedCache>", + ty = "SizedCache>", create = "{ SizedCache::with_size(1) }", convert = "{ _current_epoch }", result = true diff --git a/crates/iota-json-rpc/src/indexer_api.rs b/crates/iota-json-rpc/src/indexer_api.rs index 8af5adb7f11..d6e8d41cbdf 100644 --- a/crates/iota-json-rpc/src/indexer_api.rs +++ b/crates/iota-json-rpc/src/indexer_api.rs @@ -2,11 +2,11 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; -use anyhow::bail; +use anyhow::{anyhow, bail}; use async_trait::async_trait; -use futures::{future, Stream}; +use futures::{future, Stream, StreamExt}; use iota_core::authority::AuthorityState; use iota_json::IotaJsonValue; use iota_json_rpc_api::{ @@ -28,48 +28,72 @@ use iota_types::{ event::EventID, }; use jsonrpsee::{ - core::{error::SubscriptionClosed, RpcResult}, - types::SubscriptionResult, - RpcModule, SubscriptionSink, + core::RpcResult, PendingSubscriptionSink, RpcModule, SendTimeoutError, SubscriptionMessage, }; use move_bytecode_utils::layout::TypeLayoutBuilder; use move_core_types::language_storage::TypeTag; use mysten_metrics::spawn_monitored_task; use serde::Serialize; use tokio::sync::{OwnedSemaphorePermit, Semaphore}; -use tracing::{debug, instrument, warn}; +use tracing::{debug, instrument}; use crate::{ authority_state::{StateRead, StateReadResult}, error::{Error, IotaRpcInputError}, + logger::with_tracing, name_service::{Domain, NameRecord, NameServiceConfig, NameServiceError}, - with_tracing, IotaRpcModule, + IotaRpcModule, }; +async fn pipe_from_stream( + pending: PendingSubscriptionSink, + mut stream: impl Stream + Unpin, +) -> Result<(), anyhow::Error> { + let sink = pending.accept().await?; + + loop { + tokio::select! { + _ = sink.closed() => break Ok(()), + maybe_item = stream.next() => { + let Some(item) = maybe_item else { + break Ok(()); + }; + + let msg = SubscriptionMessage::from_json(&item)?; + + if let Err(e) = sink.send_timeout(msg, Duration::from_secs(60)).await { + match e { + // The subscription or connection was closed. + SendTimeoutError::Closed(_) => break Ok(()), + // The subscription send timeout expired + // the message is returned and you could save that message + // and retry again later. + SendTimeoutError::Timeout(_) => break Err(anyhow::anyhow!("Subscription timeout expired")), + } + } + } + } + } +} + pub fn spawn_subscription( - mut sink: SubscriptionSink, + pending: PendingSubscriptionSink, rx: S, permit: Option, ) where S: Stream + Unpin + Send + 'static, - T: Serialize, + T: Serialize + Send, { spawn_monitored_task!(async move { let _permit = permit; - match sink.pipe_from_stream(rx).await { - SubscriptionClosed::Success => { + match pipe_from_stream(pending, rx).await { + Ok(_) => { debug!("Subscription completed."); - sink.close(SubscriptionClosed::Success); - } - SubscriptionClosed::RemotePeerAborted => { - debug!("Subscription aborted by remote peer."); - sink.close(SubscriptionClosed::RemotePeerAborted); } - SubscriptionClosed::Failed(err) => { + Err(err) => { debug!("Subscription failed: {err:?}"); - sink.close(err); } - }; + } }); } const DEFAULT_MAX_SUBSCRIPTIONS: usize = 100; @@ -146,51 +170,55 @@ impl IndexerApiServer for IndexerApi { cursor: Option, limit: Option, ) -> RpcResult { - with_tracing!(async move { - let limit = - validate_limit(limit, *QUERY_MAX_RESULT_LIMIT).map_err(IotaRpcInputError::from)?; - self.metrics.get_owned_objects_limit.report(limit as u64); - let IotaObjectResponseQuery { filter, options } = query.unwrap_or_default(); - let options = options.unwrap_or_default(); - let mut objects = self - .state - .get_owner_objects_with_limit(address, cursor, limit + 1, filter) - .map_err(Error::from)?; - - // objects here are of size (limit + 1), where the last one is the cursor for - // the next page - let has_next_page = objects.len() > limit; - objects.truncate(limit); - let next_cursor = objects - .last() - .cloned() - .map_or(cursor, |o_info| Some(o_info.object_id)); - - let data = match options.is_not_in_object_info() { - true => { - let object_ids = objects.iter().map(|obj| obj.object_id).collect(); - self.read_api - .multi_get_objects(object_ids, Some(options)) - .await? - } - false => objects - .into_iter() - .map(|o_info| IotaObjectResponse::try_from((o_info, options.clone()))) - .collect::, _>>()?, - }; - - self.metrics - .get_owned_objects_result_size - .report(data.len() as u64); - self.metrics - .get_owned_objects_result_size_total - .inc_by(data.len() as u64); - Ok(Page { - data, - next_cursor, - has_next_page, - }) - }) + with_tracing( + async move { + let limit = validate_limit(limit, *QUERY_MAX_RESULT_LIMIT) + .map_err(IotaRpcInputError::from)?; + self.metrics.get_owned_objects_limit.report(limit as u64); + let IotaObjectResponseQuery { filter, options } = query.unwrap_or_default(); + let options = options.unwrap_or_default(); + let mut objects = + self.state + .get_owner_objects_with_limit(address, cursor, limit + 1, filter)?; + + // objects here are of size (limit + 1), where the last one is the cursor for + // the next page + let has_next_page = objects.len() > limit; + objects.truncate(limit); + let next_cursor = objects + .last() + .cloned() + .map_or(cursor, |o_info| Some(o_info.object_id)); + + let data = match options.is_not_in_object_info() { + true => { + let object_ids = objects.iter().map(|obj| obj.object_id).collect(); + self.read_api + .multi_get_objects(object_ids, Some(options)) + .await + .map_err(|e| Error::InternalError(anyhow!(e)))? + } + false => objects + .into_iter() + .map(|o_info| IotaObjectResponse::try_from((o_info, options.clone()))) + .collect::, _>>()?, + }; + + self.metrics + .get_owned_objects_result_size + .report(data.len() as u64); + self.metrics + .get_owned_objects_result_size_total + .inc_by(data.len() as u64); + Ok(Page { + data, + next_cursor, + has_next_page, + }) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -202,53 +230,58 @@ impl IndexerApiServer for IndexerApi { limit: Option, descending_order: Option, ) -> RpcResult { - with_tracing!(async move { - let limit = cap_page_limit(limit); - self.metrics.query_tx_blocks_limit.report(limit as u64); - let descending = descending_order.unwrap_or_default(); - let opts = query.options.unwrap_or_default(); - - // Retrieve 1 extra item for next cursor - let mut digests = self - .state - .get_transactions( - &self.transaction_kv_store, - query.filter, - cursor, - Some(limit + 1), - descending, - ) - .await - .map_err(Error::from)?; - - // extract next cursor - let has_next_page = digests.len() > limit; - digests.truncate(limit); - let next_cursor = digests.last().cloned().map_or(cursor, Some); - - let data: Vec = if opts.only_digest() { - digests - .into_iter() - .map(IotaTransactionBlockResponse::new) - .collect() - } else { - self.read_api - .multi_get_transaction_blocks(digests, Some(opts)) - .await? - }; - - self.metrics - .query_tx_blocks_result_size - .report(data.len() as u64); - self.metrics - .query_tx_blocks_result_size_total - .inc_by(data.len() as u64); - Ok(Page { - data, - next_cursor, - has_next_page, - }) - }) + with_tracing( + async move { + let limit = cap_page_limit(limit); + self.metrics.query_tx_blocks_limit.report(limit as u64); + let descending = descending_order.unwrap_or_default(); + let opts = query.options.unwrap_or_default(); + + // Retrieve 1 extra item for next cursor + let mut digests = self + .state + .get_transactions( + &self.transaction_kv_store, + query.filter, + cursor, + Some(limit + 1), + descending, + ) + .await + .map_err(Error::from)?; + + // extract next cursor + let has_next_page = digests.len() > limit; + digests.truncate(limit); + let next_cursor = digests.last().cloned().map_or(cursor, Some); + + let data: Vec = if opts.only_digest() { + digests + .into_iter() + .map(IotaTransactionBlockResponse::new) + .collect() + } else { + self.read_api + .multi_get_transaction_blocks(digests, Some(opts)) + .await + .map_err(|e| Error::InternalError(anyhow!(e)))? + }; + + self.metrics + .query_tx_blocks_result_size + .report(data.len() as u64); + self.metrics + .query_tx_blocks_result_size_total + .inc_by(data.len() as u64); + Ok(Page { + data, + next_cursor, + has_next_page, + }) + }, + None, + ) + .await } #[instrument(skip(self))] async fn query_events( @@ -259,66 +292,68 @@ impl IndexerApiServer for IndexerApi { limit: Option, descending_order: Option, ) -> RpcResult { - with_tracing!(async move { - let descending = descending_order.unwrap_or_default(); - let limit = cap_page_limit(limit); - self.metrics.query_events_limit.report(limit as u64); - // Retrieve 1 extra item for next cursor - let mut data = self - .state - .query_events( - &self.transaction_kv_store, - query, - cursor, - limit + 1, - descending, - ) - .await - .map_err(Error::from)?; - let has_next_page = data.len() > limit; - data.truncate(limit); - let next_cursor = data.last().map_or(cursor, |e| Some(e.id)); - self.metrics - .query_events_result_size - .report(data.len() as u64); - self.metrics - .query_events_result_size_total - .inc_by(data.len() as u64); - Ok(EventPage { - data, - next_cursor, - has_next_page, - }) - }) + with_tracing( + async move { + let descending = descending_order.unwrap_or_default(); + let limit = cap_page_limit(limit); + self.metrics.query_events_limit.report(limit as u64); + // Retrieve 1 extra item for next cursor + let mut data = self + .state + .query_events( + &self.transaction_kv_store, + query, + cursor, + limit + 1, + descending, + ) + .await + .map_err(Error::from)?; + let has_next_page = data.len() > limit; + data.truncate(limit); + let next_cursor = data.last().map_or(cursor, |e| Some(e.id)); + self.metrics + .query_events_result_size + .report(data.len() as u64); + self.metrics + .query_events_result_size_total + .inc_by(data.len() as u64); + Ok(EventPage { + data, + next_cursor, + has_next_page, + }) + }, + None, + ) + .await } #[instrument(skip(self))] - fn subscribe_event(&self, sink: SubscriptionSink, filter: EventFilter) -> SubscriptionResult { - let permit = self.acquire_subscribe_permit()?; + async fn subscribe_event(&self, sink: PendingSubscriptionSink, filter: EventFilter) { + let permit = self.acquire_subscribe_permit().ok(); spawn_subscription( sink, self.state .get_subscription_handler() .subscribe_events(filter), - Some(permit), + permit, ); - Ok(()) } - fn subscribe_transaction( + async fn subscribe_transaction( &self, - sink: SubscriptionSink, + sink: PendingSubscriptionSink, filter: TransactionFilter, - ) -> SubscriptionResult { - let permit = self.acquire_subscribe_permit()?; + ) { + let permit = self.acquire_subscribe_permit().ok(); spawn_subscription( sink, self.state .get_subscription_handler() .subscribe_transactions(filter), - Some(permit), + permit, ); - Ok(()) } #[instrument(skip(self))] @@ -329,28 +364,32 @@ impl IndexerApiServer for IndexerApi { cursor: Option, limit: Option, ) -> RpcResult { - with_tracing!(async move { - let limit = cap_page_limit(limit); - self.metrics.get_dynamic_fields_limit.report(limit as u64); - let mut data = self - .state - .get_dynamic_fields(parent_object_id, cursor, limit + 1) - .map_err(Error::from)?; - let has_next_page = data.len() > limit; - data.truncate(limit); - let next_cursor = data.last().cloned().map_or(cursor, |c| Some(c.0)); - self.metrics - .get_dynamic_fields_result_size - .report(data.len() as u64); - self.metrics - .get_dynamic_fields_result_size_total - .inc_by(data.len() as u64); - Ok(DynamicFieldPage { - data: data.into_iter().map(|(_, w)| w).collect(), - next_cursor, - has_next_page, - }) - }) + with_tracing( + async move { + let limit = cap_page_limit(limit); + self.metrics.get_dynamic_fields_limit.report(limit as u64); + let mut data = self + .state + .get_dynamic_fields(parent_object_id, cursor, limit + 1) + .map_err(Error::from)?; + let has_next_page = data.len() > limit; + data.truncate(limit); + let next_cursor = data.last().cloned().map_or(cursor, |c| Some(c.0)); + self.metrics + .get_dynamic_fields_result_size + .report(data.len() as u64); + self.metrics + .get_dynamic_fields_result_size_total + .inc_by(data.len() as u64); + Ok(DynamicFieldPage { + data: data.into_iter().map(|(_, w)| w).collect(), + next_cursor, + has_next_page, + }) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -359,94 +398,103 @@ impl IndexerApiServer for IndexerApi { parent_object_id: ObjectID, name: DynamicFieldName, ) -> RpcResult { - with_tracing!(async move { - let (name_type, name_bcs_value) = self.extract_values_from_dynamic_field_name(name)?; - - let id = self - .state - .get_dynamic_field_object_id(parent_object_id, name_type, &name_bcs_value) - .map_err(Error::from)?; - // TODO(chris): add options to `get_dynamic_field_object` API as well - if let Some(id) = id { - self.read_api - .get_object(id, Some(IotaObjectDataOptions::full_content())) - .await - .map_err(Error::from) - } else { - Ok(IotaObjectResponse::new_with_error( - IotaObjectResponseError::DynamicFieldNotFound { parent_object_id }, - )) - } - }) + with_tracing( + async move { + let (name_type, name_bcs_value) = + self.extract_values_from_dynamic_field_name(name)?; + + let id = self + .state + .get_dynamic_field_object_id(parent_object_id, name_type, &name_bcs_value) + .map_err(Error::from)?; + // TODO(chris): add options to `get_dynamic_field_object` API as well + if let Some(id) = id { + self.read_api + .get_object(id, Some(IotaObjectDataOptions::full_content())) + .await + .map_err(|e| Error::InternalError(anyhow!(e))) + } else { + Ok(IotaObjectResponse::new_with_error( + IotaObjectResponseError::DynamicFieldNotFound { parent_object_id }, + )) + } + }, + None, + ) + .await } #[instrument(skip(self))] async fn resolve_name_service_address(&self, name: String) -> RpcResult> { - with_tracing!(async move { - // prepare the requested domain's field id. - let domain = name.parse::().map_err(Error::from)?; - let record_id = self.name_service_config.record_field_id(&domain); + with_tracing( + async move { + // prepare the requested domain's field id. + let domain = name.parse::().map_err(Error::from)?; + let record_id = self.name_service_config.record_field_id(&domain); - // prepare the parent's field id. - let parent_domain = domain.parent(); - let parent_record_id = self.name_service_config.record_field_id(&parent_domain); + // prepare the parent's field id. + let parent_domain = domain.parent(); + let parent_record_id = self.name_service_config.record_field_id(&parent_domain); - let current_timestamp_ms = self.get_latest_checkpoint_timestamp_ms()?; + let current_timestamp_ms = self.get_latest_checkpoint_timestamp_ms()?; - // Do these two reads in parallel. - let mut requests = vec![self.state.get_object(&record_id)]; + // Do these two reads in parallel. + let mut requests = vec![self.state.get_object(&record_id)]; - // Also add the parent in the DB reads if the requested domain is a subdomain. - if domain.is_subdomain() { - requests.push(self.state.get_object(&parent_record_id)); - } + // Also add the parent in the DB reads if the requested domain is a subdomain. + if domain.is_subdomain() { + requests.push(self.state.get_object(&parent_record_id)); + } + + // Couldn't find a `multi_get_object` for this crate (looks like it uses a k,v + // db) Always fetching both parent + child at the same time (even + // for node subdomains), to avoid sequential db reads. We do this + // because we do not know if the requested domain is a node + // subdomain or a leaf subdomain, and we can save a trip to the db. + let mut results = future::try_join_all(requests).await?; + + // Removing without checking vector len, since it is known (== 1 or 2 depending + // on whether it is a subdomain or not). + let Some(object) = results.remove(0) else { + return Ok(None); + }; + + let name_record = NameRecord::try_from(object)?; + + // Handling SLD names & node subdomains is the same (we handle them as `node` + // records) We check their expiration, and and if not expired, + // return the target address. + if !name_record.is_leaf_record() { + return if !name_record.is_node_expired(current_timestamp_ms) { + Ok(name_record.target_address) + } else { + Err(Error::from(NameServiceError::NameExpired)) + }; + } - // Couldn't find a `multi_get_object` for this crate (looks like it uses a k,v - // db) Always fetching both parent + child at the same time (even - // for node subdomains), to avoid sequential db reads. We do this - // because we do not know if the requested domain is a node - // subdomain or a leaf subdomain, and we can save a trip to the db. - let mut results = future::try_join_all(requests).await?; - - // Removing without checking vector len, since it is known (== 1 or 2 depending - // on whether it is a subdomain or not). - let Some(object) = results.remove(0) else { - return Ok(None); - }; - - let name_record = NameRecord::try_from(object)?; - - // Handling SLD names & node subdomains is the same (we handle them as `node` - // records) We check their expiration, and and if not expired, - // return the target address. - if !name_record.is_leaf_record() { - return if !name_record.is_node_expired(current_timestamp_ms) { + // == Handle leaf subdomains case == + // We can remove since we know that if we're here, we have a parent + // (which also means we queried it in the future above). + let Some(parent_object) = results.remove(0) else { + return Err(Error::from(NameServiceError::NameExpired)); + }; + + let parent_name_record = NameRecord::try_from(parent_object)?; + + // For a leaf record, we check that: + // 1. The parent is a valid parent for that leaf record + // 2. The parent is not expired + if parent_name_record.is_valid_leaf_parent(&name_record) + && !parent_name_record.is_node_expired(current_timestamp_ms) + { Ok(name_record.target_address) } else { Err(Error::from(NameServiceError::NameExpired)) - }; - } - - // == Handle leaf subdomains case == - // We can remove since we know that if we're here, we have a parent - // (which also means we queried it in the future above). - let Some(parent_object) = results.remove(0) else { - return Err(Error::from(NameServiceError::NameExpired)); - }; - - let parent_name_record = NameRecord::try_from(parent_object)?; - - // For a leaf record, we check that: - // 1. The parent is a valid parent for that leaf record - // 2. The parent is not expired - if parent_name_record.is_valid_leaf_parent(&name_record) - && !parent_name_record.is_node_expired(current_timestamp_ms) - { - Ok(name_record.target_address) - } else { - Err(Error::from(NameServiceError::NameExpired)) - } - }) + } + }, + None, + ) + .await } #[instrument(skip(self))] @@ -456,47 +504,52 @@ impl IndexerApiServer for IndexerApi { _cursor: Option, _limit: Option, ) -> RpcResult> { - with_tracing!(async move { - let reverse_record_id = self - .name_service_config - .reverse_record_field_id(address.as_ref()); - - let mut result = Page { - data: vec![], - next_cursor: None, - has_next_page: false, - }; - - let Some(field_reverse_record_object) = - self.state.get_object(&reverse_record_id).await? - else { - return Ok(result); - }; - - let domain = field_reverse_record_object - .to_rust::>() - .ok_or_else(|| { - Error::UnexpectedError(format!("Malformed Object {reverse_record_id}")) - })? - .value; - - let domain_name = domain.to_string(); - - let resolved_address = self - .resolve_name_service_address(domain_name.clone()) - .await?; - - // If looking up the domain returns an empty result, we return an empty result. - if resolved_address.is_none() { - return Ok(result); - } + with_tracing( + async move { + let reverse_record_id = self + .name_service_config + .reverse_record_field_id(address.as_ref()); + + let mut result = Page { + data: vec![], + next_cursor: None, + has_next_page: false, + }; + + let Some(field_reverse_record_object) = + self.state.get_object(&reverse_record_id).await? + else { + return Ok(result); + }; + + let domain = field_reverse_record_object + .to_rust::>() + .ok_or_else(|| { + Error::UnexpectedError(format!("Malformed Object {reverse_record_id}")) + })? + .value; + + let domain_name = domain.to_string(); + + let resolved_address = self + .resolve_name_service_address(domain_name.clone()) + .await + .map_err(|e| Error::InternalError(anyhow!(e)))?; + + // If looking up the domain returns an empty result, we return an empty result. + if resolved_address.is_none() { + return Ok(result); + } - // TODO(manos): Discuss why is this even a paginated response. - // This API is always going to return a single domain name. - result.data.push(domain_name); + // TODO(manos): Discuss why is this even a paginated response. + // This API is always going to return a single domain name. + result.data.push(domain_name); - Ok(result) - }) + Ok(result) + }, + None, + ) + .await } } diff --git a/crates/iota-json-rpc/src/lib.rs b/crates/iota-json-rpc/src/lib.rs index b711c9505e5..5cf6b40810a 100644 --- a/crates/iota-json-rpc/src/lib.rs +++ b/crates/iota-json-rpc/src/lib.rs @@ -4,17 +4,20 @@ use std::{env, net::SocketAddr, str::FromStr}; -use axum::routing::{get, post}; +use axum::{ + body::Body, + routing::{get, post}, +}; pub use balance_changes::*; use hyper::{ header::{HeaderName, HeaderValue}, - Body, Method, Request, + Method, Request, }; use iota_json_rpc_api::{ CLIENT_SDK_TYPE_HEADER, CLIENT_SDK_VERSION_HEADER, CLIENT_TARGET_API_VERSION_HEADER, }; use iota_open_rpc::{Module, Project}; -use jsonrpsee::RpcModule; +use jsonrpsee::{types::ErrorObjectOwned, Extensions, RpcModule}; pub use object_changes::*; use prometheus::Registry; use tokio::runtime::Handle; @@ -120,7 +123,7 @@ impl JsonRpcServerBuilder { fn trace_layer() -> TraceLayer< tower_http::classify::SharedClassifier, - impl tower_http::trace::MakeSpan + Clone, + impl tower_http::trace::MakeSpan + Clone, (), (), (), @@ -163,7 +166,9 @@ impl JsonRpcServerBuilder { let rpc_docs = self.rpc_doc.clone(); let mut module = self.module.clone(); - module.register_method("rpc.discover", move |_, _| Ok(rpc_docs.clone()))?; + module.register_method("rpc.discover", move |_, _, _| { + Result::<_, ErrorObjectOwned>::Ok(rpc_docs.clone()) + })?; let methods_names = module.method_names().collect::>(); let metrics_logger = MetricsLogger::new(&self.registry, &methods_names); @@ -172,8 +177,12 @@ impl JsonRpcServerBuilder { .layer(Self::trace_layer()) .layer(Self::cors()?); - let service = - crate::axum_router::JsonRpcService::new(module.into(), rpc_router, metrics_logger); + let service = crate::axum_router::JsonRpcService::new( + module.into(), + rpc_router, + metrics_logger, + Extensions::new(), + ); let mut router = axum::Router::new(); @@ -214,10 +223,20 @@ impl JsonRpcServerBuilder { ) -> Result { let app = self.to_router(server_type)?; - let server = axum::Server::bind(&listen_address).serve(app.into_make_service()); - - let addr = server.local_addr(); - let handle = tokio::spawn(async move { server.await.unwrap() }); + let listener = tokio::net::TcpListener::bind(listen_address) + .await + .map_err(|e| { + Error::UnexpectedError(format!("invalid listen address {listen_address}: {e}")) + })?; + + let addr = listener.local_addr().map_err(|e| { + Error::UnexpectedError(format!("invalid listen address {listen_address}: {e}")) + })?; + let handle = tokio::spawn(async move { + axum::serve(listener, app.into_make_service()) + .await + .unwrap() + }); let handle = ServerHandle { handle: ServerHandleInner::Axum(handle), diff --git a/crates/iota-json-rpc/src/logger.rs b/crates/iota-json-rpc/src/logger.rs index c402cf7a44e..38975bbdd7c 100644 --- a/crates/iota-json-rpc/src/logger.rs +++ b/crates/iota-json-rpc/src/logger.rs @@ -2,39 +2,183 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -#[macro_export] -macro_rules! with_tracing { - ($time_spent_threshold:expr, $future:expr) => {{ - use tracing::{info, error, Instrument, Span}; - use jsonrpsee::core::{RpcResult, Error as RpcError}; - use jsonrpsee::types::error::{CallError}; - use $crate::error::RpcInterimResult; - use anyhow::anyhow; - - async move { - let start = std::time::Instant::now(); - let interim_result: RpcInterimResult<_> = $future.await; - let elapsed = start.elapsed(); - let result: RpcResult<_> = interim_result.map_err(|e: Error| { - let anyhow_error = anyhow!("{:?}", e); - - let rpc_error: RpcError = e.into(); - if !matches!(rpc_error, RpcError::Call(CallError::InvalidParams(_))) { - error!(error=?anyhow_error); - } - rpc_error - }); - - if elapsed > $time_spent_threshold { - info!(?elapsed, "RPC took longer than threshold to complete."); +use std::{net::SocketAddr, time::Duration}; + +use anyhow::anyhow; +use futures::Future; +use iota_json_rpc_api::error_object_from_rpc; +use jsonrpsee::{ + core::{ClientError as RpcError, RpcResult}, + server::HttpRequest, + types::Params, + MethodKind, +}; +use tracing::{error, info, Instrument, Span}; + +use crate::error::RpcInterimResult; + +/// The transport protocol used to send or receive a call or request. +#[derive(Debug, Copy, Clone)] +pub enum TransportProtocol { + /// HTTP transport. + Http, + /// WebSocket transport. + WebSocket, +} + +impl std::fmt::Display for TransportProtocol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::Http => "http", + Self::WebSocket => "websocket", + }; + + write!(f, "{s}") + } +} + +/// Defines a logger specifically for WebSocket connections with callbacks +/// during the RPC request life-cycle. The primary use case for this is to +/// collect timings for a larger metrics collection solution. +pub trait Logger: Send + Sync + Clone + 'static { + /// Intended to carry timestamp of a request, for example + /// `std::time::Instant`. How the trait measures time, if at all, is + /// entirely up to the implementation. + type Instant: std::fmt::Debug + Send + Sync + Copy; + + /// Called when a new client connects + fn on_connect(&self, _remote_addr: SocketAddr, _request: &HttpRequest, _t: TransportProtocol); + + /// Called when a new JSON-RPC request comes to the server. + fn on_request(&self, transport: TransportProtocol) -> Self::Instant; + + /// Called on each JSON-RPC method call, batch requests will trigger + /// `on_call` multiple times. + fn on_call( + &self, + method_name: &str, + params: Params, + kind: MethodKind, + transport: TransportProtocol, + ); + + /// Called on each JSON-RPC method completion, batch requests will trigger + /// `on_result` multiple times. + fn on_result( + &self, + method_name: &str, + success: bool, + error_code: Option, + started_at: Self::Instant, + transport: TransportProtocol, + ); + + /// Called once the JSON-RPC request is finished and response is sent to the + /// output buffer. + fn on_response(&self, result: &str, started_at: Self::Instant, transport: TransportProtocol); + + /// Called when a client disconnects + fn on_disconnect(&self, _remote_addr: SocketAddr, transport: TransportProtocol); +} + +impl Logger for () { + type Instant = (); + + fn on_connect(&self, _: SocketAddr, _: &HttpRequest, _p: TransportProtocol) -> Self::Instant {} + + fn on_request(&self, _p: TransportProtocol) -> Self::Instant {} + + fn on_call(&self, _: &str, _: Params, _: MethodKind, _p: TransportProtocol) {} + + fn on_result(&self, _: &str, _: bool, _: Option, _: Self::Instant, _p: TransportProtocol) { + } + + fn on_response(&self, _: &str, _: Self::Instant, _p: TransportProtocol) {} + + fn on_disconnect(&self, _: SocketAddr, _p: TransportProtocol) {} +} + +impl Logger for (A, B) +where + A: Logger, + B: Logger, +{ + type Instant = (A::Instant, B::Instant); + + fn on_connect( + &self, + remote_addr: std::net::SocketAddr, + request: &HttpRequest, + transport: TransportProtocol, + ) { + self.0.on_connect(remote_addr, request, transport); + self.1.on_connect(remote_addr, request, transport); + } + + fn on_request(&self, transport: TransportProtocol) -> Self::Instant { + (self.0.on_request(transport), self.1.on_request(transport)) + } + + fn on_call( + &self, + method_name: &str, + params: Params, + kind: MethodKind, + transport: TransportProtocol, + ) { + self.0.on_call(method_name, params.clone(), kind, transport); + self.1.on_call(method_name, params, kind, transport); + } + + fn on_result( + &self, + method_name: &str, + success: bool, + error_code: Option, + started_at: Self::Instant, + transport: TransportProtocol, + ) { + self.0 + .on_result(method_name, success, error_code, started_at.0, transport); + self.1 + .on_result(method_name, success, error_code, started_at.1, transport); + } + + fn on_response(&self, result: &str, started_at: Self::Instant, transport: TransportProtocol) { + self.0.on_response(result, started_at.0, transport); + self.1.on_response(result, started_at.1, transport); + } + + fn on_disconnect(&self, remote_addr: SocketAddr, transport: TransportProtocol) { + self.0.on_disconnect(remote_addr, transport); + self.1.on_disconnect(remote_addr, transport); + } +} + +pub async fn with_tracing>>( + future: F, + timeout: impl Into>, +) -> RpcResult { + let timeout = timeout.into().unwrap_or(Duration::from_secs(1)); + async move { + let start = std::time::Instant::now(); + let interim_result: RpcInterimResult<_> = future.await; + let elapsed = start.elapsed(); + let result = interim_result.map_err(|e| { + let anyhow_error = anyhow!("{:?}", e); + + let rpc_error: RpcError = e.into(); + if !matches!(rpc_error, RpcError::Call(_)) { + error!(error=?anyhow_error); } - result - } - .instrument(Span::current()) - .await - }}; + error_object_from_rpc(rpc_error) + }); - ($future:expr) => {{ - with_tracing!(std::time::Duration::from_secs(1), $future) - }}; + if elapsed > timeout { + info!(?elapsed, "RPC took longer than threshold to complete."); + } + result + } + .instrument(Span::current()) + .await } diff --git a/crates/iota-json-rpc/src/metrics.rs b/crates/iota-json-rpc/src/metrics.rs index c19616a5538..5a2017eeb0d 100644 --- a/crates/iota-json-rpc/src/metrics.rs +++ b/crates/iota-json-rpc/src/metrics.rs @@ -4,20 +4,19 @@ use std::{collections::HashSet, net::SocketAddr}; -use hyper::body::HttpBody; +use http_body::Body; use iota_json_rpc_api::{ CLIENT_SDK_TYPE_HEADER, CLIENT_TARGET_API_VERSION_HEADER, TRANSIENT_ERROR_CODE, }; -use jsonrpsee::{ - server::logger::{HttpRequest, Logger, MethodKind, TransportProtocol}, - types::Params, -}; +use jsonrpsee::{server::HttpRequest, types::Params, MethodKind}; use prometheus::{ register_histogram_vec_with_registry, register_int_counter_vec_with_registry, register_int_gauge_vec_with_registry, HistogramVec, IntCounterVec, IntGaugeVec, }; use tokio::time::Instant; +use crate::logger::{Logger, TransportProtocol}; + const SPAM_LABEL: &str = "SPAM"; const LATENCY_SEC_BUCKETS: &[f64] = &[ 0.001, 0.005, 0.01, 0.05, 0.1, 0.25, 0.5, 1., 2.5, 5., 10., 20., 30., 60., 90., diff --git a/crates/iota-json-rpc/src/move_utils.rs b/crates/iota-json-rpc/src/move_utils.rs index b65a40e9a1e..fb39c4bcb3a 100644 --- a/crates/iota-json-rpc/src/move_utils.rs +++ b/crates/iota-json-rpc/src/move_utils.rs @@ -31,7 +31,8 @@ use tracing::{error, instrument, warn}; use crate::{ authority_state::StateRead, error::{Error, IotaRpcInputError}, - with_tracing, IotaRpcModule, + logger::with_tracing, + IotaRpcModule, }; #[cfg_attr(test, automock)] @@ -156,13 +157,17 @@ impl MoveUtilsServer for MoveUtils { &self, package: ObjectID, ) -> RpcResult> { - with_tracing!(async move { - let modules = self.internal.get_move_modules_by_package(package).await?; - Ok(modules - .into_iter() - .map(|(name, module)| (name, module.into())) - .collect::>()) - }) + with_tracing( + async move { + let modules = self.internal.get_move_modules_by_package(package).await?; + Ok(modules + .into_iter() + .map(|(name, module)| (name, module.into())) + .collect::>()) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -171,10 +176,14 @@ impl MoveUtilsServer for MoveUtils { package: ObjectID, module_name: String, ) -> RpcResult { - with_tracing!(async move { - let module = self.internal.get_move_module(package, module_name).await?; - Ok(module.into()) - }) + with_tracing( + async move { + let module = self.internal.get_move_module(package, module_name).await?; + Ok(module.into()) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -184,19 +193,23 @@ impl MoveUtilsServer for MoveUtils { module_name: String, struct_name: String, ) -> RpcResult { - with_tracing!(async move { - let module = self.internal.get_move_module(package, module_name).await?; - let structs = module.structs; - let identifier = Identifier::new(struct_name.as_str()) - .map_err(|e| IotaRpcInputError::GenericInvalid(format!("{e}")))?; - match structs.get(&identifier) { - Some(struct_) => Ok(struct_.clone().into()), - None => Err(IotaRpcInputError::GenericNotFound(format!( - "No struct was found with struct name {}", - struct_name - )))?, - } - }) + with_tracing( + async move { + let module = self.internal.get_move_module(package, module_name).await?; + let structs = module.structs; + let identifier = Identifier::new(struct_name.as_str()) + .map_err(|e| IotaRpcInputError::GenericInvalid(format!("{e}")))?; + match structs.get(&identifier) { + Some(struct_) => Ok(struct_.clone().into()), + None => Err(IotaRpcInputError::GenericNotFound(format!( + "No struct was found with struct name {}", + struct_name + )))?, + } + }, + None, + ) + .await } #[instrument(skip(self))] @@ -206,18 +219,22 @@ impl MoveUtilsServer for MoveUtils { module_name: String, function_name: String, ) -> RpcResult { - with_tracing!(async move { - let module = self.internal.get_move_module(package, module_name).await?; - let functions = module.functions; - let identifier = Identifier::new(function_name.as_str()) - .map_err(|e| IotaRpcInputError::GenericInvalid(format!("{e}")))?; - match functions.get(&identifier) { - Some(function) => Ok(function.clone().into()), - None => Err(IotaRpcInputError::GenericNotFound(format!( - "No function was found with function name {function_name}" - )))?, - } - }) + with_tracing( + async move { + let module = self.internal.get_move_module(package, module_name).await?; + let functions = module.functions; + let identifier = Identifier::new(function_name.as_str()) + .map_err(|e| IotaRpcInputError::GenericInvalid(format!("{e}")))?; + match functions.get(&identifier) { + Some(function) => Ok(function.clone().into()), + None => Err(IotaRpcInputError::GenericNotFound(format!( + "No function was found with function name {function_name}", + )))?, + } + }, + None, + ) + .await } #[instrument(skip(self))] @@ -227,57 +244,62 @@ impl MoveUtilsServer for MoveUtils { module: String, function: String, ) -> RpcResult> { - with_tracing!(async move { - let object_read = self.internal.get_object_read(package)?; - - let normalized = match object_read { - ObjectRead::Exists(_obj_ref, object, _layout) => match object.into_inner().data { - Data::Package(p) => { - // we are on the read path - it's OK to use VERSION_MAX of the supported - // Move binary format - let binary_config = BinaryConfig::with_extraneous_bytes_check(false); - normalize_modules(p.serialized_module_map().values(), &binary_config) - .map_err(Error::from) - } - _ => Err(IotaRpcInputError::GenericInvalid(format!( - "Object is not a package with ID {package}" - )))?, - }, - _ => Err(IotaRpcInputError::GenericNotFound(format!( - "Package object does not exist with ID {package}" - )))?, - }?; - - let identifier = Identifier::new(function.as_str()) - .map_err(|e| IotaRpcInputError::GenericInvalid(format!("{e}")))?; - let parameters = normalized - .get(&module) - .and_then(|m| m.functions.get(&identifier).map(|f| f.parameters.clone())); - - match parameters { - Some(parameters) => Ok(parameters - .iter() - .map(|p| match p { - Type::Struct { - address: _, - module: _, - name: _, - type_arguments: _, - } => MoveFunctionArgType::Object(ObjectValueKind::ByValue), - Type::Reference(_) => { - MoveFunctionArgType::Object(ObjectValueKind::ByImmutableReference) - } - Type::MutableReference(_) => { - MoveFunctionArgType::Object(ObjectValueKind::ByMutableReference) + with_tracing( + async move { + let object_read = self.internal.get_object_read(package)?; + + let normalized = match object_read { + ObjectRead::Exists(_obj_ref, object, _layout) => match object.into_inner().data + { + Data::Package(p) => { + // we are on the read path - it's OK to use VERSION_MAX of the supported + // Move binary format + let binary_config = BinaryConfig::with_extraneous_bytes_check(false); + normalize_modules(p.serialized_module_map().values(), &binary_config) + .map_err(Error::from) } - _ => MoveFunctionArgType::Pure, - }) - .collect::>()), - None => Err(IotaRpcInputError::GenericNotFound(format!( - "No parameters found for function {function}" - )))?, - } - }) + _ => Err(IotaRpcInputError::GenericInvalid(format!( + "Object is not a package with ID {package}", + )))?, + }, + _ => Err(IotaRpcInputError::GenericNotFound(format!( + "Package object does not exist with ID {package}", + )))?, + }?; + + let identifier = Identifier::new(function.as_str()) + .map_err(|e| IotaRpcInputError::GenericInvalid(format!("{e}")))?; + let parameters = normalized + .get(&module) + .and_then(|m| m.functions.get(&identifier).map(|f| f.parameters.clone())); + + match parameters { + Some(parameters) => Ok(parameters + .iter() + .map(|p| match p { + Type::Struct { + address: _, + module: _, + name: _, + type_arguments: _, + } => MoveFunctionArgType::Object(ObjectValueKind::ByValue), + Type::Reference(_) => { + MoveFunctionArgType::Object(ObjectValueKind::ByImmutableReference) + } + Type::MutableReference(_) => { + MoveFunctionArgType::Object(ObjectValueKind::ByMutableReference) + } + _ => MoveFunctionArgType::Pure, + }) + .collect::>()), + None => Err(IotaRpcInputError::GenericNotFound(format!( + "No parameters found for function {function}", + )))?, + } + }, + None, + ) + .await } } @@ -285,7 +307,6 @@ impl MoveUtilsServer for MoveUtils { mod tests { mod get_normalized_move_module_tests { - use jsonrpsee::types::ErrorObjectOwned; use move_binary_format::file_format::basic_test_module; use super::super::*; @@ -338,10 +359,9 @@ mod tests { .get_normalized_move_module(package, module_name) .await; let error_result = response.unwrap_err(); - let error_object: ErrorObjectOwned = error_result.into(); - assert_eq!(error_object.code(), -32602); - assert_eq!(error_object.message(), &error_string); + assert_eq!(error_result.code(), -32602); + assert_eq!(error_result.message(), &error_string); } } } diff --git a/crates/iota-json-rpc/src/read_api.rs b/crates/iota-json-rpc/src/read_api.rs index dbe40ea3c7d..d1b15ab1a18 100644 --- a/crates/iota-json-rpc/src/read_api.rs +++ b/crates/iota-json-rpc/src/read_api.rs @@ -16,7 +16,7 @@ use iota_json_rpc_api::{ use iota_json_rpc_types::{ BalanceChange, Checkpoint, CheckpointId, CheckpointPage, DisplayFieldsResponse, EventFilter, IotaEvent, IotaGetPastObjectRequest, IotaLoadedChildObject, IotaLoadedChildObjectsResponse, - IotaMoveStruct, IotaMoveValue, IotaObjectDataOptions, IotaObjectResponse, + IotaMoveStruct, IotaMoveValue, IotaObjectData, IotaObjectDataOptions, IotaObjectResponse, IotaPastObjectResponse, IotaTransactionBlock, IotaTransactionBlockEvents, IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, ObjectChange, ProtocolConfigResponse, @@ -49,13 +49,14 @@ use move_core_types::{ }; use mysten_metrics::spawn_monitored_task; use tap::TapFallible; -use tracing::{debug, error, info, instrument, trace, warn}; +use tracing::{debug, error, instrument, trace, warn}; use crate::{ authority_state::{StateRead, StateReadError, StateReadResult}, error::{Error, IotaRpcInputError, RpcInterimResult}, - get_balance_changes_from_effect, get_object_changes, with_tracing, IotaRpcModule, - ObjectProviderCache, + get_balance_changes_from_effect, get_object_changes, + logger::with_tracing, + IotaRpcModule, ObjectProviderCache, }; const MAX_DISPLAY_NESTED_LEVEL: usize = 10; @@ -489,52 +490,62 @@ impl ReadApiServer for ReadApi { object_id: ObjectID, options: Option, ) -> RpcResult { - with_tracing!(async move { - let state = self.state.clone(); - let object_read = spawn_monitored_task!(async move { - state.get_object_read(&object_id).map_err(|e| { - warn!(?object_id, "Failed to get object: {:?}", e); - Error::from(e) + with_tracing( + async move { + let state = self.state.clone(); + let object_read = spawn_monitored_task!(async move { + state.get_object_read(&object_id).map_err(|e| { + warn!(?object_id, "Failed to get object: {:?}", e); + Error::from(e) + }) }) - }) - .await - .map_err(Error::from)??; - let options = options.unwrap_or_default(); - - match object_read { - ObjectRead::NotExists(id) => Ok(IotaObjectResponse::new_with_error( - IotaObjectResponseError::NotExists { object_id: id }, - )), - ObjectRead::Exists(object_ref, o, layout) => { - let mut display_fields = None; - if options.show_display { - match get_display_fields(self, &self.transaction_kv_store, &o, &layout) - .await - { - Ok(rendered_fields) => display_fields = Some(rendered_fields), - Err(e) => { - return Ok(IotaObjectResponse::new( - Some((object_ref, o, layout, options, None).try_into()?), - Some(IotaObjectResponseError::DisplayError { - error: e.to_string(), - }), - )); + .await + .map_err(Error::from)??; + let options = options.unwrap_or_default(); + + match object_read { + ObjectRead::NotExists(id) => Ok(IotaObjectResponse::new_with_error( + IotaObjectResponseError::NotExists { object_id: id }, + )), + ObjectRead::Exists(object_ref, o, layout) => { + let mut display_fields = None; + if options.show_display { + match get_display_fields(self, &self.transaction_kv_store, &o, &layout) + .await + { + Ok(rendered_fields) => display_fields = Some(rendered_fields), + Err(e) => { + return Ok(IotaObjectResponse::new( + Some(IotaObjectData::new( + object_ref, o, layout, options, None, + )?), + Some(IotaObjectResponseError::DisplayError { + error: e.to_string(), + }), + )); + } } } + Ok(IotaObjectResponse::new_with_data(IotaObjectData::new( + object_ref, + o, + layout, + options, + display_fields, + )?)) } - Ok(IotaObjectResponse::new_with_data( - (object_ref, o, layout, options, display_fields).try_into()?, - )) + ObjectRead::Deleted((object_id, version, digest)) => Ok( + IotaObjectResponse::new_with_error(IotaObjectResponseError::Deleted { + object_id, + version, + digest, + }), + ), } - ObjectRead::Deleted((object_id, version, digest)) => Ok( - IotaObjectResponse::new_with_error(IotaObjectResponseError::Deleted { - object_id, - version, - digest, - }), - ), - } - }) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -543,45 +554,52 @@ impl ReadApiServer for ReadApi { object_ids: Vec, options: Option, ) -> RpcResult> { - with_tracing!(async move { - if object_ids.len() <= *QUERY_MAX_RESULT_LIMIT { - self.metrics - .get_objects_limit - .report(object_ids.len() as u64); - let mut futures = vec![]; - for object_id in object_ids { - futures.push(self.get_object(object_id, options.clone())); - } - let results = join_all(futures).await; - - let objects_result: Result, String> = results - .into_iter() - .map(|result| match result { - Ok(response) => Ok(response), - Err(error) => { - error!("Failed to fetch object with error: {error:?}"); - Err(format!("Error: {}", error)) - } - }) - .collect(); - - let objects = objects_result.map_err(|err| { - Error::UnexpectedError(format!("Failed to fetch objects with error: {}", err)) - })?; + with_tracing( + async move { + if object_ids.len() <= *QUERY_MAX_RESULT_LIMIT { + self.metrics + .get_objects_limit + .report(object_ids.len() as u64); + let mut futures = vec![]; + for object_id in object_ids { + futures.push(self.get_object(object_id, options.clone())); + } + let results = join_all(futures).await; - self.metrics - .get_objects_result_size - .report(objects.len() as u64); - self.metrics - .get_objects_result_size_total - .inc_by(objects.len() as u64); - Ok(objects) - } else { - Err(IotaRpcInputError::SizeLimitExceeded( - QUERY_MAX_RESULT_LIMIT.to_string(), - ))? - } - }) + let objects_result: Result, String> = results + .into_iter() + .map(|result| match result { + Ok(response) => Ok(response), + Err(error) => { + error!("Failed to fetch object with error: {error:?}"); + Err(format!("Error: {}", error)) + } + }) + .collect(); + + let objects = objects_result.map_err(|err| { + Error::UnexpectedError(format!( + "Failed to fetch objects with error: {}", + err + )) + })?; + + self.metrics + .get_objects_result_size + .report(objects.len() as u64); + self.metrics + .get_objects_result_size_total + .inc_by(objects.len() as u64); + Ok(objects) + } else { + Err(IotaRpcInputError::SizeLimitExceeded( + QUERY_MAX_RESULT_LIMIT.to_string(), + ))? + } + }, + None, + ) + .await } #[instrument(skip(self))] @@ -591,7 +609,7 @@ impl ReadApiServer for ReadApi { version: SequenceNumber, options: Option, ) -> RpcResult { - with_tracing!(async move { + with_tracing(async move { let state = self.state.clone(); let past_read = spawn_monitored_task!(async move { state.get_past_object_read(&object_id, version) @@ -620,7 +638,7 @@ impl ReadApiServer for ReadApi { None }; Ok(IotaPastObjectResponse::VersionFound( - (object_ref, o, layout, options, display_fields).try_into()?, + IotaObjectData::new(object_ref, o, layout, options, display_fields)?, )) } PastObjectRead::ObjectDeleted(oref) => { @@ -639,7 +657,10 @@ impl ReadApiServer for ReadApi { latest_version, }), } - }) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -648,48 +669,57 @@ impl ReadApiServer for ReadApi { past_objects: Vec, options: Option, ) -> RpcResult> { - with_tracing!(async move { - if past_objects.len() <= *QUERY_MAX_RESULT_LIMIT { - let mut futures = vec![]; - for past_object in past_objects { - futures.push(self.try_get_past_object( - past_object.object_id, - past_object.version, - options.clone(), - )); - } - let results = join_all(futures).await; - - let (oks, errs): (Vec<_>, Vec<_>) = results.into_iter().partition(Result::is_ok); - let success = oks.into_iter().filter_map(Result::ok).collect(); - let errors: Vec<_> = errs.into_iter().filter_map(Result::err).collect(); - if !errors.is_empty() { - let error_string = errors - .iter() - .map(|e| e.to_string()) - .collect::>() - .join("; "); - Err(anyhow!("{error_string}").into()) // Collects errors not related to IotaPastObjectResponse variants + with_tracing( + async move { + if past_objects.len() <= *QUERY_MAX_RESULT_LIMIT { + let mut futures = vec![]; + for past_object in past_objects { + futures.push(self.try_get_past_object( + past_object.object_id, + past_object.version, + options.clone(), + )); + } + let results = join_all(futures).await; + + let (oks, errs): (Vec<_>, Vec<_>) = + results.into_iter().partition(Result::is_ok); + let success = oks.into_iter().filter_map(Result::ok).collect(); + let errors: Vec<_> = errs.into_iter().filter_map(Result::err).collect(); + if !errors.is_empty() { + let error_string = errors + .iter() + .map(|e| e.to_string()) + .collect::>() + .join("; "); + Err(anyhow!("{error_string}").into()) // Collects errors not related to IotaPastObjectResponse variants + } else { + Ok(success) + } } else { - Ok(success) + Err(IotaRpcInputError::SizeLimitExceeded( + QUERY_MAX_RESULT_LIMIT.to_string(), + ))? } - } else { - Err(IotaRpcInputError::SizeLimitExceeded( - QUERY_MAX_RESULT_LIMIT.to_string(), - ))? - } - }) + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_total_transaction_blocks(&self) -> RpcResult> { - with_tracing!(async move { - Ok(self - .state - .get_total_transaction_blocks() - .map_err(Error::from)? - .into()) // converts into BigInt - }) + with_tracing( + async move { + Ok(self + .state + .get_total_transaction_blocks() + .map_err(Error::from)? + .into()) // converts into BigInt + }, + None, + ) + .await } #[instrument(skip(self))] @@ -698,7 +728,7 @@ impl ReadApiServer for ReadApi { digest: TransactionDigest, opts: Option, ) -> RpcResult { - with_tracing!(async move { + with_tracing(async move { let opts = opts.unwrap_or_default(); let mut temp_response = IntermediateTransactionResponse::new(digest); @@ -846,7 +876,10 @@ impl ReadApiServer for ReadApi { } let epoch_store = self.state.load_epoch_store_one_call_per_task(); convert_to_response(temp_response, &opts, epoch_store.module_cache()) - }) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -855,21 +888,25 @@ impl ReadApiServer for ReadApi { digests: Vec, opts: Option, ) -> RpcResult> { - with_tracing!(async move { - let cloned_self = self.clone(); - spawn_monitored_task!(async move { - cloned_self - .multi_get_transaction_blocks_internal(digests, opts) - .await - }) - .await - .map_err(Error::from)? - }) + with_tracing( + async move { + let cloned_self = self.clone(); + spawn_monitored_task!(async move { + cloned_self + .multi_get_transaction_blocks_internal(digests, opts) + .await + }) + .await + .map_err(Error::from)? + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_events(&self, transaction_digest: TransactionDigest) -> RpcResult> { - with_tracing!(async move { + with_tracing(async move { let state = self.state.clone(); let transaction_kv_store = self.transaction_kv_store.clone(); spawn_monitored_task!(async move{ @@ -879,55 +916,64 @@ impl ReadApiServer for ReadApi { .await .map_err(Error::from)?; let events = if let Some(event_digest) = effect.events_digest() { - transaction_kv_store - .get_events(*event_digest) - .await - .map_err( - |e| { - error!("Failed to get transaction events for event digest {event_digest:?} with error: {e:?}"); - Error::StateReadError(e.into()) - })? - .data - .into_iter() - .enumerate() - .map(|(seq, e)| { - let layout = store.executor().type_layout_resolver(Box::new(&state.get_backing_package_store().as_ref())).get_annotated_layout(&e.type_)?; - IotaEvent::try_from( - e, - *effect.transaction_digest(), - seq as u64, - None, - layout, - ) - }) - .collect::, _>>() - .map_err(Error::IotaError)? - } else { - vec![] - }; - Ok(events) - }).await.map_err(Error::from)? - }) + transaction_kv_store + .get_events(*event_digest) + .await + .map_err( + |e| { + error!("Failed to get transaction events for event digest {event_digest:?} with error: {e:?}"); + Error::StateReadError(e.into()) + })? + .data + .into_iter() + .enumerate() + .map(|(seq, e)| { + let layout = store.executor().type_layout_resolver(Box::new(&state.get_backing_package_store().as_ref())).get_annotated_layout(&e.type_)?; + IotaEvent::try_from( + e, + *effect.transaction_digest(), + seq as u64, + None, + layout, + ) + }) + .collect::, _>>() + .map_err(Error::IotaError)? + } else { + Vec::new() + }; + Ok(events) + }) + .await + .map_err(Error::from)? + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_latest_checkpoint_sequence_number(&self) -> RpcResult> { - with_tracing!(async move { - Ok(self - .state - .get_latest_checkpoint_sequence_number() - .map_err(|e| { - IotaRpcInputError::GenericNotFound(format!( - "Latest checkpoint sequence number was not found with error :{e}" - )) - })? - .into()) - }) + with_tracing( + async move { + Ok(self + .state + .get_latest_checkpoint_sequence_number() + .map_err(|e| { + IotaRpcInputError::GenericNotFound(format!( + "Latest checkpoint sequence number was not found with error :{e}" + )) + })? + .into()) + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_checkpoint(&self, id: CheckpointId) -> RpcResult { - with_tracing!(self.get_checkpoint_internal(id)) + with_tracing(self.get_checkpoint_internal(id), None).await } #[instrument(skip(self))] @@ -938,48 +984,52 @@ impl ReadApiServer for ReadApi { limit: Option, descending_order: bool, ) -> RpcResult { - with_tracing!(async move { - let limit = validate_limit(limit, QUERY_MAX_RESULT_LIMIT_CHECKPOINTS) - .map_err(IotaRpcInputError::from)?; + with_tracing( + async move { + let limit = validate_limit(limit, QUERY_MAX_RESULT_LIMIT_CHECKPOINTS) + .map_err(IotaRpcInputError::from)?; - let state = self.state.clone(); - let kv_store = self.transaction_kv_store.clone(); + let state = self.state.clone(); + let kv_store = self.transaction_kv_store.clone(); - self.metrics.get_checkpoints_limit.report(limit as u64); + self.metrics.get_checkpoints_limit.report(limit as u64); - let mut data = spawn_monitored_task!(Self::get_checkpoints_internal( - state, - kv_store, - cursor.map(|s| *s), - limit as u64 + 1, - descending_order, - )) - .await - .map_err(Error::from)? - .map_err(Error::from)?; + let mut data = spawn_monitored_task!(Self::get_checkpoints_internal( + state, + kv_store, + cursor.map(|s| *s), + limit as u64 + 1, + descending_order, + )) + .await + .map_err(Error::from)? + .map_err(Error::from)?; - let has_next_page = data.len() > limit; - data.truncate(limit); + let has_next_page = data.len() > limit; + data.truncate(limit); - let next_cursor = if has_next_page { - data.last().cloned().map(|d| d.sequence_number.into()) - } else { - None - }; - - self.metrics - .get_checkpoints_result_size - .report(data.len() as u64); - self.metrics - .get_checkpoints_result_size_total - .inc_by(data.len() as u64); - - Ok(CheckpointPage { - data, - next_cursor, - has_next_page, - }) - }) + let next_cursor = if has_next_page { + data.last().cloned().map(|d| d.sequence_number.into()) + } else { + None + }; + + self.metrics + .get_checkpoints_result_size + .report(data.len() as u64); + self.metrics + .get_checkpoints_result_size_total + .inc_by(data.len() as u64); + + Ok(CheckpointPage { + data, + next_cursor, + has_next_page, + }) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -989,11 +1039,8 @@ impl ReadApiServer for ReadApi { limit: Option>, descending_order: bool, ) -> RpcResult { - with_tracing!(async move { - self.get_checkpoints(cursor, limit.map(|l| *l as usize), descending_order) - .await - .map_err(Error::from) - }) + self.get_checkpoints(cursor, limit.map(|l| *l as usize), descending_order) + .await } #[instrument(skip(self))] @@ -1001,24 +1048,30 @@ impl ReadApiServer for ReadApi { &self, digest: TransactionDigest, ) -> RpcResult { - with_tracing!(async move { - let res = self - .state - .loaded_child_object_versions(&digest) - .map_err(|e| { - error!("Failed to get loaded child objects at {digest:?} with error: {e:?}"); - Error::StateReadError(e) - })?; - Ok(IotaLoadedChildObjectsResponse { - loaded_child_objects: match res { - Some(v) => v - .into_iter() - .map(|q| IotaLoadedChildObject::new(q.0, q.1)) - .collect::>(), - None => vec![], - }, - }) - }) + with_tracing( + async move { + let res = self + .state + .loaded_child_object_versions(&digest) + .map_err(|e| { + error!( + "Failed to get loaded child objects at {digest:?} with error: {e:?}" + ); + Error::StateReadError(e) + })?; + Ok(IotaLoadedChildObjectsResponse { + loaded_child_objects: match res { + Some(v) => v + .into_iter() + .map(|q| IotaLoadedChildObject::new(q.0, q.1)) + .collect::>(), + None => vec![], + }, + }) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -1026,34 +1079,42 @@ impl ReadApiServer for ReadApi { &self, version: Option>, ) -> RpcResult { - with_tracing!(async move { - version - .map(|v| { - ProtocolConfig::get_for_version_if_supported( - (*v).into(), - self.state.get_chain_identifier()?.chain(), - ) - .ok_or(IotaRpcInputError::ProtocolVersionUnsupported( - ProtocolVersion::MIN.as_u64(), - ProtocolVersion::MAX.as_u64(), - )) - .map_err(Error::from) - }) - .unwrap_or(Ok(self - .state - .load_epoch_store_one_call_per_task() - .protocol_config() - .clone())) - .map(ProtocolConfigResponse::from) - }) + with_tracing( + async move { + version + .map(|v| { + ProtocolConfig::get_for_version_if_supported( + (*v).into(), + self.state.get_chain_identifier()?.chain(), + ) + .ok_or(IotaRpcInputError::ProtocolVersionUnsupported( + ProtocolVersion::MIN.as_u64(), + ProtocolVersion::MAX.as_u64(), + )) + .map_err(Error::from) + }) + .unwrap_or(Ok(self + .state + .load_epoch_store_one_call_per_task() + .protocol_config() + .clone())) + .map(ProtocolConfigResponse::from) + }, + None, + ) + .await } #[instrument(skip(self))] async fn get_chain_identifier(&self) -> RpcResult { - with_tracing!(async move { - let ci = self.state.get_chain_identifier()?; - Ok(ci.to_string()) - }) + with_tracing( + async move { + let ci = self.state.get_chain_identifier()?; + Ok(ci.to_string()) + }, + None, + ) + .await } } diff --git a/crates/iota-json-rpc/src/transaction_builder_api.rs b/crates/iota-json-rpc/src/transaction_builder_api.rs index 8181cf0eb5e..2c034f7d2f3 100644 --- a/crates/iota-json-rpc/src/transaction_builder_api.rs +++ b/crates/iota-json-rpc/src/transaction_builder_api.rs @@ -8,7 +8,7 @@ use async_trait::async_trait; use fastcrypto::encoding::Base64; use iota_core::authority::AuthorityState; use iota_json::IotaJsonValue; -use iota_json_rpc_api::{TransactionBuilderOpenRpc, TransactionBuilderServer}; +use iota_json_rpc_api::{internal_error, TransactionBuilderOpenRpc, TransactionBuilderServer}; use iota_json_rpc_types::{ IotaObjectDataFilter, IotaObjectDataOptions, IotaObjectResponse, IotaTransactionBlockBuilderMode, IotaTypeTag, RPCTransactionRequestParams, @@ -91,8 +91,9 @@ impl TransactionBuilderServer for TransactionBuilderApi { let data = self .0 .transfer_object(signer, object_id, gas, *gas_budget, recipient) - .await?; - Ok(TransactionBlockBytes::from_data(data)?) + .await + .map_err(internal_error)?; + Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?) } async fn transfer_iota( @@ -112,8 +113,9 @@ impl TransactionBuilderServer for TransactionBuilderApi { recipient, amount.map(|a| *a), ) - .await?; - Ok(TransactionBlockBytes::from_data(data)?) + .await + .map_err(internal_error)?; + Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?) } async fn pay( @@ -135,8 +137,9 @@ impl TransactionBuilderServer for TransactionBuilderApi { gas, *gas_budget, ) - .await?; - Ok(TransactionBlockBytes::from_data(data)?) + .await + .map_err(internal_error)?; + Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?) } async fn pay_iota( @@ -156,8 +159,9 @@ impl TransactionBuilderServer for TransactionBuilderApi { amounts.into_iter().map(|a| *a).collect(), *gas_budget, ) - .await?; - Ok(TransactionBlockBytes::from_data(data)?) + .await + .map_err(internal_error)?; + Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?) } async fn pay_all_iota( @@ -170,8 +174,9 @@ impl TransactionBuilderServer for TransactionBuilderApi { let data = self .0 .pay_all_iota(signer, input_coins, recipient, *gas_budget) - .await?; - Ok(TransactionBlockBytes::from_data(data)?) + .await + .map_err(internal_error)?; + Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?) } async fn publish( @@ -185,12 +190,14 @@ impl TransactionBuilderServer for TransactionBuilderApi { let compiled_modules = compiled_modules .into_iter() .map(|data| data.to_vec().map_err(|e| anyhow::anyhow!(e))) - .collect::, _>>()?; + .collect::, _>>() + .map_err(internal_error)?; let data = self .0 .publish(sender, compiled_modules, dependencies, gas, *gas_budget) - .await?; - Ok(TransactionBlockBytes::from_data(data)?) + .await + .map_err(internal_error)?; + Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?) } async fn split_coin( @@ -205,8 +212,9 @@ impl TransactionBuilderServer for TransactionBuilderApi { let data = self .0 .split_coin(signer, coin_object_id, split_amounts, gas, *gas_budget) - .await?; - Ok(TransactionBlockBytes::from_data(data)?) + .await + .map_err(internal_error)?; + Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?) } async fn split_coin_equal( @@ -220,8 +228,9 @@ impl TransactionBuilderServer for TransactionBuilderApi { let data = self .0 .split_coin_equal(signer, coin_object_id, *split_count, gas, *gas_budget) - .await?; - Ok(TransactionBlockBytes::from_data(data)?) + .await + .map_err(internal_error)?; + Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?) } async fn merge_coin( @@ -235,8 +244,9 @@ impl TransactionBuilderServer for TransactionBuilderApi { let data = self .0 .merge_coins(signer, primary_coin, coin_to_merge, gas, *gas_budget) - .await?; - Ok(TransactionBlockBytes::from_data(data)?) + .await + .map_err(internal_error)?; + Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?) } async fn move_call( @@ -264,8 +274,10 @@ impl TransactionBuilderServer for TransactionBuilderApi { *gas_budget, None, ) - .await?, - )?) + .await + .map_err(internal_error)?, + ) + .map_err(internal_error)?) } async fn batch_transaction( @@ -279,8 +291,10 @@ impl TransactionBuilderServer for TransactionBuilderApi { Ok(TransactionBlockBytes::from_data( self.0 .batch_transaction(signer, params, gas, *gas_budget) - .await?, - )?) + .await + .map_err(internal_error)?, + ) + .map_err(internal_error)?) } async fn request_add_stake( @@ -296,8 +310,10 @@ impl TransactionBuilderServer for TransactionBuilderApi { Ok(TransactionBlockBytes::from_data( self.0 .request_add_stake(signer, coins, amount, validator, gas, *gas_budget) - .await?, - )?) + .await + .map_err(internal_error)?, + ) + .map_err(internal_error)?) } async fn request_withdraw_stake( @@ -310,8 +326,10 @@ impl TransactionBuilderServer for TransactionBuilderApi { Ok(TransactionBlockBytes::from_data( self.0 .request_withdraw_stake(signer, staked_iota, gas, *gas_budget) - .await?, - )?) + .await + .map_err(internal_error)?, + ) + .map_err(internal_error)?) } async fn request_add_timelocked_stake( @@ -325,8 +343,10 @@ impl TransactionBuilderServer for TransactionBuilderApi { Ok(TransactionBlockBytes::from_data( self.0 .request_add_timelocked_stake(signer, locked_balance, validator, gas, *gas_budget) - .await?, - )?) + .await + .map_err(internal_error)?, + ) + .map_err(internal_error)?) } async fn request_withdraw_timelocked_stake( @@ -339,8 +359,10 @@ impl TransactionBuilderServer for TransactionBuilderApi { Ok(TransactionBlockBytes::from_data( self.0 .request_withdraw_timelocked_stake(signer, timelocked_staked_iota, gas, *gas_budget) - .await?, - )?) + .await + .map_err(internal_error)?, + ) + .map_err(internal_error)?) } } diff --git a/crates/iota-json-rpc/src/transaction_execution_api.rs b/crates/iota-json-rpc/src/transaction_execution_api.rs index b5588fdc2f7..8767f2477e0 100644 --- a/crates/iota-json-rpc/src/transaction_execution_api.rs +++ b/crates/iota-json-rpc/src/transaction_execution_api.rs @@ -38,8 +38,9 @@ use tracing::instrument; use crate::{ authority_state::StateRead, error::{Error, IotaRpcInputError}, - get_balance_changes_from_effect, get_object_changes, with_tracing, IotaRpcModule, - ObjectProviderCache, + get_balance_changes_from_effect, get_object_changes, + logger::with_tracing, + IotaRpcModule, ObjectProviderCache, }; pub struct TransactionExecutionApi { @@ -282,10 +283,11 @@ impl WriteApiServer for TransactionExecutionApi { opts: Option, request_type: Option, ) -> RpcResult { - with_tracing!(Duration::from_secs(10), async move { - self.execute_transaction_block(tx_bytes, signatures, opts, request_type) - .await - }) + with_tracing( + self.execute_transaction_block(tx_bytes, signatures, opts, request_type), + Duration::from_secs(10), + ) + .await } #[instrument(skip(self))] @@ -297,29 +299,33 @@ impl WriteApiServer for TransactionExecutionApi { _epoch: Option>, additional_args: Option, ) -> RpcResult { - with_tracing!(async move { - let DevInspectArgs { - gas_sponsor, - gas_budget, - gas_objects, - show_raw_txn_data_and_effects, - skip_checks, - } = additional_args.unwrap_or_default(); - let tx_kind: TransactionKind = self.convert_bytes(tx_bytes)?; - self.state - .dev_inspect_transaction_block( - sender_address, - tx_kind, - gas_price.map(|i| *i), - gas_budget.map(|i| *i), + with_tracing( + async move { + let DevInspectArgs { gas_sponsor, + gas_budget, gas_objects, show_raw_txn_data_and_effects, skip_checks, - ) - .await - .map_err(Error::from) - }) + } = additional_args.unwrap_or_default(); + let tx_kind: TransactionKind = self.convert_bytes(tx_bytes)?; + self.state + .dev_inspect_transaction_block( + sender_address, + tx_kind, + gas_price.map(|i| *i), + gas_budget.map(|i| *i), + gas_sponsor, + gas_objects, + show_raw_txn_data_and_effects, + skip_checks, + ) + .await + .map_err(Error::from) + }, + None, + ) + .await } #[instrument(skip(self))] @@ -327,7 +333,7 @@ impl WriteApiServer for TransactionExecutionApi { &self, tx_bytes: Base64, ) -> RpcResult { - with_tracing!(async move { self.dry_run_transaction_block(tx_bytes).await }) + with_tracing(self.dry_run_transaction_block(tx_bytes), None).await } } diff --git a/crates/iota-light-client/src/main.rs b/crates/iota-light-client/src/main.rs index 6786e113c95..c76ecfad0ad 100644 --- a/crates/iota-light-client/src/main.rs +++ b/crates/iota-light-client/src/main.rs @@ -182,7 +182,8 @@ fn write_checkpoint_list( let mut checkpoints_path = config.checkpoint_summary_dir.clone(); checkpoints_path.push("checkpoints.yaml"); let mut writer = fs::File::create(checkpoints_path.clone())?; - let bytes = serde_yaml::to_vec(&checkpoints_list)?; + let mut bytes = Vec::new(); + serde_yaml::to_writer(&mut bytes, &checkpoints_list)?; writer .write_all(&bytes) .map_err(|_| anyhow!("Unable to serialize checkpoint list")) diff --git a/crates/iota-metric-checker/Cargo.toml b/crates/iota-metric-checker/Cargo.toml index 6882d28c4d1..0ec62202954 100644 --- a/crates/iota-metric-checker/Cargo.toml +++ b/crates/iota-metric-checker/Cargo.toml @@ -18,7 +18,7 @@ prometheus-http-query.workspace = true reqwest.workspace = true serde.workspace = true serde_yaml = "0.9.21" -strum_macros.workspace = true +strum.workspace = true telemetry-subscribers.workspace = true tokio = { workspace = true, features = ["full"] } tracing.workspace = true diff --git a/crates/iota-metric-checker/src/lib.rs b/crates/iota-metric-checker/src/lib.rs index ced987028ca..62623719ace 100644 --- a/crates/iota-metric-checker/src/lib.rs +++ b/crates/iota-metric-checker/src/lib.rs @@ -5,7 +5,7 @@ use anyhow::anyhow; use chrono::{DateTime, Duration, NaiveDateTime, Utc}; use humantime::parse_duration; use serde::Deserialize; -use strum_macros::Display; +use strum::Display; pub mod query; @@ -129,9 +129,9 @@ pub fn fails_threshold_condition( } fn unix_seconds_to_timestamp_string(unix_seconds: i64) -> String { - let datetime = NaiveDateTime::from_timestamp_opt(unix_seconds, 0); - let timestamp: DateTime = DateTime::from_naive_utc_and_offset(datetime.unwrap(), Utc); - timestamp.to_string() + DateTime::::from_timestamp(unix_seconds, 0) + .unwrap() + .to_string() } #[cfg(test)] diff --git a/crates/iota-move-build/Cargo.toml b/crates/iota-move-build/Cargo.toml index 3d2b92f1125..9ddb6783df9 100644 --- a/crates/iota-move-build/Cargo.toml +++ b/crates/iota-move-build/Cargo.toml @@ -17,7 +17,6 @@ move-bytecode-verifier = { path = "../../external-crates/move/crates/move-byteco iota-protocol-config.workspace = true iota-types.workspace = true -serde-reflection.workspace = true move-binary-format.workspace = true move-bytecode-utils.workspace = true diff --git a/crates/iota-move-build/src/lib.rs b/crates/iota-move-build/src/lib.rs index 1c2f7a0e1df..7ec2f15ebf9 100644 --- a/crates/iota-move-build/src/lib.rs +++ b/crates/iota-move-build/src/lib.rs @@ -27,7 +27,10 @@ use move_binary_format::{ normalized::{self, Type}, CompiledModule, }; -use move_bytecode_utils::{layout::SerdeLayoutBuilder, module_cache::GetModule}; +use move_bytecode_utils::{ + layout::{SerdeLayoutBuilder, YamlRegistry}, + module_cache::GetModule, +}; use move_compiler::{ compiled_unit::AnnotatedCompiledModule, diagnostics::{report_diagnostics_to_buffer, report_warnings, Diagnostics, FilesSourceText}, @@ -48,7 +51,6 @@ use move_package::{ BuildConfig as MoveBuildConfig, }; use move_symbol_pool::Symbol; -use serde_reflection::Registry; #[cfg(test)] #[path = "unit_tests/build_tests.rs"] @@ -456,7 +458,7 @@ impl CompiledPackage { /// These layout schemas can be consumed by clients (e.g., the TypeScript /// SDK) to enable BCS serialization/deserialization of the package's /// objects, tx arguments, and events. - pub fn generate_struct_layouts(&self) -> Registry { + pub fn generate_struct_layouts(&self) -> YamlRegistry { let mut package_types = BTreeSet::new(); for m in self.get_modules() { let normalized_m = normalized::Module::new(m); diff --git a/crates/iota-node/src/admin.rs b/crates/iota-node/src/admin.rs index 647d7c359d0..d7f917c1c38 100644 --- a/crates/iota-node/src/admin.rs +++ b/crates/iota-node/src/admin.rs @@ -101,10 +101,10 @@ pub async fn run_admin_server(node: Arc, port: u16, tracing_handle: Tr "starting admin server" ); - axum::Server::bind(&socket_address) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind(socket_address).await.unwrap(); + axum::serve(listener, app.into_make_service()) .await - .unwrap() + .unwrap(); } #[derive(Deserialize)] diff --git a/crates/iota-node/src/lib.rs b/crates/iota-node/src/lib.rs index 16e1997d7a9..7d95b392643 100644 --- a/crates/iota-node/src/lib.rs +++ b/crates/iota-node/src/lib.rs @@ -20,6 +20,7 @@ use anemo_tower::{ }; use anyhow::{anyhow, Result}; use arc_swap::ArcSwap; +use fastcrypto::error::FastCryptoError; use fastcrypto_zkp::bn254::zk_login::{JwkId, OIDCProvider, JWK}; use futures::TryFutureExt; pub use handle::IotaNodeHandle; @@ -674,7 +675,8 @@ impl IotaNode { &prometheus_registry, custom_rpc_runtime, software_version, - )?; + ) + .await?; let accumulator = Arc::new(StateAccumulator::new(store)); @@ -1762,7 +1764,6 @@ impl IotaNode { _authority: AuthorityName, provider: &OIDCProvider, ) -> IotaResult> { - use fastcrypto_zkp::bn254::zk_login::fetch_jwks; let client = reqwest::Client::new(); fetch_jwks(provider, &client) .await @@ -1770,6 +1771,31 @@ impl IotaNode { } } +pub async fn fetch_jwks( + provider: &OIDCProvider, + client: &reqwest::Client, +) -> Result, FastCryptoError> { + let response = client + .get(provider.get_config().jwk_endpoint) + .send() + .await + .map_err(|e| { + FastCryptoError::GeneralError(format!( + "Failed to get JWK {:?} {:?}", + e.to_string(), + provider + )) + })?; + let bytes = response.bytes().await.map_err(|e| { + FastCryptoError::GeneralError(format!( + "Failed to get bytes {:?} {:?}", + e.to_string(), + provider + )) + })?; + fastcrypto_zkp::bn254::zk_login::parse_jwks(&bytes, provider) +} + #[cfg(msim)] impl IotaNode { pub fn get_sim_node_id(&self) -> iota_simulator::task::NodeId { @@ -1852,7 +1878,7 @@ fn build_kv_store( ))) } -pub fn build_http_server( +pub async fn build_http_server( state: Arc, store: RocksDbStore, transaction_orchestrator: &Option>>, @@ -1944,10 +1970,14 @@ pub fn build_http_server( router = router.nest("/rest", rest_router); } - let server = axum::Server::bind(&config.json_rpc_address).serve(router.into_make_service()); + let listener = tokio::net::TcpListener::bind(&config.json_rpc_address).await?; - let addr = server.local_addr(); - let handle = tokio::spawn(async move { server.await.unwrap() }); + let addr = listener.local_addr()?; + let handle = tokio::spawn(async move { + axum::serve(listener, router.into_make_service()) + .await + .unwrap() + }); info!(local_addr =? addr, "Iota JSON-RPC server listening on {addr}"); diff --git a/crates/iota-package-resolver/src/lib.rs b/crates/iota-package-resolver/src/lib.rs index cd3db14c195..67194bee66f 100644 --- a/crates/iota-package-resolver/src/lib.rs +++ b/crates/iota-package-resolver/src/lib.rs @@ -1918,7 +1918,7 @@ mod tests { ], ); - insta::assert_display_snapshot!( + insta::assert_snapshot!( sig.instantiate(&[T::U64, T::Bool]).unwrap_err(), @"Type Parameter 99 out of bounds (2)" ); @@ -2304,7 +2304,7 @@ mod tests { ], }; - insta::assert_display_snapshot!( + insta::assert_snapshot!( resolver.pure_input_layouts(&ptb).await.unwrap_err(), @"Conflicting types for input 3: u64 and u32" ); diff --git a/crates/iota-replay/src/types.rs b/crates/iota-replay/src/types.rs index d7288783e69..904b9b9d44a 100644 --- a/crates/iota-replay/src/types.rs +++ b/crates/iota-replay/src/types.rs @@ -14,7 +14,7 @@ use iota_types::{ object::Object, transaction::{InputObjectKind, SenderSignedData, TransactionKind}, }; -use jsonrpsee::core::Error as JsonRpseeError; +use jsonrpsee::core::ClientError as JsonRpseeError; use move_binary_format::CompiledModule; use move_core_types::{ account_address::AccountAddress, diff --git a/crates/iota-rest-api/Cargo.toml b/crates/iota-rest-api/Cargo.toml index ccef523f406..eb1d7adbb42 100644 --- a/crates/iota-rest-api/Cargo.toml +++ b/crates/iota-rest-api/Cargo.toml @@ -17,6 +17,7 @@ serde_json.workspace = true serde_with.workspace = true tap.workspace = true thiserror.workspace = true +tokio.workspace = true fastcrypto.workspace = true iota-types.workspace = true diff --git a/crates/iota-rest-api/src/accept.rs b/crates/iota-rest-api/src/accept.rs index b535a09ee65..7af070ce8d8 100644 --- a/crates/iota-rest-api/src/accept.rs +++ b/crates/iota-rest-api/src/accept.rs @@ -78,7 +78,10 @@ where mod tests { use std::str::FromStr; - use axum::{extract::FromRequest, http::Request}; + use axum::{ + body::Body, + extract::{FromRequest, Request}, + }; use http::header; use super::*; @@ -90,7 +93,7 @@ mod tests { header::ACCEPT, "text/html, text/yaml;q=0.5, application/xhtml+xml, application/xml;q=0.9, */*;q=0.1", ) - .body(()) + .body(Body::empty()) .unwrap(); let accept = Accept::from_request(req, &()).await.unwrap(); assert_eq!( @@ -109,14 +112,14 @@ mod tests { async fn test_accept_format() { let req = Request::builder() .header(header::ACCEPT, "*/*, application/bcs") - .body(()) + .body(Body::empty()) .unwrap(); let accept = AcceptFormat::from_request(req, &()).await.unwrap(); assert_eq!(accept, AcceptFormat::Bcs); let req = Request::builder() .header(header::ACCEPT, "*/*") - .body(()) + .body(Body::empty()) .unwrap(); let accept = AcceptFormat::from_request(req, &()).await.unwrap(); assert_eq!(accept, AcceptFormat::Json); diff --git a/crates/iota-rest-api/src/lib.rs b/crates/iota-rest-api/src/lib.rs index 4dca060ff62..8dfb810dd27 100644 --- a/crates/iota-rest-api/src/lib.rs +++ b/crates/iota-rest-api/src/lib.rs @@ -78,8 +78,8 @@ impl RestService { app = Router::new().nest(&base, app); } - axum::Server::bind(&socket_address) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind(socket_address).await.unwrap(); + axum::serve(listener, app.into_make_service()) .await .unwrap(); } diff --git a/crates/iota-rosetta/Cargo.toml b/crates/iota-rosetta/Cargo.toml index 459562436dc..847bcbceff1 100644 --- a/crates/iota-rosetta/Cargo.toml +++ b/crates/iota-rosetta/Cargo.toml @@ -10,7 +10,7 @@ publish = false anyhow.workspace = true async-trait.workspace = true axum.workspace = true -axum-extra.workspace = true +axum-extra = { workspace = true, features = ["typed-header"] } bcs.workspace = true clap.workspace = true eyre.workspace = true @@ -31,7 +31,6 @@ serde_json.workspace = true shared-crypto.workspace = true signature.workspace = true strum.workspace = true -strum_macros.workspace = true tempfile.workspace = true thiserror.workspace = true tokio.workspace = true @@ -47,5 +46,6 @@ iota-move-build.workspace = true iota-sdk.workspace = true rand.workspace = true reqwest.workspace = true +signature = { workspace = true, features = ["rand_core"] } tempfile.workspace = true test-cluster.workspace = true diff --git a/crates/iota-rosetta/src/errors.rs b/crates/iota-rosetta/src/errors.rs index 373662a1de2..941b4221df6 100644 --- a/crates/iota-rosetta/src/errors.rs +++ b/crates/iota-rosetta/src/errors.rs @@ -14,8 +14,7 @@ use fastcrypto::error::FastCryptoError; use iota_types::error::IotaError; use serde::{Serialize, Serializer}; use serde_json::{json, Value}; -use strum::{EnumProperty, IntoEnumIterator}; -use strum_macros::{Display, EnumDiscriminants, EnumIter}; +use strum::{Display, EnumDiscriminants, EnumIter, EnumProperty, IntoEnumIterator}; use thiserror::Error; use typed_store::TypedStoreError; diff --git a/crates/iota-rosetta/src/lib.rs b/crates/iota-rosetta/src/lib.rs index 6d42c649e7e..ec33d8fe468 100644 --- a/crates/iota-rosetta/src/lib.rs +++ b/crates/iota-rosetta/src/lib.rs @@ -46,7 +46,7 @@ impl RosettaOnlineServer { } } - pub fn serve(self, addr: SocketAddr) -> JoinHandle> { + pub async fn serve(self, addr: SocketAddr) -> anyhow::Result>> { // Online endpoints let app = Router::new() .route("/account/balance", post(account::balance)) @@ -60,12 +60,14 @@ impl RosettaOnlineServer { .route("/network/options", post(network::options)) .layer(Extension(self.env)) .with_state(self.context); - let server = axum::Server::bind(&addr).serve(app.into_make_service()); + let listener = tokio::net::TcpListener::bind(addr).await?; info!( "Iota Rosetta online server listening on {}", - server.local_addr() + listener.local_addr()? ); - spawn_monitored_task!(server) + Ok(spawn_monitored_task!(async { + axum::serve(listener, app.into_make_service()).await + })) } } @@ -78,7 +80,7 @@ impl RosettaOfflineServer { Self { env } } - pub fn serve(self, addr: SocketAddr) -> JoinHandle> { + pub async fn serve(self, addr: SocketAddr) -> anyhow::Result>> { // Online endpoints let app = Router::new() .route("/construction/derive", post(construction::derive)) @@ -90,11 +92,13 @@ impl RosettaOfflineServer { .route("/network/list", post(network::list)) .route("/network/options", post(network::options)) .layer(Extension(self.env)); - let server = axum::Server::bind(&addr).serve(app.into_make_service()); + let listener = tokio::net::TcpListener::bind(addr).await?; info!( "Iota Rosetta offline server listening on {}", - server.local_addr() + listener.local_addr()? ); - spawn_monitored_task!(server) + Ok(spawn_monitored_task!(async { + axum::serve(listener, app.into_make_service()).await + })) } } diff --git a/crates/iota-rosetta/src/main.rs b/crates/iota-rosetta/src/main.rs index 250e6564240..44cd6086eaa 100644 --- a/crates/iota-rosetta/src/main.rs +++ b/crates/iota-rosetta/src/main.rs @@ -139,7 +139,7 @@ impl RosettaServerCommand { RosettaServerCommand::StartOfflineServer { env, addr } => { info!("Starting Rosetta Offline Server."); let server = RosettaOfflineServer::new(env); - server.serve(addr).await??; + server.serve(addr).await?.await??; } RosettaServerCommand::StartOnlineRemoteServer { env, @@ -154,7 +154,7 @@ impl RosettaServerCommand { let rosetta_path = data_path.join("rosetta_db"); info!("Rosetta db path : {rosetta_path:?}"); let rosetta = RosettaOnlineServer::new(env, iota_client); - rosetta.serve(addr).await??; + rosetta.serve(addr).await?.await??; } RosettaServerCommand::StartOnlineServer { @@ -187,7 +187,7 @@ impl RosettaServerCommand { let rosetta_path = data_path.join("rosetta_db"); info!("Rosetta db path : {rosetta_path:?}"); let rosetta = RosettaOnlineServer::new(env, iota_client); - rosetta.serve(addr).await??; + rosetta.serve(addr).await?.await??; } }; Ok(()) diff --git a/crates/iota-rosetta/src/types.rs b/crates/iota-rosetta/src/types.rs index 96afb99a4ff..f19deba2832 100644 --- a/crates/iota-rosetta/src/types.rs +++ b/crates/iota-rosetta/src/types.rs @@ -22,7 +22,7 @@ use iota_types::{ }; use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; -use strum_macros::{EnumIter, EnumString}; +use strum::{EnumIter, EnumString}; use crate::{ errors::{Error, ErrorType}, diff --git a/crates/iota-rosetta/tests/rosetta_client.rs b/crates/iota-rosetta/tests/rosetta_client.rs index b9aa14b4c60..1b20c818170 100644 --- a/crates/iota-rosetta/tests/rosetta_client.rs +++ b/crates/iota-rosetta/tests/rosetta_client.rs @@ -32,16 +32,22 @@ use tokio::task::JoinHandle; pub async fn start_rosetta_test_server( client: IotaClient, -) -> (RosettaClient, Vec>>) { +) -> (RosettaClient, Vec>>) { let online_server = RosettaOnlineServer::new(IotaEnv::LocalNet, client); let offline_server = RosettaOfflineServer::new(IotaEnv::LocalNet); let local_ip = local_ip_utils::localhost_for_testing(); let port = local_ip_utils::get_available_port(&local_ip); let rosetta_address = format!("{}:{}", local_ip, port); - let online_handle = online_server.serve(SocketAddr::from_str(&rosetta_address).unwrap()); + let online_handle = online_server + .serve(SocketAddr::from_str(&rosetta_address).unwrap()) + .await + .unwrap(); let offline_port = local_ip_utils::get_available_port(&local_ip); let offline_address = format!("{}:{}", local_ip, offline_port); - let offline_handle = offline_server.serve(SocketAddr::from_str(&offline_address).unwrap()); + let offline_handle = offline_server + .serve(SocketAddr::from_str(&offline_address).unwrap()) + .await + .unwrap(); // allow rosetta to process the genesis block. tokio::task::yield_now().await; diff --git a/crates/iota-rpc-loadgen/Cargo.toml b/crates/iota-rpc-loadgen/Cargo.toml index 8a205a821c0..b3bc5a6fb1a 100644 --- a/crates/iota-rpc-loadgen/Cargo.toml +++ b/crates/iota-rpc-loadgen/Cargo.toml @@ -28,7 +28,6 @@ iota-types = { workspace = true, features = ["test-utils"] } serde_json.workspace = true shared-crypto.workspace = true strum.workspace = true -strum_macros.workspace = true telemetry-subscribers.workspace = true [[bin]] diff --git a/crates/iota-rpc-loadgen/src/payload/mod.rs b/crates/iota-rpc-loadgen/src/payload/mod.rs index 984db2417c7..a2a54ade2d0 100644 --- a/crates/iota-rpc-loadgen/src/payload/mod.rs +++ b/crates/iota-rpc-loadgen/src/payload/mod.rs @@ -26,7 +26,7 @@ use iota_types::{ pub use rpc_command_processor::{ load_addresses_from_file, load_digests_from_file, load_objects_from_file, RpcCommandProcessor, }; -use strum_macros::EnumString; +use strum::EnumString; use crate::load_test::LoadTestConfig; diff --git a/crates/iota-sdk/src/error.rs b/crates/iota-sdk/src/error.rs index f2aabf86550..809973f33d0 100644 --- a/crates/iota-sdk/src/error.rs +++ b/crates/iota-sdk/src/error.rs @@ -12,7 +12,7 @@ pub type IotaRpcResult = Result; #[derive(Error, Debug)] pub enum Error { #[error(transparent)] - Rpc(#[from] jsonrpsee::core::Error), + Rpc(#[from] jsonrpsee::core::ClientError), #[error(transparent)] BcsSerialization(#[from] bcs::Error), #[error("Subscription error : {0}")] @@ -30,4 +30,6 @@ pub enum Error { }, #[error("Insufficient fund for address [{address}], requested amount: {amount}")] InsufficientFund { address: IotaAddress, amount: u128 }, + #[error(transparent)] + Json(#[from] serde_json::Error), } diff --git a/crates/iota-sdk/src/json_rpc_error.rs b/crates/iota-sdk/src/json_rpc_error.rs index e6bdf3b549e..4fb8aa49a37 100644 --- a/crates/iota-sdk/src/json_rpc_error.rs +++ b/crates/iota-sdk/src/json_rpc_error.rs @@ -1,8 +1,10 @@ // Copyright (c) Mysten Labs, Inc. // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 + +use iota_json_rpc_api::error_object_from_rpc; pub use iota_json_rpc_api::{TRANSACTION_EXECUTION_CLIENT_ERROR_CODE, TRANSIENT_ERROR_CODE}; -use jsonrpsee::types::{error::UNKNOWN_ERROR_CODE, ErrorObjectOwned}; +use jsonrpsee::types::error::UNKNOWN_ERROR_CODE; use thiserror::Error; #[derive(Error, Debug, Clone)] @@ -51,16 +53,17 @@ impl Error { } } -impl From for Error { - fn from(err: jsonrpsee::core::Error) -> Self { - // The following code relies on jsonrpsee's From for ErrorObjectOwned - // implementation It converts any variant that is not Error::Call into +impl From for Error { + fn from(err: jsonrpsee::core::ClientError) -> Self { + // The following code converts any variant that is not Error::Call into // an ErrorObject with UNKNOWN_ERROR_CODE - let error_object_owned: ErrorObjectOwned = err.into(); + let error_object_owned = error_object_from_rpc(err); Error { code: error_object_owned.code(), message: error_object_owned.message().to_string(), - data: None, + data: error_object_owned + .data() + .map(|v| serde_json::from_str(v.get()).expect("raw json is always valid")), } } } diff --git a/crates/iota-sdk/src/lib.rs b/crates/iota-sdk/src/lib.rs index 202581ff8eb..53ec9d70418 100644 --- a/crates/iota-sdk/src/lib.rs +++ b/crates/iota-sdk/src/lib.rs @@ -96,7 +96,7 @@ use jsonrpsee::{ core::client::ClientT, http_client::{HeaderMap, HeaderValue, HttpClient, HttpClientBuilder}, rpc_params, - ws_client::{WsClient, WsClientBuilder}, + ws_client::{PingConfig, WsClient, WsClientBuilder}, }; use move_core_types::language_storage::StructTag; use serde_json::Value; @@ -218,13 +218,13 @@ impl IotaClientBuilder { let ws = if let Some(url) = self.ws_url { let mut builder = WsClientBuilder::default() - .max_request_body_size(2 << 30) + .max_request_size(2 << 30) .max_concurrent_requests(self.max_concurrent_requests) .set_headers(headers.clone()) .request_timeout(self.request_timeout); if let Some(duration) = self.ws_ping_interval { - builder = builder.ping_interval(duration) + builder = builder.enable_ws_ping(PingConfig::new().ping_interval(duration)) } Some(builder.build(url).await?) @@ -233,8 +233,7 @@ impl IotaClientBuilder { }; let http = HttpClientBuilder::default() - .max_request_body_size(2 << 30) - .max_concurrent_requests(self.max_concurrent_requests) + .max_request_size(2 << 30) .set_headers(headers.clone()) .request_timeout(self.request_timeout) .build(http)?; diff --git a/crates/iota-single-node-benchmark/Cargo.toml b/crates/iota-single-node-benchmark/Cargo.toml index 946abffb50f..471045f39d9 100644 --- a/crates/iota-single-node-benchmark/Cargo.toml +++ b/crates/iota-single-node-benchmark/Cargo.toml @@ -28,7 +28,6 @@ prometheus.workspace = true serde = { version = "1.0.190", features = ["derive"] } serde_json.workspace = true strum.workspace = true -strum_macros.workspace = true telemetry-subscribers.workspace = true tokio = { workspace = true, features = ["full", "tracing", "test-util"] } tracing.workspace = true diff --git a/crates/iota-single-node-benchmark/src/command.rs b/crates/iota-single-node-benchmark/src/command.rs index fb2e31f4524..f9d76b82013 100644 --- a/crates/iota-single-node-benchmark/src/command.rs +++ b/crates/iota-single-node-benchmark/src/command.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use clap::{Parser, Subcommand, ValueEnum}; -use strum_macros::EnumIter; +use strum::EnumIter; #[derive(Parser)] #[clap( diff --git a/crates/iota-source-validation-service/Cargo.toml b/crates/iota-source-validation-service/Cargo.toml index 37dfd95bed0..93089b72df3 100644 --- a/crates/iota-source-validation-service/Cargo.toml +++ b/crates/iota-source-validation-service/Cargo.toml @@ -16,12 +16,12 @@ name = "iota-source-validation-service" [dependencies] anyhow = { version = "1.0.64", features = ["backtrace"] } clap.workspace = true -hyper = "0.14" +hyper.workspace = true jsonrpsee.workspace = true serde = { version = "1.0.144", features = ["derive"] } tempfile = "3.3.0" tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } -toml = { version = "0.7.4", features = ["preserve_order"] } +toml = { version = "0.8.14", features = ["preserve_order"] } tracing = "0.1.36" url = "2.3.1" @@ -46,7 +46,7 @@ tower-http.workspace = true [dev-dependencies] expect-test = "1.4.0" fs_extra = "1.3.0" -reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"] } +reqwest.workspace = true iota.workspace = true iota-json-rpc-types.workspace = true diff --git a/crates/iota-source-validation-service/src/lib.rs b/crates/iota-source-validation-service/src/lib.rs index 5845379cade..c825df46d92 100644 --- a/crates/iota-source-validation-service/src/lib.rs +++ b/crates/iota-source-validation-service/src/lib.rs @@ -6,7 +6,6 @@ use std::{ collections::BTreeMap, ffi::OsString, fmt, fs, - net::TcpListener, path::{Path, PathBuf}, process::Command, sync::{Arc, RwLock}, @@ -18,12 +17,11 @@ use axum::{ extract::{Query, State}, middleware::{self, Next}, response::{IntoResponse, Response}, - routing::{get, IntoMakeService}, - Extension, Json, Router, Server, + routing::get, + Extension, Json, Router, }; use hyper::{ http::{HeaderName, HeaderValue, Method}, - server::conn::AddrIncoming, HeaderMap, StatusCode, }; use iota_move::build::resolve_lock_file_path; @@ -39,7 +37,7 @@ use jsonrpsee::{ client::{Subscription, SubscriptionClientT}, params::ArrayParams, }, - ws_client::{WsClient, WsClientBuilder}, + ws_client::{PingConfig, WsClient, WsClientBuilder}, }; use move_core_types::account_address::AccountAddress; use move_package::{BuildConfig as MoveBuildConfig, LintFlag}; @@ -47,7 +45,7 @@ use move_symbol_pool::Symbol; use mysten_metrics::RegistryService; use prometheus::{register_int_counter_with_registry, IntCounter, Registry}; use serde::{Deserialize, Serialize}; -use tokio::sync::oneshot::Sender; +use tokio::{net::TcpListener, sync::oneshot::Sender}; use tower::ServiceBuilder; use tracing::{debug, error, info}; use url::Url; @@ -485,7 +483,7 @@ pub async fn watch_for_upgrades( }; let client: WsClient = WsClientBuilder::default() - .ping_interval(WS_PING_INTERVAL) + .enable_ws_ping(PingConfig::new().ping_interval(WS_PING_INTERVAL)) .build(websocket_url) .await?; let mut subscription: Subscription = client @@ -541,9 +539,9 @@ pub struct AppState { pub sources_list: NetworkLookup, } -pub fn serve( +pub async fn serve( app_state: Arc>, -) -> anyhow::Result>> { +) -> anyhow::Result>> { let app = Router::new() .route("/api", get(api_route)) .route("/api/list", get(list_route)) @@ -557,8 +555,10 @@ pub fn serve( .layer(middleware::from_fn(check_version_header)), ) .with_state(app_state); - let listener = TcpListener::bind(host_port())?; - Ok(Server::from_tcp(listener)?.serve(app.into_make_service())) + let listener = TcpListener::bind(host_port()).await?; + Ok(tokio::spawn(async { + Ok(axum::serve(listener, app.into_make_service()).await?) + })) } #[derive(Deserialize)] @@ -631,10 +631,10 @@ async fn api_route( } } -async fn check_version_header( +async fn check_version_header( headers: HeaderMap, - req: hyper::Request, - next: Next, + req: axum::extract::Request, + next: Next, ) -> Response { let version = headers .get(IOTA_SOURCE_VALIDATION_VERSION_HEADER) @@ -645,8 +645,7 @@ async fn check_version_header( match version { Some(v) if v != IOTA_SOURCE_VALIDATION_VERSION => { let error = format!( - "Unsupported version '{v}' specified in header \ - {IOTA_SOURCE_VALIDATION_VERSION_HEADER}" + "Unsupported version '{v}' specified in header {IOTA_SOURCE_VALIDATION_VERSION_HEADER}", ); let mut headers = HeaderMap::new(); headers.insert( @@ -695,7 +694,7 @@ impl SourceServiceMetrics { } } -pub fn start_prometheus_server(addr: TcpListener) -> RegistryService { +pub fn start_prometheus_server(listener: TcpListener) -> RegistryService { let registry = Registry::new(); let registry_service = RegistryService::new(registry); @@ -705,9 +704,7 @@ pub fn start_prometheus_server(addr: TcpListener) -> RegistryService { .layer(Extension(registry_service.clone())); tokio::spawn(async move { - axum::Server::from_tcp(addr) - .unwrap() - .serve(app.into_make_service()) + axum::serve(listener, app.into_make_service()) .await .unwrap(); }); diff --git a/crates/iota-source-validation-service/src/main.rs b/crates/iota-source-validation-service/src/main.rs index fd6899bfc2f..cb9c5d6c11f 100644 --- a/crates/iota-source-validation-service/src/main.rs +++ b/crates/iota-source-validation-service/src/main.rs @@ -44,7 +44,7 @@ pub async fn main() -> anyhow::Result<()> { let (sources, sources_list) = initialize(&package_config, tmp_dir.path()).await?; info!("verification complete in {:?}", start.elapsed()); - let metrics_listener = std::net::TcpListener::bind(METRICS_HOST_PORT)?; + let metrics_listener = tokio::net::TcpListener::bind(METRICS_HOST_PORT).await?; let registry_service = start_prometheus_server(metrics_listener); let prometheus_registry = registry_service.default_registry(); let metrics = SourceServiceMetrics::new(&prometheus_registry); @@ -91,7 +91,7 @@ pub async fn main() -> anyhow::Result<()> { } let app_state_copy = app_state.clone(); - let server = tokio::spawn(async { serve(app_state_copy)?.await.map_err(anyhow::Error::from) }); + let server = serve(app_state_copy).await?; threads.push(server); info!("serving on {}", host_port()); for t in threads { diff --git a/crates/iota-source-validation-service/tests/tests.rs b/crates/iota-source-validation-service/tests/tests.rs index fe8a987bbc8..6e4e6595497 100644 --- a/crates/iota-source-validation-service/tests/tests.rs +++ b/crates/iota-source-validation-service/tests/tests.rs @@ -317,7 +317,8 @@ async fn test_api_route() -> anyhow::Result<()> { metrics: None, sources_list, })); - tokio::spawn(serve(app_state).expect("Cannot start service.")); + + serve(app_state).await.expect("Cannot start service"); let client = Client::new(); @@ -329,7 +330,7 @@ async fn test_api_route() -> anyhow::Result<()> { )) .send() .await - .expect("Request failed.") + .expect("Request failed") .json::() .await?; @@ -370,7 +371,7 @@ async fn test_api_route() -> anyhow::Result<()> { #[tokio::test] async fn test_metrics_route() -> anyhow::Result<()> { // Start metrics server - let metrics_listener = std::net::TcpListener::bind(METRICS_HOST_PORT)?; + let metrics_listener = tokio::net::TcpListener::bind(METRICS_HOST_PORT).await?; let registry_service = start_prometheus_server(metrics_listener); let prometheus_registry = registry_service.default_registry(); SourceServiceMetrics::new(&prometheus_registry); diff --git a/crates/iota-storage/Cargo.toml b/crates/iota-storage/Cargo.toml index acef2214da3..c573f7a209b 100644 --- a/crates/iota-storage/Cargo.toml +++ b/crates/iota-storage/Cargo.toml @@ -15,11 +15,13 @@ bcs.workspace = true byteorder.workspace = true bytes.workspace = true chrono.workspace = true -clap = "4.3.2" +clap.workspace = true fastcrypto.workspace = true futures.workspace = true +http-body-util.workspace = true hyper.workspace = true hyper-rustls.workspace = true +hyper-util = { workspace = true, features = ["http2", "client-legacy", "tokio"] } indicatif.workspace = true integer-encoding.workspace = true itertools.workspace = true diff --git a/crates/iota-storage/src/http_key_value_store.rs b/crates/iota-storage/src/http_key_value_store.rs index f5eebdb174d..e05be69cef7 100644 --- a/crates/iota-storage/src/http_key_value_store.rs +++ b/crates/iota-storage/src/http_key_value_store.rs @@ -7,12 +7,16 @@ use std::{str::FromStr, sync::Arc}; use async_trait::async_trait; use bytes::Bytes; use futures::stream::{self, StreamExt}; +use http_body_util::BodyExt; use hyper::{ - client::HttpConnector, header::{HeaderValue, CONTENT_LENGTH}, - Client, Uri, + Uri, }; use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; +use hyper_util::{ + client::legacy::{connect::HttpConnector, Client}, + rt::TokioExecutor, +}; use iota_types::{ base_types::{ObjectID, SequenceNumber, VersionNumber}, digests::{ @@ -39,7 +43,7 @@ use crate::{ pub struct HttpKVStore { base_url: Url, - client: Arc>>, + client: Arc, reqwest::Body>>, } pub fn encode_digest>(digest: &T) -> String { @@ -136,9 +140,9 @@ impl HttpKVStore { .enable_http2() .build(); - let client = Client::builder() + let client = Client::builder(TokioExecutor::new()) .http2_only(true) - .build::<_, hyper::Body>(http); + .build::<_, reqwest::Body>(http); let base_url = if base_url.ends_with('/') { base_url.to_string() @@ -183,8 +187,10 @@ impl HttpKVStore { ); // return None if 400 if resp.status().is_success() { - hyper::body::to_bytes(resp.into_body()) + resp.into_body() + .collect() .await + .map(|c| c.to_bytes()) .map(Some) .into_iota_result() } else { diff --git a/crates/iota-storage/src/indexes.rs b/crates/iota-storage/src/indexes.rs index 60e91bcbbc6..07aa64dabe3 100644 --- a/crates/iota-storage/src/indexes.rs +++ b/crates/iota-storage/src/indexes.rs @@ -1486,7 +1486,7 @@ impl IndexStore { metrics.all_balance_lookup_from_db.inc(); let mut balances: HashMap = HashMap::new(); let coins = Self::get_owned_coins_iterator(&coin_index, owner, None)? - .group_by(|(coin_type, _obj_id, _coin)| coin_type.clone()); + .chunk_by(|(coin_type, _obj_id, _coin)| coin_type.clone()); for (coin_type, coins) in &coins { let mut total_balance = 0i128; let mut coin_object_count = 0; diff --git a/crates/iota-storage/src/object_store/http/mod.rs b/crates/iota-storage/src/object_store/http/mod.rs index 5bad654fe19..dd9aa2fbf2a 100644 --- a/crates/iota-storage/src/object_store/http/mod.rs +++ b/crates/iota-storage/src/object_store/http/mod.rs @@ -12,7 +12,7 @@ use anyhow::{anyhow, Context, Result}; use chrono::{DateTime, Utc}; use futures::{StreamExt, TryStreamExt}; use iota_config::object_storage_config::{ObjectStoreConfig, ObjectStoreType}; -use object_store::{path::Path, Error, GetResult, GetResultPayload, ObjectMeta}; +use object_store::{path::Path, Attributes, Error, GetResult, GetResultPayload, ObjectMeta}; use reqwest::{ header::{HeaderMap, CONTENT_LENGTH, ETAG, LAST_MODIFIED}, Client, Method, @@ -92,6 +92,7 @@ async fn get( range: 0..meta.size, payload: GetResultPayload::Stream(stream), meta, + attributes: Attributes::new(), }) } @@ -120,6 +121,7 @@ fn header_meta(location: &Path, headers: &HeaderMap) -> Result { last_modified, size: content_length, e_tag: Some(e_tag.to_string()), + version: None, }) } diff --git a/crates/iota-storage/src/object_store/mod.rs b/crates/iota-storage/src/object_store/mod.rs index 317ba0637f1..370d16678ae 100644 --- a/crates/iota-storage/src/object_store/mod.rs +++ b/crates/iota-storage/src/object_store/mod.rs @@ -76,7 +76,7 @@ impl ObjectStoreListExt for Arc { &self, src: Option<&Path>, ) -> object_store::Result>> { - self.list(src).await + Ok(self.list(src)) } } @@ -103,7 +103,7 @@ as_ref_put_ext_impl!(Box); #[async_trait] impl ObjectStorePutExt for Arc { async fn put_bytes(&self, src: &Path, bytes: Bytes) -> Result<()> { - self.put(src, bytes).await?; + self.put(src, bytes.into()).await?; Ok(()) } } diff --git a/crates/iota-test-validator/src/main.rs b/crates/iota-test-validator/src/main.rs index a2810000861..a1eb5b3cd8e 100644 --- a/crates/iota-test-validator/src/main.rs +++ b/crates/iota-test-validator/src/main.rs @@ -212,9 +212,8 @@ async fn start_faucet(cluster: &LocalNewCluster, port: u16) -> Result<()> { println!("Faucet URL: http://{}", addr); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await?; + let listener = tokio::net::TcpListener::bind(addr).await?; + axum::serve(listener, app.into_make_service()).await?; Ok(()) } diff --git a/crates/iota-tool/Cargo.toml b/crates/iota-tool/Cargo.toml index 22f3f874cb5..1edfa6b128d 100644 --- a/crates/iota-tool/Cargo.toml +++ b/crates/iota-tool/Cargo.toml @@ -27,7 +27,6 @@ ron.workspace = true serde.workspace = true serde_json.workspace = true strum.workspace = true -strum_macros.workspace = true tempfile.workspace = true tracing.workspace = true diff --git a/crates/iota-tool/src/db_tool/db_dump.rs b/crates/iota-tool/src/db_tool/db_dump.rs index 95608625328..9761abb91a8 100644 --- a/crates/iota-tool/src/db_tool/db_dump.rs +++ b/crates/iota-tool/src/db_tool/db_dump.rs @@ -30,7 +30,7 @@ use iota_storage::{mutex_table::RwLockTable, IndexStoreTables}; use iota_types::base_types::{EpochId, ObjectID}; use prometheus::Registry; use rocksdb::MultiThreaded; -use strum_macros::EnumString; +use strum::EnumString; use tracing::info; use typed_store::{ rocks::{default_db_options, MetricConf}, diff --git a/crates/iota-tool/src/lib.rs b/crates/iota-tool/src/lib.rs index a24aa779f50..a4451c21114 100644 --- a/crates/iota-tool/src/lib.rs +++ b/crates/iota-tool/src/lib.rs @@ -170,10 +170,10 @@ impl std::fmt::Display for GroupedObjectOutput { Ord::cmp(&b.2, &a.2) .then_with(|| Ord::cmp(&format!("{:?}", &b.5), &format!("{:?}", &a.5))) }) - .group_by(|(_, _, seq_num, _r, _ts, _)| **seq_num); + .chunk_by(|(_, _, seq_num, _r, _ts, _)| **seq_num); for (seq_num, group) in &responses { writeln!(f, "seq num: {}", seq_num.opt_debug("latest-seq-num"))?; - let cur_version_resp = group.group_by(|(_, _, _, r, _, _)| match r { + let cur_version_resp = group.chunk_by(|(_, _, _, r, _, _)| match r { Ok(result) => { let parent_tx_digest = result.object.previous_transaction; let obj_digest = result.object.compute_object_reference().2; @@ -395,7 +395,7 @@ pub async fn get_transaction_block( .sorted_by(|(k1, err1, _), (k2, err2, _)| { Ord::cmp(k1, k2).then_with(|| Ord::cmp(err1, err2)) }) - .group_by(|(_, _err, r)| { + .chunk_by(|(_, _err, r)| { r.2.as_ref().map(|ok_result| match &ok_result.status { TransactionStatus::Signed(_) => None, TransactionStatus::Executed(_, effects, _) => Some(( diff --git a/crates/iota-types/Cargo.toml b/crates/iota-types/Cargo.toml index 2d64e8b49df..a30b605c2d8 100644 --- a/crates/iota-types/Cargo.toml +++ b/crates/iota-types/Cargo.toml @@ -30,7 +30,6 @@ move-ir-types.workspace = true move-vm-profiler.workspace = true move-vm-test-utils.workspace = true move-vm-types.workspace = true -nonempty.workspace = true num-bigint = { version = "0.4", default-features = false, features = ["rand"] } num-rational = "0.4" num-traits = "0.2.18" @@ -47,7 +46,6 @@ serde_with.workspace = true signature.workspace = true static_assertions.workspace = true strum.workspace = true -strum_macros.workspace = true tap.workspace = true thiserror.workspace = true tonic.workspace = true diff --git a/crates/iota-types/src/crypto.rs b/crates/iota-types/src/crypto.rs index 2eeac54c787..eaea95aace4 100644 --- a/crates/iota-types/src/crypto.rs +++ b/crates/iota-types/src/crypto.rs @@ -1620,9 +1620,7 @@ pub mod bcs_signable_test { } } -#[derive( - Clone, Copy, Deserialize, Serialize, JsonSchema, Debug, EnumString, strum_macros::Display, -)] +#[derive(Clone, Copy, Deserialize, Serialize, JsonSchema, Debug, EnumString, strum::Display)] #[strum(serialize_all = "lowercase")] pub enum SignatureScheme { ED25519, diff --git a/crates/iota-types/src/error.rs b/crates/iota-types/src/error.rs index c00be815963..a23d88ad662 100644 --- a/crates/iota-types/src/error.rs +++ b/crates/iota-types/src/error.rs @@ -7,7 +7,7 @@ use std::{collections::BTreeMap, fmt::Debug}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use strum_macros::{AsRefStr, IntoStaticStr}; +use strum::{AsRefStr, IntoStaticStr}; use thiserror::Error; use tonic::Status; use typed_store_error::TypedStoreError; diff --git a/crates/iota-types/src/transaction.rs b/crates/iota-types/src/transaction.rs index 6f31b27db3e..5ed77d6596c 100644 --- a/crates/iota-types/src/transaction.rs +++ b/crates/iota-types/src/transaction.rs @@ -20,7 +20,6 @@ use move_core_types::{ identifier::{IdentStr, Identifier}, language_storage::TypeTag, }; -use nonempty::{nonempty, NonEmpty}; use serde::{Deserialize, Serialize}; use shared_crypto::intent::{Intent, IntentMessage, IntentScope}; use strum::IntoStaticStr; @@ -1914,7 +1913,7 @@ pub trait TransactionDataAPI { fn into_kind(self) -> TransactionKind; /// Transaction signer and Gas owner - fn signers(&self) -> NonEmpty; + fn signers(&self) -> Vec; fn gas_data(&self) -> &GasData; @@ -1981,8 +1980,8 @@ impl TransactionDataAPI for TransactionDataV1 { } /// Transaction signer and Gas owner - fn signers(&self) -> NonEmpty { - let mut signers = nonempty![self.sender]; + fn signers(&self) -> Vec { + let mut signers = vec![self.sender]; if self.gas_owner() != self.sender { signers.push(self.gas_owner()); } diff --git a/crates/iota-upgrade-compatibility-transactional-tests/tests/tests.rs b/crates/iota-upgrade-compatibility-transactional-tests/tests/tests.rs index 7708029680e..38bec190bee 100644 --- a/crates/iota-upgrade-compatibility-transactional-tests/tests/tests.rs +++ b/crates/iota-upgrade-compatibility-transactional-tests/tests/tests.rs @@ -140,7 +140,7 @@ fn check_all_compatibilities( .join("\n"); results.push_str(&inclusion_results); - insta::assert_display_snapshot!(name, results); + insta::assert_snapshot!(name, results); Ok(()) } diff --git a/crates/iota/Cargo.toml b/crates/iota/Cargo.toml index 25ced83cfac..7e99e716c24 100644 --- a/crates/iota/Cargo.toml +++ b/crates/iota/Cargo.toml @@ -11,6 +11,8 @@ anemo.workspace = true anyhow.workspace = true async-recursion.workspace = true async-trait.workspace = true +aws-config.workspace = true +aws-sdk-kms.workspace = true bcs.workspace = true bip32.workspace = true camino.workspace = true @@ -28,8 +30,6 @@ prometheus.workspace = true rand.workspace = true regex.workspace = true reqwest.workspace = true -rusoto_core.workspace = true -rusoto_kms.workspace = true serde.workspace = true serde_json.workspace = true serde_yaml.workspace = true diff --git a/crates/iota/src/keytool.rs b/crates/iota/src/keytool.rs index edbc8e12312..f1b1156b919 100644 --- a/crates/iota/src/keytool.rs +++ b/crates/iota/src/keytool.rs @@ -8,18 +8,24 @@ use std::{ }; use anyhow::anyhow; +use aws_sdk_kms::{ + primitives::Blob, + types::{MessageType, SigningAlgorithmSpec}, + Client as KmsClient, +}; use bip32::DerivationPath; use clap::*; use fastcrypto::{ ed25519::Ed25519KeyPair, encoding::{Base64, Encoding, Hex}, + error::FastCryptoError, hash::HashFunction, secp256k1::recoverable::Secp256k1Sig, traits::{KeyPair, ToFromBytes}, }; use fastcrypto_zkp::bn254::{ utils::{get_oidc_url, get_token_exchange_url}, - zk_login::{fetch_jwks, JwkId, OIDCProvider, JWK}, + zk_login::{JwkId, OIDCProvider, JWK}, zk_login_api::ZkLoginEnv, }; use im::hashmap::HashMap as ImHashMap; @@ -49,8 +55,6 @@ use iota_types::{ use json_to_table::{json_to_table, Orientation}; use num_bigint::BigUint; use rand::{rngs::StdRng, Rng, SeedableRng}; -use rusoto_core::Region; -use rusoto_kms::{Kms, KmsClient, SignRequest}; use serde::Serialize; use serde_json::json; use shared_crypto::intent::{Intent, IntentMessage, IntentScope, PersonalMessage}; @@ -870,27 +874,24 @@ impl KeyToolCommand { info!("Digest to sign: {:?}", Base64::encode(digest)); // Set up the KMS client in default region. - let region: Region = Region::default(); - let kms: KmsClient = KmsClient::new(region); - - // Construct the signing request. - let request: SignRequest = SignRequest { - key_id: keyid.to_string(), - message: digest.to_vec().into(), - message_type: Some("RAW".to_string()), - signing_algorithm: "ECDSA_SHA_256".to_string(), - ..Default::default() - }; + let config = aws_config::from_env().load().await; + let kms = KmsClient::new(&config); // Sign the message, normalize the signature and then compacts it // serialize_compact is loaded as bytes for Secp256k1Sinaturere - let response = kms.sign(request).await?; + let response = kms + .sign() + .key_id(keyid) + .message_type(MessageType::Raw) + .message(Blob::new(digest)) + .signing_algorithm(SigningAlgorithmSpec::EcdsaSha256) + .send() + .await?; let sig_bytes_der = response .signature - .map(|b| b.to_vec()) .expect("Requires Asymmetric Key Generated in KMS"); - let mut external_sig = Secp256k1Sig::from_der(&sig_bytes_der)?; + let mut external_sig = Secp256k1Sig::from_der(sig_bytes_der.as_ref())?; external_sig.normalize_s(); let sig_compact = external_sig.serialize_compact(); @@ -1173,6 +1174,31 @@ impl KeyToolCommand { } } +pub async fn fetch_jwks( + provider: &OIDCProvider, + client: &reqwest::Client, +) -> Result, FastCryptoError> { + let response = client + .get(provider.get_config().jwk_endpoint) + .send() + .await + .map_err(|e| { + FastCryptoError::GeneralError(format!( + "Failed to get JWK {:?} {:?}", + e.to_string(), + provider + )) + })?; + let bytes = response.bytes().await.map_err(|e| { + FastCryptoError::GeneralError(format!( + "Failed to get bytes {:?} {:?}", + e.to_string(), + provider + )) + })?; + fastcrypto_zkp::bn254::zk_login::parse_jwks(&bytes, provider) +} + impl From<&IotaKeyPair> for Key { fn from(skp: &IotaKeyPair) -> Self { Key::from(skp.public()) diff --git a/crates/iota/tests/ptb_files_tests.rs b/crates/iota/tests/ptb_files_tests.rs index 2e5c28512ae..78b130b9b8f 100644 --- a/crates/iota/tests/ptb_files_tests.rs +++ b/crates/iota/tests/ptb_files_tests.rs @@ -45,7 +45,7 @@ async fn test_ptb_files(path: &Path) -> datatest_stable::Result<()> { for e in rendered.iter() { results.push(format!("{:?}", e)); } - insta::assert_display_snapshot!(fname(), results.join("\n")); + insta::assert_snapshot!(fname(), results.join("\n")); return Ok(()); } }; @@ -98,7 +98,7 @@ async fn test_ptb_files(path: &Path) -> datatest_stable::Result<()> { } // === FINALLY DO THE ASSERTION === - insta::assert_display_snapshot!(fname(), results.join("\n")); + insta::assert_snapshot!(fname(), results.join("\n")); Ok(()) } diff --git a/crates/mysten-metrics/src/lib.rs b/crates/mysten-metrics/src/lib.rs index 209f9a0863a..a3426f1ed2f 100644 --- a/crates/mysten-metrics/src/lib.rs +++ b/crates/mysten-metrics/src/lib.rs @@ -375,10 +375,8 @@ pub fn start_prometheus_server(addr: SocketAddr) -> RegistryService { .layer(Extension(registry_service.clone())); tokio::spawn(async move { - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await - .unwrap(); + let listener = tokio::net::TcpListener::bind(addr).await?; + axum::serve(listener, app.into_make_service()).await }); registry_service @@ -462,12 +460,12 @@ mod tests { assert_eq!(metric_default.get_help(), "counter_desc"); let metric_1 = metrics.remove(0); - assert_eq!(metric_1.get_name(), "narwhal_counter_1"); - assert_eq!(metric_1.get_help(), "counter_1_desc"); + assert_eq!(metric_1.get_name(), "iota_counter_2"); + assert_eq!(metric_1.get_help(), "counter_2_desc"); let metric_2 = metrics.remove(0); - assert_eq!(metric_2.get_name(), "iota_counter_2"); - assert_eq!(metric_2.get_help(), "counter_2_desc"); + assert_eq!(metric_2.get_name(), "narwhal_counter_1"); + assert_eq!(metric_2.get_help(), "counter_1_desc"); // AND remove first registry assert!(registry_service.remove(registry_1_id)); diff --git a/crates/mysten-network/Cargo.toml b/crates/mysten-network/Cargo.toml index 1b92ccf8c69..829c02fc401 100644 --- a/crates/mysten-network/Cargo.toml +++ b/crates/mysten-network/Cargo.toml @@ -14,8 +14,9 @@ bytes.workspace = true eyre.workspace = true futures.workspace = true http.workspace = true +hyper-util = { workspace = true, features = ["tokio"] } multiaddr.workspace = true -pin-project-lite = "0.2.13" +pin-project-lite = "0.2.14" serde.workspace = true snap.workspace = true tokio = { workspace = true, features = ["sync", "rt", "macros"] } diff --git a/crates/mysten-network/src/client.rs b/crates/mysten-network/src/client.rs index 7c7507b412a..80dc83f831d 100644 --- a/crates/mysten-network/src/client.rs +++ b/crates/mysten-network/src/client.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 use eyre::{eyre, Context, Result}; +use hyper_util::rt::TokioIo; use tonic::transport::{Channel, Endpoint, Uri}; use crate::{ @@ -112,7 +113,11 @@ impl MyEndpoint { let path = path.clone(); // Connect to a Uds socket - tokio::net::UnixStream::connect(path) + async { + Ok::<_, std::io::Error>(TokioIo::new( + tokio::net::UnixStream::connect(path).await?, + )) + } })); } @@ -128,7 +133,11 @@ impl MyEndpoint { let path = path.clone(); // Connect to a Uds socket - tokio::net::UnixStream::connect(path) + async { + Ok::<_, std::io::Error>(TokioIo::new( + tokio::net::UnixStream::connect(path).await?, + )) + } })) .await .map_err(Into::into); diff --git a/crates/mysten-network/src/metrics.rs b/crates/mysten-network/src/metrics.rs index 246e00fc7f8..ca91be8f0e2 100644 --- a/crates/mysten-network/src/metrics.rs +++ b/crates/mysten-network/src/metrics.rs @@ -3,10 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 use std::time::Duration; -use tonic::{ - codegen::http::{header::HeaderName, HeaderValue, Request, Response}, - Code, Status, -}; +use http::{HeaderName, HeaderValue, Request, Response}; +use tonic::{Code, Status}; use tower_http::{ classify::GrpcFailureClass, trace::{OnFailure, OnRequest, OnResponse}, diff --git a/crates/mysten-network/src/server.rs b/crates/mysten-network/src/server.rs index 013efc4ea21..7c69adb0dce 100644 --- a/crates/mysten-network/src/server.rs +++ b/crates/mysten-network/src/server.rs @@ -48,7 +48,7 @@ pub struct ServerBuilder) -> Option; +type AddPathToHeaderFunction = fn(&Request) -> Option; type WrapperService = Stack< Stack< @@ -110,7 +110,7 @@ impl ServerBuilder { .global_concurrency_limit .map(tower::limit::GlobalConcurrencyLimitLayer::new); - fn add_path_to_request_header(request: &Request) -> Option { + fn add_path_to_request_header(request: &Request) -> Option { let path = request.uri().path(); Some(HeaderValue::from_str(path).unwrap()) } @@ -151,7 +151,7 @@ impl ServerBuilder { /// Add a new service to this Server. pub fn add_service(mut self, svc: S) -> Self where - S: Service, Response = Response, Error = Infallible> + S: Service, Response = Response, Error = Infallible> + NamedService + Clone + Send diff --git a/crates/telemetry-subscribers/Cargo.toml b/crates/telemetry-subscribers/Cargo.toml index e8cabfeb2bb..ef1e8b5b3cb 100644 --- a/crates/telemetry-subscribers/Cargo.toml +++ b/crates/telemetry-subscribers/Cargo.toml @@ -17,21 +17,21 @@ console-subscriber = { workspace = true, optional = true } crossterm.workspace = true futures.workspace = true once_cell.workspace = true -opentelemetry = { version = "0.20.0", features = ["rt-tokio"], optional = true } -opentelemetry-otlp = { version = "0.13.0", features = ["grpc-tonic"], optional = true } -opentelemetry-proto = { version = "0.3", optional = true } -opentelemetry_api = { version = "0.20.0", optional = true } +opentelemetry = { version = "0.23.0", optional = true } +opentelemetry-otlp = { version = "0.16.0", features = ["grpc-tonic"], optional = true } +opentelemetry-proto = { version = "0.6.0", optional = true } +opentelemetry_sdk = { version = "0.23.0", features = ["rt-tokio"], optional = true } prometheus.workspace = true tokio = { workspace = true, features = ["full"] } tracing.workspace = true tracing-appender.workspace = true -tracing-opentelemetry = { version = "0.21.0", optional = true } +tracing-opentelemetry = { version = "0.24.0", optional = true } tracing-subscriber.workspace = true # must use same version as opentelemetry for tonic and prost, so we can't use from # workspace -prost = "0.11.9" -tonic = { version = "0.9" } +prost = "0.12" +tonic = "0.11" [features] default = ["otlp"] @@ -39,9 +39,9 @@ tokio-console = ["console-subscriber"] otlp = [ "tracing-opentelemetry", "opentelemetry", + "opentelemetry_sdk", "opentelemetry-otlp", "opentelemetry-proto", - "opentelemetry_api", ] [dev-dependencies] diff --git a/crates/telemetry-subscribers/src/file_exporter.rs b/crates/telemetry-subscribers/src/file_exporter.rs index 5cf343fb157..e61696c26da 100644 --- a/crates/telemetry-subscribers/src/file_exporter.rs +++ b/crates/telemetry-subscribers/src/file_exporter.rs @@ -10,11 +10,9 @@ use std::{ }; use futures::{future::BoxFuture, FutureExt}; -use opentelemetry::{ - sdk::export::trace::{ExportResult, SpanData, SpanExporter}, - trace::TraceError, -}; +use opentelemetry::trace::TraceError; use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest; +use opentelemetry_sdk::export::trace::{ExportResult, SpanData, SpanExporter}; use prost::Message; #[derive(Clone)] diff --git a/crates/telemetry-subscribers/src/lib.rs b/crates/telemetry-subscribers/src/lib.rs index b1b954facf0..501dbc37bcf 100644 --- a/crates/telemetry-subscribers/src/lib.rs +++ b/crates/telemetry-subscribers/src/lib.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 use std::{ - collections::hash_map::RandomState, env, io::{stderr, Write}, path::PathBuf, @@ -16,18 +15,14 @@ use atomic_float::AtomicF64; use crossterm::tty::IsTty; use once_cell::sync::Lazy; use opentelemetry::{ - sdk::{ - self, runtime, - trace::{BatchSpanProcessor, Sampler, ShouldSample, TracerProvider}, - Resource, - }, - trace::TracerProvider as _, -}; -use opentelemetry_api::{ - trace::{Link, SamplingResult, SpanKind, TraceId}, - Context, Key, OrderMap, Value, + trace::{Link, SamplingResult, SpanKind, TraceId, TracerProvider as _}, + Context, KeyValue, }; use opentelemetry_otlp::WithExportConfig; +use opentelemetry_sdk::{ + trace::{BatchSpanProcessor, Sampler, ShouldSample, TracerProvider}, + Resource, +}; use span_latency_prom::PrometheusSpanLatencyLayer; use tracing::{error, info, metadata::LevelFilter, Level}; use tracing_appender::non_blocking::{NonBlocking, WorkerGuard}; @@ -389,7 +384,7 @@ impl TelemetryConfig { if config.enable_otlp_tracing { let trace_file = env::var("TRACE_FILE").ok(); - let config = sdk::trace::config() + let config = opentelemetry_sdk::trace::config() .with_resource(Resource::new(vec![opentelemetry::KeyValue::new( "service.name", "iota-node", @@ -402,7 +397,9 @@ impl TelemetryConfig { let exporter = FileExporter::new(Some(trace_file.into())).expect("Failed to create exporter"); file_output = exporter.cached_open_file.clone(); - let processor = BatchSpanProcessor::builder(exporter, runtime::Tokio).build(); + let processor = + BatchSpanProcessor::builder(exporter, opentelemetry_sdk::runtime::Tokio) + .build(); let p = TracerProvider::builder() .with_config(config) @@ -425,7 +422,7 @@ impl TelemetryConfig { .with_endpoint(endpoint), ) .with_trace_config(config) - .install_batch(sdk::runtime::Tokio) + .install_batch(opentelemetry_sdk::runtime::Tokio) .expect("Could not create async Tracer"); tracing_opentelemetry::layer().with_tracer(tracer) @@ -433,7 +430,7 @@ impl TelemetryConfig { // Enable Trace Contexts for tying spans together opentelemetry::global::set_text_map_propagator( - opentelemetry::sdk::propagation::TraceContextPropagator::new(), + opentelemetry_sdk::propagation::TraceContextPropagator::new(), ); let trace_env_filter = EnvFilter::try_from_env("TRACE_FILTER").unwrap(); @@ -522,7 +519,7 @@ impl ShouldSample for SamplingFilter { trace_id: TraceId, name: &str, span_kind: &SpanKind, - attributes: &OrderMap, + attributes: &[KeyValue], links: &[Link], ) -> SamplingResult { let sample_rate = self.sample_rate.load(Ordering::Relaxed); diff --git a/iota-execution/cut/src/plan.rs b/iota-execution/cut/src/plan.rs index 8c4b09fc620..86ae1d9f7f9 100644 --- a/iota-execution/cut/src/plan.rs +++ b/iota-execution/cut/src/plan.rs @@ -11,7 +11,7 @@ use std::{ use anyhow::{bail, Context, Result}; use thiserror::Error; use toml::value::Value; -use toml_edit::{self, Document, Item}; +use toml_edit::{self, DocumentMut, Item}; use crate::{ args::Args, @@ -321,7 +321,7 @@ impl CutPlan { /// information). fn update_package(&self, package: &CutPackage) -> Result<()> { let path = package.dst_path.join("Cargo.toml"); - let mut toml = fs::read_to_string(&path)?.parse::()?; + let mut toml = fs::read_to_string(&path)?.parse::()?; // Update the package name toml["package"]["name"] = toml_edit::value(&package.dst_name); @@ -422,7 +422,7 @@ impl CutPlan { bail!(Error::NoWorkspace(path)); } - let mut toml = fs::read_to_string(&path)?.parse::()?; + let mut toml = fs::read_to_string(&path)?.parse::()?; for package in self.packages.values() { match package.ws_state { WorkspaceState::Unknown => { @@ -853,12 +853,6 @@ mod tests { "$PATH/iota-execution/exec-cut", }, packages: { - "move-core-types": CutPackage { - dst_name: "move-core-types-feature", - src_path: "$PATH/external-crates/move/crates/move-core-types", - dst_path: "$PATH/iota-execution/cut-move-core-types", - ws_state: Exclude, - }, "iota-adapter-latest": CutPackage { dst_name: "iota-adapter-feature", src_path: "$PATH/iota-execution/latest/iota-adapter", @@ -877,6 +871,12 @@ mod tests { dst_path: "$PATH/iota-execution/exec-cut/iota-verifier", ws_state: Member, }, + "move-core-types": CutPackage { + dst_name: "move-core-types-feature", + src_path: "$PATH/external-crates/move/crates/move-core-types", + dst_path: "$PATH/iota-execution/cut-move-core-types", + ws_state: Exclude, + }, }, }"#]] .assert_eq(&debug_for_test(&plan)); @@ -954,12 +954,6 @@ mod tests { "$PATH/iota-execution/feature", }, packages: { - "move-core-types": CutPackage { - dst_name: "move-core-types-feature", - src_path: "$PATH/external-crates/move/crates/move-core-types", - dst_path: "$PATH/iota-execution/feature/move/crates/move-core-types", - ws_state: Exclude, - }, "iota-adapter-latest": CutPackage { dst_name: "iota-adapter-feature", src_path: "$PATH/iota-execution/latest/iota-adapter", @@ -978,6 +972,12 @@ mod tests { dst_path: "$PATH/iota-execution/feature/iota-verifier", ws_state: Member, }, + "move-core-types": CutPackage { + dst_name: "move-core-types-feature", + src_path: "$PATH/external-crates/move/crates/move-core-types", + dst_path: "$PATH/iota-execution/feature/move/crates/move-core-types", + ws_state: Exclude, + }, }, }"#]] .assert_eq(&debug_for_test(&plan)); @@ -1361,12 +1361,6 @@ mod tests { "$PATH/iota-execution/exec-cut", }, packages: { - "move-core-types": CutPackage { - dst_name: "move-core-types-feature", - src_path: "$PATH/external-crates/move/crates/move-core-types", - dst_path: "$PATH/iota-execution/cut-move-core-types", - ws_state: Unknown, - }, "iota-adapter-latest": CutPackage { dst_name: "iota-adapter-feature", src_path: "$PATH/iota-execution/latest/iota-adapter", @@ -1385,6 +1379,12 @@ mod tests { dst_path: "$PATH/iota-execution/exec-cut/iota-verifier", ws_state: Unknown, }, + "move-core-types": CutPackage { + dst_name: "move-core-types-feature", + src_path: "$PATH/external-crates/move/crates/move-core-types", + dst_path: "$PATH/iota-execution/cut-move-core-types", + ws_state: Unknown, + }, }, }"#]] .assert_eq(&debug_for_test(&plan)); diff --git a/narwhal/network/src/admin.rs b/narwhal/network/src/admin.rs index 3a982d8ab0b..c2bf95ea7b4 100644 --- a/narwhal/network/src/admin.rs +++ b/narwhal/network/src/admin.rs @@ -37,7 +37,7 @@ pub fn start_admin_server( // Spawn a task to shutdown server. handles.push(spawn_monitored_task!(async move { _ = tr_shutdown.receiver.recv().await; - handle.clone().shutdown(); + handle.shutdown(); })); handles.push(spawn_logged_monitored_task!( diff --git a/narwhal/node/Cargo.toml b/narwhal/node/Cargo.toml index b13c3e867e4..68d28d9aa44 100644 --- a/narwhal/node/Cargo.toml +++ b/narwhal/node/Cargo.toml @@ -46,7 +46,9 @@ anemo.workspace = true reqwest.workspace = true [dev-dependencies] +move-bytecode-utils.workspace = true pretty_assertions.workspace = true +serde.workspace = true serde-reflection.workspace = true serde_yaml.workspace = true test-utils = { path = "../test-utils", package = "narwhal-test-utils" } diff --git a/narwhal/node/src/generate_format.rs b/narwhal/node/src/generate_format.rs index 15a024756f4..bf9b4917784 100644 --- a/narwhal/node/src/generate_format.rs +++ b/narwhal/node/src/generate_format.rs @@ -10,9 +10,10 @@ use fastcrypto::{ hash::Hash, traits::{KeyPair as _, Signer}, }; +use move_bytecode_utils::layout::YamlRegistry; use mysten_network::Multiaddr; use rand::{prelude::StdRng, SeedableRng}; -use serde_reflection::{Registry, Result, Samples, Tracer, TracerConfig}; +use serde_reflection::{Result, Samples, Tracer, TracerConfig}; use types::{ Batch, BatchDigest, Certificate, CertificateDigest, Header, HeaderDigest, HeaderV1Builder, MetadataV1, VersionedMetadata, WorkerOthersBatchMessage, WorkerOwnBatchMessage, @@ -20,7 +21,7 @@ use types::{ }; #[allow(clippy::mutable_key_type)] -fn get_registry() -> Result { +fn get_registry() -> Result { let mut tracer = Tracer::new(TracerConfig::default()); let mut samples = Samples::new(); // 1. Record samples for types with custom deserializers. @@ -141,7 +142,7 @@ fn get_registry() -> Result { tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; - tracer.registry() + Ok(YamlRegistry(tracer.registry()?)) } #[derive(Debug, clap::ValueEnum, Clone, Copy)] @@ -181,7 +182,7 @@ fn main() { // cargo -q run --example narwhal-generate-format -- print > // tests/staged/narwhal.yaml let reference = std::fs::read_to_string(FILE_PATH).unwrap(); - let reference: Registry = serde_yaml::from_str(&reference).unwrap(); + let reference: YamlRegistry = serde_yaml::from_str(&reference).unwrap(); pretty_assertions::assert_eq!(reference, registry); } } diff --git a/narwhal/node/src/metrics.rs b/narwhal/node/src/metrics.rs index e2d88e82adf..36b10e39b6b 100644 --- a/narwhal/node/src/metrics.rs +++ b/narwhal/node/src/metrics.rs @@ -107,8 +107,8 @@ pub fn start_prometheus_server(addr: Multiaddr, registry: &Registry) -> JoinHand spawn_logged_monitored_task!( async move { - axum::Server::bind(&socket_addr) - .serve(app.into_make_service()) + let listener = tokio::net::TcpListener::bind(socket_addr).await.unwrap(); + axum::serve(listener, app.into_make_service()) .await .unwrap(); }, diff --git a/narwhal/node/tests/formats.rs b/narwhal/node/tests/formats.rs index a0efe399ef5..50caa3bd625 100644 --- a/narwhal/node/tests/formats.rs +++ b/narwhal/node/tests/formats.rs @@ -4,9 +4,10 @@ #[test] fn test_format() { + #[rustfmt::skip] // If this test breaks and you intended a format change, you need to run to get - // the fresh format: # cargo -q run --example narwhal-generate-format -- - // print > narwhal/node/tests/staged/narwhal.yaml + // the fresh format: + // # cargo -q run --example narwhal-generate-format -- print > narwhal/node/tests/staged/narwhal.yaml let status = std::process::Command::new("cargo") .current_dir("..") diff --git a/narwhal/node/tests/staged/narwhal.yaml b/narwhal/node/tests/staged/narwhal.yaml index 951b16ef56e..1e172f0fbac 100644 --- a/narwhal/node/tests/staged/narwhal.yaml +++ b/narwhal/node/tests/staged/narwhal.yaml @@ -1,21 +1,20 @@ ---- Authority: STRUCT: - - protocol_key: - TUPLEARRAY: - CONTENT: U8 - SIZE: 96 - - protocol_key_bytes: - TUPLEARRAY: - CONTENT: U8 - SIZE: 96 - - stake: U64 - - primary_address: STR - - network_key: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 - - hostname: STR + - protocol_key: + TUPLEARRAY: + CONTENT: U8 + SIZE: 96 + - protocol_key_bytes: + TUPLEARRAY: + CONTENT: U8 + SIZE: 96 + - stake: U64 + - primary_address: STR + - network_key: + TUPLEARRAY: + CONTENT: U8 + SIZE: 32 + - hostname: STR AuthorityIdentifier: NEWTYPESTRUCT: U16 Batch: @@ -31,11 +30,11 @@ BatchDigest: SIZE: 32 BatchV2: STRUCT: - - transactions: - SEQ: - SEQ: U8 - - versioned_metadata: - TYPENAME: VersionedMetadata + - transactions: + SEQ: + SEQ: U8 + - versioned_metadata: + TYPENAME: VersionedMetadata Certificate: ENUM: 0: @@ -49,24 +48,24 @@ CertificateDigest: SIZE: 32 CertificateV2: STRUCT: - - header: - TYPENAME: Header - - signature_verification_state: - TYPENAME: SignatureVerificationState - - signed_authorities: BYTES - - metadata: - TYPENAME: Metadata + - header: + TYPENAME: Header + - signature_verification_state: + TYPENAME: SignatureVerificationState + - signed_authorities: BYTES + - metadata: + TYPENAME: Metadata Committee: STRUCT: - - authorities: - MAP: - KEY: - TUPLEARRAY: - CONTENT: U8 - SIZE: 96 - VALUE: - TYPENAME: Authority - - epoch: U64 + - authorities: + MAP: + KEY: + TUPLEARRAY: + CONTENT: U8 + SIZE: 96 + VALUE: + TYPENAME: Authority + - epoch: U64 Header: ENUM: 0: @@ -80,29 +79,29 @@ HeaderDigest: SIZE: 32 HeaderV1: STRUCT: - - author: - TYPENAME: AuthorityIdentifier - - round: U64 - - epoch: U64 - - created_at: U64 - - payload: - SEQ: - TUPLE: - - TYPENAME: BatchDigest - - TUPLE: - - U32 - - U64 - - parents: - SEQ: - TYPENAME: CertificateDigest + - author: + TYPENAME: AuthorityIdentifier + - round: U64 + - epoch: U64 + - created_at: U64 + - payload: + SEQ: + TUPLE: + - TYPENAME: BatchDigest + - TUPLE: + - U32 + - U64 + - parents: + SEQ: + TYPENAME: CertificateDigest Metadata: STRUCT: - - created_at: U64 + - created_at: U64 MetadataV1: STRUCT: - - created_at: U64 - - received_at: - OPTION: U64 + - created_at: U64 + - received_at: + OPTION: U64 SignatureVerificationState: ENUM: 0: @@ -125,29 +124,30 @@ WorkerIndex: TYPENAME: WorkerInfo WorkerInfo: STRUCT: - - name: - TUPLEARRAY: - CONTENT: U8 - SIZE: 32 - - transactions: STR - - worker_address: STR + - name: + TUPLEARRAY: + CONTENT: U8 + SIZE: 32 + - transactions: STR + - worker_address: STR WorkerOthersBatchMessage: STRUCT: - - digest: - TYPENAME: BatchDigest - - worker_id: U32 + - digest: + TYPENAME: BatchDigest + - worker_id: U32 WorkerOwnBatchMessage: STRUCT: - - digest: - TYPENAME: BatchDigest - - worker_id: U32 - - metadata: - TYPENAME: VersionedMetadata + - digest: + TYPENAME: BatchDigest + - worker_id: U32 + - metadata: + TYPENAME: VersionedMetadata WorkerSynchronizeMessage: STRUCT: - - digests: - SEQ: - TYPENAME: BatchDigest - - target: - TYPENAME: AuthorityIdentifier - - is_certified: BOOL + - digests: + SEQ: + TYPENAME: BatchDigest + - target: + TYPENAME: AuthorityIdentifier + - is_certified: BOOL +