Skip to content

Commit

Permalink
feat: 🎸 New config paramter to skip contract match requirement
Browse files Browse the repository at this point in the history
Added new config parameter `requireContractMatch`, which - when set to
`false` - skips the requirement for maching a contract using the
deployed contract address or a fingerprint in order for ethlogger to
emit the names of parameters for decoded calls and events
  • Loading branch information
ziegfried committed Jun 8, 2020
1 parent 465efdd commit 29403f4
Show file tree
Hide file tree
Showing 12 changed files with 37 additions and 11 deletions.
4 changes: 4 additions & 0 deletions config.schema.json
Expand Up @@ -20,6 +20,10 @@
"description": "If enabled, the ABI repsitory will creates hashes of all function and event signatures of an ABI\n(the hash is the fingerprint) and match it against the EVM bytecode obtained from live smart contracts\nwe encounter.",
"type": "boolean"
},
"requireContractMatch": {
"description": "If enabled, signature matches will be treated as anonyomous (parameter names will be omitted from\nthe output) if a contract cannot be tied to an ABI definition via either a fingerprint match,\nor a contract address match (when the ABI file includes the address of the deployed contract).\nEnabled by default. Setting this to `false` will output parameter names for any matching signature.",
"type": "boolean"
},
"searchRecursive": {
"description": "`true` to search ABI directory recursively for ABI files",
"type": "boolean"
Expand Down
1 change: 1 addition & 0 deletions defaults.ethlogger.yaml
Expand Up @@ -63,6 +63,7 @@ abi:
abiFileExtension: .json
searchRecursive: true
fingerprintContracts: true
requireContractMatch: true
decodeAnonymous: true
contractInfo:
maxCacheEntries: 25000
Expand Down
15 changes: 8 additions & 7 deletions docs/configuration.md
Expand Up @@ -212,13 +212,14 @@ The checkpoint is where ethlogger keeps track of its state, which blocks have al

The ABI repository is used to decode ABI information from smart contract calls and event logs. It generates and adds some additional information in transactions and events, including smart contract method call parameter names, values and data types, as well as smart contract names associated with a particular contract address.

| Name | Type | Description |
| ---------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `directory` | `string` | If specified, the ABI repository will recursively search this directory for ABI files |
| `searchRecursive` | `boolean` | `true` to search ABI directory recursively for ABI files |
| `abiFileExtension` | `string` | Set to `.json` by default as the file extension for ABIs |
| `fingerprintContracts` | `boolean` | If enabled, the ABI repsitory will creates hashes of all function and event signatures of an ABI (the hash is the fingerprint) and match it against the EVM bytecode obtained from live smart contracts we encounter. |
| `decodeAnonymous` | `boolean` | If enabled, ethlogger will attempt to decode function calls and event logs using a set of common signatures as a fallback if no match against any supplied ABI definition was found. |
| Name | Type | Description |
| ---------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `directory` | `string` | If specified, the ABI repository will recursively search this directory for ABI files |
| `searchRecursive` | `boolean` | `true` to search ABI directory recursively for ABI files |
| `abiFileExtension` | `string` | Set to `.json` by default as the file extension for ABIs |
| `fingerprintContracts` | `boolean` | If enabled, the ABI repsitory will creates hashes of all function and event signatures of an ABI (the hash is the fingerprint) and match it against the EVM bytecode obtained from live smart contracts we encounter. |
| `requireContractMatch` | `boolean` | If enabled, signature matches will be treated as anonyomous (parameter names will be omitted from the output) if a contract cannot be tied to an ABI definition via either a fingerprint match, or a contract address match (when the ABI file includes the address of the deployed contract). Enabled by default. Setting this to `false` will output parameter names for any matching signature. |
| `decodeAnonymous` | `boolean` | If enabled, ethlogger will attempt to decode function calls and event logs using a set of common signatures as a fallback if no match against any supplied ABI definition was found. |

### ContractInfo

Expand Down
10 changes: 6 additions & 4 deletions src/abi/repo.ts
Expand Up @@ -79,18 +79,17 @@ export class AbiRepository implements ManagedResource {

public async initialize() {
const config = this.config;
debug('Initializing ABI repository with config %O', config);
if (config.directory != null) {
const abiCount = await this.loadAbisFromDir(config.directory!, config);
info('Loaded %d ABIs from directory %s', abiCount, config.directory);
}

if (config.decodeAnonymous) {
const fnCount = await this.loadAnonymousSignatures(joinPath(__dirname, '../../data/fns.abisigs.gz'));
const evCount = await this.loadAnonymousSignatures(joinPath(__dirname, '../../data/evts.abisigs.gz'));
info('Loaded %d anonymous ABI signatures from built-in signature files', fnCount + evCount);
}
}

public async loadAnonymousSignatures(file: string): Promise<number> {
debug('Loading anonymous signatures from %s', file);
let count = 0;
Expand Down Expand Up @@ -206,6 +205,9 @@ export class AbiRepository implements ManagedResource {
}
}
if (match.length > 0) {
if (this.config.requireContractMatch === false) {
return { candidates: match, anonymous: false };
}
return { candidates: match, anonymous: true };
}
if (TRACE_ENABLED) {
Expand All @@ -222,7 +224,7 @@ export class AbiRepository implements ManagedResource {
private abiDecode<T>(
sigHash: string,
matchParams: AbiMatchParams,
decodeFn: (abis: AbiItemDefinition[], anonymous: boolean) => T
decoder: (abis: AbiItemDefinition[], anonymous: boolean) => T
): T | undefined {
const matchingAbis = this.findMatchingAbis(sigHash, matchParams);
trace('Found %d matching ABIs for signature %s', matchingAbis?.candidates?.length ?? 0, sigHash);
Expand All @@ -236,7 +238,7 @@ export class AbiRepository implements ManagedResource {
return;
}
try {
return decodeFn(matchingAbis.candidates, matchingAbis!.anonymous);
return decoder(matchingAbis.candidates, matchingAbis.anonymous);
} catch (e) {
if (matchingAbis!.anonymous) {
debug('Failed to decode anonymous ABI', e);
Expand Down
8 changes: 8 additions & 0 deletions src/config.ts
Expand Up @@ -152,6 +152,13 @@ export interface AbiRepositoryConfigSchema {
* we encounter.
*/
fingerprintContracts: boolean;
/**
* If enabled, signature matches will be treated as anonyomous (parameter names will be omitted from
* the output) if a contract cannot be tied to an ABI definition via either a fingerprint match,
* or a contract address match (when the ABI file includes the address of the deployed contract).
* Enabled by default. Setting this to `false` will output parameter names for any matching signature.
*/
requireContractMatch: boolean;
/**
* If enabled, ethlogger will attempt to decode function calls and event logs using a set of
* common signatures as a fallback if no match against any supplied ABI definition was found.
Expand Down Expand Up @@ -765,6 +772,7 @@ export async function loadEthloggerConfig(flags: CliFlags, dryRun: boolean = fal
directory: flags['abi-dir'] ?? defaults.abi?.directory,
abiFileExtension: defaults.abi?.abiFileExtension,
fingerprintContracts: defaults.abi?.fingerprintContracts ?? true,
requireContractMatch: defaults.abi?.requireContractMatch ?? true,
decodeAnonymous: defaults.abi?.decodeAnonymous ?? true,
},
blockWatcher: {
Expand Down
1 change: 1 addition & 0 deletions test/abi/contract.test.ts
Expand Up @@ -8,6 +8,7 @@ test('extractFunctionsAndEvents', async () => {
decodeAnonymous: false,
fingerprintContracts: true,
abiFileExtension: '.json',
requireContractMatch: true,
};
const abis = new AbiRepository(config);
await abis.loadAbiFile(join(__dirname, '../abis/BCB.json'), config);
Expand Down
2 changes: 2 additions & 0 deletions test/abi/files.test.ts
Expand Up @@ -7,6 +7,7 @@ describe('loadAbiFile', () => {
decodeAnonymous: false,
fingerprintContracts: true,
directory: join(__dirname, '../abis'),
requireContractMatch: true,
});

expect(result).toMatchSnapshot();
Expand All @@ -18,6 +19,7 @@ describe('loadAbiFile', () => {
decodeAnonymous: false,
fingerprintContracts: true,
directory: join(__dirname, '..'),
requireContractMatch: true,
})
).resolves.toMatchSnapshot();
});
Expand Down
3 changes: 3 additions & 0 deletions test/abi/repo.test.ts
Expand Up @@ -19,6 +19,7 @@ test('AbiRepository#decodeFunctionCall', async () => {
abiFileExtension: '.json',
directory: join(__dirname, '../abis'),
searchRecursive: true,
requireContractMatch: true,
});

await abiRepo.initialize();
Expand Down Expand Up @@ -125,6 +126,7 @@ test('AbiRepository#decodeLogEvent', async () => {
abiFileExtension: '.json',
directory: join(__dirname, '../abis'),
searchRecursive: true,
requireContractMatch: true,
});
await abiRepo.initialize();

Expand Down Expand Up @@ -182,6 +184,7 @@ test('decode anonymous with collision', async () => {
const abiRepo = new AbiRepository({
decodeAnonymous: true,
fingerprintContracts: false,
requireContractMatch: true,
});
await abiRepo.initialize();

Expand Down
1 change: 1 addition & 0 deletions test/abi/testcases/ethdenver.test.ts
Expand Up @@ -38,6 +38,7 @@ test('blockwatcher', async () => {
abiFileExtension: '.json',
directory: join(__dirname, '../../abis'),
searchRecursive: true,
requireContractMatch: true,
});
await abiRepo.initialize();
const checkpoints = new Checkpoint({
Expand Down
1 change: 1 addition & 0 deletions test/abi/testcases/ost.test.ts
Expand Up @@ -38,6 +38,7 @@ test('blockwatcher', async () => {
abiFileExtension: '.json',
directory: join(__dirname, 'abis'),
searchRecursive: true,
requireContractMatch: true,
});
await abiRepo.initialize();
const checkpoints = new Checkpoint({
Expand Down
1 change: 1 addition & 0 deletions test/blockwatcher.test.ts
Expand Up @@ -34,6 +34,7 @@ test('blockwatcher', async () => {
abiFileExtension: '.json',
directory: join(__dirname, './abis'),
searchRecursive: true,
requireContractMatch: true,
});
await abiRepo.initialize();
const checkpoints = new Checkpoint({
Expand Down
1 change: 1 addition & 0 deletions test/config.test.ts
Expand Up @@ -15,6 +15,7 @@ test('defaults', async () => {
"decodeAnonymous": true,
"directory": undefined,
"fingerprintContracts": true,
"requireContractMatch": true,
},
"blockWatcher": Object {
"blocksMaxChunkSize": 25,
Expand Down

0 comments on commit 29403f4

Please sign in to comment.