From 1948fcbe00ccead37578f4c7e7a978879680f8e1 Mon Sep 17 00:00:00 2001 From: Siegfried Puchbauer Date: Wed, 15 Jan 2020 14:01:57 -0800 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20improved=20debug=20loggin?= =?UTF-8?q?g=20for=20ABI=20repository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improved debug logging of of code portions related to ABI decoding and contract fingerprints --- src/abi.ts | 38 ++++++++++++++++++++++++++++++-------- src/contract.ts | 8 ++++---- src/index.ts | 3 ++- test/abi.test.ts | 4 ++-- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/abi.ts b/src/abi.ts index c14cf58..271898a 100644 --- a/src/abi.ts +++ b/src/abi.ts @@ -3,12 +3,12 @@ import { basename, join } from 'path'; import { AbiCoder } from 'web3-eth-abi'; import { AbiInput, AbiItem, sha3, toChecksumAddress } from 'web3-utils'; import { computeContractFingerprint } from './contract'; -import { createModuleDebug } from './utils/debug'; +import { createModuleDebug, TRACE_ENABLED } from './utils/debug'; import { ManagedResource } from './utils/resource'; import { RawLogResponse } from './eth/responses'; import { parseBigInt } from './utils/bn'; -const { debug, warn } = createModuleDebug('abi'); +const { debug, warn, trace } = createModuleDebug('abi'); export type ScalarValue = string | number | boolean; export type Value = ScalarValue | ScalarValue[]; @@ -141,10 +141,13 @@ export class AbiRepository implements ManagedResource { public async loadAbiDir( dir: string, { recursive = true, fileNameSuffix = '.json' }: { recursive?: boolean; fileNameSuffix?: string } = {} - ) { + ): Promise { debug('Searching for ABI files in %s', dir); - const dirContents = await readdir(dir); + const dirContents = await readdir(dir).catch(e => + Promise.reject(new Error(`Failed to load ABIs from directory ${dir}: ${e}`)) + ); const subdirs = []; + let loaded = 0; for (const f of dirContents) { const full = join(dir, f); const s = await stat(full); @@ -152,9 +155,11 @@ export class AbiRepository implements ManagedResource { subdirs.push(join(dir, f)); } else if (s.isFile() && f.endsWith(fileNameSuffix)) { await this.loadAbiFile(full); + loaded++; } } - await Promise.all(subdirs.map(sub => this.loadAbiDir(sub, { recursive, fileNameSuffix }))); + const counts = await Promise.all(subdirs.map(sub => this.loadAbiDir(sub, { recursive, fileNameSuffix }))); + return loaded + counts.reduce((a, b) => a + b, 0); } public async loadAbiFile(path: string) { @@ -164,6 +169,7 @@ export class AbiRepository implements ManagedResource { } public loadAbi(abiData: any, fileName: string) { + debug('Loading ABI %s', fileName); let abis: AbiItem[]; let contractName: string; if (isTruffleBuildFile(abiData)) { @@ -201,6 +207,7 @@ export class AbiRepository implements ManagedResource { for (const i of items) { const { sigName, item } = i; const sigHash = computeSignatureHash(sigName, item.type as 'function' | 'event'); + debug('Signature for %s %s => %s', item.type, sigName, sigHash); let match: AbiMatch | undefined = this.signatures.get(sigHash); if (match == null) { match = { @@ -226,6 +233,7 @@ export class AbiRepository implements ManagedResource { } if (contractFingerprint != null) { + debug('Computed contract fingerprint %s for contract signature %s', contractFingerprint, contractName); this.contracts.set(contractFingerprint, { contractName, fileName, @@ -260,11 +268,10 @@ export class AbiRepository implements ManagedResource { ); if (abi != null) { debug( - 'Found ABI %s matching fingerprint %s from contract %s (%s)', + 'Found ABI %s matching fingerprint %s from contract %s', match.name, contractFingerprint, - abi.contractName, - abi.fileName + abi.contractName ); const inputs = abi.inputs ?? []; const decodedParams = this.abiCoder.decodeParameters( @@ -294,6 +301,13 @@ export class AbiRepository implements ManagedResource { params, args, }; + } else if (TRACE_ENABLED) { + trace( + 'No matching contract found for method signature %s hash %s and contract fingerprint %s', + match.name, + sigHash, + contractFingerprint + ); } return; } @@ -304,6 +318,7 @@ export class AbiRepository implements ManagedResource { if (match != null) { const abi = match.candidates.find(c => c.contractFingerprint === contractFingerprint); if (abi != null) { + debug('Found ABI %s matching fingerprint from contract %s', abi.name, abi.contractName); const { data, topics } = logEvent; const nonIndexedTypes = abi.inputs.filter(i => !i.indexed).map(i => i.type); const decodedData = this.abiCoder.decodeParameters(nonIndexedTypes, data.slice(2)); @@ -344,6 +359,13 @@ export class AbiRepository implements ManagedResource { params, args, }; + } else if (TRACE_ENABLED) { + trace( + 'No matching contract found for log event signature %s (hash %s) and contract fingerprint %s', + match.name, + sigHash, + contractFingerprint + ); } } return; diff --git a/src/contract.ts b/src/contract.ts index 9b4949b..1902ee3 100644 --- a/src/contract.ts +++ b/src/contract.ts @@ -4,7 +4,7 @@ import { getCode } from './eth/requests'; import { sha3 } from 'web3-utils'; import { createModuleDebug } from './utils/debug'; -const { debug } = createModuleDebug('contract'); +const { debug, trace } = createModuleDebug('contract'); export interface ContractInfo { /** True if the corresponding account is a smart contract, otherwise false */ @@ -58,6 +58,7 @@ export function computeContractFingerprint( return; } const fingerprint = sha3(`${functions.join(',')}|${events.join(',')}`).slice(2); + trace('Computed contract fingerprint %o for contract functions %o and events %o', fingerprint, functions, events); return fingerprint; } @@ -79,9 +80,8 @@ export async function getContractInfo( if (signatureMatcher == null) { return { isContract: true }; } - const fingerprint = computeContractFingerprint( - extractFunctionsAndEvents(code, (fingerprint: string) => signatureMatcher(fingerprint, address)) - ); + const fnsEvts = extractFunctionsAndEvents(code, (fingerprint: string) => signatureMatcher(fingerprint, address)); + const fingerprint = computeContractFingerprint(fnsEvts); const contractName = fingerprint != null && contractNameLookup != null ? contractNameLookup(address, fingerprint) : undefined; return { diff --git a/src/index.ts b/src/index.ts index d150458..461ca2a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -132,7 +132,8 @@ class Ethlogger extends Command { const abiRepo = new AbiRepository(); addResource(abiRepo); if (config.abi.directory != null) { - await abiRepo.loadAbiDir(config.abi.directory!); + const abiCount = await abiRepo.loadAbiDir(config.abi.directory!); + info('Loaded %d ABIs from directory %s', abiCount, config.abi.directory); } const contractInfoCache = new LRUCache>({ diff --git a/test/abi.test.ts b/test/abi.test.ts index 96559a4..a3960eb 100644 --- a/test/abi.test.ts +++ b/test/abi.test.ts @@ -4,7 +4,7 @@ import { join } from 'path'; test('AbiRepository#decodeMethod', async () => { const abiRepo = new AbiRepository(); - await expect(abiRepo.loadAbiDir(join(__dirname, 'abi'))).resolves.toBeUndefined(); + await expect(abiRepo.loadAbiDir(join(__dirname, 'abi'))).resolves.toMatchInlineSnapshot(`3`); expect(abiRepo.signatureCount).toMatchInlineSnapshot(`30`); @@ -70,7 +70,7 @@ test('AbiRepository#decodeMethod', async () => { test('AbiRepository#decodeLogEvent', async () => { const abiRepo = new AbiRepository(); - await expect(abiRepo.loadAbiDir(join(__dirname, 'abi'))).resolves.toBeUndefined(); + await expect(abiRepo.loadAbiDir(join(__dirname, 'abi'))).resolves.toMatchInlineSnapshot(`3`); expect( abiRepo.decodeLogEvent(