Print reasonable warnings on bytecode collisions

This commit is contained in:
Leonid Logvinov
2019-01-21 15:55:06 +01:00
parent ddb0e29253
commit 61910f264c
2 changed files with 17 additions and 14 deletions

View File

@@ -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"
}
]
},

View File

@@ -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);