Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 26 additions & 15 deletions src/source-verifier/tact-source-verifier.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import path from "path";
import semver from "semver";
import type { verify as VerifyFunctionLegacy } from "tact-1.4.0";
import type { TactLogger, verify as VerifyFunctionLegacy } from "tact-1.4.0";
import { Logger, PackageFileFormat } from "tact-1.4.1";
import type { verify as VerifyFunction } from "tact-1.5.2";
import { Cell } from "ton";
import { getSupportedVersions } from "../fetch-compiler-versions";
import { CompileResult, SourceVerifier, SourceVerifyPayload } from "../types";
import { timeoutPromise } from "../utils";
import { getLogger } from "../logger";
import { verifyTactNew } from "./verify";

const logger = getLogger("tact-source-verifier");

Expand All @@ -33,6 +34,24 @@ class OutputAppendingLogger extends Logger {
}
}

type VerifyFunctionType =
| typeof VerifyFunctionLegacy
| typeof VerifyFunction
| ReturnType<typeof verifyTactNew>;

async function dispatchVerify(version: string, output: string[]): Promise<VerifyFunctionType> {
try {
if (version < "1.6.0") {
return (await import(`tact-${version}`)).verify;
} else {
return await verifyTactNew(version);
}
} catch (e) {
output.push(`Failed to load tact v${version}. It probably doesn't exist on the server.`);
throw e;
}
}

export class TactSourceVerifier implements SourceVerifier {
fileSystem: FileSystem;

Expand Down Expand Up @@ -97,26 +116,18 @@ export class TactSourceVerifier implements SourceVerifier {
throw new Error("Unsupported tact version: " + pkgParsed.compiler.version);
}

const verify: typeof VerifyFunctionLegacy | typeof VerifyFunction = await import(
`tact-${pkgParsed.compiler.version}`
)
.then((m) => m.verify)
.catch((e) => {
output.push(
`Failed to load tact v${pkgParsed.compiler.version}. It probably doesn't exist on the server.`,
);
throw e;
});
const verify = await dispatchVerify(pkgParsed.compiler.version, output);

let vPromise;

if (this.isLegacyLogger(verify, pkgParsed.compiler.version)) {
const logger: TactLogger = {
log: (message: string) => output.push(message),
error: (message: string) => output.push(message),
};
vPromise = verify({
pkg,
logger: {
log: (message: string) => output.push(message),
error: (message: string) => output.push(message),
},
logger,
});
} else {
vPromise = verify({
Expand Down
107 changes: 107 additions & 0 deletions src/source-verifier/verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import normalize from "path-normalize";
import { Cell } from "@ton/core";
import type { Config, Options, ILogger, PackageFileFormat } from "tact-1.6.0";

export type VerifyResult =
| {
ok: true;
package: PackageFileFormat;
files: Record<string, string>;
}
| {
ok: false;
error:
| "invalid-package-format"
| "invalid-compiler"
| "invalid-compiler-version"
| "compilation-failed"
| "verification-failed";
};

export async function verifyTactNew(version: string) {
const { Logger, run } = (await import(`tact-${version}`)) as typeof import("tact-1.6.0");
const { fileFormat } = (await import(
`tact-${version}/dist/packaging/fileFormat`
)) as typeof import("tact-1.6.0/dist/packaging/fileFormat");
const { getCompilerVersion } = (await import(
`tact-${version}/dist/pipeline/version`
)) as typeof import("tact-1.6.0/dist/pipeline/version");

return async function verify(args: {
pkg: string;
logger?: ILogger | null | undefined;
}): Promise<VerifyResult> {
const logger: ILogger = args.logger ?? new Logger();

// Loading package
let unpacked: PackageFileFormat;
try {
const data = JSON.parse(args.pkg);
unpacked = fileFormat.parse(data);
} catch (_) {
return { ok: false, error: "invalid-package-format" };
}

if (unpacked.sources === undefined) {
return { ok: false, error: "invalid-package-format" };
}

// Check compiler and version
if (unpacked.compiler.name !== "tact") {
return { ok: false, error: "invalid-compiler" };
}
if (unpacked.compiler.version !== getCompilerVersion()) {
return { ok: false, error: "invalid-compiler-version" };
}

// Create a options
if (!unpacked.compiler.parameters) {
return { ok: false, error: "invalid-package-format" };
}
const params = JSON.parse(unpacked.compiler.parameters);
if (typeof params.entrypoint !== "string") {
return { ok: false, error: "invalid-package-format" };
}
const options: Options = params.options || {};
const entrypoint: string = params.entrypoint;

// Create config
const config: Config = {
projects: [
{
name: "verifier",
path: normalize("./contract/" + entrypoint),
output: "./output",
options,
},
],
};

// Build
const files: Record<string, string> = {};
for (const [name, source] of Object.entries(unpacked.sources)) {
files["contract/" + name] = source;
}

const result = await run({ config, files, logger });
if (!result.ok) {
return { ok: false, error: "compilation-failed" };
}

// Read output
const compiledCell = files["output/verifier_" + unpacked.name + ".code.boc"];
if (!compiledCell) {
return { ok: false, error: "verification-failed" };
}

// Check output
const a = Cell.fromBase64(compiledCell);
const b = Cell.fromBase64(unpacked.code);
if (!a.equals(b)) {
return { ok: false, error: "verification-failed" };
}

// Return
return { ok: true, package: unpacked, files };
};
}
Loading