diff --git a/ci/config.json.ci b/ci/config.json.ci index a2dd5d87c..8f072010e 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -175,6 +175,10 @@ "jobRedecodeTx": { "limitRecordGet": 100 }, + "jobReassignMsgIndexToEvent": { + "millisecondCrawl": 1000, + "blocksPerCall": 100 + }, "crawlIbcTao": { "key": "crawlIbcTao", "millisecondRepeatJob": 2000, diff --git a/config.json b/config.json index 731b618f4..3854081fc 100644 --- a/config.json +++ b/config.json @@ -175,6 +175,10 @@ "jobRedecodeTx": { "limitRecordGet": 100 }, + "jobReassignMsgIndexToEvent": { + "millisecondCrawl": 1000, + "blocksPerCall": 100 + }, "crawlIbcTao": { "key": "crawlIbcTao", "millisecondRepeatJob": 2000, diff --git a/src/common/constant.ts b/src/common/constant.ts index a0a60bed2..7980dddc5 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -71,6 +71,7 @@ export const BULL_JOB_NAME = { REINDEX_CW721_HISTORY: 'reindex:cw721-history', HANDLE_MIGRATE_CONTRACT: 'handle:migrate-contract', JOB_REDECODE_TX: 'job:redecode-tx', + JOB_REASSIGN_MSG_INDEX_TO_EVENT: 'job:reassign-msg-index-to-event', CRAWL_IBC_TAO: 'crawl:ibc-tao', CRAWL_GENESIS_IBC_TAO: 'crawl:genesis-ibc-tao', CRAWL_IBC_APP: 'crawl:ibc-app', @@ -232,6 +233,10 @@ export const SERVICE = { key: 'ReDecodeTx', path: 'v1.ReDecodeTx', }, + ReAssignMsgIndexToEvent: { + key: 'ReAssignMsgIndexToEvent', + path: 'v1.ReAssignMsgIndexToEvent', + }, }, CrawlIBCTaoService: { key: 'CrawlIBCTaoService', diff --git a/src/models/event.ts b/src/models/event.ts index 13dfe8e3e..0ac936d41 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -12,7 +12,7 @@ export class Event extends BaseModel { tx_id!: number; - tx_msg_index: number | undefined; + tx_msg_index?: number | undefined; type!: string; @@ -32,7 +32,6 @@ export class Event extends BaseModel { required: ['type'], properties: { tx_id: { type: 'number' }, - tx_msg_index: { type: 'number' }, type: { type: 'string' }, block_height: { type: 'number' }, source: { type: 'string', enum: Object.values(this.SOURCE) }, diff --git a/src/services/crawl-tx/crawl_tx.service.ts b/src/services/crawl-tx/crawl_tx.service.ts index 156fa3b97..e90a6657d 100644 --- a/src/services/crawl-tx/crawl_tx.service.ts +++ b/src/services/crawl-tx/crawl_tx.service.ts @@ -333,58 +333,251 @@ export default class CrawlTxService extends BullableService { this.logger.debug('result insert tx', resultInsertGraph); } - private setMsgIndexToEvent(tx: any) { - const mapEventMsgIdx: Map = new Map(); + public mappingFlatEventToLog( + eventHasIndex: any, + eventEncodedFlat: any, + indexMsg: number + ) { + const attributesInEvent = eventHasIndex.attributes; + // i, j are index of eventHasIndex and eventEncodedFlat + let i = 0; + let j = 0; + while (i < attributesInEvent.length && j < eventEncodedFlat.length) { + // find last element with same eventIndex + let lastIndexSameEvent = j; + while ( + lastIndexSameEvent < eventEncodedFlat.length && + eventEncodedFlat[lastIndexSameEvent].indexEvent === + eventEncodedFlat[j].indexEvent + ) { + lastIndexSameEvent += 1; + } + + if ( + attributesInEvent[i].key === eventEncodedFlat[j].key && + attributesInEvent[i].value === eventEncodedFlat[j].value + ) { + const isEventMapped = eventEncodedFlat + .slice(j, lastIndexSameEvent) + // eslint-disable-next-line @typescript-eslint/no-loop-func, no-inner-declarations + .every((item: any, index: number) => { + if ( + attributesInEvent[i + index].key === item.key && + attributesInEvent[i + index].value === item.value + ) { + return true; + } + return false; + }); + if (isEventMapped) { + // mark event encoded to be mapped + for (let k = j; k < lastIndexSameEvent; k += 1) { + // eslint-disable-next-line no-param-reassign + eventEncodedFlat[k].indexMapped = indexMsg; + } + i += lastIndexSameEvent - j; + } + } + j = lastIndexSameEvent; + } + } + + // self check msg index by counting event + private selfCheckByAnotherWay(tx: any) { + // count total attribute for each message, countAttributeInEvent[i] = x mean message i has x attributes + const countAttributeInEvent: number[] = []; + tx.tx_response.logs.forEach((log: any) => { + const countAttribute = log.events.reduce( + (acc: number, curr: any) => acc + curr.attributes.length, + 0 + ); + countAttributeInEvent.push(countAttribute); + }); + + let reachLastEventTypeTx = false; + let countCurrentAttribute = 0; + let currentCompareEventId = 0; + for (let i = 0; i < tx.tx_response.events.length; i += 1) { + if (tx.tx_response.events[i].type === 'tx') { + reachLastEventTypeTx = true; + } + if (reachLastEventTypeTx && tx.tx_response.events[i].type !== 'tx') { + if ( + countCurrentAttribute < countAttributeInEvent[currentCompareEventId] + ) { + countCurrentAttribute += tx.tx_response.events[i].attributes.length; + } + + // after count, check if count is equal countAttributeInEvent[currentCompareEventId] or not + if ( + countCurrentAttribute === countAttributeInEvent[currentCompareEventId] + ) { + // if true, count success, then next currentCompareEventId and reset count = 0 + currentCompareEventId += 1; + countCurrentAttribute = 0; + } else if ( + countCurrentAttribute > countAttributeInEvent[currentCompareEventId] + ) { + this.logger.warn('Count event in log is not equal event encoded'); + return false; + } + } + } + return true; + } + + private checkMappingEventToLog(tx: any) { + this.logger.info('checking mapping log in tx :', tx.tx_response.txhash); + let flattenLog: string[] = []; + let flattenEventEncoded: string[] = []; - // set index_msg from log to mapEventMsgIdx - tx.tx_response.logs?.forEach((log: any, index: number) => { + tx?.tx_response?.logs?.forEach((log: any, index: number) => { log.events.forEach((event: any) => { - const { type } = event; - event.attributes.forEach((attribute: any) => { - const keyInMap = `${type}_${attribute.key}_${attribute.value}`; - if (mapEventMsgIdx.has(keyInMap)) { - const listIndex = mapEventMsgIdx.get(keyInMap); - listIndex?.push(index); + event.attributes.forEach((attr: any) => { + if (attr.value === undefined) { + flattenLog.push(`${index}-${event.type}-${attr.key}-null`); } else { - mapEventMsgIdx.set(keyInMap, [index]); + flattenLog.push(`${index}-${event.type}-${attr.key}-${attr.value}`); } }); }); }); - // set index_msg from mapEventMsgIdx to event - tx.tx_response.events.forEach((event: any) => { - const { type } = event; - event.attributes.forEach((attribute: any) => { - const key = attribute?.key - ? fromUtf8(fromBase64(attribute?.key)) - : null; - const value = attribute?.value - ? fromUtf8(fromBase64(attribute?.value)) - : null; - const keyInMap = `${type}_${key}_${value}`; - - const listIndex = mapEventMsgIdx.get(keyInMap); - // get first index with this key - const firstIndex = listIndex?.shift(); - - if (firstIndex != null) { - if (event.msg_index && event.msg_index !== firstIndex) { - this.logger.warn( - `something wrong: setting index ${firstIndex} to existed index ${event.msg_index}` - ); - } else { - // eslint-disable-next-line no-param-reassign - event.msg_index = firstIndex; - } - } - - // delete key in map if value is empty - if (listIndex?.length === 0) { - mapEventMsgIdx.delete(keyInMap); + tx?.tx_response?.events?.forEach((event: any) => { + event.attributes.forEach((attr: any) => { + if (event.msg_index !== undefined) { + const key = attr.key ? fromUtf8(fromBase64(attr.key)) : null; + const value = attr.value ? fromUtf8(fromBase64(attr.value)) : null; + flattenEventEncoded.push( + `${event.msg_index}-${event.type}-${key}-${value}` + ); } }); }); + // compare 2 array + if (flattenLog.length !== flattenEventEncoded.length) { + this.logger.warn('Length between 2 flatten array is not equal'); + } + flattenLog = flattenLog.sort(); + flattenEventEncoded = flattenEventEncoded.sort(); + const checkResult = flattenLog.every( + (item: string, index: number) => item === flattenEventEncoded[index] + ); + if (checkResult === false) { + this.logger.warn('Mapping event to log is wrong'); + } + } + + public setMsgIndexToEvent(tx: any) { + /*------ + DO NOT USE CURRENTLY + MAPPING BY ORDER IN EVENT AND LOG + THIS CASE BASED ON ORDER NOT CHANGED BETWEEN EVENT AND LOG + --------*/ + // // flatten event and decode key value + // const eventEncodedFlats: any[] = []; + // tx.tx_response.events.forEach((event: any, index: number) => { + // event.attributes.forEach((attr: any) => { + // eventEncodedFlats.push({ + // ...{ + // key: attr.key ? fromUtf8(fromBase64(attr.key)) : null, + // value: attr.value ? fromUtf8(fromBase64(attr.value)) : null, + // }, + // indexEvent: index, + // type: event.type, + // }); + // }); + // }); + // // loop logs (has order for each messages) + // tx.tx_response.logs.forEach((log: any, index: number) => { + // // loop each event in log to compare with event encoded + // log.events.forEach((eventInLog: any) => { + // // filter list event has type equal eventInLog.type and not be setted index msg + // const filtedEventByTypeFlat = eventEncodedFlats.filter( + // (eventEncoded: any) => + // eventEncoded.type === eventInLog.type && + // eventEncoded.msg_index === undefined + // ); + // // mapping between log and event + // this.mappingFlatEventToLog(eventInLog, filtedEventByTypeFlat, index); + // }); + // }); + // // set index msg to event encoded + // eventEncodedFlats + // .filter((item: any) => item.indexMapped !== undefined) + // .forEach((item: any) => { + // if ( + // tx.tx_response.events[item.indexEvent].msg_index !== undefined && + // tx.tx_response.events[item.indexEvent].msg_index !== item.indexMapped + // ) { + // this.logger.warn( + // `something wrong: setting index ${ + // item.indexMapped + // } to existed index ${ + // tx.tx_response.eventstx.tx_response.events[item.indexEvent] + // .msg_index + // }` + // ); + // } + // // eslint-disable-next-line no-param-reassign + // tx.tx_response.events[item.indexEvent].msg_index = item.indexMapped; + // }); + // // self check msg index by counting event + // const selfCheck = this.selfCheckByAnotherWay(tx); + // if (!selfCheck) { + // this.logger.warn('selfcheck fail'); + // } + + /*--------- + TESTING + MAPPING EVENT BY COUNT EACH LOG AND EVENT MUST BE SAME + -----------*/ + + // if this is failed tx, then no need to set index msg + if (!tx.tx_response.logs) { + this.logger.info('Failed tx, no need to set index msg'); + return; + } + // count total attribute for each message, countAttributeInEvent[i] = x mean message i has x attributes + const countAttributeInEvent: number[] = tx?.tx_response?.logs?.map( + (log: any) => + log.events.reduce( + (acc: number, curr: any) => acc + curr.attributes.length, + 0 + ) + ); + + let reachLastEventTypeTx = false; + let countCurrentAttribute = 0; + let currentCompareEventId = 0; + for (let i = 0; i < tx?.tx_response?.events?.length; i += 1) { + if (tx.tx_response.events[i].type === 'tx') { + reachLastEventTypeTx = true; + } + if (reachLastEventTypeTx && tx.tx_response.events[i].type !== 'tx') { + if ( + countCurrentAttribute < countAttributeInEvent[currentCompareEventId] + ) { + countCurrentAttribute += tx.tx_response.events[i].attributes.length; + // eslint-disable-next-line no-param-reassign + tx.tx_response.events[i].msg_index = currentCompareEventId; + } + + // after count, check if count is equal countAttributeInEvent[currentCompareEventId] or not + if ( + countCurrentAttribute === countAttributeInEvent[currentCompareEventId] + ) { + // if true, count success, then next currentCompareEventId and reset count = 0 + currentCompareEventId += 1; + countCurrentAttribute = 0; + } else if ( + countCurrentAttribute > countAttributeInEvent[currentCompareEventId] + ) { + this.logger.warn('Count event in log is not equal event encoded'); + } + } + } + this.checkMappingEventToLog(tx); } private _findAttribute( diff --git a/src/services/job/reassign_msg_index_to_event.service.ts b/src/services/job/reassign_msg_index_to_event.service.ts new file mode 100644 index 000000000..f20b49ab6 --- /dev/null +++ b/src/services/job/reassign_msg_index_to_event.service.ts @@ -0,0 +1,195 @@ +/* eslint-disable no-await-in-loop */ +import { Service } from '@ourparentcenter/moleculer-decorators-extended'; +import { ServiceBroker } from 'moleculer'; +import { fromBase64, fromUtf8 } from '@cosmjs/encoding'; +import { Knex } from 'knex'; +import { + Transaction, + EventAttribute, + BlockCheckpoint, + Event, +} from '../../models'; +import BullableService, { QueueHandler } from '../../base/bullable.service'; +import { BULL_JOB_NAME, SERVICE } from '../../common'; +import config from '../../../config.json' assert { type: 'json' }; +import knex from '../../common/utils/db_connection'; +import CrawlTxService from '../crawl-tx/crawl_tx.service'; + +@Service({ + name: SERVICE.V1.JobService.ReAssignMsgIndexToEvent.key, + version: 1, +}) +export default class JobReAssignMsgIndexToEvent extends BullableService { + public constructor(public broker: ServiceBroker) { + super(broker); + } + + crawlTxService: CrawlTxService = new ServiceBroker({ + logger: true, + }).createService(CrawlTxService) as CrawlTxService; + + @QueueHandler({ + queueName: BULL_JOB_NAME.JOB_REASSIGN_MSG_INDEX_TO_EVENT, + jobName: BULL_JOB_NAME.JOB_REASSIGN_MSG_INDEX_TO_EVENT, + }) + async reassignMsgIndexToEvent(_payload: { lastBlockCrawled: number }) { + const blockCheckpoint = await BlockCheckpoint.query().findOne({ + job_name: BULL_JOB_NAME.JOB_REASSIGN_MSG_INDEX_TO_EVENT, + }); + this.logger.info( + `Re assign msg index start from block ${blockCheckpoint?.height}` + ); + if (blockCheckpoint?.height === _payload.lastBlockCrawled) { + return; + } + + let lastBlock = + (blockCheckpoint?.height ?? 0) + + config.jobReassignMsgIndexToEvent.blocksPerCall; + if (lastBlock > _payload.lastBlockCrawled) { + lastBlock = _payload.lastBlockCrawled; + } + await knex.transaction(async (trx) => { + const listTx = await Transaction.query() + .withGraphFetched('events.[attributes]') + .modifyGraph('events', (builder) => { + builder.orderBy('id', 'asc'); + }) + .modifyGraph('events.[attributes]', (builder) => { + builder.orderBy('index', 'asc'); + }) + .orderBy('id', 'asc') + .where('height', '>=', blockCheckpoint?.height ?? 0) + .andWhere('height', '<', lastBlock) + .transacting(trx); + + const eventPatches: any[] = []; + const txPatches: any[] = []; + this.logger.info(`Re assign msg index ${listTx.length} txs`); + listTx.forEach((tx: any) => { + const { eventPatchInTx, txPatchInTx } = this.generateListUpdateMsgIndex( + trx, + tx + ); + eventPatches.push(...eventPatchInTx); + txPatches.push(...txPatchInTx); + }); + this.logger.info('mapping done'); + await Promise.all(eventPatches); + this.logger.info('update event done'); + await Promise.all(txPatches); + this.logger.info('update tx done'); + + await BlockCheckpoint.query() + .update( + BlockCheckpoint.fromJson({ + job_name: BULL_JOB_NAME.JOB_REASSIGN_MSG_INDEX_TO_EVENT, + height: lastBlock, + }) + ) + .where({ + job_name: BULL_JOB_NAME.JOB_REASSIGN_MSG_INDEX_TO_EVENT, + }); + }); + } + + generateListUpdateMsgIndex( + trx: Knex.Transaction, + tx: any + ): { eventPatchInTx: any[]; txPatchInTx: any } { + const eventPatchInTx: any[] = []; + const txPatchInTx: any[] = []; + + // get data raw in tx + const rawData = tx.data; + // set msg_index to event + rawData.tx_response.events.forEach((event: any) => { + // eslint-disable-next-line no-param-reassign + delete event.msg_index; + }); + this.crawlTxService.setMsgIndexToEvent(rawData); + txPatchInTx.push( + Transaction.query() + .patch({ data: rawData }) + .where('id', tx.id) + .transacting(trx) + ); + const mapUpdateEvent = new Map(); + tx.events.forEach((event: any, index: number) => { + const rawEvents = rawData.tx_response.events; + // check if event in raw is the same as event in db + if (rawEvents[index].type === event.type) { + const checkIndex = event.attributes.every((attr: EventAttribute) => { + const decodedKey = rawEvents[index].attributes[attr.index].key + ? fromUtf8(fromBase64(rawEvents[index].attributes[attr.index].key)) + : ''; + const decodedValue = rawEvents[index].attributes[attr.index].value + ? fromUtf8( + fromBase64(rawEvents[index].attributes[attr.index].value) + ) + : ''; + return attr.key === decodedKey && attr.value === decodedValue; + }); + if (!checkIndex) { + throw new Error('order attribute is wrong'); + } else { + const msgIndex = rawEvents[index].msg_index ?? null; + if (mapUpdateEvent.has(msgIndex)) { + mapUpdateEvent.get(msgIndex).push(event.id); + } else { + mapUpdateEvent.set(msgIndex, [event.id]); + } + } + } else { + throw new Error(`order event is wrong, ${event.id}, ${index}`); + } + }); + mapUpdateEvent.forEach((value: number[], key: null | number) => { + eventPatchInTx.push( + Event.query() + .patch({ + tx_msg_index: key, + }) + .whereIn('id', value) + .transacting(trx) + ); + }); + return { + eventPatchInTx, + txPatchInTx, + }; + } + + async _start(): Promise { + const blockCheckpoint = await BlockCheckpoint.query().findOne({ + job_name: BULL_JOB_NAME.JOB_REASSIGN_MSG_INDEX_TO_EVENT, + }); + if (!blockCheckpoint) { + await BlockCheckpoint.query().insert({ + job_name: BULL_JOB_NAME.JOB_REASSIGN_MSG_INDEX_TO_EVENT, + height: config.crawlBlock.startBlock, + }); + const crawlBlockCheckpoint = await BlockCheckpoint.query().findOne({ + job_name: BULL_JOB_NAME.CRAWL_BLOCK, + }); + + this.createJob( + BULL_JOB_NAME.JOB_REASSIGN_MSG_INDEX_TO_EVENT, + BULL_JOB_NAME.JOB_REASSIGN_MSG_INDEX_TO_EVENT, + { + lastBlockCrawled: crawlBlockCheckpoint?.height ?? 0, + }, + { + removeOnComplete: true, + removeOnFail: { + count: 3, + }, + repeat: { + every: config.jobReassignMsgIndexToEvent.millisecondCrawl, + }, + } + ); + } + return super._start(); + } +} diff --git a/test/unit/services/crawl-transaction/parse_transaction.spec.ts b/test/unit/services/crawl-transaction/parse_transaction.spec.ts index c72cbe542..ff785aa85 100644 --- a/test/unit/services/crawl-transaction/parse_transaction.spec.ts +++ b/test/unit/services/crawl-transaction/parse_transaction.spec.ts @@ -4,6 +4,7 @@ import { AfterEach, BeforeEach, Describe, Test } from '@jest-decorated/core'; import { ServiceBroker } from 'moleculer'; import { Log } from '@cosmjs/stargate/build/logs'; import { Attribute, Event } from '@cosmjs/stargate/build/events'; +import { fromBase64, fromUtf8 } from '@cosmjs/encoding'; import { Transaction, Event as EventModel, @@ -154,6 +155,420 @@ export default class CrawlTransactionTest { // ); } + arrDest = { + index: 3, + type: 'coin_received', + attributes: [ + { key: 'receiver', value: 'aura162x2llsxzxmavtyuxjesceewmy4wvrp79ndcrw' }, + { key: 'amount', value: '1341uaura' }, + { key: 'authz_msg_index', value: '0' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '909uaura' }, + { key: 'authz_msg_index', value: '0' }, + { key: 'receiver', value: 'aura1ujv2gmfwrwzj504ntggqld0q5euafp76vgx5lj' }, + { key: 'amount', value: '154uaura' }, + { key: 'authz_msg_index', value: '1' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '104uaura' }, + { key: 'authz_msg_index', value: '1' }, + { key: 'receiver', value: 'aura177cgzmjve5m0je6yjukcj4mmmwj8p4dkqekglz' }, + { key: 'amount', value: '4511uaura' }, + { key: 'authz_msg_index', value: '2' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '3056uaura' }, + { key: 'authz_msg_index', value: '2' }, + { key: 'receiver', value: 'aura1rqll2d4wyylvl03ht6mhglswj46gkcr3ksvkm7' }, + { key: 'amount', value: '3617uaura' }, + { key: 'authz_msg_index', value: '3' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '2451uaura' }, + { key: 'authz_msg_index', value: '3' }, + { key: 'receiver', value: 'aura1a6x0znjhztz73tq07gjvzt9ru99866jm665w9p' }, + { key: 'amount', value: '5417uaura' }, + { key: 'authz_msg_index', value: '4' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '3670uaura' }, + { key: 'authz_msg_index', value: '4' }, + { key: 'receiver', value: 'aura1q93xkwtfv7nut0eqjjws377wkjk97265zsxlx6' }, + { key: 'amount', value: '30uaura' }, + { key: 'authz_msg_index', value: '5' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '20uaura' }, + { key: 'authz_msg_index', value: '5' }, + { key: 'receiver', value: 'aura1xk6nnn0gen9n9fduz0t3twyzt8c2uzedy2545a' }, + { key: 'amount', value: '2797uaura' }, + { key: 'authz_msg_index', value: '6' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '1895uaura' }, + { key: 'authz_msg_index', value: '6' }, + { key: 'receiver', value: 'aura1f3qxww8pnx0xrea7e03ffxnlau0fk5ufs3v0zj' }, + { key: 'amount', value: '2457uaura' }, + { key: 'authz_msg_index', value: '7' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '1665uaura' }, + { key: 'authz_msg_index', value: '7' }, + { key: 'receiver', value: 'aura1fwtkqe4yp652svrj5lzdu9lnykysh947msc4xq' }, + { key: 'amount', value: '2589uaura' }, + { key: 'authz_msg_index', value: '8' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '1754uaura' }, + { key: 'authz_msg_index', value: '8' }, + { key: 'receiver', value: 'aura1j6kwc05lw0p8e08ce3r5z5qlygjzu0aq095scc' }, + { key: 'amount', value: '212uaura' }, + { key: 'authz_msg_index', value: '9' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '143uaura' }, + { key: 'authz_msg_index', value: '9' }, + { key: 'receiver', value: 'aura15f6wn3nymdnhnh5ddlqletuptjag09tryrtpq5' }, + { key: 'amount', value: '1689uaura' }, + { key: 'authz_msg_index', value: '10' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '1144uaura' }, + { key: 'authz_msg_index', value: '10' }, + { key: 'receiver', value: 'aura1efq5q4uzn583nh5mauzc5cmgms53w9l6vs5dxz' }, + { key: 'amount', value: '105uaura' }, + { key: 'authz_msg_index', value: '11' }, + { key: 'receiver', value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw' }, + { key: 'amount', value: '71uaura' }, + { key: 'authz_msg_index', value: '11' }, + ], + }; + + arrSrc: any[] = [ + { + // this event not has checkedIndex because its belong to tx event + type: 'coin_received', + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==', + }, + { key: 'YW1vdW50', value: 'ODk2dWF1cmE=' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTE2MngybGxzeHp4bWF2dHl1eGplc2NlZXdteTR3dnJwNzluZGNydw==', + }, + { key: 'YW1vdW50', value: 'MTM0MXVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'MA==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'OTA5dWF1cmE=' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'MA==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTF1anYyZ21md3J3emo1MDRudGdncWxkMHE1ZXVhZnA3NnZneDVsag==', + }, + { key: 'YW1vdW50', value: 'MTU0dWF1cmE=' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'MQ==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MTA0dWF1cmE=' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'MQ==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTE3N2Nnem1qdmU1bTBqZTZ5anVrY2o0bW1td2o4cDRka3Fla2dseg==', + }, + { key: 'YW1vdW50', value: 'NDUxMXVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'Mg==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MzA1NnVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'Mg==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFycWxsMmQ0d3l5bHZsMDNodDZtaGdsc3dqNDZna2NyM2tzdmttNw==', + }, + { key: 'YW1vdW50', value: 'MzYxN3VhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'Mw==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MjQ1MXVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'Mw==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFhNngwem5qaHp0ejczdHEwN2dqdnp0OXJ1OTk4NjZqbTY2NXc5cA==', + }, + { key: 'YW1vdW50', value: 'NTQxN3VhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'NA==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MzY3MHVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'NA==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFxOTN4a3d0ZnY3bnV0MGVxamp3czM3N3drams5NzI2NXpzeGx4Ng==', + }, + { key: 'YW1vdW50', value: 'MzB1YXVyYQ==' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'NQ==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MjB1YXVyYQ==' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'NQ==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTF4azZubm4wZ2VuOW45ZmR1ejB0M3R3eXp0OGMydXplZHkyNTQ1YQ==', + }, + { key: 'YW1vdW50', value: 'Mjc5N3VhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'Ng==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MTg5NXVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'Ng==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmM3F4d3c4cG54MHhyZWE3ZTAzZmZ4bmxhdTBmazV1ZnMzdjB6ag==', + }, + { key: 'YW1vdW50', value: 'MjQ1N3VhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'Nw==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MTY2NXVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'Nw==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmd3RrcWU0eXA2NTJzdnJqNWx6ZHU5bG55a3lzaDk0N21zYzR4cQ==', + }, + { key: 'YW1vdW50', value: 'MjU4OXVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'OA==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MTc1NHVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'OA==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFqNmt3YzA1bHcwcDhlMDhjZTNyNXo1cWx5Z2p6dTBhcTA5NXNjYw==', + }, + { key: 'YW1vdW50', value: 'MjEydWF1cmE=' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'OQ==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MTQzdWF1cmE=' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'OQ==' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTE1ZjZ3bjNueW1kbmhuaDVkZGxxbGV0dXB0amFnMDl0cnlydHBxNQ==', + }, + { key: 'YW1vdW50', value: 'MTY4OXVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'MTA=' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'MTE0NHVhdXJh' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'MTA=' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFlZnE1cTR1em41ODNuaDVtYXV6YzVjbWdtczUzdzlsNnZzNWR4eg==', + }, + { key: 'YW1vdW50', value: 'MTA1dWF1cmE=' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'MTE=' }, + ], + }, + { + type: 'coin_received', + checkedIndex: 3, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + value: 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { key: 'YW1vdW50', value: 'NzF1YXVyYQ==' }, + { key: 'YXV0aHpfbXNnX2luZGV4', value: 'MTE=' }, + ], + }, + ]; + + @Test('Mapping event and log') + public async testMappingEventToLog() { + const eventEncodedFlats: any[] = []; + this.arrSrc.forEach((event: any, index: number) => { + event.attributes.forEach((attr: any) => { + eventEncodedFlats.push({ + ...{ + key: attr.key ? fromUtf8(fromBase64(attr.key)) : null, + value: attr.value ? fromUtf8(fromBase64(attr.value)) : null, + }, + indexEvent: index, + type: event.type, + }); + }); + }); + + this.crawlTxService?.mappingFlatEventToLog( + this.arrDest, + this.arrSrc, + this.arrDest.index + ); + + eventEncodedFlats + .filter((item: any) => item.indexMapped !== undefined) + .forEach((item: any) => { + // eslint-disable-next-line no-param-reassign + this.arrSrc[item.indexEvent].msg_index = item.indexMapped; + expect(this.arrSrc[item.indexEvent].msg_index).toEqual( + item.checkedIndex + ); + }); + } + @AfterEach() async tearDown() { this.crawlTxService?.getQueueManager().stopAll(); diff --git a/test/unit/services/job/reassign_msg_index_to_event.spec.ts b/test/unit/services/job/reassign_msg_index_to_event.spec.ts new file mode 100644 index 000000000..ec885db43 --- /dev/null +++ b/test/unit/services/job/reassign_msg_index_to_event.spec.ts @@ -0,0 +1,896 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable no-restricted-syntax */ +import { AfterEach, BeforeEach, Describe, Test } from '@jest-decorated/core'; +import { ServiceBroker } from 'moleculer'; +import { Block, Transaction } from '../../../../src/models'; +import knex from '../../../../src/common/utils/db_connection'; +import JobReAssignMsgIndexToEvent from '../../../../src/services/job/reassign_msg_index_to_event.service'; +import CrawlTxService from '../../../../src/services/crawl-tx/crawl_tx.service'; + +@Describe('Test job reassign msg index to event attribute') +export default class CrawlTransactionTest { + broker = new ServiceBroker({ logger: false }); + + reassignMsgIndexService?: JobReAssignMsgIndexToEvent; + + crawlTxService?: CrawlTxService; + + @BeforeEach() + async initSuite() { + this.reassignMsgIndexService = this.broker.createService( + JobReAssignMsgIndexToEvent + ) as JobReAssignMsgIndexToEvent; + + this.crawlTxService = this.broker.createService( + CrawlTxService + ) as CrawlTxService; + + await Promise.all([ + knex.raw('TRUNCATE TABLE block RESTART IDENTITY CASCADE'), + knex.raw('TRUNCATE TABLE block_checkpoint RESTART IDENTITY CASCADE'), + ]); + } + + @Test('Test generate list update msg index') + public async testGenerateListUpdateMsgIndex() { + const blockData = { + ...Block.fromJson({ + height: 1, + hash: 1, + time: '2022-12-20T20:48:10.081247527Z', + proposer_address: 'xxx', + data: {}, + }), + txs: [ + { + '#id': 'transaction-marked', + hash: 'xxx', + codespace: '', + code: 0, + gas_used: '0', + gas_wanted: '0', + gas_limit: '0', + fee: '0', + index: 0, + timestamp: '2022-12-20T20:48:10.081247527Z', + data: { + tx_response: { + logs: [ + { + events: [ + { + type: 'coin_received', + attributes: [ + { + key: 'receiver', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + { + key: 'amount', + value: '672714ueaura', + }, + { + key: 'receiver', + value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw', + }, + { + key: 'amount', + value: '4266152ueaura', + }, + ], + }, + { + type: 'coin_spent', + attributes: [ + { + key: 'spender', + value: 'aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx', + }, + { + key: 'amount', + value: '672714ueaura', + }, + { + key: 'spender', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + { + key: 'amount', + value: '4266152ueaura', + }, + ], + }, + { + type: 'delegate', + attributes: [ + { + key: 'validator', + value: + 'auravaloper1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfe2raql', + }, + { + key: 'amount', + value: '4266152ueaura', + }, + { + key: 'new_shares', + value: '4266152.000000000000000000', + }, + ], + }, + { + type: 'message', + attributes: [ + { + key: 'action', + value: '/cosmos.staking.v1beta1.MsgDelegate', + }, + { + key: 'sender', + value: 'aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx', + }, + { + key: 'module', + value: 'staking', + }, + { + key: 'sender', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + ], + }, + { + type: 'transfer', + attributes: [ + { + key: 'recipient', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + { + key: 'sender', + value: 'aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx', + }, + { + key: 'amount', + value: '672714ueaura', + }, + ], + }, + { + type: 'withdraw_rewards', + attributes: [ + { + key: 'amount', + value: '672714ueaura', + }, + { + key: 'validator', + value: + 'auravaloper1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfe2raql', + }, + ], + }, + ], + }, + ], + events: [ + { + type: 'coin_spent', + msg_index: 0, + attributes: [ + { + key: 'c3BlbmRlcg==', + index: true, + value: + 'YXVyYTFjanAyamEzNno5dGRhYTBsMzd5cGt6ejJ2YXhoMjRsZnpjajRjcA==', + }, + { + key: 'YW1vdW50', + index: true, + value: 'MjAwdWVhdXJh', + }, + ], + }, + { + type: 'coin_received', + attributes: [ + { + key: 'cmVjZWl2ZXI=', + index: true, + value: + 'YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==', + }, + { + key: 'YW1vdW50', + index: true, + value: 'MjAwdWVhdXJh', + }, + ], + }, + { + type: 'transfer', + attributes: [ + { + key: 'cmVjaXBpZW50', + index: true, + value: + 'YXVyYTE3eHBmdmFrbTJhbWc5NjJ5bHM2Zjg0ejNrZWxsOGM1bHQwNXpmeQ==', + }, + { + key: 'c2VuZGVy', + index: true, + value: + 'YXVyYTFjanAyamEzNno5dGRhYTBsMzd5cGt6ejJ2YXhoMjRsZnpjajRjcA==', + }, + { + key: 'YW1vdW50', + index: true, + value: 'MjAwdWVhdXJh', + }, + ], + }, + { + type: 'message', + msg_index: 0, + attributes: [ + { + key: 'c2VuZGVy', + index: true, + value: + 'YXVyYTFjanAyamEzNno5dGRhYTBsMzd5cGt6ejJ2YXhoMjRsZnpjajRjcA==', + }, + ], + }, + { + type: 'tx', + attributes: [ + { + key: 'ZmVl', + index: true, + value: 'MjAwdWVhdXJh', + }, + { + key: 'ZmVlX3BheWVy', + index: true, + value: + 'YXVyYTFjanAyamEzNno5dGRhYTBsMzd5cGt6ejJ2YXhoMjRsZnpjajRjcA==', + }, + ], + }, + { + type: 'tx', + attributes: [ + { + key: 'YWNjX3NlcQ==', + index: true, + value: + 'YXVyYTFjanAyamEzNno5dGRhYTBsMzd5cGt6ejJ2YXhoMjRsZnpjajRjcC8zOTkxNjg=', + }, + ], + }, + { + type: 'tx', + attributes: [ + { + key: 'c2lnbmF0dXJl', + index: true, + value: + 'aXV2UFVMUEo1dFBHdmR3V0FOR0RPOTNqYUdJNDhjeVNybVFrSnB1S0psTWFSRk9YbTd2b0FGa0NEejJvT1JYcWFIMHV1VVlSWjJCbmhFcjFsV3VlS3c9PQ==', + }, + ], + }, + { + type: 'message', + msg_index: 0, + attributes: [ + { + key: 'YWN0aW9u', + index: true, + value: 'L2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGU=', + }, + ], + }, + { + type: 'coin_spent', + msg_index: 0, + attributes: [ + { + key: 'c3BlbmRlcg==', + index: true, + value: + 'YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==', + }, + { + key: 'YW1vdW50', + index: true, + value: 'NjcyNzE0dWVhdXJh', + }, + ], + }, + { + type: 'coin_received', + msg_index: 0, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + index: true, + value: + 'YXVyYTFjanAyamEzNno5dGRhYTBsMzd5cGt6ejJ2YXhoMjRsZnpjajRjcA==', + }, + { + key: 'YW1vdW50', + index: true, + value: 'NjcyNzE0dWVhdXJh', + }, + ], + }, + { + type: 'transfer', + msg_index: 0, + attributes: [ + { + key: 'cmVjaXBpZW50', + index: true, + value: + 'YXVyYTFjanAyamEzNno5dGRhYTBsMzd5cGt6ejJ2YXhoMjRsZnpjajRjcA==', + }, + { + key: 'c2VuZGVy', + index: true, + value: + 'YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==', + }, + { + key: 'YW1vdW50', + index: true, + value: 'NjcyNzE0dWVhdXJh', + }, + ], + }, + { + type: 'message', + msg_index: 0, + attributes: [ + { + key: 'c2VuZGVy', + index: true, + value: + 'YXVyYTFqdjY1czNncnFmNnY2amwzZHA0dDZjOXQ5cms5OWNkOHVmbjd0eA==', + }, + ], + }, + { + type: 'withdraw_rewards', + msg_index: 0, + attributes: [ + { + key: 'YW1vdW50', + index: true, + value: 'NjcyNzE0dWVhdXJh', + }, + { + key: 'dmFsaWRhdG9y', + index: true, + value: + 'YXVyYXZhbG9wZXIxY2pwMmphMzZ6OXRkYWEwbDM3eXBrenoydmF4aDI0bGZlMnJhcWw=', + }, + ], + }, + { + type: 'coin_spent', + msg_index: 0, + attributes: [ + { + key: 'c3BlbmRlcg==', + index: true, + value: + 'YXVyYTFjanAyamEzNno5dGRhYTBsMzd5cGt6ejJ2YXhoMjRsZnpjajRjcA==', + }, + { + key: 'YW1vdW50', + index: true, + value: 'NDI2NjE1MnVlYXVyYQ==', + }, + ], + }, + { + type: 'coin_received', + msg_index: 0, + attributes: [ + { + key: 'cmVjZWl2ZXI=', + index: true, + value: + 'YXVyYTFmbDQ4dnNubXNkemN2ODVxNWQycTR6NWFqZGhhOHl1M3dkN2Rtdw==', + }, + { + key: 'YW1vdW50', + index: true, + value: 'NDI2NjE1MnVlYXVyYQ==', + }, + ], + }, + { + type: 'delegate', + msg_index: 0, + attributes: [ + { + key: 'dmFsaWRhdG9y', + index: true, + value: + 'YXVyYXZhbG9wZXIxY2pwMmphMzZ6OXRkYWEwbDM3eXBrenoydmF4aDI0bGZlMnJhcWw=', + }, + { + key: 'YW1vdW50', + index: true, + value: 'NDI2NjE1MnVlYXVyYQ==', + }, + { + key: 'bmV3X3NoYXJlcw==', + index: true, + value: 'NDI2NjE1Mi4wMDAwMDAwMDAwMDAwMDAwMDA=', + }, + ], + }, + { + type: 'message', + msg_index: 0, + attributes: [ + { + key: 'bW9kdWxl', + index: true, + value: 'c3Rha2luZw==', + }, + { + key: 'c2VuZGVy', + index: true, + value: + 'YXVyYTFjanAyamEzNno5dGRhYTBsMzd5cGt6ejJ2YXhoMjRsZnpjajRjcA==', + }, + ], + }, + ], + }, + }, + events: [ + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'coin_spent', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_spent.spender', + index: 0, + key: 'spender', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_spent.amount', + index: 1, + key: 'amount', + value: '200ueaura', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: null, + type: 'coin_received', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_received.receiver', + index: 0, + key: 'receiver', + value: 'aura17xpfvakm2amg962yls6f84z3kell8c5lt05zfy', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_received.amount', + index: 1, + key: 'amount', + value: '200ueaura', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: null, + type: 'transfer', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'transfer.recipient', + index: 0, + key: 'recipient', + value: 'aura17xpfvakm2amg962yls6f84z3kell8c5lt05zfy', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'transfer.sender', + index: 1, + key: 'sender', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'transfer.amount', + index: 2, + key: 'amount', + value: '200ueaura', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'message', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'message.sender', + index: 0, + key: 'sender', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: null, + type: 'tx', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'tx.fee', + index: 0, + key: 'fee', + value: '200ueaura', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'tx.fee_payer', + index: 1, + key: 'fee_payer', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: null, + type: 'tx', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'tx.acc_seq', + index: 0, + key: 'acc_seq', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp/399168', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: null, + type: 'tx', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'tx.signature', + index: 0, + key: 'signature', + value: + 'iuvPULPJ5tPGvdwWANGDO93jaGI48cySrmQkJpuKJlMaRFOXm7voAFkCDz2oORXqaH0uuUYRZ2BnhEr1lWueKw==', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'message', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'message.action', + index: 0, + key: 'action', + value: '/cosmos.staking.v1beta1.MsgDelegate', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'coin_spent', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_spent.spender', + index: 0, + key: 'spender', + value: 'aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_spent.amount', + index: 1, + key: 'amount', + value: '672714ueaura', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'coin_received', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_received.receiver', + index: 0, + key: 'receiver', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_received.amount', + index: 1, + key: 'amount', + value: '672714ueaura', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'transfer', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'transfer.recipient', + index: 0, + key: 'recipient', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'transfer.sender', + index: 1, + key: 'sender', + value: 'aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'transfer.amount', + index: 2, + key: 'amount', + value: '672714ueaura', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'message', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'message.sender', + index: 0, + key: 'sender', + value: 'aura1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8ufn7tx', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'withdraw_rewards', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'withdraw_rewards.amount', + index: 0, + key: 'amount', + value: '672714ueaura', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'withdraw_rewards.validator', + index: 1, + key: 'validator', + value: 'auravaloper1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfe2raql', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'coin_spent', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_spent.spender', + index: 0, + key: 'spender', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_spent.amount', + index: 1, + key: 'amount', + value: '4266152ueaura', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'coin_received', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_received.receiver', + index: 0, + key: 'receiver', + value: 'aura1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3wd7dmw', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'coin_received.amount', + index: 1, + key: 'amount', + value: '4266152ueaura', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'delegate', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'delegate.validator', + index: 0, + key: 'validator', + value: 'auravaloper1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfe2raql', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'delegate.amount', + index: 1, + key: 'amount', + value: '4266152ueaura', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'delegate.new_shares', + index: 2, + key: 'new_shares', + value: '4266152.000000000000000000', + }, + ], + }, + { + source: 'TX_EVENT', + tx_msg_index: 0, + type: 'message', + attributes: [ + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'message.module', + index: 0, + key: 'module', + value: 'staking', + }, + { + tx_id: '#ref{transaction-marked.id}', + block_height: 1, + composite_key: 'message.sender', + index: 1, + key: 'sender', + value: 'aura1cjp2ja36z9tdaa0l37ypkzz2vaxh24lfzcj4cp', + }, + ], + }, + ], + }, + ], + }; + await knex.transaction(async (trx) => { + await Block.query() + .insertGraph(blockData, { allowRefs: true }) + .transacting(trx); + const tx = await Transaction.query() + .findOne({ + hash: 'xxx', + }) + .withGraphFetched('events.[attributes]') + .modifyGraph('events', (builder) => { + builder.orderBy('id', 'asc'); + }) + .modifyGraph('events.[attributes]', (builder) => { + builder.orderBy('index', 'asc'); + }) + .transacting(trx); + this.crawlTxService?.setMsgIndexToEvent(tx?.data); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const { eventPatchInTx, txPatchInTx } = + // eslint-disable-next-line no-unsafe-optional-chaining + this.reassignMsgIndexService?.generateListUpdateMsgIndex(trx, tx); + await Promise.all([...eventPatchInTx, ...txPatchInTx]); + }); + const tx = await Transaction.query() + .findOne({ + hash: 'xxx', + }) + .withGraphFetched('events.[attributes]') + .modifyGraph('events', (builder) => { + builder.orderBy('id', 'asc'); + }) + .modifyGraph('events.[attributes]', (builder) => { + builder.orderBy('index', 'asc'); + }); + expect(tx?.events[0].tx_msg_index).toEqual(null); + expect(tx?.events[1].tx_msg_index).toEqual(null); + expect(tx?.events[2].tx_msg_index).toEqual(null); + expect(tx?.events[3].tx_msg_index).toEqual(null); + expect(tx?.events[4].tx_msg_index).toEqual(null); + expect(tx?.events[5].tx_msg_index).toEqual(null); + expect(tx?.events[6].tx_msg_index).toEqual(null); + expect(tx?.events[7].tx_msg_index).toEqual(0); + expect(tx?.events[8].tx_msg_index).toEqual(0); + expect(tx?.events[9].tx_msg_index).toEqual(0); + expect(tx?.events[10].tx_msg_index).toEqual(0); + expect(tx?.events[11].tx_msg_index).toEqual(0); + expect(tx?.events[12].tx_msg_index).toEqual(0); + expect(tx?.events[13].tx_msg_index).toEqual(0); + expect(tx?.events[14].tx_msg_index).toEqual(0); + expect(tx?.events[15].tx_msg_index).toEqual(0); + expect(tx?.events[16].tx_msg_index).toEqual(0); + } + + @AfterEach() + async tearDown() { + await Promise.all([ + knex.raw('TRUNCATE TABLE block RESTART IDENTITY CASCADE'), + ]); + } +}