Skip to content

Commit 8cc8d79

Browse files
jintukumardasBelfordZmhanson-github
authored
SHARD-2024: Fix incorrect transaction index (#139)
* Fix incorrect transaction index * Add undefined check * Fix transaction index assignment to handle NaN values * fix: extract and test * Prettier'd * Fix lint error --------- Co-authored-by: Zachary Belford <belfordz66@gmail.com> Co-authored-by: Marc Hanson <marc.hanson.github@gmail.com>
1 parent 9c3f7c1 commit 8cc8d79

File tree

5 files changed

+1017
-156
lines changed

5 files changed

+1017
-156
lines changed

src/api.ts

Lines changed: 28 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ import { nestedCountersInstance } from './utils/nestedCounters'
5151
import { trySpendServicePoints } from './utils/servicePoints'
5252
import { archiverAPI } from './external/Archiver'
5353
import { TTLMap } from './utils/TTLMap'
54+
import { buildGetTransactionByBlockHashAndIndex } from './eth-handlers/eth_getTransactionByBlockHashAndIndex'
55+
import { buildGetTransactionByBlockNumberAndIndex } from './eth-handlers/eth_getTransactionByBlockNumberAndIndex'
5456

5557
export const verbose = config.verbose
5658
export const firstLineLogs = config.firstLineLogs
@@ -2381,162 +2383,32 @@ export const methods = {
23812383
callback(null, result)
23822384
countSuccessResponse(api_name, 'success', 'TBD')
23832385
},
2384-
eth_getTransactionByBlockHashAndIndex: async function (args: RequestParamsLike, callback: JSONRPCCallbackTypePlain) {
2385-
const api_name = 'eth_getTransactionByBlockHashAndIndex'
2386-
nestedCountersInstance.countEvent('endpoint', api_name)
2387-
if (!ensureArrayArgs(args, callback)) {
2388-
countFailedResponse(api_name, 'Invalid params: non-array args')
2389-
return
2390-
}
2391-
const ticket = crypto
2392-
.createHash('sha1')
2393-
.update(api_name + Math.random() + Date.now())
2394-
.digest('hex')
2395-
logEventEmitter.emit('fn_start', ticket, api_name, performance.now())
2396-
/* prettier-ignore */ if (firstLineLogs) { console.log('Running eth_getTransactionByBlockHashAndIndex', args) }
2397-
2398-
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
2399-
let result: string | completeReadableReceipt | null | undefined = null
2400-
2401-
try {
2402-
const blockResp = await collectorAPI.getBlock(args[0], 'hash', true)
2403-
result = blockResp?.transactions[Number(args[1])]
2404-
if (result) {
2405-
callback(null, result)
2406-
countSuccessResponse(api_name, 'success', 'collector')
2407-
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
2408-
return
2409-
}
2410-
} catch (e) {
2411-
callback(errorBusy)
2412-
countFailedResponse(api_name, 'exception in collectorAPI.getBlock')
2413-
logEventEmitter.emit('fn_end', ticket, { success: false }, performance.now())
2414-
}
2415-
const blockHash = args[0]
2416-
const index = parseInt(args[1], 16)
2417-
//if (blockHash !== 'latest') blockHash = parseInt(blockHash, 16)
2418-
if (config.queryFromValidator && config.queryFromExplorer) {
2419-
const explorerUrl = config.explorerUrl
2420-
try {
2421-
const res = await axios.get(`${explorerUrl}/api/transaction?blockHash=${blockHash}`)
2422-
if (verbose) {
2423-
console.log('url', `${explorerUrl}/api/transaction?blockHash=${blockHash}`)
2424-
console.log('res', JSON.stringify(res.data))
2425-
}
2426-
2427-
let result
2428-
if (res.data.success) {
2429-
if (typeof index === 'number' && index >= 0 && index < res.data.transactions.length) {
2430-
// eslint-disable-next-line security/detect-object-injection
2431-
result = extractTransactionObject(res.data.transactions[index], index)
2432-
}
2433-
} else result = null
2434-
2435-
const nodeUrl = config.explorerUrl
2436-
if (verbose) console.log('TRANSACTION DETAIL', result)
2437-
callback(null, result)
2438-
countSuccessResponse(api_name, 'success', 'explorer')
2439-
logEventEmitter.emit(
2440-
'fn_end',
2441-
ticket,
2442-
{ nodeUrl, success: res.data.transactions.length ? true : false },
2443-
performance.now()
2444-
)
2445-
} catch (error) {
2446-
/* prettier-ignore */ if (verbose) console.log('Error: eth_getTransactionByBlockHashAndIndex', (error as AxiosError).message)
2447-
callback(null, null)
2448-
countFailedResponse(api_name, 'exception in axios.get')
2449-
logEventEmitter.emit('fn_end', ticket, { success: false }, performance.now())
2450-
}
2451-
} else {
2452-
console.log('queryFromValidator and/or queryFromExplorer turned off. Could not process request')
2453-
callback(null, null)
2454-
countFailedResponse(api_name, 'queryFromValidator and/or queryFromExplorer turned off')
2455-
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
2456-
}
2457-
callback(null, result)
2458-
countSuccessResponse(api_name, 'success', 'fallback')
2459-
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
2460-
},
2461-
eth_getTransactionByBlockNumberAndIndex: async function (
2462-
args: RequestParamsLike,
2463-
callback: JSONRPCCallbackTypePlain
2464-
) {
2465-
const api_name = 'eth_getTransactionByBlockNumberAndIndex'
2466-
nestedCountersInstance.countEvent('endpoint', api_name)
2467-
if (!ensureArrayArgs(args, callback)) {
2468-
countFailedResponse(api_name, 'Invalid params: non-array args')
2469-
return
2470-
}
2471-
const ticket = crypto
2472-
.createHash('sha1')
2473-
.update(api_name + Math.random() + Date.now())
2474-
.digest('hex')
2475-
logEventEmitter.emit('fn_start', ticket, api_name, performance.now())
2476-
2477-
let result: string | completeReadableReceipt | null | undefined = null
2478-
try {
2479-
const blockResp = await collectorAPI.getBlock(args[0], 'hex_num', true)
2480-
result = blockResp?.transactions[Number(args[1])]
2481-
if (result) {
2482-
callback(null, result)
2483-
countSuccessResponse(api_name, 'success', 'collector')
2484-
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
2485-
return
2486-
}
2487-
} catch (e) {
2488-
callback(errorBusy)
2489-
countFailedResponse(api_name, 'exception in collectorAPI.getBlock')
2490-
logEventEmitter.emit('fn_end', ticket, { success: false }, performance.now())
2491-
}
2492-
/* prettier-ignore */ if (firstLineLogs) { console.log('Running eth_getTransactionByBlockNumberAndIndex', args) }
2493-
let blockNumber = args[0]
2494-
const index = parseInt(args[1], 16)
2495-
if (blockNumber !== 'latest' && blockNumber !== 'earliest') blockNumber = parseInt(blockNumber, 16)
2496-
if (blockNumber === 'earliest') blockNumber = 0
2497-
if (config.queryFromExplorer) {
2498-
const explorerUrl = config.explorerUrl
2499-
try {
2500-
const res = await axios.get(`${explorerUrl}/api/transaction?blockNumber=${blockNumber}`)
2501-
if (verbose) {
2502-
console.log('url', `${explorerUrl}/api/transaction?blockNumber=${blockNumber}`)
2503-
console.log('res', JSON.stringify(res.data))
2504-
}
2505-
2506-
let result
2507-
if (res.data.success) {
2508-
if (typeof index === 'number' && index >= 0 && index < res.data.transactions.length) {
2509-
// eslint-disable-next-line security/detect-object-injection
2510-
result = extractTransactionObject(res.data.transactions[index], index)
2511-
}
2512-
} else result = null
2513-
2514-
const nodeUrl = config.explorerUrl
2515-
if (verbose) console.log('TRANSACTION DETAIL', result)
2516-
callback(null, result)
2517-
countSuccessResponse(api_name, 'success', 'explorer')
2518-
logEventEmitter.emit(
2519-
'fn_end',
2520-
ticket,
2521-
{ nodeUrl, success: res.data.transactions.length ? true : false },
2522-
performance.now()
2523-
)
2524-
} catch (error) {
2525-
/* prettier-ignore */ if (verbose) console.log('Error: eth_getTransactionByBlockNumberAndIndex', (error as AxiosError).message)
2526-
callback(null, null)
2527-
countFailedResponse(api_name, 'exception in axios.get')
2528-
logEventEmitter.emit('fn_end', ticket, { success: false }, performance.now())
2529-
}
2530-
} else {
2531-
console.log('queryFromExplorer turned off. Could not process request')
2532-
callback(null, null)
2533-
countFailedResponse(api_name, 'queryFromExplorer turned off')
2534-
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
2535-
}
2536-
callback(null, result)
2537-
countSuccessResponse(api_name, 'success', 'fallback')
2538-
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
2539-
},
2386+
eth_getTransactionByBlockHashAndIndex: buildGetTransactionByBlockHashAndIndex({
2387+
nestedCountersInstance,
2388+
ensureArrayArgs,
2389+
countFailedResponse,
2390+
logEventEmitter,
2391+
firstLineLogs,
2392+
collectorAPI,
2393+
extractTransactionObject,
2394+
countSuccessResponse,
2395+
config,
2396+
verbose,
2397+
errorBusy,
2398+
}),
2399+
eth_getTransactionByBlockNumberAndIndex: buildGetTransactionByBlockNumberAndIndex({
2400+
nestedCountersInstance,
2401+
ensureArrayArgs,
2402+
countFailedResponse,
2403+
logEventEmitter,
2404+
firstLineLogs,
2405+
collectorAPI,
2406+
extractTransactionObject,
2407+
countSuccessResponse,
2408+
config,
2409+
verbose,
2410+
errorBusy,
2411+
}),
25402412
eth_getTransactionReceipt: async function (args: RequestParamsLike, callback: JSONRPCCallbackTypePlain) {
25412413
const api_name = 'eth_getTransactionReceipt'
25422414
nestedCountersInstance.countEvent('endpoint', api_name)
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { JSONRPCCallbackTypePlain, RequestParamsLike } from 'jayson'
2+
import crypto from 'crypto'
3+
import { completeReadableReceipt } from '../external/Collector'
4+
import axios, { AxiosError } from 'axios'
5+
6+
interface BuildGetTransactionByBlockHashAndIndex {
7+
nestedCountersInstance: any
8+
ensureArrayArgs: any
9+
countFailedResponse: any
10+
logEventEmitter: any
11+
firstLineLogs: any
12+
collectorAPI: any
13+
extractTransactionObject: any
14+
countSuccessResponse: any
15+
config: any
16+
verbose: any
17+
errorBusy: any
18+
}
19+
20+
export const buildGetTransactionByBlockHashAndIndex = ({
21+
nestedCountersInstance,
22+
ensureArrayArgs,
23+
countFailedResponse,
24+
logEventEmitter,
25+
firstLineLogs,
26+
collectorAPI,
27+
extractTransactionObject,
28+
countSuccessResponse,
29+
config,
30+
verbose,
31+
errorBusy,
32+
}: BuildGetTransactionByBlockHashAndIndex): ((
33+
args: RequestParamsLike,
34+
callback: JSONRPCCallbackTypePlain
35+
) => Promise<void>) => {
36+
const eth_getTransactionByBlockHashAndIndex = async (
37+
args: RequestParamsLike,
38+
callback: JSONRPCCallbackTypePlain
39+
): Promise<void> => {
40+
const api_name = 'eth_getTransactionByBlockHashAndIndex'
41+
nestedCountersInstance.countEvent('endpoint', api_name)
42+
if (!ensureArrayArgs(args, callback)) {
43+
countFailedResponse(api_name, 'Invalid params: non-array args')
44+
return
45+
}
46+
const ticket = crypto
47+
.createHash('sha1')
48+
.update(api_name + Math.random() + Date.now())
49+
.digest('hex')
50+
logEventEmitter.emit('fn_start', ticket, api_name, performance.now())
51+
/* prettier-ignore */ if (firstLineLogs) { console.log('Running eth_getTransactionByBlockHashAndIndex', args) }
52+
53+
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
54+
let result: string | completeReadableReceipt | null | undefined = null
55+
56+
try {
57+
const blockResp = await collectorAPI.getBlock((args as any)[0], 'hash', true)
58+
result = blockResp?.transactions[Number((args as any)[1])]
59+
if (result) {
60+
if (typeof result === 'object' && result.transactionIndex && (args as any)[1] !== undefined) {
61+
const transactionIndex = parseInt((args as any)[1], 16)
62+
if (!isNaN(transactionIndex)) {
63+
result.transactionIndex = '0x' + transactionIndex.toString(16)
64+
}
65+
}
66+
callback(null, result)
67+
countSuccessResponse(api_name, 'success', 'collector')
68+
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
69+
return
70+
}
71+
} catch (e) {
72+
callback(errorBusy)
73+
countFailedResponse(api_name, 'exception in collectorAPI.getBlock')
74+
logEventEmitter.emit('fn_end', ticket, { success: false }, performance.now())
75+
}
76+
const blockHash = (args as any)[0]
77+
const index = parseInt((args as any)[1], 16)
78+
//if (blockHash !== 'latest') blockHash = parseInt(blockHash, 16)
79+
if (config.queryFromValidator && config.queryFromExplorer) {
80+
const explorerUrl = config.explorerUrl
81+
try {
82+
const res = await axios.get(`${explorerUrl}/api/transaction?blockHash=${blockHash}`)
83+
if (verbose) {
84+
console.log('url', `${explorerUrl}/api/transaction?blockHash=${blockHash}`)
85+
console.log('res', JSON.stringify(res.data))
86+
}
87+
88+
let result
89+
if (res.data.success) {
90+
if (typeof index === 'number' && index >= 0 && index < res.data.transactions.length) {
91+
// eslint-disable-next-line security/detect-object-injection
92+
result = extractTransactionObject(res.data.transactions[index], index)
93+
}
94+
} else result = null
95+
96+
const nodeUrl = config.explorerUrl
97+
if (verbose) console.log('TRANSACTION DETAIL', result)
98+
callback(null, result)
99+
countSuccessResponse(api_name, 'success', 'explorer')
100+
logEventEmitter.emit(
101+
'fn_end',
102+
ticket,
103+
{ nodeUrl, success: res.data.transactions.length ? true : false },
104+
performance.now()
105+
)
106+
} catch (error) {
107+
/* prettier-ignore */ if (verbose) console.log('Error: eth_getTransactionByBlockHashAndIndex', (error as AxiosError).message)
108+
callback(null, null)
109+
countFailedResponse(api_name, 'exception in axios.get')
110+
logEventEmitter.emit('fn_end', ticket, { success: false }, performance.now())
111+
}
112+
} else {
113+
console.log('queryFromValidator and/or queryFromExplorer turned off. Could not process request')
114+
callback(null, null)
115+
countFailedResponse(api_name, 'queryFromValidator and/or queryFromExplorer turned off')
116+
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
117+
}
118+
callback(null, result)
119+
countSuccessResponse(api_name, 'success', 'fallback')
120+
logEventEmitter.emit('fn_end', ticket, { success: true }, performance.now())
121+
}
122+
123+
return eth_getTransactionByBlockHashAndIndex
124+
}

0 commit comments

Comments
 (0)