Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Fix export microservice #2016

Merged
merged 26 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1315b5f
:zap: Allow slack in index readiness when scheduling history export jobs
sameersubudhi Jan 24, 2024
7348e75
:zap: Do not re-schedule duplicate jobs
sameersubudhi Jan 25, 2024
9ac0d63
:pencil: Improve logging
sameersubudhi Jan 25, 2024
aaed0eb
:bug: Fix index readiness check logic for export job scheduling
sameersubudhi Jan 25, 2024
8b7feae
:racehorse: Optimize code
sameersubudhi Jan 25, 2024
6735f95
:hammer: Refactor code. Prefer early exit from the loop
sameersubudhi Jan 25, 2024
5fab2b4
:bug: Fix queue config and initialization
sameersubudhi Jan 25, 2024
8bf9fb8
:hammer: Automatically se-schedule a job if it timesout
sameersubudhi Jan 25, 2024
e7debd7
:wrench: Add microservice dependencies for export microservice
sameersubudhi Jan 25, 2024
4336100
:racehorse: Optimize genesis asset query
sameersubudhi Jan 25, 2024
8fe0ba7
:hammer: Fix broken unit tests
sameersubudhi Jan 25, 2024
c2d00ed
:heavy_check_mark: Fix unit tests
nagdahimanshu Jan 25, 2024
f28f824
:rotating_light: Add new unit tests
sameersubudhi Jan 26, 2024
1f8ec22
:ok_hand: Apply review suggestion
sameersubudhi Jan 26, 2024
5dc2596
:wrench: Limit indexing pace to assist in app node performance
sameersubudhi Jan 26, 2024
fb3ba7d
:racehorse: Use lighter endpoint invocations to verify account existence
sameersubudhi Jan 26, 2024
f652672
:hammer: Locally cache genesis token assets at init
sameersubudhi Jan 26, 2024
d63cd77
:heavy_check_mark: Fix unit tests
sameersubudhi Jan 26, 2024
4409285
:hammer: Clear any stale intervals
sameersubudhi Jan 26, 2024
d6d937f
:art: Add logs
sameersubudhi Jan 26, 2024
58fd9b4
:wrench: Revert ratelimiting on the indexing jobs
sameersubudhi Jan 26, 2024
0fe3729
:hammer: Increase query payload size
sameersubudhi Jan 26, 2024
1f0cc29
:white_check_mark: Add unit tests
nagdahimanshu Jan 26, 2024
b65e6d6
:bug: Add prefix handling when extracting transactionID from event to…
sameersubudhi Jan 27, 2024
3e32931
:rotating_light: Add more unit tests
sameersubudhi Jan 27, 2024
a551dfa
:pencil: Fix swagger docs
sameersubudhi Jan 29, 2024
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
8 changes: 8 additions & 0 deletions services/blockchain-connector/methods/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const {
getTokenBalances,
getTokenInitializationFees,
tokenHasEscrowAccount,
getTokenBalanceAtGenesis,
} = require('../shared/sdk');

module.exports = [
Expand Down Expand Up @@ -84,4 +85,11 @@ module.exports = [
controller: async () => getTokenInitializationFees(),
params: {},
},
{
name: 'getTokenBalanceAtGenesis',
controller: async ({ address }) => getTokenBalanceAtGenesis(address),
params: {
address: { optional: false, type: 'string' },
},
},
];
4 changes: 3 additions & 1 deletion services/blockchain-connector/shared/sdk/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const {
getTotalSupply,
getTokenInitializationFees,
updateTokenInfo,
getTokenBalanceAtGenesis,
} = require('./token');

const {
Expand Down Expand Up @@ -178,7 +179,7 @@ module.exports = {
dryRunTransaction,
formatTransaction,

// Tokens
// Token
tokenHasUserAccount,
tokenHasEscrowAccount,
getTokenBalance,
Expand All @@ -187,6 +188,7 @@ module.exports = {
getSupportedTokens,
getTotalSupply,
getTokenInitializationFees,
getTokenBalanceAtGenesis,

// PoS
getAllPosValidators,
Expand Down
17 changes: 17 additions & 0 deletions services/blockchain-connector/shared/sdk/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
const { invokeEndpoint } = require('./client');
const { isMainchain } = require('./interoperability');
const { getGenesisAssetByModule } = require('./genesisBlock');
const { MODULE_NAME_TOKEN } = require('./constants/names');
Dismissed Show dismissed Hide dismissed

let escrowedAmounts;
let supportedTokens;
Expand Down Expand Up @@ -73,6 +75,20 @@ const updateTokenInfo = async () => {
totalSupply = await getTotalSupply(true);
};

const getTokenBalanceAtGenesis = async address => {
const MODULE_TOKEN_SUBSTORE_USER = 'userSubstore';
Dismissed Show dismissed Hide dismissed

const tokenModuleGenesisAssets = await getGenesisAssetByModule({
module: MODULE_NAME_TOKEN,
subStore: MODULE_TOKEN_SUBSTORE_USER,
});

const balancesAtGenesis = tokenModuleGenesisAssets[MODULE_TOKEN_SUBSTORE_USER];
const balancesByAddress = balancesAtGenesis.find(e => e.address === address);

return balancesByAddress;
};

module.exports = {
tokenHasUserAccount: hasUserAccount,
tokenHasEscrowAccount: hasEscrowAccount,
Expand All @@ -83,4 +99,5 @@ module.exports = {
getTotalSupply,
getTokenInitializationFees,
updateTokenInfo,
getTokenBalanceAtGenesis,
};
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ module.exports = [
address: { optional: true, type: 'string', pattern: regex.ADDRESS_LISK32 },
publicKey: { optional: true, type: 'string', pattern: regex.PUBLIC_KEY },
name: { optional: true, type: 'string', pattern: regex.NAME },
tokenID: { optional: false, type: 'string', pattern: regex.TOKEN_ID },
// Set tokenID as optional in indexer because export microservice needs it to be optional. Should remain mandatory everywhere else.
tokenID: { optional: true, type: 'string', pattern: regex.TOKEN_ID },
},
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const { requestConnector } = require('../../../utils/request');
const { getAddressByName } = require('../../utils/validator');

const { getLisk32AddressFromPublicKey } = require('../../../utils/account');
const { getAvailableTokenIDs } = require('./availableIDs');

const tokenHasUserAccount = async params => {
const response = {
Expand All @@ -26,7 +27,6 @@ const tokenHasUserAccount = async params => {
meta: {},
};

const { tokenID } = params;
let { address } = params;

if (!address && params.name) {
Expand All @@ -39,11 +39,28 @@ const tokenHasUserAccount = async params => {

// Check existence if address found. Return false otherwise
if (address) {
const { exists: isExists } = await requestConnector('tokenHasUserAccount', {
address,
tokenID,
});
response.data.isExists = isExists;
const tokenIDs = [];

if (params.tokenID) {
tokenIDs.push(params.tokenID);
} else {
// Logic introduced to support the export microservice
const result = await getAvailableTokenIDs();
tokenIDs.push(...result.data.tokenIDs);
}

// eslint-disable-next-line no-restricted-syntax
for (const tokenID of tokenIDs) {
const { exists: isExists } = await requestConnector('tokenHasUserAccount', {
address,
tokenID,
});

if (isExists) {
response.data.isExists = isExists;
break;
}
}
}

return response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const MYSQL_ENDPOINT = config.endpoints.mysqlReplica;

const getAccountBalancesTable = () => getTableInstance(accountBalancesTableSchema, MYSQL_ENDPOINT);

const getAvailableTokenIDs = async params => {
const getAvailableTokenIDs = async (params = {}) => {
const response = {
data: {},
meta: {},
Expand Down
1 change: 1 addition & 0 deletions services/blockchain-indexer/shared/indexer/genesisBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ const indexPosModuleAssets = async dbTrx => {
};

const indexGenesisBlockAssets = async dbTrx => {
clearTimeout(intervalTimeout);
logger.info('Starting to index the genesis assets.');
intervalTimeout = setInterval(
() => logger.info('Genesis assets indexing still in progress...'),
Expand Down
4 changes: 3 additions & 1 deletion services/blockchain-indexer/shared/utils/transactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ const getTransactionExecutionStatus = (tx, events) => {
e => e.topics.includes(EVENT_TOPIC_PREFIX.TX_ID.concat(tx.id)) || e.topics.includes(tx.id),
);
if (!txExecResultEvent)
throw Error(`Event unavailable to determine execution status for transaction: ${tx.id}.`);
throw Error(
`Event unavailable to determine execution status for transaction: ${tx.id}.\nEnsure that you have set 'system.keepEventsForHeights: -1' in your node config before syncing it with the network.`,
);

return txExecResultEvent.data.success ? TRANSACTION_STATUS.SUCCESSFUL : TRANSACTION_STATUS.FAILED;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* LiskHQ/lisk-service
* Copyright © 2024 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*
*/

/* eslint-disable import/no-dynamic-require */
const { resolve } = require('path');

const mockRequestFilePath = resolve(`${__dirname}/../../../../../../shared/utils/request`);
const mockTokenAvailableIDsFilePath = resolve(
`${__dirname}/../../../../../../shared/dataService/business/token/availableIDs`,
);
const mockTokenaccountExistsFilePath = resolve(
`${__dirname}/../../../../../../shared/dataService/business/token/accountExists`,
);
const mockValidatorUtilsPath = resolve(
`${__dirname}/../../../../../../shared/dataService/utils/validator`,
);

beforeEach(() => jest.resetModules());

jest.mock('lisk-service-framework', () => {
const actual = jest.requireActual('lisk-service-framework');
return {
...actual,
DB: {
...actual.DB,
MySQL: {
...actual.DB.MySQL,
KVStore: {
...actual.DB.MySQL.KVStore,
getKeyValueTable: jest.fn(),
},
},
},
CacheRedis: jest.fn(),
CacheLRU: jest.fn(),
};
});

describe('tokenHasUserAccount', () => {
const tokenID = '0000000000000000';
const accAddressExists = 'lskyvvam5rxyvbvofxbdfcupxetzmqxu22phm4yuo';
const accAddressNotExists = 'lskz23xokaxhmmkpbzdjt5agcq59qkby7bne2hwpk';
const name = 'testAccount';
const publicKey = '3972849f2ab66376a68671c10a00e8b8b67d880434cc65b04c6ed886dfa91c2c';

describe('should return isExists true when user account exists', () => {
it('when called with tokenID and address', async () => {
jest.mock(mockRequestFilePath, () => ({
requestConnector: jest.fn(() => ({ exists: true })),
}));

// Make a query to tokenHasUserAccount function
const { tokenHasUserAccount } = require(mockTokenaccountExistsFilePath);
const result = await tokenHasUserAccount({ address: accAddressExists, tokenID });

expect(result).toEqual({
data: {
isExists: true,
},
meta: {},
});
});

it('when called with tokenID and publicKey', async () => {
jest.mock(mockRequestFilePath, () => ({
requestConnector: jest.fn(() => ({ exists: true })),
}));

// Make a query to tokenHasUserAccount function
const { tokenHasUserAccount } = require(mockTokenaccountExistsFilePath);
const result = await tokenHasUserAccount({ publicKey, tokenID });
expect(result).toEqual({
data: {
isExists: true,
},
meta: {},
});
});

it('when called with tokenID and name', async () => {
jest.mock(mockRequestFilePath, () => ({
requestConnector: jest.fn(() => ({ exists: true })),
}));

jest.mock(mockValidatorUtilsPath);
const { getAddressByName } = require(mockValidatorUtilsPath);
getAddressByName.mockReturnValueOnce(accAddressExists);

// Make a query to tokenHasUserAccount function
const { tokenHasUserAccount } = require(mockTokenaccountExistsFilePath);
const result = await tokenHasUserAccount({ name, tokenID });
expect(result).toEqual({
data: {
isExists: true,
},
meta: {},
});
});

it('when called with address', async () => {
jest.mock(mockRequestFilePath, () => ({
requestConnector: jest.fn(() => ({ exists: true })),
}));

jest.mock(mockTokenAvailableIDsFilePath);
const { getAvailableTokenIDs } = require(mockTokenAvailableIDsFilePath);
getAvailableTokenIDs.mockReturnValueOnce({
data: { tokenIDs: ['0000000000000000'] },
meta: {},
});

// Make a query to tokenHasUserAccount function
const { tokenHasUserAccount } = require(mockTokenaccountExistsFilePath);
const result = await tokenHasUserAccount({ address: accAddressExists });

expect(result).toEqual({
data: {
isExists: true,
},
meta: {},
});
});
});

describe('should return isExists false when user account does not exists', () => {
it('when called with tokenID and address', async () => {
jest.mock(mockRequestFilePath, () => ({
requestConnector: jest.fn(() => ({ exists: false })),
}));

// Make a query to tokenHasUserAccount function
const { tokenHasUserAccount } = require(mockTokenaccountExistsFilePath);
const result = await tokenHasUserAccount({ address: accAddressNotExists, tokenID });

expect(result).toEqual({
data: {
isExists: false,
},
meta: {},
});
});
});
});
17 changes: 15 additions & 2 deletions services/export/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
*
*/
const path = require('path');
const { Microservice, LoggerConfig, Logger } = require('lisk-service-framework');
const { Signals, Microservice, LoggerConfig, Logger } = require('lisk-service-framework');

const config = require('./config');

LoggerConfig(config.log);

const packageJson = require('./package.json');
const { setAppContext } = require('./shared/helpers');
const { getTokenBalancesAtGenesis } = require('./shared/transactionsExport');

const logger = Logger();

Expand All @@ -32,6 +33,17 @@ const app = Microservice({
timeout: config.brokerTimeout,
packageJson,
logger: config.log,
events: {
systemNodeInfo: async payload => {
logger.debug("Received a 'systemNodeInfo' moleculer event from connecter.");
Signals.get('nodeInfo').dispatch(payload);
},
'update.index.status': async payload => {
logger.debug("Received a 'update.index.status' moleculer event from indexer.");
Signals.get('updateIndexStatus').dispatch(payload);
},
},
dependencies: ['connector', 'indexer', 'app-registry'],
});

setAppContext(app);
Expand All @@ -43,8 +55,9 @@ app.addJobs(path.join(__dirname, 'jobs'));
// Run the application
app
.run()
.then(() => {
.then(async () => {
logger.info(`Service started ${packageJson.name}.`);
await getTokenBalancesAtGenesis();
})
.catch(err => {
logger.fatal(`Failed to start service ${packageJson.name} due to: ${err.message}`);
Expand Down
15 changes: 14 additions & 1 deletion services/export/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,20 @@ config.excel.sheets = {
config.queue = {
scheduleTransactionExport: {
name: 'ScheduleTransactionExportQueue',
concurrency: 50,
concurrency: 10,
options: {
defaultJobOptions: {
attempts: 5,
timeout: 5 * 60 * 1000, // millisecs
backoff: {
type: 'exponential',
delay: 1 * 60 * 1000, // millisecs
},
removeOnComplete: true,
removeOnFail: true,
stackTraceLimit: 0,
},
},
},
defaults: {
jobOptions: {
Expand Down
Loading
Loading