Skip to content

Commit

Permalink
[logging] Add additional logging, add exception to Rosetta error deta…
Browse files Browse the repository at this point in the history
…ils (#60)
  • Loading branch information
stefanmendoza-cb authored Aug 10, 2023
1 parent b850387 commit c68e15c
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 32 deletions.
13 changes: 9 additions & 4 deletions server/src/helpers/construct-tx.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { methods, deriveAddress, decode } from '@substrate/txwrapper-polkadot';
import { hexToString, hexToU8a, stringToHex, u8aConcat, u8aToHex } from '@polkadot/util';
import { EXTRINSIC_VERSION } from '@polkadot/types/extrinsic/v4/Extrinsic';
import { GearNetworkIdentifier } from '../networks';
import { hexToString, hexToU8a, stringToHex, u8aConcat, u8aToHex } from '@polkadot/util';
import { decode, deriveAddress, methods } from '@substrate/txwrapper-polkadot';

import logger from '../logger';
import { GearNetworkIdentifier } from '../networks';

export interface TxParams {
dest: string;
Expand Down Expand Up @@ -45,6 +45,10 @@ export function constructTx({
},
);

const loggedUnsignedTx = unsigned;
loggedUnsignedTx.metadataRpc = "0x...truncated...";
logger.info('[constructTx] Generated unsigned tx', { tx: loggedUnsignedTx })

const { method, version, address } = unsigned;
const unsignedTx = stringToHex(JSON.stringify({ method, version, address, nonce, era: unsigned.era }));

Expand All @@ -64,6 +68,7 @@ export function constructSignedTx(unsigned: any, signature: string, { registry }
header[0] = 0;
const sigWithHeader = u8aConcat(header, sigU8a);
const tx = JSON.parse(hexToString(unsigned));

const extrinsic = registry.createType('Extrinsic', tx, { version: tx.version });
extrinsic.addSignature(tx.address, sigWithHeader, tx);
return extrinsic.toHex();
Expand All @@ -80,7 +85,7 @@ export function parseTransaction(

tx.metadataRpc = "0x...truncated...";

logger.info(`Decoded ${signed ? 'signed' : 'unsigned'} transaction`, {
logger.info(`[parseTransaction] Decoded transaction`, {
signed: signed,
encoded_tx: transaction,
decoded_tx: tx
Expand Down
26 changes: 21 additions & 5 deletions server/src/helpers/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Error } from 'rosetta-client';
import { Error as RosettaClientError } from 'rosetta-client';

interface RosettaError {
code: number;
Expand Down Expand Up @@ -101,15 +101,31 @@ const errors: Record<number, RosettaError> = {

export function constructRosettaError(errorCode: ApiError, details?: object) {
const { message, retriable, code } = errors[errorCode];
return Error.constructFromObject({ code, message, retriable, details });
return RosettaClientError.constructFromObject({ code, message, retriable, details });
}

export function throwError(errorCode: ApiError, details?: object) {
throw constructRosettaError(errorCode, details);
export function throwError(errorCode: ApiError, metadata?: object, error?: Error) {
let rosettaDetails: Record<string, any> = {};

if(metadata !== undefined) {
rosettaDetails = { ...metadata };
}

if (error !== undefined) {
const errorDetails: object = {
message: error.message,
error_type: error.constructor.name,
stack_trace: error.stack
};

rosettaDetails.error = errorDetails;
}

throw constructRosettaError(errorCode, rosettaDetails);
}

export const allErrors = Object.values(errors).map(
({ code, message, retriable }) => new Error(code, message, retriable),
({ code, message, retriable }) => new RosettaClientError(code, message, retriable),
);

export function isRosettaError(error: object): boolean {
Expand Down
21 changes: 13 additions & 8 deletions server/src/helpers/gear.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { BlockIdentifier, Peer, SyncStatus } from 'rosetta-client';
import { Header, Index, SignedBlock, SyncState } from '@polkadot/types/interfaces';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { ApiDecoration } from '@polkadot/api/types';
import { Header, Index, SignedBlock, SyncState } from '@polkadot/types/interfaces';
import { BlockIdentifier, Peer, SyncStatus } from 'rosetta-client';

import { NetworkConfig } from 'types';
import { ApiError, throwError } from './errors';
import logger from '../logger';
import { ApiDecoration } from '@polkadot/api/types';
import { ApiError, throwError } from './errors';

export class GearApi {
provider: WsProvider;
Expand Down Expand Up @@ -55,8 +56,13 @@ export class GearApi {

return { block, apiAt };
} catch (err) {
logger.error(`Unable to get block ${at}`, { error: err });
throwError(ApiError.UNABLE_TO_GET_BLOCK, typeof at === 'string' ? { hash: at } : { number: at });
const errorMetadata = typeof at === 'string'
? { hash: at }
: typeof at === 'number'
? { index: at }
: undefined;

throwError(ApiError.UNABLE_TO_GET_BLOCK, errorMetadata, err);
}
}

Expand Down Expand Up @@ -101,8 +107,7 @@ export class GearApi {
try {
return await this.api.at(hash);
} catch (err) {
logger.error(`Unable to get api instance at ${hash}`, { error: err });
throwError(ApiError.UNABLE_TO_GET_BLOCK, { hash });
throwError(ApiError.UNABLE_TO_GET_BLOCK, { hash: hash }, err);
}
}

Expand Down
2 changes: 1 addition & 1 deletion server/src/helpers/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function getNetworkIdent({ blockchain, network }: NetworkIdentifier): Gea
);

if (!networkIdent) {
throwError(ApiError.NETWORK_NOT_SUPPORTED);
throwError(ApiError.NETWORK_NOT_SUPPORTED, { blockchain: blockchain, network: network });
}

return networkIdent;
Expand Down
2 changes: 1 addition & 1 deletion server/src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default createLogger({
new transports.Console({
format: format.combine(
format.printf(({ level, message, ...meta }) => {
let logEvent: {[index: string]: any; } = {};
let logEvent: Record<string, any> = {};

if (!message) {
if(!meta.error) {
Expand Down
62 changes: 59 additions & 3 deletions server/src/services/BlockService.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { DispatchError, Event } from '@polkadot/types/interfaces';
import { u8aToBn } from '@polkadot/util';
import {
Block,
BlockRequest,
Expand All @@ -8,8 +10,17 @@ import {
TransactionIdentifier,
} from 'rosetta-client';

import { ApiError, getNetworkIdent, getOperations, getOperationStatus, getTxsAndEvents, throwError } from '../helpers';

import config from '../config';
import { ApiError, getNetworkIdent, getOperations, getOperationStatus, getTxsAndEvents, throwError } from '../helpers';
import logger from '../logger';
import { OperationStatus } from '../types';

interface TransactionErrorMetadata {
pallet: string;
error: string;
description: string;
}

/**
* Get a Block
Expand Down Expand Up @@ -52,8 +63,20 @@ const block = async ({ body: { network_identifier, block_identifier } }: { body:
api,
_block.block.header.parentHash.toHex(),
);

if (operations.length > 0) {
transactions.push(new Transaction(transactionIdent, operations));
const rosettaTransaction = new Transaction(transactionIdent, operations);

if (opStatus == OperationStatus.FAILURE) {
try {
const transactionMetadata = lookupError(statusEvent.event);
rosettaTransaction.metadata = { error: transactionMetadata };
} catch (err) {
logger.error('Failed to lookup error for failed extrinsic', { error: err });
}
}

transactions.push(rosettaTransaction);
}
}

Expand Down Expand Up @@ -100,9 +123,42 @@ const blockTransaction = async ({
block.block.header.parentHash.toHex(),
);

return new BlockTransactionResponse(new Transaction(transaction_identifier, operations));
const rosettaTransaction = new Transaction(transaction_identifier, operations);

if (opStatus == OperationStatus.FAILURE) {
try {
const transactionMetadata = lookupError(statusEvent.event);
rosettaTransaction.metadata = { error: transactionMetadata };
} catch (err) {
logger.error('Failed to lookup error for failed extrinsic', { error: err });
}
}

return new BlockTransactionResponse(rosettaTransaction);
};

function lookupError(failureEvent: Event): TransactionErrorMetadata {
const [error, _] = failureEvent.data;

const dispatchError: DispatchError = error as unknown as DispatchError;

const errorIndex = dispatchError.asModule.index.toBn();
const errorType = u8aToBn(dispatchError.asModule.error);

if (dispatchError.isModule) {
const decoded = failureEvent.registry.findMetaError({ error: errorType, index: errorIndex });
const { docs, name, section } = decoded;

return {
pallet: section,
error: name,
description: docs.join(' ')
}
} else {
throw Error(`Could not lookup error using registry [Index = ${errorIndex}, Error = ${errorType}]`)
}
}

export default {
block,
blockTransaction,
Expand Down
19 changes: 9 additions & 10 deletions server/src/services/ConstructionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {

import config from '../config';
import { ApiError, constructSignedTx, constructTx, getNetworkIdent, parseTransaction, throwError } from '../helpers';
import logger from '../logger';
import { ApiRequest } from '../types';

/**
Expand All @@ -52,7 +51,7 @@ const constructionDerive = async ({

return ConstructionDeriveResponse.constructFromObject({ address, account_identifier });
} catch (error) {
throwError(ApiError.INVALID_ACCOUNT_ADDRESS);
throwError(ApiError.INVALID_ACCOUNT_ADDRESS, undefined, error);
}
};

Expand Down Expand Up @@ -216,7 +215,7 @@ const constructionCombine = async ({
const networkIdent = getNetworkIdent(network_identifier);

if (signature_type.toLowerCase() !== 'ed25519') {
throwError(ApiError.SIG_TYPE_NOT_SUPPORTED);
throwError(ApiError.SIG_TYPE_NOT_SUPPORTED, { signature_type: signature_type });
}

const tx = constructSignedTx(unsigned_transaction, hex_bytes, networkIdent);
Expand Down Expand Up @@ -261,15 +260,15 @@ const constructionSubmit = async ({
try {
const { result } = await nodeRequest(networkIdent.httpAddress, 'author_submitExtrinsic', [signed_transaction]);
return new TransactionIdentifierResponse({ hash: result });
} catch (e) {
if (e.message === 'Transaction is outdated') {
throwError(ApiError.TRANSACTION_IS_OUTDATED);
} catch (err) {
if (err.message === 'Transaction is outdated') {
throwError(ApiError.TRANSACTION_IS_OUTDATED, undefined, err);
}
if (e.message === 'Transaction has a bad signature') {
throwError(ApiError.TRANSACTION_BAD_SIG);
if (err.message === 'Transaction has a bad signature') {
throwError(ApiError.TRANSACTION_BAD_SIG, undefined, err);
}
logger.error(null, { error: e });
throwError(ApiError.BROADCAST_TRANSACTION);

throwError(ApiError.BROADCAST_TRANSACTION, undefined, err);
}
};

Expand Down

0 comments on commit c68e15c

Please sign in to comment.