Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update cruntime do use machine-emulator-sdk:0.18.0 #5

Closed
wants to merge 7 commits into from
Closed
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
6 changes: 5 additions & 1 deletion apps/cli/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ module.exports = {
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: ["./tsconfig.eslint.json", "./tsconfig.json"],
project: [
"./tsconfig.eslint.json",
"./tsconfig.json",
"./test/tsconfig.json",
],
tsconfigRootDir: __dirname,
},
};
67 changes: 34 additions & 33 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,54 +18,55 @@
"/oclif.manifest.json"
],
"dependencies": {
"@inquirer/confirm": "^3.1.17",
"@inquirer/core": "^9.0.5",
"@inquirer/input": "^2.2.4",
"@inquirer/select": "^2.4.2",
"@inquirer/type": "^1.5.1",
"@oclif/core": "^4.0.14",
"@oclif/plugin-help": "^6.2.7",
"@oclif/plugin-plugins": "^5.3.9",
"bytes": "^3.1",
"@inquirer/confirm": "^4.0.1",
"@inquirer/core": "^9.2.1",
"@inquirer/input": "^3.0.1",
"@inquirer/select": "^3.0.1",
"@inquirer/type": "^1.5.5",
"@oclif/core": "^4.0.22",
"@oclif/plugin-help": "^6.2.11",
"@oclif/plugin-plugins": "^5.4.8",
"bytes": "^3.1.2",
"chalk": "^5.3.0",
"cli-table3": "^0.6.5",
"execa": "^9.3.0",
"fs-extra": "^11",
"execa": "^9.4.0",
"fs-extra": "^11.2.0",
"giget": "^1.2.3",
"lookpath": "^1.2.2",
"open": "^10.1.0",
"ora": "^8.0.1",
"progress-stream": "^2.0",
"ora": "^8.1.0",
"progress-stream": "^2.0.0",
"semver": "^7.6.3",
"smol-toml": "^1.3.0",
"tmp": "^0.2.3",
"viem": "^2.18.2"
"viem": "^2.21.9"
},
"devDependencies": {
"@cartesi/devnet": "workspace:*",
"@cartesi/eslint-config": "workspace:*",
"@sunodo/wagmi-plugin-hardhat-deploy": "^0.3.0",
"@types/bytes": "^3.1",
"@types/fs-extra": "^11",
"@types/inquirer": "^9",
"@types/node": "^20.14.12",
"@types/node-fetch": "^2.6",
"@types/progress-stream": "^2.0",
"@types/prompts": "^2.4",
"@types/bytes": "^3.1.4",
"@types/fs-extra": "^11.0.4",
"@types/inquirer": "^9.0.7",
"@types/node": "^22.5.5",
"@types/node-fetch": "^2.6.11",
"@types/progress-stream": "^2.0.5",
"@types/prompts": "^2.4.9",
"@types/semver": "^7.5.8",
"@types/tmp": "^0.2",
"@wagmi/cli": "^2.1.15",
"copyfiles": "^2",
"@types/tmp": "^0.2.6",
"@wagmi/cli": "^2.1.16",
"copyfiles": "^2.4.1",
"eslint": "^8.57.0",
"eslint-config-oclif": "^5.2.0",
"eslint-config-oclif-typescript": "^3.1.8",
"npm-run-all": "^4",
"oclif": "^4.14.9",
"eslint-config-oclif": "^5.2.1",
"eslint-config-oclif-typescript": "^3.1.11",
"npm-run-all": "^4.1.5",
"oclif": "^4.14.34",
"rimraf": "^6.0.1",
"ts-node": "^10",
"ts-node": "^10.9.2",
"tsconfig": "workspace:*",
"tslib": "^2.6.3",
"typescript": "^5.5.4",
"vitest": "^2.0.4"
"tslib": "^2.7.0",
"typescript": "^5.6.2",
"vitest": "^2.1.1"
},
"oclif": {
"bin": "cartesi",
Expand All @@ -84,7 +85,7 @@
"clean": "rimraf dist",
"codegen": "run-p codegen:wagmi",
"codegen:wagmi": "wagmi generate",
"compile": "tsc -b",
"compile": "tsc -p tsconfig.build.json",
"copy-files": "copyfiles -u 1 \"src/**/*.yaml\" \"src/**/*.env\" \"src/**/*.txt\" dist",
"lint": "eslint \"src/**/*.ts*\"",
"postpack": "rimraf oclif.manifest.json",
Expand Down
28 changes: 13 additions & 15 deletions apps/cli/src/baseCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import chalk from "chalk";
import { execa } from "execa";
import fs from "fs";
import path from "path";
import { Address, Hash, isHash } from "viem";
import { Address, Hash, getAddress, isHash } from "viem";

import { Config, parse } from "./config.js";
import {
authorityHistoryPairFactoryAddress,
cartesiDAppFactoryAddress,
Expand All @@ -26,14 +27,7 @@ export type Flags<T extends typeof Command> = Interfaces.InferredFlags<
(typeof BaseCommand)["baseFlags"] & T["flags"]
>;
export type Args<T extends typeof Command> = Interfaces.InferredArgs<T["args"]>;

export type AddressBook = Record<string, Address>;
export interface DApp {
address: Address;
blockHash: Address;
blockNumber: number;
transactionHash: Address;
}

export abstract class BaseCommand<T extends typeof Command> extends Command {
protected flags!: Flags<T>;
Expand Down Expand Up @@ -61,6 +55,12 @@ export abstract class BaseCommand<T extends typeof Command> extends Command {
return path.join(".cartesi", ...paths);
}

protected getApplicationConfig(configPath: string): Config {
return fs.existsSync(configPath)
? parse(fs.readFileSync(configPath).toString())
: parse("");
}

protected getMachineHash(): Hash | undefined {
// read hash of the cartesi machine snapshot, if one exists
const hashPath = this.getContextPath("image", "hash");
Expand All @@ -77,15 +77,18 @@ export abstract class BaseCommand<T extends typeof Command> extends Command {
this.log(`${chalk.green("?")} ${title} ${chalk.cyan(value)}`);
}

protected async getApplicationAddress(): Promise<Address | undefined> {
protected async getApplicationAddress(): Promise<Address> {
// fixed value, as we do deterministic deployment with a zero hash
return "0xab7528bb862fb57e8a2bcd567a2e929a0be56a5e";
return getAddress("0xab7528bb862fb57e8a2bcd567a2e929a0be56a5e");
}

protected async getAddressBook(): Promise<AddressBook> {
const applicationAddress = await this.getApplicationAddress();

// build rollups contracts address book
const contracts: AddressBook = {
AuthorityHistoryPairFactory: authorityHistoryPairFactoryAddress,
CartesiDApp: applicationAddress,
CartesiDAppFactory: cartesiDAppFactoryAddress,
DAppAddressRelay: dAppAddressRelayAddress,
EntryPointV06: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
Expand All @@ -110,11 +113,6 @@ export abstract class BaseCommand<T extends typeof Command> extends Command {
VerifyingPaymasterV07: "0xc5c97885C67F7361aBAfD2B95067a5bBdA603608",
};

// get dapp address
const applicationAddress = await this.getApplicationAddress();
if (applicationAddress) {
contracts["CartesiDApp"] = applicationAddress;
}
return contracts;
}

Expand Down
65 changes: 65 additions & 0 deletions apps/cli/src/builder/directory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import fs from "fs-extra";
import path from "path";
import { DirectoryDriveConfig } from "../config.js";
import { execaDockerFallback } from "../exec.js";

export const build = async (
name: string,
drive: DirectoryDriveConfig,
sdkImage: string,
destination: string,
): Promise<void> => {
const filename = `${name}.${drive.format}`;
const blockSize = 4096; // fixed at 4k
const extraBlocks = Math.ceil(drive.extraSize / blockSize);
const extraSize = `+${extraBlocks}`;

// copy directory to destination
const dest = path.join(destination, name);
await fs.mkdirp(dest);
await fs.copy(drive.directory, dest);

try {
switch (drive.format) {
case "ext2": {
const command = "xgenext2fs";
const args = [
"--block-size",
blockSize.toString(),
"--faketime",
"--root",
name,
"--readjustment",
extraSize,
];
await execaDockerFallback(command, args, {
cwd: destination,
image: sdkImage,
});
break;
}
case "sqfs": {
const compression = "lzo"; // make customizable? default is gzip
const command = "mksquashfs";
const args = [
"-all-time",
"0",
"-all-root", // XXX: should we use this?
"-noappend",
"-comp",
compression,
"-no-progress",
name,
filename,
];
await execaDockerFallback(command, args, {
cwd: destination,
image: sdkImage,
});
}
}
} finally {
// delete copied
await fs.remove(dest);
}
};
151 changes: 151 additions & 0 deletions apps/cli/src/builder/docker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { execa } from "execa";
import fs from "fs-extra";
import path from "path";
import tmp from "tmp";
import { DockerDriveConfig } from "../config.js";
import { execaDockerFallback, spawnSyncDockerFallback } from "../exec.js";
import { tarToExt } from "./index.js";

type ImageBuildOptions = Pick<
DockerDriveConfig,
"dockerfile" | "tags" | "target"
>;

type ImageInfo = {
cmd: string[];
entrypoint: string[];
env: string[];
workdir: string;
};

/**
* Build Docker image (linux/riscv64). Returns image id.
*/
const buildImage = async (options: ImageBuildOptions): Promise<string> => {
const { dockerfile, tags, target } = options;
const buildResult = tmp.tmpNameSync();
const args = [
"buildx",
"build",
"--file",
dockerfile,
"--load",
"--iidfile",
buildResult,
];

// set tags for the image built
args.push(...tags.map((tag) => ["--tag", tag]).flat());

if (target) {
args.push("--target", target);
}

await execa("docker", [...args, process.cwd()], { stdio: "inherit" });
return fs.readFileSync(buildResult, "utf8");
};

/**
* Query the image using docker image inspect
* @param image image id or name
* @returns Information about the image
*/
const getImageInfo = async (image: string): Promise<ImageInfo> => {
const { stdout: jsonStr } = await execa("docker", [
"image",
"inspect",
image,
]);
// parse image info from docker inspect output
const [imageInfo] = JSON.parse(jsonStr);

// validate image architecture (must be riscv64)
if (imageInfo["Architecture"] !== "riscv64") {
throw new Error(
`Invalid image Architecture: ${imageInfo["Architecture"]}. Expected riscv64`,
);
}

const info: ImageInfo = {
cmd: imageInfo["Config"]["Cmd"] ?? [],
entrypoint: imageInfo["Config"]["Entrypoint"] ?? [],
env: imageInfo["Config"]["Env"] || [],
workdir: imageInfo["Config"]["WorkingDir"],
};

return info;
};

export const build = async (
name: string,
drive: DockerDriveConfig,
sdkImage: string,
destination: string,
): Promise<ImageInfo | undefined> => {
const { format } = drive;

const ocitar = `${name}.oci.tar`;
const tar = `${name}.tar`;
const filename = `${name}.${format}`;

// use pre-existing image or build docker image
const image = drive.image || (await buildImage(drive));

// get image info
const imageInfo = await getImageInfo(image);

try {
// create OCI Docker tarball from Docker image
await execa("docker", ["image", "save", image, "-o", ocitar], {
cwd: destination,
});

// create rootfs tar from OCI tar
await spawnSyncDockerFallback("crane", ["export", "-", "-"], {
stdio: [
fs.openSync(path.join(destination, ocitar), "r"),
fs.openSync(path.join(destination, tar), "w"),
"inherit",
],
image: sdkImage,
});

switch (format) {
case "ext2": {
// create ext2
await tarToExt(tar, filename, format, drive.extraSize, {
cwd: destination,
image: sdkImage,
});
break;
}
case "sqfs": {
const compression = "lzo"; // make customizable? default is gzip
const command = "mksquashfs";
const args = [
"-tar",
"-all-time",
"0",
"-all-root", // XXX: should we use this?
"-noappend",
"-comp",
compression,
"-no-progress",
filename,
];
await execaDockerFallback(command, args, {
cwd: destination,
image: sdkImage,
inputFile: tar,
});
break;
}
}
} finally {
// delete intermediate files
// await fs.remove(path.join(destination, ocitar));
// await fs.remove(path.join(destination, tar));
}

return imageInfo;
};
Loading
Loading