Skip to content

Commit f197193

Browse files
committed
fix: added isDecodedValid util to test for valid decoded in inputs and outputs
1 parent 5088868 commit f197193

File tree

9 files changed

+90
-42
lines changed

9 files changed

+90
-42
lines changed

packages/common/__tests__/utils/nft.utils.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jest.mock('winston', () => {
1515
error = jest.fn();
1616
info = jest.fn();
1717
debug = jest.fn();
18-
};
18+
}
1919

2020
return {
2121
Logger: FakeLogger,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { isDecodedValid } from '@src/utils/wallet.utils';
2+
3+
describe('walletUtils', () => {
4+
it('should validate common invalid inputs', () => {
5+
expect.hasAssertions();
6+
7+
expect(isDecodedValid({})).toBeFalsy();
8+
expect(isDecodedValid(false)).toBeFalsy();
9+
expect(isDecodedValid(null)).toBeFalsy();
10+
expect(isDecodedValid(undefined)).toBeFalsy();
11+
expect(isDecodedValid({
12+
address: 'addr1',
13+
type: 'PPK',
14+
})).toBeTruthy();
15+
});
16+
17+
it('should validate requiredKeys', () => {
18+
expect.hasAssertions();
19+
20+
expect(isDecodedValid({
21+
address: 'addr1',
22+
type: 'PPK',
23+
}, ['address', 'type'])).toBeTruthy();
24+
25+
expect(isDecodedValid({
26+
address: 'addr1',
27+
}, ['address', 'type'])).toBeFalsy();
28+
});
29+
});

packages/common/src/types.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
import { constants } from '@hathor/wallet-lib';
14-
import { isAuthority } from './utils/wallet.utils';
14+
import { isAuthority, isDecodedValid } from './utils/wallet.utils';
1515

1616
export interface StringMap<T> {
1717
[x: string]: T;
@@ -376,7 +376,7 @@ export class TokenBalanceMap {
376376
* @returns The TokenBalanceMap object
377377
*/
378378
static fromTxOutput(output: TxOutput): TokenBalanceMap {
379-
if (!output.decoded) {
379+
if (!isDecodedValid(output.decoded)) {
380380
throw new Error('Output has no decoded script');
381381
}
382382
const token = output.token;

packages/common/src/utils/alerting.utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export const addAlert = async (
6161

6262
try {
6363
await client.send(command);
64-
} catch(err) {
64+
} catch (err) {
6565
logger.error('[ALERT] Erroed while sending message to the alert sqs queue', err);
6666
}
6767
};

packages/common/src/utils/wallet.utils.ts

+15
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,18 @@ import { constants } from '@hathor/wallet-lib';
1717
export const isAuthority = (tokenData: number): boolean => (
1818
(tokenData & constants.TOKEN_AUTHORITY_MASK) > 0
1919
);
20+
21+
/**
22+
* Checks if a decoded output object is valid (not null, undefined or empty object).
23+
*
24+
* @param decoded - The decoded output object to check
25+
* @returns true if the decoded object is valid, false otherwise
26+
*/
27+
export const isDecodedValid = (decoded: any, requiredKeys: string[] = []): boolean => {
28+
return (decoded != null
29+
&& typeof decoded === 'object'
30+
&& Object.keys(decoded).length > 0)
31+
&& requiredKeys.reduce((state, key: string) => (
32+
state && decoded[key] != null
33+
), true);
34+
};

packages/daemon/__tests__/integration/balances.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ let mysql: Connection;
7171
beforeAll(async () => {
7272
try {
7373
mysql = await getDbConnection();
74-
} catch(e) {
74+
} catch (e) {
7575
console.error('Failed to establish db connection', e);
7676
throw e;
7777
}
@@ -290,6 +290,7 @@ describe('empty script scenario', () => {
290290
// @ts-ignore
291291
await transitionUntilEvent(mysql, machine, EMPTY_SCRIPT_LAST_EVENT);
292292
const addressBalances = await fetchAddressBalances(mysql);
293+
293294
// @ts-ignore
294295
expect(validateBalances(addressBalances, emptyScriptBalances));
295296
});

packages/daemon/__tests__/utils/wallet.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ describe('prepareOutputs', () => {
2525
token_data: 0,
2626
script: 'dqkUCU1EY3YLi8WURhDOEsspok4Y0XiIrA==',
2727
decoded: {
28-
type: 'P2PKH',
29-
address: 'H7NK2gjt5oaHzBEPoiH7y3d1NcPQi3Tr2F',
30-
timelock: null,
28+
type: 'P2PKH',
29+
address: 'H7NK2gjt5oaHzBEPoiH7y3d1NcPQi3Tr2F',
30+
timelock: null,
3131
}
3232
}, {
3333
value: 1,

packages/daemon/src/services/index.ts

+26-25
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
Transaction,
3030
TokenBalanceMap,
3131
TxOutputWithIndex,
32+
isDecodedValid,
3233
} from '@wallet-service/common';
3334
import {
3435
prepareOutputs,
@@ -133,8 +134,8 @@ export const metadataDiff = async (_context: Context, event: Event) => {
133134
}
134135

135136
if (first_block
136-
&& first_block.length
137-
&& first_block.length > 0) {
137+
&& first_block.length
138+
&& first_block.length > 0) {
138139
if (!dbTx.height) {
139140
return {
140141
type: METADATA_DIFF_EVENT_TYPES.TX_FIRST_BLOCK,
@@ -161,7 +162,7 @@ export const metadataDiff = async (_context: Context, event: Event) => {
161162
};
162163

163164
export const isBlock = (version: number): boolean => version === hathorLib.constants.BLOCK_VERSION
164-
|| version === hathorLib.constants.MERGED_MINED_BLOCK_VERSION;
165+
|| version === hathorLib.constants.MERGED_MINED_BLOCK_VERSION;
165166

166167
export const handleVertexAccepted = async (context: Context, _event: Event) => {
167168
const mysql = await getDbConnection();
@@ -217,7 +218,7 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {
217218
const txOutputs: TxOutputWithIndex[] = prepareOutputs(outputs, tokens);
218219
const txInputs: TxInput[] = prepareInputs(inputs, tokens);
219220

220-
let heightlock: number|null = null;
221+
let heightlock: number | null = null;
221222
if (isBlock(version)) {
222223
if (typeof height !== 'number' && !height) {
223224
throw new Error('Block with no height set in metadata.');
@@ -238,7 +239,7 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {
238239
const blockRewardOutput = outputs[0];
239240

240241
// add miner to the miners table
241-
if (blockRewardOutput.decoded) {
242+
if (isDecodedValid(blockRewardOutput.decoded)) {
242243
await addMiner(mysql, blockRewardOutput.decoded.address, hash);
243244
}
244245

@@ -303,21 +304,21 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {
303304

304305
const addressesPerWallet = Object.entries(addressWalletMap).reduce(
305306
(result: StringMap<{ addresses: string[], walletDetails: Wallet }>, [address, wallet]: [string, Wallet]) => {
306-
const { walletId } = wallet;
307-
308-
// Initialize the array if the walletId is not yet a key in result
309-
if (!result[walletId]) {
310-
result[walletId] = {
311-
addresses: [],
312-
walletDetails: wallet,
307+
const { walletId } = wallet;
308+
309+
// Initialize the array if the walletId is not yet a key in result
310+
if (!result[walletId]) {
311+
result[walletId] = {
312+
addresses: [],
313+
walletDetails: wallet,
314+
}
313315
}
314-
}
315316

316-
// Add the current key to the array
317-
result[walletId].addresses.push(address);
317+
// Add the current key to the array
318+
result[walletId].addresses.push(address);
318319

319-
return result;
320-
}, {});
320+
return result;
321+
}, {});
321322

322323
const seenWallets = Object.keys(addressesPerWallet);
323324

@@ -420,7 +421,7 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {
420421
await mysql.commit();
421422
} catch (e) {
422423
await mysql.rollback();
423-
logger.error('Error handling vertex accepted', {
424+
console.error('Error handling vertex accepted', {
424425
error: (e as Error).message,
425426
stack: (e as Error).stack,
426427
});
@@ -615,13 +616,13 @@ export const updateLastSyncedEvent = async (context: Context) => {
615616
const lastEventId = context.event.event.id;
616617

617618
if (lastDbSyncedEvent
618-
&& lastDbSyncedEvent.last_event_id > lastEventId) {
619-
logger.error('Tried to store an event lower than the one on the database', {
620-
lastEventId,
621-
lastDbSyncedEvent: JSON.stringify(lastDbSyncedEvent),
622-
});
623-
mysql.destroy();
624-
throw new Error('Event lower than stored one.');
619+
&& lastDbSyncedEvent.last_event_id > lastEventId) {
620+
logger.error('Tried to store an event lower than the one on the database', {
621+
lastEventId,
622+
lastDbSyncedEvent: JSON.stringify(lastDbSyncedEvent),
623+
});
624+
mysql.destroy();
625+
throw new Error('Event lower than stored one.');
625626
}
626627
await dbUpdateLastSyncedEvent(mysql, lastEventId);
627628

packages/daemon/src/utils/wallet.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import { constants, Output, walletUtils, addressUtils } from '@hathor/wallet-lib';
8+
import hathorLib, { constants, Output, walletUtils, addressUtils } from '@hathor/wallet-lib';
99
import { Connection as MysqlConnection } from 'mysql2/promise';
1010
import { strict as assert } from 'assert';
1111
import {
@@ -27,6 +27,7 @@ import {
2727
TxInput,
2828
TxOutput,
2929
TokenBalanceMap,
30+
isDecodedValid,
3031
} from '@wallet-service/common';
3132
import {
3233
fetchAddressBalance,
@@ -75,9 +76,9 @@ export const prepareOutputs = (outputs: EventTxOutput[], tokens: string[]): TxOu
7576
// @ts-ignore
7677
output.token = token;
7778

78-
if (!_output.decoded
79-
|| _output.decoded.type === null
80-
|| _output.decoded.type === undefined) {
79+
if (!isDecodedValid(_output.decoded)
80+
|| _output.decoded.type === null
81+
|| _output.decoded.type === undefined) {
8182
console.log('Decode failed, skipping..');
8283
return [currIndex + 1, newOutputs];
8384
}
@@ -93,7 +94,7 @@ export const prepareOutputs = (outputs: EventTxOutput[], tokens: string[]): TxOu
9394
};
9495

9596
// @ts-ignore
96-
return [ currIndex + 1, [ ...newOutputs, finalOutput, ], ];
97+
return [currIndex + 1, [...newOutputs, finalOutput,],];
9798
},
9899
[0, []],
99100
);
@@ -124,7 +125,7 @@ export const getAddressBalanceMap = (
124125
const addressBalanceMap = {};
125126

126127
for (const input of inputs) {
127-
if (!input.decoded) {
128+
if (!isDecodedValid(input.decoded)) {
128129
// If we're unable to decode the script, we will also be unable to
129130
// calculate the balance, so just skip this input.
130131
continue;
@@ -140,7 +141,7 @@ export const getAddressBalanceMap = (
140141
}
141142

142143
for (const output of outputs) {
143-
if (!output.decoded) {
144+
if (!isDecodedValid(output.decoded)) {
144145
throw new Error('Output has no decoded script');
145146
}
146147

@@ -283,7 +284,7 @@ export const prepareInputs = (inputs: EventTxInput[], tokens: string[]): TxInput
283284
const utxo: Output = new Output(output.value, Buffer.from(output.script, 'base64'), {
284285
tokenData: output.token_data,
285286
});
286-
let token = '00';
287+
let token = hathorLib.constants.NATIVE_TOKEN_UID;
287288
if (!utxo.isTokenHTR()) {
288289
token = tokens[utxo.getTokenIndex()];
289290
}
@@ -296,9 +297,10 @@ export const prepareInputs = (inputs: EventTxInput[], tokens: string[]): TxInput
296297
// @ts-ignore
297298
script: utxo.script,
298299
token,
299-
decoded: output.decoded ? {
300+
decoded: isDecodedValid(output.decoded, ['type', 'address']) ? {
300301
type: output.decoded.type,
301302
address: output.decoded.address,
303+
// timelock might actually be null, so don't pass it to requiredKeys
302304
timelock: output.decoded.timelock,
303305
} : null,
304306
};

0 commit comments

Comments
 (0)