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

Add localnet command #16

Merged
merged 28 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4ca6b76
chore(security): add workflow for leaked secrets monitoring
otani88 Feb 20, 2023
eeb45ec
Implement localnet subcommand
jpcenteno May 3, 2023
68a67ad
Use import instead of require to get type definitons.
jpcenteno May 4, 2023
a4c8d71
Cleanup
jpcenteno May 4, 2023
1a09eae
Point local-setup repo to our branch. Interface with docker-compose d…
jpcenteno May 11, 2023
cebc398
Merge pull request #2 from lambdaclass/feat-local-node
mationorato May 17, 2023
a8d342c
Add `localnet` subcommand to the README
jpcenteno May 30, 2023
1acd846
Merge pull request #5 from lambdaclass/document-localnet-subcommand
mationorato Jun 1, 2023
3973ee2
update docker compose from v1 to v2
mationorato Jun 1, 2023
d90245b
Update local-setup repo url to matter-labs main branch
mationorato Jun 5, 2023
5940293
update REPO_URL and REPO_BRANCH in ts file
mationorato Jun 5, 2023
4d44048
Merge branch 'upstream:main' into fix-merge-conflicts-with-matterlabs…
jpcenteno Jul 20, 2023
55b145c
Update help
jpcenteno Jul 20, 2023
860f415
Merge pull request #7 from lambdaclass/fix-merge-conflicts-with-matte…
jpcenteno Jul 20, 2023
edddec3
Update README.md
mationorato Aug 15, 2023
40b8aa8
Document why repoDirectory is using XDG_STATE_HOME
jpcenteno Sep 4, 2023
c58b62b
Remove new bin/ files introduced from our fork
jpcenteno Sep 4, 2023
f2a9ecf
Fix pkg version
jpcenteno Sep 4, 2023
724a224
Replace zksync 2.0 with Era for branding consistency
jpcenteno Sep 4, 2023
4dd22b2
Merge branch 'address-matterlabs-review-comments'
jpcenteno Sep 4, 2023
fcd11b1
Merge branch 'main' into main
jpcenteno Sep 4, 2023
21ea0bb
Update package lock
jpcenteno Sep 4, 2023
fbd18ed
Revert "Update package lock"
jpcenteno Sep 4, 2023
ec6a3e8
Fix messing newline at end of file
jpcenteno Sep 4, 2023
04eaa91
Address CI messages
jpcenteno Sep 4, 2023
cb5a4ba
Export localnet help function
jpcenteno Sep 4, 2023
38c14a3
Merge remote-tracking branch 'upstream/main'
jpcenteno Sep 6, 2023
7ffb3ea
fix: refactor localnet module to follow new project structure
jpcenteno Sep 6, 2023
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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ You can install this program globally with `npm i -g zksync-cli` or run the comm

> Both deposit and withdraw might take a couple of minutes to complete.

- `zksync-cli localnet`: Manages a local zkSync 2.0 and Ethereum L1 testnet. It supports a set of sub-subcommands:
- `zksync-cli localnet up`: Bootstrap L1 and L2 localnets.
- `zksync-cli localnet down`: clear L1 and L2 localnets.
- `zksync-cli localnet start`: start L1 and L2 localnets.
- `zksync-cli localnet stop`: stop L1 and L2 localnets.
- `zksync-cli localnet logs`: Display logs.
- `zksync-cli localnet help`: Display this message and quit.
- `zksync-cli localnet wallets`: Display seeded wallet keys.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


## Developing new features

### Install and build
Expand Down
7 changes: 6 additions & 1 deletion bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const figlet_1 = __importDefault(require("figlet"));
const create_1 = __importDefault(require("./create"));
const deposit_1 = __importDefault(require("./deposit"));
const withdraw_1 = __importDefault(require("./withdraw"));
const availableOptions = ['create', 'deposit', 'withdraw'];
const localnet_1 = __importDefault(require("./localnet"));
const availableOptions = ['create', 'deposit', 'withdraw', 'localnet'];
// second argument should be the selected option
const option = process.argv[2];
if (!availableOptions.includes(option)) {
Expand All @@ -33,4 +34,8 @@ switch (option) {
case 'withdraw':
(0, withdraw_1.default)();
break;
case 'localnet':
const subcommandName = process.argv[3] || undefined;
(0, localnet_1.default)(subcommandName);
break;
}
1 change: 1 addition & 0 deletions bin/localnet.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function (operationName: string | undefined): Promise<void>;
143 changes: 143 additions & 0 deletions bin/localnet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
"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 (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const REPO_URL = "https://github.com/matter-labs/local-setup.git";
const REPO_BRANCH = "main";
// ---------------------------------------------------------------------------------------
// Utilities
// ---------------------------------------------------------------------------------------
function runCommand(command, options) {
const defaultOptions = { cwd: repoDirectory(), encoding: 'utf-8' };
const unifiedOptions = Object.assign(Object.assign({}, defaultOptions), options);
return (0, child_process_1.execSync)(command, unifiedOptions).toString();
}
function repoDirectory() {
const xdgStateHome = process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local/state");
return path.join(xdgStateHome, "zksync-cli/local-setup");
}
function isRepoCloned() {
return fs.existsSync(repoDirectory());
}
function cloneRepo() {
const parentDirectory = path.join(repoDirectory(), "..");
runCommand(`mkdir -p '${parentDirectory}'`, { cwd: "/" });
const options = { cwd: parentDirectory };
runCommand(`git clone --branch '${REPO_BRANCH}' '${REPO_URL}'`, options);
}
function setUp() {
cloneRepo();
}
// ---------------------------------------------------------------------------------------
// Localnet operations
// ---------------------------------------------------------------------------------------
function logs() {
const options = { stdio: 'inherit' };
runCommand("docker compose logs --follow", options);
return 0;
}
function up() {
if (!isRepoCloned()) {
setUp();
}
runCommand("docker compose up --detach");
return 0;
}
function down() {
runCommand("docker compose down --volumes");
return 0;
}
function start() {
runCommand("docker compose start");
return 0;
}
function stop() {
runCommand("docker compose stop");
return 0;
}
function wallets() {
const rawJSON = fs.readFileSync(path.join(repoDirectory(), "rich-wallets.json")).toString();
const wallets = JSON.parse(rawJSON);
console.log(wallets);
return 0;
}
// ---------------------------------------------------------------------------------------
// Command handling
// ---------------------------------------------------------------------------------------
function help() {
console.log("USAGE: zksync-cli localnet <operation>");
console.log("");
console.log("Manage local L1 and L2 chains");
console.log("");
console.log("Available operations");
console.log(' up -- Bootstrap L1 and L2 localnets');
console.log(' down -- clear L1 and L2 localnets');
console.log(' start -- start L1 and L2 localnets');
console.log(' stop -- stop L1 and L2 localnets');
console.log(' logs -- Display logs');
console.log(' help -- Display this message and quit');
console.log(' wallets -- Display seeded wallet keys');
return 0;
}
function handleUndefinedOperation() {
console.error("No operation provided");
help();
return 1;
}
function handleInvalidOperation(operationName) {
const validOperationNames = Array.from(operationHandlers.keys());
console.error('Invalid operation: ', operationName);
help();
return 1;
}
const operationHandlers = new Map([
['up', up],
['down', down],
['start', start],
['stop', stop],
['logs', logs],
['help', help],
['wallets', wallets],
[undefined, handleUndefinedOperation],
]);
function default_1(operationName) {
return __awaiter(this, void 0, void 0, function* () {
const handler = operationHandlers.get(operationName) || (() => handleInvalidOperation(operationName));
process.exit(handler());
});
}
exports.default = default_1;
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import figlet from 'figlet';
import create from './create';
import deposit from './deposit';
import withdraw from './withdraw';
import localnet from './localnet';

const availableOptions: string[] = ['create', 'deposit', 'withdraw'];
const availableOptions: string[] = ['create', 'deposit', 'withdraw', 'localnet'];

// second argument should be the selected option
const option: string = process.argv[2];
Expand Down Expand Up @@ -43,4 +44,8 @@ switch (option) {
case 'withdraw':
withdraw();
break;
case 'localnet':
const subcommandName = process.argv[3] || undefined;
localnet(subcommandName);
break;
}
126 changes: 126 additions & 0 deletions src/localnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { execSync, ExecSyncOptions } from 'child_process';
import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';

const REPO_URL: string = "https://github.com/matter-labs/local-setup.git";
const REPO_BRANCH: string = "main"

// ---------------------------------------------------------------------------------------
// Utilities
// ---------------------------------------------------------------------------------------

function runCommand(command: string, options?: ExecSyncOptions): string {
const defaultOptions: ExecSyncOptions = { cwd: repoDirectory(), encoding: 'utf-8' };
const unifiedOptions: ExecSyncOptions = {...defaultOptions, ...options};
return execSync(command, unifiedOptions).toString();
}

function repoDirectory(): string {
const xdgStateHome = process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local/state");
mationorato marked this conversation as resolved.
Show resolved Hide resolved
return path.join(xdgStateHome, "zksync-cli/local-setup");
}

function isRepoCloned(): boolean {
return fs.existsSync(repoDirectory());
}

function cloneRepo() {
const parentDirectory = path.join(repoDirectory(), "..");
runCommand(`mkdir -p '${parentDirectory}'`, { cwd: "/" });
const options: ExecSyncOptions = { cwd: parentDirectory };
runCommand(`git clone --branch '${REPO_BRANCH}' '${REPO_URL}'`, options);
}

function setUp() {
cloneRepo();
}

// ---------------------------------------------------------------------------------------
// Localnet operations
// ---------------------------------------------------------------------------------------

function logs(): number {
const options: ExecSyncOptions = { stdio: 'inherit' };
runCommand("docker compose logs --follow", options);
return 0;
}

function up(): number {
if (! isRepoCloned()) {
setUp();
}
runCommand("docker compose up --detach");
return 0;
}

function down(): number {
runCommand("docker compose down --volumes");
return 0;
}

function start(): number {
runCommand("docker compose start");
return 0;
}

function stop(): number {
runCommand("docker compose stop");
return 0;
}

function wallets(): number {
const rawJSON = fs.readFileSync(path.join(repoDirectory(), "rich-wallets.json")).toString();
const wallets = JSON.parse(rawJSON);
console.log(wallets);
return 0;
}

// ---------------------------------------------------------------------------------------
// Command handling
// ---------------------------------------------------------------------------------------

function help(): number {
console.log("USAGE: zksync-cli localnet <operation>");
console.log("");
console.log("Manage local L1 and L2 chains");
console.log("");
console.log("Available operations");
console.log(' up -- Bootstrap L1 and L2 localnets');
console.log(' down -- clear L1 and L2 localnets');
console.log(' start -- start L1 and L2 localnets');
console.log(' stop -- stop L1 and L2 localnets');
console.log(' logs -- Display logs');
console.log(' help -- Display this message and quit');
console.log(' wallets -- Display seeded wallet keys');
return 0;
}

function handleUndefinedOperation(): number {
console.error("No operation provided");
help();
return 1;
}

function handleInvalidOperation(operationName: string): number {
const validOperationNames = Array.from(operationHandlers.keys());
console.error('Invalid operation: ', operationName);
help();
return 1;
}

const operationHandlers = new Map<string | undefined, () => number>([
['up', up],
['down', down],
['start', start],
['stop', stop],
['logs', logs],
['help', help],
['wallets', wallets],
[undefined, handleUndefinedOperation],
]);

export default async function (operationName: string | undefined) {
const handler = operationHandlers.get(operationName) || (() => handleInvalidOperation(operationName!));
process.exit(handler());
}