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

Fix transaction history report #2021

Merged
merged 27 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7ec0cf9
:hammer: Refactor code
sameersubudhi Jan 30, 2024
6e00713
:bug: Fix history export logic
sameersubudhi Jan 30, 2024
37b47a4
:hammer: Cache and reuse publicKeys
sameersubudhi Feb 2, 2024
bbf708d
:bug: Display all opening token balances
sameersubudhi Feb 2, 2024
a6a6cb2
:hammer: Add failsafe to avoid infinite auto-rescheduling
sameersubudhi Feb 2, 2024
c0d59c3
:bug: Flatten metadata entries list
sameersubudhi Feb 2, 2024
bf4ba4a
:bug: Fix normalizeTransactionFee method invocation
sameersubudhi Feb 2, 2024
6301f61
:bug: Remove cyclic dependency
sameersubudhi Feb 5, 2024
da085fe
:pencil: Add logs
sameersubudhi Feb 5, 2024
1118863
:bug: Ensure no duplicate entries
sameersubudhi Feb 5, 2024
31e3ab8
:bug: No transaction fee entries for shared reward entries
sameersubudhi Feb 6, 2024
cf01cdd
:hammer: Add handling for legacy:reclaimLSK transaction
sameersubudhi Feb 6, 2024
f52bc5b
:hammer: Refactor code
sameersubudhi Feb 6, 2024
7bd66a6
:white_check_mark: Add unit tests
nagdahimanshu Feb 6, 2024
d13f33d
:bug: Add fallback to fetch necessary missing events
sameersubudhi Feb 7, 2024
8859790
:arrow_up: Bump framework to v1.6.12
sameersubudhi Feb 7, 2024
f8e9a4e
:zap: Update lisk-service-framework dependency
sameersubudhi Feb 7, 2024
5cf84dd
:bug: Serialize BigInt to base 10
sameersubudhi Feb 7, 2024
c3db14f
:zap: Add handling for pos:reportMisbehavior transactions
sameersubudhi Feb 7, 2024
36db00a
:bug: Fix broken test
sameersubudhi Feb 7, 2024
05cc4df
:white_check_mark: Add/fix unit tests
nagdahimanshu Feb 7, 2024
cb22381
:hammer: Update test
sameersubudhi Feb 7, 2024
3b85279
:white_check_mark: Add tests
nagdahimanshu Feb 7, 2024
c221b38
:hammer: Refactor code
sameersubudhi Feb 7, 2024
b9748f5
:bug: Set proper param format
sameersubudhi Feb 8, 2024
d02e104
:wrench: Increase broker timeout to 10sec
sameersubudhi Feb 8, 2024
9032ed2
:ok_hand: Apply review recommendations
sameersubudhi Feb 8, 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
6 changes: 3 additions & 3 deletions services/blockchain-connector/methods/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const {
getTokenBalances,
getTokenInitializationFees,
tokenHasEscrowAccount,
getTokenBalanceAtGenesis,
getTokenBalancesAtGenesis,
} = require('../shared/sdk');

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

const {
Expand Down Expand Up @@ -188,7 +188,7 @@ module.exports = {
getSupportedTokens,
getTotalSupply,
getTokenInitializationFees,
getTokenBalanceAtGenesis,
getTokenBalancesAtGenesis,

// PoS
getAllPosValidators,
Expand Down
6 changes: 3 additions & 3 deletions services/blockchain-connector/shared/sdk/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const updateTokenInfo = async () => {
totalSupply = await getTotalSupply(true);
};

const getTokenBalanceAtGenesis = async address => {
const getTokenBalancesAtGenesis = async address => {
const MODULE_TOKEN_SUBSTORE_USER = 'userSubstore';

const tokenModuleGenesisAssets = await getGenesisAssetByModule({
Expand All @@ -84,7 +84,7 @@ const getTokenBalanceAtGenesis = async address => {
});

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

return balancesByAddress;
};
Expand All @@ -99,5 +99,5 @@ module.exports = {
getTotalSupply,
getTokenInitializationFees,
updateTokenInfo,
getTokenBalanceAtGenesis,
getTokenBalancesAtGenesis,
};
1 change: 1 addition & 0 deletions services/blockchain-indexer/shared/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ const EVENT = Object.freeze({
UNLOCK: 'unlock',
COMMAND_EXECUTION_RESULT: 'commandExecutionResult',
REWARD_MINTED: 'rewardMinted',
REWARDS_ASSIGNED: 'rewardsAssigned',
CCM_SEND_SUCCESS: 'ccmSendSuccess',
CCM_SENT_FAILED: 'ccmSentFailed',
});
Expand Down
17 changes: 16 additions & 1 deletion services/blockchain-indexer/shared/indexer/utils/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ const {
},
} = require('lisk-service-framework');

const { getGenesisHeight, EVENT, EVENT_TOPIC_PREFIX, LENGTH_ID } = require('../../constants');
const {
getGenesisHeight,
EVENT,
EVENT_TOPIC_PREFIX,
LENGTH_ID,
MODULE,
} = require('../../constants');

const config = require('../../../config');
const eventsTableSchema = require('../../database/schema/events');
Expand Down Expand Up @@ -99,6 +105,15 @@ const getEventsInfoToIndex = (block, events) => {
eventsInfoToIndex.eventTopicsInfo.push(eventTopicAdditionalInfo);
}
});

// Add validator address as a topic for rewardsAssigned events, required for export microservice
if (event.module === MODULE.POS && event.name === EVENT.REWARDS_ASSIGNED) {
const eventTopicAdditionalInfo = {
eventID: event.id,
topic: event.data.validatorAddress,
};
eventsInfoToIndex.eventTopicsInfo.push(eventTopicAdditionalInfo);
}
});

return eventsInfoToIndex;
Expand Down
2 changes: 1 addition & 1 deletion services/export/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ LoggerConfig(config.log);

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

const logger = Logger();

Expand Down
9 changes: 2 additions & 7 deletions services/export/shared/excelFieldMappings.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,16 @@ const transactionMappings = [
{ header: 'Date', key: 'date' },
{ header: 'Time', key: 'time' },
{ header: 'Block Height', key: 'blockHeight' },
{ header: 'Block Reward', key: 'blockReward' },
{ header: 'Block Reward Token ID', key: 'rewardTokenID' },
{ header: 'Transaction ID', key: 'transactionID' },
{ header: 'Module:Command', key: 'moduleCommand' },
{ header: 'Transaction Fee', key: 'fee' },
{ header: 'Transaction Fee Token ID', key: 'txFeeTokenID' },
{ header: 'Amount', key: 'amount' },
{ header: 'Amount Token ID', key: 'amountTokenID' },
{ header: 'Message Fee', key: 'messageFee' },
{ header: 'Message Fee Token ID', key: 'messageFeeTokenID' },
{ header: 'Sender Address', key: 'senderAddress' },
{ header: 'Sender Public Key', key: 'senderPublicKey' },
{ header: 'Recipient Address', key: 'recipientAddress' },
{ header: 'Recipient Public Key', key: 'recipientPublicKey' },
{ header: 'Reward Amount', key: 'rewardAmount' },
{ header: 'Reward Token ID', key: 'rewardTokenID' },
{ header: 'Note', key: 'note' },
{ header: 'Sending Chain ID', key: 'sendingChainID' },
{ header: 'Receiving Chain ID', key: 'receivingChainID' },
Expand All @@ -41,8 +35,9 @@ const transactionMappings = [
const metadataMappings = [
{ header: 'Chain ID', key: 'chainID' },
{ header: 'Chain Name', key: 'chainName' },
{ header: 'Opening Balance', key: 'openingBalance' },
{ header: 'Note', key: 'note' },
{ header: 'Opening Balance Amount', key: 'openingBalanceAmount' },
{ header: 'Token ID', key: 'tokenID' },
];

module.exports = {
Expand Down
109 changes: 109 additions & 0 deletions services/export/shared/helpers/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,24 @@
* Removal or modification of this copyright notice is prohibited.
*
*/
const { Logger, CacheLRU } = require('lisk-service-framework');
const {
utils: { hash },
address: { getLisk32AddressFromPublicKey: getLisk32AddressFromPublicKeyHelper },
legacyAddress: { getFirstEightBytesReversed },
} = require('@liskhq/lisk-cryptography');

const { PUBLIC_KEY, ADDRESS_LISK32 } = require('../regex');
const { requestAllCustom } = require('./requestAll');
const { MODULE, MODULE_SUB_STORE } = require('./constants');
Dismissed Show dismissed Hide dismissed
const { requestConnector, requestIndexer } = require('./request');
Dismissed Show dismissed Hide dismissed

const logger = Logger();

const publicKeyCache = CacheLRU('publicKey', { max: 100000 });
sameersubudhi marked this conversation as resolved.
Show resolved Hide resolved

let tokenModuleData;
let loadingAssets = false;

const validateLisk32Address = address =>
typeof address === 'string' && ADDRESS_LISK32.test(address);
Expand All @@ -32,9 +43,107 @@ const getLisk32AddressFromPublicKey = publicKey =>
const getLegacyAddress = publicKey =>
getFirstEightBytesReversed(hash(Buffer.from(publicKey, 'hex'))).toString('hex');

const getAddressFromParams = params =>
params.address || getLisk32AddressFromPublicKey(params.publicKey);

const checkIfAccountExists = async address => {
const response = await requestIndexer('token.account.exists', { address });
const { isExists } = response.data;
return isExists;
};

const checkIfAccountHasTransactions = async address => {
// Using getTransactions from chain.js introduces cyclic dependency
const response = await requestIndexer('transactions', { address, limit: 1 });
return !!response.data.length;
};

const checkIfAccountIsValidator = async address => {
const response = await requestIndexer('pos.validators', { address, sort: 'commission:asc' });
return !!response.data.length;
};

const getTokenBalancesAtGenesis = async () => {
if (!tokenModuleData && !loadingAssets) {
loadingAssets = true; // loadingAssets avoids repeated invocations

// Asynchronously fetch the token module genesis assets and cache locally
logger.info('Attempting to fetch and cache the token module genesis assets.');
requestConnector('getGenesisAssetsLength', {
module: MODULE.TOKEN,
subStore: MODULE_SUB_STORE.TOKEN.USER,
})
.then(async genesisBlockAssetsLength => {
const totalUsers = genesisBlockAssetsLength[MODULE.TOKEN][MODULE_SUB_STORE.TOKEN.USER];

const response = await requestAllCustom(
requestConnector,
'getGenesisAssetByModule',
{ module: MODULE.TOKEN, subStore: MODULE_SUB_STORE.TOKEN.USER, limit: 1000 },
totalUsers,
);

tokenModuleData = response[MODULE_SUB_STORE.TOKEN.USER];
loadingAssets = false;
logger.info('Successfully cached token module genesis assets.');
})
.catch(err => {
logger.warn(
`Failed to fetch token module genesis assets. Will retry later.\nError: ${err.message}`,
);
logger.debug(err.stack);

loadingAssets = false;
});
}

return tokenModuleData;
};

const getOpeningBalances = async address => {
const balancesAtGenesis = await getTokenBalancesAtGenesis();
const balancesByAddress = balancesAtGenesis
? balancesAtGenesis.filter(e => e.address === address)
: await requestConnector('getTokenBalancesAtGenesis', { address });

const openingBalances = balancesByAddress.map(e => {
const { tokenID, availableBalance, lockedBalances } = e;
const totalLockedBalance = lockedBalances.reduce(
(total, balInfo) => total + BigInt(balInfo.amount),
BigInt('0'),
);

const amount = BigInt(availableBalance) + totalLockedBalance;

return { tokenID, amount: amount.toString() };
});

return openingBalances;
};

const cachePublicKey = async publicKey => {
const address = getLisk32AddressFromPublicKey(publicKey);
await publicKeyCache.set(address, publicKey);
};

const getPublicKeyByAddress = async address => {
const publicKey = await publicKeyCache.get(address);
if (publicKey) return publicKey;

return null;
};

module.exports = {
validateLisk32Address,
validatePublicKey,
getLisk32AddressFromPublicKey,
getLegacyAddress,
getAddressFromParams,
checkIfAccountExists,
checkIfAccountHasTransactions,
checkIfAccountIsValidator,
getTokenBalancesAtGenesis,
getOpeningBalances,
cachePublicKey,
getPublicKeyByAddress,
};
Loading
Loading