Skip to content

Commit

Permalink
fix: 🐛 improved debug logging for ABI repository
Browse files Browse the repository at this point in the history
Improved debug logging of of code portions related to ABI decoding and
contract fingerprints
  • Loading branch information
ziegfried committed Jan 16, 2020
1 parent 9ed72cb commit 1948fcb
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 15 deletions.
38 changes: 30 additions & 8 deletions src/abi.ts
Expand Up @@ -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[];
Expand Down Expand Up @@ -141,20 +141,25 @@ export class AbiRepository implements ManagedResource {
public async loadAbiDir(
dir: string,
{ recursive = true, fileNameSuffix = '.json' }: { recursive?: boolean; fileNameSuffix?: string } = {}
) {
): Promise<number> {
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);
if (s.isDirectory() && recursive) {
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) {
Expand All @@ -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)) {
Expand Down Expand Up @@ -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 = {
Expand All @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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;
}
Expand All @@ -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));
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions src/contract.ts
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}

Expand All @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Expand Up @@ -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<string, Promise<ContractInfo>>({
Expand Down
4 changes: 2 additions & 2 deletions test/abi.test.ts
Expand Up @@ -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`);

Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit 1948fcb

Please sign in to comment.