Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const {
InvalidArgumentGrpcError,
AlreadyExistsGrpcError,
ResourceExhaustedGrpcError,
UnavailableGrpcError,
},
},
} = require('@dashevo/grpc-common');
Expand Down Expand Up @@ -44,7 +45,11 @@ function broadcastStateTransitionHandlerFactory(rpcClient, createGrpcErrorFromDr
try {
response = await rpcClient.request('broadcast_tx_sync', { tx });
} catch (e) {
logger.error(e, 'Failed broadcasting state transition');
logger.error(`Failed broadcasting state transition: ${e}`);

if (e.message === 'socket hang up') {
throw new UnavailableGrpcError('Tenderdash is not available');
}

throw e;
}
Expand All @@ -65,12 +70,18 @@ function broadcastStateTransitionHandlerFactory(rpcClient, createGrpcErrorFromDr
if (jsonRpcError.data.startsWith('mempool is full')) {
throw new ResourceExhaustedGrpcError(jsonRpcError.data);
}

if (jsonRpcError.data.startsWith('broadcast confirmation not received:')) {
logger.error(`Failed broadcasting state transition: ${jsonRpcError.data}`);

throw new UnavailableGrpcError(jsonRpcError.data);
}
}

const error = new Error();
Object.assign(error, jsonRpcError);

logger.error(error, 'Unexpected JSON RPC error during broadcasting state transition');
logger.error(error, `Unexpected JSON RPC error during broadcasting state transition: ${JSON.stringify(jsonRpcError)}`);

throw error;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const {
error: {
InvalidArgumentGrpcError,
AlreadyExistsGrpcError,
UnavailableGrpcError,
ResourceExhaustedGrpcError,
},
},
} = require('@dashevo/grpc-common');
Expand Down Expand Up @@ -115,23 +117,72 @@ describe('broadcastStateTransitionHandlerFactory', () => {
expect(rpcClientMock.request).to.be.calledOnceWith('broadcast_tx_sync', { tx });
});

it('should throw an error if transaction broadcast returns error', async () => {
const error = { code: -1, message: "Something didn't work", data: 'Some data' };
it('should throw a UnavailableGrpcError if tenderdash hands up', async () => {
const error = new Error('socket hang up');
rpcClientMock.request.throws(error);

response.error = error;
try {
await broadcastStateTransitionHandler(call);

expect.fail('should throw UnavailableGrpcError');
} catch (e) {
expect(e).to.be.an.instanceOf(UnavailableGrpcError);
expect(e.getMessage()).to.equal('Tenderdash is not available');
}
});

it('should throw a UnavailableGrpcError if broadcast confirmation not received', async () => {
response.error = {
code: -32603,
message: 'Internal error',
data: 'broadcast confirmation not received: heya',
};

try {
await broadcastStateTransitionHandler(call);

expect.fail('should throw an error');
expect.fail('should throw UnavailableGrpcError');
} catch (e) {
expect(e.message).to.equal(error.message);
expect(e.data).to.equal(error.data);
expect(e.code).to.equal(error.code);
expect(e).to.be.an.instanceOf(UnavailableGrpcError);
expect(e.getMessage()).to.equal(response.error.data);
}
});

it('should throw FailedPreconditionGrpcError if transaction was broadcasted twice', async () => {
it('should throw an InvalidArgumentGrpcError if state transition size is too big', async () => {
response.error = {
code: -32603,
message: 'Internal error',
data: 'Tx too large. La la la',
};

try {
await broadcastStateTransitionHandler(call);

expect.fail('should throw UnavailableGrpcError');
} catch (e) {
expect(e).to.be.an.instanceOf(InvalidArgumentGrpcError);
expect(e.getMessage()).to.equal('state transition is too large. La la la');
}
});

it('should throw a ResourceExhaustedGrpcError if mempool is full', async () => {
response.error = {
code: -32603,
message: 'Internal error',
data: 'mempool is full: heya',
};

try {
await broadcastStateTransitionHandler(call);

expect.fail('should throw UnavailableGrpcError');
} catch (e) {
expect(e).to.be.an.instanceOf(ResourceExhaustedGrpcError);
expect(e.getMessage()).to.equal(response.error.data);
}
});

it('should throw AlreadyExistsGrpcError if transaction was broadcasted twice', async () => {
response.error = {
code: -32603,
message: 'Internal error',
Expand All @@ -148,7 +199,7 @@ describe('broadcastStateTransitionHandlerFactory', () => {
}
});

it('should throw call createGrpcErrorFromDriveResponse if error code is not 0', async () => {
it('should throw a gRPC error based on drive\'s response', async () => {
const message = 'not found';
const metadata = {
data: 'some data',
Expand Down Expand Up @@ -176,4 +227,20 @@ describe('broadcastStateTransitionHandlerFactory', () => {
);
}
});

it('should throw an error if transaction broadcast returns unknown error', async () => {
const error = { code: -1, message: "Something didn't work", data: 'Some data' };

response.error = error;

try {
await broadcastStateTransitionHandler(call);

expect.fail('should throw an error');
} catch (e) {
expect(e.message).to.equal(error.message);
expect(e.data).to.equal(error.data);
expect(e.code).to.equal(error.code);
}
});
});