Skip to content
2 changes: 2 additions & 0 deletions config/config.devnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ features:
durationThresholdMs: 5000
failureCountThreshold: 5
resetTimeoutMs: 30000
elasticMigratedIndices:
logs: 'events'
statusChecker:
enabled: false
thresholds:
Expand Down
2 changes: 2 additions & 0 deletions config/config.e2e.mainnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ features:
durationThresholdMs: 5000
failureCountThreshold: 5
resetTimeoutMs: 30000
elasticMigratedIndices:
logs: 'events'
statusChecker:
enabled: false
thresholds:
Expand Down
2 changes: 2 additions & 0 deletions config/config.mainnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ features:
durationThresholdMs: 5000
failureCountThreshold: 5
resetTimeoutMs: 30000
elasticMigratedIndices:
logs: 'events'
statusChecker:
enabled: false
thresholds:
Expand Down
2 changes: 2 additions & 0 deletions config/config.testnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ features:
durationThresholdMs: 5000
failureCountThreshold: 5
resetTimeoutMs: 30000
elasticMigratedIndices:
logs: 'events'
statusChecker:
enabled: false
thresholds:
Expand Down
4 changes: 4 additions & 0 deletions src/common/api-config/api.config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,10 @@ export class ApiConfigService {
};
}

getElasticMigratedIndicesConfig(): Record<string, string> {
return this.configService.get<Record<string, string>>('features.elasticMigratedIndices') ?? {};
}

getIsWebsocketApiActive(): boolean {
return this.configService.get<boolean>('api.websocket') ?? true;
}
Expand Down
6 changes: 3 additions & 3 deletions src/common/indexer/elastic/elastic.indexer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -591,17 +591,17 @@ export class ElasticIndexerService implements IndexerInterface {
return query;
}

async getTransactionLogs(hashes: string[]): Promise<any[]> {
async getTransactionLogs(hashes: string[], eventsIndex: string, txHashField: string): Promise<any[]> {
const queries = [];
for (const hash of hashes) {
queries.push(QueryType.Match('_id', hash));
queries.push(QueryType.Match(txHashField, hash));
}

const elasticQueryLogs = ElasticQuery.create()
.withPagination({ from: 0, size: 10000 })
.withCondition(QueryConditionOptions.should, queries);

return await this.elasticService.getList('logs', 'id', elasticQueryLogs);
return await this.elasticService.getList(eventsIndex, 'id', elasticQueryLogs);
}

async getTransactionScResults(txHash: string): Promise<any[]> {
Expand Down
2 changes: 1 addition & 1 deletion src/common/indexer/entities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ export { Tag } from './tag';
export { Token } from './token';
export { TokenAccount, TokenType } from './token.account';
export { Transaction } from './transaction';
export { TransactionLog, TransactionLogEvent } from './transaction.log';
export { TransactionLog, TransactionLogEvent, ElasticTransactionLogEvent } from './transaction.log';
export { TransactionReceipt } from './transaction.receipt';
12 changes: 12 additions & 0 deletions src/common/indexer/entities/transaction.log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,15 @@ export interface TransactionLogEvent {
data?: string;
order: number;
}

export interface ElasticTransactionLogEvent {
address: string;
identifier: string;
topics: string[];
data?: string;
order: number;
txHash: string;
originalTxHash: string;
logAddress: string;
additionalData?: string[];
}
4 changes: 2 additions & 2 deletions src/common/indexer/indexer.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { TokenWithRolesFilter } from "src/endpoints/tokens/entities/token.with.r
import { TransactionFilter } from "src/endpoints/transactions/entities/transaction.filter";
import { TokenAssets } from "../assets/entities/token.assets";
import { QueryPagination } from "../entities/query.pagination";
import { Account, AccountHistory, AccountTokenHistory, Block, Collection, MiniBlock, Operation, Round, ScDeploy, ScResult, Tag, Token, TokenAccount, Transaction, TransactionLog, TransactionReceipt } from "./entities";
import { Account, AccountHistory, AccountTokenHistory, Block, Collection, MiniBlock, Operation, Round, ScDeploy, ScResult, Tag, Token, TokenAccount, Transaction, ElasticTransactionLogEvent, TransactionReceipt } from "./entities";
import { AccountAssets } from "../assets/entities/account.assets";
import { ProviderDelegators } from "./entities/provider.delegators";
import { ApplicationFilter } from "src/endpoints/applications/entities/application.filter";
Expand Down Expand Up @@ -134,7 +134,7 @@ export interface IndexerInterface {

getTokensForAddress(address: string, queryPagination: QueryPagination, filter: TokenFilter): Promise<Token[]>

getTransactionLogs(hashes: string[]): Promise<TransactionLog[]>
getTransactionLogs(hashes: string[], eventsIndex: string, txHashField: string): Promise<ElasticTransactionLogEvent[]>

getTransactionScResults(txHash: string): Promise<ScResult[]>

Expand Down
6 changes: 3 additions & 3 deletions src/common/indexer/indexer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TransactionFilter } from "src/endpoints/transactions/entities/transacti
import { MetricsEvents } from "src/utils/metrics-events.constants";
import { TokenAssets } from "../assets/entities/token.assets";
import { QueryPagination } from "../entities/query.pagination";
import { Account, AccountHistory, AccountTokenHistory, Block, Collection, MiniBlock, Operation, Round, ScDeploy, ScResult, Tag, Token, TokenAccount, Transaction, TransactionLog, TransactionReceipt } from "./entities";
import { Account, AccountHistory, AccountTokenHistory, Block, Collection, MiniBlock, Operation, Round, ScDeploy, ScResult, Tag, Token, TokenAccount, Transaction, ElasticTransactionLogEvent, TransactionReceipt } from "./entities";
import { IndexerInterface } from "./indexer.interface";
import { LogPerformanceAsync } from "src/utils/log.performance.decorator";
import { AccountQueryOptions } from "src/endpoints/accounts/entities/account.query.options";
Expand Down Expand Up @@ -302,8 +302,8 @@ export class IndexerService implements IndexerInterface {
}

@LogPerformanceAsync(MetricsEvents.SetIndexerDuration)
async getTransactionLogs(hashes: string[]): Promise<TransactionLog[]> {
return await this.indexerInterface.getTransactionLogs(hashes);
async getTransactionLogs(hashes: string[], eventsIndex: string, txHashField: string): Promise<ElasticTransactionLogEvent[]> {
return await this.indexerInterface.getTransactionLogs(hashes, eventsIndex, txHashField);
}

@LogPerformanceAsync(MetricsEvents.SetIndexerDuration)
Expand Down
3 changes: 3 additions & 0 deletions src/endpoints/transactions/entities/transaction.log.event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export class TransactionLogEvent {
@ApiProperty()
data: string = '';

@ApiProperty()
order: number = 0;

@ApiProperty()
additionalData: string[] | undefined = undefined;
}
47 changes: 45 additions & 2 deletions src/endpoints/transactions/transaction.get.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { TransactionOperationType } from "./entities/transaction.operation.type"
import { QueryPagination } from "src/common/entities/query.pagination";
import { NftFilter } from "../nfts/entities/nft.filter";
import { TokenAccount } from "src/common/indexer/entities";
import { ApiConfigService } from "../../common/api-config/api.config.service";

@Injectable()
export class TransactionGetService {
Expand All @@ -30,6 +31,7 @@ export class TransactionGetService {
private readonly gatewayService: GatewayService,
@Inject(forwardRef(() => TokenTransferService))
private readonly tokenTransferService: TokenTransferService,
private readonly apiConfigService: ApiConfigService,
) { }

private async tryGetTransactionFromElasticBySenderAndNonce(sender: string, nonce: number): Promise<TransactionDetailed | undefined> {
Expand All @@ -51,8 +53,49 @@ export class TransactionGetService {
return result.map(x => ApiUtils.mergeObjects(new TransactionLog(), x));
}

private async getTransactionLogsFromElasticInternal(hashes: string[]): Promise<any[]> {
return await this.indexerService.getTransactionLogs(hashes);
private async getTransactionLogsFromElasticInternal(hashes: string[]) {
const esMigratedIndices = this.apiConfigService.getElasticMigratedIndicesConfig();
const index = esMigratedIndices?.['logs'] ?? 'logs';
if (index === 'events') {
return await this.getTransactionLogsFromElasticInternalEventsIndex(hashes);
}

return await this.getTransactionLogsFromElasticInternalLogsIndex(hashes);
}

private async getTransactionLogsFromElasticInternalLogsIndex(hashes: string[]): Promise<any[]> {
return await this.indexerService.getTransactionLogs(hashes, 'logs', '_id');
}

private async getTransactionLogsFromElasticInternalEventsIndex(hashes: string[]): Promise<any[]> {
const rawHits = await this.indexerService.getTransactionLogs(hashes, 'events', 'txHash');

const logsMap: Map<string, TransactionLog> = new Map();

for (const source of rawHits) {
const txHash = source.txHash;

if (!logsMap.has(txHash)) {
logsMap.set(txHash, new TransactionLog({
id: txHash,
address: source.logAddress,
events: [],
}));
}

const event = {
identifier: source.identifier,
address: source.address,
data: source.data && source.data.length > 0 ? BinaryUtils.hexToBase64(source.data ?? '') : source.data,
additionalData: source.additionalData?.map(d => d && d.length > 0 ? BinaryUtils.hexToBase64(d) : d),
topics: source.topics?.map(t => t && t.length > 0 ? BinaryUtils.hexToBase64(t) : t),
order: source.order ?? 0,
};

logsMap.get(txHash)?.events.push(ApiUtils.mergeObjects(new TransactionLogEvent(), event));
}

return Array.from(logsMap.values());
}

async getTransactionScResultsFromElastic(txHash: string): Promise<SmartContractResult[]> {
Expand Down
Loading