Skip to content

Commit

Permalink
fix(ckb-indexer): wrong case handling when collect tx (#520)
Browse files Browse the repository at this point in the history
  • Loading branch information
homura authored May 25, 2023
1 parent 7b2be6b commit 59afa6c
Show file tree
Hide file tree
Showing 6 changed files with 348 additions and 169 deletions.
38 changes: 15 additions & 23 deletions packages/ckb-indexer/src/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ import {
utils,
Block,
} from "@ckb-lumos/base";
import { requestBatch } from "./services";
import { requestBatchTransactionWithStatus } from "./services";
import { CKBCellCollector } from "./collector";
import { EventEmitter } from "events";
import {
GetTransactionRPCResult,
CKBIndexerQueryOptions,
GetCellsResults,
GetLiveCellsResult,
Expand Down Expand Up @@ -271,31 +270,24 @@ export class CkbIndexer implements CellProvider, TerminableCellFetcher {
const blockNumber = block.header.number;
// publish changed events if subscribed script exists in previous output cells , skip the cellbase.
if (txIndex > 0) {
const requestData = tx.inputs.map((input, index) => {
return {
id: index,
jsonrpc: "2.0",
method: "get_transaction",
params: [input.previousOutput.txHash],
};
});
const inputTxHashes = tx.inputs.map(
(input) => input.previousOutput.txHash
);

// batch request by block
const transactionResponse: OutputToVerify[] = await requestBatch(
this.uri,
requestData
).then((response: GetTransactionRPCResult[]) => {
return response.map(
(item: GetTransactionRPCResult, index: number) => {
const cellIndex = tx.inputs[index].previousOutput.index;
const outputCell =
item.result.transaction.outputs[parseInt(cellIndex)];
const outputData =
item.result.transaction.outputsData[parseInt(cellIndex)];
return { output: outputCell, outputData } as OutputToVerify;
const transactionResponse: OutputToVerify[] =
await requestBatchTransactionWithStatus(this.uri, inputTxHashes).then(
(response) => {
return response.map((txWithStatus, index) => {
const cellIndex = tx.inputs[index].previousOutput.index;
const outputCell =
txWithStatus.transaction.outputs[parseInt(cellIndex)];
const outputData =
txWithStatus.transaction.outputsData[parseInt(cellIndex)];
return { output: outputCell, outputData };
});
}
);
});
transactionResponse.forEach(({ output, outputData }) => {
this.filterEvents(output, blockNumber, outputData);
});
Expand Down
63 changes: 53 additions & 10 deletions packages/ckb-indexer/src/services.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { utils, HexString, Script } from "@ckb-lumos/base";
import { utils, HexString } from "@ckb-lumos/base";
import { CKBIndexerQueryOptions, SearchKey } from "./type";
import fetch from "cross-fetch";
import { BI } from "@ckb-lumos/bi";
import { toScript } from "./paramsFormatter";
import type * as RPCType from "./rpcType";
import { toSearchKey } from "./resultFormatter";
import { unwrapScriptWrapper } from "./ckbIndexerFilter";
import { ResultFormatter } from "@ckb-lumos/rpc";
import { RPC as RpcTypes } from "@ckb-lumos/rpc/lib/types/rpc";
import { CKBComponents } from "@ckb-lumos/rpc/lib/types/api";

const generateSearchKey = (queries: CKBIndexerQueryOptions): SearchKey => {
let script: RPCType.Script | undefined = undefined;
Expand Down Expand Up @@ -67,17 +70,26 @@ const getHexStringBytes = (hexString: HexString): number => {
return Math.ceil(hexString.substr(2).length / 2);
};

let id = 0;
// will be tested in e2e
/* c8 ignore next 25 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const requestBatch = async (rpcUrl: string, data: unknown): Promise<any> => {
const res: Response = await fetch(rpcUrl, {
async function requestBatch<T = any>(
rpcUrl: string,
// eslint-disable-next-line
data: Record<string, unknown>[]
): Promise<{ result: T; jsonrpc: string; id: number | string }[]> {
if (!data.length) {
return [];
}

const res = await fetch(rpcUrl, {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
},
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data.map((item) => ({ id: id++, ...item }))),
});
if (res.status !== 200) {
throw new Error(`indexer request failed with HTTP code ${res.status}`);
throw new Error(`Indexer request failed with HTTP code ${res.status}`);
}
const result = await res.json();
if (result.error !== undefined) {
Expand All @@ -86,6 +98,37 @@ const requestBatch = async (rpcUrl: string, data: unknown): Promise<any> => {
);
}
return result;
};
}

/* c8 ignore next 23 */
async function requestBatchTransactionWithStatus(
rpcUrl: string,
txHashes: string[]
): Promise<CKBComponents.TransactionWithStatus[]> {
if (txHashes.length === 0) {
return [];
}
const requestBody = txHashes.map((txHash, index) => {
return {
id: index,
jsonrpc: "2.0",
method: "get_transaction",
params: [txHash],
};
});

export { generateSearchKey, getHexStringBytes, requestBatch };
const res = await requestBatch<RpcTypes.TransactionWithStatus>(
rpcUrl,
requestBody
);
return res.map((item) =>
ResultFormatter.toTransactionWithStatus(item.result)
);
}

export {
generateSearchKey,
getHexStringBytes,
requestBatch,
requestBatchTransactionWithStatus,
};
82 changes: 29 additions & 53 deletions packages/ckb-indexer/src/transaction_collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import {
IndexerTransactionList,
IOType,
Order,
GetTransactionRPCResult,
JsonRprRequestBody,
} from "./type";
import { CkbIndexer } from "./indexer";
import { instanceOfScriptWrapper } from "./ckbIndexerFilter";
Expand All @@ -43,6 +41,10 @@ export class CKBIndexerTransactionCollector extends BaseIndexerModule.Transactio
this.filterOptions = { ...defaultOptions, ...this.options };
}

/**
* @deprecated
* @param CKBRpcUrl
*/
public static asBaseTransactionCollector(
CKBRpcUrl: string
): typeof BaseTransactionCollector {
Expand Down Expand Up @@ -110,9 +112,8 @@ export class CKBIndexerTransactionCollector extends BaseIndexerModule.Transactio
public getResolvedTransactionRequestPayload(
unresolvedTransactionList: TransactionWithStatus[],
indexerTransactionList: IndexerTransactionList
): JsonRprRequestBody[] {
const requestPayload: JsonRprRequestBody[] = [];
let resolvedTransactionRequestId = 0;
): string[] {
const requestPayload: string[] = [];
unresolvedTransactionList.forEach(
(unresolvedTransaction: TransactionWithStatus, index: number) => {
const indexerTransaction = indexerTransactionList.objects[index];
Expand All @@ -121,35 +122,16 @@ export class CKBIndexerTransactionCollector extends BaseIndexerModule.Transactio
unresolvedTransaction.transaction.inputs[
Number(indexerTransaction.ioIndex)
].previousOutput;
requestPayload.push({
id: resolvedTransactionRequestId++,
jsonrpc: "2.0",
method: "get_transaction",
params: [unresolvedOutPoint.txHash],
});
requestPayload.push(unresolvedOutPoint.txHash);
}
}
);
return requestPayload;
}

public async fetchResolvedTransaction(
txIoTypeInputOutPointList: JsonRprRequestBody[]
): Promise<GetTransactionRPCResult[]> {
let resolvedTransaction: GetTransactionRPCResult[] = [];
if (txIoTypeInputOutPointList.length <= 0) {
return resolvedTransaction;
}
resolvedTransaction = await services.requestBatch(
this.CKBRpcUrl,
txIoTypeInputOutPointList
);
return resolvedTransaction;
}

public getResolvedCell(
unresolvedTransaction: TransactionWithStatus,
resolvedTransactionList: GetTransactionRPCResult[],
resolvedTransactionList: TransactionWithStatus[],
indexerTransaction: IndexerTransaction
): Output {
if (indexerTransaction.ioType !== "input") {
Expand All @@ -162,23 +144,23 @@ export class CKBIndexerTransactionCollector extends BaseIndexerModule.Transactio
Number(indexerTransaction.ioIndex)
].previousOutput;
const resolvedTransaction = resolvedTransactionList.find((tx) => {
return tx.result.transaction.hash === unresolvedOutPoint.txHash;
return tx.transaction.hash === unresolvedOutPoint.txHash;
});
if (!resolvedTransaction) {
throw new Error(`Impossible: can NOT find resolved transaction!`);
}
const resolvedCell =
resolvedTransaction.result.transaction.outputs[
resolvedTransaction.transaction.outputs[
Number(unresolvedOutPoint.index)
];
return resolvedCell;
}
}

//filter by ScriptWrapper.argsLen
public filterTransaction(
private filterTransaction(
unresolvedTransactionList: TransactionWithStatus[],
resolvedTransactionList: GetTransactionRPCResult[],
resolvedTransactionList: TransactionWithStatus[],
indexerTransactionList: IndexerTransactionList
): TransactionWithStatus[] {
const filteredTransactionList = unresolvedTransactionList.filter(
Expand Down Expand Up @@ -228,9 +210,11 @@ export class CKBIndexerTransactionCollector extends BaseIndexerModule.Transactio
unresolvedTransactionList,
indexerTransactionList
);
const resolvedTransactionList = await this.fetchResolvedTransaction(
requestPayload
);
const resolvedTransactionList =
await services.requestBatchTransactionWithStatus(
this.CKBRpcUrl,
requestPayload
);
const objects = this.filterTransaction(
unresolvedTransactionList,
resolvedTransactionList,
Expand Down Expand Up @@ -294,30 +278,22 @@ export class CKBIndexerTransactionCollector extends BaseIndexerModule.Transactio
indexerTransactionList: IndexerTransactionList
) => {
const getDetailRequestData = indexerTransactionList.objects.map(
(hashItem: IndexerTransaction, index: number) => {
return {
id: index,
jsonrpc: "2.0",
method: "get_transaction",
params: [hashItem.txHash],
};
(hashItem: IndexerTransaction) => {
return hashItem.txHash;
}
);

const transactionList: TransactionWithStatus[] = await services
.requestBatch(this.CKBRpcUrl, getDetailRequestData)
.then((response: GetTransactionRPCResult[]) => {
return response.map(
(item: GetTransactionRPCResult): TransactionWithStatus => {
if (!this.filterOptions.skipMissing && !item.result) {
throw new Error(
`Transaction ${
indexerTransactionList.objects[item.id].txHash
} is missing!`
);
}
return { ...item.result };
.requestBatchTransactionWithStatus(this.CKBRpcUrl, getDetailRequestData)
.then((response) => {
return response.map((item, index) => {
if (!this.filterOptions.skipMissing && !item.transaction) {
throw new Error(
`Transaction ${indexerTransactionList.objects[index].txHash} is missing!`
);
}
);
return item;
});
});
return transactionList;
};
Expand Down
22 changes: 11 additions & 11 deletions packages/ckb-indexer/tests/test_cases.js
Original file line number Diff line number Diff line change
Expand Up @@ -7079,7 +7079,7 @@ const transactionByLock = [
},
txStatus: {
blockHash: '0x92b197aa1fba0f63633922c61c92375c9c074a93e85963554f5499fe1450d0e5',
status: 'padding',
status: 'pending',
}
},
{
Expand Down Expand Up @@ -7115,7 +7115,7 @@ const transactionByLock = [
},
txStatus: {
blockHash: '0x7bb56e1288a1de98bab23d3e0ec7728634b6626ab03cc119ec23005a82ff12ff',
status: 'padding',
status: 'pending',
}
},
{
Expand Down Expand Up @@ -7151,7 +7151,7 @@ const transactionByLock = [
},
txStatus: {
blockHash: '0x21d6e5b949392186a6510c57da615f086f779b713a7f3d54c82a07d443e85c5d',
status: 'padding',
status: 'pending',
}
},
{
Expand Down Expand Up @@ -7187,7 +7187,7 @@ const transactionByLock = [
},
txStatus: {
blockHash: '0x3ea3a5122344ceb335ff776dbfa5f3e5a06b6edd965414f287f0d57f92304c89',
status: 'padding',
status: 'pending',
}
},
{
Expand Down Expand Up @@ -7223,7 +7223,7 @@ const transactionByLock = [
},
txStatus: {
blockHash: '0x1255b3b013addc93fb0301ad2a4150d15e6c1a1c4badbc653fc821b127a37929',
status: 'padding',
status: 'pending',
}
},
{
Expand Down Expand Up @@ -7259,7 +7259,7 @@ const transactionByLock = [
},
txStatus: {
blockHash: '0x2c96b715a2e2fe5602d518d75bc4912e6be16f34d9f1f2f420ff6e6de40c9379',
status: 'padding',
status: 'pending',
}
},
{
Expand Down Expand Up @@ -7295,7 +7295,7 @@ const transactionByLock = [
},
txStatus: {
blockHash: '0x02bfadbf60172d77af71ad141536d14f5ba191b40b5cffb2b2e7905459e8d500',
status: 'padding',
status: 'pending',
}
},
]
Expand Down Expand Up @@ -7378,10 +7378,10 @@ const transactionsByType = [
"witnesses": [
"0x55000000100000005500000055000000410000008b182319299acf5ba26e0d1f38854c69243a2fc8614ebbe813e87b24b2b9aafc35a11be33d65fcd2a3850d37cdb12c61a1693e8ea96e1a9a4a8aeebed297784a00"
]
},
},
txStatus: {
blockHash: '0x4cc7b42c12e0ed1c87c3ced726e419ba19f755be1739097d6758b6bf60c654ad',
status: 'padding',
status: 'pending',
}
},
{
Expand Down Expand Up @@ -7445,7 +7445,7 @@ const transactionsByType = [
},
txStatus: {
blockHash: '0xd4b10e5af3dac133888f47baeda057f7760fb4f81b2f4dc03a29c228c7dba7a0',
status: 'padding',
status: 'pending',
}
}
];
Expand Down Expand Up @@ -7512,7 +7512,7 @@ const transactionsByLockAndType = [
},
txStatus: {
blockHash: '0xd4b10e5af3dac133888f47baeda057f7760fb4f81b2f4dc03a29c228c7dba7a0',
status: 'padding',
status: 'pending',
}
}
];
Expand Down
Loading

1 comment on commit 59afa6c

@vercel
Copy link

@vercel vercel bot commented on 59afa6c May 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.