Skip to content

Commit

Permalink
feat(drive): handlers error codes (#1394)
Browse files Browse the repository at this point in the history
Co-authored-by: Ivan Shumkov <[email protected]>
Co-authored-by: QuantumExplorer <[email protected]>
  • Loading branch information
3 people authored Oct 3, 2023
1 parent da77043 commit f32d415
Show file tree
Hide file tree
Showing 8 changed files with 348 additions and 111 deletions.
20 changes: 4 additions & 16 deletions packages/js-dash-sdk/src/SDK/Client/Platform/Fetcher/Fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,8 @@ class Fetcher {
public async fetchIdentity(id: Identifier): Promise<GetIdentityResponse> {
// Define query
const query = async (): Promise<GetIdentityResponse> => {
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.
Expand All @@ -120,14 +114,8 @@ class Fetcher {
public async fetchDataContract(id: Identifier): Promise<GetDataContractResponse> {
// Define query
const query = async (): Promise<GetDataContractResponse> => {
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.
Expand Down
73 changes: 23 additions & 50 deletions packages/rs-drive-abci/src/abci/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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<u8> = 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(),
Expand All @@ -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<u8> = 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(),
Expand All @@ -660,20 +640,23 @@ 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![],
proof_ops: None,
height: self.platform.state.read().unwrap().height() as i64,
codespace: "".to_string(),
};

tracing::error!(?response, "platform version not initialized");

return Ok(response);
Expand All @@ -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<u8> = 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 {
Expand Down
172 changes: 172 additions & 0 deletions packages/rs-drive-abci/src/error/handlers.rs
Original file line number Diff line number Diff line change
@@ -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<String, ResponseException> {
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()),
}
}
}
2 changes: 2 additions & 0 deletions packages/rs-drive-abci/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions packages/rs-drive-abci/src/error/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<QueryError> for ResponseException {
Expand Down
Loading

0 comments on commit f32d415

Please sign in to comment.