UNPKG

@ethereum-sourcify/bytecode-utils

Version:

Decode the CBOR encoded data at the end of an Ethereum contract's bytecode.

282 lines 19.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.splitAuxdata = exports.decode = exports.AuxdataStyle = void 0; const bytes_1 = require("@ethersproject/bytes"); const bs58_1 = __importDefault(require("bs58")); const CBOR = __importStar(require("cbor-x")); const semver_1 = __importDefault(require("semver")); var AuxdataStyle; (function (AuxdataStyle) { AuxdataStyle["SOLIDITY"] = "solidity"; AuxdataStyle["VYPER"] = "vyper"; AuxdataStyle["VYPER_LT_0_3_10"] = "vyper_lt_0_3_10"; AuxdataStyle["VYPER_LT_0_3_5"] = "vyper_lt_0_3_5"; })(AuxdataStyle || (exports.AuxdataStyle = AuxdataStyle = {})); /** * Decode contract's bytecode * @param bytecode - hex of the bytecode with 0x prefix * @param auxdataStyle - The style of auxdata, check AuxdataStyle enum for more info * @returns Object describing the contract */ const decode = (bytecode, auxdataStyle) => { if (bytecode.length === 0) { throw Error('Bytecode cannot be null'); } if (bytecode.substring(0, 2) !== '0x') { bytecode = '0x' + bytecode; } // split auxdata const [, auxdata] = (0, exports.splitAuxdata)(bytecode, auxdataStyle); if (!auxdata) { throw Error('Auxdata is not in the bytecode'); } // See more here: https://github.com/vyperlang/vyper/pull/3010 if (auxdataStyle === AuxdataStyle.VYPER) { // cbor decode the object and get a json const cborDecodedObject = CBOR.decode((0, bytes_1.arrayify)(`0x${auxdata}`)); // Starting with version 0.3.10, Vyper stores the auxdata as an array // after 0.3.10: [runtimesize, datasize,immutablesize,version_cbor_object] // after 0.4.1: [integrity,runtimesize, datasize,immutablesize,version_cbor_object] // See more here: https://github.com/vyperlang/vyper/pull/3584 if (cborDecodedObject instanceof Array) { // read the last element from array, it contains the compiler version const compilerVersion = cborDecodedObject[cborDecodedObject.length - 1].vyper.join('.'); if (semver_1.default.gte(compilerVersion, '0.4.1')) { // Starting with version 0.4.1 Vyper added the integrity field // See more here: https://github.com/vyperlang/vyper/pull/4234 return { integrity: cborDecodedObject[0], runtimeSize: cborDecodedObject[1], dataSizes: cborDecodedObject[2], immutableSize: cborDecodedObject[3], vyperVersion: compilerVersion, }; } else if (semver_1.default.gte(compilerVersion, '0.3.10')) { return { runtimeSize: cborDecodedObject[0], dataSizes: cborDecodedObject[1], immutableSize: cborDecodedObject[2], vyperVersion: compilerVersion, }; } } throw Error('This version of Vyper is not supported'); } else if (auxdataStyle === AuxdataStyle.VYPER_LT_0_3_10 || auxdataStyle === AuxdataStyle.VYPER_LT_0_3_5) { // cbor decode the object and get a json const cborDecodedObject = CBOR.decode((0, bytes_1.arrayify)(`0x${auxdata}`)); return { vyperVersion: cborDecodedObject.vyper.join('.'), }; } else if (auxdataStyle === AuxdataStyle.SOLIDITY) { // cbor decode the object and get a json const cborDecodedObject = CBOR.decode((0, bytes_1.arrayify)(`0x${auxdata}`)); const result = {}; // Decode all the parameters from the json Object.keys(cborDecodedObject).forEach((key) => { switch (key) { case 'ipfs': { const ipfsCID = bs58_1.default.encode(cborDecodedObject.ipfs); result.ipfs = ipfsCID; break; } case 'solc': { // nightly builds are string encoded if (typeof cborDecodedObject.solc === 'string') { result.solcVersion = cborDecodedObject.solc; } else { result.solcVersion = cborDecodedObject.solc.join('.'); } break; } case 'experimental': { result.experimental = cborDecodedObject.experimental; break; } case 'bzzr0': case 'bzzr1': default: { result[key] = (0, bytes_1.hexlify)(cborDecodedObject[key]); break; } } }); return result; } else { throw Error('Invalid auxdata style'); } }; exports.decode = decode; /** * Splits the bytecode into execution bytecode and auxdata. * If the bytecode does not contain CBOR-encoded auxdata, returns the whole bytecode. * * @param bytecode - Hex string of the bytecode with 0x prefix * @param auxdataStyle - The style of auxdata (Solidity or Vyper) * @returns An array containing execution bytecode and optionally auxdata and its length */ const splitAuxdata = (bytecode, auxdataStyle) => { validateBytecode(bytecode); bytecode = ensureHexPrefix(bytecode); const bytesLength = 4; const cborBytesLength = getCborBytesLength(bytecode, auxdataStyle, bytesLength); if (isCborLengthInvalid(bytecode, cborBytesLength, bytesLength)) { return [bytecode]; } const auxdata = extractAuxdata(bytecode, auxdataStyle, cborBytesLength, bytesLength); const executionBytecode = extractExecutionBytecode(bytecode, cborBytesLength, bytesLength); if (isCborEncoded(auxdata)) { const cborLengthHex = getCborLengthHex(bytecode, auxdataStyle, bytesLength); return [executionBytecode, auxdata, cborLengthHex]; } return [bytecode]; }; exports.splitAuxdata = splitAuxdata; /** * Validates that the bytecode is not empty. * * @param bytecode - The bytecode string to validate */ const validateBytecode = (bytecode) => { if (bytecode.length === 0) { throw Error('Bytecode cannot be null'); } }; /** * Ensures the bytecode string starts with '0x'. * * @param bytecode - The bytecode string * @returns The bytecode string with '0x' prefix */ const ensureHexPrefix = (bytecode) => { return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`; }; /** * Determines the length of the CBOR auxdata in bytes. * * @param bytecode - The complete bytecode string * @param auxdataStyle - The style of auxdata * @param bytesLength - The length of bytes used to encode the CBOR length * @returns An object containing the CBOR bytes length and a flag for legacy Vyper */ const getCborBytesLength = (bytecode, auxdataStyle, bytesLength) => { if (auxdataStyle === AuxdataStyle.VYPER_LT_0_3_5) { return 22; } const cborLengthHex = bytecode.slice(-bytesLength); return parseInt(cborLengthHex, 16) * 2; }; /** * Checks if the CBOR length is invalid based on the bytecode length. * * @param bytecode - The complete bytecode string * @param cborBytesLength - The length of CBOR auxdata in bytes * @param bytesLength - The length of bytes used to encode the CBOR length * @returns True if the CBOR length is invalid, otherwise false */ const isCborLengthInvalid = (bytecode, cborBytesLength, bytesLength) => { return bytecode.length - bytesLength - cborBytesLength <= 0; }; /** * Extracts the auxdata from the bytecode based on the auxdata style. * * @param bytecode - The complete bytecode string * @param auxdataStyle - The style of auxdata * @param cborBytesLength - The length of CBOR auxdata in bytes * @param bytesLength - The length of bytes used to encode the CBOR length * @returns The extracted auxdata as a hex string */ const extractAuxdata = (bytecode, auxdataStyle, cborBytesLength, bytesLength) => { switch (auxdataStyle) { case AuxdataStyle.VYPER_LT_0_3_10: case AuxdataStyle.SOLIDITY: return bytecode.substring(bytecode.length - bytesLength - cborBytesLength, bytecode.length - bytesLength); case AuxdataStyle.VYPER: return bytecode.substring(bytecode.length - cborBytesLength, bytecode.length - bytesLength); case AuxdataStyle.VYPER_LT_0_3_5: return bytecode.substring(bytecode.length - 22, bytecode.length); default: throw Error('Unsupported auxdata style'); } }; /** * Extracts the execution bytecode from the complete bytecode string. * * @param bytecode - The complete bytecode string * @param cborBytesLength - The length of CBOR auxdata in bytes * @param bytesLength - The length of bytes used to encode the CBOR length * @returns The execution bytecode as a hex string */ const extractExecutionBytecode = (bytecode, cborBytesLength, bytesLength) => { return bytecode.substring(0, bytecode.length - bytesLength - cborBytesLength); }; /** * Attempts to decode the auxdata to verify if it's CBOR-encoded. * * @param auxdata - The auxdata string to decode * @returns True if auxdata is CBOR-encoded, otherwise false */ const isCborEncoded = (auxdata) => { try { CBOR.decode((0, bytes_1.arrayify)(`0x${auxdata}`)); return true; } catch (_a) { return false; } }; /** * Retrieves the CBOR length from the bytecode based on the auxdata style. * * @param bytecode - The complete bytecode string * @param auxdataStyle - The style of auxdata * @param bytesLength - The length of bytes used to encode the CBOR length * @returns The CBOR length as a hex string */ const getCborLengthHex = (bytecode, auxdataStyle, bytesLength) => { if (auxdataStyle === AuxdataStyle.VYPER_LT_0_3_5) return ''; return bytecode.slice(-bytesLength); }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnl0ZWNvZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL2J5dGVjb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLGdEQUF5RDtBQUN6RCxnREFBd0I7QUFDeEIsNkNBQStCO0FBQy9CLG9EQUE0QjtBQTBCNUIsSUFBWSxZQUtYO0FBTEQsV0FBWSxZQUFZO0lBQ3RCLHFDQUFxQixDQUFBO0lBQ3JCLCtCQUFlLENBQUE7SUFDZixtREFBbUMsQ0FBQTtJQUNuQyxpREFBaUMsQ0FBQTtBQUNuQyxDQUFDLEVBTFcsWUFBWSw0QkFBWixZQUFZLFFBS3ZCO0FBRUQ7Ozs7O0dBS0c7QUFDSSxNQUFNLE1BQU0sR0FBRyxDQUNwQixRQUFnQixFQUNoQixZQUFlLEVBR00sRUFBRTtJQUN2QixJQUFJLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDMUIsTUFBTSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBQ0QsSUFBSSxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUN0QyxRQUFRLEdBQUcsSUFBSSxHQUFHLFFBQVEsQ0FBQztJQUM3QixDQUFDO0lBRUQsZ0JBQWdCO0lBQ2hCLE1BQU0sQ0FBQyxFQUFFLE9BQU8sQ0FBQyxHQUFHLElBQUEsb0JBQVksRUFBQyxRQUFRLEVBQUUsWUFBWSxDQUFDLENBQUM7SUFDekQsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2IsTUFBTSxLQUFLLENBQUMsZ0NBQWdDLENBQUMsQ0FBQztJQUNoRCxDQUFDO0lBRUQsOERBQThEO0lBQzlELElBQUksWUFBWSxLQUFLLFlBQVksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUN4Qyx3Q0FBd0M7UUFDeEMsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsTUFB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