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
19 changes: 19 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = {
"extends": [
"react-app",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"settings": {
"react": {
"version": "999.999.999"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the tsdx defaults:
https://github.com/formium/tsdx/blob/master/src/createEslintConfig.ts

Just a validation for major, minor, patch

},
},
"rules": {
"prettier/prettier": [
"error", {
"endOfLine": "auto"
},
],
}
}
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
name: CI
on: [push]
env:
NODE_OPTIONS: --max_old_space_size=4096
jobs:
build:
name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }}
Expand Down
75 changes: 48 additions & 27 deletions src/api/fullnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import axios from 'axios';
import { globalCache } from '../utils';
import logger from '../logger';

const DEFAULT_SERVER = process.env.DEFAULT_SERVER || 'https://node1.foxtrot.testnet.hathor.network/v1a/';
const DEFAULT_SERVER =
process.env.DEFAULT_SERVER ||
'https://node1.foxtrot.testnet.hathor.network/v1a/';

/**
* Returns a transaction from the fullnode
Expand Down Expand Up @@ -61,8 +63,12 @@ export const downloadMempool = async () => {
*
* @param height - The block's height
*/
export const downloadBlockByHeight = async (height: number): Promise<FullBlock> => {
const response = await axios.get(`${DEFAULT_SERVER}block_at_height?height=${height}`);
export const downloadBlockByHeight = async (
height: number
): Promise<FullBlock> => {
const response = await axios.get(
`${DEFAULT_SERVER}block_at_height?height=${height}`
);

const data = response.data;

Expand All @@ -82,9 +88,13 @@ export const downloadBlockByHeight = async (height: number): Promise<FullBlock>
const typedDecodedScript: DecodedScript = {
type: input.decoded.type as string,
address: input.decoded.address as string,
timelock: input.decoded.timelock ? input.decoded.timelock as number : null,
value: input.decoded.value ? input.decoded.value as number : null,
tokenData: input.decoded.token_data ? input.decoded.token_data as number : null,
timelock: input.decoded.timelock
? (input.decoded.timelock as number)
: null,
value: input.decoded.value ? (input.decoded.value as number) : null,
tokenData: input.decoded.token_data
? (input.decoded.token_data as number)
: null,
};
const typedInput: Input = {
txId: input.tx_id as string,
Expand All @@ -98,25 +108,31 @@ export const downloadBlockByHeight = async (height: number): Promise<FullBlock>

return typedInput;
}),
outputs: responseBlock.outputs.map((output: RawOutput): Output => {
const typedDecodedScript: DecodedScript = {
type: output.decoded.type as string,
address: output.decoded.address as string,
timelock: output.decoded.timelock ? output.decoded.timelock as number : null,
value: output.decoded.value ? output.decoded.value as number : null,
tokenData: output.decoded.token_data ? output.decoded.token_data as number : null,
};

const typedOutput: Output = {
value: output.value as number,
tokenData: output.token_data as number,
script: output.script as string,
decoded: typedDecodedScript,
token: output.token as string,
};

return typedOutput;
}),
outputs: responseBlock.outputs.map(
(output: RawOutput): Output => {
const typedDecodedScript: DecodedScript = {
type: output.decoded.type as string,
address: output.decoded.address as string,
timelock: output.decoded.timelock
? (output.decoded.timelock as number)
: null,
value: output.decoded.value ? (output.decoded.value as number) : null,
tokenData: output.decoded.token_data
? (output.decoded.token_data as number)
: null,
};

const typedOutput: Output = {
value: output.value as number,
tokenData: output.token_data as number,
script: output.script as string,
decoded: typedDecodedScript,
token: output.token as string,
};

return typedOutput;
}
),
parents: responseBlock.parents,
height: responseBlock.height as number,
};
Expand All @@ -130,7 +146,10 @@ export const downloadBlockByHeight = async (height: number): Promise<FullBlock>
* @param txId - The block txId
* @param noCache - Prevents downloading the block from cache as a reorg may have ocurred
*/
export const getBlockByTxId = async (txId: string, noCache: boolean = false) => {
export const getBlockByTxId = async (
txId: string,
noCache: boolean = false
) => {
return downloadTx(txId, noCache);
};

Expand All @@ -140,7 +159,9 @@ export const getBlockByTxId = async (txId: string, noCache: boolean = false) =>
* a specialized API from the full_node to query its best block.
*/
export const getFullNodeBestBlock = async (): Promise<Block> => {
const response = await axios.get(`${DEFAULT_SERVER}transaction?type=block&count=1`);
const response = await axios.get(
`${DEFAULT_SERVER}transaction?type=block&count=1`
);
const { transactions } = response.data;

const bestBlock: Block = {
Expand Down
86 changes: 46 additions & 40 deletions src/api/lambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@

import AWS from 'aws-sdk';
import logger from '../logger';
import {
PreparedTx,
ApiResponse,
Block,
} from '../types';
import { PreparedTx, ApiResponse, Block } from '../types';

AWS.config.update({
region: process.env.AWS_REGION,
Expand All @@ -23,57 +19,67 @@ AWS.config.update({
* @param fnName - The lambda function name
* @param payload - The payload to be sent
*/
export const lambdaCall = (fnName: string, payload: any): Promise<any> => new Promise((resolve, reject) => {
const lambda = new AWS.Lambda({
apiVersion: '2015-03-31',
endpoint: process.env.WALLET_SERVICE_STAGE === 'local'
? process.env.WALLET_SERVICE_LOCAL_URL || 'http://localhost:3002'
: `https://lambda.${process.env.AWS_REGION}.amazonaws.com`,
});
export const lambdaCall = (fnName: string, payload: any): Promise<any> =>
new Promise((resolve, reject) => {
const lambda = new AWS.Lambda({
apiVersion: '2015-03-31',
endpoint:
process.env.WALLET_SERVICE_STAGE === 'local'
? process.env.WALLET_SERVICE_LOCAL_URL || 'http://localhost:3002'
: `https://lambda.${process.env.AWS_REGION}.amazonaws.com`,
});

const params = {
FunctionName: `${process.env.WALLET_SERVICE_NAME}-${process.env.WALLET_SERVICE_STAGE}-${fnName}`,
Payload: JSON.stringify({
body: payload,
}),
};
const params = {
FunctionName: `${process.env.WALLET_SERVICE_NAME}-${process.env.WALLET_SERVICE_STAGE}-${fnName}`,
Payload: JSON.stringify({
body: payload,
}),
};

lambda.invoke(params, (err, data) => {
if (err) {
logger.error(`Erroed on ${fnName} method call with payload: ${payload}`);
logger.error(err);
reject(err);
} else {
if (data.StatusCode !== 200) {
reject(new Error('Request failed.'));
}
lambda.invoke(params, (err, data) => {
if (err) {
logger.error(
`Erroed on ${fnName} method call with payload: ${payload}`
);
logger.error(err);
reject(err);
} else {
if (data.StatusCode !== 200) {
reject(new Error('Request failed.'));
}

try {
const responsePayload = JSON.parse(data.Payload as string);
const body = JSON.parse(responsePayload.body);
try {
const responsePayload = JSON.parse(data.Payload as string);
const body = JSON.parse(responsePayload.body);

resolve(body);
} catch(e) {
logger.error(`Erroed on lambda call to ${fnName} with payload: ${JSON.stringify(payload)}`);
logger.error(e);
resolve(body);
} catch (e) {
logger.error(
`Erroed on lambda call to ${fnName} with payload: ${JSON.stringify(
payload
)}`
);
logger.error(e);

return reject(e.message);
}
return reject(e.message);
}
});
});
}
});
});

/**
* Calls the onHandleReorgRequest lambda function
*/
export const invokeReorg = async (): Promise<ApiResponse> => lambdaCall('onHandleReorgRequest', {});
export const invokeReorg = async (): Promise<ApiResponse> =>
lambdaCall('onHandleReorgRequest', {});

/**
* Calls the onNewTxRequest lambda function with a PreparedTx
*
* @param tx - The prepared transaction to be sent
*/
export const sendTx = async (tx: PreparedTx): Promise<ApiResponse> => lambdaCall('onNewTxRequest', tx);
export const sendTx = async (tx: PreparedTx): Promise<ApiResponse> =>
lambdaCall('onNewTxRequest', tx);

/**
* Calls the getLatestBlock lambda function from the wallet-service returning
Expand Down
20 changes: 12 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const machine = interpret(SyncMachine).onTransition(state => {
});

const handleMessage = (message: any) => {
switch(message.type) {
switch (message.type) {
case 'dashboard:metrics':
break;

Expand Down Expand Up @@ -49,7 +49,9 @@ const handleMessage = (message: any) => {
machine.send({ type: 'NEW_BLOCK' });
}
if (message.state === Connection.CONNECTING) {
logger.info(`Websocket is attempting to connect to ${process.env.DEFAULT_SERVER}`);
logger.info(
`Websocket is attempting to connect to ${process.env.DEFAULT_SERVER}`
);
}
if (message.state === Connection.CLOSED) {
logger.error('Websocket connection was closed.');
Expand All @@ -65,14 +67,16 @@ const conn = new Connection({
});

// @ts-ignore
conn.websocket.on('network', (message) => handleMessage(message));
conn.websocket.on('network', message => handleMessage(message));
// @ts-ignore
conn.on('state', (state) => handleMessage({
type: 'state_update',
state,
}));
conn.on('state', state =>
handleMessage({
type: 'state_update',
state,
})
);
// @ts-ignore
conn.websocket.on('connection_error', (evt) => {
conn.websocket.on('connection_error', evt => {
logger.error(`Websocket connection error: ${evt.message}`);
});

Expand Down
52 changes: 27 additions & 25 deletions src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,44 @@ import * as winston from 'winston';

const CONSOLE_LEVEL = process.env.CONSOLE_LEVEL || 'info';

const myFormat = winston.format.printf(({ level, message, service, timestamp, ...args }) => {
let argsStr = '';

if (Object.keys(args).length > 0) {
// Adapted from https://github.com/winstonjs/logform/blob/master/pretty-print.js
const stripped = Object.assign({}, args);

const levelSymbol = Symbol.for('level');
const messageSymbol = Symbol.for('message');
const splatSymbol = Symbol.for('splat');

// Typing Symbol as any is a workaround for https://github.com/microsoft/TypeScript/issues/1863
delete stripped[levelSymbol as any];
delete stripped[messageSymbol as any];
delete stripped[splatSymbol as any];

argsStr = util.inspect(stripped, {compact: true, breakLength: Infinity});
const myFormat = winston.format.printf(
({ level, message, service, timestamp, ...args }) => {
let argsStr = '';

if (Object.keys(args).length > 0) {
// Adapted from https://github.com/winstonjs/logform/blob/master/pretty-print.js
const stripped = Object.assign({}, args);

const levelSymbol = Symbol.for('level');
const messageSymbol = Symbol.for('message');
const splatSymbol = Symbol.for('splat');

// Typing Symbol as any is a workaround for https://github.com/microsoft/TypeScript/issues/1863
delete stripped[levelSymbol as any];
delete stripped[messageSymbol as any];
delete stripped[splatSymbol as any];

argsStr = util.inspect(stripped, {
compact: true,
breakLength: Infinity,
});
}

return `${timestamp} [${service}] ${level}: ${message} ${argsStr}`;
}

return `${timestamp} [${service}] ${level}: ${message} ${argsStr}`;
});
);

const transports = [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
myFormat,
),
format: winston.format.combine(winston.format.colorize(), myFormat),
level: CONSOLE_LEVEL,
}),
];

const logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.errors({ stack: true })
),
defaultMeta: { service: 'wallet-service-daemon' },
transports: transports,
Expand Down
Loading