Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/drive/grpc error codes 2 #1448

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
df500e4
fix(drive): -32603 error code on broadcast
shumkov Sep 8, 2023
4548545
fix: wrong cbor macros syntax
shumkov Sep 8, 2023
0d72be2
fix: wrong error data encoded
shumkov Sep 8, 2023
db84706
fix: json error from tenderdash code passed as an actual code in meta…
shumkov Sep 8, 2023
bb525d7
feat(rs-platform-value): to_cbor_buffer on Value
markin-io Sep 8, 2023
3bc2f74
refactor(drive-abci): response errors
markin-io Sep 8, 2023
c0a34cb
feat: internal error
markin-io Sep 13, 2023
1c8202c
feat: not found error
markin-io Sep 13, 2023
ca5e06f
feat: invalid argument error
markin-io Sep 14, 2023
5a769f6
Merge branch 'v0.25-dev' into feat/drive/grpc-error-codes
markin-io Sep 14, 2023
5256a21
test(dapi): fix
markin-io Sep 14, 2023
e541dba
refactor(dapi): remove commented code
markin-io Sep 14, 2023
3180b4f
fix comments
markin-io Sep 15, 2023
3bc1b6c
feat(drive): return all error codes
markin-io Sep 15, 2023
8b7eb87
refactor(drive): rename Query error to DocumentQuery
markin-io Sep 15, 2023
7b15814
Merge branch 'v0.25-dev' into feat/drive/grpc-error-codes
markin-io Sep 19, 2023
1bdf494
refactor: remove info encoding duplication
shumkov Sep 22, 2023
ddb0aa9
fix(drive): get rid of redundant handler error prefix
markin-io Sep 22, 2023
141468e
Merge branch 'feat/drive/grpc-error-codes' of github.com:dashpay/plat…
markin-io Sep 22, 2023
b4e5685
Revert "refactor(drive): rename Query error to DocumentQuery"
markin-io Sep 22, 2023
75fac6d
fix(drive): documentation
markin-io Sep 22, 2023
72bf115
fix(drive): get rid of unnecessary proof length check
markin-io Sep 22, 2023
9377fa0
Merge branch 'v0.25-dev' into feat/drive/grpc-error-codes
QuantumExplorer Sep 25, 2023
9e494b9
Merge branch 'v0.25-dev' into feat/drive/grpc-error-codes
markin-io Sep 29, 2023
6c5a2ce
build: cargo.lock update
markin-io Sep 29, 2023
97dfb56
Merge branch 'v0.25-dev' into feat/drive/grpc-error-codes
shumkov Oct 1, 2023
7bd8b46
wip: test rs-platform-value buffer
markin-io Oct 3, 2023
0d99331
Revert "wip: test rs-platform-value buffer"
markin-io Oct 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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 @@
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 All @@ -148,7 +136,7 @@
id: Identifier, startAMs: number, limit: number, offset: number,
): Promise<GetDataContractHistoryResponse> {
// Define query
const query = async (): Promise<GetDataContractHistoryResponse> => await this

Check warning on line 139 in packages/js-dash-sdk/src/SDK/Client/Platform/Fetcher/Fetcher.ts

View workflow job for this annotation

GitHub Actions / JS / Linting

Returning an awaited value that is not a promise is not allowed
.dapiClient.platform.getDataContractHistory(id, startAMs, limit, offset);

// 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 dpp::platform_value::string_encoding::{encode, Encoding};

use crate::error::serialization::SerializationError;
use crate::error::handlers::{HandlerError, HandlerErrorCode};

Check warning on line 58 in packages/rs-drive-abci/src/abci/handlers.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `HandlerErrorCode`

warning: unused import: `HandlerErrorCode` --> packages/rs-drive-abci/src/abci/handlers.rs:58:44 | 58 | use crate::error::handlers::{HandlerError, HandlerErrorCode}; | ^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
use crate::execution::types::block_execution_context::v0::{
BlockExecutionContextV0Getters, BlockExecutionContextV0MutableGetters,
BlockExecutionContextV0Setters,
Expand All @@ -72,9 +70,9 @@
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 @@ -429,7 +427,7 @@
Err(Error::from(AbciError::RequestForWrongBlockReceived(format!(
"received extend vote request for height: {} round: {}, block: {}; expected height: {} round: {}, block: {}",
height, round, hex::encode(block_hash),
block_state_info.height(), block_state_info.round(), block_state_info.block_hash().map(|block_hash| hex::encode(block_hash)).unwrap_or("None".to_string())

Check warning on line 430 in packages/rs-drive-abci/src/abci/handlers.rs

View workflow job for this annotation

GitHub Actions / clippy

redundant closure

warning: redundant closure --> packages/rs-drive-abci/src/abci/handlers.rs:430:104 | 430 | ...lock_hash().map(|block_hash| hex::encode(block_hash)).unwrap_or("None".to_string()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `hex::encode` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure = note: `#[warn(clippy::redundant_closure)]` on by default
)))
.into())
} else {
Expand Down Expand Up @@ -587,20 +585,13 @@
.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 @@
})
}
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 @@

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 @@
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
Loading