From e3f04a00b333576f320996428f874b24851e6b82 Mon Sep 17 00:00:00 2001 From: Siegfried Puchbauer Date: Thu, 30 Jan 2020 12:55:58 -0800 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20geth=20node=20introspecti?= =?UTF-8?q?on=20if=20nodeInfo=20method=20is=20disabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed error when talking to Geth nodes with admin_nodeInfo method disabled by falling back to the generic node platform adapter. Also improved logging of enode field value in node info messages. --- src/eth/client.ts | 1 + src/eth/http.ts | 12 +++++++++++- src/eth/jsonrpc.ts | 4 ++-- src/introspect.ts | 13 ++++++++++++- src/meta.ts | 5 ++--- src/platforms/generic.ts | 22 ++++++++++++++-------- src/platforms/geth.ts | 7 ++++++- src/platforms/index.ts | 1 + src/platforms/parity.ts | 5 +++++ 9 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/eth/client.ts b/src/eth/client.ts index 525b098..d0b7f33 100644 --- a/src/eth/client.ts +++ b/src/eth/client.ts @@ -115,6 +115,7 @@ export class BatchedEthereumClient extends EthereumClient { callback: (e, result) => { if (e) { reject(e); + return; } try { checkError(result); diff --git a/src/eth/http.ts b/src/eth/http.ts index c7d896b..1bce5c6 100644 --- a/src/eth/http.ts +++ b/src/eth/http.ts @@ -2,7 +2,7 @@ import { default as HttpAgent, HttpOptions, HttpsAgent } from 'agentkeepalive'; import fetch from 'node-fetch'; import { createModuleDebug } from '../utils/debug'; import { isHttps } from '../utils/httputils'; -import { isValidJsonRpcResponse, JsonRpcRequest, JsonRpcResponse } from './jsonrpc'; +import { isValidJsonRpcResponse, JsonRpcRequest, JsonRpcResponse, checkError } from './jsonrpc'; import { EthereumTransport } from './transport'; import { httpClientStats, AggregateMetric } from '../utils/stats'; import { HttpTransportConfig } from '../config'; @@ -99,6 +99,16 @@ export class HttpTransport implements EthereumTransport { } this.aggregates.requestDuration.push(Date.now() - startTime); debug('Completed JSON RPC request in %d ms', Date.now() - startTime); + + if (Array.isArray(request) !== Array.isArray(data)) { + checkError(Array.isArray(data) ? data[0] : data); + throw new Error( + Array.isArray(request) + ? 'JSON RPC returned single message, was expecting batch' + : 'JSON RPC returned batch, was expecting single message' + ); + } + return data as JsonRpcResponse | JsonRpcResponse[]; } catch (e) { this.counters.errors++; diff --git a/src/eth/jsonrpc.ts b/src/eth/jsonrpc.ts index d8be67d..5483ff4 100644 --- a/src/eth/jsonrpc.ts +++ b/src/eth/jsonrpc.ts @@ -41,8 +41,8 @@ export class JsonRpcError extends Error { } } -export function checkError(msg: JsonRpcResponse) { - if (msg.error) { +export function checkError(msg?: JsonRpcResponse) { + if (msg?.error) { throw new JsonRpcError(msg.error.message, msg.error.code, msg.error.data); } } diff --git a/src/introspect.ts b/src/introspect.ts index 26b392c..1d35e7c 100644 --- a/src/introspect.ts +++ b/src/introspect.ts @@ -46,13 +46,24 @@ export async function introspectTargetNodePlatform( }); info('Retrieved ethereum node version: %s', version); - const adapter = createNodeAdapter(version, network); + let adapter = createNodeAdapter(version, network); if (typeof adapter.initialize === 'function') { debug('Initializing node platform adatper: %s', adapter.name); try { await adapter.initialize(eth); } catch (e) { error('Failed to initialize node platform adapter:', e); + + try { + adapter = new GenericNodeAdapter(version, network); + info('Attempting to use generic node platform adapter: %s', adapter.name); + if (typeof adapter.initialize === 'function') { + await adapter.initialize(eth); + } + return adapter; + } catch (e) { + error('Failed to initialize generic node platform adapter:', e); + } throw new Error(`Failed initialize node platform adapter ${adapter.name}`); } } diff --git a/src/meta.ts b/src/meta.ts index 3ba01fd..59c8aec 100644 --- a/src/meta.ts +++ b/src/meta.ts @@ -1,10 +1,9 @@ import { hostname } from 'os'; import { EthloggerConfig } from './config'; -import { KNOWN_NETOWORK_NAMES } from './eth/networks'; import { NodePlatformAdapter } from './platforms'; import { createModuleDebug } from './utils/debug'; import { removeEmtpyValues } from './utils/obj'; -import { subsituteVariablesInValues, subsituteVariables } from './utils/vars'; +import { subsituteVariables, subsituteVariablesInValues } from './utils/vars'; const { debug } = createModuleDebug('meta'); @@ -63,7 +62,7 @@ export function substituteVariablesInHecConfig( ENODE: platformAdapter.enode ?? undefined, PLATFORM: platformAdapter.name, NETWORK_ID: networkId != null ? String(networkId) : undefined, - NETWORK: config.eth.network ?? (networkId != null ? KNOWN_NETOWORK_NAMES[networkId] : undefined), + NETWORK: platformAdapter.networkName ?? undefined, PID: String(pid), VERSION: ethloggerVersion, NODE_VERSION: nodeVersion, diff --git a/src/platforms/generic.ts b/src/platforms/generic.ts index 830b246..c8ddfd4 100644 --- a/src/platforms/generic.ts +++ b/src/platforms/generic.ts @@ -1,7 +1,10 @@ import { NodePlatformAdapter } from '.'; import { EthereumClient } from '../eth/client'; +import { JsonRpcError } from '../eth/jsonrpc'; +import { KNOWN_NETOWORK_NAMES } from '../eth/networks'; import { blockNumber, + clientVersion, gasPrice, hashRate, netVersion, @@ -9,15 +12,13 @@ import { pendingTransactions, protocolVersion, syncing, - clientVersion, } from '../eth/requests'; import { formatPendingTransaction } from '../format'; -import { PendingTransactionMessage, NodeInfo } from '../msgs'; +import { NodeInfo, PendingTransactionMessage } from '../msgs'; import { OutputMessage } from '../output'; import { bigIntToNumber } from '../utils/bn'; import { createModuleDebug } from '../utils/debug'; import { prefixKeys } from '../utils/obj'; -import { JsonRpcError } from '../eth/jsonrpc'; const { debug, warn, error } = createModuleDebug('platforms:generic'); @@ -87,16 +88,17 @@ export async function checkPendingTransactionsMethodSupport(ethClient: EthereumC try { debug('Checking if generic node supports pendingTranscations RPC method'); await ethClient.request(pendingTransactions()); - debug('Pending transactions'); + debug('Pending transactions seem to be supported'); + return true; } catch (e) { - if (e instanceof JsonRpcError && e.message === 'Method not found') { - warn('Generic node does not seem to support the eth_pendingTransactions RPC method'); + if (e instanceof JsonRpcError) { + warn('Generic node does not seem to support the eth_pendingTransactions RPC method', e); return false; } else { error('Encountered unexpected error while checking for pendingTransaction method support', e); + return false; } } - return true; } export class GenericNodeAdapter implements NodePlatformAdapter { @@ -126,7 +128,7 @@ export class GenericNodeAdapter implements NodePlatformAdapter { protocolVersion: defaultInfo.protocolVersion ?? null, clientVersion: version, platform: `generic:${name}`, - network: this.network ?? null, + network: this.networkName, }; return this.nodeInfo; } @@ -147,6 +149,10 @@ export class GenericNodeAdapter implements NodePlatformAdapter { return this.nodeInfo?.networkId ?? null; } + public get networkName(): string | null { + return this.network ?? (this.networkId != null ? KNOWN_NETOWORK_NAMES[this.networkId] : null) ?? null; + } + public get protocolVersion(): number | null { return this.nodeInfo?.protocolVersion ?? null; } diff --git a/src/platforms/geth.ts b/src/platforms/geth.ts index 0f52218..4b51552 100644 --- a/src/platforms/geth.ts +++ b/src/platforms/geth.ts @@ -1,13 +1,14 @@ import { NodePlatformAdapter } from '.'; import { EthereumClient } from '../eth/client'; +import { KNOWN_NETOWORK_NAMES } from '../eth/networks'; import { clientVersion, gethMemStats, gethMetrics, gethNodeInfo, gethPeers, gethTxpool } from '../eth/requests'; import { GethMemStats, GethMetrics, GethNodeInfoResponse, GethPeer } from '../eth/responses'; import { formatPendingTransaction } from '../format'; import { NodeInfo, PendingTransactionMessage } from '../msgs'; import { OutputMessage } from '../output'; import { createModuleDebug } from '../utils/debug'; +import { durationStringToMs, parseAbbreviatedNumber } from '../utils/parse'; import { captureDefaultMetrics, fetchDefaultNodeInfo } from './generic'; -import { parseAbbreviatedNumber, durationStringToMs } from '../utils/parse'; const { debug, error } = createModuleDebug('platforms:geth'); @@ -186,6 +187,10 @@ export class GethAdapter implements NodePlatformAdapter { return this.nodeInfo?.protocolVersion ?? null; } + public get networkName(): string | null { + return this.network ?? (this.networkId != null ? KNOWN_NETOWORK_NAMES[this.networkId] : null) ?? null; + } + public async captureNodeStats(ethClient: EthereumClient, captureTime: number): Promise { const [defaultMetrics, getMetrics, txpoolData] = await Promise.all([ captureDefaultMetrics(ethClient, captureTime), diff --git a/src/platforms/index.ts b/src/platforms/index.ts index ae03744..20a2f84 100644 --- a/src/platforms/index.ts +++ b/src/platforms/index.ts @@ -9,6 +9,7 @@ export interface NodePlatformAdapter { readonly enode: string | null; readonly networkId: number | null; readonly protocolVersion: number | null; + readonly networkName: string | null; initialize?(ethClient: EthereumClient): Promise; captureNodeStats(ethClient: EthereumClient, captureTime: number): Promise; captureNodeInfo(ethClient: EthereumClient): Promise; diff --git a/src/platforms/parity.ts b/src/platforms/parity.ts index 5a1ef39..4b776a6 100644 --- a/src/platforms/parity.ts +++ b/src/platforms/parity.ts @@ -5,6 +5,7 @@ import { createModuleDebug } from '../utils/debug'; import { NodeInfo } from '../msgs'; import { fetchDefaultNodeInfo, captureDefaultMetrics } from './generic'; import { ParityMode, ParityNodeKind } from '../eth/responses'; +import { KNOWN_NETOWORK_NAMES } from '../eth/networks'; const { debug } = createModuleDebug('platforms:parity'); @@ -67,6 +68,10 @@ export class ParityAdapter implements NodePlatformAdapter { return this.nodeInfo?.protocolVersion ?? null; } + public get networkName(): string | null { + return this.network ?? (this.networkId != null ? KNOWN_NETOWORK_NAMES[this.networkId] : null) ?? null; + } + public async captureNodeStats(eth: EthereumClient, captureTime: number) { // TODO return [await captureDefaultMetrics(eth, captureTime)];