Skip to content

Commit

Permalink
Try to infer compiler versions from artifact metadata as well
Browse files Browse the repository at this point in the history
  • Loading branch information
cd1m0 committed Oct 8, 2024
1 parent 4ddd21c commit 7a24126
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 20 deletions.
57 changes: 43 additions & 14 deletions src/artifacts/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Decoder } from "cbor";
import { bytesToHex, hexToBytes } from "ethereum-cryptography/utils";
import { assert } from "solc-typed-ast";
import { assert, isExact } from "solc-typed-ast";
import { readInt16Be, toHexString } from "..";
import { HexString, PartialSolcOutput, UnprefixedHexString } from "./solc";

Expand Down Expand Up @@ -149,7 +149,7 @@ function getBytecodeHashHacky(bytecode: string | Uint8Array): ContractMdStruct |

function getDeployedBytecodeMdInfo(
deployedBytecode: UnprefixedHexString | Uint8Array
): ContractMdStruct {
): ContractMdStruct | undefined {
const len = deployedBytecode.length;

let rawMd: any = {};
Expand All @@ -174,6 +174,7 @@ function getDeployedBytecodeMdInfo(
} catch {
// The contract bytecode may not have metadata, which would result in random crashes in the decoder.
// Catch those so we don't end up crashing in the absence of metadata.
return undefined;
}

const res: ContractMdStruct = {};
Expand Down Expand Up @@ -204,6 +205,10 @@ function getDeployedBytecodeMdInfo(
export function getCodeHash(deplBytecode: UnprefixedHexString | Uint8Array): HexString | undefined {
const md = getDeployedBytecodeMdInfo(deplBytecode);

if (!md) {
return undefined;
}

// TODO: Should we prefix the hash with the hash type? bzzr0/ipfs
if (md.bzzr0 !== undefined) {
return md.bzzr0;
Expand Down Expand Up @@ -241,32 +246,56 @@ export function getCreationCodeHash(
return undefined;
}

const longVersion = /([0-9]+\.[0-9]+\.[0-9]+)\+.*/;

/**
* Given a standard solc JSON output `artifact` find the compiler version used
* to compute the contracts. We do this by walking over all of the bytecodes in
* the artifact, and decoding the CBOR-encoded metadata at the end of each
* contract. If all contracts in the artifact agree on the version they report,
* we return that.
*/
export function getArtifactCompilerVersion(artifact: PartialSolcOutput): string | undefined {
let res: string | undefined;

export function detectArtifactCompilerVersion(artifact: PartialSolcOutput): string | undefined {
for (const fileName in artifact.contracts) {
for (const contractName in artifact.contracts[fileName]) {
const version = getDeployedBytecodeMdInfo(
artifact.contracts[fileName][contractName].evm.deployedBytecode.object
).solc;
const contractArtifact = artifact.contracts[fileName][contractName];

if (contractArtifact.evm.deployedBytecode.object.length === 0) {
continue;
}

const md = getDeployedBytecodeMdInfo(contractArtifact.evm.deployedBytecode.object);

assert(
!(version !== undefined && res !== undefined && version !== res),
`Unexpected different compiler versions in the same artifact: ${version} and ${res}`
);
if (md !== undefined && md.solc !== undefined) {
return md.solc;
}

if (contractArtifact.metadata === undefined) {
continue;
}

try {
const mdJson = JSON.parse(contractArtifact.metadata);

if (mdJson.compiler && mdJson.compiler.version) {
if (isExact(mdJson.compiler.version)) {
return mdJson.compiler.version;
}

res = version;
const m = mdJson.compiler.version.match(longVersion);

if (m !== null) {
return m[1];
}
}
} catch (e) {
// Nothing to do;
console.error(e); // @todo remove
}
}
}

return res;
return undefined;
}

export function isPartialSolcOutput(arg: any): arg is PartialSolcOutput {
Expand Down
1 change: 1 addition & 0 deletions src/artifacts/solc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface PartialCompiledContract {
bytecode: PartialBytecodeDescription;
deployedBytecode: PartialBytecodeDescription;
};
metadata?: string;
}

export interface PartialSolcOutput {
Expand Down
24 changes: 18 additions & 6 deletions src/debug/artifact_manager/artifact_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ import {
PartialSolcOutput,
RawAST,
UnprefixedHexString,
detectArtifactCompilerVersion,
fastParseBytecodeSourceMapping,
findContractDef,
getArtifactCompilerVersion,
getCodeHash,
getCreationCodeHash
} from "../..";
Expand Down Expand Up @@ -158,18 +158,30 @@ export class ArtifactManager implements IArtifactManager {
return ABIEncoderVersion.V1;
}

constructor(artifacts: PartialSolcOutput[]) {
constructor(artifacts: Array<PartialSolcOutput | [PartialSolcOutput, string]>) {
this._artifacts = [];
this._contracts = [];
this._mdHashToContractInfo = new Map<string, ContractInfo>();
this._creationBytecodeTemplates = [];
this._deployedBytecodeTemplates = [];

for (const artifact of artifacts) {
for (const arg of artifacts) {
const reader = new ASTReader();
const compilerVersion = getArtifactCompilerVersion(artifact);

assert(compilerVersion !== undefined, `Couldn't find compiler version for artifact`);
let artifact: PartialSolcOutput;
let compilerVersion: string;

if (arg instanceof Array) {
[artifact, compilerVersion] = arg;
} else {
artifact = arg;
const maybeCompilerVersion = detectArtifactCompilerVersion(artifact);
assert(
maybeCompilerVersion !== undefined,
`Couldn't find compiler version for artifact`
);

compilerVersion = maybeCompilerVersion;
}

const units = reader.read(artifact);
const abiEncoderVersion = this.pickABIEncoderVersion(units, compilerVersion);
Expand Down

0 comments on commit 7a24126

Please sign in to comment.