UNPKG

@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
"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