Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/v1.4-dev' into refactor/sdk-mock…
Browse files Browse the repository at this point in the history
…-errors
  • Loading branch information
lklimek committed Oct 29, 2024
2 parents bd7499d + 244f5f9 commit 98bebee
Show file tree
Hide file tree
Showing 349 changed files with 442 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ async function createGrpcErrorFromDriveResponse(code, info) {
const message = decodedInfo.message;
const data = decodedInfo.data || {};

const serializedConsensusError = data.serializedError;
delete data.serializedError;

// gRPC error codes
if (code <= 16) {
const CommonErrorClass = COMMON_ERROR_CLASSES[code.toString()];
Expand Down Expand Up @@ -111,9 +114,15 @@ async function createGrpcErrorFromDriveResponse(code, info) {

// DPP errors
if (code >= 10000 && code < 50000) {
const consensusMetadata = {
...createRawMetadata(data),
code,
'dash-serialized-consensus-error-bin': Buffer.from(serializedConsensusError),
};

let consensusError;
try {
consensusError = deserializeConsensusError(data.serializedError || []);
consensusError = deserializeConsensusError(serializedConsensusError);
} catch (e) {
logger.error({
err: e,
Expand All @@ -128,7 +137,7 @@ async function createGrpcErrorFromDriveResponse(code, info) {
if (code >= 10000 && code < 20000) {
return new InvalidArgumentGrpcError(
consensusError.message,
{ code, ...createRawMetadata(data) },
consensusMetadata,
);
}

Expand All @@ -137,23 +146,23 @@ async function createGrpcErrorFromDriveResponse(code, info) {
return new GrpcError(
GrpcErrorCodes.UNAUTHENTICATED,
consensusError.message,
{ code, ...createRawMetadata(data) },
consensusMetadata,
);
}

// Fee
if (code >= 30000 && code < 40000) {
return new FailedPreconditionGrpcError(
consensusError.message,
{ code, ...createRawMetadata(data) },
consensusMetadata,
);
}

// State
if (code >= 40000 && code < 50000) {
return new InvalidArgumentGrpcError(
consensusError.message,
{ code, ...createRawMetadata(data) },
consensusMetadata,
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ describe('createGrpcErrorFromDriveResponse', () => {
it('should throw basic consensus error if error code = 10000', async () => {
const consensusError = new ProtocolVersionParsingError('test');

const data = { serializedError: consensusError.serialize() };
const serializedError = consensusError.serialize();
const data = { serializedError };
info = { data };

const error = await createGrpcErrorFromDriveResponse(10000, cbor.encode(info).toString('base64'));
Expand All @@ -78,7 +79,7 @@ describe('createGrpcErrorFromDriveResponse', () => {
expect(error.message).to.be.equals(consensusError.message);
expect(error.getRawMetadata()).to.deep.equal({
code: 10000,
'drive-error-data-bin': cbor.encode(data),
'dash-serialized-consensus-error-bin': serializedError,
});
});

Expand All @@ -87,7 +88,8 @@ describe('createGrpcErrorFromDriveResponse', () => {

const consensusError = new IdentityNotFoundError(id);

const data = { serializedError: consensusError.serialize() };
const serializedError = consensusError.serialize();
const data = { serializedError };
info = { data };

const error = await createGrpcErrorFromDriveResponse(
Expand All @@ -100,22 +102,23 @@ describe('createGrpcErrorFromDriveResponse', () => {
expect(error.getCode()).to.equal(GrpcErrorCodes.UNAUTHENTICATED);
expect(error.getRawMetadata()).to.deep.equal({
code: 20000,
'drive-error-data-bin': cbor.encode(data),
'dash-serialized-consensus-error-bin': serializedError,
});
});

it('should throw fee consensus error if error code = 30000', async () => {
const consensusError = new BalanceIsNotEnoughError(BigInt(20), BigInt(10));

const data = { serializedError: consensusError.serialize() };
const serializedError = consensusError.serialize();
const data = { serializedError };
info = { data };

const error = await createGrpcErrorFromDriveResponse(30000, cbor.encode(info).toString('base64'));

expect(error).to.be.an.instanceOf(FailedPreconditionGrpcError);
expect(error.getRawMetadata()).to.deep.equal({
code: 30000,
'drive-error-data-bin': cbor.encode(data),
'dash-serialized-consensus-error-bin': serializedError,
});
});

Expand All @@ -124,7 +127,8 @@ describe('createGrpcErrorFromDriveResponse', () => {

const consensusError = new DataContractAlreadyPresentError(dataContractId);

const data = { serializedError: consensusError.serialize() };
const serializedError = consensusError.serialize();
const data = { serializedError };
info = { data };

const error = await createGrpcErrorFromDriveResponse(
Expand All @@ -135,7 +139,7 @@ describe('createGrpcErrorFromDriveResponse', () => {
expect(error).to.be.an.instanceOf(InvalidArgumentGrpcError);
expect(error.getRawMetadata()).to.deep.equal({
code: 40000,
'drive-error-data-bin': cbor.encode(data),
'dash-serialized-consensus-error-bin': serializedError,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,13 @@ async function createGrpcTransportError(grpcError, dapiAddress) {

// DPP consensus errors
if (code >= 10000 && code < 50000) {
const consensusError = deserializeConsensusError(data.serializedError || []);
const consensusErrorString = metadata['dash-serialized-consensus-error-bin'];
if (!consensusErrorString) {
throw new Error(`Can't deserialize consensus error ${code}: serialized data is missing`);
}

const consensusErrorBytes = Buffer.from(consensusErrorString, 'base64');
const consensusError = deserializeConsensusError(consensusErrorBytes);

delete data.serializedError;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,14 @@ describe('createGrpcTransportError', () => {

it('should return InvalidRequestDPPError', async () => {
// grpc-js expects Buffer
let driveErrorDataBin = cbor.encode({
serializedError: new ProtocolVersionParsingError('test').serialize(),
...errorData,
});
let serializedError = new ProtocolVersionParsingError('test').serialize();

// and grpc-web expects string
// TODO: remove when we switch to single grpc implementation for both Node and Web
if (typeof window !== 'undefined') {
driveErrorDataBin = driveErrorDataBin.toString('base64');
serializedError = serializedError.toString('base64');
}
metadata.set('drive-error-data-bin', driveErrorDataBin);
metadata.set('dash-serialized-consensus-error-bin', serializedError);

const grpcError = new GrpcError(
10001,
Expand Down
31 changes: 14 additions & 17 deletions packages/rs-dapi-client/src/dapi_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ use crate::connection_pool::ConnectionPool;
#[cfg(feature = "mocks")]
use crate::Address;

Check warning on line 15 in packages/rs-dapi-client/src/dapi_client.rs

View workflow job for this annotation

GitHub Actions / Rust packages (dash-sdk) / Linting

unused import: `crate::Address`

warning: unused import: `crate::Address` --> packages/rs-dapi-client/src/dapi_client.rs:15:5 | 15 | use crate::Address; | ^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default

Check warning on line 15 in packages/rs-dapi-client/src/dapi_client.rs

View workflow job for this annotation

GitHub Actions / Rust packages (rs-dapi-client) / Linting

unused import: `crate::Address`

warning: unused import: `crate::Address` --> packages/rs-dapi-client/src/dapi_client.rs:15:5 | 15 | use crate::Address; | ^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
use crate::{
transport::{TransportClient, TransportRequest},
transport::{TransportClient, TransportError, TransportRequest},
AddressList, CanRetry, DapiRequestExecutor, ExecutionError, ExecutionResponse, ExecutionResult,
RequestSettings,
};

/// General DAPI request error type.
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "mocks", derive(serde::Serialize, serde::Deserialize))]
pub enum DapiClientError<TE: Mockable> {
pub enum DapiClientError {
/// The error happened on transport layer
#[error("transport error: {0}")]
Transport(#[cfg_attr(feature = "mocks", serde(with = "dapi_grpc::mock::serde_mockable"))] TE),
Transport(
#[cfg_attr(feature = "mocks", serde(with = "dapi_grpc::mock::serde_mockable"))]
TransportError,
),
/// There are no valid DAPI addresses to use.
#[error("no available addresses to use")]
NoAvailableAddresses,
Expand All @@ -39,7 +42,7 @@ pub enum DapiClientError<TE: Mockable> {
Mock(#[from] crate::mock::MockError),
}

impl<TE: CanRetry + Mockable> CanRetry for DapiClientError<TE> {
impl CanRetry for DapiClientError {
fn can_retry(&self) -> bool {
use DapiClientError::*;
match self {
Expand All @@ -52,17 +55,10 @@ impl<TE: CanRetry + Mockable> CanRetry for DapiClientError<TE> {
}
}

#[cfg(feature = "mocks")]
#[derive(serde::Serialize, serde::Deserialize)]
struct TransportErrorData {
transport_error: Vec<u8>,
address: Address,
}

/// Serialization of [DapiClientError].
///
/// We need to do manual serialization because of the generic type parameter which doesn't support serde derive.
impl<TE: Mockable> Mockable for DapiClientError<TE> {
impl Mockable for DapiClientError {
#[cfg(feature = "mocks")]
fn mock_serialize(&self) -> Option<Vec<u8>> {
Some(serde_json::to_vec(self).expect("serialize DAPI client error"))
Expand Down Expand Up @@ -112,11 +108,11 @@ impl DapiRequestExecutor for DapiClient {
&self,
request: R,
settings: RequestSettings,
) -> ExecutionResult<R::Response, DapiClientError<<R::Client as TransportClient>::Error>>
) -> ExecutionResult<R::Response, DapiClientError>
where
R: TransportRequest + Mockable,
R::Response: Mockable,
<R::Client as TransportClient>::Error: Mockable,
TransportError: Mockable,
{
// Join settings of different sources to get final version of the settings for this execution:
let applied_settings = self
Expand Down Expand Up @@ -150,9 +146,10 @@ impl DapiRequestExecutor for DapiClient {
.read()
.expect("can't get address list for read");

let address_result = address_list.get_live_address().cloned().ok_or(
DapiClientError::<<R::Client as TransportClient>::Error>::NoAvailableAddresses,
);
let address_result = address_list
.get_live_address()
.cloned()
.ok_or(DapiClientError::NoAvailableAddresses);

drop(address_list);

Expand Down
7 changes: 3 additions & 4 deletions packages/rs-dapi-client/src/executor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::transport::{TransportClient, TransportRequest};
use crate::transport::TransportRequest;
use crate::{Address, CanRetry, DapiClientError, RequestSettings};
use dapi_grpc::mock::Mockable;
use dapi_grpc::tonic::async_trait;
Expand All @@ -12,11 +12,10 @@ pub trait DapiRequestExecutor {
&self,
request: R,
settings: RequestSettings,
) -> ExecutionResult<R::Response, DapiClientError<<R::Client as TransportClient>::Error>>
) -> ExecutionResult<R::Response, DapiClientError>
where
R: TransportRequest + Mockable,
R::Response: Mockable,
<R::Client as TransportClient>::Error: Mockable;
R::Response: Mockable;
}

/// Unwrap wrapped types
Expand Down
11 changes: 3 additions & 8 deletions packages/rs-dapi-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ pub use address_list::AddressList;
pub use address_list::AddressListError;
pub use connection_pool::ConnectionPool;
pub use dapi_client::{DapiClient, DapiClientError};
use dapi_grpc::mock::Mockable;
#[cfg(feature = "dump")]
pub use dump::DumpData;
pub use executor::{
Expand All @@ -38,21 +37,19 @@ pub use request_settings::RequestSettings;
/// let mut client = MockDapiClient::new();
/// let request: proto::GetIdentityRequest = proto::get_identity_request::GetIdentityRequestV0 { id: b"0".to_vec(), prove: true }.into();
/// let response = request.execute(&mut client, RequestSettings::default()).await?;
/// # Ok::<(), ExecutionError<DapiClientError<_>>>(())
/// # Ok::<(), ExecutionError<DapiClientError>>(())
/// # };
/// ```
pub trait DapiRequest {
/// Response from DAPI for this specific request.
type Response;
/// An error type for the transport this request uses.
type TransportError: Mockable;

/// Executes the request.
fn execute<'c, D: DapiRequestExecutor>(
self,
dapi_client: &'c D,
settings: RequestSettings,
) -> BoxFuture<'c, ExecutionResult<Self::Response, DapiClientError<Self::TransportError>>>
) -> BoxFuture<'c, ExecutionResult<Self::Response, DapiClientError>>
where
Self: 'c;
}
Expand All @@ -61,13 +58,11 @@ pub trait DapiRequest {
impl<T: transport::TransportRequest + Send> DapiRequest for T {
type Response = T::Response;

type TransportError = <T::Client as transport::TransportClient>::Error;

fn execute<'c, D: DapiRequestExecutor>(
self,
dapi_client: &'c D,
settings: RequestSettings,
) -> BoxFuture<'c, ExecutionResult<Self::Response, DapiClientError<Self::TransportError>>>
) -> BoxFuture<'c, ExecutionResult<Self::Response, DapiClientError>>
where
Self: 'c,
{
Expand Down
26 changes: 3 additions & 23 deletions packages/rs-dapi-client/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@
//! See `tests/mock_dapi_client.rs` for an example.
use crate::{
transport::{TransportClient, TransportRequest},
DapiClientError, DapiRequestExecutor, ExecutionError, ExecutionResponse, ExecutionResult,
RequestSettings,
transport::TransportRequest, DapiClientError, DapiRequestExecutor, ExecutionError,
ExecutionResponse, ExecutionResult, RequestSettings,
};
use dapi_grpc::mock::Mockable;
use dapi_grpc::tonic::async_trait;
Expand All @@ -36,10 +35,7 @@ pub struct MockDapiClient {
expectations: Expectations,
}
/// Result of executing a mock request
pub type MockResult<R> = ExecutionResult<
<R as TransportRequest>::Response,
DapiClientError<<<R as TransportRequest>::Client as TransportClient>::Error>,
>;
pub type MockResult<T> = ExecutionResult<<T as TransportRequest>::Response, DapiClientError>;

impl MockDapiClient {
/// Create a new mock client
Expand Down Expand Up @@ -366,19 +362,3 @@ where
}
}
}

/// Convert response into inner type
pub trait InnerInto<T> {
/// Convert response into inner type
fn inner_into(self) -> T;
}

/// Blanket implementation of InnerInto for types that implement FromInner
impl<T, U> InnerInto<U> for T
where
U: FromInner<T>,
{
fn inner_into(self) -> U {
U::from_inner(self)
}
}
Loading

0 comments on commit 98bebee

Please sign in to comment.