diff --git a/packages/js-dash-sdk/src/SDK/Client/Platform/Fetcher/Fetcher.ts b/packages/js-dash-sdk/src/SDK/Client/Platform/Fetcher/Fetcher.ts index dccb83e80b..1ec345812c 100644 --- a/packages/js-dash-sdk/src/SDK/Client/Platform/Fetcher/Fetcher.ts +++ b/packages/js-dash-sdk/src/SDK/Client/Platform/Fetcher/Fetcher.ts @@ -96,14 +96,8 @@ class Fetcher { public async fetchIdentity(id: Identifier): Promise { // Define query const query = async (): Promise => { - const result = await this.dapiClient.platform - .getIdentity(id); - - // TODO(rs-drive-abci): Remove this when rs-drive-abci returns error instead of empty bytes - if (result.getIdentity().length === 0) { - throw new NotFoundError(`Identity with id "${id}" not found`); - } - return result; + const { platform } = this.dapiClient; + return platform.getIdentity(id); }; // Define retry attempts. @@ -120,14 +114,8 @@ class Fetcher { public async fetchDataContract(id: Identifier): Promise { // Define query const query = async (): Promise => { - const result = await this.dapiClient.platform - .getDataContract(id); - - // TODO(rs-drive-abci): Remove this when rs-drive-abci returns error instead of empty bytes - if (result.getDataContract().length === 0) { - throw new NotFoundError(`DataContract with id "${id}" not found`); - } - return result; + const { platform } = this.dapiClient; + return platform.getDataContract(id); }; // Define retry attempts. diff --git a/packages/rs-drive-abci/src/abci/handlers.rs b/packages/rs-drive-abci/src/abci/handlers.rs index e3cbadad4d..b8f75b197b 100644 --- a/packages/rs-drive-abci/src/abci/handlers.rs +++ b/packages/rs-drive-abci/src/abci/handlers.rs @@ -37,12 +37,10 @@ use crate::abci::server::AbciApplication; use crate::error::execution::ExecutionError; -use ciborium::cbor; use crate::error::Error; use crate::rpc::core::CoreRPCLike; use dpp::errors::consensus::codes::ErrorWithCode; -use serde_json::Value; use tenderdash_abci::proto::abci::response_verify_vote_extension::VerifyStatus; use tenderdash_abci::proto::abci::tx_record::TxAction; use tenderdash_abci::proto::abci::{self as proto, ExtendVoteExtension, ResponseException}; @@ -57,7 +55,7 @@ use super::AbciError; use dpp::platform_value::string_encoding::{encode, Encoding}; -use crate::error::serialization::SerializationError; +use crate::error::handlers::{HandlerError, HandlerErrorCode}; use crate::execution::types::block_execution_context::v0::{ BlockExecutionContextV0Getters, BlockExecutionContextV0MutableGetters, BlockExecutionContextV0Setters, @@ -72,9 +70,9 @@ use crate::platform_types::platform_state::PlatformState; use crate::platform_types::withdrawal::withdrawal_txs; use dpp::dashcore::hashes::Hash; use dpp::fee::SignedCredits; +use dpp::platform_value::platform_value; use dpp::serialization::PlatformSerializableWithPlatformVersion; use dpp::version::{PlatformVersion, PlatformVersionCurrentVersion}; -use serde_json::Map; impl<'a, C> tenderdash_abci::Application for AbciApplication<'a, C> where @@ -587,20 +585,13 @@ where .serialize_to_bytes_with_platform_version(platform_version) .map_err(|e| ResponseException::from(Error::Protocol(e)))?; - let error_data = cbor!({ - "data" => { - "serializedError" => consensus_error_bytes + let error_data_buffer = platform_value!({ + "data": { + "serializedError": consensus_error_bytes } }) - .map_err(|err| { - Error::Serialization(SerializationError::CorruptedSerialization(format!( - "can't create cbor: {err}" - ))) - })?; - - let mut error_data_buffer: Vec = Vec::new(); - ciborium::ser::into_writer(&error_data, &mut error_data_buffer) - .map_err(|e| e.to_string())?; + .to_cbor_buffer() + .map_err(|e| ResponseException::from(Error::Protocol(e.into())))?; ( consensus_error.code(), @@ -627,25 +618,14 @@ where }) } Err(error) => { - let error_data = cbor!({ - "message" => "Internal error", - }) - .map_err(|err| { - Error::Serialization(SerializationError::CorruptedSerialization(format!( - "can't create cbor: {err}" - ))) - })?; - - let mut error_data_buffer: Vec = Vec::new(); - ciborium::ser::into_writer(&error_data, &mut error_data_buffer) - .map_err(|e| e.to_string())?; + let handler_error = HandlerError::Internal(error.to_string()); tracing::error!(?error, "check_tx failed"); Ok(ResponseCheckTx { - code: 13, // Internal error gRPC code + code: handler_error.code(), data: vec![], - info: encode(&error_data_buffer, Encoding::Base64), + info: handler_error.response_info()?, gas_wanted: 0 as SignedCredits, codespace: "".to_string(), sender: "".to_string(), @@ -660,13 +640,15 @@ where let RequestQuery { data, path, .. } = &request; + // TODO: It must be ResponseException let Some(platform_version) = PlatformVersion::get_maybe_current() else { + let handler_error = + HandlerError::Unavailable("platform is not initialized".to_string()); + let response = ResponseQuery { - //todo: right now just put GRPC error codes, - // later we will use own error codes - code: 1, + code: handler_error.code(), log: "".to_string(), - info: "Platform not initialized".to_string(), + info: handler_error.response_info()?, index: 0, key: vec![], value: vec![], @@ -674,6 +656,7 @@ where height: self.platform.state.read().unwrap().height() as i64, codespace: "".to_string(), }; + tracing::error!(?response, "platform version not initialized"); return Ok(response); @@ -686,24 +669,14 @@ where let (code, data, info) = if result.is_valid() { (0, result.data.unwrap_or_default(), "success".to_string()) } else { - let error = result.errors.first(); - - let error_message = if let Some(error) = error { - error.to_string() - } else { - "Unknown Drive error".to_string() - }; + let error = result + .errors + .first() + .expect("validation result should have at least one error"); - let mut error_data = Map::new(); - error_data.insert("message".to_string(), Value::String(error_message)); + let handler_error = HandlerError::from(error); - let mut error_data_buffer: Vec = Vec::new(); - ciborium::ser::into_writer(&error_data, &mut error_data_buffer) - .map_err(|e| e.to_string())?; - // TODO(rs-drive-abci): restore different error codes? - // For now return error code 2, because it is recognized by DAPI as UNKNOWN error - // and error code 1 corresponds to CANCELED grpc request which is not suitable - (2, vec![], encode(&error_data_buffer, Encoding::Base64)) + (handler_error.code(), vec![], handler_error.response_info()?) }; let response = ResponseQuery { diff --git a/packages/rs-drive-abci/src/error/handlers.rs b/packages/rs-drive-abci/src/error/handlers.rs new file mode 100644 index 0000000000..5539ea8310 --- /dev/null +++ b/packages/rs-drive-abci/src/error/handlers.rs @@ -0,0 +1,172 @@ +use crate::error::query::QueryError; +use crate::error::Error; +use dpp::platform_value::platform_value; +use dpp::platform_value::string_encoding::{encode, Encoding}; +use tenderdash_abci::proto::abci::ResponseException; + +/// ABCI handlers errors +#[derive(Debug, thiserror::Error)] +pub enum HandlerError { + /// ABCI Handler error (Cancelled) + #[error("{0}")] + Cancelled(String), + /// ABCI Handler error (Unknown) + #[error("{0}")] + Unknown(String), + /// ABCI Handler error (InvalidArgument) + #[error("{0}")] + InvalidArgument(String), + /// ABCI Handler error (DeadlineExceeded) + #[error("{0}")] + DeadlineExceeded(String), + /// ABCI Handler error (NotFound) + #[error("{0}")] + NotFound(String), + /// ABCI Handler error (AlreadyExists) + #[error("{0}")] + AlreadyExists(String), + /// ABCI Handler error (PermissionDenied) + #[error("{0}")] + PermissionDenied(String), + /// ABCI Handler error (ResourceExhausted) + #[error("{0}")] + ResourceExhausted(String), + /// ABCI Handler error (FailedPrecondition) + #[error("{0}")] + FailedPrecondition(String), + /// ABCI Handler error (Aborted) + #[error("{0}")] + Aborted(String), + /// ABCI Handler error (OutOfRange) + #[error("{0}")] + OutOfRange(String), + /// ABCI Handler error (Unimplemented) + #[error("{0}")] + Unimplemented(String), + /// ABCI Handler error (Internal) + #[error("{0}")] + Internal(String), + /// ABCI Handler error (Unavailable) + #[error("{0}")] + Unavailable(String), + /// ABCI Handler error (DataLoss) + #[error("{0}")] + DataLoss(String), + /// ABCI Handler error (Unauthenticated) + #[error("{0}")] + Unauthenticated(String), +} + +/// Error codes for ABCI handlers +#[repr(u32)] +pub enum HandlerErrorCode { + /// ABCI Handler error (Cancelled) + Cancelled = 1, + /// ABCI Handler error (Unknown) + Unknown = 2, + /// ABCI Handler error (InvalidArgument) + InvalidArgument = 3, + /// ABCI Handler error (DeadlineExceeded) + DeadlineExceeded = 4, + /// ABCI Handler error (NotFound) + NotFound = 5, + /// ABCI Handler error (AlreadyExists) + AlreadyExists = 6, + /// ABCI Handler error (PermissionDenied) + PermissionDenied = 7, + /// ABCI Handler error (ResourceExhausted) + ResourceExhausted = 8, + /// ABCI Handler error (FailedPrecondition) + FailedPrecondition = 9, + /// ABCI Handler error (Aborted) + Aborted = 10, + /// ABCI Handler error (OutOfRange) + OutOfRange = 11, + /// ABCI Handler error (Unimplemented) + Unimplemented = 12, + /// ABCI Handler error (Internal) + Internal = 13, + /// ABCI Handler error (Unavailable) + Unavailable = 14, + /// ABCI Handler error (DataLoss) + DataLoss = 15, + /// ABCI Handler error (Unauthenticated) + Unauthenticated = 16, +} + +impl HandlerError { + /// Returns ABCI handler error code + pub fn code(&self) -> u32 { + let code = match self { + HandlerError::Cancelled(_) => HandlerErrorCode::Cancelled, + HandlerError::Unknown(_) => HandlerErrorCode::Unknown, + HandlerError::InvalidArgument(_) => HandlerErrorCode::InvalidArgument, + HandlerError::DeadlineExceeded(_) => HandlerErrorCode::DeadlineExceeded, + HandlerError::NotFound(_) => HandlerErrorCode::NotFound, + HandlerError::AlreadyExists(_) => HandlerErrorCode::AlreadyExists, + HandlerError::PermissionDenied(_) => HandlerErrorCode::PermissionDenied, + HandlerError::ResourceExhausted(_) => HandlerErrorCode::ResourceExhausted, + HandlerError::FailedPrecondition(_) => HandlerErrorCode::FailedPrecondition, + HandlerError::Aborted(_) => HandlerErrorCode::Aborted, + HandlerError::OutOfRange(_) => HandlerErrorCode::OutOfRange, + HandlerError::Unimplemented(_) => HandlerErrorCode::Unimplemented, + HandlerError::Internal(_) => HandlerErrorCode::Internal, + HandlerError::Unavailable(_) => HandlerErrorCode::Unavailable, + HandlerError::DataLoss(_) => HandlerErrorCode::DataLoss, + HandlerError::Unauthenticated(_) => HandlerErrorCode::Unauthenticated, + }; + + code as u32 + } + + /// Returns error message + pub fn message(&self) -> &str { + match self { + HandlerError::Cancelled(message) => message, + HandlerError::Unknown(message) => message, + HandlerError::InvalidArgument(message) => message, + HandlerError::DeadlineExceeded(message) => message, + HandlerError::NotFound(message) => message, + HandlerError::AlreadyExists(message) => message, + HandlerError::PermissionDenied(message) => message, + HandlerError::ResourceExhausted(message) => message, + HandlerError::FailedPrecondition(message) => message, + HandlerError::Aborted(message) => message, + HandlerError::OutOfRange(message) => message, + HandlerError::Unimplemented(message) => message, + HandlerError::Internal(message) => message, + HandlerError::Unavailable(message) => message, + HandlerError::DataLoss(message) => message, + HandlerError::Unauthenticated(message) => message, + } + } + + /// Returns base64-encoded message for info field of ABCI handler responses + pub fn response_info(&self) -> Result { + let error_data_buffer = platform_value!({ + "message": self.message().to_string(), + // TODO: consider capturing stack with one of the libs + // and send it to the client + //"stack": "..." + }) + .to_cbor_buffer() + .map_err(|e| ResponseException::from(Error::Protocol(e.into())))?; + + let error_data_base64 = encode(&error_data_buffer, Encoding::Base64); + + Ok(error_data_base64) + } +} + +impl From<&QueryError> for HandlerError { + fn from(value: &QueryError) -> Self { + match value { + QueryError::NotFound(message) => HandlerError::NotFound(message.to_owned()), + QueryError::InvalidArgument(message) => { + HandlerError::InvalidArgument(message.to_owned()) + } + QueryError::Query(error) => HandlerError::InvalidArgument(error.to_string()), + _ => HandlerError::Unknown(value.to_string()), + } + } +} diff --git a/packages/rs-drive-abci/src/error/mod.rs b/packages/rs-drive-abci/src/error/mod.rs index 3de8a1d107..a66add7fb3 100644 --- a/packages/rs-drive-abci/src/error/mod.rs +++ b/packages/rs-drive-abci/src/error/mod.rs @@ -13,6 +13,8 @@ use tracing::error; /// Execution errors module pub mod execution; +/// ABCI handlers erors module +pub mod handlers; /// Query errors module pub mod query; /// Serialization errors module diff --git a/packages/rs-drive-abci/src/error/query.rs b/packages/rs-drive-abci/src/error/query.rs index d8fe2df8fe..eb839fe344 100644 --- a/packages/rs-drive-abci/src/error/query.rs +++ b/packages/rs-drive-abci/src/error/query.rs @@ -32,6 +32,14 @@ pub enum QueryError { /// Decoding error Error #[error("protobuf decoding error: {0}")] ProtobufDecode(#[from] DecodeError), + + /// Invalid argument Error + #[error("invalid argument error: {0}")] + InvalidArgument(String), + + /// Not found Error + #[error("not found error: {0}")] + NotFound(String), } impl From for ResponseException { diff --git a/packages/rs-drive-abci/src/query/v0/mod.rs b/packages/rs-drive-abci/src/query/v0/mod.rs index 7d77186240..f0c7a7927b 100644 --- a/packages/rs-drive-abci/src/query/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/v0/mod.rs @@ -115,13 +115,18 @@ impl Platform { "/identity" => { let GetIdentityRequest { id, prove } = check_validation_result_with_data!(GetIdentityRequest::decode(query_data)); - let identity_id: Identifier = check_validation_result_with_data!(id.try_into()); + let identity_id: Identifier = check_validation_result_with_data!(id + .try_into() + .map_err(|_| QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string() + ))); let response_data = if prove { let proof = check_validation_result_with_data!(self.drive.prove_full_identity( identity_id.into_buffer(), None, &platform_version.drive )); + GetIdentityResponse { result: Some(get_identity_response::Result::Proof(Proof { grovedb_proof: proof, @@ -135,16 +140,19 @@ impl Platform { } .encode_to_vec() } else { - let identity = check_validation_result_with_data!(self + let maybe_identity = check_validation_result_with_data!(self .drive .fetch_full_identity(identity_id.into_buffer(), None, platform_version) - .map_err(QueryError::Drive) + .map_err(QueryError::Drive)); + + let identity = check_validation_result_with_data!(maybe_identity + .ok_or_else(|| { + QueryError::NotFound(format!("identity {} not found", identity_id)) + }) .and_then(|identity| identity - .map(|identity| identity - .serialize_consume_to_bytes() - .map_err(QueryError::Protocol)) - .transpose())) - .unwrap_or_default(); + .serialize_consume_to_bytes() + .map_err(QueryError::Protocol))); + GetIdentityResponse { result: Some(get_identity_response::Result::Identity(identity)), metadata: Some(metadata), @@ -159,9 +167,15 @@ impl Platform { let identity_ids = check_validation_result_with_data!(ids .into_iter() .map(|identity_id_vec| { - Bytes32::from_vec(identity_id_vec).map(|bytes| bytes.0) + Bytes32::from_vec(identity_id_vec) + .map(|bytes| bytes.0) + .map_err(|_| { + QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string(), + ) + }) }) - .collect::, dpp::platform_value::Error>>()); + .collect::, QueryError>>()); let response_data = if prove { let proof = check_validation_result_with_data!(self.drive.prove_full_identities( @@ -219,7 +233,11 @@ impl Platform { "/identity/balance" => { let GetIdentityRequest { id, prove } = check_validation_result_with_data!(GetIdentityRequest::decode(query_data)); - let identity_id: Identifier = check_validation_result_with_data!(id.try_into()); + let identity_id: Identifier = check_validation_result_with_data!(id + .try_into() + .map_err(|_| QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string() + ))); let response_data = if prove { let proof = check_validation_result_with_data!(self.drive.prove_identity_balance( @@ -256,7 +274,11 @@ impl Platform { "/identity/balanceAndRevision" => { let GetIdentityRequest { id, prove } = check_validation_result_with_data!(GetIdentityRequest::decode(query_data)); - let identity_id: Identifier = check_validation_result_with_data!(id.try_into()); + let identity_id: Identifier = check_validation_result_with_data!(id + .try_into() + .map_err(|_| QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string() + ))); let response_data = if prove { let proof = check_validation_result_with_data!(self .drive @@ -308,8 +330,11 @@ impl Platform { offset, prove, } = check_validation_result_with_data!(GetIdentityKeysRequest::decode(query_data)); - let identity_id: Identifier = - check_validation_result_with_data!(identity_id.try_into()); + let identity_id: Identifier = check_validation_result_with_data!(identity_id + .try_into() + .map_err(|_| QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string() + ))); if let Some(limit) = limit { if limit > u16::MAX as u32 { return Ok(QueryValidationResult::new_with_error(QueryError::Query( @@ -389,13 +414,18 @@ impl Platform { "/dataContract" => { let GetDataContractRequest { id, prove } = check_validation_result_with_data!(GetDataContractRequest::decode(query_data)); - let contract_id: Identifier = check_validation_result_with_data!(id.try_into()); + let contract_id: Identifier = check_validation_result_with_data!(id + .try_into() + .map_err(|_| QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string() + ))); let response_data = if prove { let proof = check_validation_result_with_data!(self.drive.prove_contract( contract_id.into_buffer(), None, platform_version )); + GetDataContractResponse { result: Some(get_data_contract_response::Result::Proof(Proof { grovedb_proof: proof, @@ -409,7 +439,7 @@ impl Platform { } .encode_to_vec() } else { - let contract = check_validation_result_with_data!(self + let maybe_data_contract = check_validation_result_with_data!(self .drive .fetch_contract( contract_id.into_buffer(), @@ -418,16 +448,20 @@ impl Platform { None, platform_version ) - .unwrap()) - .map(|contract| { - contract + .unwrap()); + + let data_contract = check_validation_result_with_data!(maybe_data_contract + .ok_or_else(|| { + QueryError::NotFound(format!("data contract {} not found", contract_id)) + }) + .and_then(|data_contract| data_contract .contract .serialize_to_bytes_with_platform_version(platform_version) - }) - .transpose()?; + .map_err(QueryError::Protocol))); + GetDataContractResponse { result: Some(get_data_contract_response::Result::DataContract( - contract.unwrap_or_default(), + data_contract, )), metadata: Some(metadata), } @@ -441,9 +475,15 @@ impl Platform { let contract_ids = check_validation_result_with_data!(ids .into_iter() .map(|contract_id_vec| { - Bytes32::from_vec(contract_id_vec).map(|bytes| bytes.0) + Bytes32::from_vec(contract_id_vec) + .map(|bytes| bytes.0) + .map_err(|_| { + QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string(), + ) + }) }) - .collect::, dpp::platform_value::Error>>()); + .collect::, QueryError>>()); let response_data = if prove { let proof = check_validation_result_with_data!(self.drive.prove_contracts( contract_ids.as_slice(), @@ -518,7 +558,11 @@ impl Platform { } = check_validation_result_with_data!(GetDataContractHistoryRequest::decode( query_data )); - let contract_id: Identifier = check_validation_result_with_data!(id.try_into()); + let contract_id: Identifier = check_validation_result_with_data!(id + .try_into() + .map_err(|_| QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string() + ))); // TODO: make a cast safe let limit = limit @@ -614,8 +658,11 @@ impl Platform { prove, start, } = check_validation_result_with_data!(GetDocumentsRequest::decode(query_data)); - let contract_id: Identifier = - check_validation_result_with_data!(data_contract_id.try_into()); + let contract_id: Identifier = check_validation_result_with_data!(data_contract_id + .try_into() + .map_err(|_| QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string() + ))); let (_, contract) = check_validation_result_with_data!(self .drive .get_contract_with_fetch_info_and_fee( @@ -748,10 +795,12 @@ impl Platform { } = check_validation_result_with_data!( GetIdentityByPublicKeyHashesRequest::decode(query_data) ); - let public_key_hash = check_validation_result_with_data!(Bytes20::from_vec( - public_key_hash - ) - .map(|bytes| bytes.0)); + let public_key_hash = + check_validation_result_with_data!(Bytes20::from_vec(public_key_hash) + .map(|bytes| bytes.0) + .map_err(|_| QueryError::InvalidArgument( + "public key hash must be 20 bytes long".to_string() + ))); let response_data = if prove { let proof = check_validation_result_with_data!(self .drive @@ -760,6 +809,7 @@ impl Platform { None, platform_version )); + GetIdentityByPublicKeyHashesResponse { metadata: Some(metadata), result: Some(get_identity_by_public_key_hashes_response::Result::Proof( @@ -783,13 +833,21 @@ impl Platform { platform_version )); let serialized_identity = check_validation_result_with_data!(maybe_identity - .map(|identity| identity.serialize_consume_to_bytes()) - .transpose()); + .ok_or_else(|| { + QueryError::NotFound(format!( + "identity {} not found", + hex::encode(public_key_hash) + )) + }) + .and_then(|identity| identity + .serialize_consume_to_bytes() + .map_err(QueryError::Protocol))); + GetIdentityByPublicKeyHashesResponse { metadata: Some(metadata), result: Some( get_identity_by_public_key_hashes_response::Result::Identity( - serialized_identity.unwrap_or_default(), + serialized_identity, ), ), } @@ -807,9 +865,15 @@ impl Platform { let public_key_hashes = check_validation_result_with_data!(public_key_hashes .into_iter() .map(|pub_key_hash_vec| { - Bytes20::from_vec(pub_key_hash_vec).map(|bytes| bytes.0) + Bytes20::from_vec(pub_key_hash_vec) + .map(|bytes| bytes.0) + .map_err(|_| { + QueryError::InvalidArgument( + "public key hash must be 20 bytes long".to_string(), + ) + }) }) - .collect::, dpp::platform_value::Error>>()); + .collect::, QueryError>>()); let response_data = if prove { let proof = check_validation_result_with_data!(self .drive @@ -870,15 +934,26 @@ impl Platform { let contract_ids = check_validation_result_with_data!(contracts .into_iter() .map(|contract_request| { - Bytes32::from_vec(contract_request.contract_id).map(|bytes| bytes.0) + Bytes32::from_vec(contract_request.contract_id) + .map(|bytes| bytes.0) + .map_err(|_| { + QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string(), + ) + }) }) - .collect::, dpp::platform_value::Error>>()); + .collect::, QueryError>>()); let identity_requests = check_validation_result_with_data!(identities .into_iter() .map(|identity_request| { Ok(IdentityDriveQuery { identity_id: Bytes32::from_vec(identity_request.identity_id) - .map(|bytes| bytes.0)?, + .map(|bytes| bytes.0) + .map_err(|_| { + QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string(), + ) + })?, prove_request_type: IdentityProveRequestType::try_from( identity_request.request_type as u8, )?, @@ -889,9 +964,17 @@ impl Platform { .into_iter() .map(|document_proof_request| { let contract_id: Identifier = - document_proof_request.contract_id.try_into()?; + document_proof_request.contract_id.try_into().map_err(|_| { + QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string(), + ) + })?; let document_id: Identifier = - document_proof_request.document_id.try_into()?; + document_proof_request.document_id.try_into().map_err(|_| { + QueryError::InvalidArgument( + "id must be a valid identifier (32 bytes long)".to_string(), + ) + })?; Ok(SingleDocumentDriveQuery { contract_id: contract_id.into_buffer(), @@ -902,7 +985,7 @@ impl Platform { block_time_ms: None, //None because we want latest }) }) - .collect::, dpp::platform_value::Error>>()); + .collect::, QueryError>>()); let proof = check_validation_result_with_data!(self.drive.prove_multiple( &identity_requests, &contract_ids, @@ -924,9 +1007,9 @@ impl Platform { .encode_to_vec(); Ok(QueryValidationResult::new_with_data(response_data)) } - other => Ok(QueryValidationResult::new_with_error(QueryError::Query( - QuerySyntaxError::Unsupported(format!("query path '{}' is not supported", other)), - ))), + other => Ok(QueryValidationResult::new_with_error( + QueryError::InvalidArgument(format!("query path '{}' is not supported", other)), + )), } } } diff --git a/packages/rs-platform-value/src/converter/ciborium.rs b/packages/rs-platform-value/src/converter/ciborium.rs index b16136dbe8..8703fed88b 100644 --- a/packages/rs-platform-value/src/converter/ciborium.rs +++ b/packages/rs-platform-value/src/converter/ciborium.rs @@ -23,6 +23,14 @@ impl Value { .map(|(key, value)| Ok((key, value.try_into()?))) .collect() } + + pub fn to_cbor_buffer(&self) -> Result, Error> { + let mut buffer: Vec = Vec::new(); + ciborium::ser::into_writer(self, &mut buffer) + .map_err(|e| Error::CborSerializationError(e.to_string()))?; + + Ok(buffer) + } } impl TryFrom for Value { diff --git a/packages/rs-platform-value/src/error.rs b/packages/rs-platform-value/src/error.rs index 953944a7b8..129456bbc2 100644 --- a/packages/rs-platform-value/src/error.rs +++ b/packages/rs-platform-value/src/error.rs @@ -39,6 +39,9 @@ pub enum Error { #[error("serde deserialization error: {0}")] SerdeDeserializationError(String), + + #[error("cbor serialization error: {0}")] + CborSerializationError(String), } impl serde::ser::Error for Error {