From 61910f264c4671f057f774a415c673f10c09ddb1 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 21 Jan 2019 15:55:06 +0100 Subject: Print reasonable warnings on bytecode collisions --- packages/sol-tracing-utils/CHANGELOG.json | 4 ++++ packages/sol-tracing-utils/src/utils.ts | 27 +++++++++++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/sol-tracing-utils/CHANGELOG.json b/packages/sol-tracing-utils/CHANGELOG.json index c3c4bd058..202fb9bd0 100644 --- a/packages/sol-tracing-utils/CHANGELOG.json +++ b/packages/sol-tracing-utils/CHANGELOG.json @@ -13,6 +13,10 @@ { "note": "`SolCompilerArtifactAdapter` now doesn't return the `ContractData` for interfaces", "pr": "TODO" + }, + { + "note": "Print resasonable error message on bytecode collision", + "pr": "TODO" } ] }, diff --git a/packages/sol-tracing-utils/src/utils.ts b/packages/sol-tracing-utils/src/utils.ts index 40aa3e98a..7dc1844a5 100644 --- a/packages/sol-tracing-utils/src/utils.ts +++ b/packages/sol-tracing-utils/src/utils.ts @@ -1,13 +1,10 @@ -import { addressUtils, BigNumber } from '@0x/utils'; +import { addressUtils, BigNumber, logUtils } from '@0x/utils'; import { OpCode, StructLog } from 'ethereum-types'; import { addHexPrefix } from 'ethereumjs-util'; import * as _ from 'lodash'; import { ContractData, LineColumn, SingleFileSourceRange } from './types'; -// This is the minimum length of valid contract bytecode. The Solidity compiler -// metadata is 86 bytes. If you add the '0x' prefix, we get 88. -const MIN_CONTRACT_BYTECODE_LENGTH = 88; const STATICCALL_GAS_COST = 40; const bytecodeToContractDataIfExists: { [bytecode: string]: ContractData | undefined } = {}; @@ -54,22 +51,24 @@ export const utils = { if (bytecodeToContractDataIfExists.hasOwnProperty(bytecode)) { return bytecodeToContractDataIfExists[bytecode]; } - const contractData = _.find(contractsData, contractDataCandidate => { + const contractDataCandidates = _.filter(contractsData, contractDataCandidate => { const bytecodeRegex = utils.bytecodeToBytecodeRegex(contractDataCandidate.bytecode); - // If the bytecode is less than the minimum length, we are probably - // dealing with an interface. This isn't what we're looking for. - if (bytecodeRegex.length < MIN_CONTRACT_BYTECODE_LENGTH) { - return false; - } const runtimeBytecodeRegex = utils.bytecodeToBytecodeRegex(contractDataCandidate.runtimeBytecode); - if (runtimeBytecodeRegex.length < MIN_CONTRACT_BYTECODE_LENGTH) { - return false; - } // We use that function to find by bytecode or runtimeBytecode. Those are quasi-random strings so // collisions are practically impossible and it allows us to reuse that code return !_.isNull(bytecode.match(bytecodeRegex)) || !_.isNull(bytecode.match(runtimeBytecodeRegex)); }); - return (bytecodeToContractDataIfExists[bytecode] = contractData); + if (contractDataCandidates.length > 1) { + const candidates = contractDataCandidates.map( + contractDataCandidate => _.values(contractDataCandidate.sources)[0], + ); + const errMsg = + "We've found more than one artifact that contains the exact same bytecode and therefore are unable to detect which contract was executed. " + + "We'll be assigning all traces to the first one."; + logUtils.warn(errMsg); + logUtils.warn(candidates); + } + return (bytecodeToContractDataIfExists[bytecode] = contractDataCandidates[0]); }, isCallLike(op: OpCode): boolean { return _.includes([OpCode.CallCode, OpCode.StaticCall, OpCode.Call, OpCode.DelegateCall], op); -- cgit v1.2.3