@nomiclabs/buidler
Version:
Buidler is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
126 lines • 5.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ethereumjs_util_1 = require("ethereumjs-util");
const library_utils_1 = require("./library-utils");
const message_trace_1 = require("./message-trace");
/**
* This class represent a somewhat special Trie of bytecodes.
*
* What makes it special is that every node has a set of all of its descendants and its depth.
*/
class BytecodeTrie {
constructor(depth) {
this.depth = depth;
this.childNodes = new Map();
this.descendants = new Set();
}
static isBytecodeTrie(o) {
if (o === undefined || o === null) {
return false;
}
return "childNodes" in o;
}
add(bytecode) {
// tslint:disable-next-line no-this-assignment
let trieNode = this;
for (let currentCodeByte = 0; currentCodeByte <= bytecode.normalizedCode.length; currentCodeByte += 1) {
if (currentCodeByte === bytecode.normalizedCode.length) {
// If multiple contracts with the exact same bytecode are added we keep the last of them,
// which is probably correct, especially if we are going to support multiple compilations
trieNode.match = bytecode;
return;
}
const byte = bytecode.normalizedCode[currentCodeByte];
trieNode.descendants.add(bytecode);
let childNode = trieNode.childNodes.get(byte);
if (childNode === undefined) {
childNode = new BytecodeTrie(currentCodeByte);
trieNode.childNodes.set(byte, childNode);
}
trieNode = childNode;
}
}
/**
* Searches for a bytecode. If it's an exact match, it is returned. If there's no match, but a
* prefix of the code is found in the trie, the node of the longest prefix is returned. If the
* entire code is covered by the trie, and there's no match, we return undefined.
*/
search(code, currentCodeByte = 0) {
if (currentCodeByte > code.length) {
return undefined;
}
// tslint:disable-next-line no-this-assignment
let trieNode = this;
for (; currentCodeByte <= code.length; currentCodeByte += 1) {
if (currentCodeByte === code.length) {
return trieNode.match;
}
const childNode = trieNode.childNodes.get(code[currentCodeByte]);
if (childNode === undefined) {
return trieNode;
}
trieNode = childNode;
}
}
}
class ContractsIdentifier {
constructor(_enableCache = true) {
this._enableCache = _enableCache;
this._trie = new BytecodeTrie(-1);
this._cache = new Map();
}
addBytecode(bytecode) {
this._trie.add(bytecode);
this._cache.clear();
}
getBytecodeFromMessageTrace(trace) {
const normalizedCode = library_utils_1.normalizeLibraryRuntimeBytecodeIfNecessary(trace.code);
let normalizedCodeHex;
if (this._enableCache) {
normalizedCodeHex = ethereumjs_util_1.bufferToHex(normalizedCode);
const cached = this._cache.get(normalizedCodeHex);
if (cached !== undefined) {
return cached;
}
}
const result = this._searchBytecode(trace, normalizedCode);
if (this._enableCache) {
if (result !== undefined) {
this._cache.set(normalizedCodeHex, result);
}
}
return result;
}
_searchBytecode(trace, code, normalizeLibraries = true, trie = this._trie, firstByteToSearch = 0) {
const searchResult = trie.search(code, firstByteToSearch);
if (searchResult === undefined) {
return undefined;
}
if (!BytecodeTrie.isBytecodeTrie(searchResult)) {
return searchResult;
}
// Create traces are followed by metadata that we don't index
if (message_trace_1.isCreateTrace(trace) &&
searchResult.match !== undefined &&
searchResult.match.isDeployment) {
return searchResult.match;
}
if (normalizeLibraries) {
for (const bytecodeWithLibraries of searchResult.descendants) {
if (bytecodeWithLibraries.libraryAddressPositions.length === 0 &&
bytecodeWithLibraries.immutableReferences.length === 0) {
continue;
}
const normalizedLibrariesCode = library_utils_1.zeroOutAddresses(code, bytecodeWithLibraries.libraryAddressPositions);
const normalizedCode = library_utils_1.zeroOutSlices(normalizedLibrariesCode, bytecodeWithLibraries.immutableReferences);
const normalizedResult = this._searchBytecode(trace, normalizedCode, false, searchResult, searchResult.depth + 1);
if (normalizedResult !== undefined) {
return normalizedResult;
}
}
}
return undefined;
}
}
exports.ContractsIdentifier = ContractsIdentifier;
//# sourceMappingURL=contracts-identifier.js.map